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