18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/fs/lockd/svc.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This is the central lockd service.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * FIXME: Separate the lockd NFS server functionality from the lockd NFS
88c2ecf20Sopenharmony_ci * 	  client functionality. Oh why didn't Sun create two separate
98c2ecf20Sopenharmony_ci *	  services in the first place?
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Authors:	Olaf Kirch (okir@monad.swb.de)
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
198c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
228c2ecf20Sopenharmony_ci#include <linux/errno.h>
238c2ecf20Sopenharmony_ci#include <linux/in.h>
248c2ecf20Sopenharmony_ci#include <linux/uio.h>
258c2ecf20Sopenharmony_ci#include <linux/smp.h>
268c2ecf20Sopenharmony_ci#include <linux/mutex.h>
278c2ecf20Sopenharmony_ci#include <linux/kthread.h>
288c2ecf20Sopenharmony_ci#include <linux/freezer.h>
298c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <linux/sunrpc/types.h>
328c2ecf20Sopenharmony_ci#include <linux/sunrpc/stats.h>
338c2ecf20Sopenharmony_ci#include <linux/sunrpc/clnt.h>
348c2ecf20Sopenharmony_ci#include <linux/sunrpc/svc.h>
358c2ecf20Sopenharmony_ci#include <linux/sunrpc/svcsock.h>
368c2ecf20Sopenharmony_ci#include <linux/sunrpc/svc_xprt.h>
378c2ecf20Sopenharmony_ci#include <net/ip.h>
388c2ecf20Sopenharmony_ci#include <net/addrconf.h>
398c2ecf20Sopenharmony_ci#include <net/ipv6.h>
408c2ecf20Sopenharmony_ci#include <linux/lockd/lockd.h>
418c2ecf20Sopenharmony_ci#include <linux/nfs.h>
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#include "netns.h"
448c2ecf20Sopenharmony_ci#include "procfs.h"
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define NLMDBG_FACILITY		NLMDBG_SVC
478c2ecf20Sopenharmony_ci#define LOCKD_BUFSIZE		(1024 + NLMSVC_XDRSIZE)
488c2ecf20Sopenharmony_ci#define ALLOWED_SIGS		(sigmask(SIGKILL))
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic struct svc_program	nlmsvc_program;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ciconst struct nlmsvc_binding	*nlmsvc_ops;
538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nlmsvc_ops);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(nlmsvc_mutex);
568c2ecf20Sopenharmony_cistatic unsigned int		nlmsvc_users;
578c2ecf20Sopenharmony_cistatic struct task_struct	*nlmsvc_task;
588c2ecf20Sopenharmony_cistatic struct svc_rqst		*nlmsvc_rqst;
598c2ecf20Sopenharmony_ciunsigned long			nlmsvc_timeout;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic atomic_t nlm_ntf_refcnt = ATOMIC_INIT(0);
628c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(nlm_ntf_wq);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ciunsigned int lockd_net_id;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/*
678c2ecf20Sopenharmony_ci * These can be set at insmod time (useful for NFS as root filesystem),
688c2ecf20Sopenharmony_ci * and also changed through the sysctl interface.  -- Jamie Lokier, Aug 2003
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistatic unsigned long		nlm_grace_period;
718c2ecf20Sopenharmony_cistatic unsigned long		nlm_timeout = LOCKD_DFLT_TIMEO;
728c2ecf20Sopenharmony_cistatic int			nlm_udpport, nlm_tcpport;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */
758c2ecf20Sopenharmony_cistatic unsigned int		nlm_max_connections = 1024;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/*
788c2ecf20Sopenharmony_ci * Constants needed for the sysctl interface.
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_cistatic const unsigned long	nlm_grace_period_min = 0;
818c2ecf20Sopenharmony_cistatic const unsigned long	nlm_grace_period_max = 240;
828c2ecf20Sopenharmony_cistatic const unsigned long	nlm_timeout_min = 3;
838c2ecf20Sopenharmony_cistatic const unsigned long	nlm_timeout_max = 20;
848c2ecf20Sopenharmony_cistatic const int		nlm_port_min = 0, nlm_port_max = 65535;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
878c2ecf20Sopenharmony_cistatic struct ctl_table_header * nlm_sysctl_table;
888c2ecf20Sopenharmony_ci#endif
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic unsigned long get_lockd_grace_period(void)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	/* Note: nlm_timeout should always be nonzero */
938c2ecf20Sopenharmony_ci	if (nlm_grace_period)
948c2ecf20Sopenharmony_ci		return roundup(nlm_grace_period, nlm_timeout) * HZ;
958c2ecf20Sopenharmony_ci	else
968c2ecf20Sopenharmony_ci		return nlm_timeout * 5 * HZ;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic void grace_ender(struct work_struct *grace)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct delayed_work *dwork = to_delayed_work(grace);
1028c2ecf20Sopenharmony_ci	struct lockd_net *ln = container_of(dwork, struct lockd_net,
1038c2ecf20Sopenharmony_ci					    grace_period_end);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	locks_end_grace(&ln->lockd_manager);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic void set_grace_period(struct net *net)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	unsigned long grace_period = get_lockd_grace_period();
1118c2ecf20Sopenharmony_ci	struct lockd_net *ln = net_generic(net, lockd_net_id);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	locks_start_grace(net, &ln->lockd_manager);
1148c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&ln->grace_period_end);
1158c2ecf20Sopenharmony_ci	schedule_delayed_work(&ln->grace_period_end, grace_period);
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic void restart_grace(void)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	if (nlmsvc_ops) {
1218c2ecf20Sopenharmony_ci		struct net *net = &init_net;
1228c2ecf20Sopenharmony_ci		struct lockd_net *ln = net_generic(net, lockd_net_id);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&ln->grace_period_end);
1258c2ecf20Sopenharmony_ci		locks_end_grace(&ln->lockd_manager);
1268c2ecf20Sopenharmony_ci		nlmsvc_invalidate_all();
1278c2ecf20Sopenharmony_ci		set_grace_period(net);
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci/*
1328c2ecf20Sopenharmony_ci * This is the lockd kernel thread
1338c2ecf20Sopenharmony_ci */
1348c2ecf20Sopenharmony_cistatic int
1358c2ecf20Sopenharmony_cilockd(void *vrqstp)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	int		err = 0;
1388c2ecf20Sopenharmony_ci	struct svc_rqst *rqstp = vrqstp;
1398c2ecf20Sopenharmony_ci	struct net *net = &init_net;
1408c2ecf20Sopenharmony_ci	struct lockd_net *ln = net_generic(net, lockd_net_id);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* try_to_freeze() is called from svc_recv() */
1438c2ecf20Sopenharmony_ci	set_freezable();
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* Allow SIGKILL to tell lockd to drop all of its locks */
1468c2ecf20Sopenharmony_ci	allow_signal(SIGKILL);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/*
1518c2ecf20Sopenharmony_ci	 * The main request loop. We don't terminate until the last
1528c2ecf20Sopenharmony_ci	 * NFS mount or NFS daemon has gone away.
1538c2ecf20Sopenharmony_ci	 */
1548c2ecf20Sopenharmony_ci	while (!kthread_should_stop()) {
1558c2ecf20Sopenharmony_ci		long timeout = MAX_SCHEDULE_TIMEOUT;
1568c2ecf20Sopenharmony_ci		RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		/* update sv_maxconn if it has changed */
1598c2ecf20Sopenharmony_ci		rqstp->rq_server->sv_maxconn = nlm_max_connections;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		if (signalled()) {
1628c2ecf20Sopenharmony_ci			flush_signals(current);
1638c2ecf20Sopenharmony_ci			restart_grace();
1648c2ecf20Sopenharmony_ci			continue;
1658c2ecf20Sopenharmony_ci		}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		timeout = nlmsvc_retry_blocked();
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		/*
1708c2ecf20Sopenharmony_ci		 * Find a socket with data available and call its
1718c2ecf20Sopenharmony_ci		 * recvfrom routine.
1728c2ecf20Sopenharmony_ci		 */
1738c2ecf20Sopenharmony_ci		err = svc_recv(rqstp, timeout);
1748c2ecf20Sopenharmony_ci		if (err == -EAGAIN || err == -EINTR)
1758c2ecf20Sopenharmony_ci			continue;
1768c2ecf20Sopenharmony_ci		dprintk("lockd: request from %s\n",
1778c2ecf20Sopenharmony_ci				svc_print_addr(rqstp, buf, sizeof(buf)));
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		svc_process(rqstp);
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci	flush_signals(current);
1828c2ecf20Sopenharmony_ci	if (nlmsvc_ops)
1838c2ecf20Sopenharmony_ci		nlmsvc_invalidate_all();
1848c2ecf20Sopenharmony_ci	nlm_shutdown_hosts();
1858c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&ln->grace_period_end);
1868c2ecf20Sopenharmony_ci	locks_end_grace(&ln->lockd_manager);
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic int create_lockd_listener(struct svc_serv *serv, const char *name,
1918c2ecf20Sopenharmony_ci				 struct net *net, const int family,
1928c2ecf20Sopenharmony_ci				 const unsigned short port,
1938c2ecf20Sopenharmony_ci				 const struct cred *cred)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct svc_xprt *xprt;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	xprt = svc_find_xprt(serv, name, net, family, 0);
1988c2ecf20Sopenharmony_ci	if (xprt == NULL)
1998c2ecf20Sopenharmony_ci		return svc_create_xprt(serv, name, net, family, port,
2008c2ecf20Sopenharmony_ci						SVC_SOCK_DEFAULTS, cred);
2018c2ecf20Sopenharmony_ci	svc_xprt_put(xprt);
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int create_lockd_family(struct svc_serv *serv, struct net *net,
2068c2ecf20Sopenharmony_ci			       const int family, const struct cred *cred)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	int err;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	err = create_lockd_listener(serv, "udp", net, family, nlm_udpport,
2118c2ecf20Sopenharmony_ci			cred);
2128c2ecf20Sopenharmony_ci	if (err < 0)
2138c2ecf20Sopenharmony_ci		return err;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return create_lockd_listener(serv, "tcp", net, family, nlm_tcpport,
2168c2ecf20Sopenharmony_ci			cred);
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/*
2208c2ecf20Sopenharmony_ci * Ensure there are active UDP and TCP listeners for lockd.
2218c2ecf20Sopenharmony_ci *
2228c2ecf20Sopenharmony_ci * Even if we have only TCP NFS mounts and/or TCP NFSDs, some
2238c2ecf20Sopenharmony_ci * local services (such as rpc.statd) still require UDP, and
2248c2ecf20Sopenharmony_ci * some NFS servers do not yet support NLM over TCP.
2258c2ecf20Sopenharmony_ci *
2268c2ecf20Sopenharmony_ci * Returns zero if all listeners are available; otherwise a
2278c2ecf20Sopenharmony_ci * negative errno value is returned.
2288c2ecf20Sopenharmony_ci */
2298c2ecf20Sopenharmony_cistatic int make_socks(struct svc_serv *serv, struct net *net,
2308c2ecf20Sopenharmony_ci		const struct cred *cred)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	static int warned;
2338c2ecf20Sopenharmony_ci	int err;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	err = create_lockd_family(serv, net, PF_INET, cred);
2368c2ecf20Sopenharmony_ci	if (err < 0)
2378c2ecf20Sopenharmony_ci		goto out_err;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	err = create_lockd_family(serv, net, PF_INET6, cred);
2408c2ecf20Sopenharmony_ci	if (err < 0 && err != -EAFNOSUPPORT)
2418c2ecf20Sopenharmony_ci		goto out_err;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	warned = 0;
2448c2ecf20Sopenharmony_ci	return 0;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ciout_err:
2478c2ecf20Sopenharmony_ci	if (warned++ == 0)
2488c2ecf20Sopenharmony_ci		printk(KERN_WARNING
2498c2ecf20Sopenharmony_ci			"lockd_up: makesock failed, error=%d\n", err);
2508c2ecf20Sopenharmony_ci	svc_shutdown_net(serv, net);
2518c2ecf20Sopenharmony_ci	return err;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic int lockd_up_net(struct svc_serv *serv, struct net *net,
2558c2ecf20Sopenharmony_ci		const struct cred *cred)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct lockd_net *ln = net_generic(net, lockd_net_id);
2588c2ecf20Sopenharmony_ci	int error;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (ln->nlmsvc_users++)
2618c2ecf20Sopenharmony_ci		return 0;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	error = svc_bind(serv, net);
2648c2ecf20Sopenharmony_ci	if (error)
2658c2ecf20Sopenharmony_ci		goto err_bind;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	error = make_socks(serv, net, cred);
2688c2ecf20Sopenharmony_ci	if (error < 0)
2698c2ecf20Sopenharmony_ci		goto err_bind;
2708c2ecf20Sopenharmony_ci	set_grace_period(net);
2718c2ecf20Sopenharmony_ci	dprintk("%s: per-net data created; net=%x\n", __func__, net->ns.inum);
2728c2ecf20Sopenharmony_ci	return 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cierr_bind:
2758c2ecf20Sopenharmony_ci	ln->nlmsvc_users--;
2768c2ecf20Sopenharmony_ci	return error;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic void lockd_down_net(struct svc_serv *serv, struct net *net)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct lockd_net *ln = net_generic(net, lockd_net_id);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (ln->nlmsvc_users) {
2848c2ecf20Sopenharmony_ci		if (--ln->nlmsvc_users == 0) {
2858c2ecf20Sopenharmony_ci			nlm_shutdown_hosts_net(net);
2868c2ecf20Sopenharmony_ci			cancel_delayed_work_sync(&ln->grace_period_end);
2878c2ecf20Sopenharmony_ci			locks_end_grace(&ln->lockd_manager);
2888c2ecf20Sopenharmony_ci			svc_shutdown_net(serv, net);
2898c2ecf20Sopenharmony_ci			dprintk("%s: per-net data destroyed; net=%x\n",
2908c2ecf20Sopenharmony_ci				__func__, net->ns.inum);
2918c2ecf20Sopenharmony_ci		}
2928c2ecf20Sopenharmony_ci	} else {
2938c2ecf20Sopenharmony_ci		pr_err("%s: no users! task=%p, net=%x\n",
2948c2ecf20Sopenharmony_ci			__func__, nlmsvc_task, net->ns.inum);
2958c2ecf20Sopenharmony_ci		BUG();
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic int lockd_inetaddr_event(struct notifier_block *this,
3008c2ecf20Sopenharmony_ci	unsigned long event, void *ptr)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
3038c2ecf20Sopenharmony_ci	struct sockaddr_in sin;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if ((event != NETDEV_DOWN) ||
3068c2ecf20Sopenharmony_ci	    !atomic_inc_not_zero(&nlm_ntf_refcnt))
3078c2ecf20Sopenharmony_ci		goto out;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (nlmsvc_rqst) {
3108c2ecf20Sopenharmony_ci		dprintk("lockd_inetaddr_event: removed %pI4\n",
3118c2ecf20Sopenharmony_ci			&ifa->ifa_local);
3128c2ecf20Sopenharmony_ci		sin.sin_family = AF_INET;
3138c2ecf20Sopenharmony_ci		sin.sin_addr.s_addr = ifa->ifa_local;
3148c2ecf20Sopenharmony_ci		svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
3158c2ecf20Sopenharmony_ci			(struct sockaddr *)&sin);
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci	atomic_dec(&nlm_ntf_refcnt);
3188c2ecf20Sopenharmony_ci	wake_up(&nlm_ntf_wq);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ciout:
3218c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic struct notifier_block lockd_inetaddr_notifier = {
3258c2ecf20Sopenharmony_ci	.notifier_call = lockd_inetaddr_event,
3268c2ecf20Sopenharmony_ci};
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
3298c2ecf20Sopenharmony_cistatic int lockd_inet6addr_event(struct notifier_block *this,
3308c2ecf20Sopenharmony_ci	unsigned long event, void *ptr)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
3338c2ecf20Sopenharmony_ci	struct sockaddr_in6 sin6;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if ((event != NETDEV_DOWN) ||
3368c2ecf20Sopenharmony_ci	    !atomic_inc_not_zero(&nlm_ntf_refcnt))
3378c2ecf20Sopenharmony_ci		goto out;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (nlmsvc_rqst) {
3408c2ecf20Sopenharmony_ci		dprintk("lockd_inet6addr_event: removed %pI6\n", &ifa->addr);
3418c2ecf20Sopenharmony_ci		sin6.sin6_family = AF_INET6;
3428c2ecf20Sopenharmony_ci		sin6.sin6_addr = ifa->addr;
3438c2ecf20Sopenharmony_ci		if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
3448c2ecf20Sopenharmony_ci			sin6.sin6_scope_id = ifa->idev->dev->ifindex;
3458c2ecf20Sopenharmony_ci		svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
3468c2ecf20Sopenharmony_ci			(struct sockaddr *)&sin6);
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci	atomic_dec(&nlm_ntf_refcnt);
3498c2ecf20Sopenharmony_ci	wake_up(&nlm_ntf_wq);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ciout:
3528c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic struct notifier_block lockd_inet6addr_notifier = {
3568c2ecf20Sopenharmony_ci	.notifier_call = lockd_inet6addr_event,
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci#endif
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic void lockd_unregister_notifiers(void)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	unregister_inetaddr_notifier(&lockd_inetaddr_notifier);
3638c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
3648c2ecf20Sopenharmony_ci	unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
3658c2ecf20Sopenharmony_ci#endif
3668c2ecf20Sopenharmony_ci	wait_event(nlm_ntf_wq, atomic_read(&nlm_ntf_refcnt) == 0);
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic void lockd_svc_exit_thread(void)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	atomic_dec(&nlm_ntf_refcnt);
3728c2ecf20Sopenharmony_ci	lockd_unregister_notifiers();
3738c2ecf20Sopenharmony_ci	svc_exit_thread(nlmsvc_rqst);
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic int lockd_start_svc(struct svc_serv *serv)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	int error;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (nlmsvc_rqst)
3818c2ecf20Sopenharmony_ci		return 0;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/*
3848c2ecf20Sopenharmony_ci	 * Create the kernel thread and wait for it to start.
3858c2ecf20Sopenharmony_ci	 */
3868c2ecf20Sopenharmony_ci	nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
3878c2ecf20Sopenharmony_ci	if (IS_ERR(nlmsvc_rqst)) {
3888c2ecf20Sopenharmony_ci		error = PTR_ERR(nlmsvc_rqst);
3898c2ecf20Sopenharmony_ci		printk(KERN_WARNING
3908c2ecf20Sopenharmony_ci			"lockd_up: svc_rqst allocation failed, error=%d\n",
3918c2ecf20Sopenharmony_ci			error);
3928c2ecf20Sopenharmony_ci		lockd_unregister_notifiers();
3938c2ecf20Sopenharmony_ci		goto out_rqst;
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	atomic_inc(&nlm_ntf_refcnt);
3978c2ecf20Sopenharmony_ci	svc_sock_update_bufs(serv);
3988c2ecf20Sopenharmony_ci	serv->sv_maxconn = nlm_max_connections;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	nlmsvc_task = kthread_create(lockd, nlmsvc_rqst, "%s", serv->sv_name);
4018c2ecf20Sopenharmony_ci	if (IS_ERR(nlmsvc_task)) {
4028c2ecf20Sopenharmony_ci		error = PTR_ERR(nlmsvc_task);
4038c2ecf20Sopenharmony_ci		printk(KERN_WARNING
4048c2ecf20Sopenharmony_ci			"lockd_up: kthread_run failed, error=%d\n", error);
4058c2ecf20Sopenharmony_ci		goto out_task;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci	nlmsvc_rqst->rq_task = nlmsvc_task;
4088c2ecf20Sopenharmony_ci	wake_up_process(nlmsvc_task);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	dprintk("lockd_up: service started\n");
4118c2ecf20Sopenharmony_ci	return 0;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ciout_task:
4148c2ecf20Sopenharmony_ci	lockd_svc_exit_thread();
4158c2ecf20Sopenharmony_ci	nlmsvc_task = NULL;
4168c2ecf20Sopenharmony_ciout_rqst:
4178c2ecf20Sopenharmony_ci	nlmsvc_rqst = NULL;
4188c2ecf20Sopenharmony_ci	return error;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic const struct svc_serv_ops lockd_sv_ops = {
4228c2ecf20Sopenharmony_ci	.svo_shutdown		= svc_rpcb_cleanup,
4238c2ecf20Sopenharmony_ci	.svo_enqueue_xprt	= svc_xprt_do_enqueue,
4248c2ecf20Sopenharmony_ci};
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic struct svc_serv *lockd_create_svc(void)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct svc_serv *serv;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/*
4318c2ecf20Sopenharmony_ci	 * Check whether we're already up and running.
4328c2ecf20Sopenharmony_ci	 */
4338c2ecf20Sopenharmony_ci	if (nlmsvc_rqst) {
4348c2ecf20Sopenharmony_ci		/*
4358c2ecf20Sopenharmony_ci		 * Note: increase service usage, because later in case of error
4368c2ecf20Sopenharmony_ci		 * svc_destroy() will be called.
4378c2ecf20Sopenharmony_ci		 */
4388c2ecf20Sopenharmony_ci		svc_get(nlmsvc_rqst->rq_server);
4398c2ecf20Sopenharmony_ci		return nlmsvc_rqst->rq_server;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	/*
4438c2ecf20Sopenharmony_ci	 * Sanity check: if there's no pid,
4448c2ecf20Sopenharmony_ci	 * we should be the first user ...
4458c2ecf20Sopenharmony_ci	 */
4468c2ecf20Sopenharmony_ci	if (nlmsvc_users)
4478c2ecf20Sopenharmony_ci		printk(KERN_WARNING
4488c2ecf20Sopenharmony_ci			"lockd_up: no pid, %d users??\n", nlmsvc_users);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (!nlm_timeout)
4518c2ecf20Sopenharmony_ci		nlm_timeout = LOCKD_DFLT_TIMEO;
4528c2ecf20Sopenharmony_ci	nlmsvc_timeout = nlm_timeout * HZ;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, &lockd_sv_ops);
4558c2ecf20Sopenharmony_ci	if (!serv) {
4568c2ecf20Sopenharmony_ci		printk(KERN_WARNING "lockd_up: create service failed\n");
4578c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci	register_inetaddr_notifier(&lockd_inetaddr_notifier);
4608c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
4618c2ecf20Sopenharmony_ci	register_inet6addr_notifier(&lockd_inet6addr_notifier);
4628c2ecf20Sopenharmony_ci#endif
4638c2ecf20Sopenharmony_ci	dprintk("lockd_up: service created\n");
4648c2ecf20Sopenharmony_ci	return serv;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci/*
4688c2ecf20Sopenharmony_ci * Bring up the lockd process if it's not already up.
4698c2ecf20Sopenharmony_ci */
4708c2ecf20Sopenharmony_ciint lockd_up(struct net *net, const struct cred *cred)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	struct svc_serv *serv;
4738c2ecf20Sopenharmony_ci	int error;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	mutex_lock(&nlmsvc_mutex);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	serv = lockd_create_svc();
4788c2ecf20Sopenharmony_ci	if (IS_ERR(serv)) {
4798c2ecf20Sopenharmony_ci		error = PTR_ERR(serv);
4808c2ecf20Sopenharmony_ci		goto err_create;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	error = lockd_up_net(serv, net, cred);
4848c2ecf20Sopenharmony_ci	if (error < 0) {
4858c2ecf20Sopenharmony_ci		lockd_unregister_notifiers();
4868c2ecf20Sopenharmony_ci		goto err_put;
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	error = lockd_start_svc(serv);
4908c2ecf20Sopenharmony_ci	if (error < 0) {
4918c2ecf20Sopenharmony_ci		lockd_down_net(serv, net);
4928c2ecf20Sopenharmony_ci		goto err_put;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci	nlmsvc_users++;
4958c2ecf20Sopenharmony_ci	/*
4968c2ecf20Sopenharmony_ci	 * Note: svc_serv structures have an initial use count of 1,
4978c2ecf20Sopenharmony_ci	 * so we exit through here on both success and failure.
4988c2ecf20Sopenharmony_ci	 */
4998c2ecf20Sopenharmony_cierr_put:
5008c2ecf20Sopenharmony_ci	svc_destroy(serv);
5018c2ecf20Sopenharmony_cierr_create:
5028c2ecf20Sopenharmony_ci	mutex_unlock(&nlmsvc_mutex);
5038c2ecf20Sopenharmony_ci	return error;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lockd_up);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci/*
5088c2ecf20Sopenharmony_ci * Decrement the user count and bring down lockd if we're the last.
5098c2ecf20Sopenharmony_ci */
5108c2ecf20Sopenharmony_civoid
5118c2ecf20Sopenharmony_cilockd_down(struct net *net)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	mutex_lock(&nlmsvc_mutex);
5148c2ecf20Sopenharmony_ci	lockd_down_net(nlmsvc_rqst->rq_server, net);
5158c2ecf20Sopenharmony_ci	if (nlmsvc_users) {
5168c2ecf20Sopenharmony_ci		if (--nlmsvc_users)
5178c2ecf20Sopenharmony_ci			goto out;
5188c2ecf20Sopenharmony_ci	} else {
5198c2ecf20Sopenharmony_ci		printk(KERN_ERR "lockd_down: no users! task=%p\n",
5208c2ecf20Sopenharmony_ci			nlmsvc_task);
5218c2ecf20Sopenharmony_ci		BUG();
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	if (!nlmsvc_task) {
5258c2ecf20Sopenharmony_ci		printk(KERN_ERR "lockd_down: no lockd running.\n");
5268c2ecf20Sopenharmony_ci		BUG();
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci	kthread_stop(nlmsvc_task);
5298c2ecf20Sopenharmony_ci	dprintk("lockd_down: service stopped\n");
5308c2ecf20Sopenharmony_ci	lockd_svc_exit_thread();
5318c2ecf20Sopenharmony_ci	dprintk("lockd_down: service destroyed\n");
5328c2ecf20Sopenharmony_ci	nlmsvc_task = NULL;
5338c2ecf20Sopenharmony_ci	nlmsvc_rqst = NULL;
5348c2ecf20Sopenharmony_ciout:
5358c2ecf20Sopenharmony_ci	mutex_unlock(&nlmsvc_mutex);
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lockd_down);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci/*
5428c2ecf20Sopenharmony_ci * Sysctl parameters (same as module parameters, different interface).
5438c2ecf20Sopenharmony_ci */
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic struct ctl_table nlm_sysctls[] = {
5468c2ecf20Sopenharmony_ci	{
5478c2ecf20Sopenharmony_ci		.procname	= "nlm_grace_period",
5488c2ecf20Sopenharmony_ci		.data		= &nlm_grace_period,
5498c2ecf20Sopenharmony_ci		.maxlen		= sizeof(unsigned long),
5508c2ecf20Sopenharmony_ci		.mode		= 0644,
5518c2ecf20Sopenharmony_ci		.proc_handler	= proc_doulongvec_minmax,
5528c2ecf20Sopenharmony_ci		.extra1		= (unsigned long *) &nlm_grace_period_min,
5538c2ecf20Sopenharmony_ci		.extra2		= (unsigned long *) &nlm_grace_period_max,
5548c2ecf20Sopenharmony_ci	},
5558c2ecf20Sopenharmony_ci	{
5568c2ecf20Sopenharmony_ci		.procname	= "nlm_timeout",
5578c2ecf20Sopenharmony_ci		.data		= &nlm_timeout,
5588c2ecf20Sopenharmony_ci		.maxlen		= sizeof(unsigned long),
5598c2ecf20Sopenharmony_ci		.mode		= 0644,
5608c2ecf20Sopenharmony_ci		.proc_handler	= proc_doulongvec_minmax,
5618c2ecf20Sopenharmony_ci		.extra1		= (unsigned long *) &nlm_timeout_min,
5628c2ecf20Sopenharmony_ci		.extra2		= (unsigned long *) &nlm_timeout_max,
5638c2ecf20Sopenharmony_ci	},
5648c2ecf20Sopenharmony_ci	{
5658c2ecf20Sopenharmony_ci		.procname	= "nlm_udpport",
5668c2ecf20Sopenharmony_ci		.data		= &nlm_udpport,
5678c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
5688c2ecf20Sopenharmony_ci		.mode		= 0644,
5698c2ecf20Sopenharmony_ci		.proc_handler	= proc_dointvec_minmax,
5708c2ecf20Sopenharmony_ci		.extra1		= (int *) &nlm_port_min,
5718c2ecf20Sopenharmony_ci		.extra2		= (int *) &nlm_port_max,
5728c2ecf20Sopenharmony_ci	},
5738c2ecf20Sopenharmony_ci	{
5748c2ecf20Sopenharmony_ci		.procname	= "nlm_tcpport",
5758c2ecf20Sopenharmony_ci		.data		= &nlm_tcpport,
5768c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
5778c2ecf20Sopenharmony_ci		.mode		= 0644,
5788c2ecf20Sopenharmony_ci		.proc_handler	= proc_dointvec_minmax,
5798c2ecf20Sopenharmony_ci		.extra1		= (int *) &nlm_port_min,
5808c2ecf20Sopenharmony_ci		.extra2		= (int *) &nlm_port_max,
5818c2ecf20Sopenharmony_ci	},
5828c2ecf20Sopenharmony_ci	{
5838c2ecf20Sopenharmony_ci		.procname	= "nsm_use_hostnames",
5848c2ecf20Sopenharmony_ci		.data		= &nsm_use_hostnames,
5858c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
5868c2ecf20Sopenharmony_ci		.mode		= 0644,
5878c2ecf20Sopenharmony_ci		.proc_handler	= proc_dointvec,
5888c2ecf20Sopenharmony_ci	},
5898c2ecf20Sopenharmony_ci	{
5908c2ecf20Sopenharmony_ci		.procname	= "nsm_local_state",
5918c2ecf20Sopenharmony_ci		.data		= &nsm_local_state,
5928c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
5938c2ecf20Sopenharmony_ci		.mode		= 0644,
5948c2ecf20Sopenharmony_ci		.proc_handler	= proc_dointvec,
5958c2ecf20Sopenharmony_ci	},
5968c2ecf20Sopenharmony_ci	{ }
5978c2ecf20Sopenharmony_ci};
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic struct ctl_table nlm_sysctl_dir[] = {
6008c2ecf20Sopenharmony_ci	{
6018c2ecf20Sopenharmony_ci		.procname	= "nfs",
6028c2ecf20Sopenharmony_ci		.mode		= 0555,
6038c2ecf20Sopenharmony_ci		.child		= nlm_sysctls,
6048c2ecf20Sopenharmony_ci	},
6058c2ecf20Sopenharmony_ci	{ }
6068c2ecf20Sopenharmony_ci};
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic struct ctl_table nlm_sysctl_root[] = {
6098c2ecf20Sopenharmony_ci	{
6108c2ecf20Sopenharmony_ci		.procname	= "fs",
6118c2ecf20Sopenharmony_ci		.mode		= 0555,
6128c2ecf20Sopenharmony_ci		.child		= nlm_sysctl_dir,
6138c2ecf20Sopenharmony_ci	},
6148c2ecf20Sopenharmony_ci	{ }
6158c2ecf20Sopenharmony_ci};
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci#endif	/* CONFIG_SYSCTL */
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci/*
6208c2ecf20Sopenharmony_ci * Module (and sysfs) parameters.
6218c2ecf20Sopenharmony_ci */
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci#define param_set_min_max(name, type, which_strtol, min, max)		\
6248c2ecf20Sopenharmony_cistatic int param_set_##name(const char *val, const struct kernel_param *kp) \
6258c2ecf20Sopenharmony_ci{									\
6268c2ecf20Sopenharmony_ci	char *endp;							\
6278c2ecf20Sopenharmony_ci	__typeof__(type) num = which_strtol(val, &endp, 0);		\
6288c2ecf20Sopenharmony_ci	if (endp == val || *endp || num < (min) || num > (max))		\
6298c2ecf20Sopenharmony_ci		return -EINVAL;						\
6308c2ecf20Sopenharmony_ci	*((type *) kp->arg) = num;					\
6318c2ecf20Sopenharmony_ci	return 0;							\
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic inline int is_callback(u32 proc)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	return proc == NLMPROC_GRANTED
6378c2ecf20Sopenharmony_ci		|| proc == NLMPROC_GRANTED_MSG
6388c2ecf20Sopenharmony_ci		|| proc == NLMPROC_TEST_RES
6398c2ecf20Sopenharmony_ci		|| proc == NLMPROC_LOCK_RES
6408c2ecf20Sopenharmony_ci		|| proc == NLMPROC_CANCEL_RES
6418c2ecf20Sopenharmony_ci		|| proc == NLMPROC_UNLOCK_RES
6428c2ecf20Sopenharmony_ci		|| proc == NLMPROC_NSM_NOTIFY;
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic int lockd_authenticate(struct svc_rqst *rqstp)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	rqstp->rq_client = NULL;
6498c2ecf20Sopenharmony_ci	switch (rqstp->rq_authop->flavour) {
6508c2ecf20Sopenharmony_ci		case RPC_AUTH_NULL:
6518c2ecf20Sopenharmony_ci		case RPC_AUTH_UNIX:
6528c2ecf20Sopenharmony_ci			if (rqstp->rq_proc == 0)
6538c2ecf20Sopenharmony_ci				return SVC_OK;
6548c2ecf20Sopenharmony_ci			if (is_callback(rqstp->rq_proc)) {
6558c2ecf20Sopenharmony_ci				/* Leave it to individual procedures to
6568c2ecf20Sopenharmony_ci				 * call nlmsvc_lookup_host(rqstp)
6578c2ecf20Sopenharmony_ci				 */
6588c2ecf20Sopenharmony_ci				return SVC_OK;
6598c2ecf20Sopenharmony_ci			}
6608c2ecf20Sopenharmony_ci			return svc_set_client(rqstp);
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci	return SVC_DENIED;
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ciparam_set_min_max(port, int, simple_strtol, 0, 65535)
6678c2ecf20Sopenharmony_ciparam_set_min_max(grace_period, unsigned long, simple_strtoul,
6688c2ecf20Sopenharmony_ci		  nlm_grace_period_min, nlm_grace_period_max)
6698c2ecf20Sopenharmony_ciparam_set_min_max(timeout, unsigned long, simple_strtoul,
6708c2ecf20Sopenharmony_ci		  nlm_timeout_min, nlm_timeout_max)
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ciMODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
6738c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION ".");
6748c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cimodule_param_call(nlm_grace_period, param_set_grace_period, param_get_ulong,
6778c2ecf20Sopenharmony_ci		  &nlm_grace_period, 0644);
6788c2ecf20Sopenharmony_cimodule_param_call(nlm_timeout, param_set_timeout, param_get_ulong,
6798c2ecf20Sopenharmony_ci		  &nlm_timeout, 0644);
6808c2ecf20Sopenharmony_cimodule_param_call(nlm_udpport, param_set_port, param_get_int,
6818c2ecf20Sopenharmony_ci		  &nlm_udpport, 0644);
6828c2ecf20Sopenharmony_cimodule_param_call(nlm_tcpport, param_set_port, param_get_int,
6838c2ecf20Sopenharmony_ci		  &nlm_tcpport, 0644);
6848c2ecf20Sopenharmony_cimodule_param(nsm_use_hostnames, bool, 0644);
6858c2ecf20Sopenharmony_cimodule_param(nlm_max_connections, uint, 0644);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_cistatic int lockd_init_net(struct net *net)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	struct lockd_net *ln = net_generic(net, lockd_net_id);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender);
6928c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ln->lockd_manager.list);
6938c2ecf20Sopenharmony_ci	ln->lockd_manager.block_opens = false;
6948c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ln->nsm_handles);
6958c2ecf20Sopenharmony_ci	return 0;
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic void lockd_exit_net(struct net *net)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct lockd_net *ln = net_generic(net, lockd_net_id);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	WARN_ONCE(!list_empty(&ln->lockd_manager.list),
7038c2ecf20Sopenharmony_ci		  "net %x %s: lockd_manager.list is not empty\n",
7048c2ecf20Sopenharmony_ci		  net->ns.inum, __func__);
7058c2ecf20Sopenharmony_ci	WARN_ONCE(!list_empty(&ln->nsm_handles),
7068c2ecf20Sopenharmony_ci		  "net %x %s: nsm_handles list is not empty\n",
7078c2ecf20Sopenharmony_ci		  net->ns.inum, __func__);
7088c2ecf20Sopenharmony_ci	WARN_ONCE(delayed_work_pending(&ln->grace_period_end),
7098c2ecf20Sopenharmony_ci		  "net %x %s: grace_period_end was not cancelled\n",
7108c2ecf20Sopenharmony_ci		  net->ns.inum, __func__);
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic struct pernet_operations lockd_net_ops = {
7148c2ecf20Sopenharmony_ci	.init = lockd_init_net,
7158c2ecf20Sopenharmony_ci	.exit = lockd_exit_net,
7168c2ecf20Sopenharmony_ci	.id = &lockd_net_id,
7178c2ecf20Sopenharmony_ci	.size = sizeof(struct lockd_net),
7188c2ecf20Sopenharmony_ci};
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci/*
7228c2ecf20Sopenharmony_ci * Initialising and terminating the module.
7238c2ecf20Sopenharmony_ci */
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_cistatic int __init init_nlm(void)
7268c2ecf20Sopenharmony_ci{
7278c2ecf20Sopenharmony_ci	int err;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
7308c2ecf20Sopenharmony_ci	err = -ENOMEM;
7318c2ecf20Sopenharmony_ci	nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root);
7328c2ecf20Sopenharmony_ci	if (nlm_sysctl_table == NULL)
7338c2ecf20Sopenharmony_ci		goto err_sysctl;
7348c2ecf20Sopenharmony_ci#endif
7358c2ecf20Sopenharmony_ci	err = register_pernet_subsys(&lockd_net_ops);
7368c2ecf20Sopenharmony_ci	if (err)
7378c2ecf20Sopenharmony_ci		goto err_pernet;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	err = lockd_create_procfs();
7408c2ecf20Sopenharmony_ci	if (err)
7418c2ecf20Sopenharmony_ci		goto err_procfs;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	return 0;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cierr_procfs:
7468c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&lockd_net_ops);
7478c2ecf20Sopenharmony_cierr_pernet:
7488c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
7498c2ecf20Sopenharmony_ci	unregister_sysctl_table(nlm_sysctl_table);
7508c2ecf20Sopenharmony_cierr_sysctl:
7518c2ecf20Sopenharmony_ci#endif
7528c2ecf20Sopenharmony_ci	return err;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic void __exit exit_nlm(void)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	/* FIXME: delete all NLM clients */
7588c2ecf20Sopenharmony_ci	nlm_shutdown_hosts();
7598c2ecf20Sopenharmony_ci	lockd_remove_procfs();
7608c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&lockd_net_ops);
7618c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
7628c2ecf20Sopenharmony_ci	unregister_sysctl_table(nlm_sysctl_table);
7638c2ecf20Sopenharmony_ci#endif
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cimodule_init(init_nlm);
7678c2ecf20Sopenharmony_cimodule_exit(exit_nlm);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci/*
7708c2ecf20Sopenharmony_ci * Define NLM program and procedures
7718c2ecf20Sopenharmony_ci */
7728c2ecf20Sopenharmony_cistatic unsigned int nlmsvc_version1_count[17];
7738c2ecf20Sopenharmony_cistatic const struct svc_version	nlmsvc_version1 = {
7748c2ecf20Sopenharmony_ci	.vs_vers	= 1,
7758c2ecf20Sopenharmony_ci	.vs_nproc	= 17,
7768c2ecf20Sopenharmony_ci	.vs_proc	= nlmsvc_procedures,
7778c2ecf20Sopenharmony_ci	.vs_count	= nlmsvc_version1_count,
7788c2ecf20Sopenharmony_ci	.vs_xdrsize	= NLMSVC_XDRSIZE,
7798c2ecf20Sopenharmony_ci};
7808c2ecf20Sopenharmony_cistatic unsigned int nlmsvc_version3_count[24];
7818c2ecf20Sopenharmony_cistatic const struct svc_version	nlmsvc_version3 = {
7828c2ecf20Sopenharmony_ci	.vs_vers	= 3,
7838c2ecf20Sopenharmony_ci	.vs_nproc	= 24,
7848c2ecf20Sopenharmony_ci	.vs_proc	= nlmsvc_procedures,
7858c2ecf20Sopenharmony_ci	.vs_count	= nlmsvc_version3_count,
7868c2ecf20Sopenharmony_ci	.vs_xdrsize	= NLMSVC_XDRSIZE,
7878c2ecf20Sopenharmony_ci};
7888c2ecf20Sopenharmony_ci#ifdef CONFIG_LOCKD_V4
7898c2ecf20Sopenharmony_cistatic unsigned int nlmsvc_version4_count[24];
7908c2ecf20Sopenharmony_cistatic const struct svc_version	nlmsvc_version4 = {
7918c2ecf20Sopenharmony_ci	.vs_vers	= 4,
7928c2ecf20Sopenharmony_ci	.vs_nproc	= 24,
7938c2ecf20Sopenharmony_ci	.vs_proc	= nlmsvc_procedures4,
7948c2ecf20Sopenharmony_ci	.vs_count	= nlmsvc_version4_count,
7958c2ecf20Sopenharmony_ci	.vs_xdrsize	= NLMSVC_XDRSIZE,
7968c2ecf20Sopenharmony_ci};
7978c2ecf20Sopenharmony_ci#endif
7988c2ecf20Sopenharmony_cistatic const struct svc_version *nlmsvc_version[] = {
7998c2ecf20Sopenharmony_ci	[1] = &nlmsvc_version1,
8008c2ecf20Sopenharmony_ci	[3] = &nlmsvc_version3,
8018c2ecf20Sopenharmony_ci#ifdef CONFIG_LOCKD_V4
8028c2ecf20Sopenharmony_ci	[4] = &nlmsvc_version4,
8038c2ecf20Sopenharmony_ci#endif
8048c2ecf20Sopenharmony_ci};
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_cistatic struct svc_stat		nlmsvc_stats;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci#define NLM_NRVERS	ARRAY_SIZE(nlmsvc_version)
8098c2ecf20Sopenharmony_cistatic struct svc_program	nlmsvc_program = {
8108c2ecf20Sopenharmony_ci	.pg_prog		= NLM_PROGRAM,		/* program number */
8118c2ecf20Sopenharmony_ci	.pg_nvers		= NLM_NRVERS,		/* number of entries in nlmsvc_version */
8128c2ecf20Sopenharmony_ci	.pg_vers		= nlmsvc_version,	/* version table */
8138c2ecf20Sopenharmony_ci	.pg_name		= "lockd",		/* service name */
8148c2ecf20Sopenharmony_ci	.pg_class		= "nfsd",		/* share authentication with nfsd */
8158c2ecf20Sopenharmony_ci	.pg_stats		= &nlmsvc_stats,	/* stats table */
8168c2ecf20Sopenharmony_ci	.pg_authenticate	= &lockd_authenticate,	/* export authentication */
8178c2ecf20Sopenharmony_ci	.pg_init_request	= svc_generic_init_request,
8188c2ecf20Sopenharmony_ci	.pg_rpcbind_set		= svc_generic_rpcbind_set,
8198c2ecf20Sopenharmony_ci};
820