xref: /openbmc/linux/fs/lockd/mon.c (revision 5cf1c4b19db99d21d44c2ab457cfd44eb86b4439)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/fs/lockd/mon.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * The kernel statd client.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
71da177e4SLinus Torvalds  */
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds #include <linux/types.h>
101da177e4SLinus Torvalds #include <linux/utsname.h>
111da177e4SLinus Torvalds #include <linux/kernel.h>
121da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
130896a725S\"Talpey, Thomas\ #include <linux/sunrpc/xprtsock.h>
141da177e4SLinus Torvalds #include <linux/sunrpc/svc.h>
151da177e4SLinus Torvalds #include <linux/lockd/lockd.h>
161da177e4SLinus Torvalds #include <linux/lockd/sm_inter.h>
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds #define NLMDBG_FACILITY		NLMDBG_MONITOR
2036e8e668SChuck Lever #define NSM_PROGRAM		100024
2136e8e668SChuck Lever #define NSM_VERSION		1
2236e8e668SChuck Lever 
2336e8e668SChuck Lever enum {
2436e8e668SChuck Lever 	NSMPROC_NULL,
2536e8e668SChuck Lever 	NSMPROC_STAT,
2636e8e668SChuck Lever 	NSMPROC_MON,
2736e8e668SChuck Lever 	NSMPROC_UNMON,
2836e8e668SChuck Lever 	NSMPROC_UNMON_ALL,
2936e8e668SChuck Lever 	NSMPROC_SIMU_CRASH,
3036e8e668SChuck Lever 	NSMPROC_NOTIFY,
3136e8e668SChuck Lever };
321da177e4SLinus Torvalds 
339c1bfd03SChuck Lever struct nsm_args {
349c1bfd03SChuck Lever 	__be32			addr;		/* remote address */
359c1bfd03SChuck Lever 	u32			prog;		/* RPC callback info */
369c1bfd03SChuck Lever 	u32			vers;
379c1bfd03SChuck Lever 	u32			proc;
389c1bfd03SChuck Lever 
399c1bfd03SChuck Lever 	char			*mon_name;
409c1bfd03SChuck Lever };
419c1bfd03SChuck Lever 
429c1bfd03SChuck Lever struct nsm_res {
439c1bfd03SChuck Lever 	u32			status;
449c1bfd03SChuck Lever 	u32			state;
459c1bfd03SChuck Lever };
469c1bfd03SChuck Lever 
471da177e4SLinus Torvalds static struct rpc_clnt *	nsm_create(void);
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds static struct rpc_program	nsm_program;
5067c6d107SChuck Lever static				LIST_HEAD(nsm_handles);
5167c6d107SChuck Lever static				DEFINE_SPINLOCK(nsm_lock);
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds /*
541da177e4SLinus Torvalds  * Local NSM state
551da177e4SLinus Torvalds  */
56460f5cacSOlaf Kirch int				nsm_local_state;
571da177e4SLinus Torvalds 
5867c6d107SChuck Lever static void nsm_display_ipv4_address(const struct sockaddr *sap, char *buf,
5967c6d107SChuck Lever 				     const size_t len)
6067c6d107SChuck Lever {
6167c6d107SChuck Lever 	const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
6267c6d107SChuck Lever 	snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr);
6367c6d107SChuck Lever }
6467c6d107SChuck Lever 
6567c6d107SChuck Lever static void nsm_display_ipv6_address(const struct sockaddr *sap, char *buf,
6667c6d107SChuck Lever 				     const size_t len)
6767c6d107SChuck Lever {
6867c6d107SChuck Lever 	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
6967c6d107SChuck Lever 
7067c6d107SChuck Lever 	if (ipv6_addr_v4mapped(&sin6->sin6_addr))
7167c6d107SChuck Lever 		snprintf(buf, len, "%pI4", &sin6->sin6_addr.s6_addr32[3]);
7267c6d107SChuck Lever 	else if (sin6->sin6_scope_id != 0)
7367c6d107SChuck Lever 		snprintf(buf, len, "%pI6%%%u", &sin6->sin6_addr,
7467c6d107SChuck Lever 				sin6->sin6_scope_id);
7567c6d107SChuck Lever 	else
7667c6d107SChuck Lever 		snprintf(buf, len, "%pI6", &sin6->sin6_addr);
7767c6d107SChuck Lever }
7867c6d107SChuck Lever 
7967c6d107SChuck Lever static void nsm_display_address(const struct sockaddr *sap,
8067c6d107SChuck Lever 				char *buf, const size_t len)
8167c6d107SChuck Lever {
8267c6d107SChuck Lever 	switch (sap->sa_family) {
8367c6d107SChuck Lever 	case AF_INET:
8467c6d107SChuck Lever 		nsm_display_ipv4_address(sap, buf, len);
8567c6d107SChuck Lever 		break;
8667c6d107SChuck Lever 	case AF_INET6:
8767c6d107SChuck Lever 		nsm_display_ipv6_address(sap, buf, len);
8867c6d107SChuck Lever 		break;
8967c6d107SChuck Lever 	default:
9067c6d107SChuck Lever 		snprintf(buf, len, "unsupported address family");
9167c6d107SChuck Lever 		break;
9267c6d107SChuck Lever 	}
9367c6d107SChuck Lever }
9467c6d107SChuck Lever 
951da177e4SLinus Torvalds /*
9636e8e668SChuck Lever  * Common procedure for NSMPROC_MON/NSMPROC_UNMON calls
971da177e4SLinus Torvalds  */
981da177e4SLinus Torvalds static int
999502c522SOlaf Kirch nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds 	struct rpc_clnt	*clnt;
1021da177e4SLinus Torvalds 	int		status;
103a4846750SChuck Lever 	struct nsm_args args = {
104a4846750SChuck Lever 		.addr		= nsm_addr_in(nsm)->sin_addr.s_addr,
105a4846750SChuck Lever 		.prog		= NLM_PROGRAM,
106a4846750SChuck Lever 		.vers		= 3,
107a4846750SChuck Lever 		.proc		= NLMPROC_NSM_NOTIFY,
10829ed1407SChuck Lever 		.mon_name	= nsm->sm_mon_name,
109a4846750SChuck Lever 	};
110dead28daSChuck Lever 	struct rpc_message msg = {
111dead28daSChuck Lever 		.rpc_argp	= &args,
112dead28daSChuck Lever 		.rpc_resp	= res,
113dead28daSChuck Lever 	};
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 	clnt = nsm_create();
1161da177e4SLinus Torvalds 	if (IS_ERR(clnt)) {
1171da177e4SLinus Torvalds 		status = PTR_ERR(clnt);
1185acf4315SChuck Lever 		dprintk("lockd: failed to create NSM upcall transport, "
1195acf4315SChuck Lever 				"status=%d\n", status);
1201da177e4SLinus Torvalds 		goto out;
1211da177e4SLinus Torvalds 	}
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 	memset(res, 0, sizeof(*res));
1241da177e4SLinus Torvalds 
125dead28daSChuck Lever 	msg.rpc_proc = &clnt->cl_procinfo[proc];
126dead28daSChuck Lever 	status = rpc_call_sync(clnt, &msg, 0);
1271da177e4SLinus Torvalds 	if (status < 0)
1285acf4315SChuck Lever 		dprintk("lockd: NSM upcall RPC failed, status=%d\n",
1291da177e4SLinus Torvalds 				status);
1301da177e4SLinus Torvalds 	else
1311da177e4SLinus Torvalds 		status = 0;
13290c5755fSTrond Myklebust 	rpc_shutdown_client(clnt);
1331da177e4SLinus Torvalds  out:
1341da177e4SLinus Torvalds 	return status;
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds 
1371e49323cSChuck Lever /**
1381e49323cSChuck Lever  * nsm_monitor - Notify a peer in case we reboot
1391e49323cSChuck Lever  * @host: pointer to nlm_host of peer to notify
1401e49323cSChuck Lever  *
1411e49323cSChuck Lever  * If this peer is not already monitored, this function sends an
1421e49323cSChuck Lever  * upcall to the local rpc.statd to record the name/address of
1431e49323cSChuck Lever  * the peer to notify in case we reboot.
1441e49323cSChuck Lever  *
1451e49323cSChuck Lever  * Returns zero if the peer is monitored by the local rpc.statd;
1461e49323cSChuck Lever  * otherwise a negative errno value is returned.
1471da177e4SLinus Torvalds  */
1481e49323cSChuck Lever int nsm_monitor(const struct nlm_host *host)
1491da177e4SLinus Torvalds {
1508dead0dbSOlaf Kirch 	struct nsm_handle *nsm = host->h_nsmhandle;
1511da177e4SLinus Torvalds 	struct nsm_res	res;
1521da177e4SLinus Torvalds 	int		status;
1531da177e4SLinus Torvalds 
1549fee4902SChuck Lever 	dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name);
1558dead0dbSOlaf Kirch 
1568dead0dbSOlaf Kirch 	if (nsm->sm_monitored)
157977faf39SOlaf Kirch 		return 0;
1581da177e4SLinus Torvalds 
15929ed1407SChuck Lever 	/*
16029ed1407SChuck Lever 	 * Choose whether to record the caller_name or IP address of
16129ed1407SChuck Lever 	 * this peer in the local rpc.statd's database.
16229ed1407SChuck Lever 	 */
16329ed1407SChuck Lever 	nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;
16429ed1407SChuck Lever 
16536e8e668SChuck Lever 	status = nsm_mon_unmon(nsm, NSMPROC_MON, &res);
1665d254b11SChuck Lever 	if (res.status != 0)
1675d254b11SChuck Lever 		status = -EIO;
1685d254b11SChuck Lever 	if (status < 0)
1699fee4902SChuck Lever 		printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name);
1701da177e4SLinus Torvalds 	else
1718dead0dbSOlaf Kirch 		nsm->sm_monitored = 1;
1721da177e4SLinus Torvalds 	return status;
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds 
175356c3eb4SChuck Lever /**
176356c3eb4SChuck Lever  * nsm_unmonitor - Unregister peer notification
177356c3eb4SChuck Lever  * @host: pointer to nlm_host of peer to stop monitoring
178356c3eb4SChuck Lever  *
179356c3eb4SChuck Lever  * If this peer is monitored, this function sends an upcall to
180356c3eb4SChuck Lever  * tell the local rpc.statd not to send this peer a notification
181356c3eb4SChuck Lever  * when we reboot.
1821da177e4SLinus Torvalds  */
183356c3eb4SChuck Lever void nsm_unmonitor(const struct nlm_host *host)
1841da177e4SLinus Torvalds {
1858dead0dbSOlaf Kirch 	struct nsm_handle *nsm = host->h_nsmhandle;
1861da177e4SLinus Torvalds 	struct nsm_res	res;
187356c3eb4SChuck Lever 	int status;
1881da177e4SLinus Torvalds 
1899502c522SOlaf Kirch 	if (atomic_read(&nsm->sm_count) == 1
1909502c522SOlaf Kirch 	 && nsm->sm_monitored && !nsm->sm_sticky) {
1919fee4902SChuck Lever 		dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name);
1929502c522SOlaf Kirch 
19336e8e668SChuck Lever 		status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res);
1940c7aef45SChuck Lever 		if (res.status != 0)
1950c7aef45SChuck Lever 			status = -EIO;
1961da177e4SLinus Torvalds 		if (status < 0)
1979502c522SOlaf Kirch 			printk(KERN_NOTICE "lockd: cannot unmonitor %s\n",
1989fee4902SChuck Lever 					nsm->sm_name);
1999502c522SOlaf Kirch 		else
2008dead0dbSOlaf Kirch 			nsm->sm_monitored = 0;
201977faf39SOlaf Kirch 	}
2021da177e4SLinus Torvalds }
2031da177e4SLinus Torvalds 
20467c6d107SChuck Lever /**
20567c6d107SChuck Lever  * nsm_find - Find or create a cached nsm_handle
20667c6d107SChuck Lever  * @sap: pointer to socket address of handle to find
20767c6d107SChuck Lever  * @salen: length of socket address
20867c6d107SChuck Lever  * @hostname: pointer to C string containing hostname to find
20967c6d107SChuck Lever  * @hostname_len: length of C string
21067c6d107SChuck Lever  * @create: one means create new handle if not found in cache
21167c6d107SChuck Lever  *
21267c6d107SChuck Lever  * Behavior is modulated by the global nsm_use_hostnames variable
21367c6d107SChuck Lever  * and by the @create argument.
21467c6d107SChuck Lever  *
21567c6d107SChuck Lever  * Returns a cached nsm_handle after bumping its ref count, or if
21667c6d107SChuck Lever  * @create is set, returns a fresh nsm_handle if a handle that
21767c6d107SChuck Lever  * matches @sap and/or @hostname cannot be found in the handle cache.
21867c6d107SChuck Lever  * Returns NULL if an error occurs.
21967c6d107SChuck Lever  */
22067c6d107SChuck Lever struct nsm_handle *nsm_find(const struct sockaddr *sap, const size_t salen,
22167c6d107SChuck Lever 			    const char *hostname, const size_t hostname_len,
22267c6d107SChuck Lever 			    const int create)
22367c6d107SChuck Lever {
22467c6d107SChuck Lever 	struct nsm_handle *nsm = NULL;
22567c6d107SChuck Lever 	struct nsm_handle *pos;
22667c6d107SChuck Lever 
22767c6d107SChuck Lever 	if (!sap)
22867c6d107SChuck Lever 		return NULL;
22967c6d107SChuck Lever 
23067c6d107SChuck Lever 	if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
23167c6d107SChuck Lever 		if (printk_ratelimit()) {
23267c6d107SChuck Lever 			printk(KERN_WARNING "Invalid hostname \"%.*s\" "
23367c6d107SChuck Lever 					    "in NFS lock request\n",
23467c6d107SChuck Lever 				(int)hostname_len, hostname);
23567c6d107SChuck Lever 		}
23667c6d107SChuck Lever 		return NULL;
23767c6d107SChuck Lever 	}
23867c6d107SChuck Lever 
23967c6d107SChuck Lever retry:
24067c6d107SChuck Lever 	spin_lock(&nsm_lock);
24167c6d107SChuck Lever 	list_for_each_entry(pos, &nsm_handles, sm_link) {
24267c6d107SChuck Lever 
24367c6d107SChuck Lever 		if (hostname && nsm_use_hostnames) {
24467c6d107SChuck Lever 			if (strlen(pos->sm_name) != hostname_len
24567c6d107SChuck Lever 			 || memcmp(pos->sm_name, hostname, hostname_len))
24667c6d107SChuck Lever 				continue;
24767c6d107SChuck Lever 		} else if (!nlm_cmp_addr(nsm_addr(pos), sap))
24867c6d107SChuck Lever 			continue;
24967c6d107SChuck Lever 		atomic_inc(&pos->sm_count);
25067c6d107SChuck Lever 		kfree(nsm);
25167c6d107SChuck Lever 		nsm = pos;
252*5cf1c4b1SChuck Lever 		dprintk("lockd: found nsm_handle for %s (%s), cnt %d\n",
253*5cf1c4b1SChuck Lever 				pos->sm_name, pos->sm_addrbuf,
254*5cf1c4b1SChuck Lever 				atomic_read(&pos->sm_count));
25567c6d107SChuck Lever 		goto found;
25667c6d107SChuck Lever 	}
25767c6d107SChuck Lever 	if (nsm) {
25867c6d107SChuck Lever 		list_add(&nsm->sm_link, &nsm_handles);
259*5cf1c4b1SChuck Lever 		dprintk("lockd: created nsm_handle for %s (%s)\n",
260*5cf1c4b1SChuck Lever 				nsm->sm_name, nsm->sm_addrbuf);
26167c6d107SChuck Lever 		goto found;
26267c6d107SChuck Lever 	}
26367c6d107SChuck Lever 	spin_unlock(&nsm_lock);
26467c6d107SChuck Lever 
26567c6d107SChuck Lever 	if (!create)
26667c6d107SChuck Lever 		return NULL;
26767c6d107SChuck Lever 
26867c6d107SChuck Lever 	nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL);
26967c6d107SChuck Lever 	if (nsm == NULL)
27067c6d107SChuck Lever 		return NULL;
27167c6d107SChuck Lever 
27267c6d107SChuck Lever 	memcpy(nsm_addr(nsm), sap, salen);
27367c6d107SChuck Lever 	nsm->sm_addrlen = salen;
27467c6d107SChuck Lever 	nsm->sm_name = (char *) (nsm + 1);
27567c6d107SChuck Lever 	memcpy(nsm->sm_name, hostname, hostname_len);
27667c6d107SChuck Lever 	nsm->sm_name[hostname_len] = '\0';
27767c6d107SChuck Lever 	nsm_display_address((struct sockaddr *)&nsm->sm_addr,
27867c6d107SChuck Lever 				nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf));
27967c6d107SChuck Lever 	atomic_set(&nsm->sm_count, 1);
28067c6d107SChuck Lever 	goto retry;
28167c6d107SChuck Lever 
28267c6d107SChuck Lever found:
28367c6d107SChuck Lever 	spin_unlock(&nsm_lock);
28467c6d107SChuck Lever 	return nsm;
28567c6d107SChuck Lever }
28667c6d107SChuck Lever 
28767c6d107SChuck Lever /**
28867c6d107SChuck Lever  * nsm_release - Release an NSM handle
28967c6d107SChuck Lever  * @nsm: pointer to handle to be released
29067c6d107SChuck Lever  *
29167c6d107SChuck Lever  */
29267c6d107SChuck Lever void nsm_release(struct nsm_handle *nsm)
29367c6d107SChuck Lever {
29467c6d107SChuck Lever 	if (!nsm)
29567c6d107SChuck Lever 		return;
29667c6d107SChuck Lever 	if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) {
29767c6d107SChuck Lever 		list_del(&nsm->sm_link);
29867c6d107SChuck Lever 		spin_unlock(&nsm_lock);
299*5cf1c4b1SChuck Lever 		dprintk("lockd: destroyed nsm_handle for %s (%s)\n",
300*5cf1c4b1SChuck Lever 				nsm->sm_name, nsm->sm_addrbuf);
30167c6d107SChuck Lever 		kfree(nsm);
30267c6d107SChuck Lever 	}
30367c6d107SChuck Lever }
30467c6d107SChuck Lever 
3051da177e4SLinus Torvalds /*
3061da177e4SLinus Torvalds  * Create NSM client for the local host
3071da177e4SLinus Torvalds  */
3081da177e4SLinus Torvalds static struct rpc_clnt *
3091da177e4SLinus Torvalds nsm_create(void)
3101da177e4SLinus Torvalds {
311e1ec7892SChuck Lever 	struct sockaddr_in	sin = {
312e1ec7892SChuck Lever 		.sin_family	= AF_INET,
313e1ec7892SChuck Lever 		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
314e1ec7892SChuck Lever 		.sin_port	= 0,
315e1ec7892SChuck Lever 	};
316e1ec7892SChuck Lever 	struct rpc_create_args args = {
3170896a725S\"Talpey, Thomas\ 		.protocol	= XPRT_TRANSPORT_UDP,
318e1ec7892SChuck Lever 		.address	= (struct sockaddr *)&sin,
319e1ec7892SChuck Lever 		.addrsize	= sizeof(sin),
320e1ec7892SChuck Lever 		.servername	= "localhost",
321e1ec7892SChuck Lever 		.program	= &nsm_program,
32236e8e668SChuck Lever 		.version	= NSM_VERSION,
323e1ec7892SChuck Lever 		.authflavor	= RPC_AUTH_NULL,
324e1ec7892SChuck Lever 	};
3251da177e4SLinus Torvalds 
326e1ec7892SChuck Lever 	return rpc_create(&args);
3271da177e4SLinus Torvalds }
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds /*
3301da177e4SLinus Torvalds  * XDR functions for NSM.
3312ca7754dSChuck Lever  *
3322ca7754dSChuck Lever  * See http://www.opengroup.org/ for details on the Network
3332ca7754dSChuck Lever  * Status Monitor wire protocol.
3341da177e4SLinus Torvalds  */
3351da177e4SLinus Torvalds 
33603eb1dcbSChuck Lever static int encode_nsm_string(struct xdr_stream *xdr, const char *string)
337099bd05fSChuck Lever {
33803eb1dcbSChuck Lever 	const u32 len = strlen(string);
33903eb1dcbSChuck Lever 	__be32 *p;
340099bd05fSChuck Lever 
34103eb1dcbSChuck Lever 	if (unlikely(len > SM_MAXSTRLEN))
34203eb1dcbSChuck Lever 		return -EIO;
34303eb1dcbSChuck Lever 	p = xdr_reserve_space(xdr, sizeof(u32) + len);
34403eb1dcbSChuck Lever 	if (unlikely(p == NULL))
34503eb1dcbSChuck Lever 		return -EIO;
34603eb1dcbSChuck Lever 	xdr_encode_opaque(p, string, len);
34703eb1dcbSChuck Lever 	return 0;
348099bd05fSChuck Lever }
349099bd05fSChuck Lever 
35049695174SChuck Lever /*
35149695174SChuck Lever  * "mon_name" specifies the host to be monitored.
35249695174SChuck Lever  */
35303eb1dcbSChuck Lever static int encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp)
35449695174SChuck Lever {
35503eb1dcbSChuck Lever 	return encode_nsm_string(xdr, argp->mon_name);
35649695174SChuck Lever }
35749695174SChuck Lever 
358850c95fdSChuck Lever /*
359850c95fdSChuck Lever  * The "my_id" argument specifies the hostname and RPC procedure
360850c95fdSChuck Lever  * to be called when the status manager receives notification
36136e8e668SChuck Lever  * (via the NLMPROC_SM_NOTIFY call) that the state of host "mon_name"
362850c95fdSChuck Lever  * has changed.
363850c95fdSChuck Lever  */
36403eb1dcbSChuck Lever static int encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp)
365850c95fdSChuck Lever {
36603eb1dcbSChuck Lever 	int status;
36703eb1dcbSChuck Lever 	__be32 *p;
368850c95fdSChuck Lever 
36903eb1dcbSChuck Lever 	status = encode_nsm_string(xdr, utsname()->nodename);
37003eb1dcbSChuck Lever 	if (unlikely(status != 0))
37103eb1dcbSChuck Lever 		return status;
37203eb1dcbSChuck Lever 	p = xdr_reserve_space(xdr, 3 * sizeof(u32));
37303eb1dcbSChuck Lever 	if (unlikely(p == NULL))
37403eb1dcbSChuck Lever 		return -EIO;
375850c95fdSChuck Lever 	*p++ = htonl(argp->prog);
376850c95fdSChuck Lever 	*p++ = htonl(argp->vers);
377850c95fdSChuck Lever 	*p++ = htonl(argp->proc);
37803eb1dcbSChuck Lever 	return 0;
379850c95fdSChuck Lever }
380850c95fdSChuck Lever 
381ea72a7f1SChuck Lever /*
382ea72a7f1SChuck Lever  * The "mon_id" argument specifies the non-private arguments
38336e8e668SChuck Lever  * of an NSMPROC_MON or NSMPROC_UNMON call.
384ea72a7f1SChuck Lever  */
38503eb1dcbSChuck Lever static int encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp)
386ea72a7f1SChuck Lever {
38703eb1dcbSChuck Lever 	int status;
388ea72a7f1SChuck Lever 
38903eb1dcbSChuck Lever 	status = encode_mon_name(xdr, argp);
39003eb1dcbSChuck Lever 	if (unlikely(status != 0))
39103eb1dcbSChuck Lever 		return status;
39203eb1dcbSChuck Lever 	return encode_my_id(xdr, argp);
393ea72a7f1SChuck Lever }
394ea72a7f1SChuck Lever 
3950490a54aSChuck Lever /*
3960490a54aSChuck Lever  * The "priv" argument may contain private information required
39736e8e668SChuck Lever  * by the NSMPROC_MON call. This information will be supplied in the
39836e8e668SChuck Lever  * NLMPROC_SM_NOTIFY call.
3990490a54aSChuck Lever  *
4000490a54aSChuck Lever  * Linux provides the raw IP address of the monitored host,
4010490a54aSChuck Lever  * left in network byte order.
4020490a54aSChuck Lever  */
40303eb1dcbSChuck Lever static int encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp)
4040490a54aSChuck Lever {
40503eb1dcbSChuck Lever 	__be32 *p;
40603eb1dcbSChuck Lever 
40703eb1dcbSChuck Lever 	p = xdr_reserve_space(xdr, SM_PRIV_SIZE);
40803eb1dcbSChuck Lever 	if (unlikely(p == NULL))
40903eb1dcbSChuck Lever 		return -EIO;
4100490a54aSChuck Lever 	*p++ = argp->addr;
4110490a54aSChuck Lever 	*p++ = 0;
4120490a54aSChuck Lever 	*p++ = 0;
4130490a54aSChuck Lever 	*p++ = 0;
4141da177e4SLinus Torvalds 	return 0;
4151da177e4SLinus Torvalds }
4161da177e4SLinus Torvalds 
41703eb1dcbSChuck Lever static int xdr_enc_mon(struct rpc_rqst *req, __be32 *p,
41803eb1dcbSChuck Lever 		       const struct nsm_args *argp)
4191da177e4SLinus Torvalds {
42003eb1dcbSChuck Lever 	struct xdr_stream xdr;
42103eb1dcbSChuck Lever 	int status;
42203eb1dcbSChuck Lever 
42303eb1dcbSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
42403eb1dcbSChuck Lever 	status = encode_mon_id(&xdr, argp);
42503eb1dcbSChuck Lever 	if (unlikely(status))
42603eb1dcbSChuck Lever 		return status;
42703eb1dcbSChuck Lever 	return encode_priv(&xdr, argp);
4281da177e4SLinus Torvalds }
4291da177e4SLinus Torvalds 
43003eb1dcbSChuck Lever static int xdr_enc_unmon(struct rpc_rqst *req, __be32 *p,
43103eb1dcbSChuck Lever 			 const struct nsm_args *argp)
4321da177e4SLinus Torvalds {
43303eb1dcbSChuck Lever 	struct xdr_stream xdr;
43403eb1dcbSChuck Lever 
43503eb1dcbSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
43603eb1dcbSChuck Lever 	return encode_mon_id(&xdr, argp);
43703eb1dcbSChuck Lever }
43803eb1dcbSChuck Lever 
43903eb1dcbSChuck Lever static int xdr_dec_stat_res(struct rpc_rqst *rqstp, __be32 *p,
44003eb1dcbSChuck Lever 			    struct nsm_res *resp)
44103eb1dcbSChuck Lever {
44203eb1dcbSChuck Lever 	struct xdr_stream xdr;
44303eb1dcbSChuck Lever 
44403eb1dcbSChuck Lever 	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
44503eb1dcbSChuck Lever 	p = xdr_inline_decode(&xdr, 2 * sizeof(u32));
44603eb1dcbSChuck Lever 	if (unlikely(p == NULL))
44703eb1dcbSChuck Lever 		return -EIO;
4481da177e4SLinus Torvalds 	resp->status = ntohl(*p++);
44903eb1dcbSChuck Lever 	resp->state = ntohl(*p);
45003eb1dcbSChuck Lever 
45103eb1dcbSChuck Lever 	dprintk("lockd: xdr_dec_stat_res status %d state %d\n",
4521da177e4SLinus Torvalds 			resp->status, resp->state);
4531da177e4SLinus Torvalds 	return 0;
4541da177e4SLinus Torvalds }
4551da177e4SLinus Torvalds 
45603eb1dcbSChuck Lever static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p,
45703eb1dcbSChuck Lever 			struct nsm_res *resp)
4581da177e4SLinus Torvalds {
45903eb1dcbSChuck Lever 	struct xdr_stream xdr;
46003eb1dcbSChuck Lever 
46103eb1dcbSChuck Lever 	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
46203eb1dcbSChuck Lever 	p = xdr_inline_decode(&xdr, sizeof(u32));
46303eb1dcbSChuck Lever 	if (unlikely(p == NULL))
46403eb1dcbSChuck Lever 		return -EIO;
46503eb1dcbSChuck Lever 	resp->state = ntohl(*p);
46603eb1dcbSChuck Lever 
46703eb1dcbSChuck Lever 	dprintk("lockd: xdr_dec_stat state %d\n", resp->state);
4681da177e4SLinus Torvalds 	return 0;
4691da177e4SLinus Torvalds }
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds #define SM_my_name_sz	(1+XDR_QUADLEN(SM_MAXSTRLEN))
4722ca7754dSChuck Lever #define SM_my_id_sz	(SM_my_name_sz+3)
4732ca7754dSChuck Lever #define SM_mon_name_sz	(1+XDR_QUADLEN(SM_MAXSTRLEN))
4742ca7754dSChuck Lever #define SM_mon_id_sz	(SM_mon_name_sz+SM_my_id_sz)
4750490a54aSChuck Lever #define SM_priv_sz	(XDR_QUADLEN(SM_PRIV_SIZE))
4760490a54aSChuck Lever #define SM_mon_sz	(SM_mon_id_sz+SM_priv_sz)
4771da177e4SLinus Torvalds #define SM_monres_sz	2
4781da177e4SLinus Torvalds #define SM_unmonres_sz	1
4791da177e4SLinus Torvalds 
4801da177e4SLinus Torvalds static struct rpc_procinfo	nsm_procedures[] = {
48136e8e668SChuck Lever [NSMPROC_MON] = {
48236e8e668SChuck Lever 		.p_proc		= NSMPROC_MON,
48303eb1dcbSChuck Lever 		.p_encode	= (kxdrproc_t)xdr_enc_mon,
48403eb1dcbSChuck Lever 		.p_decode	= (kxdrproc_t)xdr_dec_stat_res,
4852bea90d4SChuck Lever 		.p_arglen	= SM_mon_sz,
4862bea90d4SChuck Lever 		.p_replen	= SM_monres_sz,
48736e8e668SChuck Lever 		.p_statidx	= NSMPROC_MON,
488cc0175c1SChuck Lever 		.p_name		= "MONITOR",
4891da177e4SLinus Torvalds 	},
49036e8e668SChuck Lever [NSMPROC_UNMON] = {
49136e8e668SChuck Lever 		.p_proc		= NSMPROC_UNMON,
49203eb1dcbSChuck Lever 		.p_encode	= (kxdrproc_t)xdr_enc_unmon,
49303eb1dcbSChuck Lever 		.p_decode	= (kxdrproc_t)xdr_dec_stat,
4942bea90d4SChuck Lever 		.p_arglen	= SM_mon_id_sz,
4952bea90d4SChuck Lever 		.p_replen	= SM_unmonres_sz,
49636e8e668SChuck Lever 		.p_statidx	= NSMPROC_UNMON,
497cc0175c1SChuck Lever 		.p_name		= "UNMONITOR",
4981da177e4SLinus Torvalds 	},
4991da177e4SLinus Torvalds };
5001da177e4SLinus Torvalds 
5011da177e4SLinus Torvalds static struct rpc_version	nsm_version1 = {
5021da177e4SLinus Torvalds 		.number		= 1,
503e8c96f8cSTobias Klauser 		.nrprocs	= ARRAY_SIZE(nsm_procedures),
5041da177e4SLinus Torvalds 		.procs		= nsm_procedures
5051da177e4SLinus Torvalds };
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds static struct rpc_version *	nsm_version[] = {
5081da177e4SLinus Torvalds 	[1] = &nsm_version1,
5091da177e4SLinus Torvalds };
5101da177e4SLinus Torvalds 
5111da177e4SLinus Torvalds static struct rpc_stat		nsm_stats;
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds static struct rpc_program	nsm_program = {
5141da177e4SLinus Torvalds 		.name		= "statd",
51536e8e668SChuck Lever 		.number		= NSM_PROGRAM,
516e8c96f8cSTobias Klauser 		.nrvers		= ARRAY_SIZE(nsm_version),
5171da177e4SLinus Torvalds 		.version	= nsm_version,
5181da177e4SLinus Torvalds 		.stats		= &nsm_stats
5191da177e4SLinus Torvalds };
520