162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/fs/lockd/svclock.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Handling of server-side locks, mostly of the blocked variety.
662306a36Sopenharmony_ci * This is the ugliest part of lockd because we tread on very thin ice.
762306a36Sopenharmony_ci * GRANT and CANCEL calls may get stuck, meet in mid-flight, etc.
862306a36Sopenharmony_ci * IMNSHO introducing the grant callback into the NLM protocol was one
962306a36Sopenharmony_ci * of the worst ideas Sun ever had. Except maybe for the idea of doing
1062306a36Sopenharmony_ci * NFS file locking at all.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * I'm trying hard to avoid race conditions by protecting most accesses
1362306a36Sopenharmony_ci * to a file's list of blocked locks through a semaphore. The global
1462306a36Sopenharmony_ci * list of blocked locks is not protected in this fashion however.
1562306a36Sopenharmony_ci * Therefore, some functions (such as the RPC callback for the async grant
1662306a36Sopenharmony_ci * call) move blocked locks towards the head of the list *while some other
1762306a36Sopenharmony_ci * process might be traversing it*. This should not be a problem in
1862306a36Sopenharmony_ci * practice, because this will only cause functions traversing the list
1962306a36Sopenharmony_ci * to visit some blocks twice.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/types.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <linux/errno.h>
2762306a36Sopenharmony_ci#include <linux/kernel.h>
2862306a36Sopenharmony_ci#include <linux/sched.h>
2962306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h>
3062306a36Sopenharmony_ci#include <linux/sunrpc/svc_xprt.h>
3162306a36Sopenharmony_ci#include <linux/lockd/nlm.h>
3262306a36Sopenharmony_ci#include <linux/lockd/lockd.h>
3362306a36Sopenharmony_ci#include <linux/kthread.h>
3462306a36Sopenharmony_ci#include <linux/exportfs.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define NLMDBG_FACILITY		NLMDBG_SVCLOCK
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#ifdef CONFIG_LOCKD_V4
3962306a36Sopenharmony_ci#define nlm_deadlock	nlm4_deadlock
4062306a36Sopenharmony_ci#else
4162306a36Sopenharmony_ci#define nlm_deadlock	nlm_lck_denied
4262306a36Sopenharmony_ci#endif
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic void nlmsvc_release_block(struct nlm_block *block);
4562306a36Sopenharmony_cistatic void	nlmsvc_insert_block(struct nlm_block *block, unsigned long);
4662306a36Sopenharmony_cistatic void	nlmsvc_remove_block(struct nlm_block *block);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock);
4962306a36Sopenharmony_cistatic void nlmsvc_freegrantargs(struct nlm_rqst *call);
5062306a36Sopenharmony_cistatic const struct rpc_call_ops nlmsvc_grant_ops;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/*
5362306a36Sopenharmony_ci * The list of blocked locks to retry
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_cistatic LIST_HEAD(nlm_blocked);
5662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nlm_blocked_lock);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
5962306a36Sopenharmony_cistatic const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	/*
6262306a36Sopenharmony_ci	 * We can get away with a static buffer because this is only called
6362306a36Sopenharmony_ci	 * from lockd, which is single-threaded.
6462306a36Sopenharmony_ci	 */
6562306a36Sopenharmony_ci	static char buf[2*NLM_MAXCOOKIELEN+1];
6662306a36Sopenharmony_ci	unsigned int i, len = sizeof(buf);
6762306a36Sopenharmony_ci	char *p = buf;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	len--;	/* allow for trailing \0 */
7062306a36Sopenharmony_ci	if (len < 3)
7162306a36Sopenharmony_ci		return "???";
7262306a36Sopenharmony_ci	for (i = 0 ; i < cookie->len ; i++) {
7362306a36Sopenharmony_ci		if (len < 2) {
7462306a36Sopenharmony_ci			strcpy(p-3, "...");
7562306a36Sopenharmony_ci			break;
7662306a36Sopenharmony_ci		}
7762306a36Sopenharmony_ci		sprintf(p, "%02x", cookie->data[i]);
7862306a36Sopenharmony_ci		p += 2;
7962306a36Sopenharmony_ci		len -= 2;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci	*p = '\0';
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return buf;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci#endif
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * Insert a blocked lock into the global list
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistatic void
9162306a36Sopenharmony_cinlmsvc_insert_block_locked(struct nlm_block *block, unsigned long when)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct nlm_block *b;
9462306a36Sopenharmony_ci	struct list_head *pos;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	dprintk("lockd: nlmsvc_insert_block(%p, %ld)\n", block, when);
9762306a36Sopenharmony_ci	if (list_empty(&block->b_list)) {
9862306a36Sopenharmony_ci		kref_get(&block->b_count);
9962306a36Sopenharmony_ci	} else {
10062306a36Sopenharmony_ci		list_del_init(&block->b_list);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	pos = &nlm_blocked;
10462306a36Sopenharmony_ci	if (when != NLM_NEVER) {
10562306a36Sopenharmony_ci		if ((when += jiffies) == NLM_NEVER)
10662306a36Sopenharmony_ci			when ++;
10762306a36Sopenharmony_ci		list_for_each(pos, &nlm_blocked) {
10862306a36Sopenharmony_ci			b = list_entry(pos, struct nlm_block, b_list);
10962306a36Sopenharmony_ci			if (time_after(b->b_when,when) || b->b_when == NLM_NEVER)
11062306a36Sopenharmony_ci				break;
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci		/* On normal exit from the loop, pos == &nlm_blocked,
11362306a36Sopenharmony_ci		 * so we will be adding to the end of the list - good
11462306a36Sopenharmony_ci		 */
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	list_add_tail(&block->b_list, pos);
11862306a36Sopenharmony_ci	block->b_when = when;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
12462306a36Sopenharmony_ci	nlmsvc_insert_block_locked(block, when);
12562306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/*
12962306a36Sopenharmony_ci * Remove a block from the global list
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_cistatic inline void
13262306a36Sopenharmony_cinlmsvc_remove_block(struct nlm_block *block)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
13562306a36Sopenharmony_ci	if (!list_empty(&block->b_list)) {
13662306a36Sopenharmony_ci		list_del_init(&block->b_list);
13762306a36Sopenharmony_ci		spin_unlock(&nlm_blocked_lock);
13862306a36Sopenharmony_ci		nlmsvc_release_block(block);
13962306a36Sopenharmony_ci		return;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/*
14562306a36Sopenharmony_ci * Find a block for a given lock
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_cistatic struct nlm_block *
14862306a36Sopenharmony_cinlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct nlm_block	*block;
15162306a36Sopenharmony_ci	struct file_lock	*fl;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	dprintk("lockd: nlmsvc_lookup_block f=%p pd=%d %Ld-%Ld ty=%d\n",
15462306a36Sopenharmony_ci				file, lock->fl.fl_pid,
15562306a36Sopenharmony_ci				(long long)lock->fl.fl_start,
15662306a36Sopenharmony_ci				(long long)lock->fl.fl_end, lock->fl.fl_type);
15762306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
15862306a36Sopenharmony_ci	list_for_each_entry(block, &nlm_blocked, b_list) {
15962306a36Sopenharmony_ci		fl = &block->b_call->a_args.lock.fl;
16062306a36Sopenharmony_ci		dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s\n",
16162306a36Sopenharmony_ci				block->b_file, fl->fl_pid,
16262306a36Sopenharmony_ci				(long long)fl->fl_start,
16362306a36Sopenharmony_ci				(long long)fl->fl_end, fl->fl_type,
16462306a36Sopenharmony_ci				nlmdbg_cookie2a(&block->b_call->a_args.cookie));
16562306a36Sopenharmony_ci		if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
16662306a36Sopenharmony_ci			kref_get(&block->b_count);
16762306a36Sopenharmony_ci			spin_unlock(&nlm_blocked_lock);
16862306a36Sopenharmony_ci			return block;
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return NULL;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	if (a->len != b->len)
17962306a36Sopenharmony_ci		return 0;
18062306a36Sopenharmony_ci	if (memcmp(a->data, b->data, a->len))
18162306a36Sopenharmony_ci		return 0;
18262306a36Sopenharmony_ci	return 1;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/*
18662306a36Sopenharmony_ci * Find a block with a given NLM cookie.
18762306a36Sopenharmony_ci */
18862306a36Sopenharmony_cistatic inline struct nlm_block *
18962306a36Sopenharmony_cinlmsvc_find_block(struct nlm_cookie *cookie)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct nlm_block *block;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
19462306a36Sopenharmony_ci	list_for_each_entry(block, &nlm_blocked, b_list) {
19562306a36Sopenharmony_ci		if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie))
19662306a36Sopenharmony_ci			goto found;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	return NULL;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cifound:
20362306a36Sopenharmony_ci	dprintk("nlmsvc_find_block(%s): block=%p\n", nlmdbg_cookie2a(cookie), block);
20462306a36Sopenharmony_ci	kref_get(&block->b_count);
20562306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
20662306a36Sopenharmony_ci	return block;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/*
21062306a36Sopenharmony_ci * Create a block and initialize it.
21162306a36Sopenharmony_ci *
21262306a36Sopenharmony_ci * Note: we explicitly set the cookie of the grant reply to that of
21362306a36Sopenharmony_ci * the blocked lock request. The spec explicitly mentions that the client
21462306a36Sopenharmony_ci * should _not_ rely on the callback containing the same cookie as the
21562306a36Sopenharmony_ci * request, but (as I found out later) that's because some implementations
21662306a36Sopenharmony_ci * do just this. Never mind the standards comittees, they support our
21762306a36Sopenharmony_ci * logging industries.
21862306a36Sopenharmony_ci *
21962306a36Sopenharmony_ci * 10 years later: I hope we can safely ignore these old and broken
22062306a36Sopenharmony_ci * clients by now. Let's fix this so we can uniquely identify an incoming
22162306a36Sopenharmony_ci * GRANTED_RES message by cookie, without having to rely on the client's IP
22262306a36Sopenharmony_ci * address. --okir
22362306a36Sopenharmony_ci */
22462306a36Sopenharmony_cistatic struct nlm_block *
22562306a36Sopenharmony_cinlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
22662306a36Sopenharmony_ci		    struct nlm_file *file, struct nlm_lock *lock,
22762306a36Sopenharmony_ci		    struct nlm_cookie *cookie)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct nlm_block	*block;
23062306a36Sopenharmony_ci	struct nlm_rqst		*call = NULL;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	call = nlm_alloc_call(host);
23362306a36Sopenharmony_ci	if (call == NULL)
23462306a36Sopenharmony_ci		return NULL;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* Allocate memory for block, and initialize arguments */
23762306a36Sopenharmony_ci	block = kzalloc(sizeof(*block), GFP_KERNEL);
23862306a36Sopenharmony_ci	if (block == NULL)
23962306a36Sopenharmony_ci		goto failed;
24062306a36Sopenharmony_ci	kref_init(&block->b_count);
24162306a36Sopenharmony_ci	INIT_LIST_HEAD(&block->b_list);
24262306a36Sopenharmony_ci	INIT_LIST_HEAD(&block->b_flist);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (!nlmsvc_setgrantargs(call, lock))
24562306a36Sopenharmony_ci		goto failed_free;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* Set notifier function for VFS, and init args */
24862306a36Sopenharmony_ci	call->a_args.lock.fl.fl_flags |= FL_SLEEP;
24962306a36Sopenharmony_ci	call->a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations;
25062306a36Sopenharmony_ci	nlmclnt_next_cookie(&call->a_args.cookie);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	dprintk("lockd: created block %p...\n", block);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Create and initialize the block */
25562306a36Sopenharmony_ci	block->b_daemon = rqstp->rq_server;
25662306a36Sopenharmony_ci	block->b_host   = host;
25762306a36Sopenharmony_ci	block->b_file   = file;
25862306a36Sopenharmony_ci	file->f_count++;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* Add to file's list of blocks */
26162306a36Sopenharmony_ci	list_add(&block->b_flist, &file->f_blocks);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/* Set up RPC arguments for callback */
26462306a36Sopenharmony_ci	block->b_call = call;
26562306a36Sopenharmony_ci	call->a_flags   = RPC_TASK_ASYNC;
26662306a36Sopenharmony_ci	call->a_block = block;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return block;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cifailed_free:
27162306a36Sopenharmony_ci	kfree(block);
27262306a36Sopenharmony_cifailed:
27362306a36Sopenharmony_ci	nlmsvc_release_call(call);
27462306a36Sopenharmony_ci	return NULL;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/*
27862306a36Sopenharmony_ci * Delete a block.
27962306a36Sopenharmony_ci * It is the caller's responsibility to check whether the file
28062306a36Sopenharmony_ci * can be closed hereafter.
28162306a36Sopenharmony_ci */
28262306a36Sopenharmony_cistatic int nlmsvc_unlink_block(struct nlm_block *block)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	int status;
28562306a36Sopenharmony_ci	dprintk("lockd: unlinking block %p...\n", block);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* Remove block from list */
28862306a36Sopenharmony_ci	status = locks_delete_block(&block->b_call->a_args.lock.fl);
28962306a36Sopenharmony_ci	nlmsvc_remove_block(block);
29062306a36Sopenharmony_ci	return status;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic void nlmsvc_free_block(struct kref *kref)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct nlm_block *block = container_of(kref, struct nlm_block, b_count);
29662306a36Sopenharmony_ci	struct nlm_file		*file = block->b_file;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	dprintk("lockd: freeing block %p...\n", block);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/* Remove block from file's list of blocks */
30162306a36Sopenharmony_ci	list_del_init(&block->b_flist);
30262306a36Sopenharmony_ci	mutex_unlock(&file->f_mutex);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	nlmsvc_freegrantargs(block->b_call);
30562306a36Sopenharmony_ci	nlmsvc_release_call(block->b_call);
30662306a36Sopenharmony_ci	nlm_release_file(block->b_file);
30762306a36Sopenharmony_ci	kfree(block);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic void nlmsvc_release_block(struct nlm_block *block)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	if (block != NULL)
31362306a36Sopenharmony_ci		kref_put_mutex(&block->b_count, nlmsvc_free_block, &block->b_file->f_mutex);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci/*
31762306a36Sopenharmony_ci * Loop over all blocks and delete blocks held by
31862306a36Sopenharmony_ci * a matching host.
31962306a36Sopenharmony_ci */
32062306a36Sopenharmony_civoid nlmsvc_traverse_blocks(struct nlm_host *host,
32162306a36Sopenharmony_ci			struct nlm_file *file,
32262306a36Sopenharmony_ci			nlm_host_match_fn_t match)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct nlm_block *block, *next;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cirestart:
32762306a36Sopenharmony_ci	mutex_lock(&file->f_mutex);
32862306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
32962306a36Sopenharmony_ci	list_for_each_entry_safe(block, next, &file->f_blocks, b_flist) {
33062306a36Sopenharmony_ci		if (!match(block->b_host, host))
33162306a36Sopenharmony_ci			continue;
33262306a36Sopenharmony_ci		/* Do not destroy blocks that are not on
33362306a36Sopenharmony_ci		 * the global retry list - why? */
33462306a36Sopenharmony_ci		if (list_empty(&block->b_list))
33562306a36Sopenharmony_ci			continue;
33662306a36Sopenharmony_ci		kref_get(&block->b_count);
33762306a36Sopenharmony_ci		spin_unlock(&nlm_blocked_lock);
33862306a36Sopenharmony_ci		mutex_unlock(&file->f_mutex);
33962306a36Sopenharmony_ci		nlmsvc_unlink_block(block);
34062306a36Sopenharmony_ci		nlmsvc_release_block(block);
34162306a36Sopenharmony_ci		goto restart;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
34462306a36Sopenharmony_ci	mutex_unlock(&file->f_mutex);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic struct nlm_lockowner *
34862306a36Sopenharmony_cinlmsvc_get_lockowner(struct nlm_lockowner *lockowner)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	refcount_inc(&lockowner->count);
35162306a36Sopenharmony_ci	return lockowner;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_civoid nlmsvc_put_lockowner(struct nlm_lockowner *lockowner)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock))
35762306a36Sopenharmony_ci		return;
35862306a36Sopenharmony_ci	list_del(&lockowner->list);
35962306a36Sopenharmony_ci	spin_unlock(&lockowner->host->h_lock);
36062306a36Sopenharmony_ci	nlmsvc_release_host(lockowner->host);
36162306a36Sopenharmony_ci	kfree(lockowner);
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic struct nlm_lockowner *__nlmsvc_find_lockowner(struct nlm_host *host, pid_t pid)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct nlm_lockowner *lockowner;
36762306a36Sopenharmony_ci	list_for_each_entry(lockowner, &host->h_lockowners, list) {
36862306a36Sopenharmony_ci		if (lockowner->pid != pid)
36962306a36Sopenharmony_ci			continue;
37062306a36Sopenharmony_ci		return nlmsvc_get_lockowner(lockowner);
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci	return NULL;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic struct nlm_lockowner *nlmsvc_find_lockowner(struct nlm_host *host, pid_t pid)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct nlm_lockowner *res, *new = NULL;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	spin_lock(&host->h_lock);
38062306a36Sopenharmony_ci	res = __nlmsvc_find_lockowner(host, pid);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (res == NULL) {
38362306a36Sopenharmony_ci		spin_unlock(&host->h_lock);
38462306a36Sopenharmony_ci		new = kmalloc(sizeof(*res), GFP_KERNEL);
38562306a36Sopenharmony_ci		spin_lock(&host->h_lock);
38662306a36Sopenharmony_ci		res = __nlmsvc_find_lockowner(host, pid);
38762306a36Sopenharmony_ci		if (res == NULL && new != NULL) {
38862306a36Sopenharmony_ci			res = new;
38962306a36Sopenharmony_ci			/* fs/locks.c will manage the refcount through lock_ops */
39062306a36Sopenharmony_ci			refcount_set(&new->count, 1);
39162306a36Sopenharmony_ci			new->pid = pid;
39262306a36Sopenharmony_ci			new->host = nlm_get_host(host);
39362306a36Sopenharmony_ci			list_add(&new->list, &host->h_lockowners);
39462306a36Sopenharmony_ci			new = NULL;
39562306a36Sopenharmony_ci		}
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	spin_unlock(&host->h_lock);
39962306a36Sopenharmony_ci	kfree(new);
40062306a36Sopenharmony_ci	return res;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_civoid
40462306a36Sopenharmony_cinlmsvc_release_lockowner(struct nlm_lock *lock)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	if (lock->fl.fl_owner)
40762306a36Sopenharmony_ci		nlmsvc_put_lockowner(lock->fl.fl_owner);
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_civoid nlmsvc_locks_init_private(struct file_lock *fl, struct nlm_host *host,
41162306a36Sopenharmony_ci						pid_t pid)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	fl->fl_owner = nlmsvc_find_lockowner(host, pid);
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci/*
41762306a36Sopenharmony_ci * Initialize arguments for GRANTED call. The nlm_rqst structure
41862306a36Sopenharmony_ci * has been cleared already.
41962306a36Sopenharmony_ci */
42062306a36Sopenharmony_cistatic int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	locks_copy_lock(&call->a_args.lock.fl, &lock->fl);
42362306a36Sopenharmony_ci	memcpy(&call->a_args.lock.fh, &lock->fh, sizeof(call->a_args.lock.fh));
42462306a36Sopenharmony_ci	call->a_args.lock.caller = utsname()->nodename;
42562306a36Sopenharmony_ci	call->a_args.lock.oh.len = lock->oh.len;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/* set default data area */
42862306a36Sopenharmony_ci	call->a_args.lock.oh.data = call->a_owner;
42962306a36Sopenharmony_ci	call->a_args.lock.svid = ((struct nlm_lockowner *)lock->fl.fl_owner)->pid;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (lock->oh.len > NLMCLNT_OHSIZE) {
43262306a36Sopenharmony_ci		void *data = kmalloc(lock->oh.len, GFP_KERNEL);
43362306a36Sopenharmony_ci		if (!data)
43462306a36Sopenharmony_ci			return 0;
43562306a36Sopenharmony_ci		call->a_args.lock.oh.data = (u8 *) data;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	memcpy(call->a_args.lock.oh.data, lock->oh.data, lock->oh.len);
43962306a36Sopenharmony_ci	return 1;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic void nlmsvc_freegrantargs(struct nlm_rqst *call)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	if (call->a_args.lock.oh.data != call->a_owner)
44562306a36Sopenharmony_ci		kfree(call->a_args.lock.oh.data);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	locks_release_private(&call->a_args.lock.fl);
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci/*
45162306a36Sopenharmony_ci * Deferred lock request handling for non-blocking lock
45262306a36Sopenharmony_ci */
45362306a36Sopenharmony_cistatic __be32
45462306a36Sopenharmony_cinlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	__be32 status = nlm_lck_denied_nolocks;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	block->b_flags |= B_QUEUED;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	nlmsvc_insert_block(block, NLM_TIMEOUT);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	block->b_cache_req = &rqstp->rq_chandle;
46362306a36Sopenharmony_ci	if (rqstp->rq_chandle.defer) {
46462306a36Sopenharmony_ci		block->b_deferred_req =
46562306a36Sopenharmony_ci			rqstp->rq_chandle.defer(block->b_cache_req);
46662306a36Sopenharmony_ci		if (block->b_deferred_req != NULL)
46762306a36Sopenharmony_ci			status = nlm_drop_reply;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci	dprintk("lockd: nlmsvc_defer_lock_rqst block %p flags %d status %d\n",
47062306a36Sopenharmony_ci		block, block->b_flags, ntohl(status));
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	return status;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci/*
47662306a36Sopenharmony_ci * Attempt to establish a lock, and if it can't be granted, block it
47762306a36Sopenharmony_ci * if required.
47862306a36Sopenharmony_ci */
47962306a36Sopenharmony_ci__be32
48062306a36Sopenharmony_cinlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
48162306a36Sopenharmony_ci	    struct nlm_host *host, struct nlm_lock *lock, int wait,
48262306a36Sopenharmony_ci	    struct nlm_cookie *cookie, int reclaim)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
48562306a36Sopenharmony_ci	struct inode		*inode = nlmsvc_file_inode(file);
48662306a36Sopenharmony_ci#endif
48762306a36Sopenharmony_ci	struct nlm_block	*block = NULL;
48862306a36Sopenharmony_ci	int			error;
48962306a36Sopenharmony_ci	int			mode;
49062306a36Sopenharmony_ci	int			async_block = 0;
49162306a36Sopenharmony_ci	__be32			ret;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n",
49462306a36Sopenharmony_ci				inode->i_sb->s_id, inode->i_ino,
49562306a36Sopenharmony_ci				lock->fl.fl_type, lock->fl.fl_pid,
49662306a36Sopenharmony_ci				(long long)lock->fl.fl_start,
49762306a36Sopenharmony_ci				(long long)lock->fl.fl_end,
49862306a36Sopenharmony_ci				wait);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (nlmsvc_file_file(file)->f_op->lock) {
50162306a36Sopenharmony_ci		async_block = wait;
50262306a36Sopenharmony_ci		wait = 0;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/* Lock file against concurrent access */
50662306a36Sopenharmony_ci	mutex_lock(&file->f_mutex);
50762306a36Sopenharmony_ci	/* Get existing block (in case client is busy-waiting)
50862306a36Sopenharmony_ci	 * or create new block
50962306a36Sopenharmony_ci	 */
51062306a36Sopenharmony_ci	block = nlmsvc_lookup_block(file, lock);
51162306a36Sopenharmony_ci	if (block == NULL) {
51262306a36Sopenharmony_ci		block = nlmsvc_create_block(rqstp, host, file, lock, cookie);
51362306a36Sopenharmony_ci		ret = nlm_lck_denied_nolocks;
51462306a36Sopenharmony_ci		if (block == NULL)
51562306a36Sopenharmony_ci			goto out;
51662306a36Sopenharmony_ci		lock = &block->b_call->a_args.lock;
51762306a36Sopenharmony_ci	} else
51862306a36Sopenharmony_ci		lock->fl.fl_flags &= ~FL_SLEEP;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (block->b_flags & B_QUEUED) {
52162306a36Sopenharmony_ci		dprintk("lockd: nlmsvc_lock deferred block %p flags %d\n",
52262306a36Sopenharmony_ci							block, block->b_flags);
52362306a36Sopenharmony_ci		if (block->b_granted) {
52462306a36Sopenharmony_ci			nlmsvc_unlink_block(block);
52562306a36Sopenharmony_ci			ret = nlm_granted;
52662306a36Sopenharmony_ci			goto out;
52762306a36Sopenharmony_ci		}
52862306a36Sopenharmony_ci		if (block->b_flags & B_TIMED_OUT) {
52962306a36Sopenharmony_ci			nlmsvc_unlink_block(block);
53062306a36Sopenharmony_ci			ret = nlm_lck_denied;
53162306a36Sopenharmony_ci			goto out;
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci		ret = nlm_drop_reply;
53462306a36Sopenharmony_ci		goto out;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (locks_in_grace(SVC_NET(rqstp)) && !reclaim) {
53862306a36Sopenharmony_ci		ret = nlm_lck_denied_grace_period;
53962306a36Sopenharmony_ci		goto out;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci	if (reclaim && !locks_in_grace(SVC_NET(rqstp))) {
54262306a36Sopenharmony_ci		ret = nlm_lck_denied_grace_period;
54362306a36Sopenharmony_ci		goto out;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (!wait)
54762306a36Sopenharmony_ci		lock->fl.fl_flags &= ~FL_SLEEP;
54862306a36Sopenharmony_ci	mode = lock_to_openmode(&lock->fl);
54962306a36Sopenharmony_ci	error = vfs_lock_file(file->f_file[mode], F_SETLK, &lock->fl, NULL);
55062306a36Sopenharmony_ci	lock->fl.fl_flags &= ~FL_SLEEP;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	dprintk("lockd: vfs_lock_file returned %d\n", error);
55362306a36Sopenharmony_ci	switch (error) {
55462306a36Sopenharmony_ci		case 0:
55562306a36Sopenharmony_ci			ret = nlm_granted;
55662306a36Sopenharmony_ci			goto out;
55762306a36Sopenharmony_ci		case -EAGAIN:
55862306a36Sopenharmony_ci			/*
55962306a36Sopenharmony_ci			 * If this is a blocking request for an
56062306a36Sopenharmony_ci			 * already pending lock request then we need
56162306a36Sopenharmony_ci			 * to put it back on lockd's block list
56262306a36Sopenharmony_ci			 */
56362306a36Sopenharmony_ci			if (wait)
56462306a36Sopenharmony_ci				break;
56562306a36Sopenharmony_ci			ret = async_block ? nlm_lck_blocked : nlm_lck_denied;
56662306a36Sopenharmony_ci			goto out;
56762306a36Sopenharmony_ci		case FILE_LOCK_DEFERRED:
56862306a36Sopenharmony_ci			if (wait)
56962306a36Sopenharmony_ci				break;
57062306a36Sopenharmony_ci			/* Filesystem lock operation is in progress
57162306a36Sopenharmony_ci			   Add it to the queue waiting for callback */
57262306a36Sopenharmony_ci			ret = nlmsvc_defer_lock_rqst(rqstp, block);
57362306a36Sopenharmony_ci			goto out;
57462306a36Sopenharmony_ci		case -EDEADLK:
57562306a36Sopenharmony_ci			ret = nlm_deadlock;
57662306a36Sopenharmony_ci			goto out;
57762306a36Sopenharmony_ci		default:			/* includes ENOLCK */
57862306a36Sopenharmony_ci			ret = nlm_lck_denied_nolocks;
57962306a36Sopenharmony_ci			goto out;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	ret = nlm_lck_blocked;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* Append to list of blocked */
58562306a36Sopenharmony_ci	nlmsvc_insert_block(block, NLM_NEVER);
58662306a36Sopenharmony_ciout:
58762306a36Sopenharmony_ci	mutex_unlock(&file->f_mutex);
58862306a36Sopenharmony_ci	nlmsvc_release_block(block);
58962306a36Sopenharmony_ci	dprintk("lockd: nlmsvc_lock returned %u\n", ret);
59062306a36Sopenharmony_ci	return ret;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci/*
59462306a36Sopenharmony_ci * Test for presence of a conflicting lock.
59562306a36Sopenharmony_ci */
59662306a36Sopenharmony_ci__be32
59762306a36Sopenharmony_cinlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
59862306a36Sopenharmony_ci		struct nlm_host *host, struct nlm_lock *lock,
59962306a36Sopenharmony_ci		struct nlm_lock *conflock, struct nlm_cookie *cookie)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	int			error;
60262306a36Sopenharmony_ci	int			mode;
60362306a36Sopenharmony_ci	__be32			ret;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
60662306a36Sopenharmony_ci				nlmsvc_file_inode(file)->i_sb->s_id,
60762306a36Sopenharmony_ci				nlmsvc_file_inode(file)->i_ino,
60862306a36Sopenharmony_ci				lock->fl.fl_type,
60962306a36Sopenharmony_ci				(long long)lock->fl.fl_start,
61062306a36Sopenharmony_ci				(long long)lock->fl.fl_end);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (locks_in_grace(SVC_NET(rqstp))) {
61362306a36Sopenharmony_ci		ret = nlm_lck_denied_grace_period;
61462306a36Sopenharmony_ci		goto out;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	mode = lock_to_openmode(&lock->fl);
61862306a36Sopenharmony_ci	error = vfs_test_lock(file->f_file[mode], &lock->fl);
61962306a36Sopenharmony_ci	if (error) {
62062306a36Sopenharmony_ci		/* We can't currently deal with deferred test requests */
62162306a36Sopenharmony_ci		if (error == FILE_LOCK_DEFERRED)
62262306a36Sopenharmony_ci			WARN_ON_ONCE(1);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		ret = nlm_lck_denied_nolocks;
62562306a36Sopenharmony_ci		goto out;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (lock->fl.fl_type == F_UNLCK) {
62962306a36Sopenharmony_ci		ret = nlm_granted;
63062306a36Sopenharmony_ci		goto out;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
63462306a36Sopenharmony_ci		lock->fl.fl_type, (long long)lock->fl.fl_start,
63562306a36Sopenharmony_ci		(long long)lock->fl.fl_end);
63662306a36Sopenharmony_ci	conflock->caller = "somehost";	/* FIXME */
63762306a36Sopenharmony_ci	conflock->len = strlen(conflock->caller);
63862306a36Sopenharmony_ci	conflock->oh.len = 0;		/* don't return OH info */
63962306a36Sopenharmony_ci	conflock->svid = lock->fl.fl_pid;
64062306a36Sopenharmony_ci	conflock->fl.fl_type = lock->fl.fl_type;
64162306a36Sopenharmony_ci	conflock->fl.fl_start = lock->fl.fl_start;
64262306a36Sopenharmony_ci	conflock->fl.fl_end = lock->fl.fl_end;
64362306a36Sopenharmony_ci	locks_release_private(&lock->fl);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	ret = nlm_lck_denied;
64662306a36Sopenharmony_ciout:
64762306a36Sopenharmony_ci	return ret;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci/*
65162306a36Sopenharmony_ci * Remove a lock.
65262306a36Sopenharmony_ci * This implies a CANCEL call: We send a GRANT_MSG, the client replies
65362306a36Sopenharmony_ci * with a GRANT_RES call which gets lost, and calls UNLOCK immediately
65462306a36Sopenharmony_ci * afterwards. In this case the block will still be there, and hence
65562306a36Sopenharmony_ci * must be removed.
65662306a36Sopenharmony_ci */
65762306a36Sopenharmony_ci__be32
65862306a36Sopenharmony_cinlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	int	error = 0;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)\n",
66362306a36Sopenharmony_ci				nlmsvc_file_inode(file)->i_sb->s_id,
66462306a36Sopenharmony_ci				nlmsvc_file_inode(file)->i_ino,
66562306a36Sopenharmony_ci				lock->fl.fl_pid,
66662306a36Sopenharmony_ci				(long long)lock->fl.fl_start,
66762306a36Sopenharmony_ci				(long long)lock->fl.fl_end);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	/* First, cancel any lock that might be there */
67062306a36Sopenharmony_ci	nlmsvc_cancel_blocked(net, file, lock);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	lock->fl.fl_type = F_UNLCK;
67362306a36Sopenharmony_ci	lock->fl.fl_file = file->f_file[O_RDONLY];
67462306a36Sopenharmony_ci	if (lock->fl.fl_file)
67562306a36Sopenharmony_ci		error = vfs_lock_file(lock->fl.fl_file, F_SETLK,
67662306a36Sopenharmony_ci					&lock->fl, NULL);
67762306a36Sopenharmony_ci	lock->fl.fl_file = file->f_file[O_WRONLY];
67862306a36Sopenharmony_ci	if (lock->fl.fl_file)
67962306a36Sopenharmony_ci		error |= vfs_lock_file(lock->fl.fl_file, F_SETLK,
68062306a36Sopenharmony_ci					&lock->fl, NULL);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci/*
68662306a36Sopenharmony_ci * Cancel a previously blocked request.
68762306a36Sopenharmony_ci *
68862306a36Sopenharmony_ci * A cancel request always overrides any grant that may currently
68962306a36Sopenharmony_ci * be in progress.
69062306a36Sopenharmony_ci * The calling procedure must check whether the file can be closed.
69162306a36Sopenharmony_ci */
69262306a36Sopenharmony_ci__be32
69362306a36Sopenharmony_cinlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	struct nlm_block	*block;
69662306a36Sopenharmony_ci	int status = 0;
69762306a36Sopenharmony_ci	int mode;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n",
70062306a36Sopenharmony_ci				nlmsvc_file_inode(file)->i_sb->s_id,
70162306a36Sopenharmony_ci				nlmsvc_file_inode(file)->i_ino,
70262306a36Sopenharmony_ci				lock->fl.fl_pid,
70362306a36Sopenharmony_ci				(long long)lock->fl.fl_start,
70462306a36Sopenharmony_ci				(long long)lock->fl.fl_end);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (locks_in_grace(net))
70762306a36Sopenharmony_ci		return nlm_lck_denied_grace_period;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	mutex_lock(&file->f_mutex);
71062306a36Sopenharmony_ci	block = nlmsvc_lookup_block(file, lock);
71162306a36Sopenharmony_ci	mutex_unlock(&file->f_mutex);
71262306a36Sopenharmony_ci	if (block != NULL) {
71362306a36Sopenharmony_ci		struct file_lock *fl = &block->b_call->a_args.lock.fl;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		mode = lock_to_openmode(fl);
71662306a36Sopenharmony_ci		vfs_cancel_lock(block->b_file->f_file[mode], fl);
71762306a36Sopenharmony_ci		status = nlmsvc_unlink_block(block);
71862306a36Sopenharmony_ci		nlmsvc_release_block(block);
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci	return status ? nlm_lck_denied : nlm_granted;
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci/*
72462306a36Sopenharmony_ci * This is a callback from the filesystem for VFS file lock requests.
72562306a36Sopenharmony_ci * It will be used if lm_grant is defined and the filesystem can not
72662306a36Sopenharmony_ci * respond to the request immediately.
72762306a36Sopenharmony_ci * For SETLK or SETLKW request it will get the local posix lock.
72862306a36Sopenharmony_ci * In all cases it will move the block to the head of nlm_blocked q where
72962306a36Sopenharmony_ci * nlmsvc_retry_blocked() can send back a reply for SETLKW or revisit the
73062306a36Sopenharmony_ci * deferred rpc for GETLK and SETLK.
73162306a36Sopenharmony_ci */
73262306a36Sopenharmony_cistatic void
73362306a36Sopenharmony_cinlmsvc_update_deferred_block(struct nlm_block *block, int result)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	block->b_flags |= B_GOT_CALLBACK;
73662306a36Sopenharmony_ci	if (result == 0)
73762306a36Sopenharmony_ci		block->b_granted = 1;
73862306a36Sopenharmony_ci	else
73962306a36Sopenharmony_ci		block->b_flags |= B_TIMED_OUT;
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_cistatic int nlmsvc_grant_deferred(struct file_lock *fl, int result)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct nlm_block *block;
74562306a36Sopenharmony_ci	int rc = -ENOENT;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
74862306a36Sopenharmony_ci	list_for_each_entry(block, &nlm_blocked, b_list) {
74962306a36Sopenharmony_ci		if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
75062306a36Sopenharmony_ci			dprintk("lockd: nlmsvc_notify_blocked block %p flags %d\n",
75162306a36Sopenharmony_ci							block, block->b_flags);
75262306a36Sopenharmony_ci			if (block->b_flags & B_QUEUED) {
75362306a36Sopenharmony_ci				if (block->b_flags & B_TIMED_OUT) {
75462306a36Sopenharmony_ci					rc = -ENOLCK;
75562306a36Sopenharmony_ci					break;
75662306a36Sopenharmony_ci				}
75762306a36Sopenharmony_ci				nlmsvc_update_deferred_block(block, result);
75862306a36Sopenharmony_ci			} else if (result == 0)
75962306a36Sopenharmony_ci				block->b_granted = 1;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci			nlmsvc_insert_block_locked(block, 0);
76262306a36Sopenharmony_ci			svc_wake_up(block->b_daemon);
76362306a36Sopenharmony_ci			rc = 0;
76462306a36Sopenharmony_ci			break;
76562306a36Sopenharmony_ci		}
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
76862306a36Sopenharmony_ci	if (rc == -ENOENT)
76962306a36Sopenharmony_ci		printk(KERN_WARNING "lockd: grant for unknown block\n");
77062306a36Sopenharmony_ci	return rc;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci/*
77462306a36Sopenharmony_ci * Unblock a blocked lock request. This is a callback invoked from the
77562306a36Sopenharmony_ci * VFS layer when a lock on which we blocked is removed.
77662306a36Sopenharmony_ci *
77762306a36Sopenharmony_ci * This function doesn't grant the blocked lock instantly, but rather moves
77862306a36Sopenharmony_ci * the block to the head of nlm_blocked where it can be picked up by lockd.
77962306a36Sopenharmony_ci */
78062306a36Sopenharmony_cistatic void
78162306a36Sopenharmony_cinlmsvc_notify_blocked(struct file_lock *fl)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct nlm_block	*block;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	dprintk("lockd: VFS unblock notification for block %p\n", fl);
78662306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
78762306a36Sopenharmony_ci	list_for_each_entry(block, &nlm_blocked, b_list) {
78862306a36Sopenharmony_ci		if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
78962306a36Sopenharmony_ci			nlmsvc_insert_block_locked(block, 0);
79062306a36Sopenharmony_ci			spin_unlock(&nlm_blocked_lock);
79162306a36Sopenharmony_ci			svc_wake_up(block->b_daemon);
79262306a36Sopenharmony_ci			return;
79362306a36Sopenharmony_ci		}
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
79662306a36Sopenharmony_ci	printk(KERN_WARNING "lockd: notification for unknown block!\n");
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cistatic fl_owner_t nlmsvc_get_owner(fl_owner_t owner)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	return nlmsvc_get_lockowner(owner);
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic void nlmsvc_put_owner(fl_owner_t owner)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	nlmsvc_put_lockowner(owner);
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ciconst struct lock_manager_operations nlmsvc_lock_operations = {
81062306a36Sopenharmony_ci	.lm_notify = nlmsvc_notify_blocked,
81162306a36Sopenharmony_ci	.lm_grant = nlmsvc_grant_deferred,
81262306a36Sopenharmony_ci	.lm_get_owner = nlmsvc_get_owner,
81362306a36Sopenharmony_ci	.lm_put_owner = nlmsvc_put_owner,
81462306a36Sopenharmony_ci};
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci/*
81762306a36Sopenharmony_ci * Try to claim a lock that was previously blocked.
81862306a36Sopenharmony_ci *
81962306a36Sopenharmony_ci * Note that we use both the RPC_GRANTED_MSG call _and_ an async
82062306a36Sopenharmony_ci * RPC thread when notifying the client. This seems like overkill...
82162306a36Sopenharmony_ci * Here's why:
82262306a36Sopenharmony_ci *  -	we don't want to use a synchronous RPC thread, otherwise
82362306a36Sopenharmony_ci *	we might find ourselves hanging on a dead portmapper.
82462306a36Sopenharmony_ci *  -	Some lockd implementations (e.g. HP) don't react to
82562306a36Sopenharmony_ci *	RPC_GRANTED calls; they seem to insist on RPC_GRANTED_MSG calls.
82662306a36Sopenharmony_ci */
82762306a36Sopenharmony_cistatic void
82862306a36Sopenharmony_cinlmsvc_grant_blocked(struct nlm_block *block)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct nlm_file		*file = block->b_file;
83162306a36Sopenharmony_ci	struct nlm_lock		*lock = &block->b_call->a_args.lock;
83262306a36Sopenharmony_ci	int			mode;
83362306a36Sopenharmony_ci	int			error;
83462306a36Sopenharmony_ci	loff_t			fl_start, fl_end;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	dprintk("lockd: grant blocked lock %p\n", block);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	kref_get(&block->b_count);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	/* Unlink block request from list */
84162306a36Sopenharmony_ci	nlmsvc_unlink_block(block);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/* If b_granted is true this means we've been here before.
84462306a36Sopenharmony_ci	 * Just retry the grant callback, possibly refreshing the RPC
84562306a36Sopenharmony_ci	 * binding */
84662306a36Sopenharmony_ci	if (block->b_granted) {
84762306a36Sopenharmony_ci		nlm_rebind_host(block->b_host);
84862306a36Sopenharmony_ci		goto callback;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	/* Try the lock operation again */
85262306a36Sopenharmony_ci	/* vfs_lock_file() can mangle fl_start and fl_end, but we need
85362306a36Sopenharmony_ci	 * them unchanged for the GRANT_MSG
85462306a36Sopenharmony_ci	 */
85562306a36Sopenharmony_ci	lock->fl.fl_flags |= FL_SLEEP;
85662306a36Sopenharmony_ci	fl_start = lock->fl.fl_start;
85762306a36Sopenharmony_ci	fl_end = lock->fl.fl_end;
85862306a36Sopenharmony_ci	mode = lock_to_openmode(&lock->fl);
85962306a36Sopenharmony_ci	error = vfs_lock_file(file->f_file[mode], F_SETLK, &lock->fl, NULL);
86062306a36Sopenharmony_ci	lock->fl.fl_flags &= ~FL_SLEEP;
86162306a36Sopenharmony_ci	lock->fl.fl_start = fl_start;
86262306a36Sopenharmony_ci	lock->fl.fl_end = fl_end;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	switch (error) {
86562306a36Sopenharmony_ci	case 0:
86662306a36Sopenharmony_ci		break;
86762306a36Sopenharmony_ci	case FILE_LOCK_DEFERRED:
86862306a36Sopenharmony_ci		dprintk("lockd: lock still blocked error %d\n", error);
86962306a36Sopenharmony_ci		nlmsvc_insert_block(block, NLM_NEVER);
87062306a36Sopenharmony_ci		nlmsvc_release_block(block);
87162306a36Sopenharmony_ci		return;
87262306a36Sopenharmony_ci	default:
87362306a36Sopenharmony_ci		printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
87462306a36Sopenharmony_ci				-error, __func__);
87562306a36Sopenharmony_ci		nlmsvc_insert_block(block, 10 * HZ);
87662306a36Sopenharmony_ci		nlmsvc_release_block(block);
87762306a36Sopenharmony_ci		return;
87862306a36Sopenharmony_ci	}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cicallback:
88162306a36Sopenharmony_ci	/* Lock was granted by VFS. */
88262306a36Sopenharmony_ci	dprintk("lockd: GRANTing blocked lock.\n");
88362306a36Sopenharmony_ci	block->b_granted = 1;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	/* keep block on the list, but don't reattempt until the RPC
88662306a36Sopenharmony_ci	 * completes or the submission fails
88762306a36Sopenharmony_ci	 */
88862306a36Sopenharmony_ci	nlmsvc_insert_block(block, NLM_NEVER);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	/* Call the client -- use a soft RPC task since nlmsvc_retry_blocked
89162306a36Sopenharmony_ci	 * will queue up a new one if this one times out
89262306a36Sopenharmony_ci	 */
89362306a36Sopenharmony_ci	error = nlm_async_call(block->b_call, NLMPROC_GRANTED_MSG,
89462306a36Sopenharmony_ci				&nlmsvc_grant_ops);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	/* RPC submission failed, wait a bit and retry */
89762306a36Sopenharmony_ci	if (error < 0)
89862306a36Sopenharmony_ci		nlmsvc_insert_block(block, 10 * HZ);
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci/*
90262306a36Sopenharmony_ci * This is the callback from the RPC layer when the NLM_GRANTED_MSG
90362306a36Sopenharmony_ci * RPC call has succeeded or timed out.
90462306a36Sopenharmony_ci * Like all RPC callbacks, it is invoked by the rpciod process, so it
90562306a36Sopenharmony_ci * better not sleep. Therefore, we put the blocked lock on the nlm_blocked
90662306a36Sopenharmony_ci * chain once more in order to have it removed by lockd itself (which can
90762306a36Sopenharmony_ci * then sleep on the file semaphore without disrupting e.g. the nfs client).
90862306a36Sopenharmony_ci */
90962306a36Sopenharmony_cistatic void nlmsvc_grant_callback(struct rpc_task *task, void *data)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct nlm_rqst		*call = data;
91262306a36Sopenharmony_ci	struct nlm_block	*block = call->a_block;
91362306a36Sopenharmony_ci	unsigned long		timeout;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	dprintk("lockd: GRANT_MSG RPC callback\n");
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
91862306a36Sopenharmony_ci	/* if the block is not on a list at this point then it has
91962306a36Sopenharmony_ci	 * been invalidated. Don't try to requeue it.
92062306a36Sopenharmony_ci	 *
92162306a36Sopenharmony_ci	 * FIXME: it's possible that the block is removed from the list
92262306a36Sopenharmony_ci	 * after this check but before the nlmsvc_insert_block. In that
92362306a36Sopenharmony_ci	 * case it will be added back. Perhaps we need better locking
92462306a36Sopenharmony_ci	 * for nlm_blocked?
92562306a36Sopenharmony_ci	 */
92662306a36Sopenharmony_ci	if (list_empty(&block->b_list))
92762306a36Sopenharmony_ci		goto out;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	/* Technically, we should down the file semaphore here. Since we
93062306a36Sopenharmony_ci	 * move the block towards the head of the queue only, no harm
93162306a36Sopenharmony_ci	 * can be done, though. */
93262306a36Sopenharmony_ci	if (task->tk_status < 0) {
93362306a36Sopenharmony_ci		/* RPC error: Re-insert for retransmission */
93462306a36Sopenharmony_ci		timeout = 10 * HZ;
93562306a36Sopenharmony_ci	} else {
93662306a36Sopenharmony_ci		/* Call was successful, now wait for client callback */
93762306a36Sopenharmony_ci		timeout = 60 * HZ;
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci	nlmsvc_insert_block_locked(block, timeout);
94062306a36Sopenharmony_ci	svc_wake_up(block->b_daemon);
94162306a36Sopenharmony_ciout:
94262306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci/*
94662306a36Sopenharmony_ci * FIXME: nlmsvc_release_block() grabs a mutex.  This is not allowed for an
94762306a36Sopenharmony_ci * .rpc_release rpc_call_op
94862306a36Sopenharmony_ci */
94962306a36Sopenharmony_cistatic void nlmsvc_grant_release(void *data)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	struct nlm_rqst		*call = data;
95262306a36Sopenharmony_ci	nlmsvc_release_block(call->a_block);
95362306a36Sopenharmony_ci}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_cistatic const struct rpc_call_ops nlmsvc_grant_ops = {
95662306a36Sopenharmony_ci	.rpc_call_done = nlmsvc_grant_callback,
95762306a36Sopenharmony_ci	.rpc_release = nlmsvc_grant_release,
95862306a36Sopenharmony_ci};
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci/*
96162306a36Sopenharmony_ci * We received a GRANT_RES callback. Try to find the corresponding
96262306a36Sopenharmony_ci * block.
96362306a36Sopenharmony_ci */
96462306a36Sopenharmony_civoid
96562306a36Sopenharmony_cinlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci	struct nlm_block	*block;
96862306a36Sopenharmony_ci	struct file_lock	*fl;
96962306a36Sopenharmony_ci	int			error;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	dprintk("grant_reply: looking for cookie %x, s=%d \n",
97262306a36Sopenharmony_ci		*(unsigned int *)(cookie->data), status);
97362306a36Sopenharmony_ci	if (!(block = nlmsvc_find_block(cookie)))
97462306a36Sopenharmony_ci		return;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	switch (status) {
97762306a36Sopenharmony_ci	case nlm_lck_denied_grace_period:
97862306a36Sopenharmony_ci		/* Try again in a couple of seconds */
97962306a36Sopenharmony_ci		nlmsvc_insert_block(block, 10 * HZ);
98062306a36Sopenharmony_ci		break;
98162306a36Sopenharmony_ci	case nlm_lck_denied:
98262306a36Sopenharmony_ci		/* Client doesn't want it, just unlock it */
98362306a36Sopenharmony_ci		nlmsvc_unlink_block(block);
98462306a36Sopenharmony_ci		fl = &block->b_call->a_args.lock.fl;
98562306a36Sopenharmony_ci		fl->fl_type = F_UNLCK;
98662306a36Sopenharmony_ci		error = vfs_lock_file(fl->fl_file, F_SETLK, fl, NULL);
98762306a36Sopenharmony_ci		if (error)
98862306a36Sopenharmony_ci			pr_warn("lockd: unable to unlock lock rejected by client!\n");
98962306a36Sopenharmony_ci		break;
99062306a36Sopenharmony_ci	default:
99162306a36Sopenharmony_ci		/*
99262306a36Sopenharmony_ci		 * Either it was accepted or the status makes no sense
99362306a36Sopenharmony_ci		 * just unlink it either way.
99462306a36Sopenharmony_ci		 */
99562306a36Sopenharmony_ci		nlmsvc_unlink_block(block);
99662306a36Sopenharmony_ci	}
99762306a36Sopenharmony_ci	nlmsvc_release_block(block);
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci/* Helper function to handle retry of a deferred block.
100162306a36Sopenharmony_ci * If it is a blocking lock, call grant_blocked.
100262306a36Sopenharmony_ci * For a non-blocking lock or test lock, revisit the request.
100362306a36Sopenharmony_ci */
100462306a36Sopenharmony_cistatic void
100562306a36Sopenharmony_ciretry_deferred_block(struct nlm_block *block)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	if (!(block->b_flags & B_GOT_CALLBACK))
100862306a36Sopenharmony_ci		block->b_flags |= B_TIMED_OUT;
100962306a36Sopenharmony_ci	nlmsvc_insert_block(block, NLM_TIMEOUT);
101062306a36Sopenharmony_ci	dprintk("revisit block %p flags %d\n",	block, block->b_flags);
101162306a36Sopenharmony_ci	if (block->b_deferred_req) {
101262306a36Sopenharmony_ci		block->b_deferred_req->revisit(block->b_deferred_req, 0);
101362306a36Sopenharmony_ci		block->b_deferred_req = NULL;
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci/*
101862306a36Sopenharmony_ci * Retry all blocked locks that have been notified. This is where lockd
101962306a36Sopenharmony_ci * picks up locks that can be granted, or grant notifications that must
102062306a36Sopenharmony_ci * be retransmitted.
102162306a36Sopenharmony_ci */
102262306a36Sopenharmony_civoid
102362306a36Sopenharmony_cinlmsvc_retry_blocked(void)
102462306a36Sopenharmony_ci{
102562306a36Sopenharmony_ci	unsigned long	timeout = MAX_SCHEDULE_TIMEOUT;
102662306a36Sopenharmony_ci	struct nlm_block *block;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
102962306a36Sopenharmony_ci	while (!list_empty(&nlm_blocked) && !kthread_should_stop()) {
103062306a36Sopenharmony_ci		block = list_entry(nlm_blocked.next, struct nlm_block, b_list);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		if (block->b_when == NLM_NEVER)
103362306a36Sopenharmony_ci			break;
103462306a36Sopenharmony_ci		if (time_after(block->b_when, jiffies)) {
103562306a36Sopenharmony_ci			timeout = block->b_when - jiffies;
103662306a36Sopenharmony_ci			break;
103762306a36Sopenharmony_ci		}
103862306a36Sopenharmony_ci		spin_unlock(&nlm_blocked_lock);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci		dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
104162306a36Sopenharmony_ci			block, block->b_when);
104262306a36Sopenharmony_ci		if (block->b_flags & B_QUEUED) {
104362306a36Sopenharmony_ci			dprintk("nlmsvc_retry_blocked delete block (%p, granted=%d, flags=%d)\n",
104462306a36Sopenharmony_ci				block, block->b_granted, block->b_flags);
104562306a36Sopenharmony_ci			retry_deferred_block(block);
104662306a36Sopenharmony_ci		} else
104762306a36Sopenharmony_ci			nlmsvc_grant_blocked(block);
104862306a36Sopenharmony_ci		spin_lock(&nlm_blocked_lock);
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (timeout < MAX_SCHEDULE_TIMEOUT)
105362306a36Sopenharmony_ci		mod_timer(&nlmsvc_retry, jiffies + timeout);
105462306a36Sopenharmony_ci}
1055