cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

svc.c (18722B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * linux/fs/lockd/svc.c
      4 *
      5 * This is the central lockd service.
      6 *
      7 * FIXME: Separate the lockd NFS server functionality from the lockd NFS
      8 * 	  client functionality. Oh why didn't Sun create two separate
      9 *	  services in the first place?
     10 *
     11 * Authors:	Olaf Kirch (okir@monad.swb.de)
     12 *
     13 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
     14 */
     15
     16#include <linux/module.h>
     17#include <linux/init.h>
     18#include <linux/sysctl.h>
     19#include <linux/moduleparam.h>
     20
     21#include <linux/sched/signal.h>
     22#include <linux/errno.h>
     23#include <linux/in.h>
     24#include <linux/uio.h>
     25#include <linux/smp.h>
     26#include <linux/mutex.h>
     27#include <linux/kthread.h>
     28#include <linux/freezer.h>
     29#include <linux/inetdevice.h>
     30
     31#include <linux/sunrpc/types.h>
     32#include <linux/sunrpc/stats.h>
     33#include <linux/sunrpc/clnt.h>
     34#include <linux/sunrpc/svc.h>
     35#include <linux/sunrpc/svcsock.h>
     36#include <linux/sunrpc/svc_xprt.h>
     37#include <net/ip.h>
     38#include <net/addrconf.h>
     39#include <net/ipv6.h>
     40#include <linux/lockd/lockd.h>
     41#include <linux/nfs.h>
     42
     43#include "netns.h"
     44#include "procfs.h"
     45
     46#define NLMDBG_FACILITY		NLMDBG_SVC
     47#define LOCKD_BUFSIZE		(1024 + NLMSVC_XDRSIZE)
     48#define ALLOWED_SIGS		(sigmask(SIGKILL))
     49
     50static struct svc_program	nlmsvc_program;
     51
     52const struct nlmsvc_binding	*nlmsvc_ops;
     53EXPORT_SYMBOL_GPL(nlmsvc_ops);
     54
     55static DEFINE_MUTEX(nlmsvc_mutex);
     56static unsigned int		nlmsvc_users;
     57static struct svc_serv		*nlmsvc_serv;
     58unsigned long			nlmsvc_timeout;
     59
     60unsigned int lockd_net_id;
     61
     62/*
     63 * These can be set at insmod time (useful for NFS as root filesystem),
     64 * and also changed through the sysctl interface.  -- Jamie Lokier, Aug 2003
     65 */
     66static unsigned long		nlm_grace_period;
     67static unsigned long		nlm_timeout = LOCKD_DFLT_TIMEO;
     68static int			nlm_udpport, nlm_tcpport;
     69
     70/* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */
     71static unsigned int		nlm_max_connections = 1024;
     72
     73/*
     74 * Constants needed for the sysctl interface.
     75 */
     76static const unsigned long	nlm_grace_period_min = 0;
     77static const unsigned long	nlm_grace_period_max = 240;
     78static const unsigned long	nlm_timeout_min = 3;
     79static const unsigned long	nlm_timeout_max = 20;
     80static const int		nlm_port_min = 0, nlm_port_max = 65535;
     81
     82#ifdef CONFIG_SYSCTL
     83static struct ctl_table_header * nlm_sysctl_table;
     84#endif
     85
     86static unsigned long get_lockd_grace_period(void)
     87{
     88	/* Note: nlm_timeout should always be nonzero */
     89	if (nlm_grace_period)
     90		return roundup(nlm_grace_period, nlm_timeout) * HZ;
     91	else
     92		return nlm_timeout * 5 * HZ;
     93}
     94
     95static void grace_ender(struct work_struct *grace)
     96{
     97	struct delayed_work *dwork = to_delayed_work(grace);
     98	struct lockd_net *ln = container_of(dwork, struct lockd_net,
     99					    grace_period_end);
    100
    101	locks_end_grace(&ln->lockd_manager);
    102}
    103
    104static void set_grace_period(struct net *net)
    105{
    106	unsigned long grace_period = get_lockd_grace_period();
    107	struct lockd_net *ln = net_generic(net, lockd_net_id);
    108
    109	locks_start_grace(net, &ln->lockd_manager);
    110	cancel_delayed_work_sync(&ln->grace_period_end);
    111	schedule_delayed_work(&ln->grace_period_end, grace_period);
    112}
    113
    114static void restart_grace(void)
    115{
    116	if (nlmsvc_ops) {
    117		struct net *net = &init_net;
    118		struct lockd_net *ln = net_generic(net, lockd_net_id);
    119
    120		cancel_delayed_work_sync(&ln->grace_period_end);
    121		locks_end_grace(&ln->lockd_manager);
    122		nlmsvc_invalidate_all();
    123		set_grace_period(net);
    124	}
    125}
    126
    127/*
    128 * This is the lockd kernel thread
    129 */
    130static int
    131lockd(void *vrqstp)
    132{
    133	int		err = 0;
    134	struct svc_rqst *rqstp = vrqstp;
    135	struct net *net = &init_net;
    136	struct lockd_net *ln = net_generic(net, lockd_net_id);
    137
    138	/* try_to_freeze() is called from svc_recv() */
    139	set_freezable();
    140
    141	/* Allow SIGKILL to tell lockd to drop all of its locks */
    142	allow_signal(SIGKILL);
    143
    144	dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
    145
    146	/*
    147	 * The main request loop. We don't terminate until the last
    148	 * NFS mount or NFS daemon has gone away.
    149	 */
    150	while (!kthread_should_stop()) {
    151		long timeout = MAX_SCHEDULE_TIMEOUT;
    152		RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
    153
    154		/* update sv_maxconn if it has changed */
    155		rqstp->rq_server->sv_maxconn = nlm_max_connections;
    156
    157		if (signalled()) {
    158			flush_signals(current);
    159			restart_grace();
    160			continue;
    161		}
    162
    163		timeout = nlmsvc_retry_blocked();
    164
    165		/*
    166		 * Find a socket with data available and call its
    167		 * recvfrom routine.
    168		 */
    169		err = svc_recv(rqstp, timeout);
    170		if (err == -EAGAIN || err == -EINTR)
    171			continue;
    172		dprintk("lockd: request from %s\n",
    173				svc_print_addr(rqstp, buf, sizeof(buf)));
    174
    175		svc_process(rqstp);
    176	}
    177	flush_signals(current);
    178	if (nlmsvc_ops)
    179		nlmsvc_invalidate_all();
    180	nlm_shutdown_hosts();
    181	cancel_delayed_work_sync(&ln->grace_period_end);
    182	locks_end_grace(&ln->lockd_manager);
    183
    184	dprintk("lockd_down: service stopped\n");
    185
    186	svc_exit_thread(rqstp);
    187	return 0;
    188}
    189
    190static int create_lockd_listener(struct svc_serv *serv, const char *name,
    191				 struct net *net, const int family,
    192				 const unsigned short port,
    193				 const struct cred *cred)
    194{
    195	struct svc_xprt *xprt;
    196
    197	xprt = svc_find_xprt(serv, name, net, family, 0);
    198	if (xprt == NULL)
    199		return svc_xprt_create(serv, name, net, family, port,
    200				       SVC_SOCK_DEFAULTS, cred);
    201	svc_xprt_put(xprt);
    202	return 0;
    203}
    204
    205static int create_lockd_family(struct svc_serv *serv, struct net *net,
    206			       const int family, const struct cred *cred)
    207{
    208	int err;
    209
    210	err = create_lockd_listener(serv, "udp", net, family, nlm_udpport,
    211			cred);
    212	if (err < 0)
    213		return err;
    214
    215	return create_lockd_listener(serv, "tcp", net, family, nlm_tcpport,
    216			cred);
    217}
    218
    219/*
    220 * Ensure there are active UDP and TCP listeners for lockd.
    221 *
    222 * Even if we have only TCP NFS mounts and/or TCP NFSDs, some
    223 * local services (such as rpc.statd) still require UDP, and
    224 * some NFS servers do not yet support NLM over TCP.
    225 *
    226 * Returns zero if all listeners are available; otherwise a
    227 * negative errno value is returned.
    228 */
    229static int make_socks(struct svc_serv *serv, struct net *net,
    230		const struct cred *cred)
    231{
    232	static int warned;
    233	int err;
    234
    235	err = create_lockd_family(serv, net, PF_INET, cred);
    236	if (err < 0)
    237		goto out_err;
    238
    239	err = create_lockd_family(serv, net, PF_INET6, cred);
    240	if (err < 0 && err != -EAFNOSUPPORT)
    241		goto out_err;
    242
    243	warned = 0;
    244	return 0;
    245
    246out_err:
    247	if (warned++ == 0)
    248		printk(KERN_WARNING
    249			"lockd_up: makesock failed, error=%d\n", err);
    250	svc_xprt_destroy_all(serv, net);
    251	svc_rpcb_cleanup(serv, net);
    252	return err;
    253}
    254
    255static int lockd_up_net(struct svc_serv *serv, struct net *net,
    256		const struct cred *cred)
    257{
    258	struct lockd_net *ln = net_generic(net, lockd_net_id);
    259	int error;
    260
    261	if (ln->nlmsvc_users++)
    262		return 0;
    263
    264	error = svc_bind(serv, net);
    265	if (error)
    266		goto err_bind;
    267
    268	error = make_socks(serv, net, cred);
    269	if (error < 0)
    270		goto err_bind;
    271	set_grace_period(net);
    272	dprintk("%s: per-net data created; net=%x\n", __func__, net->ns.inum);
    273	return 0;
    274
    275err_bind:
    276	ln->nlmsvc_users--;
    277	return error;
    278}
    279
    280static void lockd_down_net(struct svc_serv *serv, struct net *net)
    281{
    282	struct lockd_net *ln = net_generic(net, lockd_net_id);
    283
    284	if (ln->nlmsvc_users) {
    285		if (--ln->nlmsvc_users == 0) {
    286			nlm_shutdown_hosts_net(net);
    287			cancel_delayed_work_sync(&ln->grace_period_end);
    288			locks_end_grace(&ln->lockd_manager);
    289			svc_xprt_destroy_all(serv, net);
    290			svc_rpcb_cleanup(serv, net);
    291		}
    292	} else {
    293		pr_err("%s: no users! net=%x\n",
    294			__func__, net->ns.inum);
    295		BUG();
    296	}
    297}
    298
    299static int lockd_inetaddr_event(struct notifier_block *this,
    300	unsigned long event, void *ptr)
    301{
    302	struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
    303	struct sockaddr_in sin;
    304
    305	if (event != NETDEV_DOWN)
    306		goto out;
    307
    308	if (nlmsvc_serv) {
    309		dprintk("lockd_inetaddr_event: removed %pI4\n",
    310			&ifa->ifa_local);
    311		sin.sin_family = AF_INET;
    312		sin.sin_addr.s_addr = ifa->ifa_local;
    313		svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin);
    314	}
    315
    316out:
    317	return NOTIFY_DONE;
    318}
    319
    320static struct notifier_block lockd_inetaddr_notifier = {
    321	.notifier_call = lockd_inetaddr_event,
    322};
    323
    324#if IS_ENABLED(CONFIG_IPV6)
    325static int lockd_inet6addr_event(struct notifier_block *this,
    326	unsigned long event, void *ptr)
    327{
    328	struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
    329	struct sockaddr_in6 sin6;
    330
    331	if (event != NETDEV_DOWN)
    332		goto out;
    333
    334	if (nlmsvc_serv) {
    335		dprintk("lockd_inet6addr_event: removed %pI6\n", &ifa->addr);
    336		sin6.sin6_family = AF_INET6;
    337		sin6.sin6_addr = ifa->addr;
    338		if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
    339			sin6.sin6_scope_id = ifa->idev->dev->ifindex;
    340		svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin6);
    341	}
    342
    343out:
    344	return NOTIFY_DONE;
    345}
    346
    347static struct notifier_block lockd_inet6addr_notifier = {
    348	.notifier_call = lockd_inet6addr_event,
    349};
    350#endif
    351
    352static int lockd_get(void)
    353{
    354	struct svc_serv *serv;
    355	int error;
    356
    357	if (nlmsvc_serv) {
    358		svc_get(nlmsvc_serv);
    359		nlmsvc_users++;
    360		return 0;
    361	}
    362
    363	/*
    364	 * Sanity check: if there's no pid,
    365	 * we should be the first user ...
    366	 */
    367	if (nlmsvc_users)
    368		printk(KERN_WARNING
    369			"lockd_up: no pid, %d users??\n", nlmsvc_users);
    370
    371	if (!nlm_timeout)
    372		nlm_timeout = LOCKD_DFLT_TIMEO;
    373	nlmsvc_timeout = nlm_timeout * HZ;
    374
    375	serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, lockd);
    376	if (!serv) {
    377		printk(KERN_WARNING "lockd_up: create service failed\n");
    378		return -ENOMEM;
    379	}
    380
    381	serv->sv_maxconn = nlm_max_connections;
    382	error = svc_set_num_threads(serv, NULL, 1);
    383	/* The thread now holds the only reference */
    384	svc_put(serv);
    385	if (error < 0)
    386		return error;
    387
    388	nlmsvc_serv = serv;
    389	register_inetaddr_notifier(&lockd_inetaddr_notifier);
    390#if IS_ENABLED(CONFIG_IPV6)
    391	register_inet6addr_notifier(&lockd_inet6addr_notifier);
    392#endif
    393	dprintk("lockd_up: service created\n");
    394	nlmsvc_users++;
    395	return 0;
    396}
    397
    398static void lockd_put(void)
    399{
    400	if (WARN(nlmsvc_users <= 0, "lockd_down: no users!\n"))
    401		return;
    402	if (--nlmsvc_users)
    403		return;
    404
    405	unregister_inetaddr_notifier(&lockd_inetaddr_notifier);
    406#if IS_ENABLED(CONFIG_IPV6)
    407	unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
    408#endif
    409
    410	svc_set_num_threads(nlmsvc_serv, NULL, 0);
    411	nlmsvc_serv = NULL;
    412	dprintk("lockd_down: service destroyed\n");
    413}
    414
    415/*
    416 * Bring up the lockd process if it's not already up.
    417 */
    418int lockd_up(struct net *net, const struct cred *cred)
    419{
    420	int error;
    421
    422	mutex_lock(&nlmsvc_mutex);
    423
    424	error = lockd_get();
    425	if (error)
    426		goto err;
    427
    428	error = lockd_up_net(nlmsvc_serv, net, cred);
    429	if (error < 0) {
    430		lockd_put();
    431		goto err;
    432	}
    433
    434err:
    435	mutex_unlock(&nlmsvc_mutex);
    436	return error;
    437}
    438EXPORT_SYMBOL_GPL(lockd_up);
    439
    440/*
    441 * Decrement the user count and bring down lockd if we're the last.
    442 */
    443void
    444lockd_down(struct net *net)
    445{
    446	mutex_lock(&nlmsvc_mutex);
    447	lockd_down_net(nlmsvc_serv, net);
    448	lockd_put();
    449	mutex_unlock(&nlmsvc_mutex);
    450}
    451EXPORT_SYMBOL_GPL(lockd_down);
    452
    453#ifdef CONFIG_SYSCTL
    454
    455/*
    456 * Sysctl parameters (same as module parameters, different interface).
    457 */
    458
    459static struct ctl_table nlm_sysctls[] = {
    460	{
    461		.procname	= "nlm_grace_period",
    462		.data		= &nlm_grace_period,
    463		.maxlen		= sizeof(unsigned long),
    464		.mode		= 0644,
    465		.proc_handler	= proc_doulongvec_minmax,
    466		.extra1		= (unsigned long *) &nlm_grace_period_min,
    467		.extra2		= (unsigned long *) &nlm_grace_period_max,
    468	},
    469	{
    470		.procname	= "nlm_timeout",
    471		.data		= &nlm_timeout,
    472		.maxlen		= sizeof(unsigned long),
    473		.mode		= 0644,
    474		.proc_handler	= proc_doulongvec_minmax,
    475		.extra1		= (unsigned long *) &nlm_timeout_min,
    476		.extra2		= (unsigned long *) &nlm_timeout_max,
    477	},
    478	{
    479		.procname	= "nlm_udpport",
    480		.data		= &nlm_udpport,
    481		.maxlen		= sizeof(int),
    482		.mode		= 0644,
    483		.proc_handler	= proc_dointvec_minmax,
    484		.extra1		= (int *) &nlm_port_min,
    485		.extra2		= (int *) &nlm_port_max,
    486	},
    487	{
    488		.procname	= "nlm_tcpport",
    489		.data		= &nlm_tcpport,
    490		.maxlen		= sizeof(int),
    491		.mode		= 0644,
    492		.proc_handler	= proc_dointvec_minmax,
    493		.extra1		= (int *) &nlm_port_min,
    494		.extra2		= (int *) &nlm_port_max,
    495	},
    496	{
    497		.procname	= "nsm_use_hostnames",
    498		.data		= &nsm_use_hostnames,
    499		.maxlen		= sizeof(int),
    500		.mode		= 0644,
    501		.proc_handler	= proc_dobool,
    502	},
    503	{
    504		.procname	= "nsm_local_state",
    505		.data		= &nsm_local_state,
    506		.maxlen		= sizeof(int),
    507		.mode		= 0644,
    508		.proc_handler	= proc_dointvec,
    509	},
    510	{ }
    511};
    512
    513static struct ctl_table nlm_sysctl_dir[] = {
    514	{
    515		.procname	= "nfs",
    516		.mode		= 0555,
    517		.child		= nlm_sysctls,
    518	},
    519	{ }
    520};
    521
    522static struct ctl_table nlm_sysctl_root[] = {
    523	{
    524		.procname	= "fs",
    525		.mode		= 0555,
    526		.child		= nlm_sysctl_dir,
    527	},
    528	{ }
    529};
    530
    531#endif	/* CONFIG_SYSCTL */
    532
    533/*
    534 * Module (and sysfs) parameters.
    535 */
    536
    537#define param_set_min_max(name, type, which_strtol, min, max)		\
    538static int param_set_##name(const char *val, const struct kernel_param *kp) \
    539{									\
    540	char *endp;							\
    541	__typeof__(type) num = which_strtol(val, &endp, 0);		\
    542	if (endp == val || *endp || num < (min) || num > (max))		\
    543		return -EINVAL;						\
    544	*((type *) kp->arg) = num;					\
    545	return 0;							\
    546}
    547
    548static inline int is_callback(u32 proc)
    549{
    550	return proc == NLMPROC_GRANTED
    551		|| proc == NLMPROC_GRANTED_MSG
    552		|| proc == NLMPROC_TEST_RES
    553		|| proc == NLMPROC_LOCK_RES
    554		|| proc == NLMPROC_CANCEL_RES
    555		|| proc == NLMPROC_UNLOCK_RES
    556		|| proc == NLMPROC_NSM_NOTIFY;
    557}
    558
    559
    560static int lockd_authenticate(struct svc_rqst *rqstp)
    561{
    562	rqstp->rq_client = NULL;
    563	switch (rqstp->rq_authop->flavour) {
    564		case RPC_AUTH_NULL:
    565		case RPC_AUTH_UNIX:
    566			rqstp->rq_auth_stat = rpc_auth_ok;
    567			if (rqstp->rq_proc == 0)
    568				return SVC_OK;
    569			if (is_callback(rqstp->rq_proc)) {
    570				/* Leave it to individual procedures to
    571				 * call nlmsvc_lookup_host(rqstp)
    572				 */
    573				return SVC_OK;
    574			}
    575			return svc_set_client(rqstp);
    576	}
    577	rqstp->rq_auth_stat = rpc_autherr_badcred;
    578	return SVC_DENIED;
    579}
    580
    581
    582param_set_min_max(port, int, simple_strtol, 0, 65535)
    583param_set_min_max(grace_period, unsigned long, simple_strtoul,
    584		  nlm_grace_period_min, nlm_grace_period_max)
    585param_set_min_max(timeout, unsigned long, simple_strtoul,
    586		  nlm_timeout_min, nlm_timeout_max)
    587
    588MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
    589MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION ".");
    590MODULE_LICENSE("GPL");
    591
    592module_param_call(nlm_grace_period, param_set_grace_period, param_get_ulong,
    593		  &nlm_grace_period, 0644);
    594module_param_call(nlm_timeout, param_set_timeout, param_get_ulong,
    595		  &nlm_timeout, 0644);
    596module_param_call(nlm_udpport, param_set_port, param_get_int,
    597		  &nlm_udpport, 0644);
    598module_param_call(nlm_tcpport, param_set_port, param_get_int,
    599		  &nlm_tcpport, 0644);
    600module_param(nsm_use_hostnames, bool, 0644);
    601module_param(nlm_max_connections, uint, 0644);
    602
    603static int lockd_init_net(struct net *net)
    604{
    605	struct lockd_net *ln = net_generic(net, lockd_net_id);
    606
    607	INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender);
    608	INIT_LIST_HEAD(&ln->lockd_manager.list);
    609	ln->lockd_manager.block_opens = false;
    610	INIT_LIST_HEAD(&ln->nsm_handles);
    611	return 0;
    612}
    613
    614static void lockd_exit_net(struct net *net)
    615{
    616	struct lockd_net *ln = net_generic(net, lockd_net_id);
    617
    618	WARN_ONCE(!list_empty(&ln->lockd_manager.list),
    619		  "net %x %s: lockd_manager.list is not empty\n",
    620		  net->ns.inum, __func__);
    621	WARN_ONCE(!list_empty(&ln->nsm_handles),
    622		  "net %x %s: nsm_handles list is not empty\n",
    623		  net->ns.inum, __func__);
    624	WARN_ONCE(delayed_work_pending(&ln->grace_period_end),
    625		  "net %x %s: grace_period_end was not cancelled\n",
    626		  net->ns.inum, __func__);
    627}
    628
    629static struct pernet_operations lockd_net_ops = {
    630	.init = lockd_init_net,
    631	.exit = lockd_exit_net,
    632	.id = &lockd_net_id,
    633	.size = sizeof(struct lockd_net),
    634};
    635
    636
    637/*
    638 * Initialising and terminating the module.
    639 */
    640
    641static int __init init_nlm(void)
    642{
    643	int err;
    644
    645#ifdef CONFIG_SYSCTL
    646	err = -ENOMEM;
    647	nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root);
    648	if (nlm_sysctl_table == NULL)
    649		goto err_sysctl;
    650#endif
    651	err = register_pernet_subsys(&lockd_net_ops);
    652	if (err)
    653		goto err_pernet;
    654
    655	err = lockd_create_procfs();
    656	if (err)
    657		goto err_procfs;
    658
    659	return 0;
    660
    661err_procfs:
    662	unregister_pernet_subsys(&lockd_net_ops);
    663err_pernet:
    664#ifdef CONFIG_SYSCTL
    665	unregister_sysctl_table(nlm_sysctl_table);
    666err_sysctl:
    667#endif
    668	return err;
    669}
    670
    671static void __exit exit_nlm(void)
    672{
    673	/* FIXME: delete all NLM clients */
    674	nlm_shutdown_hosts();
    675	lockd_remove_procfs();
    676	unregister_pernet_subsys(&lockd_net_ops);
    677#ifdef CONFIG_SYSCTL
    678	unregister_sysctl_table(nlm_sysctl_table);
    679#endif
    680}
    681
    682module_init(init_nlm);
    683module_exit(exit_nlm);
    684
    685/**
    686 * nlmsvc_dispatch - Process an NLM Request
    687 * @rqstp: incoming request
    688 * @statp: pointer to location of accept_stat field in RPC Reply buffer
    689 *
    690 * Return values:
    691 *  %0: Processing complete; do not send a Reply
    692 *  %1: Processing complete; send Reply in rqstp->rq_res
    693 */
    694static int nlmsvc_dispatch(struct svc_rqst *rqstp, __be32 *statp)
    695{
    696	const struct svc_procedure *procp = rqstp->rq_procinfo;
    697
    698	svcxdr_init_decode(rqstp);
    699	if (!procp->pc_decode(rqstp, &rqstp->rq_arg_stream))
    700		goto out_decode_err;
    701
    702	*statp = procp->pc_func(rqstp);
    703	if (*statp == rpc_drop_reply)
    704		return 0;
    705	if (*statp != rpc_success)
    706		return 1;
    707
    708	svcxdr_init_encode(rqstp);
    709	if (!procp->pc_encode(rqstp, &rqstp->rq_res_stream))
    710		goto out_encode_err;
    711
    712	return 1;
    713
    714out_decode_err:
    715	*statp = rpc_garbage_args;
    716	return 1;
    717
    718out_encode_err:
    719	*statp = rpc_system_err;
    720	return 1;
    721}
    722
    723/*
    724 * Define NLM program and procedures
    725 */
    726static unsigned int nlmsvc_version1_count[17];
    727static const struct svc_version	nlmsvc_version1 = {
    728	.vs_vers	= 1,
    729	.vs_nproc	= 17,
    730	.vs_proc	= nlmsvc_procedures,
    731	.vs_count	= nlmsvc_version1_count,
    732	.vs_dispatch	= nlmsvc_dispatch,
    733	.vs_xdrsize	= NLMSVC_XDRSIZE,
    734};
    735static unsigned int nlmsvc_version3_count[24];
    736static const struct svc_version	nlmsvc_version3 = {
    737	.vs_vers	= 3,
    738	.vs_nproc	= 24,
    739	.vs_proc	= nlmsvc_procedures,
    740	.vs_count	= nlmsvc_version3_count,
    741	.vs_dispatch	= nlmsvc_dispatch,
    742	.vs_xdrsize	= NLMSVC_XDRSIZE,
    743};
    744#ifdef CONFIG_LOCKD_V4
    745static unsigned int nlmsvc_version4_count[24];
    746static const struct svc_version	nlmsvc_version4 = {
    747	.vs_vers	= 4,
    748	.vs_nproc	= 24,
    749	.vs_proc	= nlmsvc_procedures4,
    750	.vs_count	= nlmsvc_version4_count,
    751	.vs_dispatch	= nlmsvc_dispatch,
    752	.vs_xdrsize	= NLMSVC_XDRSIZE,
    753};
    754#endif
    755static const struct svc_version *nlmsvc_version[] = {
    756	[1] = &nlmsvc_version1,
    757	[3] = &nlmsvc_version3,
    758#ifdef CONFIG_LOCKD_V4
    759	[4] = &nlmsvc_version4,
    760#endif
    761};
    762
    763static struct svc_stat		nlmsvc_stats;
    764
    765#define NLM_NRVERS	ARRAY_SIZE(nlmsvc_version)
    766static struct svc_program	nlmsvc_program = {
    767	.pg_prog		= NLM_PROGRAM,		/* program number */
    768	.pg_nvers		= NLM_NRVERS,		/* number of entries in nlmsvc_version */
    769	.pg_vers		= nlmsvc_version,	/* version table */
    770	.pg_name		= "lockd",		/* service name */
    771	.pg_class		= "nfsd",		/* share authentication with nfsd */
    772	.pg_stats		= &nlmsvc_stats,	/* stats table */
    773	.pg_authenticate	= &lockd_authenticate,	/* export authentication */
    774	.pg_init_request	= svc_generic_init_request,
    775	.pg_rpcbind_set		= svc_generic_rpcbind_set,
    776};