Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
svcsubs.c
Go to the documentation of this file.
1 /*
2  * linux/fs/lockd/svcsubs.c
3  *
4  * Various support routines for the NLM server.
5  *
6  * Copyright (C) 1996, Olaf Kirch <[email protected]>
7  */
8 
9 #include <linux/types.h>
10 #include <linux/string.h>
11 #include <linux/time.h>
12 #include <linux/in.h>
13 #include <linux/slab.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/module.h>
22 #include <linux/mount.h>
23 
24 #define NLMDBG_FACILITY NLMDBG_SVCSUBS
25 
26 
27 /*
28  * Global file hash table
29  */
30 #define FILE_HASH_BITS 7
31 #define FILE_NRHASH (1<<FILE_HASH_BITS)
32 static struct hlist_head nlm_files[FILE_NRHASH];
33 static DEFINE_MUTEX(nlm_file_mutex);
34 
35 #ifdef NFSD_DEBUG
36 static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
37 {
38  u32 *fhp = (u32*)f->data;
39 
40  /* print the first 32 bytes of the fh */
41  dprintk("lockd: %s (%08x %08x %08x %08x %08x %08x %08x %08x)\n",
42  msg, fhp[0], fhp[1], fhp[2], fhp[3],
43  fhp[4], fhp[5], fhp[6], fhp[7]);
44 }
45 
46 static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
47 {
48  struct inode *inode = file->f_file->f_path.dentry->d_inode;
49 
50  dprintk("lockd: %s %s/%ld\n",
51  msg, inode->i_sb->s_id, inode->i_ino);
52 }
53 #else
54 static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
55 {
56  return;
57 }
58 
59 static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
60 {
61  return;
62 }
63 #endif
64 
65 static inline unsigned int file_hash(struct nfs_fh *f)
66 {
67  unsigned int tmp=0;
68  int i;
69  for (i=0; i<NFS2_FHSIZE;i++)
70  tmp += f->data[i];
71  return tmp & (FILE_NRHASH - 1);
72 }
73 
74 /*
75  * Lookup file info. If it doesn't exist, create a file info struct
76  * and open a (VFS) file for the given inode.
77  *
78  * FIXME:
79  * Note that we open the file O_RDONLY even when creating write locks.
80  * This is not quite right, but for now, we assume the client performs
81  * the proper R/W checking.
82  */
83 __be32
84 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
85  struct nfs_fh *f)
86 {
87  struct hlist_node *pos;
88  struct nlm_file *file;
89  unsigned int hash;
90  __be32 nfserr;
91 
92  nlm_debug_print_fh("nlm_lookup_file", f);
93 
94  hash = file_hash(f);
95 
96  /* Lock file table */
97  mutex_lock(&nlm_file_mutex);
98 
99  hlist_for_each_entry(file, pos, &nlm_files[hash], f_list)
100  if (!nfs_compare_fh(&file->f_handle, f))
101  goto found;
102 
103  nlm_debug_print_fh("creating file for", f);
104 
105  nfserr = nlm_lck_denied_nolocks;
106  file = kzalloc(sizeof(*file), GFP_KERNEL);
107  if (!file)
108  goto out_unlock;
109 
110  memcpy(&file->f_handle, f, sizeof(struct nfs_fh));
111  mutex_init(&file->f_mutex);
112  INIT_HLIST_NODE(&file->f_list);
113  INIT_LIST_HEAD(&file->f_blocks);
114 
115  /* Open the file. Note that this must not sleep for too long, else
116  * we would lock up lockd:-) So no NFS re-exports, folks.
117  *
118  * We have to make sure we have the right credential to open
119  * the file.
120  */
121  if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) {
122  dprintk("lockd: open failed (error %d)\n", nfserr);
123  goto out_free;
124  }
125 
126  hlist_add_head(&file->f_list, &nlm_files[hash]);
127 
128 found:
129  dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
130  *result = file;
131  file->f_count++;
132  nfserr = 0;
133 
134 out_unlock:
135  mutex_unlock(&nlm_file_mutex);
136  return nfserr;
137 
138 out_free:
139  kfree(file);
140  goto out_unlock;
141 }
142 
143 /*
144  * Delete a file after having released all locks, blocks and shares
145  */
146 static inline void
147 nlm_delete_file(struct nlm_file *file)
148 {
149  nlm_debug_print_file("closing file", file);
150  if (!hlist_unhashed(&file->f_list)) {
151  hlist_del(&file->f_list);
152  nlmsvc_ops->fclose(file->f_file);
153  kfree(file);
154  } else {
155  printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
156  }
157 }
158 
159 /*
160  * Loop over all locks on the given file and perform the specified
161  * action.
162  */
163 static int
164 nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
165  nlm_host_match_fn_t match)
166 {
167  struct inode *inode = nlmsvc_file_inode(file);
168  struct file_lock *fl;
169  struct nlm_host *lockhost;
170 
171 again:
172  file->f_locks = 0;
173  lock_flocks(); /* protects i_flock list */
174  for (fl = inode->i_flock; fl; fl = fl->fl_next) {
175  if (fl->fl_lmops != &nlmsvc_lock_operations)
176  continue;
177 
178  /* update current lock count */
179  file->f_locks++;
180 
181  lockhost = (struct nlm_host *) fl->fl_owner;
182  if (match(lockhost, host)) {
183  struct file_lock lock = *fl;
184 
185  unlock_flocks();
186  lock.fl_type = F_UNLCK;
187  lock.fl_start = 0;
188  lock.fl_end = OFFSET_MAX;
189  if (vfs_lock_file(file->f_file, F_SETLK, &lock, NULL) < 0) {
190  printk("lockd: unlock failure in %s:%d\n",
191  __FILE__, __LINE__);
192  return 1;
193  }
194  goto again;
195  }
196  }
197  unlock_flocks();
198 
199  return 0;
200 }
201 
202 static int
203 nlmsvc_always_match(void *dummy1, struct nlm_host *dummy2)
204 {
205  return 1;
206 }
207 
208 /*
209  * Inspect a single file
210  */
211 static inline int
212 nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match)
213 {
214  nlmsvc_traverse_blocks(host, file, match);
215  nlmsvc_traverse_shares(host, file, match);
216  return nlm_traverse_locks(host, file, match);
217 }
218 
219 /*
220  * Quick check whether there are still any locks, blocks or
221  * shares on a given file.
222  */
223 static inline int
224 nlm_file_inuse(struct nlm_file *file)
225 {
226  struct inode *inode = nlmsvc_file_inode(file);
227  struct file_lock *fl;
228 
229  if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
230  return 1;
231 
232  lock_flocks();
233  for (fl = inode->i_flock; fl; fl = fl->fl_next) {
234  if (fl->fl_lmops == &nlmsvc_lock_operations) {
235  unlock_flocks();
236  return 1;
237  }
238  }
239  unlock_flocks();
240  file->f_locks = 0;
241  return 0;
242 }
243 
244 /*
245  * Loop over all files in the file table.
246  */
247 static int
248 nlm_traverse_files(void *data, nlm_host_match_fn_t match,
249  int (*is_failover_file)(void *data, struct nlm_file *file))
250 {
251  struct hlist_node *pos, *next;
252  struct nlm_file *file;
253  int i, ret = 0;
254 
255  mutex_lock(&nlm_file_mutex);
256  for (i = 0; i < FILE_NRHASH; i++) {
257  hlist_for_each_entry_safe(file, pos, next, &nlm_files[i], f_list) {
258  if (is_failover_file && !is_failover_file(data, file))
259  continue;
260  file->f_count++;
261  mutex_unlock(&nlm_file_mutex);
262 
263  /* Traverse locks, blocks and shares of this file
264  * and update file->f_locks count */
265  if (nlm_inspect_file(data, file, match))
266  ret = 1;
267 
268  mutex_lock(&nlm_file_mutex);
269  file->f_count--;
270  /* No more references to this file. Let go of it. */
271  if (list_empty(&file->f_blocks) && !file->f_locks
272  && !file->f_shares && !file->f_count) {
273  hlist_del(&file->f_list);
274  nlmsvc_ops->fclose(file->f_file);
275  kfree(file);
276  }
277  }
278  }
279  mutex_unlock(&nlm_file_mutex);
280  return ret;
281 }
282 
283 /*
284  * Release file. If there are no more remote locks on this file,
285  * close it and free the handle.
286  *
287  * Note that we can't do proper reference counting without major
288  * contortions because the code in fs/locks.c creates, deletes and
289  * splits locks without notification. Our only way is to walk the
290  * entire lock list each time we remove a lock.
291  */
292 void
293 nlm_release_file(struct nlm_file *file)
294 {
295  dprintk("lockd: nlm_release_file(%p, ct = %d)\n",
296  file, file->f_count);
297 
298  /* Lock file table */
299  mutex_lock(&nlm_file_mutex);
300 
301  /* If there are no more locks etc, delete the file */
302  if (--file->f_count == 0 && !nlm_file_inuse(file))
303  nlm_delete_file(file);
304 
305  mutex_unlock(&nlm_file_mutex);
306 }
307 
308 /*
309  * Helpers function for resource traversal
310  *
311  * nlmsvc_mark_host:
312  * used by the garbage collector; simply sets h_inuse only for those
313  * hosts, which passed network check.
314  * Always returns 0.
315  *
316  * nlmsvc_same_host:
317  * returns 1 iff the two hosts match. Used to release
318  * all resources bound to a specific host.
319  *
320  * nlmsvc_is_client:
321  * returns 1 iff the host is a client.
322  * Used by nlmsvc_invalidate_all
323  */
324 
325 static int
326 nlmsvc_mark_host(void *data, struct nlm_host *hint)
327 {
328  struct nlm_host *host = data;
329 
330  if ((hint->net == NULL) ||
331  (host->net == hint->net))
332  host->h_inuse = 1;
333  return 0;
334 }
335 
336 static int
337 nlmsvc_same_host(void *data, struct nlm_host *other)
338 {
339  struct nlm_host *host = data;
340 
341  return host == other;
342 }
343 
344 static int
345 nlmsvc_is_client(void *data, struct nlm_host *dummy)
346 {
347  struct nlm_host *host = data;
348 
349  if (host->h_server) {
350  /* we are destroying locks even though the client
351  * hasn't asked us too, so don't unmonitor the
352  * client
353  */
354  if (host->h_nsmhandle)
355  host->h_nsmhandle->sm_sticky = 1;
356  return 1;
357  } else
358  return 0;
359 }
360 
361 /*
362  * Mark all hosts that still hold resources
363  */
364 void
366 {
367  struct nlm_host hint;
368 
369  dprintk("lockd: nlmsvc_mark_resources for net %p\n", net);
370  hint.net = net;
371  nlm_traverse_files(&hint, nlmsvc_mark_host, NULL);
372 }
373 
374 /*
375  * Release all resources held by the given client
376  */
377 void
378 nlmsvc_free_host_resources(struct nlm_host *host)
379 {
380  dprintk("lockd: nlmsvc_free_host_resources\n");
381 
382  if (nlm_traverse_files(host, nlmsvc_same_host, NULL)) {
384  "lockd: couldn't remove all locks held by %s\n",
385  host->h_name);
386  BUG();
387  }
388 }
389 
396 void
398 {
399  /*
400  * Previously, the code would call
401  * nlmsvc_free_host_resources for each client in
402  * turn, which is about as inefficient as it gets.
403  * Now we just do it once in nlm_traverse_files.
404  */
405  nlm_traverse_files(NULL, nlmsvc_is_client, NULL);
406 }
407 
408 static int
409 nlmsvc_match_sb(void *datap, struct nlm_file *file)
410 {
411  struct super_block *sb = datap;
412 
413  return sb == file->f_file->f_path.dentry->d_sb;
414 }
415 
422 int
424 {
425  int ret;
426 
427  ret = nlm_traverse_files(sb, nlmsvc_always_match, nlmsvc_match_sb);
428  return ret ? -EIO : 0;
429 }
431 
432 static int
433 nlmsvc_match_ip(void *datap, struct nlm_host *host)
434 {
435  return rpc_cmp_addr(nlm_srcaddr(host), datap);
436 }
437 
445 int
446 nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr)
447 {
448  int ret;
449 
450  ret = nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL);
451  return ret ? -EIO : 0;
452 }