xref: /openbmc/linux/fs/lockd/svc.c (revision 1760371b277718062211fc7eb6f3042c5051c1a5)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * linux/fs/lockd/svc.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * This is the central lockd service.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * FIXME: Separate the lockd NFS server functionality from the lockd NFS
81da177e4SLinus Torvalds  * 	  client functionality. Oh why didn't Sun create two separate
91da177e4SLinus Torvalds  *	  services in the first place?
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * Authors:	Olaf Kirch (okir@monad.swb.de)
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/module.h>
171da177e4SLinus Torvalds #include <linux/init.h>
181da177e4SLinus Torvalds #include <linux/sysctl.h>
191da177e4SLinus Torvalds #include <linux/moduleparam.h>
201da177e4SLinus Torvalds 
213f07c014SIngo Molnar #include <linux/sched/signal.h>
221da177e4SLinus Torvalds #include <linux/errno.h>
231da177e4SLinus Torvalds #include <linux/in.h>
241da177e4SLinus Torvalds #include <linux/uio.h>
251da177e4SLinus Torvalds #include <linux/smp.h>
26353ab6e9SIngo Molnar #include <linux/mutex.h>
27d751a7cdSJeff Layton #include <linux/kthread.h>
2883144186SRafael J. Wysocki #include <linux/freezer.h>
290751ddf7SScott Mayhew #include <linux/inetdevice.h>
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds #include <linux/sunrpc/types.h>
321da177e4SLinus Torvalds #include <linux/sunrpc/stats.h>
331da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
341da177e4SLinus Torvalds #include <linux/sunrpc/svc.h>
351da177e4SLinus Torvalds #include <linux/sunrpc/svcsock.h>
360751ddf7SScott Mayhew #include <linux/sunrpc/svc_xprt.h>
3724e36663SNeilBrown #include <net/ip.h>
380751ddf7SScott Mayhew #include <net/addrconf.h>
390751ddf7SScott Mayhew #include <net/ipv6.h>
401da177e4SLinus Torvalds #include <linux/lockd/lockd.h>
411da177e4SLinus Torvalds #include <linux/nfs.h>
421da177e4SLinus Torvalds 
43a9c5d73aSStanislav Kinsbursky #include "netns.h"
44d68e3c4aSJeff Layton #include "procfs.h"
45a9c5d73aSStanislav Kinsbursky 
461da177e4SLinus Torvalds #define NLMDBG_FACILITY		NLMDBG_SVC
471da177e4SLinus Torvalds #define LOCKD_BUFSIZE		(1024 + NLMSVC_XDRSIZE)
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds static struct svc_program	nlmsvc_program;
501da177e4SLinus Torvalds 
512a297450SJulia Lawall const struct nlmsvc_binding	*nlmsvc_ops;
522de59872STrond Myklebust EXPORT_SYMBOL_GPL(nlmsvc_ops);
531da177e4SLinus Torvalds 
54353ab6e9SIngo Molnar static DEFINE_MUTEX(nlmsvc_mutex);
551da177e4SLinus Torvalds static unsigned int		nlmsvc_users;
562840fe86SNeilBrown static struct svc_serv		*nlmsvc_serv;
571da177e4SLinus Torvalds unsigned long			nlmsvc_timeout;
581da177e4SLinus Torvalds 
nlmsvc_request_retry(struct timer_list * tl)59c743b425SNeilBrown static void nlmsvc_request_retry(struct timer_list *tl)
60c743b425SNeilBrown {
61c743b425SNeilBrown 	svc_wake_up(nlmsvc_serv);
62c743b425SNeilBrown }
63c743b425SNeilBrown DEFINE_TIMER(nlmsvc_retry, nlmsvc_request_retry);
64c743b425SNeilBrown 
65c7d03a00SAlexey Dobriyan unsigned int lockd_net_id;
66a9c5d73aSStanislav Kinsbursky 
671da177e4SLinus Torvalds /*
681da177e4SLinus Torvalds  * These can be set at insmod time (useful for NFS as root filesystem),
691da177e4SLinus Torvalds  * and also changed through the sysctl interface.  -- Jamie Lokier, Aug 2003
701da177e4SLinus Torvalds  */
711da177e4SLinus Torvalds static unsigned long		nlm_grace_period;
721da177e4SLinus Torvalds static unsigned long		nlm_timeout = LOCKD_DFLT_TIMEO;
731da177e4SLinus Torvalds static int			nlm_udpport, nlm_tcpport;
741da177e4SLinus Torvalds 
75c72a476bSJeff Layton /* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */
76c72a476bSJeff Layton static unsigned int		nlm_max_connections = 1024;
77c72a476bSJeff Layton 
781da177e4SLinus Torvalds /*
791da177e4SLinus Torvalds  * Constants needed for the sysctl interface.
801da177e4SLinus Torvalds  */
811da177e4SLinus Torvalds static const unsigned long	nlm_grace_period_min = 0;
821da177e4SLinus Torvalds static const unsigned long	nlm_grace_period_max = 240;
831da177e4SLinus Torvalds static const unsigned long	nlm_timeout_min = 3;
841da177e4SLinus Torvalds static const unsigned long	nlm_timeout_max = 20;
851da177e4SLinus Torvalds 
8690d5b180SChuck Lever #ifdef CONFIG_SYSCTL
87fc412a61STom Rix static const int		nlm_port_min = 0, nlm_port_max = 65535;
881da177e4SLinus Torvalds static struct ctl_table_header * nlm_sysctl_table;
8990d5b180SChuck Lever #endif
901da177e4SLinus Torvalds 
get_lockd_grace_period(void)919a8db97eSMarc Eshel static unsigned long get_lockd_grace_period(void)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds 	/* Note: nlm_timeout should always be nonzero */
941da177e4SLinus Torvalds 	if (nlm_grace_period)
959a8db97eSMarc Eshel 		return roundup(nlm_grace_period, nlm_timeout) * HZ;
961da177e4SLinus Torvalds 	else
979a8db97eSMarc Eshel 		return nlm_timeout * 5 * HZ;
989a8db97eSMarc Eshel }
999a8db97eSMarc Eshel 
grace_ender(struct work_struct * grace)10008d44a35SStanislav Kinsbursky static void grace_ender(struct work_struct *grace)
1011da177e4SLinus Torvalds {
102ea44463fSGeliang Tang 	struct delayed_work *dwork = to_delayed_work(grace);
10308d44a35SStanislav Kinsbursky 	struct lockd_net *ln = container_of(dwork, struct lockd_net,
10408d44a35SStanislav Kinsbursky 					    grace_period_end);
10508d44a35SStanislav Kinsbursky 
10608d44a35SStanislav Kinsbursky 	locks_end_grace(&ln->lockd_manager);
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds 
set_grace_period(struct net * net)1095ccb0066SStanislav Kinsbursky static void set_grace_period(struct net *net)
110c8ab5f2aSJ. Bruce Fields {
111af558e33SJ. Bruce Fields 	unsigned long grace_period = get_lockd_grace_period();
1125ccb0066SStanislav Kinsbursky 	struct lockd_net *ln = net_generic(net, lockd_net_id);
113c8ab5f2aSJ. Bruce Fields 
1145ccb0066SStanislav Kinsbursky 	locks_start_grace(net, &ln->lockd_manager);
11566547b02SStanislav Kinsbursky 	cancel_delayed_work_sync(&ln->grace_period_end);
11666547b02SStanislav Kinsbursky 	schedule_delayed_work(&ln->grace_period_end, grace_period);
117c8ab5f2aSJ. Bruce Fields }
118c8ab5f2aSJ. Bruce Fields 
1191da177e4SLinus Torvalds /*
1201da177e4SLinus Torvalds  * This is the lockd kernel thread
1211da177e4SLinus Torvalds  */
122d751a7cdSJeff Layton static int
lockd(void * vrqstp)123d751a7cdSJeff Layton lockd(void *vrqstp)
1241da177e4SLinus Torvalds {
125d751a7cdSJeff Layton 	struct svc_rqst *rqstp = vrqstp;
126efda760fSJ. Bruce Fields 	struct net *net = &init_net;
127efda760fSJ. Bruce Fields 	struct lockd_net *ln = net_generic(net, lockd_net_id);
1281da177e4SLinus Torvalds 
129d751a7cdSJeff Layton 	/* try_to_freeze() is called from svc_recv() */
13083144186SRafael J. Wysocki 	set_freezable();
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds 	dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 	/*
1351da177e4SLinus Torvalds 	 * The main request loop. We don't terminate until the last
136d751a7cdSJeff Layton 	 * NFS mount or NFS daemon has gone away.
1371da177e4SLinus Torvalds 	 */
138d751a7cdSJeff Layton 	while (!kthread_should_stop()) {
139c72a476bSJeff Layton 		/* update sv_maxconn if it has changed */
140c72a476bSJeff Layton 		rqstp->rq_server->sv_maxconn = nlm_max_connections;
141c72a476bSJeff Layton 
142c743b425SNeilBrown 		nlmsvc_retry_blocked();
143c743b425SNeilBrown 		svc_recv(rqstp);
1441da177e4SLinus Torvalds 	}
1451da177e4SLinus Torvalds 	if (nlmsvc_ops)
1461da177e4SLinus Torvalds 		nlmsvc_invalidate_all();
1471da177e4SLinus Torvalds 	nlm_shutdown_hosts();
148efda760fSJ. Bruce Fields 	cancel_delayed_work_sync(&ln->grace_period_end);
149efda760fSJ. Bruce Fields 	locks_end_grace(&ln->lockd_manager);
1506a4e2527SNeilBrown 
1516a4e2527SNeilBrown 	dprintk("lockd_down: service stopped\n");
1526a4e2527SNeilBrown 
1536a4e2527SNeilBrown 	svc_exit_thread(rqstp);
154f49169c9SChuck Lever 	return 0;
1551da177e4SLinus Torvalds }
1561da177e4SLinus Torvalds 
create_lockd_listener(struct svc_serv * serv,const char * name,struct net * net,const int family,const unsigned short port,const struct cred * cred)157eb16e907SChuck Lever static int create_lockd_listener(struct svc_serv *serv, const char *name,
158c228fa20SStanislav Kinsbursky 				 struct net *net, const int family,
15940373b12STrond Myklebust 				 const unsigned short port,
16040373b12STrond Myklebust 				 const struct cred *cred)
161d3fe5ea7SChuck Lever {
162d3fe5ea7SChuck Lever 	struct svc_xprt *xprt;
163d3fe5ea7SChuck Lever 
164c228fa20SStanislav Kinsbursky 	xprt = svc_find_xprt(serv, name, net, family, 0);
165d3fe5ea7SChuck Lever 	if (xprt == NULL)
166352ad314SChuck Lever 		return svc_xprt_create(serv, name, net, family, port,
1674df493a2STrond Myklebust 				       SVC_SOCK_DEFAULTS, cred);
168d3fe5ea7SChuck Lever 	svc_xprt_put(xprt);
169d3fe5ea7SChuck Lever 	return 0;
170d3fe5ea7SChuck Lever }
171d3fe5ea7SChuck Lever 
create_lockd_family(struct svc_serv * serv,struct net * net,const int family,const struct cred * cred)172c228fa20SStanislav Kinsbursky static int create_lockd_family(struct svc_serv *serv, struct net *net,
17340373b12STrond Myklebust 			       const int family, const struct cred *cred)
174eb16e907SChuck Lever {
175eb16e907SChuck Lever 	int err;
176eb16e907SChuck Lever 
17740373b12STrond Myklebust 	err = create_lockd_listener(serv, "udp", net, family, nlm_udpport,
17840373b12STrond Myklebust 			cred);
179eb16e907SChuck Lever 	if (err < 0)
180eb16e907SChuck Lever 		return err;
181eb16e907SChuck Lever 
18240373b12STrond Myklebust 	return create_lockd_listener(serv, "tcp", net, family, nlm_tcpport,
18340373b12STrond Myklebust 			cred);
184eb16e907SChuck Lever }
185eb16e907SChuck Lever 
186482fb94eSChuck Lever /*
1878c3916f4SChuck Lever  * Ensure there are active UDP and TCP listeners for lockd.
1888c3916f4SChuck Lever  *
1898c3916f4SChuck Lever  * Even if we have only TCP NFS mounts and/or TCP NFSDs, some
1908c3916f4SChuck Lever  * local services (such as rpc.statd) still require UDP, and
1918c3916f4SChuck Lever  * some NFS servers do not yet support NLM over TCP.
1928c3916f4SChuck Lever  *
1938c3916f4SChuck Lever  * Returns zero if all listeners are available; otherwise a
1948c3916f4SChuck Lever  * negative errno value is returned.
19524e36663SNeilBrown  */
make_socks(struct svc_serv * serv,struct net * net,const struct cred * cred)19640373b12STrond Myklebust static int make_socks(struct svc_serv *serv, struct net *net,
19740373b12STrond Myklebust 		const struct cred *cred)
198482fb94eSChuck Lever {
1997dcf91ecSNeilBrown 	static int warned;
2000dba7c2aSChuck Lever 	int err;
201482fb94eSChuck Lever 
20240373b12STrond Myklebust 	err = create_lockd_family(serv, net, PF_INET, cred);
2030dba7c2aSChuck Lever 	if (err < 0)
2040dba7c2aSChuck Lever 		goto out_err;
2050dba7c2aSChuck Lever 
20640373b12STrond Myklebust 	err = create_lockd_family(serv, net, PF_INET6, cred);
207eb16e907SChuck Lever 	if (err < 0 && err != -EAFNOSUPPORT)
2080dba7c2aSChuck Lever 		goto out_err;
2090dba7c2aSChuck Lever 
2107dcf91ecSNeilBrown 	warned = 0;
2110dba7c2aSChuck Lever 	return 0;
2120dba7c2aSChuck Lever 
2130dba7c2aSChuck Lever out_err:
2140dba7c2aSChuck Lever 	if (warned++ == 0)
2157dcf91ecSNeilBrown 		printk(KERN_WARNING
2167dcf91ecSNeilBrown 			"lockd_up: makesock failed, error=%d\n", err);
217c7d7ec8fSChuck Lever 	svc_xprt_destroy_all(serv, net);
21887cdd864SChuck Lever 	svc_rpcb_cleanup(serv, net);
21924e36663SNeilBrown 	return err;
22024e36663SNeilBrown }
22124e36663SNeilBrown 
lockd_up_net(struct svc_serv * serv,struct net * net,const struct cred * cred)22240373b12STrond Myklebust static int lockd_up_net(struct svc_serv *serv, struct net *net,
22340373b12STrond Myklebust 		const struct cred *cred)
224bb2224dfSStanislav Kinsbursky {
225bb2224dfSStanislav Kinsbursky 	struct lockd_net *ln = net_generic(net, lockd_net_id);
226bb2224dfSStanislav Kinsbursky 	int error;
227bb2224dfSStanislav Kinsbursky 
228786185b5SStanislav Kinsbursky 	if (ln->nlmsvc_users++)
229bb2224dfSStanislav Kinsbursky 		return 0;
230bb2224dfSStanislav Kinsbursky 
231dbf9b5d7SStanislav Kinsbursky 	error = svc_bind(serv, net);
232bb2224dfSStanislav Kinsbursky 	if (error)
233dbf9b5d7SStanislav Kinsbursky 		goto err_bind;
234bb2224dfSStanislav Kinsbursky 
23540373b12STrond Myklebust 	error = make_socks(serv, net, cred);
236bb2224dfSStanislav Kinsbursky 	if (error < 0)
2377c17705eSJ. Bruce Fields 		goto err_bind;
2385630f7faSStanislav Kinsbursky 	set_grace_period(net);
239e919b076SVasily Averin 	dprintk("%s: per-net data created; net=%x\n", __func__, net->ns.inum);
240bb2224dfSStanislav Kinsbursky 	return 0;
241bb2224dfSStanislav Kinsbursky 
242dbf9b5d7SStanislav Kinsbursky err_bind:
243786185b5SStanislav Kinsbursky 	ln->nlmsvc_users--;
244bb2224dfSStanislav Kinsbursky 	return error;
245bb2224dfSStanislav Kinsbursky }
246bb2224dfSStanislav Kinsbursky 
lockd_down_net(struct svc_serv * serv,struct net * net)2474db77695SStanislav Kinsbursky static void lockd_down_net(struct svc_serv *serv, struct net *net)
248bb2224dfSStanislav Kinsbursky {
249bb2224dfSStanislav Kinsbursky 	struct lockd_net *ln = net_generic(net, lockd_net_id);
250bb2224dfSStanislav Kinsbursky 
251bb2224dfSStanislav Kinsbursky 	if (ln->nlmsvc_users) {
2523b64739fSStanislav Kinsbursky 		if (--ln->nlmsvc_users == 0) {
2533b64739fSStanislav Kinsbursky 			nlm_shutdown_hosts_net(net);
2543a2b19d1SVasily Averin 			cancel_delayed_work_sync(&ln->grace_period_end);
2553a2b19d1SVasily Averin 			locks_end_grace(&ln->lockd_manager);
256c7d7ec8fSChuck Lever 			svc_xprt_destroy_all(serv, net);
25787cdd864SChuck Lever 			svc_rpcb_cleanup(serv, net);
2583b64739fSStanislav Kinsbursky 		}
259bb2224dfSStanislav Kinsbursky 	} else {
2606b044fbaSNeilBrown 		pr_err("%s: no users! net=%x\n",
2616b044fbaSNeilBrown 			__func__, net->ns.inum);
262bb2224dfSStanislav Kinsbursky 		BUG();
263bb2224dfSStanislav Kinsbursky 	}
264bb2224dfSStanislav Kinsbursky }
265bb2224dfSStanislav Kinsbursky 
lockd_inetaddr_event(struct notifier_block * this,unsigned long event,void * ptr)2660751ddf7SScott Mayhew static int lockd_inetaddr_event(struct notifier_block *this,
2670751ddf7SScott Mayhew 	unsigned long event, void *ptr)
2680751ddf7SScott Mayhew {
2690751ddf7SScott Mayhew 	struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
2700751ddf7SScott Mayhew 	struct sockaddr_in sin;
2710751ddf7SScott Mayhew 
2725a8a7ff5SNeilBrown 	if (event != NETDEV_DOWN)
2730751ddf7SScott Mayhew 		goto out;
2740751ddf7SScott Mayhew 
2752840fe86SNeilBrown 	if (nlmsvc_serv) {
2760751ddf7SScott Mayhew 		dprintk("lockd_inetaddr_event: removed %pI4\n",
2770751ddf7SScott Mayhew 			&ifa->ifa_local);
2780751ddf7SScott Mayhew 		sin.sin_family = AF_INET;
2790751ddf7SScott Mayhew 		sin.sin_addr.s_addr = ifa->ifa_local;
2802840fe86SNeilBrown 		svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin);
2810751ddf7SScott Mayhew 	}
2820751ddf7SScott Mayhew 
2830751ddf7SScott Mayhew out:
2840751ddf7SScott Mayhew 	return NOTIFY_DONE;
2850751ddf7SScott Mayhew }
2860751ddf7SScott Mayhew 
2870751ddf7SScott Mayhew static struct notifier_block lockd_inetaddr_notifier = {
2880751ddf7SScott Mayhew 	.notifier_call = lockd_inetaddr_event,
2890751ddf7SScott Mayhew };
2900751ddf7SScott Mayhew 
2910751ddf7SScott Mayhew #if IS_ENABLED(CONFIG_IPV6)
lockd_inet6addr_event(struct notifier_block * this,unsigned long event,void * ptr)2920751ddf7SScott Mayhew static int lockd_inet6addr_event(struct notifier_block *this,
2930751ddf7SScott Mayhew 	unsigned long event, void *ptr)
2940751ddf7SScott Mayhew {
2950751ddf7SScott Mayhew 	struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
2960751ddf7SScott Mayhew 	struct sockaddr_in6 sin6;
2970751ddf7SScott Mayhew 
2985a8a7ff5SNeilBrown 	if (event != NETDEV_DOWN)
2990751ddf7SScott Mayhew 		goto out;
3000751ddf7SScott Mayhew 
3012840fe86SNeilBrown 	if (nlmsvc_serv) {
3020751ddf7SScott Mayhew 		dprintk("lockd_inet6addr_event: removed %pI6\n", &ifa->addr);
3030751ddf7SScott Mayhew 		sin6.sin6_family = AF_INET6;
3040751ddf7SScott Mayhew 		sin6.sin6_addr = ifa->addr;
305c01410f7SScott Mayhew 		if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
306c01410f7SScott Mayhew 			sin6.sin6_scope_id = ifa->idev->dev->ifindex;
3072840fe86SNeilBrown 		svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin6);
3080751ddf7SScott Mayhew 	}
3090751ddf7SScott Mayhew 
3100751ddf7SScott Mayhew out:
3110751ddf7SScott Mayhew 	return NOTIFY_DONE;
3120751ddf7SScott Mayhew }
3130751ddf7SScott Mayhew 
3140751ddf7SScott Mayhew static struct notifier_block lockd_inet6addr_notifier = {
3150751ddf7SScott Mayhew 	.notifier_call = lockd_inet6addr_event,
3160751ddf7SScott Mayhew };
3170751ddf7SScott Mayhew #endif
3180751ddf7SScott Mayhew 
lockd_get(void)319ecd3ad68SNeilBrown static int lockd_get(void)
3201da177e4SLinus Torvalds {
3211da177e4SLinus Torvalds 	struct svc_serv *serv;
322b73a2972SNeilBrown 	int error;
3231da177e4SLinus Torvalds 
3242840fe86SNeilBrown 	if (nlmsvc_serv) {
325ecd3ad68SNeilBrown 		nlmsvc_users++;
3262840fe86SNeilBrown 		return 0;
3272840fe86SNeilBrown 	}
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 	/*
3301da177e4SLinus Torvalds 	 * Sanity check: if there's no pid,
3311da177e4SLinus Torvalds 	 * we should be the first user ...
3321da177e4SLinus Torvalds 	 */
3334a3ae42dSNeilBrown 	if (nlmsvc_users)
3341da177e4SLinus Torvalds 		printk(KERN_WARNING
3351da177e4SLinus Torvalds 			"lockd_up: no pid, %d users??\n", nlmsvc_users);
3361da177e4SLinus Torvalds 
33706bed7d1STrond Myklebust 	if (!nlm_timeout)
33806bed7d1STrond Myklebust 		nlm_timeout = LOCKD_DFLT_TIMEO;
33906bed7d1STrond Myklebust 	nlmsvc_timeout = nlm_timeout * HZ;
34006bed7d1STrond Myklebust 
34137902c63SChuck Lever 	serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, lockd);
3421da177e4SLinus Torvalds 	if (!serv) {
3431da177e4SLinus Torvalds 		printk(KERN_WARNING "lockd_up: create service failed\n");
3442840fe86SNeilBrown 		return -ENOMEM;
34524452239SStanislav Kinsbursky 	}
346b73a2972SNeilBrown 
3476b044fbaSNeilBrown 	serv->sv_maxconn = nlm_max_connections;
3486b044fbaSNeilBrown 	error = svc_set_num_threads(serv, NULL, 1);
349b73a2972SNeilBrown 	/* The thread now holds the only reference */
350b73a2972SNeilBrown 	svc_put(serv);
351b73a2972SNeilBrown 	if (error < 0)
352b73a2972SNeilBrown 		return error;
353b73a2972SNeilBrown 
3542840fe86SNeilBrown 	nlmsvc_serv = serv;
3550751ddf7SScott Mayhew 	register_inetaddr_notifier(&lockd_inetaddr_notifier);
3560751ddf7SScott Mayhew #if IS_ENABLED(CONFIG_IPV6)
3570751ddf7SScott Mayhew 	register_inet6addr_notifier(&lockd_inet6addr_notifier);
3580751ddf7SScott Mayhew #endif
3598dbf28e4SStanislav Kinsbursky 	dprintk("lockd_up: service created\n");
360ecd3ad68SNeilBrown 	nlmsvc_users++;
3612840fe86SNeilBrown 	return 0;
36224452239SStanislav Kinsbursky }
36324452239SStanislav Kinsbursky 
lockd_put(void)364865b6740SNeilBrown static void lockd_put(void)
365865b6740SNeilBrown {
366865b6740SNeilBrown 	if (WARN(nlmsvc_users <= 0, "lockd_down: no users!\n"))
367865b6740SNeilBrown 		return;
368865b6740SNeilBrown 	if (--nlmsvc_users)
369865b6740SNeilBrown 		return;
370865b6740SNeilBrown 
371865b6740SNeilBrown 	unregister_inetaddr_notifier(&lockd_inetaddr_notifier);
372865b6740SNeilBrown #if IS_ENABLED(CONFIG_IPV6)
373865b6740SNeilBrown 	unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
374865b6740SNeilBrown #endif
375865b6740SNeilBrown 
3766b044fbaSNeilBrown 	svc_set_num_threads(nlmsvc_serv, NULL, 0);
377c743b425SNeilBrown 	timer_delete_sync(&nlmsvc_retry);
378865b6740SNeilBrown 	nlmsvc_serv = NULL;
379865b6740SNeilBrown 	dprintk("lockd_down: service destroyed\n");
380865b6740SNeilBrown }
381865b6740SNeilBrown 
38224452239SStanislav Kinsbursky /*
38324452239SStanislav Kinsbursky  * Bring up the lockd process if it's not already up.
38424452239SStanislav Kinsbursky  */
lockd_up(struct net * net,const struct cred * cred)38540373b12STrond Myklebust int lockd_up(struct net *net, const struct cred *cred)
38624452239SStanislav Kinsbursky {
3877d13ec76SStanislav Kinsbursky 	int error;
38824452239SStanislav Kinsbursky 
38924452239SStanislav Kinsbursky 	mutex_lock(&nlmsvc_mutex);
39024452239SStanislav Kinsbursky 
391ecd3ad68SNeilBrown 	error = lockd_get();
3922840fe86SNeilBrown 	if (error)
393865b6740SNeilBrown 		goto err;
3941da177e4SLinus Torvalds 
395b73a2972SNeilBrown 	error = lockd_up_net(nlmsvc_serv, net, cred);
396dc3033e1SVasily Averin 	if (error < 0) {
397865b6740SNeilBrown 		lockd_put();
398865b6740SNeilBrown 		goto err;
399dc3033e1SVasily Averin 	}
4001da177e4SLinus Torvalds 
401865b6740SNeilBrown err:
402353ab6e9SIngo Molnar 	mutex_unlock(&nlmsvc_mutex);
4031da177e4SLinus Torvalds 	return error;
4041da177e4SLinus Torvalds }
4052de59872STrond Myklebust EXPORT_SYMBOL_GPL(lockd_up);
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds /*
4081da177e4SLinus Torvalds  * Decrement the user count and bring down lockd if we're the last.
4091da177e4SLinus Torvalds  */
4101da177e4SLinus Torvalds void
lockd_down(struct net * net)411e3f70eadSStanislav Kinsbursky lockd_down(struct net *net)
4121da177e4SLinus Torvalds {
413353ab6e9SIngo Molnar 	mutex_lock(&nlmsvc_mutex);
4142840fe86SNeilBrown 	lockd_down_net(nlmsvc_serv, net);
415865b6740SNeilBrown 	lockd_put();
416353ab6e9SIngo Molnar 	mutex_unlock(&nlmsvc_mutex);
4171da177e4SLinus Torvalds }
4182de59872STrond Myklebust EXPORT_SYMBOL_GPL(lockd_down);
4191da177e4SLinus Torvalds 
42090d5b180SChuck Lever #ifdef CONFIG_SYSCTL
42190d5b180SChuck Lever 
4221da177e4SLinus Torvalds /*
4231da177e4SLinus Torvalds  * Sysctl parameters (same as module parameters, different interface).
4241da177e4SLinus Torvalds  */
4251da177e4SLinus Torvalds 
4267ac9fe57SJoe Perches static struct ctl_table nlm_sysctls[] = {
4271da177e4SLinus Torvalds 	{
4281da177e4SLinus Torvalds 		.procname	= "nlm_grace_period",
4291da177e4SLinus Torvalds 		.data		= &nlm_grace_period,
4307ee91ec1SSteve Dickson 		.maxlen		= sizeof(unsigned long),
4311da177e4SLinus Torvalds 		.mode		= 0644,
4326d456111SEric W. Biederman 		.proc_handler	= proc_doulongvec_minmax,
4331da177e4SLinus Torvalds 		.extra1		= (unsigned long *) &nlm_grace_period_min,
4341da177e4SLinus Torvalds 		.extra2		= (unsigned long *) &nlm_grace_period_max,
4351da177e4SLinus Torvalds 	},
4361da177e4SLinus Torvalds 	{
4371da177e4SLinus Torvalds 		.procname	= "nlm_timeout",
4381da177e4SLinus Torvalds 		.data		= &nlm_timeout,
4397ee91ec1SSteve Dickson 		.maxlen		= sizeof(unsigned long),
4401da177e4SLinus Torvalds 		.mode		= 0644,
4416d456111SEric W. Biederman 		.proc_handler	= proc_doulongvec_minmax,
4421da177e4SLinus Torvalds 		.extra1		= (unsigned long *) &nlm_timeout_min,
4431da177e4SLinus Torvalds 		.extra2		= (unsigned long *) &nlm_timeout_max,
4441da177e4SLinus Torvalds 	},
4451da177e4SLinus Torvalds 	{
4461da177e4SLinus Torvalds 		.procname	= "nlm_udpport",
4471da177e4SLinus Torvalds 		.data		= &nlm_udpport,
4481da177e4SLinus Torvalds 		.maxlen		= sizeof(int),
4491da177e4SLinus Torvalds 		.mode		= 0644,
4506d456111SEric W. Biederman 		.proc_handler	= proc_dointvec_minmax,
4511da177e4SLinus Torvalds 		.extra1		= (int *) &nlm_port_min,
4521da177e4SLinus Torvalds 		.extra2		= (int *) &nlm_port_max,
4531da177e4SLinus Torvalds 	},
4541da177e4SLinus Torvalds 	{
4551da177e4SLinus Torvalds 		.procname	= "nlm_tcpport",
4561da177e4SLinus Torvalds 		.data		= &nlm_tcpport,
4571da177e4SLinus Torvalds 		.maxlen		= sizeof(int),
4581da177e4SLinus Torvalds 		.mode		= 0644,
4596d456111SEric W. Biederman 		.proc_handler	= proc_dointvec_minmax,
4601da177e4SLinus Torvalds 		.extra1		= (int *) &nlm_port_min,
4611da177e4SLinus Torvalds 		.extra2		= (int *) &nlm_port_max,
4621da177e4SLinus Torvalds 	},
463abd1f500SOlaf Kirch 	{
464abd1f500SOlaf Kirch 		.procname	= "nsm_use_hostnames",
465abd1f500SOlaf Kirch 		.data		= &nsm_use_hostnames,
466f1aa2eb5SOndrej Mosnacek 		.maxlen		= sizeof(bool),
467abd1f500SOlaf Kirch 		.mode		= 0644,
468d02a3a2cSJia He 		.proc_handler	= proc_dobool,
469abd1f500SOlaf Kirch 	},
470460f5cacSOlaf Kirch 	{
471460f5cacSOlaf Kirch 		.procname	= "nsm_local_state",
472460f5cacSOlaf Kirch 		.data		= &nsm_local_state,
473460f5cacSOlaf Kirch 		.maxlen		= sizeof(int),
474460f5cacSOlaf Kirch 		.mode		= 0644,
4756d456111SEric W. Biederman 		.proc_handler	= proc_dointvec,
476460f5cacSOlaf Kirch 	},
477ab09203eSEric W. Biederman 	{ }
4781da177e4SLinus Torvalds };
4791da177e4SLinus Torvalds 
48090d5b180SChuck Lever #endif	/* CONFIG_SYSCTL */
48190d5b180SChuck Lever 
4821da177e4SLinus Torvalds /*
483405ae7d3SRobert P. J. Day  * Module (and sysfs) parameters.
4841da177e4SLinus Torvalds  */
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds #define param_set_min_max(name, type, which_strtol, min, max)		\
487e4dca7b7SKees Cook static int param_set_##name(const char *val, const struct kernel_param *kp) \
4881da177e4SLinus Torvalds {									\
4891da177e4SLinus Torvalds 	char *endp;							\
4901da177e4SLinus Torvalds 	__typeof__(type) num = which_strtol(val, &endp, 0);		\
4911da177e4SLinus Torvalds 	if (endp == val || *endp || num < (min) || num > (max))		\
4921da177e4SLinus Torvalds 		return -EINVAL;						\
493de5b8e8eSNeilBrown 	*((type *) kp->arg) = num;					\
4941da177e4SLinus Torvalds 	return 0;							\
4951da177e4SLinus Torvalds }
4961da177e4SLinus Torvalds 
is_callback(u32 proc)4971da177e4SLinus Torvalds static inline int is_callback(u32 proc)
4981da177e4SLinus Torvalds {
4991da177e4SLinus Torvalds 	return proc == NLMPROC_GRANTED
5001da177e4SLinus Torvalds 		|| proc == NLMPROC_GRANTED_MSG
5011da177e4SLinus Torvalds 		|| proc == NLMPROC_TEST_RES
5021da177e4SLinus Torvalds 		|| proc == NLMPROC_LOCK_RES
5031da177e4SLinus Torvalds 		|| proc == NLMPROC_CANCEL_RES
5041da177e4SLinus Torvalds 		|| proc == NLMPROC_UNLOCK_RES
5051da177e4SLinus Torvalds 		|| proc == NLMPROC_NSM_NOTIFY;
5061da177e4SLinus Torvalds }
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds 
lockd_authenticate(struct svc_rqst * rqstp)509*78c542f9SChuck Lever static enum svc_auth_status lockd_authenticate(struct svc_rqst *rqstp)
5101da177e4SLinus Torvalds {
5111da177e4SLinus Torvalds 	rqstp->rq_client = NULL;
5121da177e4SLinus Torvalds 	switch (rqstp->rq_authop->flavour) {
5131da177e4SLinus Torvalds 		case RPC_AUTH_NULL:
5141da177e4SLinus Torvalds 		case RPC_AUTH_UNIX:
5155c2465dfSChuck Lever 			rqstp->rq_auth_stat = rpc_auth_ok;
5161da177e4SLinus Torvalds 			if (rqstp->rq_proc == 0)
5171da177e4SLinus Torvalds 				return SVC_OK;
5181da177e4SLinus Torvalds 			if (is_callback(rqstp->rq_proc)) {
5191da177e4SLinus Torvalds 				/* Leave it to individual procedures to
5201da177e4SLinus Torvalds 				 * call nlmsvc_lookup_host(rqstp)
5211da177e4SLinus Torvalds 				 */
5221da177e4SLinus Torvalds 				return SVC_OK;
5231da177e4SLinus Torvalds 			}
5241da177e4SLinus Torvalds 			return svc_set_client(rqstp);
5251da177e4SLinus Torvalds 	}
5265c2465dfSChuck Lever 	rqstp->rq_auth_stat = rpc_autherr_badcred;
5271da177e4SLinus Torvalds 	return SVC_DENIED;
5281da177e4SLinus Torvalds }
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds param_set_min_max(port, int, simple_strtol, 0, 65535)
5321da177e4SLinus Torvalds param_set_min_max(grace_period, unsigned long, simple_strtoul,
5331da177e4SLinus Torvalds 		  nlm_grace_period_min, nlm_grace_period_max)
5341da177e4SLinus Torvalds param_set_min_max(timeout, unsigned long, simple_strtoul,
5351da177e4SLinus Torvalds 		  nlm_timeout_min, nlm_timeout_max)
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
5381da177e4SLinus Torvalds MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION ".");
5391da177e4SLinus Torvalds MODULE_LICENSE("GPL");
5401da177e4SLinus Torvalds 
5411da177e4SLinus Torvalds module_param_call(nlm_grace_period, param_set_grace_period, param_get_ulong,
5421da177e4SLinus Torvalds 		  &nlm_grace_period, 0644);
5431da177e4SLinus Torvalds module_param_call(nlm_timeout, param_set_timeout, param_get_ulong,
5441da177e4SLinus Torvalds 		  &nlm_timeout, 0644);
5451da177e4SLinus Torvalds module_param_call(nlm_udpport, param_set_port, param_get_int,
5461da177e4SLinus Torvalds 		  &nlm_udpport, 0644);
5471da177e4SLinus Torvalds module_param_call(nlm_tcpport, param_set_port, param_get_int,
5481da177e4SLinus Torvalds 		  &nlm_tcpport, 0644);
549abd1f500SOlaf Kirch module_param(nsm_use_hostnames, bool, 0644);
550c72a476bSJeff Layton module_param(nlm_max_connections, uint, 0644);
5511da177e4SLinus Torvalds 
lockd_init_net(struct net * net)552a9c5d73aSStanislav Kinsbursky static int lockd_init_net(struct net *net)
553a9c5d73aSStanislav Kinsbursky {
55466547b02SStanislav Kinsbursky 	struct lockd_net *ln = net_generic(net, lockd_net_id);
55566547b02SStanislav Kinsbursky 
55666547b02SStanislav Kinsbursky 	INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender);
557f7790029SJeff Layton 	INIT_LIST_HEAD(&ln->lockd_manager.list);
558c87fb4a3SJ. Bruce Fields 	ln->lockd_manager.block_opens = false;
5590ad95472SAndrey Ryabinin 	INIT_LIST_HEAD(&ln->nsm_handles);
560a9c5d73aSStanislav Kinsbursky 	return 0;
561a9c5d73aSStanislav Kinsbursky }
562a9c5d73aSStanislav Kinsbursky 
lockd_exit_net(struct net * net)563a9c5d73aSStanislav Kinsbursky static void lockd_exit_net(struct net *net)
564a9c5d73aSStanislav Kinsbursky {
565a3152f14SVasily Averin 	struct lockd_net *ln = net_generic(net, lockd_net_id);
566a3152f14SVasily Averin 
567a3152f14SVasily Averin 	WARN_ONCE(!list_empty(&ln->lockd_manager.list),
568a3152f14SVasily Averin 		  "net %x %s: lockd_manager.list is not empty\n",
569a3152f14SVasily Averin 		  net->ns.inum, __func__);
570a3152f14SVasily Averin 	WARN_ONCE(!list_empty(&ln->nsm_handles),
571a3152f14SVasily Averin 		  "net %x %s: nsm_handles list is not empty\n",
572a3152f14SVasily Averin 		  net->ns.inum, __func__);
573a3152f14SVasily Averin 	WARN_ONCE(delayed_work_pending(&ln->grace_period_end),
574a3152f14SVasily Averin 		  "net %x %s: grace_period_end was not cancelled\n",
575a3152f14SVasily Averin 		  net->ns.inum, __func__);
576a9c5d73aSStanislav Kinsbursky }
577a9c5d73aSStanislav Kinsbursky 
578a9c5d73aSStanislav Kinsbursky static struct pernet_operations lockd_net_ops = {
579a9c5d73aSStanislav Kinsbursky 	.init = lockd_init_net,
580a9c5d73aSStanislav Kinsbursky 	.exit = lockd_exit_net,
581a9c5d73aSStanislav Kinsbursky 	.id = &lockd_net_id,
582a9c5d73aSStanislav Kinsbursky 	.size = sizeof(struct lockd_net),
583a9c5d73aSStanislav Kinsbursky };
584a9c5d73aSStanislav Kinsbursky 
585a9c5d73aSStanislav Kinsbursky 
5861da177e4SLinus Torvalds /*
5871da177e4SLinus Torvalds  * Initialising and terminating the module.
5881da177e4SLinus Torvalds  */
5891da177e4SLinus Torvalds 
init_nlm(void)5901da177e4SLinus Torvalds static int __init init_nlm(void)
5911da177e4SLinus Torvalds {
592a9c5d73aSStanislav Kinsbursky 	int err;
593a9c5d73aSStanislav Kinsbursky 
59490d5b180SChuck Lever #ifdef CONFIG_SYSCTL
595a9c5d73aSStanislav Kinsbursky 	err = -ENOMEM;
59637b768ceSLuis Chamberlain 	nlm_sysctl_table = register_sysctl("fs/nfs", nlm_sysctls);
597a9c5d73aSStanislav Kinsbursky 	if (nlm_sysctl_table == NULL)
598a9c5d73aSStanislav Kinsbursky 		goto err_sysctl;
59990d5b180SChuck Lever #endif
600a9c5d73aSStanislav Kinsbursky 	err = register_pernet_subsys(&lockd_net_ops);
601a9c5d73aSStanislav Kinsbursky 	if (err)
602a9c5d73aSStanislav Kinsbursky 		goto err_pernet;
603d68e3c4aSJeff Layton 
604d68e3c4aSJeff Layton 	err = lockd_create_procfs();
605d68e3c4aSJeff Layton 	if (err)
606d68e3c4aSJeff Layton 		goto err_procfs;
607d68e3c4aSJeff Layton 
608a9c5d73aSStanislav Kinsbursky 	return 0;
609a9c5d73aSStanislav Kinsbursky 
610d68e3c4aSJeff Layton err_procfs:
611d68e3c4aSJeff Layton 	unregister_pernet_subsys(&lockd_net_ops);
612a9c5d73aSStanislav Kinsbursky err_pernet:
613a9c5d73aSStanislav Kinsbursky #ifdef CONFIG_SYSCTL
614a9c5d73aSStanislav Kinsbursky 	unregister_sysctl_table(nlm_sysctl_table);
615a9c5d73aSStanislav Kinsbursky err_sysctl:
61612dd7ecfSKees Cook #endif
617a9c5d73aSStanislav Kinsbursky 	return err;
6181da177e4SLinus Torvalds }
6191da177e4SLinus Torvalds 
exit_nlm(void)6201da177e4SLinus Torvalds static void __exit exit_nlm(void)
6211da177e4SLinus Torvalds {
6221da177e4SLinus Torvalds 	/* FIXME: delete all NLM clients */
6231da177e4SLinus Torvalds 	nlm_shutdown_hosts();
624d68e3c4aSJeff Layton 	lockd_remove_procfs();
625a9c5d73aSStanislav Kinsbursky 	unregister_pernet_subsys(&lockd_net_ops);
62690d5b180SChuck Lever #ifdef CONFIG_SYSCTL
6271da177e4SLinus Torvalds 	unregister_sysctl_table(nlm_sysctl_table);
62890d5b180SChuck Lever #endif
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds 
6311da177e4SLinus Torvalds module_init(init_nlm);
6321da177e4SLinus Torvalds module_exit(exit_nlm);
6331da177e4SLinus Torvalds 
634a9ad1a80SChuck Lever /**
635a9ad1a80SChuck Lever  * nlmsvc_dispatch - Process an NLM Request
636a9ad1a80SChuck Lever  * @rqstp: incoming request
637a9ad1a80SChuck Lever  *
638a9ad1a80SChuck Lever  * Return values:
639a9ad1a80SChuck Lever  *  %0: Processing complete; do not send a Reply
640a9ad1a80SChuck Lever  *  %1: Processing complete; send Reply in rqstp->rq_res
641a9ad1a80SChuck Lever  */
nlmsvc_dispatch(struct svc_rqst * rqstp)642cee4db19SChuck Lever static int nlmsvc_dispatch(struct svc_rqst *rqstp)
643a9ad1a80SChuck Lever {
644a9ad1a80SChuck Lever 	const struct svc_procedure *procp = rqstp->rq_procinfo;
645cee4db19SChuck Lever 	__be32 *statp = rqstp->rq_accept_statp;
646a9ad1a80SChuck Lever 
64716c66364SChuck Lever 	if (!procp->pc_decode(rqstp, &rqstp->rq_arg_stream))
648a9ad1a80SChuck Lever 		goto out_decode_err;
649a9ad1a80SChuck Lever 
650a9ad1a80SChuck Lever 	*statp = procp->pc_func(rqstp);
651a9ad1a80SChuck Lever 	if (*statp == rpc_drop_reply)
652a9ad1a80SChuck Lever 		return 0;
653a9ad1a80SChuck Lever 	if (*statp != rpc_success)
654a9ad1a80SChuck Lever 		return 1;
655a9ad1a80SChuck Lever 
656fda49441SChuck Lever 	if (!procp->pc_encode(rqstp, &rqstp->rq_res_stream))
657a9ad1a80SChuck Lever 		goto out_encode_err;
658a9ad1a80SChuck Lever 
659a9ad1a80SChuck Lever 	return 1;
660a9ad1a80SChuck Lever 
661a9ad1a80SChuck Lever out_decode_err:
662a9ad1a80SChuck Lever 	*statp = rpc_garbage_args;
663a9ad1a80SChuck Lever 	return 1;
664a9ad1a80SChuck Lever 
665a9ad1a80SChuck Lever out_encode_err:
666a9ad1a80SChuck Lever 	*statp = rpc_system_err;
667a9ad1a80SChuck Lever 	return 1;
668a9ad1a80SChuck Lever }
669a9ad1a80SChuck Lever 
6701da177e4SLinus Torvalds /*
6711da177e4SLinus Torvalds  * Define NLM program and procedures
6721da177e4SLinus Torvalds  */
67365ba3d24SChuck Lever static DEFINE_PER_CPU_ALIGNED(unsigned long, nlmsvc_version1_count[17]);
674e9679189SChristoph Hellwig static const struct svc_version	nlmsvc_version1 = {
6751da177e4SLinus Torvalds 	.vs_vers	= 1,
6761da177e4SLinus Torvalds 	.vs_nproc	= 17,
6771da177e4SLinus Torvalds 	.vs_proc	= nlmsvc_procedures,
6787fd38af9SChristoph Hellwig 	.vs_count	= nlmsvc_version1_count,
679a9ad1a80SChuck Lever 	.vs_dispatch	= nlmsvc_dispatch,
6801da177e4SLinus Torvalds 	.vs_xdrsize	= NLMSVC_XDRSIZE,
6811da177e4SLinus Torvalds };
68265ba3d24SChuck Lever 
68365ba3d24SChuck Lever static DEFINE_PER_CPU_ALIGNED(unsigned long,
68465ba3d24SChuck Lever 			      nlmsvc_version3_count[ARRAY_SIZE(nlmsvc_procedures)]);
685e9679189SChristoph Hellwig static const struct svc_version	nlmsvc_version3 = {
6861da177e4SLinus Torvalds 	.vs_vers	= 3,
68765ba3d24SChuck Lever 	.vs_nproc	= ARRAY_SIZE(nlmsvc_procedures),
6881da177e4SLinus Torvalds 	.vs_proc	= nlmsvc_procedures,
6897fd38af9SChristoph Hellwig 	.vs_count	= nlmsvc_version3_count,
690a9ad1a80SChuck Lever 	.vs_dispatch	= nlmsvc_dispatch,
6911da177e4SLinus Torvalds 	.vs_xdrsize	= NLMSVC_XDRSIZE,
6921da177e4SLinus Torvalds };
69365ba3d24SChuck Lever 
6941da177e4SLinus Torvalds #ifdef CONFIG_LOCKD_V4
69565ba3d24SChuck Lever static DEFINE_PER_CPU_ALIGNED(unsigned long,
69665ba3d24SChuck Lever 			      nlmsvc_version4_count[ARRAY_SIZE(nlmsvc_procedures4)]);
697e9679189SChristoph Hellwig static const struct svc_version	nlmsvc_version4 = {
6981da177e4SLinus Torvalds 	.vs_vers	= 4,
69965ba3d24SChuck Lever 	.vs_nproc	= ARRAY_SIZE(nlmsvc_procedures4),
7001da177e4SLinus Torvalds 	.vs_proc	= nlmsvc_procedures4,
7017fd38af9SChristoph Hellwig 	.vs_count	= nlmsvc_version4_count,
702a9ad1a80SChuck Lever 	.vs_dispatch	= nlmsvc_dispatch,
7031da177e4SLinus Torvalds 	.vs_xdrsize	= NLMSVC_XDRSIZE,
7041da177e4SLinus Torvalds };
7051da177e4SLinus Torvalds #endif
70665ba3d24SChuck Lever 
707e9679189SChristoph Hellwig static const struct svc_version *nlmsvc_version[] = {
7081da177e4SLinus Torvalds 	[1] = &nlmsvc_version1,
7091da177e4SLinus Torvalds 	[3] = &nlmsvc_version3,
7101da177e4SLinus Torvalds #ifdef CONFIG_LOCKD_V4
7111da177e4SLinus Torvalds 	[4] = &nlmsvc_version4,
7121da177e4SLinus Torvalds #endif
7131da177e4SLinus Torvalds };
7141da177e4SLinus Torvalds 
715e8c96f8cSTobias Klauser #define NLM_NRVERS	ARRAY_SIZE(nlmsvc_version)
7161da177e4SLinus Torvalds static struct svc_program	nlmsvc_program = {
7171da177e4SLinus Torvalds 	.pg_prog		= NLM_PROGRAM,		/* program number */
7181da177e4SLinus Torvalds 	.pg_nvers		= NLM_NRVERS,		/* number of entries in nlmsvc_version */
7191da177e4SLinus Torvalds 	.pg_vers		= nlmsvc_version,	/* version table */
7201da177e4SLinus Torvalds 	.pg_name		= "lockd",		/* service name */
7211da177e4SLinus Torvalds 	.pg_class		= "nfsd",		/* share authentication with nfsd */
7228e5b6773STrond Myklebust 	.pg_authenticate	= &lockd_authenticate,	/* export authentication */
7238e5b6773STrond Myklebust 	.pg_init_request	= svc_generic_init_request,
724642ee6b2STrond Myklebust 	.pg_rpcbind_set		= svc_generic_rpcbind_set,
7251da177e4SLinus Torvalds };
726