xref: /linux/fs/lockd/host.c (revision d5850ff9eaaa9bed0f0b56702db105e02ce4b709)
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 
2066697bfdSStanislav Kinsbursky #include <linux/sunrpc/svc_xprt.h>
2166697bfdSStanislav Kinsbursky 
221b333c54SChuck Lever #include <net/ipv6.h>
231da177e4SLinus Torvalds 
243cf7fb07SStanislav Kinsbursky #include "netns.h"
253cf7fb07SStanislav Kinsbursky 
261da177e4SLinus Torvalds #define NLMDBG_FACILITY		NLMDBG_HOSTCACHE
271da177e4SLinus Torvalds #define NLM_HOST_NRHASH		32
281da177e4SLinus Torvalds #define NLM_HOST_REBIND		(60 * HZ)
291447d25eSNeilBrown #define NLM_HOST_EXPIRE		(300 * HZ)
301447d25eSNeilBrown #define NLM_HOST_COLLECT	(120 * HZ)
311da177e4SLinus Torvalds 
32d2df0484SChuck Lever static struct hlist_head	nlm_server_hosts[NLM_HOST_NRHASH];
338ea6ecc8SChuck Lever static struct hlist_head	nlm_client_hosts[NLM_HOST_NRHASH];
34b1137468SJ. Bruce Fields 
35b1137468SJ. Bruce Fields #define for_each_host(host, pos, chain, table) \
36b1137468SJ. Bruce Fields 	for ((chain) = (table); \
37b1137468SJ. Bruce Fields 	     (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
38b1137468SJ. Bruce Fields 		hlist_for_each_entry((host), (pos), (chain), h_hash)
39b1137468SJ. Bruce Fields 
40b1137468SJ. Bruce Fields #define for_each_host_safe(host, pos, next, chain, table) \
41b1137468SJ. Bruce Fields 	for ((chain) = (table); \
42b1137468SJ. Bruce Fields 	     (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
43b1137468SJ. Bruce Fields 		hlist_for_each_entry_safe((host), (pos), (next), \
44b1137468SJ. Bruce Fields 						(chain), h_hash)
45b1137468SJ. Bruce Fields 
46fcc072c7SChuck Lever static unsigned long		nrhosts;
47353ab6e9SIngo Molnar static DEFINE_MUTEX(nlm_host_mutex);
481da177e4SLinus Torvalds 
4927adaddcSStanislav Kinsbursky static void			nlm_gc_hosts(struct net *net);
501da177e4SLinus Torvalds 
517f1ed18bSChuck Lever struct nlm_lookup_host_info {
527f1ed18bSChuck Lever 	const int		server;		/* search for server|client */
5388541c84SChuck Lever 	const struct sockaddr	*sap;		/* address to search for */
5488541c84SChuck Lever 	const size_t		salen;		/* it's length */
557f1ed18bSChuck Lever 	const unsigned short	protocol;	/* transport to search for*/
567f1ed18bSChuck Lever 	const u32		version;	/* NLM version to search for */
577f1ed18bSChuck Lever 	const char		*hostname;	/* remote's hostname */
587f1ed18bSChuck Lever 	const size_t		hostname_len;	/* it's length */
590cb2659bSChuck Lever 	const int		noresvport;	/* use non-priv port */
6066697bfdSStanislav Kinsbursky 	struct net		*net;		/* network namespace to bind */
617f1ed18bSChuck Lever };
627f1ed18bSChuck Lever 
63ede2fea0SChuck Lever /*
64ede2fea0SChuck Lever  * Hash function must work well on big- and little-endian platforms
65ede2fea0SChuck Lever  */
66ede2fea0SChuck Lever static unsigned int __nlm_hash32(const __be32 n)
67ede2fea0SChuck Lever {
68ede2fea0SChuck Lever 	unsigned int hash = (__force u32)n ^ ((__force u32)n >> 16);
69ede2fea0SChuck Lever 	return hash ^ (hash >> 8);
70ede2fea0SChuck Lever }
71ede2fea0SChuck Lever 
72ede2fea0SChuck Lever static unsigned int __nlm_hash_addr4(const struct sockaddr *sap)
73ede2fea0SChuck Lever {
74ede2fea0SChuck Lever 	const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
75ede2fea0SChuck Lever 	return __nlm_hash32(sin->sin_addr.s_addr);
76ede2fea0SChuck Lever }
77ede2fea0SChuck Lever 
78ede2fea0SChuck Lever static unsigned int __nlm_hash_addr6(const struct sockaddr *sap)
79ede2fea0SChuck Lever {
80ede2fea0SChuck Lever 	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
81ede2fea0SChuck Lever 	const struct in6_addr addr = sin6->sin6_addr;
82ede2fea0SChuck Lever 	return __nlm_hash32(addr.s6_addr32[0]) ^
83ede2fea0SChuck Lever 	       __nlm_hash32(addr.s6_addr32[1]) ^
84ede2fea0SChuck Lever 	       __nlm_hash32(addr.s6_addr32[2]) ^
85ede2fea0SChuck Lever 	       __nlm_hash32(addr.s6_addr32[3]);
86ede2fea0SChuck Lever }
87ede2fea0SChuck Lever 
88ede2fea0SChuck Lever static unsigned int nlm_hash_address(const struct sockaddr *sap)
89ede2fea0SChuck Lever {
90ede2fea0SChuck Lever 	unsigned int hash;
91ede2fea0SChuck Lever 
92ede2fea0SChuck Lever 	switch (sap->sa_family) {
93ede2fea0SChuck Lever 	case AF_INET:
94ede2fea0SChuck Lever 		hash = __nlm_hash_addr4(sap);
95ede2fea0SChuck Lever 		break;
96ede2fea0SChuck Lever 	case AF_INET6:
97ede2fea0SChuck Lever 		hash = __nlm_hash_addr6(sap);
98ede2fea0SChuck Lever 		break;
99ede2fea0SChuck Lever 	default:
100ede2fea0SChuck Lever 		hash = 0;
101ede2fea0SChuck Lever 	}
102ede2fea0SChuck Lever 	return hash & (NLM_HOST_NRHASH - 1);
103ede2fea0SChuck Lever }
104ede2fea0SChuck Lever 
1051da177e4SLinus Torvalds /*
106a7952f40SChuck Lever  * Allocate and initialize an nlm_host.  Common to both client and server.
107a7952f40SChuck Lever  */
108a7952f40SChuck Lever static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
109a7952f40SChuck Lever 				       struct nsm_handle *nsm)
110a7952f40SChuck Lever {
111a7952f40SChuck Lever 	struct nlm_host *host = NULL;
112a7952f40SChuck Lever 	unsigned long now = jiffies;
113a7952f40SChuck Lever 
114a7952f40SChuck Lever 	if (nsm != NULL)
115a7952f40SChuck Lever 		atomic_inc(&nsm->sm_count);
116a7952f40SChuck Lever 	else {
117a7952f40SChuck Lever 		host = NULL;
118a7952f40SChuck Lever 		nsm = nsm_get_handle(ni->sap, ni->salen,
119a7952f40SChuck Lever 					ni->hostname, ni->hostname_len);
120a7952f40SChuck Lever 		if (unlikely(nsm == NULL)) {
121a7952f40SChuck Lever 			dprintk("lockd: %s failed; no nsm handle\n",
122a7952f40SChuck Lever 				__func__);
123a7952f40SChuck Lever 			goto out;
124a7952f40SChuck Lever 		}
125a7952f40SChuck Lever 	}
126a7952f40SChuck Lever 
127a7952f40SChuck Lever 	host = kmalloc(sizeof(*host), GFP_KERNEL);
128a7952f40SChuck Lever 	if (unlikely(host == NULL)) {
129a7952f40SChuck Lever 		dprintk("lockd: %s failed; no memory\n", __func__);
130a7952f40SChuck Lever 		nsm_release(nsm);
131a7952f40SChuck Lever 		goto out;
132a7952f40SChuck Lever 	}
133a7952f40SChuck Lever 
134a7952f40SChuck Lever 	memcpy(nlm_addr(host), ni->sap, ni->salen);
135a7952f40SChuck Lever 	host->h_addrlen    = ni->salen;
136a7952f40SChuck Lever 	rpc_set_port(nlm_addr(host), 0);
137a7952f40SChuck Lever 	host->h_srcaddrlen = 0;
138a7952f40SChuck Lever 
139a7952f40SChuck Lever 	host->h_rpcclnt    = NULL;
140a7952f40SChuck Lever 	host->h_name	   = nsm->sm_name;
141a7952f40SChuck Lever 	host->h_version    = ni->version;
142a7952f40SChuck Lever 	host->h_proto      = ni->protocol;
143a7952f40SChuck Lever 	host->h_reclaiming = 0;
144a7952f40SChuck Lever 	host->h_server     = ni->server;
145a7952f40SChuck Lever 	host->h_noresvport = ni->noresvport;
146a7952f40SChuck Lever 	host->h_inuse      = 0;
147a7952f40SChuck Lever 	init_waitqueue_head(&host->h_gracewait);
148a7952f40SChuck Lever 	init_rwsem(&host->h_rwsem);
149a7952f40SChuck Lever 	host->h_state      = 0;
150a7952f40SChuck Lever 	host->h_nsmstate   = 0;
151a7952f40SChuck Lever 	host->h_pidcount   = 0;
152a7952f40SChuck Lever 	atomic_set(&host->h_count, 1);
153a7952f40SChuck Lever 	mutex_init(&host->h_mutex);
154a7952f40SChuck Lever 	host->h_nextrebind = now + NLM_HOST_REBIND;
155a7952f40SChuck Lever 	host->h_expires    = now + NLM_HOST_EXPIRE;
156a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_lockowners);
157a7952f40SChuck Lever 	spin_lock_init(&host->h_lock);
158a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_granted);
159a7952f40SChuck Lever 	INIT_LIST_HEAD(&host->h_reclaim);
160a7952f40SChuck Lever 	host->h_nsmhandle  = nsm;
161a7952f40SChuck Lever 	host->h_addrbuf    = nsm->sm_addrbuf;
16266697bfdSStanislav Kinsbursky 	host->net	   = ni->net;
163a7952f40SChuck Lever 
164a7952f40SChuck Lever out:
165a7952f40SChuck Lever 	return host;
166a7952f40SChuck Lever }
167a7952f40SChuck Lever 
168a7952f40SChuck Lever /*
169723bb5b5SChuck Lever  * Destroy an nlm_host and free associated resources
170723bb5b5SChuck Lever  *
171723bb5b5SChuck Lever  * Caller must hold nlm_host_mutex.
172c53c1bb9SOlaf Kirch  */
173723bb5b5SChuck Lever static void nlm_destroy_host_locked(struct nlm_host *host)
174c53c1bb9SOlaf Kirch {
175c53c1bb9SOlaf Kirch 	struct rpc_clnt	*clnt;
176caa4e76bSStanislav Kinsbursky 	struct lockd_net *ln = net_generic(host->net, lockd_net_id);
177c53c1bb9SOlaf Kirch 
178723bb5b5SChuck Lever 	dprintk("lockd: destroy host %s\n", host->h_name);
179723bb5b5SChuck Lever 
180c53c1bb9SOlaf Kirch 	BUG_ON(!list_empty(&host->h_lockowners));
181c53c1bb9SOlaf Kirch 	BUG_ON(atomic_read(&host->h_count));
182c53c1bb9SOlaf Kirch 
183723bb5b5SChuck Lever 	hlist_del_init(&host->h_hash);
184723bb5b5SChuck Lever 
185c53c1bb9SOlaf Kirch 	nsm_unmonitor(host);
186c8c23c42SChuck Lever 	nsm_release(host->h_nsmhandle);
187c53c1bb9SOlaf Kirch 
18834f52e35STrond Myklebust 	clnt = host->h_rpcclnt;
18934f52e35STrond Myklebust 	if (clnt != NULL)
19034f52e35STrond Myklebust 		rpc_shutdown_client(clnt);
191c53c1bb9SOlaf Kirch 	kfree(host);
192723bb5b5SChuck Lever 
193caa4e76bSStanislav Kinsbursky 	ln->nrhosts--;
194723bb5b5SChuck Lever 	nrhosts--;
195c53c1bb9SOlaf Kirch }
196c53c1bb9SOlaf Kirch 
197d7d20440SChuck Lever /**
198d7d20440SChuck Lever  * nlmclnt_lookup_host - Find an NLM host handle matching a remote server
199d7d20440SChuck Lever  * @sap: network address of server
200d7d20440SChuck Lever  * @salen: length of server address
201d7d20440SChuck Lever  * @protocol: transport protocol to use
202d7d20440SChuck Lever  * @version: NLM protocol version
203d7d20440SChuck Lever  * @hostname: '\0'-terminated hostname of server
2040cb2659bSChuck Lever  * @noresvport: 1 if non-privileged port should be used
205d7d20440SChuck Lever  *
206d7d20440SChuck Lever  * Returns an nlm_host structure that matches the passed-in
207d7d20440SChuck Lever  * [server address, transport protocol, NLM version, server hostname].
208d7d20440SChuck Lever  * If one doesn't already exist in the host cache, a new handle is
209d7d20440SChuck Lever  * created and returned.
210c585646dSAdrian Bunk  */
211d7d20440SChuck Lever struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
212d7d20440SChuck Lever 				     const size_t salen,
213d7d20440SChuck Lever 				     const unsigned short protocol,
2140cb2659bSChuck Lever 				     const u32 version,
2150cb2659bSChuck Lever 				     const char *hostname,
21666697bfdSStanislav Kinsbursky 				     int noresvport,
21766697bfdSStanislav Kinsbursky 				     struct net *net)
218c585646dSAdrian Bunk {
2197f1ed18bSChuck Lever 	struct nlm_lookup_host_info ni = {
2207f1ed18bSChuck Lever 		.server		= 0,
221d7d20440SChuck Lever 		.sap		= sap,
222d7d20440SChuck Lever 		.salen		= salen,
223d7d20440SChuck Lever 		.protocol	= protocol,
2247f1ed18bSChuck Lever 		.version	= version,
2257f1ed18bSChuck Lever 		.hostname	= hostname,
226d7d20440SChuck Lever 		.hostname_len	= strlen(hostname),
2270cb2659bSChuck Lever 		.noresvport	= noresvport,
22866697bfdSStanislav Kinsbursky 		.net		= net,
2297f1ed18bSChuck Lever 	};
2308ea6ecc8SChuck Lever 	struct hlist_head *chain;
2318ea6ecc8SChuck Lever 	struct hlist_node *pos;
2328ea6ecc8SChuck Lever 	struct nlm_host	*host;
2338ea6ecc8SChuck Lever 	struct nsm_handle *nsm = NULL;
234caa4e76bSStanislav Kinsbursky 	struct lockd_net *ln = net_generic(net, lockd_net_id);
235c98451bdSFrank van Maarseveen 
2367f1ed18bSChuck Lever 	dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
2377f1ed18bSChuck Lever 			(hostname ? hostname : "<none>"), version,
238d7d20440SChuck Lever 			(protocol == IPPROTO_UDP ? "udp" : "tcp"));
2397f1ed18bSChuck Lever 
2408ea6ecc8SChuck Lever 	mutex_lock(&nlm_host_mutex);
2418ea6ecc8SChuck Lever 
2428ea6ecc8SChuck Lever 	chain = &nlm_client_hosts[nlm_hash_address(sap)];
2438ea6ecc8SChuck Lever 	hlist_for_each_entry(host, pos, chain, h_hash) {
24466697bfdSStanislav Kinsbursky 		if (host->net != net)
24566697bfdSStanislav Kinsbursky 			continue;
2468ea6ecc8SChuck Lever 		if (!rpc_cmp_addr(nlm_addr(host), sap))
2478ea6ecc8SChuck Lever 			continue;
2488ea6ecc8SChuck Lever 
2498ea6ecc8SChuck Lever 		/* Same address. Share an NSM handle if we already have one */
2508ea6ecc8SChuck Lever 		if (nsm == NULL)
2518ea6ecc8SChuck Lever 			nsm = host->h_nsmhandle;
2528ea6ecc8SChuck Lever 
2538ea6ecc8SChuck Lever 		if (host->h_proto != protocol)
2548ea6ecc8SChuck Lever 			continue;
2558ea6ecc8SChuck Lever 		if (host->h_version != version)
2568ea6ecc8SChuck Lever 			continue;
2578ea6ecc8SChuck Lever 
2588ea6ecc8SChuck Lever 		nlm_get_host(host);
2598ea6ecc8SChuck Lever 		dprintk("lockd: %s found host %s (%s)\n", __func__,
2608ea6ecc8SChuck Lever 			host->h_name, host->h_addrbuf);
2618ea6ecc8SChuck Lever 		goto out;
2628ea6ecc8SChuck Lever 	}
2638ea6ecc8SChuck Lever 
2648ea6ecc8SChuck Lever 	host = nlm_alloc_host(&ni, nsm);
2658ea6ecc8SChuck Lever 	if (unlikely(host == NULL))
2668ea6ecc8SChuck Lever 		goto out;
2678ea6ecc8SChuck Lever 
2688ea6ecc8SChuck Lever 	hlist_add_head(&host->h_hash, chain);
269caa4e76bSStanislav Kinsbursky 	ln->nrhosts++;
2708ea6ecc8SChuck Lever 	nrhosts++;
2718ea6ecc8SChuck Lever 
2728ea6ecc8SChuck Lever 	dprintk("lockd: %s created host %s (%s)\n", __func__,
2738ea6ecc8SChuck Lever 		host->h_name, host->h_addrbuf);
2748ea6ecc8SChuck Lever 
2758ea6ecc8SChuck Lever out:
2768ea6ecc8SChuck Lever 	mutex_unlock(&nlm_host_mutex);
2778ea6ecc8SChuck Lever 	return host;
2788ea6ecc8SChuck Lever }
2798ea6ecc8SChuck Lever 
2808ea6ecc8SChuck Lever /**
2818ea6ecc8SChuck Lever  * nlmclnt_release_host - release client nlm_host
2828ea6ecc8SChuck Lever  * @host: nlm_host to release
2838ea6ecc8SChuck Lever  *
2848ea6ecc8SChuck Lever  */
2858ea6ecc8SChuck Lever void nlmclnt_release_host(struct nlm_host *host)
2868ea6ecc8SChuck Lever {
2878ea6ecc8SChuck Lever 	if (host == NULL)
2888ea6ecc8SChuck Lever 		return;
2898ea6ecc8SChuck Lever 
2908ea6ecc8SChuck Lever 	dprintk("lockd: release client host %s\n", host->h_name);
2918ea6ecc8SChuck Lever 
2928ea6ecc8SChuck Lever 	BUG_ON(atomic_read(&host->h_count) < 0);
2938ea6ecc8SChuck Lever 	BUG_ON(host->h_server);
2948ea6ecc8SChuck Lever 
2958ea6ecc8SChuck Lever 	if (atomic_dec_and_test(&host->h_count)) {
2968ea6ecc8SChuck Lever 		BUG_ON(!list_empty(&host->h_lockowners));
2978ea6ecc8SChuck Lever 		BUG_ON(!list_empty(&host->h_granted));
2988ea6ecc8SChuck Lever 		BUG_ON(!list_empty(&host->h_reclaim));
2998ea6ecc8SChuck Lever 
3008ea6ecc8SChuck Lever 		mutex_lock(&nlm_host_mutex);
3018ea6ecc8SChuck Lever 		nlm_destroy_host_locked(host);
3028ea6ecc8SChuck Lever 		mutex_unlock(&nlm_host_mutex);
3038ea6ecc8SChuck Lever 	}
304c585646dSAdrian Bunk }
305c585646dSAdrian Bunk 
3066bfbe8afSChuck Lever /**
3076bfbe8afSChuck Lever  * nlmsvc_lookup_host - Find an NLM host handle matching a remote client
3086bfbe8afSChuck Lever  * @rqstp: incoming NLM request
3096bfbe8afSChuck Lever  * @hostname: name of client host
3106bfbe8afSChuck Lever  * @hostname_len: length of client hostname
3116bfbe8afSChuck Lever  *
3126bfbe8afSChuck Lever  * Returns an nlm_host structure that matches the [client address,
3136bfbe8afSChuck Lever  * transport protocol, NLM version, client hostname] of the passed-in
3146bfbe8afSChuck Lever  * NLM request.  If one doesn't already exist in the host cache, a
3156bfbe8afSChuck Lever  * new handle is created and returned.
3166bfbe8afSChuck Lever  *
3176bfbe8afSChuck Lever  * Before possibly creating a new nlm_host, construct a sockaddr
3186bfbe8afSChuck Lever  * for a specific source address in case the local system has
3196bfbe8afSChuck Lever  * multiple network addresses.  The family of the address in
3206bfbe8afSChuck Lever  * rq_daddr is guaranteed to be the same as the family of the
3216bfbe8afSChuck Lever  * address in rq_addr, so it's safe to use the same family for
3226bfbe8afSChuck Lever  * the source address.
323c585646dSAdrian Bunk  */
3246bfbe8afSChuck Lever struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
3256bfbe8afSChuck Lever 				    const char *hostname,
3266bfbe8afSChuck Lever 				    const size_t hostname_len)
327c585646dSAdrian Bunk {
32867216b94SChuck Lever 	struct hlist_head *chain;
32967216b94SChuck Lever 	struct hlist_node *pos;
33067216b94SChuck Lever 	struct nlm_host	*host = NULL;
33167216b94SChuck Lever 	struct nsm_handle *nsm = NULL;
332849a1cf1SMi Jinlong 	struct sockaddr *src_sap = svc_daddr(rqstp);
333849a1cf1SMi Jinlong 	size_t src_len = rqstp->rq_daddrlen;
33466697bfdSStanislav Kinsbursky 	struct net *net = rqstp->rq_xprt->xpt_net;
3357f1ed18bSChuck Lever 	struct nlm_lookup_host_info ni = {
3367f1ed18bSChuck Lever 		.server		= 1,
33788541c84SChuck Lever 		.sap		= svc_addr(rqstp),
33888541c84SChuck Lever 		.salen		= rqstp->rq_addrlen,
3397f1ed18bSChuck Lever 		.protocol	= rqstp->rq_prot,
3407f1ed18bSChuck Lever 		.version	= rqstp->rq_vers,
3417f1ed18bSChuck Lever 		.hostname	= hostname,
3427f1ed18bSChuck Lever 		.hostname_len	= hostname_len,
34366697bfdSStanislav Kinsbursky 		.net		= net,
3447f1ed18bSChuck Lever 	};
3453cf7fb07SStanislav Kinsbursky 	struct lockd_net *ln = net_generic(net, lockd_net_id);
346c98451bdSFrank van Maarseveen 
3477f1ed18bSChuck Lever 	dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__,
3487f1ed18bSChuck Lever 			(int)hostname_len, hostname, rqstp->rq_vers,
3497f1ed18bSChuck Lever 			(rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
3507f1ed18bSChuck Lever 
35167216b94SChuck Lever 	mutex_lock(&nlm_host_mutex);
35267216b94SChuck Lever 
3533cf7fb07SStanislav Kinsbursky 	if (time_after_eq(jiffies, ln->next_gc))
35427adaddcSStanislav Kinsbursky 		nlm_gc_hosts(net);
35567216b94SChuck Lever 
356d2df0484SChuck Lever 	chain = &nlm_server_hosts[nlm_hash_address(ni.sap)];
35767216b94SChuck Lever 	hlist_for_each_entry(host, pos, chain, h_hash) {
35866697bfdSStanislav Kinsbursky 		if (host->net != net)
35966697bfdSStanislav Kinsbursky 			continue;
36067216b94SChuck Lever 		if (!rpc_cmp_addr(nlm_addr(host), ni.sap))
36167216b94SChuck Lever 			continue;
36267216b94SChuck Lever 
36367216b94SChuck Lever 		/* Same address. Share an NSM handle if we already have one */
36467216b94SChuck Lever 		if (nsm == NULL)
36567216b94SChuck Lever 			nsm = host->h_nsmhandle;
36667216b94SChuck Lever 
36767216b94SChuck Lever 		if (host->h_proto != ni.protocol)
36867216b94SChuck Lever 			continue;
36967216b94SChuck Lever 		if (host->h_version != ni.version)
37067216b94SChuck Lever 			continue;
37179691836SChuck Lever 		if (!rpc_cmp_addr(nlm_srcaddr(host), src_sap))
37267216b94SChuck Lever 			continue;
37367216b94SChuck Lever 
37467216b94SChuck Lever 		/* Move to head of hash chain. */
37567216b94SChuck Lever 		hlist_del(&host->h_hash);
37667216b94SChuck Lever 		hlist_add_head(&host->h_hash, chain);
37767216b94SChuck Lever 
37867216b94SChuck Lever 		nlm_get_host(host);
37967216b94SChuck Lever 		dprintk("lockd: %s found host %s (%s)\n",
38067216b94SChuck Lever 			__func__, host->h_name, host->h_addrbuf);
38167216b94SChuck Lever 		goto out;
38267216b94SChuck Lever 	}
38367216b94SChuck Lever 
38467216b94SChuck Lever 	host = nlm_alloc_host(&ni, nsm);
38567216b94SChuck Lever 	if (unlikely(host == NULL))
38667216b94SChuck Lever 		goto out;
38767216b94SChuck Lever 
38879691836SChuck Lever 	memcpy(nlm_srcaddr(host), src_sap, src_len);
38979691836SChuck Lever 	host->h_srcaddrlen = src_len;
39067216b94SChuck Lever 	hlist_add_head(&host->h_hash, chain);
391caa4e76bSStanislav Kinsbursky 	ln->nrhosts++;
39267216b94SChuck Lever 	nrhosts++;
39367216b94SChuck Lever 
39467216b94SChuck Lever 	dprintk("lockd: %s created host %s (%s)\n",
39567216b94SChuck Lever 		__func__, host->h_name, host->h_addrbuf);
39667216b94SChuck Lever 
39767216b94SChuck Lever out:
39867216b94SChuck Lever 	mutex_unlock(&nlm_host_mutex);
39967216b94SChuck Lever 	return host;
40067216b94SChuck Lever }
40167216b94SChuck Lever 
40267216b94SChuck Lever /**
40367216b94SChuck Lever  * nlmsvc_release_host - release server nlm_host
40467216b94SChuck Lever  * @host: nlm_host to release
40567216b94SChuck Lever  *
40667216b94SChuck Lever  * Host is destroyed later in nlm_gc_host().
40767216b94SChuck Lever  */
40867216b94SChuck Lever void nlmsvc_release_host(struct nlm_host *host)
40967216b94SChuck Lever {
41067216b94SChuck Lever 	if (host == NULL)
41167216b94SChuck Lever 		return;
41267216b94SChuck Lever 
41367216b94SChuck Lever 	dprintk("lockd: release server host %s\n", host->h_name);
41467216b94SChuck Lever 
41567216b94SChuck Lever 	BUG_ON(atomic_read(&host->h_count) < 0);
41667216b94SChuck Lever 	BUG_ON(!host->h_server);
41767216b94SChuck Lever 	atomic_dec(&host->h_count);
418c585646dSAdrian Bunk }
419c585646dSAdrian Bunk 
420c585646dSAdrian Bunk /*
4211da177e4SLinus Torvalds  * Create the NLM RPC client for an NLM peer
4221da177e4SLinus Torvalds  */
4231da177e4SLinus Torvalds struct rpc_clnt *
4241da177e4SLinus Torvalds nlm_bind_host(struct nlm_host *host)
4251da177e4SLinus Torvalds {
4261da177e4SLinus Torvalds 	struct rpc_clnt	*clnt;
4271da177e4SLinus Torvalds 
4281df40b60SChuck Lever 	dprintk("lockd: nlm_bind_host %s (%s)\n",
4291df40b60SChuck Lever 			host->h_name, host->h_addrbuf);
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	/* Lock host handle */
43250467914STrond Myklebust 	mutex_lock(&host->h_mutex);
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 	/* If we've already created an RPC client, check whether
4351da177e4SLinus Torvalds 	 * RPC rebind is required
4361da177e4SLinus Torvalds 	 */
4371da177e4SLinus Torvalds 	if ((clnt = host->h_rpcclnt) != NULL) {
43843118c29SChuck Lever 		if (time_after_eq(jiffies, host->h_nextrebind)) {
43935f5a422SChuck Lever 			rpc_force_rebind(clnt);
4401da177e4SLinus Torvalds 			host->h_nextrebind = jiffies + NLM_HOST_REBIND;
4411b333c54SChuck Lever 			dprintk("lockd: next rebind in %lu jiffies\n",
4421da177e4SLinus Torvalds 					host->h_nextrebind - jiffies);
4431da177e4SLinus Torvalds 		}
4441da177e4SLinus Torvalds 	} else {
44521051ba6STrond Myklebust 		unsigned long increment = nlmsvc_timeout;
446e1ec7892SChuck Lever 		struct rpc_timeout timeparms = {
447e1ec7892SChuck Lever 			.to_initval	= increment,
448e1ec7892SChuck Lever 			.to_increment	= increment,
449e1ec7892SChuck Lever 			.to_maxval	= increment * 6UL,
450e1ec7892SChuck Lever 			.to_retries	= 5U,
451e1ec7892SChuck Lever 		};
452e1ec7892SChuck Lever 		struct rpc_create_args args = {
45366697bfdSStanislav Kinsbursky 			.net		= host->net,
454e1ec7892SChuck Lever 			.protocol	= host->h_proto,
455b4ed58fdSChuck Lever 			.address	= nlm_addr(host),
456b4ed58fdSChuck Lever 			.addrsize	= host->h_addrlen,
457e1ec7892SChuck Lever 			.timeout	= &timeparms,
458e1ec7892SChuck Lever 			.servername	= host->h_name,
459e1ec7892SChuck Lever 			.program	= &nlm_program,
460e1ec7892SChuck Lever 			.version	= host->h_version,
461e1ec7892SChuck Lever 			.authflavor	= RPC_AUTH_UNIX,
46290bd17c8SJeff Layton 			.flags		= (RPC_CLNT_CREATE_NOPING |
463e1ec7892SChuck Lever 					   RPC_CLNT_CREATE_AUTOBIND),
464e1ec7892SChuck Lever 		};
4651da177e4SLinus Torvalds 
46690bd17c8SJeff Layton 		/*
46790bd17c8SJeff Layton 		 * lockd retries server side blocks automatically so we want
46890bd17c8SJeff Layton 		 * those to be soft RPC calls. Client side calls need to be
46990bd17c8SJeff Layton 		 * hard RPC tasks.
47090bd17c8SJeff Layton 		 */
47190bd17c8SJeff Layton 		if (!host->h_server)
47290bd17c8SJeff Layton 			args.flags |= RPC_CLNT_CREATE_HARDRTRY;
4730cb2659bSChuck Lever 		if (host->h_noresvport)
4740cb2659bSChuck Lever 			args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
4758e35f8e7STrond Myklebust 		if (host->h_srcaddrlen)
4768e35f8e7STrond Myklebust 			args.saddress = nlm_srcaddr(host);
47790bd17c8SJeff Layton 
478e1ec7892SChuck Lever 		clnt = rpc_create(&args);
479e1ec7892SChuck Lever 		if (!IS_ERR(clnt))
4801da177e4SLinus Torvalds 			host->h_rpcclnt = clnt;
481e1ec7892SChuck Lever 		else {
482e1ec7892SChuck Lever 			printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
483e1ec7892SChuck Lever 			clnt = NULL;
484e1ec7892SChuck Lever 		}
4851da177e4SLinus Torvalds 	}
4861da177e4SLinus Torvalds 
48750467914STrond Myklebust 	mutex_unlock(&host->h_mutex);
4881da177e4SLinus Torvalds 	return clnt;
4891da177e4SLinus Torvalds }
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds /*
4921da177e4SLinus Torvalds  * Force a portmap lookup of the remote lockd port
4931da177e4SLinus Torvalds  */
4941da177e4SLinus Torvalds void
4951da177e4SLinus Torvalds nlm_rebind_host(struct nlm_host *host)
4961da177e4SLinus Torvalds {
4971da177e4SLinus Torvalds 	dprintk("lockd: rebind host %s\n", host->h_name);
4981da177e4SLinus Torvalds 	if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
49935f5a422SChuck Lever 		rpc_force_rebind(host->h_rpcclnt);
5001da177e4SLinus Torvalds 		host->h_nextrebind = jiffies + NLM_HOST_REBIND;
5011da177e4SLinus Torvalds 	}
5021da177e4SLinus Torvalds }
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds /*
5051da177e4SLinus Torvalds  * Increment NLM host count
5061da177e4SLinus Torvalds  */
5071da177e4SLinus Torvalds struct nlm_host * nlm_get_host(struct nlm_host *host)
5081da177e4SLinus Torvalds {
5091da177e4SLinus Torvalds 	if (host) {
5101da177e4SLinus Torvalds 		dprintk("lockd: get host %s\n", host->h_name);
5111da177e4SLinus Torvalds 		atomic_inc(&host->h_count);
5121da177e4SLinus Torvalds 		host->h_expires = jiffies + NLM_HOST_EXPIRE;
5131da177e4SLinus Torvalds 	}
5141da177e4SLinus Torvalds 	return host;
5151da177e4SLinus Torvalds }
5161da177e4SLinus Torvalds 
517b10e30f6SJ. Bruce Fields static struct nlm_host *next_host_state(struct hlist_head *cache,
518b10e30f6SJ. Bruce Fields 					struct nsm_handle *nsm,
519b10e30f6SJ. Bruce Fields 					const struct nlm_reboot *info)
520b10e30f6SJ. Bruce Fields {
52180c30e8dSChuck Lever 	struct nlm_host *host;
522b10e30f6SJ. Bruce Fields 	struct hlist_head *chain;
523b10e30f6SJ. Bruce Fields 	struct hlist_node *pos;
524b10e30f6SJ. Bruce Fields 
525b10e30f6SJ. Bruce Fields 	mutex_lock(&nlm_host_mutex);
526b10e30f6SJ. Bruce Fields 	for_each_host(host, pos, chain, cache) {
527b10e30f6SJ. Bruce Fields 		if (host->h_nsmhandle == nsm
528b10e30f6SJ. Bruce Fields 		    && host->h_nsmstate != info->state) {
529b10e30f6SJ. Bruce Fields 			host->h_nsmstate = info->state;
530b10e30f6SJ. Bruce Fields 			host->h_state++;
531b10e30f6SJ. Bruce Fields 
532b10e30f6SJ. Bruce Fields 			nlm_get_host(host);
533b10e30f6SJ. Bruce Fields 			mutex_unlock(&nlm_host_mutex);
534b10e30f6SJ. Bruce Fields 			return host;
535b10e30f6SJ. Bruce Fields 		}
53680c30e8dSChuck Lever 	}
53780c30e8dSChuck Lever 
53880c30e8dSChuck Lever 	mutex_unlock(&nlm_host_mutex);
53980c30e8dSChuck Lever 	return NULL;
54080c30e8dSChuck Lever }
541b10e30f6SJ. Bruce Fields 
5427fefc9cbSChuck Lever /**
5437fefc9cbSChuck Lever  * nlm_host_rebooted - Release all resources held by rebooted host
5447fefc9cbSChuck Lever  * @info: pointer to decoded results of NLM_SM_NOTIFY call
5457fefc9cbSChuck Lever  *
5467fefc9cbSChuck Lever  * We were notified that the specified host has rebooted.  Release
5477fefc9cbSChuck Lever  * all resources held by that peer.
548cf712c24SOlaf Kirch  */
5497fefc9cbSChuck Lever void nlm_host_rebooted(const struct nlm_reboot *info)
550cf712c24SOlaf Kirch {
5515c8dd29cSOlaf Kirch 	struct nsm_handle *nsm;
5520cea3276SOlaf Kirch 	struct nlm_host	*host;
553cf712c24SOlaf Kirch 
5548c7378fdSChuck Lever 	nsm = nsm_reboot_lookup(info);
5558c7378fdSChuck Lever 	if (unlikely(nsm == NULL))
556cf712c24SOlaf Kirch 		return;
5575c8dd29cSOlaf Kirch 
5585c8dd29cSOlaf Kirch 	/* Mark all hosts tied to this NSM state as having rebooted.
5595c8dd29cSOlaf Kirch 	 * We run the loop repeatedly, because we drop the host table
5605c8dd29cSOlaf Kirch 	 * lock for this.
5615c8dd29cSOlaf Kirch 	 * To avoid processing a host several times, we match the nsmstate.
5625c8dd29cSOlaf Kirch 	 */
563d2df0484SChuck Lever 	while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) {
564cf712c24SOlaf Kirch 		nlmsvc_free_host_resources(host);
56567216b94SChuck Lever 		nlmsvc_release_host(host);
5665c8dd29cSOlaf Kirch 	}
5678ea6ecc8SChuck Lever 	while ((host = next_host_state(nlm_client_hosts, nsm, info)) != NULL) {
5688ea6ecc8SChuck Lever 		nlmclnt_recovery(host);
5698ea6ecc8SChuck Lever 		nlmclnt_release_host(host);
5708ea6ecc8SChuck Lever 	}
5718ea6ecc8SChuck Lever 
572cdd30fa1SJeff Layton 	nsm_release(nsm);
573cf712c24SOlaf Kirch }
574cf712c24SOlaf Kirch 
575*d5850ff9SStanislav Kinsbursky static void nlm_complain_hosts(struct net *net)
576*d5850ff9SStanislav Kinsbursky {
577*d5850ff9SStanislav Kinsbursky 	struct hlist_head *chain;
578*d5850ff9SStanislav Kinsbursky 	struct hlist_node *pos;
579*d5850ff9SStanislav Kinsbursky 	struct nlm_host	*host;
580*d5850ff9SStanislav Kinsbursky 
581*d5850ff9SStanislav Kinsbursky 	if (net) {
582*d5850ff9SStanislav Kinsbursky 		struct lockd_net *ln = net_generic(net, lockd_net_id);
583*d5850ff9SStanislav Kinsbursky 
584*d5850ff9SStanislav Kinsbursky 		if (ln->nrhosts == 0)
585*d5850ff9SStanislav Kinsbursky 			return;
586*d5850ff9SStanislav Kinsbursky 		printk(KERN_WARNING "lockd: couldn't shutdown host module for net %p!\n", net);
587*d5850ff9SStanislav Kinsbursky 		dprintk("lockd: %lu hosts left in net %p:\n", ln->nrhosts, net);
588*d5850ff9SStanislav Kinsbursky 	} else {
589*d5850ff9SStanislav Kinsbursky 		if (nrhosts == 0)
590*d5850ff9SStanislav Kinsbursky 			return;
591*d5850ff9SStanislav Kinsbursky 		printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
592*d5850ff9SStanislav Kinsbursky 		dprintk("lockd: %lu hosts left:\n", nrhosts);
593*d5850ff9SStanislav Kinsbursky 	}
594*d5850ff9SStanislav Kinsbursky 
595*d5850ff9SStanislav Kinsbursky 	for_each_host(host, pos, chain, nlm_server_hosts) {
596*d5850ff9SStanislav Kinsbursky 		if (net && host->net != net)
597*d5850ff9SStanislav Kinsbursky 			continue;
598*d5850ff9SStanislav Kinsbursky 		dprintk("       %s (cnt %d use %d exp %ld net %p)\n",
599*d5850ff9SStanislav Kinsbursky 			host->h_name, atomic_read(&host->h_count),
600*d5850ff9SStanislav Kinsbursky 			host->h_inuse, host->h_expires, host->net);
601*d5850ff9SStanislav Kinsbursky 	}
602*d5850ff9SStanislav Kinsbursky }
603*d5850ff9SStanislav Kinsbursky 
6041da177e4SLinus Torvalds void
6053b64739fSStanislav Kinsbursky nlm_shutdown_hosts_net(struct net *net)
6061da177e4SLinus Torvalds {
6070cea3276SOlaf Kirch 	struct hlist_head *chain;
6080cea3276SOlaf Kirch 	struct hlist_node *pos;
6091da177e4SLinus Torvalds 	struct nlm_host	*host;
6101da177e4SLinus Torvalds 
6111da177e4SLinus Torvalds 	dprintk("lockd: shutting down host module\n");
612353ab6e9SIngo Molnar 	mutex_lock(&nlm_host_mutex);
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	/* First, make all hosts eligible for gc */
6151da177e4SLinus Torvalds 	dprintk("lockd: nuking all hosts...\n");
616d2df0484SChuck Lever 	for_each_host(host, pos, chain, nlm_server_hosts) {
6173b64739fSStanislav Kinsbursky 		if (net && host->net != net)
6183b64739fSStanislav Kinsbursky 			continue;
6191da177e4SLinus Torvalds 		host->h_expires = jiffies - 1;
620d801b861SJeff Layton 		if (host->h_rpcclnt) {
621d801b861SJeff Layton 			rpc_shutdown_client(host->h_rpcclnt);
622d801b861SJeff Layton 			host->h_rpcclnt = NULL;
623d801b861SJeff Layton 		}
624d801b861SJeff Layton 	}
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds 	/* Then, perform a garbage collection pass */
62727adaddcSStanislav Kinsbursky 	nlm_gc_hosts(net);
628353ab6e9SIngo Molnar 	mutex_unlock(&nlm_host_mutex);
629caa4e76bSStanislav Kinsbursky 
630*d5850ff9SStanislav Kinsbursky 	nlm_complain_hosts(net);
6313b64739fSStanislav Kinsbursky }
6323b64739fSStanislav Kinsbursky 
6333b64739fSStanislav Kinsbursky /*
6343b64739fSStanislav Kinsbursky  * Shut down the hosts module.
6353b64739fSStanislav Kinsbursky  * Note that this routine is called only at server shutdown time.
6363b64739fSStanislav Kinsbursky  */
6373b64739fSStanislav Kinsbursky void
6383b64739fSStanislav Kinsbursky nlm_shutdown_hosts(void)
6393b64739fSStanislav Kinsbursky {
6403b64739fSStanislav Kinsbursky 	nlm_shutdown_hosts_net(NULL);
6411da177e4SLinus Torvalds }
6421da177e4SLinus Torvalds 
6431da177e4SLinus Torvalds /*
6441da177e4SLinus Torvalds  * Garbage collect any unused NLM hosts.
6451da177e4SLinus Torvalds  * This GC combines reference counting for async operations with
6461da177e4SLinus Torvalds  * mark & sweep for resources held by remote clients.
6471da177e4SLinus Torvalds  */
6481da177e4SLinus Torvalds static void
64927adaddcSStanislav Kinsbursky nlm_gc_hosts(struct net *net)
6501da177e4SLinus Torvalds {
6510cea3276SOlaf Kirch 	struct hlist_head *chain;
6520cea3276SOlaf Kirch 	struct hlist_node *pos, *next;
6530cea3276SOlaf Kirch 	struct nlm_host	*host;
6541da177e4SLinus Torvalds 
65527adaddcSStanislav Kinsbursky 	dprintk("lockd: host garbage collection for net %p\n", net);
65627adaddcSStanislav Kinsbursky 	for_each_host(host, pos, chain, nlm_server_hosts) {
65727adaddcSStanislav Kinsbursky 		if (net && host->net != net)
65827adaddcSStanislav Kinsbursky 			continue;
6591da177e4SLinus Torvalds 		host->h_inuse = 0;
66027adaddcSStanislav Kinsbursky 	}
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds 	/* Mark all hosts that hold locks, blocks or shares */
663b26411f8SStanislav Kinsbursky 	nlmsvc_mark_resources(net);
6641da177e4SLinus Torvalds 
665d2df0484SChuck Lever 	for_each_host_safe(host, pos, next, chain, nlm_server_hosts) {
66627adaddcSStanislav Kinsbursky 		if (net && host->net != net)
66727adaddcSStanislav Kinsbursky 			continue;
6681da177e4SLinus Torvalds 		if (atomic_read(&host->h_count) || host->h_inuse
6691da177e4SLinus Torvalds 		 || time_before(jiffies, host->h_expires)) {
670b1137468SJ. Bruce Fields 			dprintk("nlm_gc_hosts skipping %s "
67127adaddcSStanislav Kinsbursky 				"(cnt %d use %d exp %ld net %p)\n",
6721da177e4SLinus Torvalds 				host->h_name, atomic_read(&host->h_count),
67327adaddcSStanislav Kinsbursky 				host->h_inuse, host->h_expires, host->net);
6741da177e4SLinus Torvalds 			continue;
6751da177e4SLinus Torvalds 		}
676723bb5b5SChuck Lever 		nlm_destroy_host_locked(host);
6771da177e4SLinus Torvalds 	}
6781da177e4SLinus Torvalds 
6793cf7fb07SStanislav Kinsbursky 	if (net) {
6803cf7fb07SStanislav Kinsbursky 		struct lockd_net *ln = net_generic(net, lockd_net_id);
6813cf7fb07SStanislav Kinsbursky 
6823cf7fb07SStanislav Kinsbursky 		ln->next_gc = jiffies + NLM_HOST_COLLECT;
6833cf7fb07SStanislav Kinsbursky 	}
6841da177e4SLinus Torvalds }
685