162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/fs/lockd/clntlock.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Lock handling for the client side NLM implementation
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/time.h>
1462306a36Sopenharmony_ci#include <linux/nfs_fs.h>
1562306a36Sopenharmony_ci#include <linux/sunrpc/addr.h>
1662306a36Sopenharmony_ci#include <linux/sunrpc/svc.h>
1762306a36Sopenharmony_ci#include <linux/sunrpc/svc_xprt.h>
1862306a36Sopenharmony_ci#include <linux/lockd/lockd.h>
1962306a36Sopenharmony_ci#include <linux/kthread.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "trace.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define NLMDBG_FACILITY		NLMDBG_CLIENT
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * Local function prototypes
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistatic int			reclaimer(void *ptr);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * The following functions handle blocking and granting from the
3262306a36Sopenharmony_ci * client perspective.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic LIST_HEAD(nlm_blocked);
3662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nlm_blocked_lock);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/**
3962306a36Sopenharmony_ci * nlmclnt_init - Set up per-NFS mount point lockd data structures
4062306a36Sopenharmony_ci * @nlm_init: pointer to arguments structure
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * Returns pointer to an appropriate nlm_host struct,
4362306a36Sopenharmony_ci * or an ERR_PTR value.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_cistruct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct nlm_host *host;
4862306a36Sopenharmony_ci	u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4;
4962306a36Sopenharmony_ci	int status;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	status = lockd_up(nlm_init->net, nlm_init->cred);
5262306a36Sopenharmony_ci	if (status < 0)
5362306a36Sopenharmony_ci		return ERR_PTR(status);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen,
5662306a36Sopenharmony_ci				   nlm_init->protocol, nlm_version,
5762306a36Sopenharmony_ci				   nlm_init->hostname, nlm_init->noresvport,
5862306a36Sopenharmony_ci				   nlm_init->net, nlm_init->cred);
5962306a36Sopenharmony_ci	if (host == NULL)
6062306a36Sopenharmony_ci		goto out_nohost;
6162306a36Sopenharmony_ci	if (host->h_rpcclnt == NULL && nlm_bind_host(host) == NULL)
6262306a36Sopenharmony_ci		goto out_nobind;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	host->h_nlmclnt_ops = nlm_init->nlmclnt_ops;
6562306a36Sopenharmony_ci	return host;
6662306a36Sopenharmony_ciout_nobind:
6762306a36Sopenharmony_ci	nlmclnt_release_host(host);
6862306a36Sopenharmony_ciout_nohost:
6962306a36Sopenharmony_ci	lockd_down(nlm_init->net);
7062306a36Sopenharmony_ci	return ERR_PTR(-ENOLCK);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmclnt_init);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/**
7562306a36Sopenharmony_ci * nlmclnt_done - Release resources allocated by nlmclnt_init()
7662306a36Sopenharmony_ci * @host: nlm_host structure reserved by nlmclnt_init()
7762306a36Sopenharmony_ci *
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_civoid nlmclnt_done(struct nlm_host *host)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct net *net = host->net;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	nlmclnt_release_host(host);
8462306a36Sopenharmony_ci	lockd_down(net);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmclnt_done);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_civoid nlmclnt_prepare_block(struct nlm_wait *block, struct nlm_host *host, struct file_lock *fl)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	block->b_host = host;
9162306a36Sopenharmony_ci	block->b_lock = fl;
9262306a36Sopenharmony_ci	init_waitqueue_head(&block->b_wait);
9362306a36Sopenharmony_ci	block->b_status = nlm_lck_blocked;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistruct rpc_clnt *nlmclnt_rpc_clnt(struct nlm_host *host)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	return host->h_rpcclnt;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmclnt_rpc_clnt);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/*
10362306a36Sopenharmony_ci * Queue up a lock for blocking so that the GRANTED request can see it
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_civoid nlmclnt_queue_block(struct nlm_wait *block)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
10862306a36Sopenharmony_ci	list_add(&block->b_list, &nlm_blocked);
10962306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * Dequeue the block and return its final status
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ci__be32 nlmclnt_dequeue_block(struct nlm_wait *block)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	__be32 status;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
12062306a36Sopenharmony_ci	list_del(&block->b_list);
12162306a36Sopenharmony_ci	status = block->b_status;
12262306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
12362306a36Sopenharmony_ci	return status;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/*
12762306a36Sopenharmony_ci * Block on a lock
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_ciint nlmclnt_wait(struct nlm_wait *block, struct nlm_rqst *req, long timeout)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	long ret;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* A borken server might ask us to block even if we didn't
13462306a36Sopenharmony_ci	 * request it. Just say no!
13562306a36Sopenharmony_ci	 */
13662306a36Sopenharmony_ci	if (block == NULL)
13762306a36Sopenharmony_ci		return -EAGAIN;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* Go to sleep waiting for GRANT callback. Some servers seem
14062306a36Sopenharmony_ci	 * to lose callbacks, however, so we're going to poll from
14162306a36Sopenharmony_ci	 * time to time just to make sure.
14262306a36Sopenharmony_ci	 *
14362306a36Sopenharmony_ci	 * For now, the retry frequency is pretty high; normally
14462306a36Sopenharmony_ci	 * a 1 minute timeout would do. See the comment before
14562306a36Sopenharmony_ci	 * nlmclnt_lock for an explanation.
14662306a36Sopenharmony_ci	 */
14762306a36Sopenharmony_ci	ret = wait_event_interruptible_timeout(block->b_wait,
14862306a36Sopenharmony_ci			block->b_status != nlm_lck_blocked,
14962306a36Sopenharmony_ci			timeout);
15062306a36Sopenharmony_ci	if (ret < 0)
15162306a36Sopenharmony_ci		return -ERESTARTSYS;
15262306a36Sopenharmony_ci	/* Reset the lock status after a server reboot so we resend */
15362306a36Sopenharmony_ci	if (block->b_status == nlm_lck_denied_grace_period)
15462306a36Sopenharmony_ci		block->b_status = nlm_lck_blocked;
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/*
15962306a36Sopenharmony_ci * The server lockd has called us back to tell us the lock was granted
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_ci__be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	const struct file_lock *fl = &lock->fl;
16462306a36Sopenharmony_ci	const struct nfs_fh *fh = &lock->fh;
16562306a36Sopenharmony_ci	struct nlm_wait	*block;
16662306a36Sopenharmony_ci	__be32 res = nlm_lck_denied;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * Look up blocked request based on arguments.
17062306a36Sopenharmony_ci	 * Warning: must not use cookie to match it!
17162306a36Sopenharmony_ci	 */
17262306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
17362306a36Sopenharmony_ci	list_for_each_entry(block, &nlm_blocked, b_list) {
17462306a36Sopenharmony_ci		struct file_lock *fl_blocked = block->b_lock;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		if (fl_blocked->fl_start != fl->fl_start)
17762306a36Sopenharmony_ci			continue;
17862306a36Sopenharmony_ci		if (fl_blocked->fl_end != fl->fl_end)
17962306a36Sopenharmony_ci			continue;
18062306a36Sopenharmony_ci		/*
18162306a36Sopenharmony_ci		 * Careful! The NLM server will return the 32-bit "pid" that
18262306a36Sopenharmony_ci		 * we put on the wire: in this case the lockowner "pid".
18362306a36Sopenharmony_ci		 */
18462306a36Sopenharmony_ci		if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid)
18562306a36Sopenharmony_ci			continue;
18662306a36Sopenharmony_ci		if (!rpc_cmp_addr(nlm_addr(block->b_host), addr))
18762306a36Sopenharmony_ci			continue;
18862306a36Sopenharmony_ci		if (nfs_compare_fh(NFS_FH(file_inode(fl_blocked->fl_file)), fh) != 0)
18962306a36Sopenharmony_ci			continue;
19062306a36Sopenharmony_ci		/* Alright, we found a lock. Set the return status
19162306a36Sopenharmony_ci		 * and wake up the caller
19262306a36Sopenharmony_ci		 */
19362306a36Sopenharmony_ci		block->b_status = nlm_granted;
19462306a36Sopenharmony_ci		wake_up(&block->b_wait);
19562306a36Sopenharmony_ci		res = nlm_granted;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
19862306a36Sopenharmony_ci	trace_nlmclnt_grant(lock, addr, svc_addr_len(addr), res);
19962306a36Sopenharmony_ci	return res;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/*
20362306a36Sopenharmony_ci * The following procedures deal with the recovery of locks after a
20462306a36Sopenharmony_ci * server crash.
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/*
20862306a36Sopenharmony_ci * Reclaim all locks on server host. We do this by spawning a separate
20962306a36Sopenharmony_ci * reclaimer thread.
21062306a36Sopenharmony_ci */
21162306a36Sopenharmony_civoid
21262306a36Sopenharmony_cinlmclnt_recovery(struct nlm_host *host)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct task_struct *task;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (!host->h_reclaiming++) {
21762306a36Sopenharmony_ci		nlm_get_host(host);
21862306a36Sopenharmony_ci		task = kthread_run(reclaimer, host, "%s-reclaim", host->h_name);
21962306a36Sopenharmony_ci		if (IS_ERR(task))
22062306a36Sopenharmony_ci			printk(KERN_ERR "lockd: unable to spawn reclaimer "
22162306a36Sopenharmony_ci				"thread. Locks for %s won't be reclaimed! "
22262306a36Sopenharmony_ci				"(%ld)\n", host->h_name, PTR_ERR(task));
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int
22762306a36Sopenharmony_cireclaimer(void *ptr)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct nlm_host	  *host = (struct nlm_host *) ptr;
23062306a36Sopenharmony_ci	struct nlm_wait	  *block;
23162306a36Sopenharmony_ci	struct nlm_rqst   *req;
23262306a36Sopenharmony_ci	struct file_lock *fl, *next;
23362306a36Sopenharmony_ci	u32 nsmstate;
23462306a36Sopenharmony_ci	struct net *net = host->net;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	req = kmalloc(sizeof(*req), GFP_KERNEL);
23762306a36Sopenharmony_ci	if (!req)
23862306a36Sopenharmony_ci		return 0;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	allow_signal(SIGKILL);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	down_write(&host->h_rwsem);
24362306a36Sopenharmony_ci	lockd_up(net, NULL);	/* note: this cannot fail as lockd is already running */
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	dprintk("lockd: reclaiming locks for host %s\n", host->h_name);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cirestart:
24862306a36Sopenharmony_ci	nsmstate = host->h_nsmstate;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* Force a portmap getport - the peer's lockd will
25162306a36Sopenharmony_ci	 * most likely end up on a different port.
25262306a36Sopenharmony_ci	 */
25362306a36Sopenharmony_ci	host->h_nextrebind = jiffies;
25462306a36Sopenharmony_ci	nlm_rebind_host(host);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* First, reclaim all locks that have been granted. */
25762306a36Sopenharmony_ci	list_splice_init(&host->h_granted, &host->h_reclaim);
25862306a36Sopenharmony_ci	list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) {
25962306a36Sopenharmony_ci		list_del_init(&fl->fl_u.nfs_fl.list);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci		/*
26262306a36Sopenharmony_ci		 * sending this thread a SIGKILL will result in any unreclaimed
26362306a36Sopenharmony_ci		 * locks being removed from the h_granted list. This means that
26462306a36Sopenharmony_ci		 * the kernel will not attempt to reclaim them again if a new
26562306a36Sopenharmony_ci		 * reclaimer thread is spawned for this host.
26662306a36Sopenharmony_ci		 */
26762306a36Sopenharmony_ci		if (signalled())
26862306a36Sopenharmony_ci			continue;
26962306a36Sopenharmony_ci		if (nlmclnt_reclaim(host, fl, req) != 0)
27062306a36Sopenharmony_ci			continue;
27162306a36Sopenharmony_ci		list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted);
27262306a36Sopenharmony_ci		if (host->h_nsmstate != nsmstate) {
27362306a36Sopenharmony_ci			/* Argh! The server rebooted again! */
27462306a36Sopenharmony_ci			goto restart;
27562306a36Sopenharmony_ci		}
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	host->h_reclaiming = 0;
27962306a36Sopenharmony_ci	up_write(&host->h_rwsem);
28062306a36Sopenharmony_ci	dprintk("NLM: done reclaiming locks for host %s\n", host->h_name);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* Now, wake up all processes that sleep on a blocked lock */
28362306a36Sopenharmony_ci	spin_lock(&nlm_blocked_lock);
28462306a36Sopenharmony_ci	list_for_each_entry(block, &nlm_blocked, b_list) {
28562306a36Sopenharmony_ci		if (block->b_host == host) {
28662306a36Sopenharmony_ci			block->b_status = nlm_lck_denied_grace_period;
28762306a36Sopenharmony_ci			wake_up(&block->b_wait);
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci	spin_unlock(&nlm_blocked_lock);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* Release host handle after use */
29362306a36Sopenharmony_ci	nlmclnt_release_host(host);
29462306a36Sopenharmony_ci	lockd_down(net);
29562306a36Sopenharmony_ci	kfree(req);
29662306a36Sopenharmony_ci	return 0;
29762306a36Sopenharmony_ci}
298