Contributors: 15
Author Tokens Token Proportion Commits Commit Proportion
Chuck Lever 4638 81.21% 39 54.17%
Linus Torvalds (pre-git) 621 10.87% 3 4.17%
Christoph Hellwig 140 2.45% 4 5.56%
Trond Myklebust 134 2.35% 5 6.94%
Neil Brown 77 1.35% 5 6.94%
Benjamin Coddington 39 0.68% 2 2.78%
Al Viro 14 0.25% 1 1.39%
Stanislav Kinsbursky 14 0.25% 1 1.39%
J. Bruce Fields 13 0.23% 3 4.17%
Marc Eshel 8 0.14% 2 2.78%
Jeff Layton 5 0.09% 2 2.78%
Oleg Drokin 3 0.05% 1 1.39%
Dave Jones 3 0.05% 2 2.78%
Olaf Kirch 1 0.02% 1 1.39%
Greg Kroah-Hartman 1 0.02% 1 1.39%
Total 5711 72


// SPDX-License-Identifier: GPL-2.0
/*
 * linux/fs/lockd/svc4proc.c
 *
 * Lockd server procedures. We don't implement the NLM_*_RES 
 * procedures because we don't use the async procedures.
 *
 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
 */

#include <linux/types.h>
#include <linux/time.h>
#include <linux/sunrpc/svc_xprt.h>

#include "lockd.h"

/*
 * xdr.h defines SM_MAXSTRLEN and SM_PRIV_SIZE as macros.
 * nlm4xdr_gen.h defines them as enum constants. Undefine the
 * macros to allow the xdrgen enum definitions to be used.
 */
#undef SM_MAXSTRLEN
#undef SM_PRIV_SIZE

#include "share.h"
#include "nlm4xdr_gen.h"

/*
 * Wrapper structures combine xdrgen types with legacy nlm_lock.
 * The xdrgen field must be first so the structure can be cast
 * to its XDR type for the RPC dispatch layer.
 */
struct nlm4_testargs_wrapper {
	struct nlm4_testargs		xdrgen;
	struct nlm_lock			lock;
};

static_assert(offsetof(struct nlm4_testargs_wrapper, xdrgen) == 0);

struct nlm4_lockargs_wrapper {
	struct nlm4_lockargs		xdrgen;
	struct nlm_cookie		cookie;
	struct nlm_lock			lock;
};

static_assert(offsetof(struct nlm4_lockargs_wrapper, xdrgen) == 0);

struct nlm4_cancargs_wrapper {
	struct nlm4_cancargs		xdrgen;
	struct nlm_lock			lock;
};

static_assert(offsetof(struct nlm4_cancargs_wrapper, xdrgen) == 0);

struct nlm4_unlockargs_wrapper {
	struct nlm4_unlockargs		xdrgen;
	struct nlm_lock			lock;
};

static_assert(offsetof(struct nlm4_unlockargs_wrapper, xdrgen) == 0);

struct nlm4_notifyargs_wrapper {
	struct nlm4_notifyargs		xdrgen;
	struct nlm_reboot		reboot;
};

static_assert(offsetof(struct nlm4_notifyargs_wrapper, xdrgen) == 0);

struct nlm4_notify_wrapper {
	struct nlm4_notify		xdrgen;
};

static_assert(offsetof(struct nlm4_notify_wrapper, xdrgen) == 0);

struct nlm4_testres_wrapper {
	struct nlm4_testres		xdrgen;
	struct nlm_lock			lock;
};

struct nlm4_shareargs_wrapper {
	struct nlm4_shareargs		xdrgen;
	struct nlm_lock			lock;
};

static_assert(offsetof(struct nlm4_shareargs_wrapper, xdrgen) == 0);

static_assert(offsetof(struct nlm4_testres_wrapper, xdrgen) == 0);

struct nlm4_res_wrapper {
	struct nlm4_res			xdrgen;
	struct nlm_cookie		cookie;
};

static_assert(offsetof(struct nlm4_res_wrapper, xdrgen) == 0);

struct nlm4_shareres_wrapper {
	struct nlm4_shareres		xdrgen;
};

static_assert(offsetof(struct nlm4_shareres_wrapper, xdrgen) == 0);

static __be32
nlm4_netobj_to_cookie(struct nlm_cookie *cookie, netobj *object)
{
	if (object->len > NLM_MAXCOOKIELEN)
		return nlm_lck_denied_nolocks;
	cookie->len = object->len;
	memcpy(cookie->data, object->data, object->len);
	return nlm_granted;
}

static __be32
nlm4_lock_to_nlm_lock(struct nlm_lock *lock, struct nlm4_lock *alock)
{
	if (alock->fh.len > NFS_MAXFHSIZE)
		return nlm_lck_denied;
	lock->fh.size = alock->fh.len;
	memcpy(lock->fh.data, alock->fh.data, alock->fh.len);
	lock->oh.len = alock->oh.len;
	lock->oh.data = alock->oh.data;
	lock->svid = alock->svid;
	locks_init_lock(&lock->fl);
	lockd_set_file_lock_range4(&lock->fl, alock->l_offset, alock->l_len);
	return nlm_granted;
}

static struct nlm_host *
nlm4svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
{
	struct nlm_host *host;

	if (!nlmsvc_ops)
		return NULL;
	host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
	if (!host)
		return NULL;
	if (monitored && nsm_monitor(host) < 0) {
		nlmsvc_release_host(host);
		return NULL;
	}
	return host;
}

static __be32
nlm4svc_lookup_file(struct svc_rqst *rqstp, struct nlm_host *host,
		    struct nlm_lock *lock, struct nlm_file **filp,
		    struct nlm4_lock *xdr_lock, unsigned char type)
{
	bool is_test = (rqstp->rq_proc == NLMPROC4_TEST ||
			rqstp->rq_proc == NLMPROC4_TEST_MSG);
	struct file_lock *fl = &lock->fl;
	struct nlm_file *file = NULL;
	int mode;
	__be32 error;

	if (xdr_lock->fh.len > NFS_MAXFHSIZE)
		return nlm_lck_denied_nolocks;
	lock->fh.size = xdr_lock->fh.len;
	memcpy(lock->fh.data, xdr_lock->fh.data, xdr_lock->fh.len);

	lock->oh.len = xdr_lock->oh.len;
	lock->oh.data = xdr_lock->oh.data;

	lock->svid = xdr_lock->svid;
	lock->lock_start = xdr_lock->l_offset;
	lock->lock_len = xdr_lock->l_len;

	if (lock->lock_start > OFFSET_MAX ||
	    (lock->lock_len && ((lock->lock_len - 1) > (OFFSET_MAX - lock->lock_start))))
		return nlm4_fbig;

	locks_init_lock(fl);
	fl->c.flc_type = type;
	lockd_set_file_lock_range4(fl, lock->lock_start, lock->lock_len);

	mode = is_test ? O_RDWR : lock_to_openmode(fl);
	error = nlm_lookup_file(rqstp, &file, lock, mode);
	switch (error) {
	case nlm_granted:
		break;
	case nlm__int__stale_fh:
		return nlm4_stale_fh;
	case nlm__int__failed:
		return nlm4_failed;
	default:
		return error;
	}
	*filp = file;

	fl->c.flc_flags = FL_POSIX;
	fl->c.flc_file = is_test ? nlmsvc_file_file(file)
				 : file->f_file[mode];
	fl->c.flc_pid = current->tgid;
	fl->fl_lmops = &nlmsvc_lock_operations;
	nlmsvc_locks_init_private(fl, host, (pid_t)lock->svid);
	if (!fl->c.flc_owner)
		return nlm_lck_denied_nolocks;

	return nlm_granted;
}

/**
 * nlm4svc_proc_null - NULL: Test for presence of service
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully
 *
 * RPC synopsis:
 *   void NLMPROC4_NULL(void) = 0;
 */
static __be32
nlm4svc_proc_null(struct svc_rqst *rqstp)
{
	return rpc_success;
}

/**
 * nlm4svc_proc_test - TEST: Check for conflicting lock
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_drop_reply:		Do not send an RPC reply.
 *
 * RPC synopsis:
 *   nlm4_testres NLMPROC4_TEST(nlm4_testargs) = 1;
 *
 * Permissible procedure status codes:
 *   %NLM4_GRANTED:		The server would be able to grant the
 *				requested lock.
 *   %NLM4_DENIED:		The requested lock conflicted with existing
 *				lock reservations for the file.
 *   %NLM4_DENIED_NOLOCKS:	The server could not allocate the resources
 *				needed to process the request.
 *   %NLM4_DENIED_GRACE_PERIOD:	The server has recently restarted and is
 *				re-establishing existing locks, and is not
 *				yet ready to accept normal service requests.
 *
 * The Linux NLM server implementation also returns:
 *   %NLM4_STALE_FH:		The request specified an invalid file handle.
 *   %NLM4_FBIG:		The request specified a length or offset
 *				that exceeds the range supported by the
 *				server.
 *   %NLM4_FAILED:		The request failed for an unspecified reason.
 */
static __be32 nlm4svc_proc_test(struct svc_rqst *rqstp)
{
	struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
	unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
	struct nlm4_testres_wrapper *resp = rqstp->rq_resp;
	struct nlm_file	*file = NULL;
	struct nlm_host	*host;

	resp->xdrgen.cookie = argp->xdrgen.cookie;

	resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
	if (!host)
		goto out;

	resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock,
						     &file, &argp->xdrgen.alock,
						     type);
	if (resp->xdrgen.stat.stat)
		goto out;

	resp->xdrgen.stat.stat = nlmsvc_testlock(rqstp, file, host,
						 &argp->lock, &resp->lock);
	nlmsvc_release_lockowner(&argp->lock);

	if (resp->xdrgen.stat.stat == nlm_lck_denied) {
		struct nlm_lock *conf = &resp->lock;
		struct nlm4_holder *holder = &resp->xdrgen.stat.u.holder;

		holder->exclusive = (conf->fl.c.flc_type != F_RDLCK);
		holder->svid = conf->svid;
		holder->oh.len = conf->oh.len;
		holder->oh.data = conf->oh.data;
		holder->l_offset = conf->fl.fl_start;
		if (conf->fl.fl_end == OFFSET_MAX)
			holder->l_len = 0;
		else
			holder->l_len = conf->fl.fl_end - conf->fl.fl_start + 1;
	}

out:
	if (file)
		nlm_release_file(file);
	nlmsvc_release_host(host);
	return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
		rpc_drop_reply : rpc_success;
}

static __be32
nlm4svc_do_lock(struct svc_rqst *rqstp, bool monitored)
{
	struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp;
	unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
	struct nlm4_res_wrapper *resp = rqstp->rq_resp;
	struct nlm_file	*file = NULL;
	struct nlm_host	*host = NULL;

	resp->xdrgen.cookie = argp->xdrgen.cookie;

	resp->xdrgen.stat.stat = nlm4_netobj_to_cookie(&argp->cookie,
						       &argp->xdrgen.cookie);
	if (resp->xdrgen.stat.stat)
		goto out;

	resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name,
				   monitored);
	if (!host)
		goto out;

	resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock,
						     &file, &argp->xdrgen.alock,
						     type);
	if (resp->xdrgen.stat.stat)
		goto out;

	resp->xdrgen.stat.stat = nlmsvc_lock(rqstp, file, host, &argp->lock,
					     argp->xdrgen.block, &argp->cookie,
					     argp->xdrgen.reclaim);
	if (resp->xdrgen.stat.stat == nlm__int__deadlock)
		resp->xdrgen.stat.stat = nlm4_deadlock;

	nlmsvc_release_lockowner(&argp->lock);

out:
	if (file)
		nlm_release_file(file);
	nlmsvc_release_host(host);
	return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
		rpc_drop_reply : rpc_success;
}

/**
 * nlm4svc_proc_lock - LOCK: Establish a monitored lock
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_drop_reply:		Do not send an RPC reply.
 *
 * RPC synopsis:
 *   nlm4_res NLMPROC4_LOCK(nlm4_lockargs) = 2;
 *
 * Permissible procedure status codes:
 *   %NLM4_GRANTED:		The requested lock was granted.
 *   %NLM4_DENIED:		The requested lock conflicted with existing
 *				lock reservations for the file.
 *   %NLM4_DENIED_NOLOCKS:	The server could not allocate the resources
 *				needed to process the request.
 *   %NLM4_BLOCKED:		The blocking request cannot be granted
 *				immediately. The server will send an
 *				NLMPROC4_GRANTED callback to the client when
 *				the lock can be granted.
 *   %NLM4_DENIED_GRACE_PERIOD:	The server has recently restarted and is
 *				re-establishing existing locks, and is not
 *				yet ready to accept normal service requests.
 *
 * The Linux NLM server implementation also returns:
 *   %NLM4_DEADLCK:		The request could not be granted and
 *				blocking would cause a deadlock.
 *   %NLM4_STALE_FH:		The request specified an invalid file handle.
 *   %NLM4_FBIG:		The request specified a length or offset
 *				that exceeds the range supported by the
 *				server.
 *   %NLM4_FAILED:		The request failed for an unspecified reason.
 */
static __be32
nlm4svc_proc_lock(struct svc_rqst *rqstp)
{
	return nlm4svc_do_lock(rqstp, true);
}

/**
 * nlm4svc_proc_cancel - CANCEL: Cancel an outstanding blocked lock request
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully
 *   %rpc_drop_reply:		Do not send an RPC reply
 *
 * RPC synopsis:
 *   nlm4_res NLMPROC4_CANCEL(nlm4_cancargs) = 3;
 *
 * Permissible procedure status codes:
 *   %NLM4_LCK_GRANTED:		The requested lock was canceled.
 *   %NLM4_LCK_DENIED:		There was no lock to cancel.
 *   %NLM4_DENIED_GRACE_PERIOD:	The server has recently restarted and is
 *				re-establishing existing locks, and is not
 *				yet ready to accept normal service requests.
 *
 * The Linux NLM server implementation also returns:
 *   %NLM4_DENIED_NOLOCKS:	A needed resource could not be allocated.
 *   %NLM4_STALE_FH:		The request specified an invalid file handle.
 *   %NLM4_FBIG:		The request specified a length or offset
 *				that exceeds the range supported by the
 *				server.
 *   %NLM4_FAILED:		The request failed for an unspecified reason.
 */
static __be32
nlm4svc_proc_cancel(struct svc_rqst *rqstp)
{
	struct nlm4_cancargs_wrapper *argp = rqstp->rq_argp;
	unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
	struct nlm4_res_wrapper *resp = rqstp->rq_resp;
	struct net *net = SVC_NET(rqstp);
	struct nlm_host	*host = NULL;
	struct nlm_file	*file = NULL;

	resp->xdrgen.cookie = argp->xdrgen.cookie;

	resp->xdrgen.stat.stat = nlm_lck_denied_grace_period;
	if (locks_in_grace(net))
		goto out;

	resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
	if (!host)
		goto out;

	resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock,
						     &file, &argp->xdrgen.alock,
						     type);
	if (resp->xdrgen.stat.stat)
		goto out;

	resp->xdrgen.stat.stat = nlmsvc_cancel_blocked(net, file, &argp->lock);
	nlmsvc_release_lockowner(&argp->lock);

out:
	if (file)
		nlm_release_file(file);
	nlmsvc_release_host(host);
	return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
		rpc_drop_reply : rpc_success;
}

/**
 * nlm4svc_proc_unlock - UNLOCK: Remove a lock
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_drop_reply:		Do not send an RPC reply.
 *
 * RPC synopsis:
 *   nlm4_res NLMPROC4_UNLOCK(nlm4_unlockargs) = 4;
 *
 * Permissible procedure status codes:
 *   %NLM4_GRANTED:		The requested lock was released.
 *   %NLM4_DENIED_GRACE_PERIOD:	The server has recently restarted and is
 *				re-establishing existing locks, and is not
 *				yet ready to accept normal service requests.
 *
 * The Linux NLM server implementation also returns:
 *   %NLM4_DENIED_NOLOCKS:	A needed resource could not be allocated.
 *   %NLM4_STALE_FH:		The request specified an invalid file handle.
 *   %NLM4_FBIG:		The request specified a length or offset
 *				that exceeds the range supported by the
 *				server.
 *   %NLM4_FAILED:		The request failed for an unspecified reason.
 */
static __be32
nlm4svc_proc_unlock(struct svc_rqst *rqstp)
{
	struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp;
	struct nlm4_res_wrapper *resp = rqstp->rq_resp;
	struct net *net = SVC_NET(rqstp);
	struct nlm_host	*host = NULL;
	struct nlm_file	*file = NULL;

	resp->xdrgen.cookie = argp->xdrgen.cookie;

	resp->xdrgen.stat.stat = nlm_lck_denied_grace_period;
	if (locks_in_grace(net))
		goto out;

	resp->xdrgen.stat.stat = nlm_lck_denied_nolocks;
	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
	if (!host)
		goto out;

	resp->xdrgen.stat.stat = nlm4svc_lookup_file(rqstp, host, &argp->lock,
						     &file, &argp->xdrgen.alock,
						     F_UNLCK);
	if (resp->xdrgen.stat.stat)
		goto out;

	resp->xdrgen.stat.stat = nlmsvc_unlock(net, file, &argp->lock);
	nlmsvc_release_lockowner(&argp->lock);

out:
	if (file)
		nlm_release_file(file);
	nlmsvc_release_host(host);
	return resp->xdrgen.stat.stat == nlm__int__drop_reply ?
		rpc_drop_reply : rpc_success;
}

/**
 * nlm4svc_proc_granted - GRANTED: Server grants a previously blocked lock
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *
 * RPC synopsis:
 *   nlm4_res NLMPROC4_GRANTED(nlm4_testargs) = 5;
 *
 * Permissible procedure status codes:
 *   %NLM4_GRANTED:		The requested lock was granted.
 *   %NLM4_DENIED:		The server could not allocate the resources
 *				needed to process the request.
 *   %NLM4_DENIED_GRACE_PERIOD:	The server has recently restarted and is
 *				re-establishing existing locks, and is not
 *				yet ready to accept normal service requests.
 */
static __be32
nlm4svc_proc_granted(struct svc_rqst *rqstp)
{
	struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
	struct nlm4_res_wrapper *resp = rqstp->rq_resp;

	resp->xdrgen.cookie = argp->xdrgen.cookie;

	resp->xdrgen.stat.stat = nlm4_lock_to_nlm_lock(&argp->lock,
						       &argp->xdrgen.alock);
	if (resp->xdrgen.stat.stat)
		goto out;

	resp->xdrgen.stat.stat = nlmclnt_grant(svc_addr(rqstp), &argp->lock);

out:
	return rpc_success;
}

/*
 * This is the generic lockd callback for async RPC calls
 */
static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
{
}

static void nlm4svc_callback_release(void *data)
{
	nlmsvc_release_call(data);
}

static const struct rpc_call_ops nlm4svc_callback_ops = {
	.rpc_call_done = nlm4svc_callback_exit,
	.rpc_release = nlm4svc_callback_release,
};

/*
 * Dispatch an async callback RPC to a client with a pre-resolved host.
 * Caller provides a reference to @host; this function takes ownership
 * and releases it via nlmsvc_release_host() before returning.
 */
static __be32
nlm4svc_callback(struct svc_rqst *rqstp, struct nlm_host *host, u32 proc,
		 __be32 (*func)(struct svc_rqst *,  struct nlm_res *))
{
	struct nlm_rqst	*call;
	__be32 stat;

	call = nlm_alloc_call(host);
	nlmsvc_release_host(host);
	if (call == NULL)
		return rpc_system_err;

	stat = func(rqstp, &call->a_res);
	if (stat != 0) {
		nlmsvc_release_call(call);
		return stat;
	}

	call->a_flags = RPC_TASK_ASYNC;
	if (nlm_async_reply(call, proc, &nlm4svc_callback_ops) < 0)
		return rpc_system_err;
	return rpc_success;
}

static __be32
__nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
{
	struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
	unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
	struct nlm_lockowner *owner;
	struct nlm_file	*file = NULL;
	struct nlm_host	*host = NULL;

	resp->status = nlm_lck_denied_nolocks;
	if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
		goto out;

	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
	if (!host)
		goto out;

	resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock,
					   &file, &argp->xdrgen.alock, type);
	if (resp->status)
		goto out;

	owner = argp->lock.fl.c.flc_owner;
	resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock,
				       &resp->lock);
	nlmsvc_put_lockowner(owner);

out:
	if (file)
		nlm_release_file(file);
	nlmsvc_release_host(host);
	return resp->status == nlm__int__drop_reply ? rpc_drop_reply : rpc_success;
}

/**
 * nlm4svc_proc_test_msg - TEST_MSG: Check for conflicting lock
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_system_err:		RPC execution failed.
 *
 * RPC synopsis:
 *   void NLMPROC4_TEST_MSG(nlm4_testargs) = 6;
 *
 * The response to this request is delivered via the TEST_RES procedure.
 */
static __be32 nlm4svc_proc_test_msg(struct svc_rqst *rqstp)
{
	struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
	struct nlm_host *host;

	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
	if (!host)
		return rpc_system_err;

	return nlm4svc_callback(rqstp, host, NLMPROC4_TEST_RES,
				__nlm4svc_proc_test_msg);
}

static __be32
__nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
{
	struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp;
	unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
	struct nlm_file	*file = NULL;
	struct nlm_host	*host = NULL;

	resp->status = nlm_lck_denied_nolocks;
	if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
		goto out;

	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true);
	if (!host)
		goto out;

	resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock,
					   &file, &argp->xdrgen.alock, type);
	if (resp->status)
		goto out;

	resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock,
				   argp->xdrgen.block, &resp->cookie,
				   argp->xdrgen.reclaim);
	nlmsvc_release_lockowner(&argp->lock);

out:
	if (file)
		nlm_release_file(file);
	nlmsvc_release_host(host);
	return resp->status == nlm__int__drop_reply ?
		rpc_drop_reply : rpc_success;
}

/**
 * nlm4svc_proc_lock_msg - LOCK_MSG: Establish a monitored lock
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_system_err:		RPC execution failed.
 *
 * RPC synopsis:
 *   void NLMPROC4_LOCK_MSG(nlm4_lockargs) = 7;
 *
 * The response to this request is delivered via the LOCK_RES procedure.
 */
static __be32 nlm4svc_proc_lock_msg(struct svc_rqst *rqstp)
{
	struct nlm4_lockargs_wrapper *argp = rqstp->rq_argp;
	struct nlm_host *host;

	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, true);
	if (!host)
		return rpc_system_err;

	return nlm4svc_callback(rqstp, host, NLMPROC4_LOCK_RES,
				__nlm4svc_proc_lock_msg);
}

static __be32
__nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
{
	struct nlm4_cancargs_wrapper *argp = rqstp->rq_argp;
	unsigned char type = argp->xdrgen.exclusive ? F_WRLCK : F_RDLCK;
	struct net *net = SVC_NET(rqstp);
	struct nlm_file	*file = NULL;
	struct nlm_host	*host = NULL;

	resp->status = nlm_lck_denied_nolocks;
	if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
		goto out;

	resp->status = nlm_lck_denied_grace_period;
	if (locks_in_grace(net))
		goto out;

	resp->status = nlm_lck_denied_nolocks;
	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
	if (!host)
		goto out;

	resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock,
					   &file, &argp->xdrgen.alock, type);
	if (resp->status)
		goto out;

	resp->status = nlmsvc_cancel_blocked(net, file, &argp->lock);
	nlmsvc_release_lockowner(&argp->lock);

out:
	if (file)
		nlm_release_file(file);
	nlmsvc_release_host(host);
	return resp->status == nlm__int__drop_reply ?
		rpc_drop_reply : rpc_success;
}

/**
 * nlm4svc_proc_cancel_msg - CANCEL_MSG: Cancel an outstanding lock request
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_system_err:		RPC execution failed.
 *
 * RPC synopsis:
 *   void NLMPROC4_CANCEL_MSG(nlm4_cancargs) = 8;
 *
 * The response to this request is delivered via the CANCEL_RES procedure.
 */
static __be32 nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp)
{
	struct nlm4_cancargs_wrapper *argp = rqstp->rq_argp;
	struct nlm_host *host;

	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
	if (!host)
		return rpc_system_err;

	return nlm4svc_callback(rqstp, host, NLMPROC4_CANCEL_RES,
				__nlm4svc_proc_cancel_msg);
}

static __be32
__nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
{
	struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp;
	struct net *net = SVC_NET(rqstp);
	struct nlm_file	*file = NULL;
	struct nlm_host	*host = NULL;

	resp->status = nlm_lck_denied_nolocks;
	if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
		goto out;

	resp->status = nlm_lck_denied_grace_period;
	if (locks_in_grace(net))
		goto out;

	resp->status = nlm_lck_denied_nolocks;
	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
	if (!host)
		goto out;

	resp->status = nlm4svc_lookup_file(rqstp, host, &argp->lock,
					   &file, &argp->xdrgen.alock, F_UNLCK);
	if (resp->status)
		goto out;

	resp->status = nlmsvc_unlock(net, file, &argp->lock);
	nlmsvc_release_lockowner(&argp->lock);

out:
	if (file)
		nlm_release_file(file);
	nlmsvc_release_host(host);
	return resp->status == nlm__int__drop_reply ?
		rpc_drop_reply : rpc_success;
}

/**
 * nlm4svc_proc_unlock_msg - UNLOCK_MSG: Remove an existing lock
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_system_err:		RPC execution failed.
 *
 * RPC synopsis:
 *   void NLMPROC4_UNLOCK_MSG(nlm4_unlockargs) = 9;
 *
 * The response to this request is delivered via the UNLOCK_RES procedure.
 */
static __be32 nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp)
{
	struct nlm4_unlockargs_wrapper *argp = rqstp->rq_argp;
	struct nlm_host *host;

	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
	if (!host)
		return rpc_system_err;

	return nlm4svc_callback(rqstp, host, NLMPROC4_UNLOCK_RES,
				__nlm4svc_proc_unlock_msg);
}

static __be32
__nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_res *resp)
{
	struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;

	resp->status = nlm_lck_denied;
	if (nlm4_netobj_to_cookie(&resp->cookie, &argp->xdrgen.cookie))
		goto out;

	if (nlm4_lock_to_nlm_lock(&argp->lock, &argp->xdrgen.alock))
		goto out;

	resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock);

out:
	return rpc_success;
}

/**
 * nlm4svc_proc_granted_msg - GRANTED_MSG: Blocked lock has been granted
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_system_err:		RPC execution failed.
 *
 * RPC synopsis:
 *   void NLMPROC4_GRANTED_MSG(nlm4_testargs) = 10;
 *
 * The response to this request is delivered via the GRANTED_RES procedure.
 */
static __be32 nlm4svc_proc_granted_msg(struct svc_rqst *rqstp)
{
	struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
	struct nlm_host *host;

	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
	if (!host)
		return rpc_system_err;

	return nlm4svc_callback(rqstp, host, NLMPROC4_GRANTED_RES,
				__nlm4svc_proc_granted_msg);
}

/**
 * nlm4svc_proc_granted_res - GRANTED_RES: Lock Granted result
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *
 * RPC synopsis:
 *   void NLMPROC4_GRANTED_RES(nlm4_res) = 15;
 */
static __be32 nlm4svc_proc_granted_res(struct svc_rqst *rqstp)
{
	struct nlm4_res_wrapper *argp = rqstp->rq_argp;

	if (!nlmsvc_ops)
		return rpc_success;

	if (nlm4_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie))
		return rpc_success;
	nlmsvc_grant_reply(&argp->cookie, argp->xdrgen.stat.stat);

	return rpc_success;
}

/**
 * nlm4svc_proc_sm_notify - SM_NOTIFY: Peer has rebooted
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_system_err:		RPC execution failed.
 *
 * The SM_NOTIFY procedure is a private callback from Linux statd and is
 * not part of the official NLM protocol.
 *
 * RPC synopsis:
 *   void NLMPROC4_SM_NOTIFY(nlm4_notifyargs) = 16;
 */
static __be32 nlm4svc_proc_sm_notify(struct svc_rqst *rqstp)
{
	struct nlm4_notifyargs_wrapper *argp = rqstp->rq_argp;
	struct nlm_reboot *reboot = &argp->reboot;

	if (!nlm_privileged_requester(rqstp)) {
		char buf[RPC_MAX_ADDRBUFLEN];

		pr_warn("lockd: rejected NSM callback from %s\n",
			svc_print_addr(rqstp, buf, sizeof(buf)));
		return rpc_system_err;
	}

	reboot->len = argp->xdrgen.notify.name.len;
	reboot->mon = (char *)argp->xdrgen.notify.name.data;
	reboot->state = argp->xdrgen.notify.state;
	memcpy(&reboot->priv.data, argp->xdrgen.private,
	       sizeof(reboot->priv.data));

	nlm_host_rebooted(SVC_NET(rqstp), reboot);

	return rpc_success;
}

/**
 * nlm4svc_proc_unused - stub for unused procedures
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_proc_unavail:	Program can't support procedure.
 */
static __be32 nlm4svc_proc_unused(struct svc_rqst *rqstp)
{
	return rpc_proc_unavail;
}

/**
 * nlm4svc_proc_share - SHARE: Open a file using DOS file-sharing modes
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_drop_reply:		Do not send an RPC reply.
 *
 * RPC synopsis:
 *   nlm4_shareres NLMPROC4_SHARE(nlm4_shareargs) = 20;
 *
 * Permissible procedure status codes:
 *   %NLM4_GRANTED:		The requested share lock was granted.
 *   %NLM4_DENIED:		The requested lock conflicted with existing
 *				lock reservations for the file.
 *   %NLM4_DENIED_GRACE_PERIOD:	The server has recently restarted and is
 *				re-establishing existing locks, and is not
 *				yet ready to accept normal service requests.
 *
 * The Linux NLM server implementation also returns:
 *   %NLM4_DENIED_NOLOCKS:	A needed resource could not be allocated.
 *   %NLM4_STALE_FH:		The request specified an invalid file handle.
 *   %NLM4_FBIG:		The request specified a length or offset
 *				that exceeds the range supported by the
 *				server.
 *   %NLM4_FAILED:		The request failed for an unspecified reason.
 */
static __be32 nlm4svc_proc_share(struct svc_rqst *rqstp)
{
	struct nlm4_shareargs_wrapper *argp = rqstp->rq_argp;
	struct nlm4_shareres_wrapper *resp = rqstp->rq_resp;
	struct nlm_lock	*lock = &argp->lock;
	struct nlm_host	*host = NULL;
	struct nlm_file	*file = NULL;
	struct nlm4_lock xdr_lock = {
		.fh		= argp->xdrgen.share.fh,
		.oh		= argp->xdrgen.share.oh,
		.svid		= LOCKD_SHARE_SVID,
	};

	resp->xdrgen.cookie = argp->xdrgen.cookie;

	resp->xdrgen.stat = nlm_lck_denied_grace_period;
	if (locks_in_grace(SVC_NET(rqstp)) && !argp->xdrgen.reclaim)
		goto out;

	resp->xdrgen.stat = nlm_lck_denied_nolocks;
	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, true);
	if (!host)
		goto out;

	resp->xdrgen.stat = nlm4svc_lookup_file(rqstp, host, lock, &file,
						&xdr_lock, F_RDLCK);
	if (resp->xdrgen.stat)
		goto out;

	resp->xdrgen.stat = nlmsvc_share_file(host, file, &lock->oh,
					      argp->xdrgen.share.access,
					      argp->xdrgen.share.mode);

	nlmsvc_release_lockowner(lock);

out:
	if (file)
		nlm_release_file(file);
	nlmsvc_release_host(host);
	return resp->xdrgen.stat == nlm__int__drop_reply ?
		rpc_drop_reply : rpc_success;
}

/**
 * nlm4svc_proc_unshare - UNSHARE: Release a share reservation
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_drop_reply:		Do not send an RPC reply.
 *
 * RPC synopsis:
 *   nlm4_shareres NLMPROC4_UNSHARE(nlm4_shareargs) = 21;
 *
 * Permissible procedure status codes:
 *   %NLM4_GRANTED:		The share reservation was released.
 *   %NLM4_DENIED_GRACE_PERIOD:	The server has recently restarted and is
 *				re-establishing existing locks, and is not
 *				yet ready to accept normal service requests.
 *
 * The Linux NLM server implementation also returns:
 *   %NLM4_DENIED_NOLOCKS:	A needed resource could not be allocated.
 *   %NLM4_STALE_FH:		The request specified an invalid file handle.
 *   %NLM4_FBIG:		The request specified a length or offset
 *				that exceeds the range supported by the
 *				server.
 *   %NLM4_FAILED:		The request failed for an unspecified reason.
 */
static __be32 nlm4svc_proc_unshare(struct svc_rqst *rqstp)
{
	struct nlm4_shareargs_wrapper *argp = rqstp->rq_argp;
	struct nlm4_shareres_wrapper *resp = rqstp->rq_resp;
	struct nlm_lock	*lock = &argp->lock;
	struct nlm4_lock xdr_lock = {
		.fh		= argp->xdrgen.share.fh,
		.oh		= argp->xdrgen.share.oh,
		.svid		= LOCKD_SHARE_SVID,
	};
	struct nlm_host	*host = NULL;
	struct nlm_file	*file = NULL;

	resp->xdrgen.cookie = argp->xdrgen.cookie;

	resp->xdrgen.stat = nlm_lck_denied_grace_period;
	if (locks_in_grace(SVC_NET(rqstp)))
		goto out;

	resp->xdrgen.stat = nlm_lck_denied_nolocks;
	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.share.caller_name, true);
	if (!host)
		goto out;

	resp->xdrgen.stat = nlm4svc_lookup_file(rqstp, host, lock, &file,
						&xdr_lock, F_RDLCK);
	if (resp->xdrgen.stat)
		goto out;

	resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh);

	nlmsvc_release_lockowner(lock);

out:
	if (file)
		nlm_release_file(file);
	nlmsvc_release_host(host);
	return resp->xdrgen.stat == nlm__int__drop_reply ?
		rpc_drop_reply : rpc_success;
}

/**
 * nlm4svc_proc_nm_lock - NM_LOCK: Establish a non-monitored lock
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *   %rpc_drop_reply:		Do not send an RPC reply.
 *
 * RPC synopsis:
 *   nlm4_res NLMPROC4_NM_LOCK(nlm4_lockargs) = 22;
 *
 * Permissible procedure status codes:
 *   %NLM4_GRANTED:		The requested lock was granted.
 *   %NLM4_DENIED:		The requested lock conflicted with existing
 *				lock reservations for the file.
 *   %NLM4_DENIED_NOLOCKS:	The server could not allocate the resources
 *				needed to process the request.
 *   %NLM4_BLOCKED:		The blocking request cannot be granted
 *				immediately. The server will send an
 *				NLMPROC4_GRANTED callback to the client when
 *				the lock can be granted.
 *   %NLM4_DENIED_GRACE_PERIOD:	The server has recently restarted and is
 *				re-establishing existing locks, and is not
 *				yet ready to accept normal service requests.
 *
 * The Linux NLM server implementation also returns:
 *   %NLM4_DEADLCK:		The request could not be granted and
 *				blocking would cause a deadlock.
 *   %NLM4_STALE_FH:		The request specified an invalid file handle.
 *   %NLM4_FBIG:		The request specified a length or offset
 *				that exceeds the range supported by the
 *				server.
 *   %NLM4_FAILED:		The request failed for an unspecified reason.
 */
static __be32 nlm4svc_proc_nm_lock(struct svc_rqst *rqstp)
{
	return nlm4svc_do_lock(rqstp, false);
}

/**
 * nlm4svc_proc_free_all - FREE_ALL: Discard client's lock and share state
 * @rqstp: RPC transaction context
 *
 * Returns:
 *   %rpc_success:		RPC executed successfully.
 *
 * RPC synopsis:
 *   void NLMPROC4_FREE_ALL(nlm4_notify) = 23;
 */
static __be32 nlm4svc_proc_free_all(struct svc_rqst *rqstp)
{
	struct nlm4_notify_wrapper *argp = rqstp->rq_argp;
	struct nlm_host	*host;

	host = nlm4svc_lookup_host(rqstp, argp->xdrgen.name, false);
	if (!host)
		goto out;

	nlmsvc_free_host_resources(host);

	nlmsvc_release_host(host);

out:
	return rpc_success;
}


/*
 * NLMv4 Server procedures.
 */

static const struct svc_procedure nlm4svc_procedures[24] = {
	[NLMPROC4_NULL] = {
		.pc_func	= nlm4svc_proc_null,
		.pc_decode	= nlm4_svc_decode_void,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= XDR_void,
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "NULL",
	},
	[NLMPROC4_TEST] = {
		.pc_func	= nlm4svc_proc_test,
		.pc_decode	= nlm4_svc_decode_nlm4_testargs,
		.pc_encode	= nlm4_svc_encode_nlm4_testres,
		.pc_argsize	= sizeof(struct nlm4_testargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= sizeof(struct nlm4_testres_wrapper),
		.pc_xdrressize	= NLM4_nlm4_testres_sz,
		.pc_name	= "TEST",
	},
	[NLMPROC4_LOCK] = {
		.pc_func	= nlm4svc_proc_lock,
		.pc_decode	= nlm4_svc_decode_nlm4_lockargs,
		.pc_encode	= nlm4_svc_encode_nlm4_res,
		.pc_argsize	= sizeof(struct nlm4_lockargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= sizeof(struct nlm4_res_wrapper),
		.pc_xdrressize	= NLM4_nlm4_res_sz,
		.pc_name	= "LOCK",
	},
	[NLMPROC4_CANCEL] = {
		.pc_func	= nlm4svc_proc_cancel,
		.pc_decode	= nlm4_svc_decode_nlm4_cancargs,
		.pc_encode	= nlm4_svc_encode_nlm4_res,
		.pc_argsize	= sizeof(struct nlm4_cancargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= sizeof(struct nlm4_res_wrapper),
		.pc_xdrressize	= NLM4_nlm4_res_sz,
		.pc_name	= "CANCEL",
	},
	[NLMPROC4_UNLOCK] = {
		.pc_func	= nlm4svc_proc_unlock,
		.pc_decode	= nlm4_svc_decode_nlm4_unlockargs,
		.pc_encode	= nlm4_svc_encode_nlm4_res,
		.pc_argsize	= sizeof(struct nlm4_unlockargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= sizeof(struct nlm4_res_wrapper),
		.pc_xdrressize	= NLM4_nlm4_res_sz,
		.pc_name	= "UNLOCK",
	},
	[NLMPROC4_GRANTED] = {
		.pc_func	= nlm4svc_proc_granted,
		.pc_decode	= nlm4_svc_decode_nlm4_testargs,
		.pc_encode	= nlm4_svc_encode_nlm4_res,
		.pc_argsize	= sizeof(struct nlm4_testargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= sizeof(struct nlm4_res_wrapper),
		.pc_xdrressize	= NLM4_nlm4_res_sz,
		.pc_name	= "GRANTED",
	},
	[NLMPROC4_TEST_MSG] = {
		.pc_func	= nlm4svc_proc_test_msg,
		.pc_decode	= nlm4_svc_decode_nlm4_testargs,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_testargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "TEST_MSG",
	},
	[NLMPROC4_LOCK_MSG] = {
		.pc_func	= nlm4svc_proc_lock_msg,
		.pc_decode	= nlm4_svc_decode_nlm4_lockargs,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_lockargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "LOCK_MSG",
	},
	[NLMPROC4_CANCEL_MSG] = {
		.pc_func	= nlm4svc_proc_cancel_msg,
		.pc_decode	= nlm4_svc_decode_nlm4_cancargs,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_cancargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "CANCEL_MSG",
	},
	[NLMPROC4_UNLOCK_MSG] = {
		.pc_func	= nlm4svc_proc_unlock_msg,
		.pc_decode	= nlm4_svc_decode_nlm4_unlockargs,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_unlockargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "UNLOCK_MSG",
	},
	[NLMPROC4_GRANTED_MSG] = {
		.pc_func	= nlm4svc_proc_granted_msg,
		.pc_decode	= nlm4_svc_decode_nlm4_testargs,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_testargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "GRANTED_MSG",
	},
	[NLMPROC4_TEST_RES] = {
		.pc_func	= nlm4svc_proc_null,
		.pc_decode	= nlm4_svc_decode_nlm4_testres,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_testres),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "TEST_RES",
	},
	[NLMPROC4_LOCK_RES] = {
		.pc_func	= nlm4svc_proc_null,
		.pc_decode	= nlm4_svc_decode_nlm4_res,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_res),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "LOCK_RES",
	},
	[NLMPROC4_CANCEL_RES] = {
		.pc_func	= nlm4svc_proc_null,
		.pc_decode	= nlm4_svc_decode_nlm4_res,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_res),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "CANCEL_RES",
	},
	[NLMPROC4_UNLOCK_RES] = {
		.pc_func	= nlm4svc_proc_null,
		.pc_decode	= nlm4_svc_decode_nlm4_res,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_res),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "UNLOCK_RES",
	},
	[NLMPROC4_GRANTED_RES] = {
		.pc_func	= nlm4svc_proc_granted_res,
		.pc_decode	= nlm4_svc_decode_nlm4_res,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_res_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "GRANTED_RES",
	},
	[NLMPROC4_SM_NOTIFY] = {
		.pc_func	= nlm4svc_proc_sm_notify,
		.pc_decode	= nlm4_svc_decode_nlm4_notifyargs,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_notifyargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "SM_NOTIFY",
	},
	[17] = {
		.pc_func	= nlm4svc_proc_unused,
		.pc_decode	= nlm4_svc_decode_void,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= 0,
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "UNUSED",
	},
	[18] = {
		.pc_func	= nlm4svc_proc_unused,
		.pc_decode	= nlm4_svc_decode_void,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= 0,
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "UNUSED",
	},
	[19] = {
		.pc_func	= nlm4svc_proc_unused,
		.pc_decode	= nlm4_svc_decode_void,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= 0,
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "UNUSED",
	},
	[NLMPROC4_SHARE] = {
		.pc_func	= nlm4svc_proc_share,
		.pc_decode	= nlm4_svc_decode_nlm4_shareargs,
		.pc_encode	= nlm4_svc_encode_nlm4_shareres,
		.pc_argsize	= sizeof(struct nlm4_shareargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= sizeof(struct nlm4_shareres_wrapper),
		.pc_xdrressize	= NLM4_nlm4_shareres_sz,
		.pc_name	= "SHARE",
	},
	[NLMPROC4_UNSHARE] = {
		.pc_func	= nlm4svc_proc_unshare,
		.pc_decode	= nlm4_svc_decode_nlm4_shareargs,
		.pc_encode	= nlm4_svc_encode_nlm4_shareres,
		.pc_argsize	= sizeof(struct nlm4_shareargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= sizeof(struct nlm4_shareres_wrapper),
		.pc_xdrressize	= NLM4_nlm4_shareres_sz,
		.pc_name	= "UNSHARE",
	},
	[NLMPROC4_NM_LOCK] = {
		.pc_func	= nlm4svc_proc_nm_lock,
		.pc_decode	= nlm4_svc_decode_nlm4_lockargs,
		.pc_encode	= nlm4_svc_encode_nlm4_res,
		.pc_argsize	= sizeof(struct nlm4_lockargs_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= sizeof(struct nlm4_res_wrapper),
		.pc_xdrressize	= NLM4_nlm4_res_sz,
		.pc_name	= "NM_LOCK",
	},
	[NLMPROC4_FREE_ALL] = {
		.pc_func	= nlm4svc_proc_free_all,
		.pc_decode	= nlm4_svc_decode_nlm4_notify,
		.pc_encode	= nlm4_svc_encode_void,
		.pc_argsize	= sizeof(struct nlm4_notify_wrapper),
		.pc_argzero	= 0,
		.pc_ressize	= 0,
		.pc_xdrressize	= XDR_void,
		.pc_name	= "FREE_ALL",
	},
};

/*
 * Storage requirements for XDR arguments and results
 */
union nlm4svc_xdrstore {
	struct nlm4_testargs_wrapper	testargs;
	struct nlm4_lockargs_wrapper	lockargs;
	struct nlm4_cancargs_wrapper	cancargs;
	struct nlm4_unlockargs_wrapper	unlockargs;
	struct nlm4_notifyargs_wrapper	notifyargs;
	struct nlm4_shareargs_wrapper	shareargs;
	struct nlm4_notify_wrapper	notify;
	struct nlm4_testres_wrapper	testres;
	struct nlm4_res_wrapper		res;
	struct nlm4_shareres_wrapper	shareres;
};

static DEFINE_PER_CPU_ALIGNED(unsigned long,
			      nlm4svc_call_counters[ARRAY_SIZE(nlm4svc_procedures)]);

const struct svc_version nlmsvc_version4 = {
	.vs_vers	= 4,
	.vs_nproc	= ARRAY_SIZE(nlm4svc_procedures),
	.vs_proc	= nlm4svc_procedures,
	.vs_count	= nlm4svc_call_counters,
	.vs_dispatch	= nlmsvc_dispatch,
	.vs_xdrsize	= sizeof(union nlm4svc_xdrstore),
};