svclock.c 25.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * 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>
 */

#include <linux/types.h>
24
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
25 26 27 28 29 30 31
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h>
#include <linux/lockd/nlm.h>
#include <linux/lockd/lockd.h>
32
#include <linux/kthread.h>
Linus Torvalds's avatar
Linus Torvalds committed
33 34 35 36 37 38 39 40 41

#define NLMDBG_FACILITY		NLMDBG_SVCLOCK

#ifdef CONFIG_LOCKD_V4
#define nlm_deadlock	nlm4_deadlock
#else
#define nlm_deadlock	nlm_lck_denied
#endif

42
static void nlmsvc_release_block(struct nlm_block *block);
Linus Torvalds's avatar
Linus Torvalds committed
43
static void	nlmsvc_insert_block(struct nlm_block *block, unsigned long);
44
static void	nlmsvc_remove_block(struct nlm_block *block);
45

46 47
static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock);
static void nlmsvc_freegrantargs(struct nlm_rqst *call);
48
static const struct rpc_call_ops nlmsvc_grant_ops;
49
static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie);
Linus Torvalds's avatar
Linus Torvalds committed
50 51 52 53

/*
 * The list of blocked locks to retry
 */
54
static LIST_HEAD(nlm_blocked);
55
static DEFINE_SPINLOCK(nlm_blocked_lock);
Linus Torvalds's avatar
Linus Torvalds committed
56 57 58 59 60

/*
 * Insert a blocked lock into the global list
 */
static void
61
nlmsvc_insert_block_locked(struct nlm_block *block, unsigned long when)
Linus Torvalds's avatar
Linus Torvalds committed
62
{
63 64
	struct nlm_block *b;
	struct list_head *pos;
Linus Torvalds's avatar
Linus Torvalds committed
65 66

	dprintk("lockd: nlmsvc_insert_block(%p, %ld)\n", block, when);
67 68 69 70 71 72 73
	if (list_empty(&block->b_list)) {
		kref_get(&block->b_count);
	} else {
		list_del_init(&block->b_list);
	}

	pos = &nlm_blocked;
Linus Torvalds's avatar
Linus Torvalds committed
74 75 76
	if (when != NLM_NEVER) {
		if ((when += jiffies) == NLM_NEVER)
			when ++;
77 78 79 80 81 82 83 84 85
		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
		 */
	}
Linus Torvalds's avatar
Linus Torvalds committed
86

87
	list_add_tail(&block->b_list, pos);
Linus Torvalds's avatar
Linus Torvalds committed
88 89 90
	block->b_when = when;
}

91 92 93 94 95 96 97
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);
}

Linus Torvalds's avatar
Linus Torvalds committed
98 99 100
/*
 * Remove a block from the global list
 */
101
static inline void
Linus Torvalds's avatar
Linus Torvalds committed
102 103
nlmsvc_remove_block(struct nlm_block *block)
{
104
	if (!list_empty(&block->b_list)) {
105
		spin_lock(&nlm_blocked_lock);
106
		list_del_init(&block->b_list);
107
		spin_unlock(&nlm_blocked_lock);
108
		nlmsvc_release_block(block);
Linus Torvalds's avatar
Linus Torvalds committed
109 110 111 112
	}
}

/*
113
 * Find a block for a given lock
Linus Torvalds's avatar
Linus Torvalds committed
114 115
 */
static struct nlm_block *
116
nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock)
Linus Torvalds's avatar
Linus Torvalds committed
117
{
118
	struct nlm_block	*block;
Linus Torvalds's avatar
Linus Torvalds committed
119 120 121 122 123 124
	struct file_lock	*fl;

	dprintk("lockd: nlmsvc_lookup_block f=%p pd=%d %Ld-%Ld ty=%d\n",
				file, lock->fl.fl_pid,
				(long long)lock->fl.fl_start,
				(long long)lock->fl.fl_end, lock->fl.fl_type);
125
	list_for_each_entry(block, &nlm_blocked, b_list) {
126
		fl = &block->b_call->a_args.lock.fl;
Linus Torvalds's avatar
Linus Torvalds committed
127 128 129 130
		dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s\n",
				block->b_file, fl->fl_pid,
				(long long)fl->fl_start,
				(long long)fl->fl_end, fl->fl_type,
131
				nlmdbg_cookie2a(&block->b_call->a_args.cookie));
Linus Torvalds's avatar
Linus Torvalds committed
132
		if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
133
			kref_get(&block->b_count);
Linus Torvalds's avatar
Linus Torvalds committed
134 135 136 137 138 139 140 141 142
			return block;
		}
	}

	return NULL;
}

static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b)
{
143
	if (a->len != b->len)
Linus Torvalds's avatar
Linus Torvalds committed
144
		return 0;
145
	if (memcmp(a->data, b->data, a->len))
Linus Torvalds's avatar
Linus Torvalds committed
146 147 148 149 150 151 152 153
		return 0;
	return 1;
}

/*
 * Find a block with a given NLM cookie.
 */
static inline struct nlm_block *
154
nlmsvc_find_block(struct nlm_cookie *cookie)
Linus Torvalds's avatar
Linus Torvalds committed
155 156 157
{
	struct nlm_block *block;

158
	list_for_each_entry(block, &nlm_blocked, b_list) {
159
		if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie))
160
			goto found;
Linus Torvalds's avatar
Linus Torvalds committed
161 162
	}

163 164 165
	return NULL;

found:
166
	dprintk("nlmsvc_find_block(%s): block=%p\n", nlmdbg_cookie2a(cookie), block);
167
	kref_get(&block->b_count);
Linus Torvalds's avatar
Linus Torvalds committed
168 169 170 171 172 173 174 175 176 177 178 179
	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.
180 181 182 183 184
 *
 * 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
Linus Torvalds's avatar
Linus Torvalds committed
185
 */
186 187 188 189
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)
Linus Torvalds's avatar
Linus Torvalds committed
190 191
{
	struct nlm_block	*block;
192
	struct nlm_rqst		*call = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
193

194
	nlm_get_host(host);
195 196 197 198
	call = nlm_alloc_call(host);
	if (call == NULL)
		return NULL;

Linus Torvalds's avatar
Linus Torvalds committed
199
	/* Allocate memory for block, and initialize arguments */
200 201
	block = kzalloc(sizeof(*block), GFP_KERNEL);
	if (block == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
202
		goto failed;
203
	kref_init(&block->b_count);
204 205
	INIT_LIST_HEAD(&block->b_list);
	INIT_LIST_HEAD(&block->b_flist);
Linus Torvalds's avatar
Linus Torvalds committed
206

207
	if (!nlmsvc_setgrantargs(call, lock))
Linus Torvalds's avatar
Linus Torvalds committed
208 209 210
		goto failed_free;

	/* Set notifier function for VFS, and init args */
211 212
	call->a_args.lock.fl.fl_flags |= FL_SLEEP;
	call->a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations;
213
	nlmclnt_next_cookie(&call->a_args.cookie);
Linus Torvalds's avatar
Linus Torvalds committed
214 215 216 217 218 219 220

	dprintk("lockd: created block %p...\n", block);

	/* Create and initialize the block */
	block->b_daemon = rqstp->rq_server;
	block->b_host   = host;
	block->b_file   = file;
Marc Eshel's avatar
Marc Eshel committed
221
	block->b_fl = NULL;
222
	file->f_count++;
Linus Torvalds's avatar
Linus Torvalds committed
223 224

	/* Add to file's list of blocks */
225
	list_add(&block->b_flist, &file->f_blocks);
Linus Torvalds's avatar
Linus Torvalds committed
226 227

	/* Set up RPC arguments for callback */
228
	block->b_call = call;
Linus Torvalds's avatar
Linus Torvalds committed
229
	call->a_flags   = RPC_TASK_ASYNC;
230
	call->a_block = block;
Linus Torvalds's avatar
Linus Torvalds committed
231 232 233 234 235 236

	return block;

failed_free:
	kfree(block);
failed:
Chuck Lever's avatar
Chuck Lever committed
237
	nlmsvc_release_call(call);
Linus Torvalds's avatar
Linus Torvalds committed
238 239 240 241
	return NULL;
}

/*
242
 * Delete a block.
Linus Torvalds's avatar
Linus Torvalds committed
243 244 245
 * It is the caller's responsibility to check whether the file
 * can be closed hereafter.
 */
246
static int nlmsvc_unlink_block(struct nlm_block *block)
Linus Torvalds's avatar
Linus Torvalds committed
247
{
248
	int status;
249
	dprintk("lockd: unlinking block %p...\n", block);
Linus Torvalds's avatar
Linus Torvalds committed
250 251

	/* Remove block from list */
252
	status = posix_unblock_lock(block->b_file->f_file, &block->b_call->a_args.lock.fl);
Linus Torvalds's avatar
Linus Torvalds committed
253
	nlmsvc_remove_block(block);
254 255
	return status;
}
Linus Torvalds's avatar
Linus Torvalds committed
256

257 258 259 260 261 262
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;

	dprintk("lockd: freeing block %p...\n", block);
Linus Torvalds's avatar
Linus Torvalds committed
263 264

	/* Remove block from file's list of blocks */
265
	mutex_lock(&file->f_mutex);
266
	list_del_init(&block->b_flist);
267
	mutex_unlock(&file->f_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
268

269
	nlmsvc_freegrantargs(block->b_call);
Chuck Lever's avatar
Chuck Lever committed
270
	nlmsvc_release_call(block->b_call);
271
	nlm_release_file(block->b_file);
Marc Eshel's avatar
Marc Eshel committed
272
	kfree(block->b_fl);
Linus Torvalds's avatar
Linus Torvalds committed
273
	kfree(block);
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);
Linus Torvalds's avatar
Linus Torvalds committed
280 281
}

282 283 284 285 286 287 288
/*
 * 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)
289
{
290
	struct nlm_block *block, *next;
291 292

restart:
293
	mutex_lock(&file->f_mutex);
294
	list_for_each_entry_safe(block, next, &file->f_blocks, b_flist) {
295
		if (!match(block->b_host, host))
296
			continue;
297 298 299
		/* Do not destroy blocks that are not on
		 * the global retry list - why? */
		if (list_empty(&block->b_list))
300 301
			continue;
		kref_get(&block->b_count);
302
		mutex_unlock(&file->f_mutex);
303 304 305 306
		nlmsvc_unlink_block(block);
		nlmsvc_release_block(block);
		goto restart;
	}
307
	mutex_unlock(&file->f_mutex);
308 309
}

310 311 312 313 314 315 316 317
/*
 * 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));
318
	call->a_args.lock.caller = utsname()->nodename;
319 320 321 322 323 324 325 326
	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);
327
		if (!data)
328 329 330 331 332 333 334 335 336 337
			return 0;
		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)
{
338
	if (call->a_args.lock.oh.data != call->a_owner)
339
		kfree(call->a_args.lock.oh.data);
340 341

	locks_release_private(&call->a_args.lock.fl);
342 343
}

344 345 346
/*
 * Deferred lock request handling for non-blocking lock
 */
347
static __be32
348 349
nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block)
{
350
	__be32 status = nlm_lck_denied_nolocks;
351 352 353 354 355 356 357 358 359 360 361 362 363

	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\n",
364
		block, block->b_flags, ntohl(status));
365 366 367 368

	return status;
}

Linus Torvalds's avatar
Linus Torvalds committed
369 370 371 372
/*
 * Attempt to establish a lock, and if it can't be granted, block it
 * if required.
 */
Al Viro's avatar
Al Viro committed
373
__be32
Linus Torvalds's avatar
Linus Torvalds committed
374
nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
375
	    struct nlm_host *host, struct nlm_lock *lock, int wait,
376
	    struct nlm_cookie *cookie, int reclaim)
Linus Torvalds's avatar
Linus Torvalds committed
377
{
378
	struct nlm_block	*block = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
379
	int			error;
Al Viro's avatar
Al Viro committed
380
	__be32			ret;
Linus Torvalds's avatar
Linus Torvalds committed
381 382

	dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n",
383 384
				file->f_file->f_path.dentry->d_inode->i_sb->s_id,
				file->f_file->f_path.dentry->d_inode->i_ino,
Linus Torvalds's avatar
Linus Torvalds committed
385 386 387 388 389 390
				lock->fl.fl_type, lock->fl.fl_pid,
				(long long)lock->fl.fl_start,
				(long long)lock->fl.fl_end,
				wait);

	/* Lock file against concurrent access */
391
	mutex_lock(&file->f_mutex);
392 393 394
	/* Get existing block (in case client is busy-waiting)
	 * or create new block
	 */
395
	block = nlmsvc_lookup_block(file, lock);
396
	if (block == NULL) {
397
		block = nlmsvc_create_block(rqstp, host, file, lock, cookie);
398 399 400
		ret = nlm_lck_denied_nolocks;
		if (block == NULL)
			goto out;
401
		lock = &block->b_call->a_args.lock;
402 403
	} else
		lock->fl.fl_flags &= ~FL_SLEEP;
Linus Torvalds's avatar
Linus Torvalds committed
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\n",
							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;
	}
Linus Torvalds's avatar
Linus Torvalds committed
421

422 423 424 425
	if (locks_in_grace() && !reclaim) {
		ret = nlm_lck_denied_grace_period;
		goto out;
	}
426 427 428 429
	if (reclaim && !locks_in_grace()) {
		ret = nlm_lck_denied_grace_period;
		goto out;
	}
430

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;
435

436
	dprintk("lockd: vfs_lock_file returned %d\n", error);
437
	switch (error) {
Linus Torvalds's avatar
Linus Torvalds committed
438
		case 0:
Andy Adamson's avatar
Andy Adamson committed
439 440
			ret = nlm_granted;
			goto out;
441
		case -EAGAIN:
442 443 444 445 446 447 448
			/*
			 * 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;
449
			ret = nlm_lck_denied;
450
			goto out;
451
		case FILE_LOCK_DEFERRED:
452 453 454 455 456 457
			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;
458
		case -EDEADLK:
Andy Adamson's avatar
Andy Adamson committed
459 460
			ret = nlm_deadlock;
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
461
		default:			/* includes ENOLCK */
Andy Adamson's avatar
Andy Adamson committed
462 463
			ret = nlm_lck_denied_nolocks;
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
464 465
	}

466
	ret = nlm_lck_blocked;
Linus Torvalds's avatar
Linus Torvalds committed
467 468

	/* Append to list of blocked */
469
	nlmsvc_insert_block(block, NLM_NEVER);
Andy Adamson's avatar
Andy Adamson committed
470
out:
471
	mutex_unlock(&file->f_mutex);
472
	nlmsvc_release_block(block);
Andy Adamson's avatar
Andy Adamson committed
473 474
	dprintk("lockd: nlmsvc_lock returned %u\n", ret);
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
475 476 477 478 479
}

/*
 * Test for presence of a conflicting lock.
 */
Al Viro's avatar
Al Viro committed
480
__be32
481
nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
482 483
		struct nlm_host *host, struct nlm_lock *lock,
		struct nlm_lock *conflock, struct nlm_cookie *cookie)
Linus Torvalds's avatar
Linus Torvalds committed
484
{
Marc Eshel's avatar
Marc Eshel committed
485 486 487 488
	struct nlm_block 	*block = NULL;
	int			error;
	__be32			ret;

Linus Torvalds's avatar
Linus Torvalds committed
489
	dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
490 491
				file->f_file->f_path.dentry->d_inode->i_sb->s_id,
				file->f_file->f_path.dentry->d_inode->i_ino,
Linus Torvalds's avatar
Linus Torvalds committed
492 493 494 495
				lock->fl.fl_type,
				(long long)lock->fl.fl_start,
				(long long)lock->fl.fl_end);

Marc Eshel's avatar
Marc Eshel committed
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;
504
		block = nlmsvc_create_block(rqstp, host, file, lock, cookie);
Marc Eshel's avatar
Marc Eshel committed
505 506 507 508 509 510 511 512 513 514 515
		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\n",
			block, block->b_flags, block->b_fl);
		if (block->b_flags & B_TIMED_OUT) {
			nlmsvc_unlink_block(block);
516 517
			ret = nlm_lck_denied;
			goto out;
Marc Eshel's avatar
Marc Eshel committed
518 519
		}
		if (block->b_flags & B_GOT_CALLBACK) {
520
			nlmsvc_unlink_block(block);
Marc Eshel's avatar
Marc Eshel committed
521 522 523 524
			if (block->b_fl != NULL
					&& block->b_fl->fl_type != F_UNLCK) {
				lock->fl = *block->b_fl;
				goto conf_lock;
525 526 527
			} else {
				ret = nlm_granted;
				goto out;
Marc Eshel's avatar
Marc Eshel committed
528 529
			}
		}
530 531
		ret = nlm_drop_reply;
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
532 533
	}

534 535 536 537
	if (locks_in_grace()) {
		ret = nlm_lck_denied_grace_period;
		goto out;
	}
Marc Eshel's avatar
Marc Eshel committed
538
	error = vfs_test_lock(file->f_file, &lock->fl);
539
	if (error == FILE_LOCK_DEFERRED) {
540 541 542
		ret = nlmsvc_defer_lock_rqst(rqstp, block);
		goto out;
	}
Marc Eshel's avatar
Marc Eshel committed
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
	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)\n",
		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;
Linus Torvalds's avatar
Linus Torvalds committed
568 569 570 571 572 573 574 575 576
}

/*
 * 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.
 */
Al Viro's avatar
Al Viro committed
577
__be32
Linus Torvalds's avatar
Linus Torvalds committed
578 579 580 581 582
nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
{
	int	error;

	dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)\n",
583 584
				file->f_file->f_path.dentry->d_inode->i_sb->s_id,
				file->f_file->f_path.dentry->d_inode->i_ino,
Linus Torvalds's avatar
Linus Torvalds committed
585 586 587 588 589 590 591 592
				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;
593
	error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
594 595 596 597 598 599 600 601 602 603 604

	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.
 */
Al Viro's avatar
Al Viro committed
605
__be32
Linus Torvalds's avatar
Linus Torvalds committed
606 607 608
nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
{
	struct nlm_block	*block;
J. Bruce Fields's avatar
J. Bruce Fields committed
609
	int status = 0;
Linus Torvalds's avatar
Linus Torvalds committed
610 611

	dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n",
612 613
				file->f_file->f_path.dentry->d_inode->i_sb->s_id,
				file->f_file->f_path.dentry->d_inode->i_ino,
Linus Torvalds's avatar
Linus Torvalds committed
614 615 616 617
				lock->fl.fl_pid,
				(long long)lock->fl.fl_start,
				(long long)lock->fl.fl_end);

618 619 620
	if (locks_in_grace())
		return nlm_lck_denied_grace_period;

621
	mutex_lock(&file->f_mutex);
622
	block = nlmsvc_lookup_block(file, lock);
623
	mutex_unlock(&file->f_mutex);
624
	if (block != NULL) {
625 626
		vfs_cancel_lock(block->b_file->f_file,
				&block->b_call->a_args.lock.fl);
627 628 629
		status = nlmsvc_unlink_block(block);
		nlmsvc_release_block(block);
	}
J. Bruce Fields's avatar
J. Bruce Fields committed
630
	return status ? nlm_lck_denied : nlm_granted;
Linus Torvalds's avatar
Linus Torvalds committed
631 632
}

Marc Eshel's avatar
Marc Eshel committed
633 634
/*
 * This is a callback from the filesystem for VFS file lock requests.
635
 * It will be used if lm_grant is defined and the filesystem can not
Marc Eshel's avatar
Marc Eshel committed
636 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) {
		if (block->b_fl)
654
			__locks_copy_lock(block->b_fl, conf);
Marc Eshel's avatar
Marc Eshel committed
655 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;

664
	spin_lock(&nlm_blocked_lock);
Marc Eshel's avatar
Marc Eshel committed
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\n",
							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;

678
			nlmsvc_insert_block_locked(block, 0);
Marc Eshel's avatar
Marc Eshel committed
679 680 681 682 683
			svc_wake_up(block->b_daemon);
			rc = 0;
			break;
		}
	}
684
	spin_unlock(&nlm_blocked_lock);
Marc Eshel's avatar
Marc Eshel committed
685 686 687 688 689
	if (rc == -ENOENT)
		printk(KERN_WARNING "lockd: grant for unknown block\n");
	return rc;
}

Linus Torvalds's avatar
Linus Torvalds committed
690 691 692 693 694 695 696 697 698 699
/*
 * 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)
{
700
	struct nlm_block	*block;
Linus Torvalds's avatar
Linus Torvalds committed
701 702

	dprintk("lockd: VFS unblock notification for block %p\n", fl);
703
	spin_lock(&nlm_blocked_lock);
704
	list_for_each_entry(block, &nlm_blocked, b_list) {
705
		if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
706 707
			nlmsvc_insert_block_locked(block, 0);
			spin_unlock(&nlm_blocked_lock);
Linus Torvalds's avatar
Linus Torvalds committed
708 709 710 711
			svc_wake_up(block->b_daemon);
			return;
		}
	}
712
	spin_unlock(&nlm_blocked_lock);
Linus Torvalds's avatar
Linus Torvalds committed
713 714 715 716 717 718 719 720
	printk(KERN_WARNING "lockd: notification for unknown block!\n");
}

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;
}

721
const struct lock_manager_operations nlmsvc_lock_operations = {
722 723 724
	.lm_compare_owner = nlmsvc_same_owner,
	.lm_notify = nlmsvc_notify_blocked,
	.lm_grant = nlmsvc_grant_deferred,
Linus Torvalds's avatar
Linus Torvalds committed
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741
};

/*
 * 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;
742
	struct nlm_lock		*lock = &block->b_call->a_args.lock;
Linus Torvalds's avatar
Linus Torvalds committed
743 744 745 746
	int			error;

	dprintk("lockd: grant blocked lock %p\n", block);

Marc Eshel's avatar
Marc Eshel committed
747 748
	kref_get(&block->b_count);

Linus Torvalds's avatar
Linus Torvalds committed
749
	/* Unlink block request from list */
750
	nlmsvc_unlink_block(block);
Linus Torvalds's avatar
Linus Torvalds committed
751 752 753 754 755 756 757 758 759 760

	/* 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 */
761
	lock->fl.fl_flags |= FL_SLEEP;
762
	error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
763 764
	lock->fl.fl_flags &= ~FL_SLEEP;

765 766 767
	switch (error) {
	case 0:
		break;
768
	case FILE_LOCK_DEFERRED:
769
		dprintk("lockd: lock still blocked error %d\n", error);
Linus Torvalds's avatar
Linus Torvalds committed
770
		nlmsvc_insert_block(block, NLM_NEVER);
Marc Eshel's avatar
Marc Eshel committed
771
		nlmsvc_release_block(block);
772
		return;
773
	default:
Linus Torvalds's avatar
Linus Torvalds committed
774
		printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
775
				-error, __func__);
Linus Torvalds's avatar
Linus Torvalds committed
776
		nlmsvc_insert_block(block, 10 * HZ);
Marc Eshel's avatar
Marc Eshel committed
777
		nlmsvc_release_block(block);
778
		return;
Linus Torvalds's avatar
Linus Torvalds committed
779 780 781 782 783 784 785
	}

callback:
	/* Lock was granted by VFS. */
	dprintk("lockd: GRANTing blocked lock.\n");
	block->b_granted = 1;

786 787 788 789
	/* keep block on the list, but don't reattempt until the RPC
	 * completes or the submission fails
	 */
	nlmsvc_insert_block(block, NLM_NEVER);
Linus Torvalds's avatar
Linus Torvalds committed
790

791 792 793 794 795 796 797 798 799
	/* 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);
Linus Torvalds's avatar
Linus Torvalds committed
800 801 802 803 804 805 806 807 808 809
}

/*
 * 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).
 */
810
static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
811
{
812
	struct nlm_rqst		*call = data;
813
	struct nlm_block	*block = call->a_block;
Linus Torvalds's avatar
Linus Torvalds committed
814 815 816 817
	unsigned long		timeout;

	dprintk("lockd: GRANT_MSG RPC callback\n");

818
	spin_lock(&nlm_blocked_lock);
819 820 821 822 823 824 825 826 827
	/* 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))
828
		goto out;
829

Linus Torvalds's avatar
Linus Torvalds committed
830 831 832 833 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;
	} else {
		/* Call was successful, now wait for client callback */
		timeout = 60 * HZ;
	}
840
	nlmsvc_insert_block_locked(block, timeout);
Linus Torvalds's avatar
Linus Torvalds committed
841
	svc_wake_up(block->b_daemon);
842
out:
843
	spin_unlock(&nlm_blocked_lock);
844 845
}

846 847 848 849
/*
 * FIXME: nlmsvc_release_block() grabs a mutex.  This is not allowed for an
 * .rpc_release rpc_call_op
 */
Adrian Bunk's avatar
Adrian Bunk committed
850
static void nlmsvc_grant_release(void *data)
851
{
852 853
	struct nlm_rqst		*call = data;
	nlmsvc_release_block(call->a_block);
Linus Torvalds's avatar
Linus Torvalds committed
854 855
}

856 857
static const struct rpc_call_ops nlmsvc_grant_ops = {
	.rpc_call_done = nlmsvc_grant_callback,
858
	.rpc_release = nlmsvc_grant_release,
859 860
};

Linus Torvalds's avatar
Linus Torvalds committed
861 862 863 864 865
/*
 * We received a GRANT_RES callback. Try to find the corresponding
 * block.
 */
void
Al Viro's avatar
Al Viro committed
866
nlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status)
Linus Torvalds's avatar
Linus Torvalds committed
867 868 869
{
	struct nlm_block	*block;

870 871 872
	dprintk("grant_reply: looking for cookie %x, s=%d \n",
		*(unsigned int *)(cookie->data), status);
	if (!(block = nlmsvc_find_block(cookie)))
Linus Torvalds's avatar
Linus Torvalds committed
873 874
		return;

875
	if (block) {
Al Viro's avatar
Al Viro committed
876
		if (status == nlm_lck_denied_grace_period) {
Linus Torvalds's avatar
Linus Torvalds committed
877 878 879 880 881
			/* Try again in a couple of seconds */
			nlmsvc_insert_block(block, 10 * HZ);
		} else {
			/* Lock is now held by client, or has been rejected.
			 * In both cases, the block should be removed. */
882
			nlmsvc_unlink_block(block);
Linus Torvalds's avatar
Linus Torvalds committed
883 884
		}
	}
885
	nlmsvc_release_block(block);
Linus Torvalds's avatar
Linus Torvalds committed
886 887
}

Marc Eshel's avatar
Marc Eshel committed
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
/* 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\n",	block, block->b_flags);
	if (block->b_deferred_req) {
		block->b_deferred_req->revisit(block->b_deferred_req, 0);
		block->b_deferred_req = NULL;
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
905 906 907 908 909 910 911 912
/*
 * 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)
{
913 914 915
	unsigned long	timeout = MAX_SCHEDULE_TIMEOUT;
	struct nlm_block *block;

916
	while (!list_empty(&nlm_blocked) && !kthread_should_stop()) {
917
		block = list_entry(nlm_blocked.next, struct nlm_block, b_list);
Linus Torvalds's avatar
Linus Torvalds committed
918 919 920

		if (block->b_when == NLM_NEVER)
			break;
921
		if (time_after(block->b_when, jiffies)) {
922
			timeout = block->b_when - jiffies;
Linus Torvalds's avatar
Linus Torvalds committed
923
			break;
924 925
		}

J. Bruce Fields's avatar
J. Bruce Fields committed
926 927
		dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
			block, block->b_when);
Marc Eshel's avatar
Marc Eshel committed
928 929 930 931 932 933
		if (block->b_flags & B_QUEUED) {
			dprintk("nlmsvc_retry_blocked delete block (%p, granted=%d, flags=%d)\n",
				block, block->b_granted, block->b_flags);
			retry_deferred_block(block);
		} else
			nlmsvc_grant_blocked(block);
Linus Torvalds's avatar
Linus Torvalds committed
934 935
	}

936
	return timeout;
Linus Torvalds's avatar
Linus Torvalds committed
937
}
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