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 {
120a7952f40SChuck Lever host = NULL;
1210ad95472SAndrey Ryabinin nsm = nsm_get_handle(ni->net, ni->sap, ni->salen,
122a7952f40SChuck Lever ni->hostname, ni->hostname_len);
123a7952f40SChuck Lever if (unlikely(nsm == NULL)) {
124a7952f40SChuck Lever dprintk("lockd: %s failed; no nsm handle\n",
125a7952f40SChuck Lever __func__);
126a7952f40SChuck Lever goto out;
127a7952f40SChuck Lever }
128a7952f40SChuck Lever }
129a7952f40SChuck Lever
130a7952f40SChuck Lever host = kmalloc(sizeof(*host), GFP_KERNEL);
131a7952f40SChuck Lever if (unlikely(host == NULL)) {
132a7952f40SChuck Lever dprintk("lockd: %s failed; no memory\n", __func__);
133a7952f40SChuck Lever nsm_release(nsm);
134a7952f40SChuck Lever goto out;
135a7952f40SChuck Lever }
136a7952f40SChuck Lever
137a7952f40SChuck Lever memcpy(nlm_addr(host), ni->sap, ni->salen);
138a7952f40SChuck Lever host->h_addrlen = ni->salen;
139a7952f40SChuck Lever rpc_set_port(nlm_addr(host), 0);
140a7952f40SChuck Lever host->h_srcaddrlen = 0;
141a7952f40SChuck Lever
142a7952f40SChuck Lever host->h_rpcclnt = NULL;
143a7952f40SChuck Lever host->h_name = nsm->sm_name;
144a7952f40SChuck Lever host->h_version = ni->version;
145a7952f40SChuck Lever host->h_proto = ni->protocol;
146a7952f40SChuck Lever host->h_reclaiming = 0;
147a7952f40SChuck Lever host->h_server = ni->server;
148a7952f40SChuck Lever host->h_noresvport = ni->noresvport;
149a7952f40SChuck Lever host->h_inuse = 0;
150a7952f40SChuck Lever init_waitqueue_head(&host->h_gracewait);
151a7952f40SChuck Lever init_rwsem(&host->h_rwsem);
152a7952f40SChuck Lever host->h_state = 0;
153a7952f40SChuck Lever host->h_nsmstate = 0;
154a7952f40SChuck Lever host->h_pidcount = 0;
155fee21fb5SElena Reshetova refcount_set(&host->h_count, 1);
156a7952f40SChuck Lever mutex_init(&host->h_mutex);
157a7952f40SChuck Lever host->h_nextrebind = now + NLM_HOST_REBIND;
158a7952f40SChuck Lever host->h_expires = now + NLM_HOST_EXPIRE;
159a7952f40SChuck Lever INIT_LIST_HEAD(&host->h_lockowners);
160a7952f40SChuck Lever spin_lock_init(&host->h_lock);
161a7952f40SChuck Lever INIT_LIST_HEAD(&host->h_granted);
162a7952f40SChuck Lever INIT_LIST_HEAD(&host->h_reclaim);
163a7952f40SChuck Lever host->h_nsmhandle = nsm;
164a7952f40SChuck Lever host->h_addrbuf = nsm->sm_addrbuf;
16566697bfdSStanislav Kinsbursky host->net = ni->net;
1663316fb80SZheng Yongjun host->h_cred = get_cred(ni->cred);
16797f8e625SWolfram Sang strscpy(host->nodename, utsname()->nodename, sizeof(host->nodename));
168a7952f40SChuck Lever
169a7952f40SChuck Lever out:
170a7952f40SChuck Lever return host;
171a7952f40SChuck Lever }
172a7952f40SChuck Lever
173a7952f40SChuck Lever /*
174723bb5b5SChuck Lever * Destroy an nlm_host and free associated resources
175723bb5b5SChuck Lever *
176723bb5b5SChuck Lever * Caller must hold nlm_host_mutex.
177c53c1bb9SOlaf Kirch */
nlm_destroy_host_locked(struct nlm_host * host)178723bb5b5SChuck Lever static void nlm_destroy_host_locked(struct nlm_host *host)
179c53c1bb9SOlaf Kirch {
180c53c1bb9SOlaf Kirch struct rpc_clnt *clnt;
181caa4e76bSStanislav Kinsbursky struct lockd_net *ln = net_generic(host->net, lockd_net_id);
182c53c1bb9SOlaf Kirch
183723bb5b5SChuck Lever dprintk("lockd: destroy host %s\n", host->h_name);
184723bb5b5SChuck Lever
185723bb5b5SChuck Lever hlist_del_init(&host->h_hash);
186723bb5b5SChuck Lever
187c53c1bb9SOlaf Kirch nsm_unmonitor(host);
188c8c23c42SChuck Lever nsm_release(host->h_nsmhandle);
189c53c1bb9SOlaf Kirch
19034f52e35STrond Myklebust clnt = host->h_rpcclnt;
19134f52e35STrond Myklebust if (clnt != NULL)
19234f52e35STrond Myklebust rpc_shutdown_client(clnt);
193b422df91STrond Myklebust put_cred(host->h_cred);
194c53c1bb9SOlaf Kirch kfree(host);
195723bb5b5SChuck Lever
196caa4e76bSStanislav Kinsbursky ln->nrhosts--;
197723bb5b5SChuck Lever nrhosts--;
198c53c1bb9SOlaf Kirch }
199c53c1bb9SOlaf Kirch
200d7d20440SChuck Lever /**
201d7d20440SChuck Lever * nlmclnt_lookup_host - Find an NLM host handle matching a remote server
202d7d20440SChuck Lever * @sap: network address of server
203d7d20440SChuck Lever * @salen: length of server address
204d7d20440SChuck Lever * @protocol: transport protocol to use
205d7d20440SChuck Lever * @version: NLM protocol version
206d7d20440SChuck Lever * @hostname: '\0'-terminated hostname of server
2070cb2659bSChuck Lever * @noresvport: 1 if non-privileged port should be used
208b422df91STrond Myklebust * @net: pointer to net namespace
209b422df91STrond Myklebust * @cred: pointer to cred
210d7d20440SChuck Lever *
211d7d20440SChuck Lever * Returns an nlm_host structure that matches the passed-in
212d7d20440SChuck Lever * [server address, transport protocol, NLM version, server hostname].
213d7d20440SChuck Lever * If one doesn't already exist in the host cache, a new handle is
214d7d20440SChuck Lever * created and returned.
215c585646dSAdrian 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)216d7d20440SChuck Lever struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
217d7d20440SChuck Lever const size_t salen,
218d7d20440SChuck Lever const unsigned short protocol,
2190cb2659bSChuck Lever const u32 version,
2200cb2659bSChuck Lever const char *hostname,
22166697bfdSStanislav Kinsbursky int noresvport,
222b422df91STrond Myklebust struct net *net,
223b422df91STrond Myklebust const struct cred *cred)
224c585646dSAdrian Bunk {
2257f1ed18bSChuck Lever struct nlm_lookup_host_info ni = {
2267f1ed18bSChuck Lever .server = 0,
227d7d20440SChuck Lever .sap = sap,
228d7d20440SChuck Lever .salen = salen,
229d7d20440SChuck Lever .protocol = protocol,
2307f1ed18bSChuck Lever .version = version,
2317f1ed18bSChuck Lever .hostname = hostname,
232d7d20440SChuck Lever .hostname_len = strlen(hostname),
2330cb2659bSChuck Lever .noresvport = noresvport,
23466697bfdSStanislav Kinsbursky .net = net,
235b422df91STrond Myklebust .cred = cred,
2367f1ed18bSChuck Lever };
2378ea6ecc8SChuck Lever struct hlist_head *chain;
2388ea6ecc8SChuck Lever struct nlm_host *host;
2398ea6ecc8SChuck Lever struct nsm_handle *nsm = NULL;
240caa4e76bSStanislav Kinsbursky struct lockd_net *ln = net_generic(net, lockd_net_id);
241c98451bdSFrank van Maarseveen
2427f1ed18bSChuck Lever dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
2437f1ed18bSChuck Lever (hostname ? hostname : "<none>"), version,
244d7d20440SChuck Lever (protocol == IPPROTO_UDP ? "udp" : "tcp"));
2457f1ed18bSChuck Lever
2468ea6ecc8SChuck Lever mutex_lock(&nlm_host_mutex);
2478ea6ecc8SChuck Lever
2488ea6ecc8SChuck Lever chain = &nlm_client_hosts[nlm_hash_address(sap)];
249b67bfe0dSSasha Levin hlist_for_each_entry(host, chain, h_hash) {
25066697bfdSStanislav Kinsbursky if (host->net != net)
25166697bfdSStanislav Kinsbursky continue;
2528ea6ecc8SChuck Lever if (!rpc_cmp_addr(nlm_addr(host), sap))
2538ea6ecc8SChuck Lever continue;
2548ea6ecc8SChuck Lever
2558ea6ecc8SChuck Lever /* Same address. Share an NSM handle if we already have one */
2568ea6ecc8SChuck Lever if (nsm == NULL)
2578ea6ecc8SChuck Lever nsm = host->h_nsmhandle;
2588ea6ecc8SChuck Lever
2598ea6ecc8SChuck Lever if (host->h_proto != protocol)
2608ea6ecc8SChuck Lever continue;
2618ea6ecc8SChuck Lever if (host->h_version != version)
2628ea6ecc8SChuck Lever continue;
2638ea6ecc8SChuck Lever
2648ea6ecc8SChuck Lever nlm_get_host(host);
2658ea6ecc8SChuck Lever dprintk("lockd: %s found host %s (%s)\n", __func__,
2668ea6ecc8SChuck Lever host->h_name, host->h_addrbuf);
2678ea6ecc8SChuck Lever goto out;
2688ea6ecc8SChuck Lever }
2698ea6ecc8SChuck Lever
2708ea6ecc8SChuck Lever host = nlm_alloc_host(&ni, nsm);
2718ea6ecc8SChuck Lever if (unlikely(host == NULL))
2728ea6ecc8SChuck Lever goto out;
2738ea6ecc8SChuck Lever
2748ea6ecc8SChuck Lever hlist_add_head(&host->h_hash, chain);
275caa4e76bSStanislav Kinsbursky ln->nrhosts++;
2768ea6ecc8SChuck Lever nrhosts++;
2778ea6ecc8SChuck Lever
2788ea6ecc8SChuck Lever dprintk("lockd: %s created host %s (%s)\n", __func__,
2798ea6ecc8SChuck Lever host->h_name, host->h_addrbuf);
2808ea6ecc8SChuck Lever
2818ea6ecc8SChuck Lever out:
2828ea6ecc8SChuck Lever mutex_unlock(&nlm_host_mutex);
2838ea6ecc8SChuck Lever return host;
2848ea6ecc8SChuck Lever }
2858ea6ecc8SChuck Lever
2868ea6ecc8SChuck Lever /**
2878ea6ecc8SChuck Lever * nlmclnt_release_host - release client nlm_host
2888ea6ecc8SChuck Lever * @host: nlm_host to release
2898ea6ecc8SChuck Lever *
2908ea6ecc8SChuck Lever */
nlmclnt_release_host(struct nlm_host * host)2918ea6ecc8SChuck Lever void nlmclnt_release_host(struct nlm_host *host)
2928ea6ecc8SChuck Lever {
2938ea6ecc8SChuck Lever if (host == NULL)
2948ea6ecc8SChuck Lever return;
2958ea6ecc8SChuck Lever
2968ea6ecc8SChuck Lever dprintk("lockd: release client host %s\n", host->h_name);
2978ea6ecc8SChuck Lever
298a2d30a54STrond Myklebust WARN_ON_ONCE(host->h_server);
2998ea6ecc8SChuck Lever
3004a9be28cSNeilBrown if (refcount_dec_and_mutex_lock(&host->h_count, &nlm_host_mutex)) {
301a2d30a54STrond Myklebust WARN_ON_ONCE(!list_empty(&host->h_lockowners));
302a2d30a54STrond Myklebust WARN_ON_ONCE(!list_empty(&host->h_granted));
303a2d30a54STrond Myklebust WARN_ON_ONCE(!list_empty(&host->h_reclaim));
3048ea6ecc8SChuck Lever
3058ea6ecc8SChuck Lever nlm_destroy_host_locked(host);
3068ea6ecc8SChuck Lever mutex_unlock(&nlm_host_mutex);
3078ea6ecc8SChuck Lever }
308c585646dSAdrian Bunk }
309c585646dSAdrian Bunk
3106bfbe8afSChuck Lever /**
3116bfbe8afSChuck Lever * nlmsvc_lookup_host - Find an NLM host handle matching a remote client
3126bfbe8afSChuck Lever * @rqstp: incoming NLM request
3136bfbe8afSChuck Lever * @hostname: name of client host
3146bfbe8afSChuck Lever * @hostname_len: length of client hostname
3156bfbe8afSChuck Lever *
3166bfbe8afSChuck Lever * Returns an nlm_host structure that matches the [client address,
3176bfbe8afSChuck Lever * transport protocol, NLM version, client hostname] of the passed-in
3186bfbe8afSChuck Lever * NLM request. If one doesn't already exist in the host cache, a
3196bfbe8afSChuck Lever * new handle is created and returned.
3206bfbe8afSChuck Lever *
3216bfbe8afSChuck Lever * Before possibly creating a new nlm_host, construct a sockaddr
3226bfbe8afSChuck Lever * for a specific source address in case the local system has
3236bfbe8afSChuck Lever * multiple network addresses. The family of the address in
3246bfbe8afSChuck Lever * rq_daddr is guaranteed to be the same as the family of the
3256bfbe8afSChuck Lever * address in rq_addr, so it's safe to use the same family for
3266bfbe8afSChuck Lever * the source address.
327c585646dSAdrian Bunk */
nlmsvc_lookup_host(const struct svc_rqst * rqstp,const char * hostname,const size_t hostname_len)3286bfbe8afSChuck Lever struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
3296bfbe8afSChuck Lever const char *hostname,
3306bfbe8afSChuck Lever const size_t hostname_len)
331c585646dSAdrian Bunk {
33267216b94SChuck Lever struct hlist_head *chain;
33367216b94SChuck Lever struct nlm_host *host = NULL;
33467216b94SChuck Lever struct nsm_handle *nsm = NULL;
335849a1cf1SMi Jinlong struct sockaddr *src_sap = svc_daddr(rqstp);
336849a1cf1SMi Jinlong size_t src_len = rqstp->rq_daddrlen;
3379695c705SStanislav Kinsbursky struct net *net = SVC_NET(rqstp);
3387f1ed18bSChuck Lever struct nlm_lookup_host_info ni = {
3397f1ed18bSChuck Lever .server = 1,
34088541c84SChuck Lever .sap = svc_addr(rqstp),
34188541c84SChuck Lever .salen = rqstp->rq_addrlen,
3427f1ed18bSChuck Lever .protocol = rqstp->rq_prot,
3437f1ed18bSChuck Lever .version = rqstp->rq_vers,
3447f1ed18bSChuck Lever .hostname = hostname,
3457f1ed18bSChuck Lever .hostname_len = hostname_len,
34666697bfdSStanislav Kinsbursky .net = net,
3477f1ed18bSChuck Lever };
3483cf7fb07SStanislav Kinsbursky struct lockd_net *ln = net_generic(net, lockd_net_id);
349c98451bdSFrank van Maarseveen
35093f38b6fSAmir Goldstein dprintk("lockd: %s(host='%.*s', vers=%u, proto=%s)\n", __func__,
3517f1ed18bSChuck Lever (int)hostname_len, hostname, rqstp->rq_vers,
3527f1ed18bSChuck Lever (rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
3537f1ed18bSChuck Lever
35467216b94SChuck Lever mutex_lock(&nlm_host_mutex);
35567216b94SChuck Lever
3563cf7fb07SStanislav Kinsbursky if (time_after_eq(jiffies, ln->next_gc))
35727adaddcSStanislav Kinsbursky nlm_gc_hosts(net);
35867216b94SChuck Lever
359d2df0484SChuck Lever chain = &nlm_server_hosts[nlm_hash_address(ni.sap)];
360b67bfe0dSSasha Levin hlist_for_each_entry(host, chain, h_hash) {
36166697bfdSStanislav Kinsbursky if (host->net != net)
36266697bfdSStanislav Kinsbursky continue;
36367216b94SChuck Lever if (!rpc_cmp_addr(nlm_addr(host), ni.sap))
36467216b94SChuck Lever continue;
36567216b94SChuck Lever
36667216b94SChuck Lever /* Same address. Share an NSM handle if we already have one */
36767216b94SChuck Lever if (nsm == NULL)
36867216b94SChuck Lever nsm = host->h_nsmhandle;
36967216b94SChuck Lever
37067216b94SChuck Lever if (host->h_proto != ni.protocol)
37167216b94SChuck Lever continue;
37267216b94SChuck Lever if (host->h_version != ni.version)
37367216b94SChuck Lever continue;
37479691836SChuck Lever if (!rpc_cmp_addr(nlm_srcaddr(host), src_sap))
37567216b94SChuck Lever continue;
37667216b94SChuck Lever
37767216b94SChuck Lever /* Move to head of hash chain. */
37867216b94SChuck Lever hlist_del(&host->h_hash);
37967216b94SChuck Lever hlist_add_head(&host->h_hash, chain);
38067216b94SChuck Lever
38167216b94SChuck Lever nlm_get_host(host);
38267216b94SChuck Lever dprintk("lockd: %s found host %s (%s)\n",
38367216b94SChuck Lever __func__, host->h_name, host->h_addrbuf);
38467216b94SChuck Lever goto out;
38567216b94SChuck Lever }
38667216b94SChuck Lever
38767216b94SChuck Lever host = nlm_alloc_host(&ni, nsm);
38867216b94SChuck Lever if (unlikely(host == NULL))
38967216b94SChuck Lever goto out;
39067216b94SChuck Lever
39179691836SChuck Lever memcpy(nlm_srcaddr(host), src_sap, src_len);
39279691836SChuck Lever host->h_srcaddrlen = src_len;
39367216b94SChuck Lever hlist_add_head(&host->h_hash, chain);
394caa4e76bSStanislav Kinsbursky ln->nrhosts++;
39567216b94SChuck Lever nrhosts++;
39667216b94SChuck Lever
397535cb8f3STrond Myklebust refcount_inc(&host->h_count);
398535cb8f3STrond Myklebust
39967216b94SChuck Lever dprintk("lockd: %s created host %s (%s)\n",
40067216b94SChuck Lever __func__, host->h_name, host->h_addrbuf);
40167216b94SChuck Lever
40267216b94SChuck Lever out:
40367216b94SChuck Lever mutex_unlock(&nlm_host_mutex);
40467216b94SChuck Lever return host;
40567216b94SChuck Lever }
40667216b94SChuck Lever
40767216b94SChuck Lever /**
40867216b94SChuck Lever * nlmsvc_release_host - release server nlm_host
40967216b94SChuck Lever * @host: nlm_host to release
41067216b94SChuck Lever *
41167216b94SChuck Lever * Host is destroyed later in nlm_gc_host().
41267216b94SChuck Lever */
nlmsvc_release_host(struct nlm_host * host)41367216b94SChuck Lever void nlmsvc_release_host(struct nlm_host *host)
41467216b94SChuck Lever {
41567216b94SChuck Lever if (host == NULL)
41667216b94SChuck Lever return;
41767216b94SChuck Lever
41867216b94SChuck Lever dprintk("lockd: release server host %s\n", host->h_name);
41967216b94SChuck Lever
420a2d30a54STrond Myklebust WARN_ON_ONCE(!host->h_server);
421fee21fb5SElena Reshetova refcount_dec(&host->h_count);
422c585646dSAdrian Bunk }
423c585646dSAdrian Bunk
424c585646dSAdrian Bunk /*
4251da177e4SLinus Torvalds * Create the NLM RPC client for an NLM peer
4261da177e4SLinus Torvalds */
4271da177e4SLinus Torvalds struct rpc_clnt *
nlm_bind_host(struct nlm_host * host)4281da177e4SLinus Torvalds nlm_bind_host(struct nlm_host *host)
4291da177e4SLinus Torvalds {
4301da177e4SLinus Torvalds struct rpc_clnt *clnt;
4311da177e4SLinus Torvalds
4321df40b60SChuck Lever dprintk("lockd: nlm_bind_host %s (%s)\n",
4331df40b60SChuck Lever host->h_name, host->h_addrbuf);
4341da177e4SLinus Torvalds
4351da177e4SLinus Torvalds /* Lock host handle */
43650467914STrond Myklebust mutex_lock(&host->h_mutex);
4371da177e4SLinus Torvalds
4381da177e4SLinus Torvalds /* If we've already created an RPC client, check whether
4391da177e4SLinus Torvalds * RPC rebind is required
4401da177e4SLinus Torvalds */
4411da177e4SLinus Torvalds if ((clnt = host->h_rpcclnt) != NULL) {
4429b82d88dSCalum Mackay nlm_rebind_host(host);
4431da177e4SLinus Torvalds } else {
44421051ba6STrond Myklebust unsigned long increment = nlmsvc_timeout;
445e1ec7892SChuck Lever struct rpc_timeout timeparms = {
446e1ec7892SChuck Lever .to_initval = increment,
447e1ec7892SChuck Lever .to_increment = increment,
448e1ec7892SChuck Lever .to_maxval = increment * 6UL,
449e1ec7892SChuck Lever .to_retries = 5U,
450e1ec7892SChuck Lever };
451e1ec7892SChuck Lever struct rpc_create_args args = {
45266697bfdSStanislav Kinsbursky .net = host->net,
453e1ec7892SChuck Lever .protocol = host->h_proto,
454b4ed58fdSChuck Lever .address = nlm_addr(host),
455b4ed58fdSChuck Lever .addrsize = host->h_addrlen,
456e1ec7892SChuck Lever .timeout = &timeparms,
457e1ec7892SChuck Lever .servername = host->h_name,
458e1ec7892SChuck Lever .program = &nlm_program,
459e1ec7892SChuck Lever .version = host->h_version,
460e1ec7892SChuck Lever .authflavor = RPC_AUTH_UNIX,
46190bd17c8SJeff Layton .flags = (RPC_CLNT_CREATE_NOPING |
462e6237b6fSTrond Myklebust RPC_CLNT_CREATE_AUTOBIND |
463e6237b6fSTrond Myklebust RPC_CLNT_CREATE_REUSEPORT),
464b422df91STrond Myklebust .cred = host->h_cred,
465e1ec7892SChuck Lever };
4661da177e4SLinus Torvalds
46790bd17c8SJeff Layton /*
46890bd17c8SJeff Layton * lockd retries server side blocks automatically so we want
46990bd17c8SJeff Layton * those to be soft RPC calls. Client side calls need to be
47090bd17c8SJeff Layton * hard RPC tasks.
47190bd17c8SJeff Layton */
47290bd17c8SJeff Layton if (!host->h_server)
47390bd17c8SJeff Layton args.flags |= RPC_CLNT_CREATE_HARDRTRY;
4740cb2659bSChuck Lever if (host->h_noresvport)
4750cb2659bSChuck Lever args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
4768e35f8e7STrond Myklebust if (host->h_srcaddrlen)
4778e35f8e7STrond Myklebust args.saddress = nlm_srcaddr(host);
47890bd17c8SJeff Layton
479e1ec7892SChuck Lever clnt = rpc_create(&args);
480e1ec7892SChuck Lever if (!IS_ERR(clnt))
4811da177e4SLinus Torvalds host->h_rpcclnt = clnt;
482e1ec7892SChuck Lever else {
483e1ec7892SChuck Lever printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
484e1ec7892SChuck Lever clnt = NULL;
485e1ec7892SChuck Lever }
4861da177e4SLinus Torvalds }
4871da177e4SLinus Torvalds
48850467914STrond Myklebust mutex_unlock(&host->h_mutex);
4891da177e4SLinus Torvalds return clnt;
4901da177e4SLinus Torvalds }
4911da177e4SLinus Torvalds
4929b82d88dSCalum Mackay /**
4939b82d88dSCalum Mackay * nlm_rebind_host - If needed, force a portmap lookup of the peer's lockd port
4949b82d88dSCalum Mackay * @host: NLM host handle for peer
4959b82d88dSCalum Mackay *
4969b82d88dSCalum Mackay * This is not needed when using a connection-oriented protocol, such as TCP.
4979b82d88dSCalum Mackay * The existing autobind mechanism is sufficient to force a rebind when
4989b82d88dSCalum Mackay * required, e.g. on connection state transitions.
4991da177e4SLinus Torvalds */
5001da177e4SLinus Torvalds void
nlm_rebind_host(struct nlm_host * host)5011da177e4SLinus Torvalds nlm_rebind_host(struct nlm_host *host)
5021da177e4SLinus Torvalds {
5039b82d88dSCalum Mackay if (host->h_proto != IPPROTO_UDP)
5049b82d88dSCalum Mackay return;
5059b82d88dSCalum Mackay
5061da177e4SLinus Torvalds if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
50735f5a422SChuck Lever rpc_force_rebind(host->h_rpcclnt);
5081da177e4SLinus Torvalds host->h_nextrebind = jiffies + NLM_HOST_REBIND;
5091da177e4SLinus Torvalds }
5101da177e4SLinus Torvalds }
5111da177e4SLinus Torvalds
5121da177e4SLinus Torvalds /*
5131da177e4SLinus Torvalds * Increment NLM host count
5141da177e4SLinus Torvalds */
nlm_get_host(struct nlm_host * host)5151da177e4SLinus Torvalds struct nlm_host * nlm_get_host(struct nlm_host *host)
5161da177e4SLinus Torvalds {
5171da177e4SLinus Torvalds if (host) {
5181da177e4SLinus Torvalds dprintk("lockd: get host %s\n", host->h_name);
519fee21fb5SElena Reshetova refcount_inc(&host->h_count);
5201da177e4SLinus Torvalds host->h_expires = jiffies + NLM_HOST_EXPIRE;
5211da177e4SLinus Torvalds }
5221da177e4SLinus Torvalds return host;
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds
next_host_state(struct hlist_head * cache,struct nsm_handle * nsm,const struct nlm_reboot * info)525b10e30f6SJ. Bruce Fields static struct nlm_host *next_host_state(struct hlist_head *cache,
526b10e30f6SJ. Bruce Fields struct nsm_handle *nsm,
527b10e30f6SJ. Bruce Fields const struct nlm_reboot *info)
528b10e30f6SJ. Bruce Fields {
52980c30e8dSChuck Lever struct nlm_host *host;
530b10e30f6SJ. Bruce Fields struct hlist_head *chain;
531b10e30f6SJ. Bruce Fields
532b10e30f6SJ. Bruce Fields mutex_lock(&nlm_host_mutex);
533b67bfe0dSSasha Levin for_each_host(host, chain, cache) {
534b10e30f6SJ. Bruce Fields if (host->h_nsmhandle == nsm
535b10e30f6SJ. Bruce Fields && host->h_nsmstate != info->state) {
536b10e30f6SJ. Bruce Fields host->h_nsmstate = info->state;
537b10e30f6SJ. Bruce Fields host->h_state++;
538b10e30f6SJ. Bruce Fields
539b10e30f6SJ. Bruce Fields nlm_get_host(host);
540b10e30f6SJ. Bruce Fields mutex_unlock(&nlm_host_mutex);
541b10e30f6SJ. Bruce Fields return host;
542b10e30f6SJ. Bruce Fields }
54380c30e8dSChuck Lever }
54480c30e8dSChuck Lever
54580c30e8dSChuck Lever mutex_unlock(&nlm_host_mutex);
54680c30e8dSChuck Lever return NULL;
54780c30e8dSChuck Lever }
548b10e30f6SJ. Bruce Fields
5497fefc9cbSChuck Lever /**
5507fefc9cbSChuck Lever * nlm_host_rebooted - Release all resources held by rebooted host
5510ad95472SAndrey Ryabinin * @net: network namespace
5527fefc9cbSChuck Lever * @info: pointer to decoded results of NLM_SM_NOTIFY call
5537fefc9cbSChuck Lever *
5547fefc9cbSChuck Lever * We were notified that the specified host has rebooted. Release
5557fefc9cbSChuck Lever * all resources held by that peer.
556cf712c24SOlaf Kirch */
nlm_host_rebooted(const struct net * net,const struct nlm_reboot * info)5570ad95472SAndrey Ryabinin void nlm_host_rebooted(const struct net *net, const struct nlm_reboot *info)
558cf712c24SOlaf Kirch {
5595c8dd29cSOlaf Kirch struct nsm_handle *nsm;
5600cea3276SOlaf Kirch struct nlm_host *host;
561cf712c24SOlaf Kirch
5620ad95472SAndrey Ryabinin nsm = nsm_reboot_lookup(net, info);
5638c7378fdSChuck Lever if (unlikely(nsm == NULL))
564cf712c24SOlaf Kirch return;
5655c8dd29cSOlaf Kirch
5665c8dd29cSOlaf Kirch /* Mark all hosts tied to this NSM state as having rebooted.
5675c8dd29cSOlaf Kirch * We run the loop repeatedly, because we drop the host table
5685c8dd29cSOlaf Kirch * lock for this.
5695c8dd29cSOlaf Kirch * To avoid processing a host several times, we match the nsmstate.
5705c8dd29cSOlaf Kirch */
571d2df0484SChuck Lever while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) {
572cf712c24SOlaf Kirch nlmsvc_free_host_resources(host);
57367216b94SChuck Lever nlmsvc_release_host(host);
5745c8dd29cSOlaf Kirch }
5758ea6ecc8SChuck Lever while ((host = next_host_state(nlm_client_hosts, nsm, info)) != NULL) {
5768ea6ecc8SChuck Lever nlmclnt_recovery(host);
5778ea6ecc8SChuck Lever nlmclnt_release_host(host);
5788ea6ecc8SChuck Lever }
5798ea6ecc8SChuck Lever
580cdd30fa1SJeff Layton nsm_release(nsm);
581cf712c24SOlaf Kirch }
582cf712c24SOlaf Kirch
nlm_complain_hosts(struct net * net)583d5850ff9SStanislav Kinsbursky static void nlm_complain_hosts(struct net *net)
584d5850ff9SStanislav Kinsbursky {
585d5850ff9SStanislav Kinsbursky struct hlist_head *chain;
586d5850ff9SStanislav Kinsbursky struct nlm_host *host;
587d5850ff9SStanislav Kinsbursky
588d5850ff9SStanislav Kinsbursky if (net) {
589d5850ff9SStanislav Kinsbursky struct lockd_net *ln = net_generic(net, lockd_net_id);
590d5850ff9SStanislav Kinsbursky
591d5850ff9SStanislav Kinsbursky if (ln->nrhosts == 0)
592d5850ff9SStanislav Kinsbursky return;
593e919b076SVasily Averin pr_warn("lockd: couldn't shutdown host module for net %x!\n",
594e919b076SVasily Averin net->ns.inum);
595e919b076SVasily Averin dprintk("lockd: %lu hosts left in net %x:\n", ln->nrhosts,
596e919b076SVasily Averin net->ns.inum);
597d5850ff9SStanislav Kinsbursky } else {
598d5850ff9SStanislav Kinsbursky if (nrhosts == 0)
599d5850ff9SStanislav Kinsbursky return;
600d5850ff9SStanislav Kinsbursky printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
601d5850ff9SStanislav Kinsbursky dprintk("lockd: %lu hosts left:\n", nrhosts);
602d5850ff9SStanislav Kinsbursky }
603d5850ff9SStanislav Kinsbursky
604b67bfe0dSSasha Levin for_each_host(host, chain, nlm_server_hosts) {
605d5850ff9SStanislav Kinsbursky if (net && host->net != net)
606d5850ff9SStanislav Kinsbursky continue;
607e919b076SVasily Averin dprintk(" %s (cnt %d use %d exp %ld net %x)\n",
608fee21fb5SElena Reshetova host->h_name, refcount_read(&host->h_count),
609e919b076SVasily Averin host->h_inuse, host->h_expires, host->net->ns.inum);
610d5850ff9SStanislav Kinsbursky }
611d5850ff9SStanislav Kinsbursky }
612d5850ff9SStanislav Kinsbursky
6131da177e4SLinus Torvalds void
nlm_shutdown_hosts_net(struct net * net)6143b64739fSStanislav Kinsbursky nlm_shutdown_hosts_net(struct net *net)
6151da177e4SLinus Torvalds {
6160cea3276SOlaf Kirch struct hlist_head *chain;
6171da177e4SLinus Torvalds struct nlm_host *host;
6181da177e4SLinus Torvalds
619353ab6e9SIngo Molnar mutex_lock(&nlm_host_mutex);
6201da177e4SLinus Torvalds
6211da177e4SLinus Torvalds /* First, make all hosts eligible for gc */
622e919b076SVasily Averin dprintk("lockd: nuking all hosts in net %x...\n",
623e919b076SVasily Averin net ? net->ns.inum : 0);
624b67bfe0dSSasha Levin for_each_host(host, chain, nlm_server_hosts) {
6253b64739fSStanislav Kinsbursky if (net && host->net != net)
6263b64739fSStanislav Kinsbursky continue;
6271da177e4SLinus Torvalds host->h_expires = jiffies - 1;
628d801b861SJeff Layton if (host->h_rpcclnt) {
629d801b861SJeff Layton rpc_shutdown_client(host->h_rpcclnt);
630d801b861SJeff Layton host->h_rpcclnt = NULL;
631d801b861SJeff Layton }
632*bfca7a6fSJeff Layton nlmsvc_free_host_resources(host);
633d801b861SJeff Layton }
6341da177e4SLinus Torvalds
6351da177e4SLinus Torvalds /* Then, perform a garbage collection pass */
63627adaddcSStanislav Kinsbursky nlm_gc_hosts(net);
637d5850ff9SStanislav Kinsbursky nlm_complain_hosts(net);
6389e137ed5SVasily Averin mutex_unlock(&nlm_host_mutex);
6393b64739fSStanislav Kinsbursky }
6403b64739fSStanislav Kinsbursky
6413b64739fSStanislav Kinsbursky /*
6423b64739fSStanislav Kinsbursky * Shut down the hosts module.
6433b64739fSStanislav Kinsbursky * Note that this routine is called only at server shutdown time.
6443b64739fSStanislav Kinsbursky */
6453b64739fSStanislav Kinsbursky void
nlm_shutdown_hosts(void)6463b64739fSStanislav Kinsbursky nlm_shutdown_hosts(void)
6473b64739fSStanislav Kinsbursky {
648e2edaa98SStanislav Kinsbursky dprintk("lockd: shutting down host module\n");
6493b64739fSStanislav Kinsbursky nlm_shutdown_hosts_net(NULL);
6501da177e4SLinus Torvalds }
6511da177e4SLinus Torvalds
6521da177e4SLinus Torvalds /*
6531da177e4SLinus Torvalds * Garbage collect any unused NLM hosts.
6541da177e4SLinus Torvalds * This GC combines reference counting for async operations with
6551da177e4SLinus Torvalds * mark & sweep for resources held by remote clients.
6561da177e4SLinus Torvalds */
6571da177e4SLinus Torvalds static void
nlm_gc_hosts(struct net * net)65827adaddcSStanislav Kinsbursky nlm_gc_hosts(struct net *net)
6591da177e4SLinus Torvalds {
6600cea3276SOlaf Kirch struct hlist_head *chain;
661b67bfe0dSSasha Levin struct hlist_node *next;
6620cea3276SOlaf Kirch struct nlm_host *host;
6631da177e4SLinus Torvalds
664e919b076SVasily Averin dprintk("lockd: host garbage collection for net %x\n",
665e919b076SVasily Averin net ? net->ns.inum : 0);
666b67bfe0dSSasha Levin for_each_host(host, chain, nlm_server_hosts) {
66727adaddcSStanislav Kinsbursky if (net && host->net != net)
66827adaddcSStanislav Kinsbursky continue;
6691da177e4SLinus Torvalds host->h_inuse = 0;
67027adaddcSStanislav Kinsbursky }
6711da177e4SLinus Torvalds
6721da177e4SLinus Torvalds /* Mark all hosts that hold locks, blocks or shares */
673b26411f8SStanislav Kinsbursky nlmsvc_mark_resources(net);
6741da177e4SLinus Torvalds
675b67bfe0dSSasha Levin for_each_host_safe(host, next, chain, nlm_server_hosts) {
67627adaddcSStanislav Kinsbursky if (net && host->net != net)
67727adaddcSStanislav Kinsbursky continue;
678535cb8f3STrond Myklebust if (host->h_inuse || time_before(jiffies, host->h_expires)) {
679b1137468SJ. Bruce Fields dprintk("nlm_gc_hosts skipping %s "
680e919b076SVasily Averin "(cnt %d use %d exp %ld net %x)\n",
681fee21fb5SElena Reshetova host->h_name, refcount_read(&host->h_count),
682e919b076SVasily Averin host->h_inuse, host->h_expires,
683e919b076SVasily Averin host->net->ns.inum);
6841da177e4SLinus Torvalds continue;
6851da177e4SLinus Torvalds }
686535cb8f3STrond Myklebust if (refcount_dec_if_one(&host->h_count))
687723bb5b5SChuck Lever nlm_destroy_host_locked(host);
6881da177e4SLinus Torvalds }
6891da177e4SLinus Torvalds
6903cf7fb07SStanislav Kinsbursky if (net) {
6913cf7fb07SStanislav Kinsbursky struct lockd_net *ln = net_generic(net, lockd_net_id);
6923cf7fb07SStanislav Kinsbursky
6933cf7fb07SStanislav Kinsbursky ln->next_gc = jiffies + NLM_HOST_COLLECT;
6943cf7fb07SStanislav Kinsbursky }
6951da177e4SLinus Torvalds }
696