xref: /openbmc/linux/fs/lockd/host.c (revision 723bb5b5052faba57060a2feb564ced22416b5bc)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/fs/lockd/host.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Management for NLM peer hosts. The nlm_host struct is shared
51da177e4SLinus Torvalds  * between client and server implementation. The only reason to
61da177e4SLinus Torvalds  * do so is to reduce code bloat.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #include <linux/types.h>
121da177e4SLinus Torvalds #include <linux/slab.h>
131da177e4SLinus Torvalds #include <linux/in.h>
141b333c54SChuck Lever #include <linux/in6.h>
151da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
161da177e4SLinus Torvalds #include <linux/sunrpc/svc.h>
171da177e4SLinus Torvalds #include <linux/lockd/lockd.h>
18353ab6e9SIngo Molnar #include <linux/mutex.h>
191da177e4SLinus Torvalds 
201b333c54SChuck Lever #include <net/ipv6.h>
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #define NLMDBG_FACILITY		NLMDBG_HOSTCACHE
231da177e4SLinus Torvalds #define NLM_HOST_NRHASH		32
241da177e4SLinus Torvalds #define NLM_HOST_REBIND		(60 * HZ)
251447d25eSNeilBrown #define NLM_HOST_EXPIRE		(300 * HZ)
261447d25eSNeilBrown #define NLM_HOST_COLLECT	(120 * HZ)
271da177e4SLinus Torvalds 
280cea3276SOlaf Kirch static struct hlist_head	nlm_hosts[NLM_HOST_NRHASH];
29b1137468SJ. Bruce Fields 
30b1137468SJ. Bruce Fields #define for_each_host(host, pos, chain, table) \
31b1137468SJ. Bruce Fields 	for ((chain) = (table); \
32b1137468SJ. Bruce Fields 	     (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
33b1137468SJ. Bruce Fields 		hlist_for_each_entry((host), (pos), (chain), h_hash)
34b1137468SJ. Bruce Fields 
35b1137468SJ. Bruce Fields #define for_each_host_safe(host, pos, next, chain, table) \
36b1137468SJ. Bruce Fields 	for ((chain) = (table); \
37b1137468SJ. Bruce Fields 	     (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
38b1137468SJ. Bruce Fields 		hlist_for_each_entry_safe((host), (pos), (next), \
39b1137468SJ. Bruce Fields 						(chain), h_hash)
40b1137468SJ. Bruce Fields 
411da177e4SLinus Torvalds static unsigned long		next_gc;
421da177e4SLinus Torvalds static int			nrhosts;
43353ab6e9SIngo Molnar static DEFINE_MUTEX(nlm_host_mutex);
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds static void			nlm_gc_hosts(void);
461da177e4SLinus Torvalds 
477f1ed18bSChuck Lever struct nlm_lookup_host_info {
487f1ed18bSChuck Lever 	const int		server;		/* search for server|client */
4988541c84SChuck Lever 	const struct sockaddr	*sap;		/* address to search for */
5088541c84SChuck Lever 	const size_t		salen;		/* it's length */
517f1ed18bSChuck Lever 	const unsigned short	protocol;	/* transport to search for*/
527f1ed18bSChuck Lever 	const u32		version;	/* NLM version to search for */
537f1ed18bSChuck Lever 	const char		*hostname;	/* remote's hostname */
547f1ed18bSChuck Lever 	const size_t		hostname_len;	/* it's length */
5588541c84SChuck Lever 	const struct sockaddr	*src_sap;	/* our address (optional) */
567f1ed18bSChuck Lever 	const size_t		src_len;	/* it's length */
570cb2659bSChuck Lever 	const int		noresvport;	/* use non-priv port */
587f1ed18bSChuck Lever };
597f1ed18bSChuck Lever 
60ede2fea0SChuck Lever /*
61ede2fea0SChuck Lever  * Hash function must work well on big- and little-endian platforms
62ede2fea0SChuck Lever  */
63ede2fea0SChuck Lever static unsigned int __nlm_hash32(const __be32 n)
64ede2fea0SChuck Lever {
65ede2fea0SChuck Lever 	unsigned int hash = (__force u32)n ^ ((__force u32)n >> 16);
66ede2fea0SChuck Lever 	return hash ^ (hash >> 8);
67ede2fea0SChuck Lever }
68ede2fea0SChuck Lever 
69ede2fea0SChuck Lever static unsigned int __nlm_hash_addr4(const struct sockaddr *sap)
70ede2fea0SChuck Lever {
71ede2fea0SChuck Lever 	const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
72ede2fea0SChuck Lever 	return __nlm_hash32(sin->sin_addr.s_addr);
73ede2fea0SChuck Lever }
74ede2fea0SChuck Lever 
75ede2fea0SChuck Lever static unsigned int __nlm_hash_addr6(const struct sockaddr *sap)
76ede2fea0SChuck Lever {
77ede2fea0SChuck Lever 	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
78ede2fea0SChuck Lever 	const struct in6_addr addr = sin6->sin6_addr;
79ede2fea0SChuck Lever 	return __nlm_hash32(addr.s6_addr32[0]) ^
80ede2fea0SChuck Lever 	       __nlm_hash32(addr.s6_addr32[1]) ^
81ede2fea0SChuck Lever 	       __nlm_hash32(addr.s6_addr32[2]) ^
82ede2fea0SChuck Lever 	       __nlm_hash32(addr.s6_addr32[3]);
83ede2fea0SChuck Lever }
84ede2fea0SChuck Lever 
85ede2fea0SChuck Lever static unsigned int nlm_hash_address(const struct sockaddr *sap)
86ede2fea0SChuck Lever {
87ede2fea0SChuck Lever 	unsigned int hash;
88ede2fea0SChuck Lever 
89ede2fea0SChuck Lever 	switch (sap->sa_family) {
90ede2fea0SChuck Lever 	case AF_INET:
91ede2fea0SChuck Lever 		hash = __nlm_hash_addr4(sap);
92ede2fea0SChuck Lever 		break;
93ede2fea0SChuck Lever 	case AF_INET6:
94ede2fea0SChuck Lever 		hash = __nlm_hash_addr6(sap);
95ede2fea0SChuck Lever 		break;
96ede2fea0SChuck Lever 	default:
97ede2fea0SChuck Lever 		hash = 0;
98ede2fea0SChuck Lever 	}
99ede2fea0SChuck Lever 	return hash & (NLM_HOST_NRHASH - 1);
100ede2fea0SChuck Lever }
101ede2fea0SChuck Lever 
1021da177e4SLinus Torvalds /*
103a7952f40SChuck Lever  * Allocate and initialize an nlm_host.  Common to both client and server.
104a7952f40SChuck Lever  */
105a7952f40SChuck Lever static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
106a7952f40SChuck Lever 				       struct nsm_handle *nsm)
107a7952f40SChuck Lever {
108a7952f40SChuck Lever 	struct nlm_host *host = NULL;
109a7952f40SChuck Lever 	unsigned long now = jiffies;
110a7952f40SChuck Lever 
111a7952f40SChuck Lever 	if (nsm != NULL)
112a7952f40SChuck Lever 		atomic_inc(&nsm->sm_count);
113a7952f40SChuck Lever 	else {
114a7952f40SChuck Lever 		host = NULL;
115a7952f40SChuck Lever 		nsm = nsm_get_handle(ni->sap, ni->salen,
116a7952f40SChuck Lever 					ni->hostname, ni->hostname_len);
117a7952f40SChuck Lever 		if (unlikely(nsm == NULL)) {
118a7952f40SChuck Lever 			dprintk("lockd: %s failed; no nsm handle\n",
119a7952f40SChuck Lever 				__func__);
120a7952f40SChuck Lever 			goto out;
121a7952f40SChuck Lever 		}
122a7952f40SChuck Lever 	}
123a7952f40SChuck Lever 
124a7952f40SChuck Lever 	host = kmalloc(sizeof(*host), GFP_KERNEL);
125a7952f40SChuck Lever 	if (unlikely(host == NULL)) {
126a7952f40SChuck Lever 		dprintk("lockd: %s failed; no memory\n", __func__);
127a7952f40SChuck Lever 		nsm_release(nsm);
128a7952f40SChuck Lever 		goto out;
129a7952f40SChuck Lever 	}
130a7952f40SChuck Lever 
131a7952f40SChuck Lever 	memcpy(nlm_addr(host), ni->sap, ni->salen);
132a7952f40SChuck Lever 	host->h_addrlen    = ni->salen;
133a7952f40SChuck Lever 	rpc_set_port(nlm_addr(host), 0);
134a7952f40SChuck Lever 	host->h_srcaddrlen = 0;
135a7952f40SChuck Lever 
136a7952f40SChuck Lever 	host->h_rpcclnt    = NULL;
137a7952f40SChuck Lever 	host->h_name	   = nsm->sm_name;
138a7952f40SChuck Lever 	host->h_version    = ni->version;
139a7952f40SChuck Lever 	host->h_proto      = ni->protocol;
140a7952f40SChuck Lever 	host->h_reclaiming = 0;
141a7952f40SChuck Lever 	host->h_server     = ni->server;
142a7952f40SChuck Lever 	host->h_noresvport = ni->noresvport;
143a7952f40SChuck Lever 	host->h_inuse      = 0;
144a7952f40SChuck Lever 	init_waitqueue_head(&host->h_gracewait);
145a7952f40SChuck Lever 	init_rwsem(&host->h_rwsem);
146a7952f40SChuck Lever 	host->h_state      = 0;
147a7952f40SChuck Lever 	host->h_nsmstate   = 0;
148a7952f40SChuck Lever 	host->h_pidcount   = 0;
149a7952f40SChuck Lever 	atomic_set(&host->h_count, 1);
150a7952f40SChuck Lever 	mutex_init(&host->h_mutex);
151a7952f40SChuck Lever 	host->h_nextrebind = now + NLM_HOST_REBIND;
152a7952f40SChuck Lever 	host->h_expires    = now + NLM_HOST_EXPIRE;
153a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_lockowners);
154a7952f40SChuck Lever 	spin_lock_init(&host->h_lock);
155a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_granted);
156a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_reclaim);
157a7952f40SChuck Lever 	host->h_nsmhandle  = nsm;
158a7952f40SChuck Lever 	host->h_addrbuf    = nsm->sm_addrbuf;
159a7952f40SChuck Lever 
160a7952f40SChuck Lever out:
161a7952f40SChuck Lever 	return host;
162a7952f40SChuck Lever }
163a7952f40SChuck Lever 
164a7952f40SChuck Lever /*
1651da177e4SLinus Torvalds  * Common host lookup routine for server & client
1661da177e4SLinus Torvalds  */
1677f1ed18bSChuck Lever static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
1681da177e4SLinus Torvalds {
1690cea3276SOlaf Kirch 	struct hlist_head *chain;
1700cea3276SOlaf Kirch 	struct hlist_node *pos;
1710cea3276SOlaf Kirch 	struct nlm_host	*host;
1728dead0dbSOlaf Kirch 	struct nsm_handle *nsm = NULL;
1731da177e4SLinus Torvalds 
174353ab6e9SIngo Molnar 	mutex_lock(&nlm_host_mutex);
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	if (time_after_eq(jiffies, next_gc))
1771da177e4SLinus Torvalds 		nlm_gc_hosts();
1781da177e4SLinus Torvalds 
1798dead0dbSOlaf Kirch 	/* We may keep several nlm_host objects for a peer, because each
1808dead0dbSOlaf Kirch 	 * nlm_host is identified by
1818dead0dbSOlaf Kirch 	 * (address, protocol, version, server/client)
1828dead0dbSOlaf Kirch 	 * We could probably simplify this a little by putting all those
1838dead0dbSOlaf Kirch 	 * different NLM rpc_clients into one single nlm_host object.
1848dead0dbSOlaf Kirch 	 * This would allow us to have one nlm_host per address.
1858dead0dbSOlaf Kirch 	 */
18688541c84SChuck Lever 	chain = &nlm_hosts[nlm_hash_address(ni->sap)];
1870cea3276SOlaf Kirch 	hlist_for_each_entry(host, pos, chain, h_hash) {
1884516fc04SJeff Layton 		if (!rpc_cmp_addr(nlm_addr(host), ni->sap))
1898dead0dbSOlaf Kirch 			continue;
1908dead0dbSOlaf Kirch 
1918dead0dbSOlaf Kirch 		/* See if we have an NSM handle for this client */
1926b54dae2SNeilBrown 		if (!nsm)
1936b54dae2SNeilBrown 			nsm = host->h_nsmhandle;
1948dead0dbSOlaf Kirch 
1957f1ed18bSChuck Lever 		if (host->h_proto != ni->protocol)
1961da177e4SLinus Torvalds 			continue;
1977f1ed18bSChuck Lever 		if (host->h_version != ni->version)
1981da177e4SLinus Torvalds 			continue;
1997f1ed18bSChuck Lever 		if (host->h_server != ni->server)
2001da177e4SLinus Torvalds 			continue;
2018e35f8e7STrond Myklebust 		if (ni->server && ni->src_len != 0 &&
2024516fc04SJeff Layton 		    !rpc_cmp_addr(nlm_srcaddr(host), ni->src_sap))
203c98451bdSFrank van Maarseveen 			continue;
2041da177e4SLinus Torvalds 
2050cea3276SOlaf Kirch 		/* Move to head of hash chain. */
2060cea3276SOlaf Kirch 		hlist_del(&host->h_hash);
2070cea3276SOlaf Kirch 		hlist_add_head(&host->h_hash, chain);
2080cea3276SOlaf Kirch 
2091da177e4SLinus Torvalds 		nlm_get_host(host);
2101b333c54SChuck Lever 		dprintk("lockd: nlm_lookup_host found host %s (%s)\n",
2111b333c54SChuck Lever 				host->h_name, host->h_addrbuf);
212f0737a39SOlaf Kirch 		goto out;
2131da177e4SLinus Torvalds 	}
214c2526f42SChuck Lever 
215a7952f40SChuck Lever 	host = nlm_alloc_host(ni, nsm);
216a7952f40SChuck Lever 	if (unlikely(host == NULL))
2178dead0dbSOlaf Kirch 		goto out;
2181da177e4SLinus Torvalds 
21988541c84SChuck Lever 	memcpy(nlm_srcaddr(host), ni->src_sap, ni->src_len);
2208e35f8e7STrond Myklebust 	host->h_srcaddrlen = ni->src_len;
2210cea3276SOlaf Kirch 	hlist_add_head(&host->h_hash, chain);
2221da177e4SLinus Torvalds 
2231447d25eSNeilBrown 	nrhosts++;
2241b333c54SChuck Lever 
2251b333c54SChuck Lever 	dprintk("lockd: nlm_lookup_host created host %s\n",
2261b333c54SChuck Lever 			host->h_name);
2271b333c54SChuck Lever 
2288dead0dbSOlaf Kirch out:
229353ab6e9SIngo Molnar 	mutex_unlock(&nlm_host_mutex);
2301da177e4SLinus Torvalds 	return host;
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds 
233c53c1bb9SOlaf Kirch /*
234*723bb5b5SChuck Lever  * Destroy an nlm_host and free associated resources
235*723bb5b5SChuck Lever  *
236*723bb5b5SChuck Lever  * Caller must hold nlm_host_mutex.
237c53c1bb9SOlaf Kirch  */
238*723bb5b5SChuck Lever static void nlm_destroy_host_locked(struct nlm_host *host)
239c53c1bb9SOlaf Kirch {
240c53c1bb9SOlaf Kirch 	struct rpc_clnt	*clnt;
241c53c1bb9SOlaf Kirch 
242*723bb5b5SChuck Lever 	dprintk("lockd: destroy host %s\n", host->h_name);
243*723bb5b5SChuck Lever 
244c53c1bb9SOlaf Kirch 	BUG_ON(!list_empty(&host->h_lockowners));
245c53c1bb9SOlaf Kirch 	BUG_ON(atomic_read(&host->h_count));
246c53c1bb9SOlaf Kirch 
247*723bb5b5SChuck Lever 	hlist_del_init(&host->h_hash);
248*723bb5b5SChuck Lever 
249c53c1bb9SOlaf Kirch 	nsm_unmonitor(host);
250c8c23c42SChuck Lever 	nsm_release(host->h_nsmhandle);
251c53c1bb9SOlaf Kirch 
25234f52e35STrond Myklebust 	clnt = host->h_rpcclnt;
25334f52e35STrond Myklebust 	if (clnt != NULL)
25434f52e35STrond Myklebust 		rpc_shutdown_client(clnt);
255c53c1bb9SOlaf Kirch 	kfree(host);
256*723bb5b5SChuck Lever 
257*723bb5b5SChuck Lever 	nrhosts--;
258c53c1bb9SOlaf Kirch }
259c53c1bb9SOlaf Kirch 
260d7d20440SChuck Lever /**
261d7d20440SChuck Lever  * nlmclnt_lookup_host - Find an NLM host handle matching a remote server
262d7d20440SChuck Lever  * @sap: network address of server
263d7d20440SChuck Lever  * @salen: length of server address
264d7d20440SChuck Lever  * @protocol: transport protocol to use
265d7d20440SChuck Lever  * @version: NLM protocol version
266d7d20440SChuck Lever  * @hostname: '\0'-terminated hostname of server
2670cb2659bSChuck Lever  * @noresvport: 1 if non-privileged port should be used
268d7d20440SChuck Lever  *
269d7d20440SChuck Lever  * Returns an nlm_host structure that matches the passed-in
270d7d20440SChuck Lever  * [server address, transport protocol, NLM version, server hostname].
271d7d20440SChuck Lever  * If one doesn't already exist in the host cache, a new handle is
272d7d20440SChuck Lever  * created and returned.
273c585646dSAdrian Bunk  */
274d7d20440SChuck Lever struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
275d7d20440SChuck Lever 				     const size_t salen,
276d7d20440SChuck Lever 				     const unsigned short protocol,
2770cb2659bSChuck Lever 				     const u32 version,
2780cb2659bSChuck Lever 				     const char *hostname,
2790cb2659bSChuck Lever 				     int noresvport)
280c585646dSAdrian Bunk {
2817f1ed18bSChuck Lever 	struct nlm_lookup_host_info ni = {
2827f1ed18bSChuck Lever 		.server		= 0,
283d7d20440SChuck Lever 		.sap		= sap,
284d7d20440SChuck Lever 		.salen		= salen,
285d7d20440SChuck Lever 		.protocol	= protocol,
2867f1ed18bSChuck Lever 		.version	= version,
2877f1ed18bSChuck Lever 		.hostname	= hostname,
288d7d20440SChuck Lever 		.hostname_len	= strlen(hostname),
2890cb2659bSChuck Lever 		.noresvport	= noresvport,
2907f1ed18bSChuck Lever 	};
291c98451bdSFrank van Maarseveen 
2927f1ed18bSChuck Lever 	dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
2937f1ed18bSChuck Lever 			(hostname ? hostname : "<none>"), version,
294d7d20440SChuck Lever 			(protocol == IPPROTO_UDP ? "udp" : "tcp"));
2957f1ed18bSChuck Lever 
2967f1ed18bSChuck Lever 	return nlm_lookup_host(&ni);
297c585646dSAdrian Bunk }
298c585646dSAdrian Bunk 
2996bfbe8afSChuck Lever /**
3006bfbe8afSChuck Lever  * nlmsvc_lookup_host - Find an NLM host handle matching a remote client
3016bfbe8afSChuck Lever  * @rqstp: incoming NLM request
3026bfbe8afSChuck Lever  * @hostname: name of client host
3036bfbe8afSChuck Lever  * @hostname_len: length of client hostname
3046bfbe8afSChuck Lever  *
3056bfbe8afSChuck Lever  * Returns an nlm_host structure that matches the [client address,
3066bfbe8afSChuck Lever  * transport protocol, NLM version, client hostname] of the passed-in
3076bfbe8afSChuck Lever  * NLM request.  If one doesn't already exist in the host cache, a
3086bfbe8afSChuck Lever  * new handle is created and returned.
3096bfbe8afSChuck Lever  *
3106bfbe8afSChuck Lever  * Before possibly creating a new nlm_host, construct a sockaddr
3116bfbe8afSChuck Lever  * for a specific source address in case the local system has
3126bfbe8afSChuck Lever  * multiple network addresses.  The family of the address in
3136bfbe8afSChuck Lever  * rq_daddr is guaranteed to be the same as the family of the
3146bfbe8afSChuck Lever  * address in rq_addr, so it's safe to use the same family for
3156bfbe8afSChuck Lever  * the source address.
316c585646dSAdrian Bunk  */
3176bfbe8afSChuck Lever struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
3186bfbe8afSChuck Lever 				    const char *hostname,
3196bfbe8afSChuck Lever 				    const size_t hostname_len)
320c585646dSAdrian Bunk {
3216bfbe8afSChuck Lever 	struct sockaddr_in sin = {
3222860a022SChuck Lever 		.sin_family	= AF_INET,
3236bfbe8afSChuck Lever 	};
3246bfbe8afSChuck Lever 	struct sockaddr_in6 sin6 = {
3256bfbe8afSChuck Lever 		.sin6_family	= AF_INET6,
3262860a022SChuck Lever 	};
3277f1ed18bSChuck Lever 	struct nlm_lookup_host_info ni = {
3287f1ed18bSChuck Lever 		.server		= 1,
32988541c84SChuck Lever 		.sap		= svc_addr(rqstp),
33088541c84SChuck Lever 		.salen		= rqstp->rq_addrlen,
3317f1ed18bSChuck Lever 		.protocol	= rqstp->rq_prot,
3327f1ed18bSChuck Lever 		.version	= rqstp->rq_vers,
3337f1ed18bSChuck Lever 		.hostname	= hostname,
3347f1ed18bSChuck Lever 		.hostname_len	= hostname_len,
3356bfbe8afSChuck Lever 		.src_len	= rqstp->rq_addrlen,
3367f1ed18bSChuck Lever 	};
337c98451bdSFrank van Maarseveen 
3387f1ed18bSChuck Lever 	dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__,
3397f1ed18bSChuck Lever 			(int)hostname_len, hostname, rqstp->rq_vers,
3407f1ed18bSChuck Lever 			(rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
3417f1ed18bSChuck Lever 
3426bfbe8afSChuck Lever 	switch (ni.sap->sa_family) {
3436bfbe8afSChuck Lever 	case AF_INET:
3446bfbe8afSChuck Lever 		sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr;
3456bfbe8afSChuck Lever 		ni.src_sap = (struct sockaddr *)&sin;
3466bfbe8afSChuck Lever 		break;
3476bfbe8afSChuck Lever 	case AF_INET6:
3486bfbe8afSChuck Lever 		ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6);
3496bfbe8afSChuck Lever 		ni.src_sap = (struct sockaddr *)&sin6;
3506bfbe8afSChuck Lever 		break;
3516bfbe8afSChuck Lever 	default:
3526bfbe8afSChuck Lever 		return NULL;
3536bfbe8afSChuck Lever 	}
3546bfbe8afSChuck Lever 
3557f1ed18bSChuck Lever 	return nlm_lookup_host(&ni);
356c585646dSAdrian Bunk }
357c585646dSAdrian Bunk 
358c585646dSAdrian Bunk /*
3591da177e4SLinus Torvalds  * Create the NLM RPC client for an NLM peer
3601da177e4SLinus Torvalds  */
3611da177e4SLinus Torvalds struct rpc_clnt *
3621da177e4SLinus Torvalds nlm_bind_host(struct nlm_host *host)
3631da177e4SLinus Torvalds {
3641da177e4SLinus Torvalds 	struct rpc_clnt	*clnt;
3651da177e4SLinus Torvalds 
3661df40b60SChuck Lever 	dprintk("lockd: nlm_bind_host %s (%s)\n",
3671df40b60SChuck Lever 			host->h_name, host->h_addrbuf);
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds 	/* Lock host handle */
37050467914STrond Myklebust 	mutex_lock(&host->h_mutex);
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 	/* If we've already created an RPC client, check whether
3731da177e4SLinus Torvalds 	 * RPC rebind is required
3741da177e4SLinus Torvalds 	 */
3751da177e4SLinus Torvalds 	if ((clnt = host->h_rpcclnt) != NULL) {
37643118c29SChuck Lever 		if (time_after_eq(jiffies, host->h_nextrebind)) {
37735f5a422SChuck Lever 			rpc_force_rebind(clnt);
3781da177e4SLinus Torvalds 			host->h_nextrebind = jiffies + NLM_HOST_REBIND;
3791b333c54SChuck Lever 			dprintk("lockd: next rebind in %lu jiffies\n",
3801da177e4SLinus Torvalds 					host->h_nextrebind - jiffies);
3811da177e4SLinus Torvalds 		}
3821da177e4SLinus Torvalds 	} else {
38321051ba6STrond Myklebust 		unsigned long increment = nlmsvc_timeout;
384e1ec7892SChuck Lever 		struct rpc_timeout timeparms = {
385e1ec7892SChuck Lever 			.to_initval	= increment,
386e1ec7892SChuck Lever 			.to_increment	= increment,
387e1ec7892SChuck Lever 			.to_maxval	= increment * 6UL,
388e1ec7892SChuck Lever 			.to_retries	= 5U,
389e1ec7892SChuck Lever 		};
390e1ec7892SChuck Lever 		struct rpc_create_args args = {
391c653ce3fSPavel Emelyanov 			.net		= &init_net,
392e1ec7892SChuck Lever 			.protocol	= host->h_proto,
393b4ed58fdSChuck Lever 			.address	= nlm_addr(host),
394b4ed58fdSChuck Lever 			.addrsize	= host->h_addrlen,
395e1ec7892SChuck Lever 			.timeout	= &timeparms,
396e1ec7892SChuck Lever 			.servername	= host->h_name,
397e1ec7892SChuck Lever 			.program	= &nlm_program,
398e1ec7892SChuck Lever 			.version	= host->h_version,
399e1ec7892SChuck Lever 			.authflavor	= RPC_AUTH_UNIX,
40090bd17c8SJeff Layton 			.flags		= (RPC_CLNT_CREATE_NOPING |
401e1ec7892SChuck Lever 					   RPC_CLNT_CREATE_AUTOBIND),
402e1ec7892SChuck Lever 		};
4031da177e4SLinus Torvalds 
40490bd17c8SJeff Layton 		/*
40590bd17c8SJeff Layton 		 * lockd retries server side blocks automatically so we want
40690bd17c8SJeff Layton 		 * those to be soft RPC calls. Client side calls need to be
40790bd17c8SJeff Layton 		 * hard RPC tasks.
40890bd17c8SJeff Layton 		 */
40990bd17c8SJeff Layton 		if (!host->h_server)
41090bd17c8SJeff Layton 			args.flags |= RPC_CLNT_CREATE_HARDRTRY;
4110cb2659bSChuck Lever 		if (host->h_noresvport)
4120cb2659bSChuck Lever 			args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
4138e35f8e7STrond Myklebust 		if (host->h_srcaddrlen)
4148e35f8e7STrond Myklebust 			args.saddress = nlm_srcaddr(host);
41590bd17c8SJeff Layton 
416e1ec7892SChuck Lever 		clnt = rpc_create(&args);
417e1ec7892SChuck Lever 		if (!IS_ERR(clnt))
4181da177e4SLinus Torvalds 			host->h_rpcclnt = clnt;
419e1ec7892SChuck Lever 		else {
420e1ec7892SChuck Lever 			printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
421e1ec7892SChuck Lever 			clnt = NULL;
422e1ec7892SChuck Lever 		}
4231da177e4SLinus Torvalds 	}
4241da177e4SLinus Torvalds 
42550467914STrond Myklebust 	mutex_unlock(&host->h_mutex);
4261da177e4SLinus Torvalds 	return clnt;
4271da177e4SLinus Torvalds }
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds /*
4301da177e4SLinus Torvalds  * Force a portmap lookup of the remote lockd port
4311da177e4SLinus Torvalds  */
4321da177e4SLinus Torvalds void
4331da177e4SLinus Torvalds nlm_rebind_host(struct nlm_host *host)
4341da177e4SLinus Torvalds {
4351da177e4SLinus Torvalds 	dprintk("lockd: rebind host %s\n", host->h_name);
4361da177e4SLinus Torvalds 	if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
43735f5a422SChuck Lever 		rpc_force_rebind(host->h_rpcclnt);
4381da177e4SLinus Torvalds 		host->h_nextrebind = jiffies + NLM_HOST_REBIND;
4391da177e4SLinus Torvalds 	}
4401da177e4SLinus Torvalds }
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds /*
4431da177e4SLinus Torvalds  * Increment NLM host count
4441da177e4SLinus Torvalds  */
4451da177e4SLinus Torvalds struct nlm_host * nlm_get_host(struct nlm_host *host)
4461da177e4SLinus Torvalds {
4471da177e4SLinus Torvalds 	if (host) {
4481da177e4SLinus Torvalds 		dprintk("lockd: get host %s\n", host->h_name);
4491da177e4SLinus Torvalds 		atomic_inc(&host->h_count);
4501da177e4SLinus Torvalds 		host->h_expires = jiffies + NLM_HOST_EXPIRE;
4511da177e4SLinus Torvalds 	}
4521da177e4SLinus Torvalds 	return host;
4531da177e4SLinus Torvalds }
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds /*
4561da177e4SLinus Torvalds  * Release NLM host after use
4571da177e4SLinus Torvalds  */
4581da177e4SLinus Torvalds void nlm_release_host(struct nlm_host *host)
4591da177e4SLinus Torvalds {
4601da177e4SLinus Torvalds 	if (host != NULL) {
4611da177e4SLinus Torvalds 		dprintk("lockd: release host %s\n", host->h_name);
4621da177e4SLinus Torvalds 		BUG_ON(atomic_read(&host->h_count) < 0);
4634c060b53STrond Myklebust 		if (atomic_dec_and_test(&host->h_count)) {
4644c060b53STrond Myklebust 			BUG_ON(!list_empty(&host->h_lockowners));
4654c060b53STrond Myklebust 			BUG_ON(!list_empty(&host->h_granted));
4664c060b53STrond Myklebust 			BUG_ON(!list_empty(&host->h_reclaim));
4674c060b53STrond Myklebust 		}
4681da177e4SLinus Torvalds 	}
4691da177e4SLinus Torvalds }
4701da177e4SLinus Torvalds 
471b10e30f6SJ. Bruce Fields static struct nlm_host *next_host_state(struct hlist_head *cache,
472b10e30f6SJ. Bruce Fields 					struct nsm_handle *nsm,
473b10e30f6SJ. Bruce Fields 					const struct nlm_reboot *info)
474b10e30f6SJ. Bruce Fields {
475b10e30f6SJ. Bruce Fields 	struct nlm_host *host = NULL;
476b10e30f6SJ. Bruce Fields 	struct hlist_head *chain;
477b10e30f6SJ. Bruce Fields 	struct hlist_node *pos;
478b10e30f6SJ. Bruce Fields 
479b10e30f6SJ. Bruce Fields 	mutex_lock(&nlm_host_mutex);
480b10e30f6SJ. Bruce Fields 	for_each_host(host, pos, chain, cache) {
481b10e30f6SJ. Bruce Fields 		if (host->h_nsmhandle == nsm
482b10e30f6SJ. Bruce Fields 		    && host->h_nsmstate != info->state) {
483b10e30f6SJ. Bruce Fields 			host->h_nsmstate = info->state;
484b10e30f6SJ. Bruce Fields 			host->h_state++;
485b10e30f6SJ. Bruce Fields 
486b10e30f6SJ. Bruce Fields 			nlm_get_host(host);
487b10e30f6SJ. Bruce Fields 			mutex_unlock(&nlm_host_mutex);
488b10e30f6SJ. Bruce Fields 			goto out;
489b10e30f6SJ. Bruce Fields 		}
490b10e30f6SJ. Bruce Fields 	}
491b10e30f6SJ. Bruce Fields out:
492b10e30f6SJ. Bruce Fields 	mutex_unlock(&nlm_host_mutex);
493b10e30f6SJ. Bruce Fields 	return host;
494b10e30f6SJ. Bruce Fields }
495b10e30f6SJ. Bruce Fields 
4967fefc9cbSChuck Lever /**
4977fefc9cbSChuck Lever  * nlm_host_rebooted - Release all resources held by rebooted host
4987fefc9cbSChuck Lever  * @info: pointer to decoded results of NLM_SM_NOTIFY call
4997fefc9cbSChuck Lever  *
5007fefc9cbSChuck Lever  * We were notified that the specified host has rebooted.  Release
5017fefc9cbSChuck Lever  * all resources held by that peer.
502cf712c24SOlaf Kirch  */
5037fefc9cbSChuck Lever void nlm_host_rebooted(const struct nlm_reboot *info)
504cf712c24SOlaf Kirch {
5055c8dd29cSOlaf Kirch 	struct nsm_handle *nsm;
5060cea3276SOlaf Kirch 	struct nlm_host	*host;
507cf712c24SOlaf Kirch 
5088c7378fdSChuck Lever 	nsm = nsm_reboot_lookup(info);
5098c7378fdSChuck Lever 	if (unlikely(nsm == NULL))
510cf712c24SOlaf Kirch 		return;
5115c8dd29cSOlaf Kirch 
5125c8dd29cSOlaf Kirch 	/* Mark all hosts tied to this NSM state as having rebooted.
5135c8dd29cSOlaf Kirch 	 * We run the loop repeatedly, because we drop the host table
5145c8dd29cSOlaf Kirch 	 * lock for this.
5155c8dd29cSOlaf Kirch 	 * To avoid processing a host several times, we match the nsmstate.
5165c8dd29cSOlaf Kirch 	 */
517b10e30f6SJ. Bruce Fields 	while ((host = next_host_state(nlm_hosts, nsm, info)) != NULL) {
5185c8dd29cSOlaf Kirch 		if (host->h_server) {
5195c8dd29cSOlaf Kirch 			/* We're server for this guy, just ditch
5205c8dd29cSOlaf Kirch 			 * all the locks he held. */
521cf712c24SOlaf Kirch 			nlmsvc_free_host_resources(host);
5225c8dd29cSOlaf Kirch 		} else {
5235c8dd29cSOlaf Kirch 			/* He's the server, initiate lock recovery. */
5245c8dd29cSOlaf Kirch 			nlmclnt_recovery(host);
525cf712c24SOlaf Kirch 		}
526cf712c24SOlaf Kirch 		nlm_release_host(host);
5275c8dd29cSOlaf Kirch 	}
528cdd30fa1SJeff Layton 	nsm_release(nsm);
529cf712c24SOlaf Kirch }
530cf712c24SOlaf Kirch 
531cf712c24SOlaf Kirch /*
5321da177e4SLinus Torvalds  * Shut down the hosts module.
5331da177e4SLinus Torvalds  * Note that this routine is called only at server shutdown time.
5341da177e4SLinus Torvalds  */
5351da177e4SLinus Torvalds void
5361da177e4SLinus Torvalds nlm_shutdown_hosts(void)
5371da177e4SLinus Torvalds {
5380cea3276SOlaf Kirch 	struct hlist_head *chain;
5390cea3276SOlaf Kirch 	struct hlist_node *pos;
5401da177e4SLinus Torvalds 	struct nlm_host	*host;
5411da177e4SLinus Torvalds 
5421da177e4SLinus Torvalds 	dprintk("lockd: shutting down host module\n");
543353ab6e9SIngo Molnar 	mutex_lock(&nlm_host_mutex);
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds 	/* First, make all hosts eligible for gc */
5461da177e4SLinus Torvalds 	dprintk("lockd: nuking all hosts...\n");
547b1137468SJ. Bruce Fields 	for_each_host(host, pos, chain, nlm_hosts) {
5481da177e4SLinus Torvalds 		host->h_expires = jiffies - 1;
549d801b861SJeff Layton 		if (host->h_rpcclnt) {
550d801b861SJeff Layton 			rpc_shutdown_client(host->h_rpcclnt);
551d801b861SJeff Layton 			host->h_rpcclnt = NULL;
552d801b861SJeff Layton 		}
553d801b861SJeff Layton 	}
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds 	/* Then, perform a garbage collection pass */
5561da177e4SLinus Torvalds 	nlm_gc_hosts();
557353ab6e9SIngo Molnar 	mutex_unlock(&nlm_host_mutex);
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds 	/* complain if any hosts are left */
5601da177e4SLinus Torvalds 	if (nrhosts) {
5611da177e4SLinus Torvalds 		printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
5621da177e4SLinus Torvalds 		dprintk("lockd: %d hosts left:\n", nrhosts);
563b1137468SJ. Bruce Fields 		for_each_host(host, pos, chain, nlm_hosts) {
5641da177e4SLinus Torvalds 			dprintk("       %s (cnt %d use %d exp %ld)\n",
5651da177e4SLinus Torvalds 				host->h_name, atomic_read(&host->h_count),
5661da177e4SLinus Torvalds 				host->h_inuse, host->h_expires);
5671da177e4SLinus Torvalds 		}
5681da177e4SLinus Torvalds 	}
5691da177e4SLinus Torvalds }
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds /*
5721da177e4SLinus Torvalds  * Garbage collect any unused NLM hosts.
5731da177e4SLinus Torvalds  * This GC combines reference counting for async operations with
5741da177e4SLinus Torvalds  * mark & sweep for resources held by remote clients.
5751da177e4SLinus Torvalds  */
5761da177e4SLinus Torvalds static void
5771da177e4SLinus Torvalds nlm_gc_hosts(void)
5781da177e4SLinus Torvalds {
5790cea3276SOlaf Kirch 	struct hlist_head *chain;
5800cea3276SOlaf Kirch 	struct hlist_node *pos, *next;
5810cea3276SOlaf Kirch 	struct nlm_host	*host;
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds 	dprintk("lockd: host garbage collection\n");
584b1137468SJ. Bruce Fields 	for_each_host(host, pos, chain, nlm_hosts)
5851da177e4SLinus Torvalds 		host->h_inuse = 0;
5861da177e4SLinus Torvalds 
5871da177e4SLinus Torvalds 	/* Mark all hosts that hold locks, blocks or shares */
5881da177e4SLinus Torvalds 	nlmsvc_mark_resources();
5891da177e4SLinus Torvalds 
590b1137468SJ. Bruce Fields 	for_each_host_safe(host, pos, next, chain, nlm_hosts) {
5911da177e4SLinus Torvalds 		if (atomic_read(&host->h_count) || host->h_inuse
5921da177e4SLinus Torvalds 		 || time_before(jiffies, host->h_expires)) {
593b1137468SJ. Bruce Fields 			dprintk("nlm_gc_hosts skipping %s "
594b1137468SJ. Bruce Fields 				"(cnt %d use %d exp %ld)\n",
5951da177e4SLinus Torvalds 				host->h_name, atomic_read(&host->h_count),
5961da177e4SLinus Torvalds 				host->h_inuse, host->h_expires);
5971da177e4SLinus Torvalds 			continue;
5981da177e4SLinus Torvalds 		}
599*723bb5b5SChuck Lever 		nlm_destroy_host_locked(host);
6001da177e4SLinus Torvalds 	}
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds 	next_gc = jiffies + NLM_HOST_COLLECT;
6031da177e4SLinus Torvalds }
604