1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/fs/lockd/host.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Management for NLM peer hosts. The nlm_host struct is shared
61da177e4SLinus Torvalds * between client and server implementation. The only reason to
71da177e4SLinus Torvalds * do so is to reduce code bloat.
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds #include <linux/types.h>
131da177e4SLinus Torvalds #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/in.h>
151b333c54SChuck Lever #include <linux/in6.h>
161da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
175976687aSJeff Layton #include <linux/sunrpc/addr.h>
181da177e4SLinus Torvalds #include <linux/sunrpc/svc.h>
191da177e4SLinus Torvalds #include <linux/lockd/lockd.h>
20353ab6e9SIngo Molnar #include <linux/mutex.h>
211da177e4SLinus Torvalds
2266697bfdSStanislav Kinsbursky #include <linux/sunrpc/svc_xprt.h>
2366697bfdSStanislav Kinsbursky
241b333c54SChuck Lever #include <net/ipv6.h>
251da177e4SLinus Torvalds
263cf7fb07SStanislav Kinsbursky #include "netns.h"
273cf7fb07SStanislav Kinsbursky
281da177e4SLinus Torvalds #define NLMDBG_FACILITY NLMDBG_HOSTCACHE
291da177e4SLinus Torvalds #define NLM_HOST_NRHASH 32
301da177e4SLinus Torvalds #define NLM_HOST_REBIND (60 * HZ)
311447d25eSNeilBrown #define NLM_HOST_EXPIRE (300 * HZ)
321447d25eSNeilBrown #define NLM_HOST_COLLECT (120 * HZ)
331da177e4SLinus Torvalds
34d2df0484SChuck Lever static struct hlist_head nlm_server_hosts[NLM_HOST_NRHASH];
358ea6ecc8SChuck Lever static struct hlist_head nlm_client_hosts[NLM_HOST_NRHASH];
36b1137468SJ. Bruce Fields
37b67bfe0dSSasha Levin #define for_each_host(host, chain, table) \
38b1137468SJ. Bruce Fields for ((chain) = (table); \
39b1137468SJ. Bruce Fields (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
40b67bfe0dSSasha Levin hlist_for_each_entry((host), (chain), h_hash)
41b1137468SJ. Bruce Fields
42b67bfe0dSSasha Levin #define for_each_host_safe(host, next, chain, table) \
43b1137468SJ. Bruce Fields for ((chain) = (table); \
44b1137468SJ. Bruce Fields (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
45b67bfe0dSSasha Levin hlist_for_each_entry_safe((host), (next), \
46b1137468SJ. Bruce Fields (chain), h_hash)
47b1137468SJ. Bruce Fields
48fcc072c7SChuck Lever static unsigned long nrhosts;
49353ab6e9SIngo Molnar static DEFINE_MUTEX(nlm_host_mutex);
501da177e4SLinus Torvalds
5127adaddcSStanislav Kinsbursky static void nlm_gc_hosts(struct net *net);
521da177e4SLinus Torvalds
537f1ed18bSChuck Lever struct nlm_lookup_host_info {
547f1ed18bSChuck Lever const int server; /* search for server|client */
5588541c84SChuck Lever const struct sockaddr *sap; /* address to search for */
5688541c84SChuck Lever const size_t salen; /* it's length */
577f1ed18bSChuck Lever const unsigned short protocol; /* transport to search for*/
587f1ed18bSChuck Lever const u32 version; /* NLM version to search for */
597f1ed18bSChuck Lever const char *hostname; /* remote's hostname */
607f1ed18bSChuck Lever const size_t hostname_len; /* it's length */
610cb2659bSChuck Lever const int noresvport; /* use non-priv port */
6266697bfdSStanislav Kinsbursky struct net *net; /* network namespace to bind */
63b422df91STrond Myklebust const struct cred *cred;
647f1ed18bSChuck Lever };
657f1ed18bSChuck Lever
66ede2fea0SChuck Lever /*
67ede2fea0SChuck Lever * Hash function must work well on big- and little-endian platforms
68ede2fea0SChuck Lever */
__nlm_hash32(const __be32 n)69ede2fea0SChuck Lever static unsigned int __nlm_hash32(const __be32 n)
70ede2fea0SChuck Lever {
71ede2fea0SChuck Lever unsigned int hash = (__force u32)n ^ ((__force u32)n >> 16);
72ede2fea0SChuck Lever return hash ^ (hash >> 8);
73ede2fea0SChuck Lever }
74ede2fea0SChuck Lever
__nlm_hash_addr4(const struct sockaddr * sap)75ede2fea0SChuck Lever static unsigned int __nlm_hash_addr4(const struct sockaddr *sap)
76ede2fea0SChuck Lever {
77ede2fea0SChuck Lever const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
78ede2fea0SChuck Lever return __nlm_hash32(sin->sin_addr.s_addr);
79ede2fea0SChuck Lever }
80ede2fea0SChuck Lever
__nlm_hash_addr6(const struct sockaddr * sap)81ede2fea0SChuck Lever static unsigned int __nlm_hash_addr6(const struct sockaddr *sap)
82ede2fea0SChuck Lever {
83ede2fea0SChuck Lever const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
84ede2fea0SChuck Lever const struct in6_addr addr = sin6->sin6_addr;
85ede2fea0SChuck Lever return __nlm_hash32(addr.s6_addr32[0]) ^
86ede2fea0SChuck Lever __nlm_hash32(addr.s6_addr32[1]) ^
87ede2fea0SChuck Lever __nlm_hash32(addr.s6_addr32[2]) ^
88ede2fea0SChuck Lever __nlm_hash32(addr.s6_addr32[3]);
89ede2fea0SChuck Lever }
90ede2fea0SChuck Lever
nlm_hash_address(const struct sockaddr * sap)91ede2fea0SChuck Lever static unsigned int nlm_hash_address(const struct sockaddr *sap)
92ede2fea0SChuck Lever {
93ede2fea0SChuck Lever unsigned int hash;
94ede2fea0SChuck Lever
95ede2fea0SChuck Lever switch (sap->sa_family) {
96ede2fea0SChuck Lever case AF_INET:
97ede2fea0SChuck Lever hash = __nlm_hash_addr4(sap);
98ede2fea0SChuck Lever break;
99ede2fea0SChuck Lever case AF_INET6:
100ede2fea0SChuck Lever hash = __nlm_hash_addr6(sap);
101ede2fea0SChuck Lever break;
102ede2fea0SChuck Lever default:
103ede2fea0SChuck Lever hash = 0;
104ede2fea0SChuck Lever }
105ede2fea0SChuck Lever return hash & (NLM_HOST_NRHASH - 1);
106ede2fea0SChuck Lever }
107ede2fea0SChuck Lever
1081da177e4SLinus Torvalds /*
109a7952f40SChuck Lever * Allocate and initialize an nlm_host. Common to both client and server.
110a7952f40SChuck Lever */
nlm_alloc_host(struct nlm_lookup_host_info * ni,struct nsm_handle * nsm)111a7952f40SChuck Lever static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
112a7952f40SChuck Lever struct nsm_handle *nsm)
113a7952f40SChuck Lever {
114a7952f40SChuck Lever struct nlm_host *host = NULL;
115a7952f40SChuck Lever unsigned long now = jiffies;
116a7952f40SChuck Lever
117a7952f40SChuck Lever if (nsm != NULL)
118c751082cSElena Reshetova refcount_inc(&nsm->sm_count);
119a7952f40SChuck Lever else {
1200ad95472SAndrey Ryabinin nsm = nsm_get_handle(ni->net, ni->sap, ni->salen,
121a7952f40SChuck Lever ni->hostname, ni->hostname_len);
122a7952f40SChuck Lever if (unlikely(nsm == NULL)) {
123a7952f40SChuck Lever dprintk("lockd: %s failed; no nsm handle\n",
124a7952f40SChuck Lever __func__);
125a7952f40SChuck Lever goto out;
126a7952f40SChuck Lever }
127a7952f40SChuck Lever }
128a7952f40SChuck Lever
129a7952f40SChuck Lever host = kmalloc(sizeof(*host), GFP_KERNEL);
130a7952f40SChuck Lever if (unlikely(host == NULL)) {
131a7952f40SChuck Lever dprintk("lockd: %s failed; no memory\n", __func__);
132a7952f40SChuck Lever nsm_release(nsm);
133a7952f40SChuck Lever goto out;
134a7952f40SChuck Lever }
135a7952f40SChuck Lever
136a7952f40SChuck Lever memcpy(nlm_addr(host), ni->sap, ni->salen);
137a7952f40SChuck Lever host->h_addrlen = ni->salen;
138a7952f40SChuck Lever rpc_set_port(nlm_addr(host), 0);
139a7952f40SChuck Lever host->h_srcaddrlen = 0;
140a7952f40SChuck Lever
141a7952f40SChuck Lever host->h_rpcclnt = NULL;
142a7952f40SChuck Lever host->h_name = nsm->sm_name;
143a7952f40SChuck Lever host->h_version = ni->version;
144a7952f40SChuck Lever host->h_proto = ni->protocol;
145a7952f40SChuck Lever host->h_reclaiming = 0;
146a7952f40SChuck Lever host->h_server = ni->server;
147a7952f40SChuck Lever host->h_noresvport = ni->noresvport;
148a7952f40SChuck Lever host->h_inuse = 0;
149a7952f40SChuck Lever init_waitqueue_head(&host->h_gracewait);
150a7952f40SChuck Lever init_rwsem(&host->h_rwsem);
151a7952f40SChuck Lever host->h_state = 0;
152a7952f40SChuck Lever host->h_nsmstate = 0;
153a7952f40SChuck Lever host->h_pidcount = 0;
154fee21fb5SElena Reshetova refcount_set(&host->h_count, 1);
155a7952f40SChuck Lever mutex_init(&host->h_mutex);
156a7952f40SChuck Lever host->h_nextrebind = now + NLM_HOST_REBIND;
157a7952f40SChuck Lever host->h_expires = now + NLM_HOST_EXPIRE;
158a7952f40SChuck Lever INIT_LIST_HEAD(&host->h_lockowners);
159a7952f40SChuck Lever spin_lock_init(&host->h_lock);
160a7952f40SChuck Lever INIT_LIST_HEAD(&host->h_granted);
161a7952f40SChuck Lever INIT_LIST_HEAD(&host->h_reclaim);
162a7952f40SChuck Lever host->h_nsmhandle = nsm;
163a7952f40SChuck Lever host->h_addrbuf = nsm->sm_addrbuf;
16466697bfdSStanislav Kinsbursky host->net = ni->net;
1653316fb80SZheng Yongjun host->h_cred = get_cred(ni->cred);
16697f8e625SWolfram Sang strscpy(host->nodename, utsname()->nodename, sizeof(host->nodename));
167a7952f40SChuck Lever
168a7952f40SChuck Lever out:
169a7952f40SChuck Lever return host;
170a7952f40SChuck Lever }
171a7952f40SChuck Lever
172a7952f40SChuck Lever /*
173723bb5b5SChuck Lever * Destroy an nlm_host and free associated resources
174723bb5b5SChuck Lever *
175723bb5b5SChuck Lever * Caller must hold nlm_host_mutex.
176c53c1bb9SOlaf Kirch */
nlm_destroy_host_locked(struct nlm_host * host)177723bb5b5SChuck Lever static void nlm_destroy_host_locked(struct nlm_host *host)
178c53c1bb9SOlaf Kirch {
179c53c1bb9SOlaf Kirch struct rpc_clnt *clnt;
180caa4e76bSStanislav Kinsbursky struct lockd_net *ln = net_generic(host->net, lockd_net_id);
181c53c1bb9SOlaf Kirch
182723bb5b5SChuck Lever dprintk("lockd: destroy host %s\n", host->h_name);
183723bb5b5SChuck Lever
184723bb5b5SChuck Lever hlist_del_init(&host->h_hash);
185723bb5b5SChuck Lever
186c53c1bb9SOlaf Kirch nsm_unmonitor(host);
187c8c23c42SChuck Lever nsm_release(host->h_nsmhandle);
188c53c1bb9SOlaf Kirch
18934f52e35STrond Myklebust clnt = host->h_rpcclnt;
19034f52e35STrond Myklebust if (clnt != NULL)
19134f52e35STrond Myklebust rpc_shutdown_client(clnt);
192b422df91STrond Myklebust put_cred(host->h_cred);
193c53c1bb9SOlaf Kirch kfree(host);
194723bb5b5SChuck Lever
195caa4e76bSStanislav Kinsbursky ln->nrhosts--;
196723bb5b5SChuck Lever nrhosts--;
197c53c1bb9SOlaf Kirch }
198c53c1bb9SOlaf Kirch
199d7d20440SChuck Lever /**
200d7d20440SChuck Lever * nlmclnt_lookup_host - Find an NLM host handle matching a remote server
201d7d20440SChuck Lever * @sap: network address of server
202d7d20440SChuck Lever * @salen: length of server address
203d7d20440SChuck Lever * @protocol: transport protocol to use
204d7d20440SChuck Lever * @version: NLM protocol version
205d7d20440SChuck Lever * @hostname: '\0'-terminated hostname of server
2060cb2659bSChuck Lever * @noresvport: 1 if non-privileged port should be used
207b422df91STrond Myklebust * @net: pointer to net namespace
208b422df91STrond Myklebust * @cred: pointer to cred
209d7d20440SChuck Lever *
210d7d20440SChuck Lever * Returns an nlm_host structure that matches the passed-in
211d7d20440SChuck Lever * [server address, transport protocol, NLM version, server hostname].
212d7d20440SChuck Lever * If one doesn't already exist in the host cache, a new handle is
213d7d20440SChuck Lever * created and returned.
214c585646dSAdrian Bunk */
nlmclnt_lookup_host(const struct sockaddr * sap,const size_t salen,const unsigned short protocol,const u32 version,const char * hostname,int noresvport,struct net * net,const struct cred * cred)215d7d20440SChuck Lever struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
216d7d20440SChuck Lever const size_t salen,
217d7d20440SChuck Lever const unsigned short protocol,
2180cb2659bSChuck Lever const u32 version,
2190cb2659bSChuck Lever const char *hostname,
22066697bfdSStanislav Kinsbursky int noresvport,
221b422df91STrond Myklebust struct net *net,
222b422df91STrond Myklebust const struct cred *cred)
223c585646dSAdrian Bunk {
2247f1ed18bSChuck Lever struct nlm_lookup_host_info ni = {
2257f1ed18bSChuck Lever .server = 0,
226d7d20440SChuck Lever .sap = sap,
227d7d20440SChuck Lever .salen = salen,
228d7d20440SChuck Lever .protocol = protocol,
2297f1ed18bSChuck Lever .version = version,
2307f1ed18bSChuck Lever .hostname = hostname,
231d7d20440SChuck Lever .hostname_len = strlen(hostname),
2320cb2659bSChuck Lever .noresvport = noresvport,
23366697bfdSStanislav Kinsbursky .net = net,
234b422df91STrond Myklebust .cred = cred,
2357f1ed18bSChuck Lever };
2368ea6ecc8SChuck Lever struct hlist_head *chain;
2378ea6ecc8SChuck Lever struct nlm_host *host;
2388ea6ecc8SChuck Lever struct nsm_handle *nsm = NULL;
239caa4e76bSStanislav Kinsbursky struct lockd_net *ln = net_generic(net, lockd_net_id);
240c98451bdSFrank van Maarseveen
2417f1ed18bSChuck Lever dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
2427f1ed18bSChuck Lever (hostname ? hostname : "<none>"), version,
243d7d20440SChuck Lever (protocol == IPPROTO_UDP ? "udp" : "tcp"));
2447f1ed18bSChuck Lever
2458ea6ecc8SChuck Lever mutex_lock(&nlm_host_mutex);
2468ea6ecc8SChuck Lever
2478ea6ecc8SChuck Lever chain = &nlm_client_hosts[nlm_hash_address(sap)];
248b67bfe0dSSasha Levin hlist_for_each_entry(host, chain, h_hash) {
24966697bfdSStanislav Kinsbursky if (host->net != net)
25066697bfdSStanislav Kinsbursky continue;
2518ea6ecc8SChuck Lever if (!rpc_cmp_addr(nlm_addr(host), sap))
2528ea6ecc8SChuck Lever continue;
2538ea6ecc8SChuck Lever
2548ea6ecc8SChuck Lever /* Same address. Share an NSM handle if we already have one */
2558ea6ecc8SChuck Lever if (nsm == NULL)
2568ea6ecc8SChuck Lever nsm = host->h_nsmhandle;
2578ea6ecc8SChuck Lever
2588ea6ecc8SChuck Lever if (host->h_proto != protocol)
2598ea6ecc8SChuck Lever continue;
2608ea6ecc8SChuck Lever if (host->h_version != version)
2618ea6ecc8SChuck Lever continue;
2628ea6ecc8SChuck Lever
2638ea6ecc8SChuck Lever nlm_get_host(host);
2648ea6ecc8SChuck Lever dprintk("lockd: %s found host %s (%s)\n", __func__,
2658ea6ecc8SChuck Lever host->h_name, host->h_addrbuf);
2668ea6ecc8SChuck Lever goto out;
2678ea6ecc8SChuck Lever }
2688ea6ecc8SChuck Lever
2698ea6ecc8SChuck Lever host = nlm_alloc_host(&ni, nsm);
2708ea6ecc8SChuck Lever if (unlikely(host == NULL))
2718ea6ecc8SChuck Lever goto out;
2728ea6ecc8SChuck Lever
2738ea6ecc8SChuck Lever hlist_add_head(&host->h_hash, chain);
274caa4e76bSStanislav Kinsbursky ln->nrhosts++;
2758ea6ecc8SChuck Lever nrhosts++;
2768ea6ecc8SChuck Lever
2778ea6ecc8SChuck Lever dprintk("lockd: %s created host %s (%s)\n", __func__,
2788ea6ecc8SChuck Lever host->h_name, host->h_addrbuf);
2798ea6ecc8SChuck Lever
2808ea6ecc8SChuck Lever out:
2818ea6ecc8SChuck Lever mutex_unlock(&nlm_host_mutex);
2828ea6ecc8SChuck Lever return host;
2838ea6ecc8SChuck Lever }
2848ea6ecc8SChuck Lever
2858ea6ecc8SChuck Lever /**
2868ea6ecc8SChuck Lever * nlmclnt_release_host - release client nlm_host
2878ea6ecc8SChuck Lever * @host: nlm_host to release
2888ea6ecc8SChuck Lever *
2898ea6ecc8SChuck Lever */
nlmclnt_release_host(struct nlm_host * host)2908ea6ecc8SChuck Lever void nlmclnt_release_host(struct nlm_host *host)
2918ea6ecc8SChuck Lever {
2928ea6ecc8SChuck Lever if (host == NULL)
2938ea6ecc8SChuck Lever return;
2948ea6ecc8SChuck Lever
2958ea6ecc8SChuck Lever dprintk("lockd: release client host %s\n", host->h_name);
2968ea6ecc8SChuck Lever
297a2d30a54STrond Myklebust WARN_ON_ONCE(host->h_server);
2988ea6ecc8SChuck Lever
2994a9be28cSNeilBrown if (refcount_dec_and_mutex_lock(&host->h_count, &nlm_host_mutex)) {
300a2d30a54STrond Myklebust WARN_ON_ONCE(!list_empty(&host->h_lockowners));
301a2d30a54STrond Myklebust WARN_ON_ONCE(!list_empty(&host->h_granted));
302a2d30a54STrond Myklebust WARN_ON_ONCE(!list_empty(&host->h_reclaim));
3038ea6ecc8SChuck Lever
3048ea6ecc8SChuck Lever nlm_destroy_host_locked(host);
3058ea6ecc8SChuck Lever mutex_unlock(&nlm_host_mutex);
3068ea6ecc8SChuck Lever }
307c585646dSAdrian Bunk }
308c585646dSAdrian Bunk
3096bfbe8afSChuck Lever /**
3106bfbe8afSChuck Lever * nlmsvc_lookup_host - Find an NLM host handle matching a remote client
3116bfbe8afSChuck Lever * @rqstp: incoming NLM request
3126bfbe8afSChuck Lever * @hostname: name of client host
3136bfbe8afSChuck Lever * @hostname_len: length of client hostname
3146bfbe8afSChuck Lever *
3156bfbe8afSChuck Lever * Returns an nlm_host structure that matches the [client address,
3166bfbe8afSChuck Lever * transport protocol, NLM version, client hostname] of the passed-in
3176bfbe8afSChuck Lever * NLM request. If one doesn't already exist in the host cache, a
3186bfbe8afSChuck Lever * new handle is created and returned.
3196bfbe8afSChuck Lever *
3206bfbe8afSChuck Lever * Before possibly creating a new nlm_host, construct a sockaddr
3216bfbe8afSChuck Lever * for a specific source address in case the local system has
3226bfbe8afSChuck Lever * multiple network addresses. The family of the address in
3236bfbe8afSChuck Lever * rq_daddr is guaranteed to be the same as the family of the
3246bfbe8afSChuck Lever * address in rq_addr, so it's safe to use the same family for
3256bfbe8afSChuck Lever * the source address.
326c585646dSAdrian Bunk */
nlmsvc_lookup_host(const struct svc_rqst * rqstp,const char * hostname,const size_t hostname_len)3276bfbe8afSChuck Lever struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
3286bfbe8afSChuck Lever const char *hostname,
3296bfbe8afSChuck Lever const size_t hostname_len)
330c585646dSAdrian Bunk {
33167216b94SChuck Lever struct hlist_head *chain;
33267216b94SChuck Lever struct nlm_host *host = NULL;
33367216b94SChuck Lever struct nsm_handle *nsm = NULL;
334849a1cf1SMi Jinlong struct sockaddr *src_sap = svc_daddr(rqstp);
335849a1cf1SMi Jinlong size_t src_len = rqstp->rq_daddrlen;
3369695c705SStanislav Kinsbursky struct net *net = SVC_NET(rqstp);
3377f1ed18bSChuck Lever struct nlm_lookup_host_info ni = {
3387f1ed18bSChuck Lever .server = 1,
33988541c84SChuck Lever .sap = svc_addr(rqstp),
34088541c84SChuck Lever .salen = rqstp->rq_addrlen,
3417f1ed18bSChuck Lever .protocol = rqstp->rq_prot,
3427f1ed18bSChuck Lever .version = rqstp->rq_vers,
3437f1ed18bSChuck Lever .hostname = hostname,
3447f1ed18bSChuck Lever .hostname_len = hostname_len,
34566697bfdSStanislav Kinsbursky .net = net,
3467f1ed18bSChuck Lever };
3473cf7fb07SStanislav Kinsbursky struct lockd_net *ln = net_generic(net, lockd_net_id);
348c98451bdSFrank van Maarseveen
34993f38b6fSAmir Goldstein dprintk("lockd: %s(host='%.*s', vers=%u, proto=%s)\n", __func__,
3507f1ed18bSChuck Lever (int)hostname_len, hostname, rqstp->rq_vers,
3517f1ed18bSChuck Lever (rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
3527f1ed18bSChuck Lever
35367216b94SChuck Lever mutex_lock(&nlm_host_mutex);
35467216b94SChuck Lever
3553cf7fb07SStanislav Kinsbursky if (time_after_eq(jiffies, ln->next_gc))
35627adaddcSStanislav Kinsbursky nlm_gc_hosts(net);
35767216b94SChuck Lever
358d2df0484SChuck Lever chain = &nlm_server_hosts[nlm_hash_address(ni.sap)];
359b67bfe0dSSasha Levin hlist_for_each_entry(host, chain, h_hash) {
36066697bfdSStanislav Kinsbursky if (host->net != net)
36166697bfdSStanislav Kinsbursky continue;
36267216b94SChuck Lever if (!rpc_cmp_addr(nlm_addr(host), ni.sap))
36367216b94SChuck Lever continue;
36467216b94SChuck Lever
36567216b94SChuck Lever /* Same address. Share an NSM handle if we already have one */
36667216b94SChuck Lever if (nsm == NULL)
36767216b94SChuck Lever nsm = host->h_nsmhandle;
36867216b94SChuck Lever
36967216b94SChuck Lever if (host->h_proto != ni.protocol)
37067216b94SChuck Lever continue;
37167216b94SChuck Lever if (host->h_version != ni.version)
37267216b94SChuck Lever continue;
37379691836SChuck Lever if (!rpc_cmp_addr(nlm_srcaddr(host), src_sap))
37467216b94SChuck Lever continue;
37567216b94SChuck Lever
37667216b94SChuck Lever /* Move to head of hash chain. */
37767216b94SChuck Lever hlist_del(&host->h_hash);
37867216b94SChuck Lever hlist_add_head(&host->h_hash, chain);
37967216b94SChuck Lever
38067216b94SChuck Lever nlm_get_host(host);
38167216b94SChuck Lever dprintk("lockd: %s found host %s (%s)\n",
38267216b94SChuck Lever __func__, host->h_name, host->h_addrbuf);
38367216b94SChuck Lever goto out;
38467216b94SChuck Lever }
38567216b94SChuck Lever
38667216b94SChuck Lever host = nlm_alloc_host(&ni, nsm);
38767216b94SChuck Lever if (unlikely(host == NULL))
38867216b94SChuck Lever goto out;
38967216b94SChuck Lever
39079691836SChuck Lever memcpy(nlm_srcaddr(host), src_sap, src_len);
39179691836SChuck Lever host->h_srcaddrlen = src_len;
39267216b94SChuck Lever hlist_add_head(&host->h_hash, chain);
393caa4e76bSStanislav Kinsbursky ln->nrhosts++;
39467216b94SChuck Lever nrhosts++;
39567216b94SChuck Lever
396535cb8f3STrond Myklebust refcount_inc(&host->h_count);
397535cb8f3STrond Myklebust
39867216b94SChuck Lever dprintk("lockd: %s created host %s (%s)\n",
39967216b94SChuck Lever __func__, host->h_name, host->h_addrbuf);
40067216b94SChuck Lever
40167216b94SChuck Lever out:
40267216b94SChuck Lever mutex_unlock(&nlm_host_mutex);
40367216b94SChuck Lever return host;
40467216b94SChuck Lever }
40567216b94SChuck Lever
40667216b94SChuck Lever /**
40767216b94SChuck Lever * nlmsvc_release_host - release server nlm_host
40867216b94SChuck Lever * @host: nlm_host to release
40967216b94SChuck Lever *
41067216b94SChuck Lever * Host is destroyed later in nlm_gc_host().
41167216b94SChuck Lever */
nlmsvc_release_host(struct nlm_host * host)41267216b94SChuck Lever void nlmsvc_release_host(struct nlm_host *host)
41367216b94SChuck Lever {
41467216b94SChuck Lever if (host == NULL)
41567216b94SChuck Lever return;
41667216b94SChuck Lever
41767216b94SChuck Lever dprintk("lockd: release server host %s\n", host->h_name);
41867216b94SChuck Lever
419a2d30a54STrond Myklebust WARN_ON_ONCE(!host->h_server);
420fee21fb5SElena Reshetova refcount_dec(&host->h_count);
421c585646dSAdrian Bunk }
422c585646dSAdrian Bunk
423c585646dSAdrian Bunk /*
4241da177e4SLinus Torvalds * Create the NLM RPC client for an NLM peer
4251da177e4SLinus Torvalds */
4261da177e4SLinus Torvalds struct rpc_clnt *
nlm_bind_host(struct nlm_host * host)4271da177e4SLinus Torvalds nlm_bind_host(struct nlm_host *host)
4281da177e4SLinus Torvalds {
4291da177e4SLinus Torvalds struct rpc_clnt *clnt;
4301da177e4SLinus Torvalds
4311df40b60SChuck Lever dprintk("lockd: nlm_bind_host %s (%s)\n",
4321df40b60SChuck Lever host->h_name, host->h_addrbuf);
4331da177e4SLinus Torvalds
4341da177e4SLinus Torvalds /* Lock host handle */
43550467914STrond Myklebust mutex_lock(&host->h_mutex);
4361da177e4SLinus Torvalds
4371da177e4SLinus Torvalds /* If we've already created an RPC client, check whether
4381da177e4SLinus Torvalds * RPC rebind is required
4391da177e4SLinus Torvalds */
4401da177e4SLinus Torvalds if ((clnt = host->h_rpcclnt) != NULL) {
4419b82d88dSCalum Mackay nlm_rebind_host(host);
4421da177e4SLinus Torvalds } else {
44321051ba6STrond Myklebust unsigned long increment = nlmsvc_timeout;
444e1ec7892SChuck Lever struct rpc_timeout timeparms = {
445e1ec7892SChuck Lever .to_initval = increment,
446e1ec7892SChuck Lever .to_increment = increment,
447e1ec7892SChuck Lever .to_maxval = increment * 6UL,
448e1ec7892SChuck Lever .to_retries = 5U,
449e1ec7892SChuck Lever };
450e1ec7892SChuck Lever struct rpc_create_args args = {
45166697bfdSStanislav Kinsbursky .net = host->net,
452e1ec7892SChuck Lever .protocol = host->h_proto,
453b4ed58fdSChuck Lever .address = nlm_addr(host),
454b4ed58fdSChuck Lever .addrsize = host->h_addrlen,
455e1ec7892SChuck Lever .timeout = &timeparms,
456e1ec7892SChuck Lever .servername = host->h_name,
457e1ec7892SChuck Lever .program = &nlm_program,
458e1ec7892SChuck Lever .version = host->h_version,
459e1ec7892SChuck Lever .authflavor = RPC_AUTH_UNIX,
46090bd17c8SJeff Layton .flags = (RPC_CLNT_CREATE_NOPING |
461e6237b6fSTrond Myklebust RPC_CLNT_CREATE_AUTOBIND |
462e6237b6fSTrond Myklebust RPC_CLNT_CREATE_REUSEPORT),
463b422df91STrond Myklebust .cred = host->h_cred,
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
4919b82d88dSCalum Mackay /**
4929b82d88dSCalum Mackay * nlm_rebind_host - If needed, force a portmap lookup of the peer's lockd port
4939b82d88dSCalum Mackay * @host: NLM host handle for peer
4949b82d88dSCalum Mackay *
4959b82d88dSCalum Mackay * This is not needed when using a connection-oriented protocol, such as TCP.
4969b82d88dSCalum Mackay * The existing autobind mechanism is sufficient to force a rebind when
4979b82d88dSCalum Mackay * required, e.g. on connection state transitions.
4981da177e4SLinus Torvalds */
4991da177e4SLinus Torvalds void
nlm_rebind_host(struct nlm_host * host)5001da177e4SLinus Torvalds nlm_rebind_host(struct nlm_host *host)
5011da177e4SLinus Torvalds {
5029b82d88dSCalum Mackay if (host->h_proto != IPPROTO_UDP)
5039b82d88dSCalum Mackay return;
5049b82d88dSCalum Mackay
5051da177e4SLinus Torvalds if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
50635f5a422SChuck Lever rpc_force_rebind(host->h_rpcclnt);
5071da177e4SLinus Torvalds host->h_nextrebind = jiffies + NLM_HOST_REBIND;
5081da177e4SLinus Torvalds }
5091da177e4SLinus Torvalds }
5101da177e4SLinus Torvalds
5111da177e4SLinus Torvalds /*
5121da177e4SLinus Torvalds * Increment NLM host count
5131da177e4SLinus Torvalds */
nlm_get_host(struct nlm_host * host)5141da177e4SLinus Torvalds struct nlm_host * nlm_get_host(struct nlm_host *host)
5151da177e4SLinus Torvalds {
5161da177e4SLinus Torvalds if (host) {
5171da177e4SLinus Torvalds dprintk("lockd: get host %s\n", host->h_name);
518fee21fb5SElena Reshetova refcount_inc(&host->h_count);
5191da177e4SLinus Torvalds host->h_expires = jiffies + NLM_HOST_EXPIRE;
5201da177e4SLinus Torvalds }
5211da177e4SLinus Torvalds return host;
5221da177e4SLinus Torvalds }
5231da177e4SLinus Torvalds
next_host_state(struct hlist_head * cache,struct nsm_handle * nsm,const struct nlm_reboot * info)524b10e30f6SJ. Bruce Fields static struct nlm_host *next_host_state(struct hlist_head *cache,
525b10e30f6SJ. Bruce Fields struct nsm_handle *nsm,
526b10e30f6SJ. Bruce Fields const struct nlm_reboot *info)
527b10e30f6SJ. Bruce Fields {
52880c30e8dSChuck Lever struct nlm_host *host;
529b10e30f6SJ. Bruce Fields struct hlist_head *chain;
530b10e30f6SJ. Bruce Fields
531b10e30f6SJ. Bruce Fields mutex_lock(&nlm_host_mutex);
532b67bfe0dSSasha Levin for_each_host(host, chain, cache) {
533b10e30f6SJ. Bruce Fields if (host->h_nsmhandle == nsm
534b10e30f6SJ. Bruce Fields && host->h_nsmstate != info->state) {
535b10e30f6SJ. Bruce Fields host->h_nsmstate = info->state;
536b10e30f6SJ. Bruce Fields host->h_state++;
537b10e30f6SJ. Bruce Fields
538b10e30f6SJ. Bruce Fields nlm_get_host(host);
539b10e30f6SJ. Bruce Fields mutex_unlock(&nlm_host_mutex);
540b10e30f6SJ. Bruce Fields return host;
541b10e30f6SJ. Bruce Fields }
54280c30e8dSChuck Lever }
54380c30e8dSChuck Lever
54480c30e8dSChuck Lever mutex_unlock(&nlm_host_mutex);
54580c30e8dSChuck Lever return NULL;
54680c30e8dSChuck Lever }
547b10e30f6SJ. Bruce Fields
5487fefc9cbSChuck Lever /**
5497fefc9cbSChuck Lever * nlm_host_rebooted - Release all resources held by rebooted host
5500ad95472SAndrey Ryabinin * @net: network namespace
5517fefc9cbSChuck Lever * @info: pointer to decoded results of NLM_SM_NOTIFY call
5527fefc9cbSChuck Lever *
5537fefc9cbSChuck Lever * We were notified that the specified host has rebooted. Release
5547fefc9cbSChuck Lever * all resources held by that peer.
555cf712c24SOlaf Kirch */
nlm_host_rebooted(const struct net * net,const struct nlm_reboot * info)5560ad95472SAndrey Ryabinin void nlm_host_rebooted(const struct net *net, const struct nlm_reboot *info)
557cf712c24SOlaf Kirch {
5585c8dd29cSOlaf Kirch struct nsm_handle *nsm;
5590cea3276SOlaf Kirch struct nlm_host *host;
560cf712c24SOlaf Kirch
5610ad95472SAndrey Ryabinin nsm = nsm_reboot_lookup(net, info);
5628c7378fdSChuck Lever if (unlikely(nsm == NULL))
563cf712c24SOlaf Kirch return;
5645c8dd29cSOlaf Kirch
5655c8dd29cSOlaf Kirch /* Mark all hosts tied to this NSM state as having rebooted.
5665c8dd29cSOlaf Kirch * We run the loop repeatedly, because we drop the host table
5675c8dd29cSOlaf Kirch * lock for this.
5685c8dd29cSOlaf Kirch * To avoid processing a host several times, we match the nsmstate.
5695c8dd29cSOlaf Kirch */
570d2df0484SChuck Lever while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) {
571cf712c24SOlaf Kirch nlmsvc_free_host_resources(host);
57267216b94SChuck Lever nlmsvc_release_host(host);
5735c8dd29cSOlaf Kirch }
5748ea6ecc8SChuck Lever while ((host = next_host_state(nlm_client_hosts, nsm, info)) != NULL) {
5758ea6ecc8SChuck Lever nlmclnt_recovery(host);
5768ea6ecc8SChuck Lever nlmclnt_release_host(host);
5778ea6ecc8SChuck Lever }
5788ea6ecc8SChuck Lever
579cdd30fa1SJeff Layton nsm_release(nsm);
580cf712c24SOlaf Kirch }
581cf712c24SOlaf Kirch
nlm_complain_hosts(struct net * net)582d5850ff9SStanislav Kinsbursky static void nlm_complain_hosts(struct net *net)
583d5850ff9SStanislav Kinsbursky {
584d5850ff9SStanislav Kinsbursky struct hlist_head *chain;
585d5850ff9SStanislav Kinsbursky struct nlm_host *host;
586d5850ff9SStanislav Kinsbursky
587d5850ff9SStanislav Kinsbursky if (net) {
588d5850ff9SStanislav Kinsbursky struct lockd_net *ln = net_generic(net, lockd_net_id);
589d5850ff9SStanislav Kinsbursky
590d5850ff9SStanislav Kinsbursky if (ln->nrhosts == 0)
591d5850ff9SStanislav Kinsbursky return;
592e919b076SVasily Averin pr_warn("lockd: couldn't shutdown host module for net %x!\n",
593e919b076SVasily Averin net->ns.inum);
594e919b076SVasily Averin dprintk("lockd: %lu hosts left in net %x:\n", ln->nrhosts,
595e919b076SVasily Averin net->ns.inum);
596d5850ff9SStanislav Kinsbursky } else {
597d5850ff9SStanislav Kinsbursky if (nrhosts == 0)
598d5850ff9SStanislav Kinsbursky return;
599d5850ff9SStanislav Kinsbursky printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
600d5850ff9SStanislav Kinsbursky dprintk("lockd: %lu hosts left:\n", nrhosts);
601d5850ff9SStanislav Kinsbursky }
602d5850ff9SStanislav Kinsbursky
603b67bfe0dSSasha Levin for_each_host(host, chain, nlm_server_hosts) {
604d5850ff9SStanislav Kinsbursky if (net && host->net != net)
605d5850ff9SStanislav Kinsbursky continue;
606e919b076SVasily Averin dprintk(" %s (cnt %d use %d exp %ld net %x)\n",
607fee21fb5SElena Reshetova host->h_name, refcount_read(&host->h_count),
608e919b076SVasily Averin host->h_inuse, host->h_expires, host->net->ns.inum);
609d5850ff9SStanislav Kinsbursky }
610d5850ff9SStanislav Kinsbursky }
611d5850ff9SStanislav Kinsbursky
6121da177e4SLinus Torvalds void
nlm_shutdown_hosts_net(struct net * net)6133b64739fSStanislav Kinsbursky nlm_shutdown_hosts_net(struct net *net)
6141da177e4SLinus Torvalds {
6150cea3276SOlaf Kirch struct hlist_head *chain;
6161da177e4SLinus Torvalds struct nlm_host *host;
6171da177e4SLinus Torvalds
618353ab6e9SIngo Molnar mutex_lock(&nlm_host_mutex);
6191da177e4SLinus Torvalds
6201da177e4SLinus Torvalds /* First, make all hosts eligible for gc */
621e919b076SVasily Averin dprintk("lockd: nuking all hosts in net %x...\n",
622e919b076SVasily Averin net ? net->ns.inum : 0);
623b67bfe0dSSasha Levin for_each_host(host, chain, nlm_server_hosts) {
6243b64739fSStanislav Kinsbursky if (net && host->net != net)
6253b64739fSStanislav Kinsbursky continue;
6261da177e4SLinus Torvalds host->h_expires = jiffies - 1;
627d801b861SJeff Layton if (host->h_rpcclnt) {
628d801b861SJeff Layton rpc_shutdown_client(host->h_rpcclnt);
629d801b861SJeff Layton host->h_rpcclnt = NULL;
630d801b861SJeff Layton }
631*bfca7a6fSJeff Layton nlmsvc_free_host_resources(host);
632d801b861SJeff Layton }
6331da177e4SLinus Torvalds
6341da177e4SLinus Torvalds /* Then, perform a garbage collection pass */
63527adaddcSStanislav Kinsbursky nlm_gc_hosts(net);
636d5850ff9SStanislav Kinsbursky nlm_complain_hosts(net);
6379e137ed5SVasily Averin mutex_unlock(&nlm_host_mutex);
6383b64739fSStanislav Kinsbursky }
6393b64739fSStanislav Kinsbursky
6403b64739fSStanislav Kinsbursky /*
6413b64739fSStanislav Kinsbursky * Shut down the hosts module.
6423b64739fSStanislav Kinsbursky * Note that this routine is called only at server shutdown time.
6433b64739fSStanislav Kinsbursky */
6443b64739fSStanislav Kinsbursky void
nlm_shutdown_hosts(void)6453b64739fSStanislav Kinsbursky nlm_shutdown_hosts(void)
6463b64739fSStanislav Kinsbursky {
647e2edaa98SStanislav Kinsbursky dprintk("lockd: shutting down host module\n");
6483b64739fSStanislav Kinsbursky nlm_shutdown_hosts_net(NULL);
6491da177e4SLinus Torvalds }
6501da177e4SLinus Torvalds
6511da177e4SLinus Torvalds /*
6521da177e4SLinus Torvalds * Garbage collect any unused NLM hosts.
6531da177e4SLinus Torvalds * This GC combines reference counting for async operations with
6541da177e4SLinus Torvalds * mark & sweep for resources held by remote clients.
6551da177e4SLinus Torvalds */
6561da177e4SLinus Torvalds static void
nlm_gc_hosts(struct net * net)65727adaddcSStanislav Kinsbursky nlm_gc_hosts(struct net *net)
6581da177e4SLinus Torvalds {
6590cea3276SOlaf Kirch struct hlist_head *chain;
660b67bfe0dSSasha Levin struct hlist_node *next;
6610cea3276SOlaf Kirch struct nlm_host *host;
6621da177e4SLinus Torvalds
663e919b076SVasily Averin dprintk("lockd: host garbage collection for net %x\n",
664e919b076SVasily Averin net ? net->ns.inum : 0);
665b67bfe0dSSasha Levin for_each_host(host, chain, nlm_server_hosts) {
66627adaddcSStanislav Kinsbursky if (net && host->net != net)
66727adaddcSStanislav Kinsbursky continue;
6681da177e4SLinus Torvalds host->h_inuse = 0;
66927adaddcSStanislav Kinsbursky }
6701da177e4SLinus Torvalds
6711da177e4SLinus Torvalds /* Mark all hosts that hold locks, blocks or shares */
672b26411f8SStanislav Kinsbursky nlmsvc_mark_resources(net);
6731da177e4SLinus Torvalds
674b67bfe0dSSasha Levin for_each_host_safe(host, next, chain, nlm_server_hosts) {
67527adaddcSStanislav Kinsbursky if (net && host->net != net)
67627adaddcSStanislav Kinsbursky continue;
677535cb8f3STrond Myklebust if (host->h_inuse || time_before(jiffies, host->h_expires)) {
678b1137468SJ. Bruce Fields dprintk("nlm_gc_hosts skipping %s "
679e919b076SVasily Averin "(cnt %d use %d exp %ld net %x)\n",
680fee21fb5SElena Reshetova host->h_name, refcount_read(&host->h_count),
681e919b076SVasily Averin host->h_inuse, host->h_expires,
682e919b076SVasily Averin host->net->ns.inum);
6831da177e4SLinus Torvalds continue;
6841da177e4SLinus Torvalds }
685535cb8f3STrond Myklebust if (refcount_dec_if_one(&host->h_count))
686723bb5b5SChuck Lever nlm_destroy_host_locked(host);
6871da177e4SLinus Torvalds }
6881da177e4SLinus Torvalds
6893cf7fb07SStanislav Kinsbursky if (net) {
6903cf7fb07SStanislav Kinsbursky struct lockd_net *ln = net_generic(net, lockd_net_id);
6913cf7fb07SStanislav Kinsbursky
6923cf7fb07SStanislav Kinsbursky ln->next_gc = jiffies + NLM_HOST_COLLECT;
6933cf7fb07SStanislav Kinsbursky }
6941da177e4SLinus Torvalds }
695