Blame view

fs/lockd/svclock.c 25.6 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /*
   * linux/fs/lockd/svclock.c
   *
   * Handling of server-side locks, mostly of the blocked variety.
   * This is the ugliest part of lockd because we tread on very thin ice.
   * GRANT and CANCEL calls may get stuck, meet in mid-flight, etc.
   * IMNSHO introducing the grant callback into the NLM protocol was one
   * of the worst ideas Sun ever had. Except maybe for the idea of doing
   * NFS file locking at all.
   *
   * I'm trying hard to avoid race conditions by protecting most accesses
   * to a file's list of blocked locks through a semaphore. The global
   * list of blocked locks is not protected in this fashion however.
   * Therefore, some functions (such as the RPC callback for the async grant
   * call) move blocked locks towards the head of the list *while some other
   * process might be traversing it*. This should not be a problem in
   * practice, because this will only cause functions traversing the list
   * to visit some blocks twice.
   *
   * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  #include <linux/types.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
23
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
  #include <linux/errno.h>
  #include <linux/kernel.h>
  #include <linux/sched.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
  #include <linux/sunrpc/clnt.h>
  #include <linux/sunrpc/svc.h>
  #include <linux/lockd/nlm.h>
  #include <linux/lockd/lockd.h>
d751a7cd0   Jeff Layton   NLM: Convert lock...
31
  #include <linux/kthread.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
34
35
36
37
38
39
  
  #define NLMDBG_FACILITY		NLMDBG_SVCLOCK
  
  #ifdef CONFIG_LOCKD_V4
  #define nlm_deadlock	nlm4_deadlock
  #else
  #define nlm_deadlock	nlm_lck_denied
  #endif
6849c0cab   Trond Myklebust   lockd: Add refcou...
40
  static void nlmsvc_release_block(struct nlm_block *block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
  static void	nlmsvc_insert_block(struct nlm_block *block, unsigned long);
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
42
  static void	nlmsvc_remove_block(struct nlm_block *block);
963d8fe53   Trond Myklebust   RPC: Clean up RPC...
43

5e1abf8cb   Trond Myklebust   lockd: Clean up o...
44
45
  static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock);
  static void nlmsvc_freegrantargs(struct nlm_rqst *call);
963d8fe53   Trond Myklebust   RPC: Clean up RPC...
46
  static const struct rpc_call_ops nlmsvc_grant_ops;
d8367c504   Chuck Lever   lockd: Move nlmdb...
47
  static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
51
  
  /*
   * The list of blocked locks to retry
   */
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
52
  static LIST_HEAD(nlm_blocked);
f904be9cc   Bryan Schumaker   lockd: Mostly rem...
53
  static DEFINE_SPINLOCK(nlm_blocked_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
56
57
58
  
  /*
   * Insert a blocked lock into the global list
   */
  static void
f904be9cc   Bryan Schumaker   lockd: Mostly rem...
59
  nlmsvc_insert_block_locked(struct nlm_block *block, unsigned long when)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
  {
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
61
62
  	struct nlm_block *b;
  	struct list_head *pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
  
  	dprintk("lockd: nlmsvc_insert_block(%p, %ld)
  ", block, when);
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
66
67
68
69
70
71
72
  	if (list_empty(&block->b_list)) {
  		kref_get(&block->b_count);
  	} else {
  		list_del_init(&block->b_list);
  	}
  
  	pos = &nlm_blocked;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
  	if (when != NLM_NEVER) {
  		if ((when += jiffies) == NLM_NEVER)
  			when ++;
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
76
77
78
79
80
81
82
83
84
  		list_for_each(pos, &nlm_blocked) {
  			b = list_entry(pos, struct nlm_block, b_list);
  			if (time_after(b->b_when,when) || b->b_when == NLM_NEVER)
  				break;
  		}
  		/* On normal exit from the loop, pos == &nlm_blocked,
  		 * so we will be adding to the end of the list - good
  		 */
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85

68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
86
  	list_add_tail(&block->b_list, pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  	block->b_when = when;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  }
f904be9cc   Bryan Schumaker   lockd: Mostly rem...
89
90
91
92
93
94
  static void nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
  {
  	spin_lock(&nlm_blocked_lock);
  	nlmsvc_insert_block_locked(block, when);
  	spin_unlock(&nlm_blocked_lock);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
97
  /*
   * Remove a block from the global list
   */
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
98
  static inline void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
  nlmsvc_remove_block(struct nlm_block *block)
  {
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
101
  	if (!list_empty(&block->b_list)) {
f904be9cc   Bryan Schumaker   lockd: Mostly rem...
102
  		spin_lock(&nlm_blocked_lock);
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
103
  		list_del_init(&block->b_list);
f904be9cc   Bryan Schumaker   lockd: Mostly rem...
104
  		spin_unlock(&nlm_blocked_lock);
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
105
  		nlmsvc_release_block(block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
  }
  
  /*
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
110
   * Find a block for a given lock
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
   */
  static struct nlm_block *
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
113
  nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  {
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
115
  	struct nlm_block	*block;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
121
122
  	struct file_lock	*fl;
  
  	dprintk("lockd: nlmsvc_lookup_block f=%p pd=%d %Ld-%Ld ty=%d
  ",
  				file, lock->fl.fl_pid,
  				(long long)lock->fl.fl_start,
  				(long long)lock->fl.fl_end, lock->fl.fl_type);
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
123
  	list_for_each_entry(block, &nlm_blocked, b_list) {
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
124
  		fl = &block->b_call->a_args.lock.fl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
129
  		dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s
  ",
  				block->b_file, fl->fl_pid,
  				(long long)fl->fl_start,
  				(long long)fl->fl_end, fl->fl_type,
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
130
  				nlmdbg_cookie2a(&block->b_call->a_args.cookie));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  		if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
6849c0cab   Trond Myklebust   lockd: Add refcou...
132
  			kref_get(&block->b_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
136
137
138
139
140
141
  			return block;
  		}
  	}
  
  	return NULL;
  }
  
  static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b)
  {
6d7bbbbac   J. Bruce Fields   lockd: minor svcl...
142
  	if (a->len != b->len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
  		return 0;
6d7bbbbac   J. Bruce Fields   lockd: minor svcl...
144
  	if (memcmp(a->data, b->data, a->len))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
147
148
149
150
151
152
  		return 0;
  	return 1;
  }
  
  /*
   * Find a block with a given NLM cookie.
   */
  static inline struct nlm_block *
39be4502c   Olaf Kirch   [PATCH] knfsd: ma...
153
  nlmsvc_find_block(struct nlm_cookie *cookie)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
155
  {
  	struct nlm_block *block;
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
156
  	list_for_each_entry(block, &nlm_blocked, b_list) {
39be4502c   Olaf Kirch   [PATCH] knfsd: ma...
157
  		if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie))
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
158
  			goto found;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
  	}
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
160
161
162
  	return NULL;
  
  found:
39be4502c   Olaf Kirch   [PATCH] knfsd: ma...
163
164
  	dprintk("nlmsvc_find_block(%s): block=%p
  ", nlmdbg_cookie2a(cookie), block);
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
165
  	kref_get(&block->b_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
169
170
171
172
173
174
175
176
177
  	return block;
  }
  
  /*
   * Create a block and initialize it.
   *
   * Note: we explicitly set the cookie of the grant reply to that of
   * the blocked lock request. The spec explicitly mentions that the client
   * should _not_ rely on the callback containing the same cookie as the
   * request, but (as I found out later) that's because some implementations
   * do just this. Never mind the standards comittees, they support our
   * logging industries.
39be4502c   Olaf Kirch   [PATCH] knfsd: ma...
178
179
180
181
182
   *
   * 10 years later: I hope we can safely ignore these old and broken
   * clients by now. Let's fix this so we can uniquely identify an incoming
   * GRANTED_RES message by cookie, without having to rely on the client's IP
   * address. --okir
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
   */
255129d1e   Trond Myklebust   NLM: Fix a circul...
184
185
186
187
  static struct nlm_block *
  nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
  		    struct nlm_file *file, struct nlm_lock *lock,
  		    struct nlm_cookie *cookie)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
  {
  	struct nlm_block	*block;
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
190
  	struct nlm_rqst		*call = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191

560de0e65   J. Bruce Fields   lockd: get host r...
192
  	nlm_get_host(host);
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
193
194
195
  	call = nlm_alloc_call(host);
  	if (call == NULL)
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  	/* Allocate memory for block, and initialize arguments */
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
197
198
  	block = kzalloc(sizeof(*block), GFP_KERNEL);
  	if (block == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
  		goto failed;
6849c0cab   Trond Myklebust   lockd: Add refcou...
200
  	kref_init(&block->b_count);
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
201
202
  	INIT_LIST_HEAD(&block->b_list);
  	INIT_LIST_HEAD(&block->b_flist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203

92737230d   Trond Myklebust   NLM: Add nlmclnt_...
204
  	if (!nlmsvc_setgrantargs(call, lock))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
  		goto failed_free;
  
  	/* Set notifier function for VFS, and init args */
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
208
209
  	call->a_args.lock.fl.fl_flags |= FL_SLEEP;
  	call->a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations;
39be4502c   Olaf Kirch   [PATCH] knfsd: ma...
210
  	nlmclnt_next_cookie(&call->a_args.cookie);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
218
  
  	dprintk("lockd: created block %p...
  ", block);
  
  	/* Create and initialize the block */
  	block->b_daemon = rqstp->rq_server;
  	block->b_host   = host;
  	block->b_file   = file;
5ea0d7503   Marc Eshel   lockd: handle tes...
219
  	block->b_fl = NULL;
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
220
  	file->f_count++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
  
  	/* Add to file's list of blocks */
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
223
  	list_add(&block->b_flist, &file->f_blocks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
  
  	/* Set up RPC arguments for callback */
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
226
  	block->b_call = call;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
  	call->a_flags   = RPC_TASK_ASYNC;
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
228
  	call->a_block = block;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
233
234
  
  	return block;
  
  failed_free:
  	kfree(block);
  failed:
7db836d4a   Chuck Lever   lockd: Split nlm_...
235
  	nlmsvc_release_call(call);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
239
  	return NULL;
  }
  
  /*
3c61eecb6   J. Bruce Fields   lockd: Fix stale ...
240
   * Delete a block.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
243
   * It is the caller's responsibility to check whether the file
   * can be closed hereafter.
   */
6849c0cab   Trond Myklebust   lockd: Add refcou...
244
  static int nlmsvc_unlink_block(struct nlm_block *block)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
  {
09c7938c5   Trond Myklebust   lockd: Fix server...
246
  	int status;
6849c0cab   Trond Myklebust   lockd: Add refcou...
247
248
  	dprintk("lockd: unlinking block %p...
  ", block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
  
  	/* Remove block from list */
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
251
  	status = posix_unblock_lock(block->b_file->f_file, &block->b_call->a_args.lock.fl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
  	nlmsvc_remove_block(block);
6849c0cab   Trond Myklebust   lockd: Add refcou...
253
254
  	return status;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255

6849c0cab   Trond Myklebust   lockd: Add refcou...
256
257
258
259
  static void nlmsvc_free_block(struct kref *kref)
  {
  	struct nlm_block *block = container_of(kref, struct nlm_block, b_count);
  	struct nlm_file		*file = block->b_file;
6849c0cab   Trond Myklebust   lockd: Add refcou...
260
261
262
  
  	dprintk("lockd: freeing block %p...
  ", block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
  
  	/* Remove block from file's list of blocks */
89e63ef60   Neil Brown   [PATCH] Convert l...
265
  	mutex_lock(&file->f_mutex);
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
266
  	list_del_init(&block->b_flist);
89e63ef60   Neil Brown   [PATCH] Convert l...
267
  	mutex_unlock(&file->f_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268

92737230d   Trond Myklebust   NLM: Add nlmclnt_...
269
  	nlmsvc_freegrantargs(block->b_call);
7db836d4a   Chuck Lever   lockd: Split nlm_...
270
  	nlmsvc_release_call(block->b_call);
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
271
  	nlm_release_file(block->b_file);
0e4ac9d93   Marc Eshel   lockd: handle fl_...
272
  	kfree(block->b_fl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  	kfree(block);
6849c0cab   Trond Myklebust   lockd: Add refcou...
274
275
276
277
278
279
  }
  
  static void nlmsvc_release_block(struct nlm_block *block)
  {
  	if (block != NULL)
  		kref_put(&block->b_count, nlmsvc_free_block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  }
f2af793db   Olaf Kirch   [PATCH] knfsd: lo...
281
282
283
284
285
286
287
  /*
   * Loop over all blocks and delete blocks held by
   * a matching host.
   */
  void nlmsvc_traverse_blocks(struct nlm_host *host,
  			struct nlm_file *file,
  			nlm_host_match_fn_t match)
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
288
  {
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
289
  	struct nlm_block *block, *next;
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
290
291
  
  restart:
89e63ef60   Neil Brown   [PATCH] Convert l...
292
  	mutex_lock(&file->f_mutex);
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
293
  	list_for_each_entry_safe(block, next, &file->f_blocks, b_flist) {
f2af793db   Olaf Kirch   [PATCH] knfsd: lo...
294
  		if (!match(block->b_host, host))
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
295
  			continue;
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
296
297
298
  		/* Do not destroy blocks that are not on
  		 * the global retry list - why? */
  		if (list_empty(&block->b_list))
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
299
300
  			continue;
  		kref_get(&block->b_count);
89e63ef60   Neil Brown   [PATCH] Convert l...
301
  		mutex_unlock(&file->f_mutex);
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
302
303
304
305
  		nlmsvc_unlink_block(block);
  		nlmsvc_release_block(block);
  		goto restart;
  	}
89e63ef60   Neil Brown   [PATCH] Convert l...
306
  	mutex_unlock(&file->f_mutex);
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
307
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
  /*
5e1abf8cb   Trond Myklebust   lockd: Clean up o...
309
310
311
312
313
314
315
   * Initialize arguments for GRANTED call. The nlm_rqst structure
   * has been cleared already.
   */
  static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
  {
  	locks_copy_lock(&call->a_args.lock.fl, &lock->fl);
  	memcpy(&call->a_args.lock.fh, &lock->fh, sizeof(call->a_args.lock.fh));
e9ff3990f   Serge E. Hallyn   [PATCH] namespace...
316
  	call->a_args.lock.caller = utsname()->nodename;
5e1abf8cb   Trond Myklebust   lockd: Clean up o...
317
318
319
320
321
322
323
324
  	call->a_args.lock.oh.len = lock->oh.len;
  
  	/* set default data area */
  	call->a_args.lock.oh.data = call->a_owner;
  	call->a_args.lock.svid = lock->fl.fl_pid;
  
  	if (lock->oh.len > NLMCLNT_OHSIZE) {
  		void *data = kmalloc(lock->oh.len, GFP_KERNEL);
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
325
  		if (!data)
5e1abf8cb   Trond Myklebust   lockd: Clean up o...
326
  			return 0;
5e1abf8cb   Trond Myklebust   lockd: Clean up o...
327
328
329
330
331
332
333
334
335
  		call->a_args.lock.oh.data = (u8 *) data;
  	}
  
  	memcpy(call->a_args.lock.oh.data, lock->oh.data, lock->oh.len);
  	return 1;
  }
  
  static void nlmsvc_freegrantargs(struct nlm_rqst *call)
  {
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
336
  	if (call->a_args.lock.oh.data != call->a_owner)
5e1abf8cb   Trond Myklebust   lockd: Clean up o...
337
  		kfree(call->a_args.lock.oh.data);
a9e61e25f   Felix Blyakher   lockd: call locks...
338
339
  
  	locks_release_private(&call->a_args.lock.fl);
5e1abf8cb   Trond Myklebust   lockd: Clean up o...
340
341
342
  }
  
  /*
2b36f412a   Marc Eshel   lockd: save lock ...
343
344
   * Deferred lock request handling for non-blocking lock
   */
ca5c8cde9   Al Viro   lockd and nfsd en...
345
  static __be32
2b36f412a   Marc Eshel   lockd: save lock ...
346
347
  nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block)
  {
ca5c8cde9   Al Viro   lockd and nfsd en...
348
  	__be32 status = nlm_lck_denied_nolocks;
2b36f412a   Marc Eshel   lockd: save lock ...
349
350
351
352
353
354
355
356
357
358
359
360
361
362
  
  	block->b_flags |= B_QUEUED;
  
  	nlmsvc_insert_block(block, NLM_TIMEOUT);
  
  	block->b_cache_req = &rqstp->rq_chandle;
  	if (rqstp->rq_chandle.defer) {
  		block->b_deferred_req =
  			rqstp->rq_chandle.defer(block->b_cache_req);
  		if (block->b_deferred_req != NULL)
  			status = nlm_drop_reply;
  	}
  	dprintk("lockd: nlmsvc_defer_lock_rqst block %p flags %d status %d
  ",
ca5c8cde9   Al Viro   lockd and nfsd en...
363
  		block, block->b_flags, ntohl(status));
2b36f412a   Marc Eshel   lockd: save lock ...
364
365
366
367
368
  
  	return status;
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
   * Attempt to establish a lock, and if it can't be granted, block it
   * if required.
   */
52921e02a   Al Viro   [PATCH] lockd end...
372
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
  nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
6cde4de80   Jeff Layton   lockd: eliminate ...
374
  	    struct nlm_host *host, struct nlm_lock *lock, int wait,
b2b502890   J. Bruce Fields   lockd: move grace...
375
  	    struct nlm_cookie *cookie, int reclaim)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
  {
f81204802   Marc Eshel   lockd: always pre...
377
  	struct nlm_block	*block = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
  	int			error;
52921e02a   Al Viro   [PATCH] lockd end...
379
  	__be32			ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
  
  	dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)
  ",
225a719f7   Josef Sipek   [PATCH] struct pa...
383
384
  				file->f_file->f_path.dentry->d_inode->i_sb->s_id,
  				file->f_file->f_path.dentry->d_inode->i_ino,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
387
388
  				lock->fl.fl_type, lock->fl.fl_pid,
  				(long long)lock->fl.fl_start,
  				(long long)lock->fl.fl_end,
  				wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
  	/* Lock file against concurrent access */
89e63ef60   Neil Brown   [PATCH] Convert l...
390
  	mutex_lock(&file->f_mutex);
f81204802   Marc Eshel   lockd: always pre...
391
392
393
  	/* Get existing block (in case client is busy-waiting)
  	 * or create new block
  	 */
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
394
  	block = nlmsvc_lookup_block(file, lock);
09c7938c5   Trond Myklebust   lockd: Fix server...
395
  	if (block == NULL) {
560de0e65   J. Bruce Fields   lockd: get host r...
396
  		block = nlmsvc_create_block(rqstp, host, file, lock, cookie);
f81204802   Marc Eshel   lockd: always pre...
397
398
399
  		ret = nlm_lck_denied_nolocks;
  		if (block == NULL)
  			goto out;
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
400
  		lock = &block->b_call->a_args.lock;
f81204802   Marc Eshel   lockd: always pre...
401
402
  	} else
  		lock->fl.fl_flags &= ~FL_SLEEP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403

1a8322b2b   Marc Eshel   lockd: add code t...
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
  	if (block->b_flags & B_QUEUED) {
  		dprintk("lockd: nlmsvc_lock deferred block %p flags %d
  ",
  							block, block->b_flags);
  		if (block->b_granted) {
  			nlmsvc_unlink_block(block);
  			ret = nlm_granted;
  			goto out;
  		}
  		if (block->b_flags & B_TIMED_OUT) {
  			nlmsvc_unlink_block(block);
  			ret = nlm_lck_denied;
  			goto out;
  		}
  		ret = nlm_drop_reply;
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421

b2b502890   J. Bruce Fields   lockd: move grace...
422
423
424
425
  	if (locks_in_grace() && !reclaim) {
  		ret = nlm_lck_denied_grace_period;
  		goto out;
  	}
d22b1cff0   J. Bruce Fields   lockd: reject rec...
426
427
428
429
  	if (reclaim && !locks_in_grace()) {
  		ret = nlm_lck_denied_grace_period;
  		goto out;
  	}
b2b502890   J. Bruce Fields   lockd: move grace...
430

1a8322b2b   Marc Eshel   lockd: add code t...
431
432
433
434
  	if (!wait)
  		lock->fl.fl_flags &= ~FL_SLEEP;
  	error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
  	lock->fl.fl_flags &= ~FL_SLEEP;
a85f193e2   Andy Adamson   lockd: make nlmsv...
435

1a8322b2b   Marc Eshel   lockd: add code t...
436
437
  	dprintk("lockd: vfs_lock_file returned %d
  ", error);
6d7bbbbac   J. Bruce Fields   lockd: minor svcl...
438
  	switch (error) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
  		case 0:
15dadef94   Andy Adamson   lockd: clean up n...
440
441
  			ret = nlm_granted;
  			goto out;
09c7938c5   Trond Myklebust   lockd: Fix server...
442
  		case -EAGAIN:
e33d1ea60   Miklos Szeredi   lockd: clean up b...
443
444
445
446
447
448
449
  			/*
  			 * If this is a blocking request for an
  			 * already pending lock request then we need
  			 * to put it back on lockd's block list
  			 */
  			if (wait)
  				break;
1a8322b2b   Marc Eshel   lockd: add code t...
450
  			ret = nlm_lck_denied;
e33d1ea60   Miklos Szeredi   lockd: clean up b...
451
  			goto out;
bde74e4bc   Miklos Szeredi   locks: add specia...
452
  		case FILE_LOCK_DEFERRED:
1a8322b2b   Marc Eshel   lockd: add code t...
453
454
455
456
457
458
  			if (wait)
  				break;
  			/* Filesystem lock operation is in progress
  			   Add it to the queue waiting for callback */
  			ret = nlmsvc_defer_lock_rqst(rqstp, block);
  			goto out;
09c7938c5   Trond Myklebust   lockd: Fix server...
459
  		case -EDEADLK:
15dadef94   Andy Adamson   lockd: clean up n...
460
461
  			ret = nlm_deadlock;
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
  		default:			/* includes ENOLCK */
15dadef94   Andy Adamson   lockd: clean up n...
463
464
  			ret = nlm_lck_denied_nolocks;
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
  	}
09c7938c5   Trond Myklebust   lockd: Fix server...
466
  	ret = nlm_lck_blocked;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
  
  	/* Append to list of blocked */
f81204802   Marc Eshel   lockd: always pre...
469
  	nlmsvc_insert_block(block, NLM_NEVER);
15dadef94   Andy Adamson   lockd: clean up n...
470
  out:
89e63ef60   Neil Brown   [PATCH] Convert l...
471
  	mutex_unlock(&file->f_mutex);
6849c0cab   Trond Myklebust   lockd: Add refcou...
472
  	nlmsvc_release_block(block);
15dadef94   Andy Adamson   lockd: clean up n...
473
474
475
  	dprintk("lockd: nlmsvc_lock returned %u
  ", ret);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
478
479
480
  }
  
  /*
   * Test for presence of a conflicting lock.
   */
52921e02a   Al Viro   [PATCH] lockd end...
481
  __be32
85f3f1b3f   Marc Eshel   lockd: pass cooki...
482
  nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
8f920d5e2   Jeff Layton   lockd: eliminate ...
483
484
  		struct nlm_host *host, struct nlm_lock *lock,
  		struct nlm_lock *conflock, struct nlm_cookie *cookie)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
  {
5ea0d7503   Marc Eshel   lockd: handle tes...
486
487
488
  	struct nlm_block 	*block = NULL;
  	int			error;
  	__be32			ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
  	dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)
  ",
225a719f7   Josef Sipek   [PATCH] struct pa...
491
492
  				file->f_file->f_path.dentry->d_inode->i_sb->s_id,
  				file->f_file->f_path.dentry->d_inode->i_ino,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
494
495
  				lock->fl.fl_type,
  				(long long)lock->fl.fl_start,
  				(long long)lock->fl.fl_end);
5ea0d7503   Marc Eshel   lockd: handle tes...
496
497
498
499
500
501
502
503
  	/* Get existing block (in case client is busy-waiting) */
  	block = nlmsvc_lookup_block(file, lock);
  
  	if (block == NULL) {
  		struct file_lock *conf = kzalloc(sizeof(*conf), GFP_KERNEL);
  
  		if (conf == NULL)
  			return nlm_granted;
255129d1e   Trond Myklebust   NLM: Fix a circul...
504
  		block = nlmsvc_create_block(rqstp, host, file, lock, cookie);
5ea0d7503   Marc Eshel   lockd: handle tes...
505
506
507
508
509
510
511
512
513
514
515
516
  		if (block == NULL) {
  			kfree(conf);
  			return nlm_granted;
  		}
  		block->b_fl = conf;
  	}
  	if (block->b_flags & B_QUEUED) {
  		dprintk("lockd: nlmsvc_testlock deferred block %p flags %d fl %p
  ",
  			block, block->b_flags, block->b_fl);
  		if (block->b_flags & B_TIMED_OUT) {
  			nlmsvc_unlink_block(block);
29dbf5461   Oleg Drokin   lockd: fix a leak...
517
518
  			ret = nlm_lck_denied;
  			goto out;
5ea0d7503   Marc Eshel   lockd: handle tes...
519
520
  		}
  		if (block->b_flags & B_GOT_CALLBACK) {
54ca95eb3   Oleg Drokin   Leak in nlmsvc_te...
521
  			nlmsvc_unlink_block(block);
5ea0d7503   Marc Eshel   lockd: handle tes...
522
523
524
525
  			if (block->b_fl != NULL
  					&& block->b_fl->fl_type != F_UNLCK) {
  				lock->fl = *block->b_fl;
  				goto conf_lock;
29dbf5461   Oleg Drokin   lockd: fix a leak...
526
  			} else {
29dbf5461   Oleg Drokin   lockd: fix a leak...
527
528
  				ret = nlm_granted;
  				goto out;
5ea0d7503   Marc Eshel   lockd: handle tes...
529
530
  			}
  		}
29dbf5461   Oleg Drokin   lockd: fix a leak...
531
532
  		ret = nlm_drop_reply;
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
  	}
b2b502890   J. Bruce Fields   lockd: move grace...
534
535
536
537
  	if (locks_in_grace()) {
  		ret = nlm_lck_denied_grace_period;
  		goto out;
  	}
5ea0d7503   Marc Eshel   lockd: handle tes...
538
  	error = vfs_test_lock(file->f_file, &lock->fl);
bde74e4bc   Miklos Szeredi   locks: add specia...
539
  	if (error == FILE_LOCK_DEFERRED) {
29dbf5461   Oleg Drokin   lockd: fix a leak...
540
541
542
  		ret = nlmsvc_defer_lock_rqst(rqstp, block);
  		goto out;
  	}
5ea0d7503   Marc Eshel   lockd: handle tes...
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
  	if (error) {
  		ret = nlm_lck_denied_nolocks;
  		goto out;
  	}
  	if (lock->fl.fl_type == F_UNLCK) {
  		ret = nlm_granted;
  		goto out;
  	}
  
  conf_lock:
  	dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)
  ",
  		lock->fl.fl_type, (long long)lock->fl.fl_start,
  		(long long)lock->fl.fl_end);
  	conflock->caller = "somehost";	/* FIXME */
  	conflock->len = strlen(conflock->caller);
  	conflock->oh.len = 0;		/* don't return OH info */
  	conflock->svid = lock->fl.fl_pid;
  	conflock->fl.fl_type = lock->fl.fl_type;
  	conflock->fl.fl_start = lock->fl.fl_start;
  	conflock->fl.fl_end = lock->fl.fl_end;
  	ret = nlm_lck_denied;
  out:
  	if (block)
  		nlmsvc_release_block(block);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
570
571
572
573
574
575
576
577
  }
  
  /*
   * Remove a lock.
   * This implies a CANCEL call: We send a GRANT_MSG, the client replies
   * with a GRANT_RES call which gets lost, and calls UNLOCK immediately
   * afterwards. In this case the block will still be there, and hence
   * must be removed.
   */
52921e02a   Al Viro   [PATCH] lockd end...
578
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579
580
581
582
583
584
  nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
  {
  	int	error;
  
  	dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)
  ",
225a719f7   Josef Sipek   [PATCH] struct pa...
585
586
  				file->f_file->f_path.dentry->d_inode->i_sb->s_id,
  				file->f_file->f_path.dentry->d_inode->i_ino,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
588
589
590
591
592
593
594
  				lock->fl.fl_pid,
  				(long long)lock->fl.fl_start,
  				(long long)lock->fl.fl_end);
  
  	/* First, cancel any lock that might be there */
  	nlmsvc_cancel_blocked(file, lock);
  
  	lock->fl.fl_type = F_UNLCK;
1a8322b2b   Marc Eshel   lockd: add code t...
595
  	error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
598
599
600
601
602
603
604
605
606
  
  	return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
  }
  
  /*
   * Cancel a previously blocked request.
   *
   * A cancel request always overrides any grant that may currently
   * be in progress.
   * The calling procedure must check whether the file can be closed.
   */
52921e02a   Al Viro   [PATCH] lockd end...
607
  __be32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
609
610
  nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
  {
  	struct nlm_block	*block;
64a318ee2   J. Bruce Fields   NLM: Further canc...
611
  	int status = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
614
  
  	dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)
  ",
225a719f7   Josef Sipek   [PATCH] struct pa...
615
616
  				file->f_file->f_path.dentry->d_inode->i_sb->s_id,
  				file->f_file->f_path.dentry->d_inode->i_ino,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
619
  				lock->fl.fl_pid,
  				(long long)lock->fl.fl_start,
  				(long long)lock->fl.fl_end);
b2b502890   J. Bruce Fields   lockd: move grace...
620
621
  	if (locks_in_grace())
  		return nlm_lck_denied_grace_period;
89e63ef60   Neil Brown   [PATCH] Convert l...
622
  	mutex_lock(&file->f_mutex);
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
623
  	block = nlmsvc_lookup_block(file, lock);
89e63ef60   Neil Brown   [PATCH] Convert l...
624
  	mutex_unlock(&file->f_mutex);
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
625
  	if (block != NULL) {
1a8322b2b   Marc Eshel   lockd: add code t...
626
627
  		vfs_cancel_lock(block->b_file->f_file,
  				&block->b_call->a_args.lock.fl);
6849c0cab   Trond Myklebust   lockd: Add refcou...
628
629
630
  		status = nlmsvc_unlink_block(block);
  		nlmsvc_release_block(block);
  	}
64a318ee2   J. Bruce Fields   NLM: Further canc...
631
  	return status ? nlm_lck_denied : nlm_granted;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
633
634
  }
  
  /*
0e4ac9d93   Marc Eshel   lockd: handle fl_...
635
   * This is a callback from the filesystem for VFS file lock requests.
8fb47a4fb   J. Bruce Fields   locks: rename loc...
636
   * It will be used if lm_grant is defined and the filesystem can not
0e4ac9d93   Marc Eshel   lockd: handle fl_...
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
   * respond to the request immediately.
   * For GETLK request it will copy the reply to the nlm_block.
   * For SETLK or SETLKW request it will get the local posix lock.
   * In all cases it will move the block to the head of nlm_blocked q where
   * nlmsvc_retry_blocked() can send back a reply for SETLKW or revisit the
   * deferred rpc for GETLK and SETLK.
   */
  static void
  nlmsvc_update_deferred_block(struct nlm_block *block, struct file_lock *conf,
  			     int result)
  {
  	block->b_flags |= B_GOT_CALLBACK;
  	if (result == 0)
  		block->b_granted = 1;
  	else
  		block->b_flags |= B_TIMED_OUT;
  	if (conf) {
0e4ac9d93   Marc Eshel   lockd: handle fl_...
654
  		if (block->b_fl)
1a747ee0c   J. Bruce Fields   locks: don't call...
655
  			__locks_copy_lock(block->b_fl, conf);
0e4ac9d93   Marc Eshel   lockd: handle fl_...
656
657
658
659
660
661
662
663
  	}
  }
  
  static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
  					int result)
  {
  	struct nlm_block *block;
  	int rc = -ENOENT;
f904be9cc   Bryan Schumaker   lockd: Mostly rem...
664
  	spin_lock(&nlm_blocked_lock);
0e4ac9d93   Marc Eshel   lockd: handle fl_...
665
666
667
668
669
670
671
672
673
674
675
676
677
  	list_for_each_entry(block, &nlm_blocked, b_list) {
  		if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
  			dprintk("lockd: nlmsvc_notify_blocked block %p flags %d
  ",
  							block, block->b_flags);
  			if (block->b_flags & B_QUEUED) {
  				if (block->b_flags & B_TIMED_OUT) {
  					rc = -ENOLCK;
  					break;
  				}
  				nlmsvc_update_deferred_block(block, conf, result);
  			} else if (result == 0)
  				block->b_granted = 1;
f904be9cc   Bryan Schumaker   lockd: Mostly rem...
678
  			nlmsvc_insert_block_locked(block, 0);
0e4ac9d93   Marc Eshel   lockd: handle fl_...
679
680
681
682
683
  			svc_wake_up(block->b_daemon);
  			rc = 0;
  			break;
  		}
  	}
f904be9cc   Bryan Schumaker   lockd: Mostly rem...
684
  	spin_unlock(&nlm_blocked_lock);
0e4ac9d93   Marc Eshel   lockd: handle fl_...
685
686
687
688
689
690
691
  	if (rc == -ENOENT)
  		printk(KERN_WARNING "lockd: grant for unknown block
  ");
  	return rc;
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
693
694
695
696
697
698
699
700
   * Unblock a blocked lock request. This is a callback invoked from the
   * VFS layer when a lock on which we blocked is removed.
   *
   * This function doesn't grant the blocked lock instantly, but rather moves
   * the block to the head of nlm_blocked where it can be picked up by lockd.
   */
  static void
  nlmsvc_notify_blocked(struct file_lock *fl)
  {
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
701
  	struct nlm_block	*block;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702
703
704
  
  	dprintk("lockd: VFS unblock notification for block %p
  ", fl);
a282a1fa6   J. Bruce Fields   lockd: fix nlmsvc...
705
  	spin_lock(&nlm_blocked_lock);
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
706
  	list_for_each_entry(block, &nlm_blocked, b_list) {
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
707
  		if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
a282a1fa6   J. Bruce Fields   lockd: fix nlmsvc...
708
709
  			nlmsvc_insert_block_locked(block, 0);
  			spin_unlock(&nlm_blocked_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
711
712
713
  			svc_wake_up(block->b_daemon);
  			return;
  		}
  	}
a282a1fa6   J. Bruce Fields   lockd: fix nlmsvc...
714
  	spin_unlock(&nlm_blocked_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
717
718
719
720
721
722
  	printk(KERN_WARNING "lockd: notification for unknown block!
  ");
  }
  
  static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2)
  {
  	return fl1->fl_owner == fl2->fl_owner && fl1->fl_pid == fl2->fl_pid;
  }
7b021967c   Alexey Dobriyan   const: make lock_...
723
  const struct lock_manager_operations nlmsvc_lock_operations = {
8fb47a4fb   J. Bruce Fields   locks: rename loc...
724
725
726
  	.lm_compare_owner = nlmsvc_same_owner,
  	.lm_notify = nlmsvc_notify_blocked,
  	.lm_grant = nlmsvc_grant_deferred,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
  };
  
  /*
   * Try to claim a lock that was previously blocked.
   *
   * Note that we use both the RPC_GRANTED_MSG call _and_ an async
   * RPC thread when notifying the client. This seems like overkill...
   * Here's why:
   *  -	we don't want to use a synchronous RPC thread, otherwise
   *	we might find ourselves hanging on a dead portmapper.
   *  -	Some lockd implementations (e.g. HP) don't react to
   *	RPC_GRANTED calls; they seem to insist on RPC_GRANTED_MSG calls.
   */
  static void
  nlmsvc_grant_blocked(struct nlm_block *block)
  {
  	struct nlm_file		*file = block->b_file;
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
744
  	struct nlm_lock		*lock = &block->b_call->a_args.lock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
746
747
748
  	int			error;
  
  	dprintk("lockd: grant blocked lock %p
  ", block);
0e4ac9d93   Marc Eshel   lockd: handle fl_...
749
  	kref_get(&block->b_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
  	/* Unlink block request from list */
6849c0cab   Trond Myklebust   lockd: Add refcou...
751
  	nlmsvc_unlink_block(block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
753
754
755
756
757
758
759
760
761
  
  	/* If b_granted is true this means we've been here before.
  	 * Just retry the grant callback, possibly refreshing the RPC
  	 * binding */
  	if (block->b_granted) {
  		nlm_rebind_host(block->b_host);
  		goto callback;
  	}
  
  	/* Try the lock operation again */
09c7938c5   Trond Myklebust   lockd: Fix server...
762
  	lock->fl.fl_flags |= FL_SLEEP;
1a8322b2b   Marc Eshel   lockd: add code t...
763
  	error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
09c7938c5   Trond Myklebust   lockd: Fix server...
764
  	lock->fl.fl_flags &= ~FL_SLEEP;
5de0e5024   Andy Adamson   lockd: simplify n...
765
766
767
  	switch (error) {
  	case 0:
  		break;
bde74e4bc   Miklos Szeredi   locks: add specia...
768
  	case FILE_LOCK_DEFERRED:
1a8322b2b   Marc Eshel   lockd: add code t...
769
770
  		dprintk("lockd: lock still blocked error %d
  ", error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
771
  		nlmsvc_insert_block(block, NLM_NEVER);
0e4ac9d93   Marc Eshel   lockd: handle fl_...
772
  		nlmsvc_release_block(block);
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
773
  		return;
5de0e5024   Andy Adamson   lockd: simplify n...
774
  	default:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
775
776
  		printk(KERN_WARNING "lockd: unexpected error %d in %s!
  ",
8e24eea72   Harvey Harrison   fs: replace remai...
777
  				-error, __func__);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
  		nlmsvc_insert_block(block, 10 * HZ);
0e4ac9d93   Marc Eshel   lockd: handle fl_...
779
  		nlmsvc_release_block(block);
d9f6eb75d   Trond Myklebust   lockd: blocks sho...
780
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
782
783
784
785
786
787
  	}
  
  callback:
  	/* Lock was granted by VFS. */
  	dprintk("lockd: GRANTing blocked lock.
  ");
  	block->b_granted = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788

9706501e4   Jeff Layton   NLM: don't reatte...
789
790
791
792
  	/* keep block on the list, but don't reattempt until the RPC
  	 * completes or the submission fails
  	 */
  	nlmsvc_insert_block(block, NLM_NEVER);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793

9706501e4   Jeff Layton   NLM: don't reatte...
794
795
796
797
798
799
800
801
802
  	/* Call the client -- use a soft RPC task since nlmsvc_retry_blocked
  	 * will queue up a new one if this one times out
  	 */
  	error = nlm_async_call(block->b_call, NLMPROC_GRANTED_MSG,
  				&nlmsvc_grant_ops);
  
  	/* RPC submission failed, wait a bit and retry */
  	if (error < 0)
  		nlmsvc_insert_block(block, 10 * HZ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
803
804
805
806
807
808
809
810
811
812
  }
  
  /*
   * This is the callback from the RPC layer when the NLM_GRANTED_MSG
   * RPC call has succeeded or timed out.
   * Like all RPC callbacks, it is invoked by the rpciod process, so it
   * better not sleep. Therefore, we put the blocked lock on the nlm_blocked
   * chain once more in order to have it removed by lockd itself (which can
   * then sleep on the file semaphore without disrupting e.g. the nfs client).
   */
963d8fe53   Trond Myklebust   RPC: Clean up RPC...
813
  static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814
  {
963d8fe53   Trond Myklebust   RPC: Clean up RPC...
815
  	struct nlm_rqst		*call = data;
92737230d   Trond Myklebust   NLM: Add nlmclnt_...
816
  	struct nlm_block	*block = call->a_block;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
  	unsigned long		timeout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
819
820
  
  	dprintk("lockd: GRANT_MSG RPC callback
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821

f904be9cc   Bryan Schumaker   lockd: Mostly rem...
822
  	spin_lock(&nlm_blocked_lock);
c64e80d55   Jeff Layton   NLM: don't requeu...
823
824
825
826
827
828
829
830
831
  	/* if the block is not on a list at this point then it has
  	 * been invalidated. Don't try to requeue it.
  	 *
  	 * FIXME: it's possible that the block is removed from the list
  	 * after this check but before the nlmsvc_insert_block. In that
  	 * case it will be added back. Perhaps we need better locking
  	 * for nlm_blocked?
  	 */
  	if (list_empty(&block->b_list))
a86dc496b   Trond Myklebust   SUNRPC: Remove th...
832
  		goto out;
c64e80d55   Jeff Layton   NLM: don't requeu...
833

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
835
836
837
838
839
  	/* Technically, we should down the file semaphore here. Since we
  	 * move the block towards the head of the queue only, no harm
  	 * can be done, though. */
  	if (task->tk_status < 0) {
  		/* RPC error: Re-insert for retransmission */
  		timeout = 10 * HZ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
840
841
842
843
  	} else {
  		/* Call was successful, now wait for client callback */
  		timeout = 60 * HZ;
  	}
f904be9cc   Bryan Schumaker   lockd: Mostly rem...
844
  	nlmsvc_insert_block_locked(block, timeout);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
  	svc_wake_up(block->b_daemon);
a86dc496b   Trond Myklebust   SUNRPC: Remove th...
846
  out:
f904be9cc   Bryan Schumaker   lockd: Mostly rem...
847
  	spin_unlock(&nlm_blocked_lock);
5e1abf8cb   Trond Myklebust   lockd: Clean up o...
848
  }
f904be9cc   Bryan Schumaker   lockd: Mostly rem...
849
850
851
852
  /*
   * FIXME: nlmsvc_release_block() grabs a mutex.  This is not allowed for an
   * .rpc_release rpc_call_op
   */
ec535ce15   Adrian Bunk   NFS: make 2 funct...
853
  static void nlmsvc_grant_release(void *data)
5e1abf8cb   Trond Myklebust   lockd: Clean up o...
854
  {
6041b7919   Trond Myklebust   lockd: Fix a typo...
855
  	struct nlm_rqst		*call = data;
6041b7919   Trond Myklebust   lockd: Fix a typo...
856
  	nlmsvc_release_block(call->a_block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
857
  }
963d8fe53   Trond Myklebust   RPC: Clean up RPC...
858
859
  static const struct rpc_call_ops nlmsvc_grant_ops = {
  	.rpc_call_done = nlmsvc_grant_callback,
5e1abf8cb   Trond Myklebust   lockd: Clean up o...
860
  	.rpc_release = nlmsvc_grant_release,
963d8fe53   Trond Myklebust   RPC: Clean up RPC...
861
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
864
865
866
  /*
   * We received a GRANT_RES callback. Try to find the corresponding
   * block.
   */
  void
e8c5c045d   Al Viro   [PATCH] lockd end...
867
  nlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
869
  {
  	struct nlm_block	*block;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
870

39be4502c   Olaf Kirch   [PATCH] knfsd: ma...
871
872
873
874
  	dprintk("grant_reply: looking for cookie %x, s=%d 
  ",
  		*(unsigned int *)(cookie->data), status);
  	if (!(block = nlmsvc_find_block(cookie)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876

f232142cc   J. Bruce Fields   NLM: Clean up nlm...
877
  	if (block) {
e8c5c045d   Al Viro   [PATCH] lockd end...
878
  		if (status == nlm_lck_denied_grace_period) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879
880
  			/* Try again in a couple of seconds */
  			nlmsvc_insert_block(block, 10 * HZ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
881
882
883
  		} else {
  			/* Lock is now held by client, or has been rejected.
  			 * In both cases, the block should be removed. */
6849c0cab   Trond Myklebust   lockd: Add refcou...
884
  			nlmsvc_unlink_block(block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
886
  		}
  	}
6849c0cab   Trond Myklebust   lockd: Add refcou...
887
  	nlmsvc_release_block(block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
  }
0e4ac9d93   Marc Eshel   lockd: handle fl_...
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
  /* Helper function to handle retry of a deferred block.
   * If it is a blocking lock, call grant_blocked.
   * For a non-blocking lock or test lock, revisit the request.
   */
  static void
  retry_deferred_block(struct nlm_block *block)
  {
  	if (!(block->b_flags & B_GOT_CALLBACK))
  		block->b_flags |= B_TIMED_OUT;
  	nlmsvc_insert_block(block, NLM_TIMEOUT);
  	dprintk("revisit block %p flags %d
  ",	block, block->b_flags);
  	if (block->b_deferred_req) {
  		block->b_deferred_req->revisit(block->b_deferred_req, 0);
  		block->b_deferred_req = NULL;
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
906
907
908
909
910
911
912
913
  /*
   * Retry all blocked locks that have been notified. This is where lockd
   * picks up locks that can be granted, or grant notifications that must
   * be retransmitted.
   */
  unsigned long
  nlmsvc_retry_blocked(void)
  {
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
914
915
  	unsigned long	timeout = MAX_SCHEDULE_TIMEOUT;
  	struct nlm_block *block;
d751a7cd0   Jeff Layton   NLM: Convert lock...
916
  	while (!list_empty(&nlm_blocked) && !kthread_should_stop()) {
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
917
  		block = list_entry(nlm_blocked.next, struct nlm_block, b_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
918

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
919
920
  		if (block->b_when == NLM_NEVER)
  			break;
6d7bbbbac   J. Bruce Fields   lockd: minor svcl...
921
  		if (time_after(block->b_when, jiffies)) {
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
922
  			timeout = block->b_when - jiffies;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
923
  			break;
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
924
  		}
f3d43c769   J. Bruce Fields   NLM/lockd: remove...
925
926
927
  		dprintk("nlmsvc_retry_blocked(%p, when=%ld)
  ",
  			block, block->b_when);
0e4ac9d93   Marc Eshel   lockd: handle fl_...
928
929
930
931
932
933
934
  		if (block->b_flags & B_QUEUED) {
  			dprintk("nlmsvc_retry_blocked delete block (%p, granted=%d, flags=%d)
  ",
  				block, block->b_granted, block->b_flags);
  			retry_deferred_block(block);
  		} else
  			nlmsvc_grant_blocked(block);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
935
  	}
68a2d76ce   Olaf Kirch   [PATCH] knfsd: lo...
936
  	return timeout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
937
  }
d8367c504   Chuck Lever   lockd: Move nlmdb...
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
  
  #ifdef RPC_DEBUG
  static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
  {
  	/*
  	 * We can get away with a static buffer because we're only
  	 * called with BKL held.
  	 */
  	static char buf[2*NLM_MAXCOOKIELEN+1];
  	unsigned int i, len = sizeof(buf);
  	char *p = buf;
  
  	len--;	/* allow for trailing \0 */
  	if (len < 3)
  		return "???";
  	for (i = 0 ; i < cookie->len ; i++) {
  		if (len < 2) {
  			strcpy(p-3, "...");
  			break;
  		}
  		sprintf(p, "%02x", cookie->data[i]);
  		p += 2;
  		len -= 2;
  	}
  	*p = '\0';
  
  	return buf;
  }
  #endif