1 /* 2 * linux/fs/lockd/svcsubs.c 3 * 4 * Various support routines for the NLM server. 5 * 6 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> 7 */ 8 9 #include <linux/config.h> 10 #include <linux/types.h> 11 #include <linux/string.h> 12 #include <linux/time.h> 13 #include <linux/in.h> 14 #include <linux/mutex.h> 15 #include <linux/sunrpc/svc.h> 16 #include <linux/sunrpc/clnt.h> 17 #include <linux/nfsd/nfsfh.h> 18 #include <linux/nfsd/export.h> 19 #include <linux/lockd/lockd.h> 20 #include <linux/lockd/share.h> 21 #include <linux/lockd/sm_inter.h> 22 23 #define NLMDBG_FACILITY NLMDBG_SVCSUBS 24 25 26 /* 27 * Global file hash table 28 */ 29 #define FILE_HASH_BITS 5 30 #define FILE_NRHASH (1<<FILE_HASH_BITS) 31 static struct nlm_file * nlm_files[FILE_NRHASH]; 32 static DEFINE_MUTEX(nlm_file_mutex); 33 34 #ifdef NFSD_DEBUG 35 static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f) 36 { 37 u32 *fhp = (u32*)f->data; 38 39 /* print the first 32 bytes of the fh */ 40 dprintk("lockd: %s (%08x %08x %08x %08x %08x %08x %08x %08x)\n", 41 msg, fhp[0], fhp[1], fhp[2], fhp[3], 42 fhp[4], fhp[5], fhp[6], fhp[7]); 43 } 44 45 static inline void nlm_debug_print_file(char *msg, struct nlm_file *file) 46 { 47 struct inode *inode = file->f_file->f_dentry->d_inode; 48 49 dprintk("lockd: %s %s/%ld\n", 50 msg, inode->i_sb->s_id, inode->i_ino); 51 } 52 #else 53 static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f) 54 { 55 return; 56 } 57 58 static inline void nlm_debug_print_file(char *msg, struct nlm_file *file) 59 { 60 return; 61 } 62 #endif 63 64 static inline unsigned int file_hash(struct nfs_fh *f) 65 { 66 unsigned int tmp=0; 67 int i; 68 for (i=0; i<NFS2_FHSIZE;i++) 69 tmp += f->data[i]; 70 return tmp & (FILE_NRHASH - 1); 71 } 72 73 /* 74 * Lookup file info. If it doesn't exist, create a file info struct 75 * and open a (VFS) file for the given inode. 76 * 77 * FIXME: 78 * Note that we open the file O_RDONLY even when creating write locks. 79 * This is not quite right, but for now, we assume the client performs 80 * the proper R/W checking. 81 */ 82 u32 83 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, 84 struct nfs_fh *f) 85 { 86 struct nlm_file *file; 87 unsigned int hash; 88 u32 nfserr; 89 90 nlm_debug_print_fh("nlm_file_lookup", f); 91 92 hash = file_hash(f); 93 94 /* Lock file table */ 95 mutex_lock(&nlm_file_mutex); 96 97 for (file = nlm_files[hash]; file; file = file->f_next) 98 if (!nfs_compare_fh(&file->f_handle, f)) 99 goto found; 100 101 nlm_debug_print_fh("creating file for", f); 102 103 nfserr = nlm_lck_denied_nolocks; 104 file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL); 105 if (!file) 106 goto out_unlock; 107 108 memset(file, 0, sizeof(*file)); 109 memcpy(&file->f_handle, f, sizeof(struct nfs_fh)); 110 file->f_hash = hash; 111 init_MUTEX(&file->f_sema); 112 113 /* Open the file. Note that this must not sleep for too long, else 114 * we would lock up lockd:-) So no NFS re-exports, folks. 115 * 116 * We have to make sure we have the right credential to open 117 * the file. 118 */ 119 if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) { 120 dprintk("lockd: open failed (nfserr %d)\n", ntohl(nfserr)); 121 goto out_free; 122 } 123 124 file->f_next = nlm_files[hash]; 125 nlm_files[hash] = file; 126 127 found: 128 dprintk("lockd: found file %p (count %d)\n", file, file->f_count); 129 *result = file; 130 file->f_count++; 131 nfserr = 0; 132 133 out_unlock: 134 mutex_unlock(&nlm_file_mutex); 135 return nfserr; 136 137 out_free: 138 kfree(file); 139 #ifdef CONFIG_LOCKD_V4 140 if (nfserr == 1) 141 nfserr = nlm4_stale_fh; 142 else 143 #endif 144 nfserr = nlm_lck_denied; 145 goto out_unlock; 146 } 147 148 /* 149 * Delete a file after having released all locks, blocks and shares 150 */ 151 static inline void 152 nlm_delete_file(struct nlm_file *file) 153 { 154 struct nlm_file **fp, *f; 155 156 nlm_debug_print_file("closing file", file); 157 158 fp = nlm_files + file->f_hash; 159 while ((f = *fp) != NULL) { 160 if (f == file) { 161 *fp = file->f_next; 162 nlmsvc_ops->fclose(file->f_file); 163 kfree(file); 164 return; 165 } 166 fp = &f->f_next; 167 } 168 169 printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); 170 } 171 172 /* 173 * Loop over all locks on the given file and perform the specified 174 * action. 175 */ 176 static int 177 nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action) 178 { 179 struct inode *inode = nlmsvc_file_inode(file); 180 struct file_lock *fl; 181 struct nlm_host *lockhost; 182 183 again: 184 file->f_locks = 0; 185 for (fl = inode->i_flock; fl; fl = fl->fl_next) { 186 if (fl->fl_lmops != &nlmsvc_lock_operations) 187 continue; 188 189 /* update current lock count */ 190 file->f_locks++; 191 lockhost = (struct nlm_host *) fl->fl_owner; 192 if (action == NLM_ACT_MARK) 193 lockhost->h_inuse = 1; 194 else if (action == NLM_ACT_CHECK) 195 return 1; 196 else if (action == NLM_ACT_UNLOCK) { 197 struct file_lock lock = *fl; 198 199 if (host && lockhost != host) 200 continue; 201 202 lock.fl_type = F_UNLCK; 203 lock.fl_start = 0; 204 lock.fl_end = OFFSET_MAX; 205 if (posix_lock_file(file->f_file, &lock) < 0) { 206 printk("lockd: unlock failure in %s:%d\n", 207 __FILE__, __LINE__); 208 return 1; 209 } 210 goto again; 211 } 212 } 213 214 return 0; 215 } 216 217 /* 218 * Operate on a single file 219 */ 220 static inline int 221 nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action) 222 { 223 if (action == NLM_ACT_CHECK) { 224 /* Fast path for mark and sweep garbage collection */ 225 if (file->f_count || file->f_blocks || file->f_shares) 226 return 1; 227 } else { 228 nlmsvc_traverse_blocks(host, file, action); 229 nlmsvc_traverse_shares(host, file, action); 230 } 231 return nlm_traverse_locks(host, file, action); 232 } 233 234 /* 235 * Loop over all files in the file table. 236 */ 237 static int 238 nlm_traverse_files(struct nlm_host *host, int action) 239 { 240 struct nlm_file *file, **fp; 241 int i; 242 243 mutex_lock(&nlm_file_mutex); 244 for (i = 0; i < FILE_NRHASH; i++) { 245 fp = nlm_files + i; 246 while ((file = *fp) != NULL) { 247 /* Traverse locks, blocks and shares of this file 248 * and update file->f_locks count */ 249 if (nlm_inspect_file(host, file, action)) { 250 mutex_unlock(&nlm_file_mutex); 251 return 1; 252 } 253 254 /* No more references to this file. Let go of it. */ 255 if (!file->f_blocks && !file->f_locks 256 && !file->f_shares && !file->f_count) { 257 *fp = file->f_next; 258 nlmsvc_ops->fclose(file->f_file); 259 kfree(file); 260 } else { 261 fp = &file->f_next; 262 } 263 } 264 } 265 mutex_unlock(&nlm_file_mutex); 266 return 0; 267 } 268 269 /* 270 * Release file. If there are no more remote locks on this file, 271 * close it and free the handle. 272 * 273 * Note that we can't do proper reference counting without major 274 * contortions because the code in fs/locks.c creates, deletes and 275 * splits locks without notification. Our only way is to walk the 276 * entire lock list each time we remove a lock. 277 */ 278 void 279 nlm_release_file(struct nlm_file *file) 280 { 281 dprintk("lockd: nlm_release_file(%p, ct = %d)\n", 282 file, file->f_count); 283 284 /* Lock file table */ 285 mutex_lock(&nlm_file_mutex); 286 287 /* If there are no more locks etc, delete the file */ 288 if(--file->f_count == 0) { 289 if(!nlm_inspect_file(NULL, file, NLM_ACT_CHECK)) 290 nlm_delete_file(file); 291 } 292 293 mutex_unlock(&nlm_file_mutex); 294 } 295 296 /* 297 * Mark all hosts that still hold resources 298 */ 299 void 300 nlmsvc_mark_resources(void) 301 { 302 dprintk("lockd: nlmsvc_mark_resources\n"); 303 304 nlm_traverse_files(NULL, NLM_ACT_MARK); 305 } 306 307 /* 308 * Release all resources held by the given client 309 */ 310 void 311 nlmsvc_free_host_resources(struct nlm_host *host) 312 { 313 dprintk("lockd: nlmsvc_free_host_resources\n"); 314 315 if (nlm_traverse_files(host, NLM_ACT_UNLOCK)) 316 printk(KERN_WARNING 317 "lockd: couldn't remove all locks held by %s", 318 host->h_name); 319 } 320 321 /* 322 * delete all hosts structs for clients 323 */ 324 void 325 nlmsvc_invalidate_all(void) 326 { 327 struct nlm_host *host; 328 while ((host = nlm_find_client()) != NULL) { 329 nlmsvc_free_host_resources(host); 330 host->h_expires = 0; 331 host->h_killed = 1; 332 nlm_release_host(host); 333 } 334 } 335