162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/fs/lockd/clntproc.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * RPC procedures 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/slab.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <linux/errno.h>
1462306a36Sopenharmony_ci#include <linux/fs.h>
1562306a36Sopenharmony_ci#include <linux/filelock.h>
1662306a36Sopenharmony_ci#include <linux/nfs_fs.h>
1762306a36Sopenharmony_ci#include <linux/utsname.h>
1862306a36Sopenharmony_ci#include <linux/freezer.h>
1962306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h>
2062306a36Sopenharmony_ci#include <linux/sunrpc/svc.h>
2162306a36Sopenharmony_ci#include <linux/lockd/lockd.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "trace.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define NLMDBG_FACILITY		NLMDBG_CLIENT
2662306a36Sopenharmony_ci#define NLMCLNT_GRACE_WAIT	(5*HZ)
2762306a36Sopenharmony_ci#define NLMCLNT_POLL_TIMEOUT	(30*HZ)
2862306a36Sopenharmony_ci#define NLMCLNT_MAX_RETRIES	3
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int	nlmclnt_test(struct nlm_rqst *, struct file_lock *);
3162306a36Sopenharmony_cistatic int	nlmclnt_lock(struct nlm_rqst *, struct file_lock *);
3262306a36Sopenharmony_cistatic int	nlmclnt_unlock(struct nlm_rqst *, struct file_lock *);
3362306a36Sopenharmony_cistatic int	nlm_stat_to_errno(__be32 stat);
3462306a36Sopenharmony_cistatic void	nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host);
3562306a36Sopenharmony_cistatic int	nlmclnt_cancel(struct nlm_host *, int , struct file_lock *);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_unlock_ops;
3862306a36Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_cancel_ops;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * Cookie counter for NLM requests
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_cistatic atomic_t	nlm_cookie = ATOMIC_INIT(0x1234);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_civoid nlmclnt_next_cookie(struct nlm_cookie *c)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	u32	cookie = atomic_inc_return(&nlm_cookie);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	memcpy(c->data, &cookie, 4);
5062306a36Sopenharmony_ci	c->len=4;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic struct nlm_lockowner *
5462306a36Sopenharmony_cinlmclnt_get_lockowner(struct nlm_lockowner *lockowner)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	refcount_inc(&lockowner->count);
5762306a36Sopenharmony_ci	return lockowner;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic void nlmclnt_put_lockowner(struct nlm_lockowner *lockowner)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	if (!refcount_dec_and_lock(&lockowner->count, &lockowner->host->h_lock))
6362306a36Sopenharmony_ci		return;
6462306a36Sopenharmony_ci	list_del(&lockowner->list);
6562306a36Sopenharmony_ci	spin_unlock(&lockowner->host->h_lock);
6662306a36Sopenharmony_ci	nlmclnt_release_host(lockowner->host);
6762306a36Sopenharmony_ci	kfree(lockowner);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic inline int nlm_pidbusy(struct nlm_host *host, uint32_t pid)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct nlm_lockowner *lockowner;
7362306a36Sopenharmony_ci	list_for_each_entry(lockowner, &host->h_lockowners, list) {
7462306a36Sopenharmony_ci		if (lockowner->pid == pid)
7562306a36Sopenharmony_ci			return -EBUSY;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic inline uint32_t __nlm_alloc_pid(struct nlm_host *host)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	uint32_t res;
8362306a36Sopenharmony_ci	do {
8462306a36Sopenharmony_ci		res = host->h_pidcount++;
8562306a36Sopenharmony_ci	} while (nlm_pidbusy(host, res) < 0);
8662306a36Sopenharmony_ci	return res;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic struct nlm_lockowner *__nlmclnt_find_lockowner(struct nlm_host *host, fl_owner_t owner)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct nlm_lockowner *lockowner;
9262306a36Sopenharmony_ci	list_for_each_entry(lockowner, &host->h_lockowners, list) {
9362306a36Sopenharmony_ci		if (lockowner->owner != owner)
9462306a36Sopenharmony_ci			continue;
9562306a36Sopenharmony_ci		return nlmclnt_get_lockowner(lockowner);
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	return NULL;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic struct nlm_lockowner *nlmclnt_find_lockowner(struct nlm_host *host, fl_owner_t owner)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct nlm_lockowner *res, *new = NULL;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	spin_lock(&host->h_lock);
10562306a36Sopenharmony_ci	res = __nlmclnt_find_lockowner(host, owner);
10662306a36Sopenharmony_ci	if (res == NULL) {
10762306a36Sopenharmony_ci		spin_unlock(&host->h_lock);
10862306a36Sopenharmony_ci		new = kmalloc(sizeof(*new), GFP_KERNEL);
10962306a36Sopenharmony_ci		spin_lock(&host->h_lock);
11062306a36Sopenharmony_ci		res = __nlmclnt_find_lockowner(host, owner);
11162306a36Sopenharmony_ci		if (res == NULL && new != NULL) {
11262306a36Sopenharmony_ci			res = new;
11362306a36Sopenharmony_ci			refcount_set(&new->count, 1);
11462306a36Sopenharmony_ci			new->owner = owner;
11562306a36Sopenharmony_ci			new->pid = __nlm_alloc_pid(host);
11662306a36Sopenharmony_ci			new->host = nlm_get_host(host);
11762306a36Sopenharmony_ci			list_add(&new->list, &host->h_lockowners);
11862306a36Sopenharmony_ci			new = NULL;
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	spin_unlock(&host->h_lock);
12262306a36Sopenharmony_ci	kfree(new);
12362306a36Sopenharmony_ci	return res;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/*
12762306a36Sopenharmony_ci * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_cistatic void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct nlm_args	*argp = &req->a_args;
13262306a36Sopenharmony_ci	struct nlm_lock	*lock = &argp->lock;
13362306a36Sopenharmony_ci	char *nodename = req->a_host->h_rpcclnt->cl_nodename;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	nlmclnt_next_cookie(&argp->cookie);
13662306a36Sopenharmony_ci	memcpy(&lock->fh, NFS_FH(file_inode(fl->fl_file)), sizeof(struct nfs_fh));
13762306a36Sopenharmony_ci	lock->caller  = nodename;
13862306a36Sopenharmony_ci	lock->oh.data = req->a_owner;
13962306a36Sopenharmony_ci	lock->oh.len  = snprintf(req->a_owner, sizeof(req->a_owner), "%u@%s",
14062306a36Sopenharmony_ci				(unsigned int)fl->fl_u.nfs_fl.owner->pid,
14162306a36Sopenharmony_ci				nodename);
14262306a36Sopenharmony_ci	lock->svid = fl->fl_u.nfs_fl.owner->pid;
14362306a36Sopenharmony_ci	lock->fl.fl_start = fl->fl_start;
14462306a36Sopenharmony_ci	lock->fl.fl_end = fl->fl_end;
14562306a36Sopenharmony_ci	lock->fl.fl_type = fl->fl_type;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic void nlmclnt_release_lockargs(struct nlm_rqst *req)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	WARN_ON_ONCE(req->a_args.lock.fl.fl_ops != NULL);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/**
15462306a36Sopenharmony_ci * nlmclnt_proc - Perform a single client-side lock request
15562306a36Sopenharmony_ci * @host: address of a valid nlm_host context representing the NLM server
15662306a36Sopenharmony_ci * @cmd: fcntl-style file lock operation to perform
15762306a36Sopenharmony_ci * @fl: address of arguments for the lock operation
15862306a36Sopenharmony_ci * @data: address of data to be sent to callback operations
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_ciint nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl, void *data)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct nlm_rqst		*call;
16462306a36Sopenharmony_ci	int			status;
16562306a36Sopenharmony_ci	const struct nlmclnt_operations *nlmclnt_ops = host->h_nlmclnt_ops;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	call = nlm_alloc_call(host);
16862306a36Sopenharmony_ci	if (call == NULL)
16962306a36Sopenharmony_ci		return -ENOMEM;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (nlmclnt_ops && nlmclnt_ops->nlmclnt_alloc_call)
17262306a36Sopenharmony_ci		nlmclnt_ops->nlmclnt_alloc_call(data);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	nlmclnt_locks_init_private(fl, host);
17562306a36Sopenharmony_ci	if (!fl->fl_u.nfs_fl.owner) {
17662306a36Sopenharmony_ci		/* lockowner allocation has failed */
17762306a36Sopenharmony_ci		nlmclnt_release_call(call);
17862306a36Sopenharmony_ci		return -ENOMEM;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci	/* Set up the argument struct */
18162306a36Sopenharmony_ci	nlmclnt_setlockargs(call, fl);
18262306a36Sopenharmony_ci	call->a_callback_data = data;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
18562306a36Sopenharmony_ci		if (fl->fl_type != F_UNLCK) {
18662306a36Sopenharmony_ci			call->a_args.block = IS_SETLKW(cmd) ? 1 : 0;
18762306a36Sopenharmony_ci			status = nlmclnt_lock(call, fl);
18862306a36Sopenharmony_ci		} else
18962306a36Sopenharmony_ci			status = nlmclnt_unlock(call, fl);
19062306a36Sopenharmony_ci	} else if (IS_GETLK(cmd))
19162306a36Sopenharmony_ci		status = nlmclnt_test(call, fl);
19262306a36Sopenharmony_ci	else
19362306a36Sopenharmony_ci		status = -EINVAL;
19462306a36Sopenharmony_ci	fl->fl_ops->fl_release_private(fl);
19562306a36Sopenharmony_ci	fl->fl_ops = NULL;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	dprintk("lockd: clnt proc returns %d\n", status);
19862306a36Sopenharmony_ci	return status;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmclnt_proc);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/*
20362306a36Sopenharmony_ci * Allocate an NLM RPC call struct
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_cistruct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct nlm_rqst	*call;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	for(;;) {
21062306a36Sopenharmony_ci		call = kzalloc(sizeof(*call), GFP_KERNEL);
21162306a36Sopenharmony_ci		if (call != NULL) {
21262306a36Sopenharmony_ci			refcount_set(&call->a_count, 1);
21362306a36Sopenharmony_ci			locks_init_lock(&call->a_args.lock.fl);
21462306a36Sopenharmony_ci			locks_init_lock(&call->a_res.lock.fl);
21562306a36Sopenharmony_ci			call->a_host = nlm_get_host(host);
21662306a36Sopenharmony_ci			return call;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci		if (signalled())
21962306a36Sopenharmony_ci			break;
22062306a36Sopenharmony_ci		printk("nlm_alloc_call: failed, waiting for memory\n");
22162306a36Sopenharmony_ci		schedule_timeout_interruptible(5*HZ);
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci	return NULL;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_civoid nlmclnt_release_call(struct nlm_rqst *call)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	const struct nlmclnt_operations *nlmclnt_ops = call->a_host->h_nlmclnt_ops;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (!refcount_dec_and_test(&call->a_count))
23162306a36Sopenharmony_ci		return;
23262306a36Sopenharmony_ci	if (nlmclnt_ops && nlmclnt_ops->nlmclnt_release_call)
23362306a36Sopenharmony_ci		nlmclnt_ops->nlmclnt_release_call(call->a_callback_data);
23462306a36Sopenharmony_ci	nlmclnt_release_host(call->a_host);
23562306a36Sopenharmony_ci	nlmclnt_release_lockargs(call);
23662306a36Sopenharmony_ci	kfree(call);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic void nlmclnt_rpc_release(void *data)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	nlmclnt_release_call(data);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int nlm_wait_on_grace(wait_queue_head_t *queue)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	DEFINE_WAIT(wait);
24762306a36Sopenharmony_ci	int status = -EINTR;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	prepare_to_wait(queue, &wait, TASK_INTERRUPTIBLE);
25062306a36Sopenharmony_ci	if (!signalled ()) {
25162306a36Sopenharmony_ci		schedule_timeout(NLMCLNT_GRACE_WAIT);
25262306a36Sopenharmony_ci		try_to_freeze();
25362306a36Sopenharmony_ci		if (!signalled ())
25462306a36Sopenharmony_ci			status = 0;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci	finish_wait(queue, &wait);
25762306a36Sopenharmony_ci	return status;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/*
26162306a36Sopenharmony_ci * Generic NLM call
26262306a36Sopenharmony_ci */
26362306a36Sopenharmony_cistatic int
26462306a36Sopenharmony_cinlmclnt_call(const struct cred *cred, struct nlm_rqst *req, u32 proc)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct nlm_host	*host = req->a_host;
26762306a36Sopenharmony_ci	struct rpc_clnt	*clnt;
26862306a36Sopenharmony_ci	struct nlm_args	*argp = &req->a_args;
26962306a36Sopenharmony_ci	struct nlm_res	*resp = &req->a_res;
27062306a36Sopenharmony_ci	struct rpc_message msg = {
27162306a36Sopenharmony_ci		.rpc_argp	= argp,
27262306a36Sopenharmony_ci		.rpc_resp	= resp,
27362306a36Sopenharmony_ci		.rpc_cred	= cred,
27462306a36Sopenharmony_ci	};
27562306a36Sopenharmony_ci	int		status;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	dprintk("lockd: call procedure %d on %s\n",
27862306a36Sopenharmony_ci			(int)proc, host->h_name);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	do {
28162306a36Sopenharmony_ci		if (host->h_reclaiming && !argp->reclaim)
28262306a36Sopenharmony_ci			goto in_grace_period;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		/* If we have no RPC client yet, create one. */
28562306a36Sopenharmony_ci		if ((clnt = nlm_bind_host(host)) == NULL)
28662306a36Sopenharmony_ci			return -ENOLCK;
28762306a36Sopenharmony_ci		msg.rpc_proc = &clnt->cl_procinfo[proc];
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		/* Perform the RPC call. If an error occurs, try again */
29062306a36Sopenharmony_ci		if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) {
29162306a36Sopenharmony_ci			dprintk("lockd: rpc_call returned error %d\n", -status);
29262306a36Sopenharmony_ci			switch (status) {
29362306a36Sopenharmony_ci			case -EPROTONOSUPPORT:
29462306a36Sopenharmony_ci				status = -EINVAL;
29562306a36Sopenharmony_ci				break;
29662306a36Sopenharmony_ci			case -ECONNREFUSED:
29762306a36Sopenharmony_ci			case -ETIMEDOUT:
29862306a36Sopenharmony_ci			case -ENOTCONN:
29962306a36Sopenharmony_ci				nlm_rebind_host(host);
30062306a36Sopenharmony_ci				status = -EAGAIN;
30162306a36Sopenharmony_ci				break;
30262306a36Sopenharmony_ci			case -ERESTARTSYS:
30362306a36Sopenharmony_ci				return signalled () ? -EINTR : status;
30462306a36Sopenharmony_ci			default:
30562306a36Sopenharmony_ci				break;
30662306a36Sopenharmony_ci			}
30762306a36Sopenharmony_ci			break;
30862306a36Sopenharmony_ci		} else
30962306a36Sopenharmony_ci		if (resp->status == nlm_lck_denied_grace_period) {
31062306a36Sopenharmony_ci			dprintk("lockd: server in grace period\n");
31162306a36Sopenharmony_ci			if (argp->reclaim) {
31262306a36Sopenharmony_ci				printk(KERN_WARNING
31362306a36Sopenharmony_ci				     "lockd: spurious grace period reject?!\n");
31462306a36Sopenharmony_ci				return -ENOLCK;
31562306a36Sopenharmony_ci			}
31662306a36Sopenharmony_ci		} else {
31762306a36Sopenharmony_ci			if (!argp->reclaim) {
31862306a36Sopenharmony_ci				/* We appear to be out of the grace period */
31962306a36Sopenharmony_ci				wake_up_all(&host->h_gracewait);
32062306a36Sopenharmony_ci			}
32162306a36Sopenharmony_ci			dprintk("lockd: server returns status %d\n",
32262306a36Sopenharmony_ci				ntohl(resp->status));
32362306a36Sopenharmony_ci			return 0;	/* Okay, call complete */
32462306a36Sopenharmony_ci		}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ciin_grace_period:
32762306a36Sopenharmony_ci		/*
32862306a36Sopenharmony_ci		 * The server has rebooted and appears to be in the grace
32962306a36Sopenharmony_ci		 * period during which locks are only allowed to be
33062306a36Sopenharmony_ci		 * reclaimed.
33162306a36Sopenharmony_ci		 * We can only back off and try again later.
33262306a36Sopenharmony_ci		 */
33362306a36Sopenharmony_ci		status = nlm_wait_on_grace(&host->h_gracewait);
33462306a36Sopenharmony_ci	} while (status == 0);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	return status;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/*
34062306a36Sopenharmony_ci * Generic NLM call, async version.
34162306a36Sopenharmony_ci */
34262306a36Sopenharmony_cistatic struct rpc_task *__nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct nlm_host	*host = req->a_host;
34562306a36Sopenharmony_ci	struct rpc_clnt	*clnt;
34662306a36Sopenharmony_ci	struct rpc_task_setup task_setup_data = {
34762306a36Sopenharmony_ci		.rpc_message = msg,
34862306a36Sopenharmony_ci		.callback_ops = tk_ops,
34962306a36Sopenharmony_ci		.callback_data = req,
35062306a36Sopenharmony_ci		.flags = RPC_TASK_ASYNC,
35162306a36Sopenharmony_ci	};
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	dprintk("lockd: call procedure %d on %s (async)\n",
35462306a36Sopenharmony_ci			(int)proc, host->h_name);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* If we have no RPC client yet, create one. */
35762306a36Sopenharmony_ci	clnt = nlm_bind_host(host);
35862306a36Sopenharmony_ci	if (clnt == NULL)
35962306a36Sopenharmony_ci		goto out_err;
36062306a36Sopenharmony_ci	msg->rpc_proc = &clnt->cl_procinfo[proc];
36162306a36Sopenharmony_ci	task_setup_data.rpc_client = clnt;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci        /* bootstrap and kick off the async RPC call */
36462306a36Sopenharmony_ci	return rpc_run_task(&task_setup_data);
36562306a36Sopenharmony_ciout_err:
36662306a36Sopenharmony_ci	tk_ops->rpc_release(req);
36762306a36Sopenharmony_ci	return ERR_PTR(-ENOLCK);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int nlm_do_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct rpc_task *task;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	task = __nlm_async_call(req, proc, msg, tk_ops);
37562306a36Sopenharmony_ci	if (IS_ERR(task))
37662306a36Sopenharmony_ci		return PTR_ERR(task);
37762306a36Sopenharmony_ci	rpc_put_task(task);
37862306a36Sopenharmony_ci	return 0;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/*
38262306a36Sopenharmony_ci * NLM asynchronous call.
38362306a36Sopenharmony_ci */
38462306a36Sopenharmony_ciint nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct rpc_message msg = {
38762306a36Sopenharmony_ci		.rpc_argp	= &req->a_args,
38862306a36Sopenharmony_ci		.rpc_resp	= &req->a_res,
38962306a36Sopenharmony_ci	};
39062306a36Sopenharmony_ci	return nlm_do_async_call(req, proc, &msg, tk_ops);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ciint nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct rpc_message msg = {
39662306a36Sopenharmony_ci		.rpc_argp	= &req->a_res,
39762306a36Sopenharmony_ci	};
39862306a36Sopenharmony_ci	return nlm_do_async_call(req, proc, &msg, tk_ops);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/*
40262306a36Sopenharmony_ci * NLM client asynchronous call.
40362306a36Sopenharmony_ci *
40462306a36Sopenharmony_ci * Note that although the calls are asynchronous, and are therefore
40562306a36Sopenharmony_ci *      guaranteed to complete, we still always attempt to wait for
40662306a36Sopenharmony_ci *      completion in order to be able to correctly track the lock
40762306a36Sopenharmony_ci *      state.
40862306a36Sopenharmony_ci */
40962306a36Sopenharmony_cistatic int nlmclnt_async_call(const struct cred *cred, struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct rpc_message msg = {
41262306a36Sopenharmony_ci		.rpc_argp	= &req->a_args,
41362306a36Sopenharmony_ci		.rpc_resp	= &req->a_res,
41462306a36Sopenharmony_ci		.rpc_cred	= cred,
41562306a36Sopenharmony_ci	};
41662306a36Sopenharmony_ci	struct rpc_task *task;
41762306a36Sopenharmony_ci	int err;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	task = __nlm_async_call(req, proc, &msg, tk_ops);
42062306a36Sopenharmony_ci	if (IS_ERR(task))
42162306a36Sopenharmony_ci		return PTR_ERR(task);
42262306a36Sopenharmony_ci	err = rpc_wait_for_completion_task(task);
42362306a36Sopenharmony_ci	rpc_put_task(task);
42462306a36Sopenharmony_ci	return err;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci/*
42862306a36Sopenharmony_ci * TEST for the presence of a conflicting lock
42962306a36Sopenharmony_ci */
43062306a36Sopenharmony_cistatic int
43162306a36Sopenharmony_cinlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	int	status;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_TEST);
43662306a36Sopenharmony_ci	if (status < 0)
43762306a36Sopenharmony_ci		goto out;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	switch (req->a_res.status) {
44062306a36Sopenharmony_ci		case nlm_granted:
44162306a36Sopenharmony_ci			fl->fl_type = F_UNLCK;
44262306a36Sopenharmony_ci			break;
44362306a36Sopenharmony_ci		case nlm_lck_denied:
44462306a36Sopenharmony_ci			/*
44562306a36Sopenharmony_ci			 * Report the conflicting lock back to the application.
44662306a36Sopenharmony_ci			 */
44762306a36Sopenharmony_ci			fl->fl_start = req->a_res.lock.fl.fl_start;
44862306a36Sopenharmony_ci			fl->fl_end = req->a_res.lock.fl.fl_end;
44962306a36Sopenharmony_ci			fl->fl_type = req->a_res.lock.fl.fl_type;
45062306a36Sopenharmony_ci			fl->fl_pid = -req->a_res.lock.fl.fl_pid;
45162306a36Sopenharmony_ci			break;
45262306a36Sopenharmony_ci		default:
45362306a36Sopenharmony_ci			status = nlm_stat_to_errno(req->a_res.status);
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ciout:
45662306a36Sopenharmony_ci	trace_nlmclnt_test(&req->a_args.lock,
45762306a36Sopenharmony_ci			   (const struct sockaddr *)&req->a_host->h_addr,
45862306a36Sopenharmony_ci			   req->a_host->h_addrlen, req->a_res.status);
45962306a36Sopenharmony_ci	nlmclnt_release_call(req);
46062306a36Sopenharmony_ci	return status;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock);
46662306a36Sopenharmony_ci	new->fl_u.nfs_fl.state = fl->fl_u.nfs_fl.state;
46762306a36Sopenharmony_ci	new->fl_u.nfs_fl.owner = nlmclnt_get_lockowner(fl->fl_u.nfs_fl.owner);
46862306a36Sopenharmony_ci	list_add_tail(&new->fl_u.nfs_fl.list, &fl->fl_u.nfs_fl.owner->host->h_granted);
46962306a36Sopenharmony_ci	spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic void nlmclnt_locks_release_private(struct file_lock *fl)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock);
47562306a36Sopenharmony_ci	list_del(&fl->fl_u.nfs_fl.list);
47662306a36Sopenharmony_ci	spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
47762306a36Sopenharmony_ci	nlmclnt_put_lockowner(fl->fl_u.nfs_fl.owner);
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic const struct file_lock_operations nlmclnt_lock_ops = {
48162306a36Sopenharmony_ci	.fl_copy_lock = nlmclnt_locks_copy_lock,
48262306a36Sopenharmony_ci	.fl_release_private = nlmclnt_locks_release_private,
48362306a36Sopenharmony_ci};
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	fl->fl_u.nfs_fl.state = 0;
48862306a36Sopenharmony_ci	fl->fl_u.nfs_fl.owner = nlmclnt_find_lockowner(host, fl->fl_owner);
48962306a36Sopenharmony_ci	INIT_LIST_HEAD(&fl->fl_u.nfs_fl.list);
49062306a36Sopenharmony_ci	fl->fl_ops = &nlmclnt_lock_ops;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic int do_vfs_lock(struct file_lock *fl)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	return locks_lock_file_wait(fl->fl_file, fl);
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci/*
49962306a36Sopenharmony_ci * LOCK: Try to create a lock
50062306a36Sopenharmony_ci *
50162306a36Sopenharmony_ci *			Programmer Harassment Alert
50262306a36Sopenharmony_ci *
50362306a36Sopenharmony_ci * When given a blocking lock request in a sync RPC call, the HPUX lockd
50462306a36Sopenharmony_ci * will faithfully return LCK_BLOCKED but never cares to notify us when
50562306a36Sopenharmony_ci * the lock could be granted. This way, our local process could hang
50662306a36Sopenharmony_ci * around forever waiting for the callback.
50762306a36Sopenharmony_ci *
50862306a36Sopenharmony_ci *  Solution A:	Implement busy-waiting
50962306a36Sopenharmony_ci *  Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES})
51062306a36Sopenharmony_ci *
51162306a36Sopenharmony_ci * For now I am implementing solution A, because I hate the idea of
51262306a36Sopenharmony_ci * re-implementing lockd for a third time in two months. The async
51362306a36Sopenharmony_ci * calls shouldn't be too hard to do, however.
51462306a36Sopenharmony_ci *
51562306a36Sopenharmony_ci * This is one of the lovely things about standards in the NFS area:
51662306a36Sopenharmony_ci * they're so soft and squishy you can't really blame HP for doing this.
51762306a36Sopenharmony_ci */
51862306a36Sopenharmony_cistatic int
51962306a36Sopenharmony_cinlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	const struct cred *cred = nfs_file_cred(fl->fl_file);
52262306a36Sopenharmony_ci	struct nlm_host	*host = req->a_host;
52362306a36Sopenharmony_ci	struct nlm_res	*resp = &req->a_res;
52462306a36Sopenharmony_ci	struct nlm_wait block;
52562306a36Sopenharmony_ci	unsigned char fl_flags = fl->fl_flags;
52662306a36Sopenharmony_ci	unsigned char fl_type;
52762306a36Sopenharmony_ci	__be32 b_status;
52862306a36Sopenharmony_ci	int status = -ENOLCK;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (nsm_monitor(host) < 0)
53162306a36Sopenharmony_ci		goto out;
53262306a36Sopenharmony_ci	req->a_args.state = nsm_local_state;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	fl->fl_flags |= FL_ACCESS;
53562306a36Sopenharmony_ci	status = do_vfs_lock(fl);
53662306a36Sopenharmony_ci	fl->fl_flags = fl_flags;
53762306a36Sopenharmony_ci	if (status < 0)
53862306a36Sopenharmony_ci		goto out;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	nlmclnt_prepare_block(&block, host, fl);
54162306a36Sopenharmony_ciagain:
54262306a36Sopenharmony_ci	/*
54362306a36Sopenharmony_ci	 * Initialise resp->status to a valid non-zero value,
54462306a36Sopenharmony_ci	 * since 0 == nlm_lck_granted
54562306a36Sopenharmony_ci	 */
54662306a36Sopenharmony_ci	resp->status = nlm_lck_blocked;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/*
54962306a36Sopenharmony_ci	 * A GRANTED callback can come at any time -- even before the reply
55062306a36Sopenharmony_ci	 * to the LOCK request arrives, so we queue the wait before
55162306a36Sopenharmony_ci	 * requesting the lock.
55262306a36Sopenharmony_ci	 */
55362306a36Sopenharmony_ci	nlmclnt_queue_block(&block);
55462306a36Sopenharmony_ci	for (;;) {
55562306a36Sopenharmony_ci		/* Reboot protection */
55662306a36Sopenharmony_ci		fl->fl_u.nfs_fl.state = host->h_state;
55762306a36Sopenharmony_ci		status = nlmclnt_call(cred, req, NLMPROC_LOCK);
55862306a36Sopenharmony_ci		if (status < 0)
55962306a36Sopenharmony_ci			break;
56062306a36Sopenharmony_ci		/* Did a reclaimer thread notify us of a server reboot? */
56162306a36Sopenharmony_ci		if (resp->status == nlm_lck_denied_grace_period)
56262306a36Sopenharmony_ci			continue;
56362306a36Sopenharmony_ci		if (resp->status != nlm_lck_blocked)
56462306a36Sopenharmony_ci			break;
56562306a36Sopenharmony_ci		/* Wait on an NLM blocking lock */
56662306a36Sopenharmony_ci		status = nlmclnt_wait(&block, req, NLMCLNT_POLL_TIMEOUT);
56762306a36Sopenharmony_ci		if (status < 0)
56862306a36Sopenharmony_ci			break;
56962306a36Sopenharmony_ci		if (block.b_status != nlm_lck_blocked)
57062306a36Sopenharmony_ci			break;
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci	b_status = nlmclnt_dequeue_block(&block);
57362306a36Sopenharmony_ci	if (resp->status == nlm_lck_blocked)
57462306a36Sopenharmony_ci		resp->status = b_status;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	/* if we were interrupted while blocking, then cancel the lock request
57762306a36Sopenharmony_ci	 * and exit
57862306a36Sopenharmony_ci	 */
57962306a36Sopenharmony_ci	if (resp->status == nlm_lck_blocked) {
58062306a36Sopenharmony_ci		if (!req->a_args.block)
58162306a36Sopenharmony_ci			goto out_unlock;
58262306a36Sopenharmony_ci		if (nlmclnt_cancel(host, req->a_args.block, fl) == 0)
58362306a36Sopenharmony_ci			goto out;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (resp->status == nlm_granted) {
58762306a36Sopenharmony_ci		down_read(&host->h_rwsem);
58862306a36Sopenharmony_ci		/* Check whether or not the server has rebooted */
58962306a36Sopenharmony_ci		if (fl->fl_u.nfs_fl.state != host->h_state) {
59062306a36Sopenharmony_ci			up_read(&host->h_rwsem);
59162306a36Sopenharmony_ci			goto again;
59262306a36Sopenharmony_ci		}
59362306a36Sopenharmony_ci		/* Ensure the resulting lock will get added to granted list */
59462306a36Sopenharmony_ci		fl->fl_flags |= FL_SLEEP;
59562306a36Sopenharmony_ci		if (do_vfs_lock(fl) < 0)
59662306a36Sopenharmony_ci			printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__);
59762306a36Sopenharmony_ci		up_read(&host->h_rwsem);
59862306a36Sopenharmony_ci		fl->fl_flags = fl_flags;
59962306a36Sopenharmony_ci		status = 0;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci	if (status < 0)
60262306a36Sopenharmony_ci		goto out_unlock;
60362306a36Sopenharmony_ci	/*
60462306a36Sopenharmony_ci	 * EAGAIN doesn't make sense for sleeping locks, and in some
60562306a36Sopenharmony_ci	 * cases NLM_LCK_DENIED is returned for a permanent error.  So
60662306a36Sopenharmony_ci	 * turn it into an ENOLCK.
60762306a36Sopenharmony_ci	 */
60862306a36Sopenharmony_ci	if (resp->status == nlm_lck_denied && (fl_flags & FL_SLEEP))
60962306a36Sopenharmony_ci		status = -ENOLCK;
61062306a36Sopenharmony_ci	else
61162306a36Sopenharmony_ci		status = nlm_stat_to_errno(resp->status);
61262306a36Sopenharmony_ciout:
61362306a36Sopenharmony_ci	trace_nlmclnt_lock(&req->a_args.lock,
61462306a36Sopenharmony_ci			   (const struct sockaddr *)&req->a_host->h_addr,
61562306a36Sopenharmony_ci			   req->a_host->h_addrlen, req->a_res.status);
61662306a36Sopenharmony_ci	nlmclnt_release_call(req);
61762306a36Sopenharmony_ci	return status;
61862306a36Sopenharmony_ciout_unlock:
61962306a36Sopenharmony_ci	/* Fatal error: ensure that we remove the lock altogether */
62062306a36Sopenharmony_ci	trace_nlmclnt_lock(&req->a_args.lock,
62162306a36Sopenharmony_ci			   (const struct sockaddr *)&req->a_host->h_addr,
62262306a36Sopenharmony_ci			   req->a_host->h_addrlen, req->a_res.status);
62362306a36Sopenharmony_ci	dprintk("lockd: lock attempt ended in fatal error.\n"
62462306a36Sopenharmony_ci		"       Attempting to unlock.\n");
62562306a36Sopenharmony_ci	fl_type = fl->fl_type;
62662306a36Sopenharmony_ci	fl->fl_type = F_UNLCK;
62762306a36Sopenharmony_ci	down_read(&host->h_rwsem);
62862306a36Sopenharmony_ci	do_vfs_lock(fl);
62962306a36Sopenharmony_ci	up_read(&host->h_rwsem);
63062306a36Sopenharmony_ci	fl->fl_type = fl_type;
63162306a36Sopenharmony_ci	fl->fl_flags = fl_flags;
63262306a36Sopenharmony_ci	nlmclnt_async_call(cred, req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
63362306a36Sopenharmony_ci	return status;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci/*
63762306a36Sopenharmony_ci * RECLAIM: Try to reclaim a lock
63862306a36Sopenharmony_ci */
63962306a36Sopenharmony_ciint
64062306a36Sopenharmony_cinlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl,
64162306a36Sopenharmony_ci		struct nlm_rqst *req)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	int		status;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	memset(req, 0, sizeof(*req));
64662306a36Sopenharmony_ci	locks_init_lock(&req->a_args.lock.fl);
64762306a36Sopenharmony_ci	locks_init_lock(&req->a_res.lock.fl);
64862306a36Sopenharmony_ci	req->a_host  = host;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/* Set up the argument struct */
65162306a36Sopenharmony_ci	nlmclnt_setlockargs(req, fl);
65262306a36Sopenharmony_ci	req->a_args.reclaim = 1;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_LOCK);
65562306a36Sopenharmony_ci	if (status >= 0 && req->a_res.status == nlm_granted)
65662306a36Sopenharmony_ci		return 0;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d "
65962306a36Sopenharmony_ci				"(errno %d, status %d)\n", fl->fl_pid,
66062306a36Sopenharmony_ci				status, ntohl(req->a_res.status));
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	/*
66362306a36Sopenharmony_ci	 * FIXME: This is a serious failure. We can
66462306a36Sopenharmony_ci	 *
66562306a36Sopenharmony_ci	 *  a.	Ignore the problem
66662306a36Sopenharmony_ci	 *  b.	Send the owning process some signal (Linux doesn't have
66762306a36Sopenharmony_ci	 *	SIGLOST, though...)
66862306a36Sopenharmony_ci	 *  c.	Retry the operation
66962306a36Sopenharmony_ci	 *
67062306a36Sopenharmony_ci	 * Until someone comes up with a simple implementation
67162306a36Sopenharmony_ci	 * for b or c, I'll choose option a.
67262306a36Sopenharmony_ci	 */
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	return -ENOLCK;
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci/*
67862306a36Sopenharmony_ci * UNLOCK: remove an existing lock
67962306a36Sopenharmony_ci */
68062306a36Sopenharmony_cistatic int
68162306a36Sopenharmony_cinlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	struct nlm_host	*host = req->a_host;
68462306a36Sopenharmony_ci	struct nlm_res	*resp = &req->a_res;
68562306a36Sopenharmony_ci	int status;
68662306a36Sopenharmony_ci	unsigned char fl_flags = fl->fl_flags;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/*
68962306a36Sopenharmony_ci	 * Note: the server is supposed to either grant us the unlock
69062306a36Sopenharmony_ci	 * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either
69162306a36Sopenharmony_ci	 * case, we want to unlock.
69262306a36Sopenharmony_ci	 */
69362306a36Sopenharmony_ci	fl->fl_flags |= FL_EXISTS;
69462306a36Sopenharmony_ci	down_read(&host->h_rwsem);
69562306a36Sopenharmony_ci	status = do_vfs_lock(fl);
69662306a36Sopenharmony_ci	up_read(&host->h_rwsem);
69762306a36Sopenharmony_ci	fl->fl_flags = fl_flags;
69862306a36Sopenharmony_ci	if (status == -ENOENT) {
69962306a36Sopenharmony_ci		status = 0;
70062306a36Sopenharmony_ci		goto out;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	refcount_inc(&req->a_count);
70462306a36Sopenharmony_ci	status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req,
70562306a36Sopenharmony_ci			NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
70662306a36Sopenharmony_ci	if (status < 0)
70762306a36Sopenharmony_ci		goto out;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (resp->status == nlm_granted)
71062306a36Sopenharmony_ci		goto out;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (resp->status != nlm_lck_denied_nolocks)
71362306a36Sopenharmony_ci		printk("lockd: unexpected unlock status: %d\n",
71462306a36Sopenharmony_ci			ntohl(resp->status));
71562306a36Sopenharmony_ci	/* What to do now? I'm out of my depth... */
71662306a36Sopenharmony_ci	status = -ENOLCK;
71762306a36Sopenharmony_ciout:
71862306a36Sopenharmony_ci	trace_nlmclnt_unlock(&req->a_args.lock,
71962306a36Sopenharmony_ci			     (const struct sockaddr *)&req->a_host->h_addr,
72062306a36Sopenharmony_ci			     req->a_host->h_addrlen, req->a_res.status);
72162306a36Sopenharmony_ci	nlmclnt_release_call(req);
72262306a36Sopenharmony_ci	return status;
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_cistatic void nlmclnt_unlock_prepare(struct rpc_task *task, void *data)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	struct nlm_rqst	*req = data;
72862306a36Sopenharmony_ci	const struct nlmclnt_operations *nlmclnt_ops = req->a_host->h_nlmclnt_ops;
72962306a36Sopenharmony_ci	bool defer_call = false;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (nlmclnt_ops && nlmclnt_ops->nlmclnt_unlock_prepare)
73262306a36Sopenharmony_ci		defer_call = nlmclnt_ops->nlmclnt_unlock_prepare(task, req->a_callback_data);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (!defer_call)
73562306a36Sopenharmony_ci		rpc_call_start(task);
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	struct nlm_rqst	*req = data;
74162306a36Sopenharmony_ci	u32 status = ntohl(req->a_res.status);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (RPC_SIGNALLED(task))
74462306a36Sopenharmony_ci		goto die;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	if (task->tk_status < 0) {
74762306a36Sopenharmony_ci		dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);
74862306a36Sopenharmony_ci		switch (task->tk_status) {
74962306a36Sopenharmony_ci		case -EACCES:
75062306a36Sopenharmony_ci		case -EIO:
75162306a36Sopenharmony_ci			goto die;
75262306a36Sopenharmony_ci		default:
75362306a36Sopenharmony_ci			goto retry_rebind;
75462306a36Sopenharmony_ci		}
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci	if (status == NLM_LCK_DENIED_GRACE_PERIOD) {
75762306a36Sopenharmony_ci		rpc_delay(task, NLMCLNT_GRACE_WAIT);
75862306a36Sopenharmony_ci		goto retry_unlock;
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci	if (status != NLM_LCK_GRANTED)
76162306a36Sopenharmony_ci		printk(KERN_WARNING "lockd: unexpected unlock status: %d\n", status);
76262306a36Sopenharmony_cidie:
76362306a36Sopenharmony_ci	return;
76462306a36Sopenharmony_ci retry_rebind:
76562306a36Sopenharmony_ci	nlm_rebind_host(req->a_host);
76662306a36Sopenharmony_ci retry_unlock:
76762306a36Sopenharmony_ci	rpc_restart_call(task);
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_unlock_ops = {
77162306a36Sopenharmony_ci	.rpc_call_prepare = nlmclnt_unlock_prepare,
77262306a36Sopenharmony_ci	.rpc_call_done = nlmclnt_unlock_callback,
77362306a36Sopenharmony_ci	.rpc_release = nlmclnt_rpc_release,
77462306a36Sopenharmony_ci};
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci/*
77762306a36Sopenharmony_ci * Cancel a blocked lock request.
77862306a36Sopenharmony_ci * We always use an async RPC call for this in order not to hang a
77962306a36Sopenharmony_ci * process that has been Ctrl-C'ed.
78062306a36Sopenharmony_ci */
78162306a36Sopenharmony_cistatic int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct nlm_rqst	*req;
78462306a36Sopenharmony_ci	int status;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	dprintk("lockd: blocking lock attempt was interrupted by a signal.\n"
78762306a36Sopenharmony_ci		"       Attempting to cancel lock.\n");
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	req = nlm_alloc_call(host);
79062306a36Sopenharmony_ci	if (!req)
79162306a36Sopenharmony_ci		return -ENOMEM;
79262306a36Sopenharmony_ci	req->a_flags = RPC_TASK_ASYNC;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	nlmclnt_setlockargs(req, fl);
79562306a36Sopenharmony_ci	req->a_args.block = block;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	refcount_inc(&req->a_count);
79862306a36Sopenharmony_ci	status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req,
79962306a36Sopenharmony_ci			NLMPROC_CANCEL, &nlmclnt_cancel_ops);
80062306a36Sopenharmony_ci	if (status == 0 && req->a_res.status == nlm_lck_denied)
80162306a36Sopenharmony_ci		status = -ENOLCK;
80262306a36Sopenharmony_ci	nlmclnt_release_call(req);
80362306a36Sopenharmony_ci	return status;
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic void nlmclnt_cancel_callback(struct rpc_task *task, void *data)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct nlm_rqst	*req = data;
80962306a36Sopenharmony_ci	u32 status = ntohl(req->a_res.status);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (RPC_SIGNALLED(task))
81262306a36Sopenharmony_ci		goto die;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	if (task->tk_status < 0) {
81562306a36Sopenharmony_ci		dprintk("lockd: CANCEL call error %d, retrying.\n",
81662306a36Sopenharmony_ci					task->tk_status);
81762306a36Sopenharmony_ci		goto retry_cancel;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	switch (status) {
82162306a36Sopenharmony_ci	case NLM_LCK_GRANTED:
82262306a36Sopenharmony_ci	case NLM_LCK_DENIED_GRACE_PERIOD:
82362306a36Sopenharmony_ci	case NLM_LCK_DENIED:
82462306a36Sopenharmony_ci		/* Everything's good */
82562306a36Sopenharmony_ci		break;
82662306a36Sopenharmony_ci	case NLM_LCK_DENIED_NOLOCKS:
82762306a36Sopenharmony_ci		dprintk("lockd: CANCEL failed (server has no locks)\n");
82862306a36Sopenharmony_ci		goto retry_cancel;
82962306a36Sopenharmony_ci	default:
83062306a36Sopenharmony_ci		printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n",
83162306a36Sopenharmony_ci			status);
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cidie:
83562306a36Sopenharmony_ci	return;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ciretry_cancel:
83862306a36Sopenharmony_ci	/* Don't ever retry more than 3 times */
83962306a36Sopenharmony_ci	if (req->a_retries++ >= NLMCLNT_MAX_RETRIES)
84062306a36Sopenharmony_ci		goto die;
84162306a36Sopenharmony_ci	nlm_rebind_host(req->a_host);
84262306a36Sopenharmony_ci	rpc_restart_call(task);
84362306a36Sopenharmony_ci	rpc_delay(task, 30 * HZ);
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic const struct rpc_call_ops nlmclnt_cancel_ops = {
84762306a36Sopenharmony_ci	.rpc_call_done = nlmclnt_cancel_callback,
84862306a36Sopenharmony_ci	.rpc_release = nlmclnt_rpc_release,
84962306a36Sopenharmony_ci};
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci/*
85262306a36Sopenharmony_ci * Convert an NLM status code to a generic kernel errno
85362306a36Sopenharmony_ci */
85462306a36Sopenharmony_cistatic int
85562306a36Sopenharmony_cinlm_stat_to_errno(__be32 status)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	switch(ntohl(status)) {
85862306a36Sopenharmony_ci	case NLM_LCK_GRANTED:
85962306a36Sopenharmony_ci		return 0;
86062306a36Sopenharmony_ci	case NLM_LCK_DENIED:
86162306a36Sopenharmony_ci		return -EAGAIN;
86262306a36Sopenharmony_ci	case NLM_LCK_DENIED_NOLOCKS:
86362306a36Sopenharmony_ci	case NLM_LCK_DENIED_GRACE_PERIOD:
86462306a36Sopenharmony_ci		return -ENOLCK;
86562306a36Sopenharmony_ci	case NLM_LCK_BLOCKED:
86662306a36Sopenharmony_ci		printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n");
86762306a36Sopenharmony_ci		return -ENOLCK;
86862306a36Sopenharmony_ci#ifdef CONFIG_LOCKD_V4
86962306a36Sopenharmony_ci	case NLM_DEADLCK:
87062306a36Sopenharmony_ci		return -EDEADLK;
87162306a36Sopenharmony_ci	case NLM_ROFS:
87262306a36Sopenharmony_ci		return -EROFS;
87362306a36Sopenharmony_ci	case NLM_STALE_FH:
87462306a36Sopenharmony_ci		return -ESTALE;
87562306a36Sopenharmony_ci	case NLM_FBIG:
87662306a36Sopenharmony_ci		return -EOVERFLOW;
87762306a36Sopenharmony_ci	case NLM_FAILED:
87862306a36Sopenharmony_ci		return -ENOLCK;
87962306a36Sopenharmony_ci#endif
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci	printk(KERN_NOTICE "lockd: unexpected server status %d\n",
88262306a36Sopenharmony_ci		 ntohl(status));
88362306a36Sopenharmony_ci	return -ENOLCK;
88462306a36Sopenharmony_ci}
885