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/sched.h> 131da177e4SLinus Torvalds #include <linux/slab.h> 141da177e4SLinus Torvalds #include <linux/in.h> 151da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h> 161da177e4SLinus Torvalds #include <linux/sunrpc/svc.h> 171da177e4SLinus Torvalds #include <linux/lockd/lockd.h> 181da177e4SLinus Torvalds #include <linux/lockd/sm_inter.h> 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds #define NLMDBG_FACILITY NLMDBG_HOSTCACHE 221da177e4SLinus Torvalds #define NLM_HOST_MAX 64 231da177e4SLinus Torvalds #define NLM_HOST_NRHASH 32 241da177e4SLinus Torvalds #define NLM_ADDRHASH(addr) (ntohl(addr) & (NLM_HOST_NRHASH-1)) 251da177e4SLinus Torvalds #define NLM_HOST_REBIND (60 * HZ) 261da177e4SLinus Torvalds #define NLM_HOST_EXPIRE ((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ) 271da177e4SLinus Torvalds #define NLM_HOST_COLLECT ((nrhosts > NLM_HOST_MAX)? 120 * HZ : 60 * HZ) 281da177e4SLinus Torvalds #define NLM_HOST_ADDR(sv) (&(sv)->s_nlmclnt->cl_xprt->addr) 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH]; 311da177e4SLinus Torvalds static unsigned long next_gc; 321da177e4SLinus Torvalds static int nrhosts; 331da177e4SLinus Torvalds static DECLARE_MUTEX(nlm_host_sema); 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds static void nlm_gc_hosts(void); 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds /* 391da177e4SLinus Torvalds * Find an NLM server handle in the cache. If there is none, create it. 401da177e4SLinus Torvalds */ 411da177e4SLinus Torvalds struct nlm_host * 421da177e4SLinus Torvalds nlmclnt_lookup_host(struct sockaddr_in *sin, int proto, int version) 431da177e4SLinus Torvalds { 441da177e4SLinus Torvalds return nlm_lookup_host(0, sin, proto, version); 451da177e4SLinus Torvalds } 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds /* 481da177e4SLinus Torvalds * Find an NLM client handle in the cache. If there is none, create it. 491da177e4SLinus Torvalds */ 501da177e4SLinus Torvalds struct nlm_host * 511da177e4SLinus Torvalds nlmsvc_lookup_host(struct svc_rqst *rqstp) 521da177e4SLinus Torvalds { 531da177e4SLinus Torvalds return nlm_lookup_host(1, &rqstp->rq_addr, 541da177e4SLinus Torvalds rqstp->rq_prot, rqstp->rq_vers); 551da177e4SLinus Torvalds } 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds /* 581da177e4SLinus Torvalds * Common host lookup routine for server & client 591da177e4SLinus Torvalds */ 601da177e4SLinus Torvalds struct nlm_host * 611da177e4SLinus Torvalds nlm_lookup_host(int server, struct sockaddr_in *sin, 621da177e4SLinus Torvalds int proto, int version) 631da177e4SLinus Torvalds { 641da177e4SLinus Torvalds struct nlm_host *host, **hp; 651da177e4SLinus Torvalds u32 addr; 661da177e4SLinus Torvalds int hash; 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds dprintk("lockd: nlm_lookup_host(%08x, p=%d, v=%d)\n", 691da177e4SLinus Torvalds (unsigned)(sin? ntohl(sin->sin_addr.s_addr) : 0), proto, version); 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds hash = NLM_ADDRHASH(sin->sin_addr.s_addr); 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds /* Lock hash table */ 741da177e4SLinus Torvalds down(&nlm_host_sema); 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds if (time_after_eq(jiffies, next_gc)) 771da177e4SLinus Torvalds nlm_gc_hosts(); 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) { 801da177e4SLinus Torvalds if (host->h_proto != proto) 811da177e4SLinus Torvalds continue; 821da177e4SLinus Torvalds if (host->h_version != version) 831da177e4SLinus Torvalds continue; 841da177e4SLinus Torvalds if (host->h_server != server) 851da177e4SLinus Torvalds continue; 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds if (nlm_cmp_addr(&host->h_addr, sin)) { 881da177e4SLinus Torvalds if (hp != nlm_hosts + hash) { 891da177e4SLinus Torvalds *hp = host->h_next; 901da177e4SLinus Torvalds host->h_next = nlm_hosts[hash]; 911da177e4SLinus Torvalds nlm_hosts[hash] = host; 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds nlm_get_host(host); 941da177e4SLinus Torvalds up(&nlm_host_sema); 951da177e4SLinus Torvalds return host; 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds /* Ooops, no host found, create it */ 1001da177e4SLinus Torvalds dprintk("lockd: creating host entry\n"); 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds if (!(host = (struct nlm_host *) kmalloc(sizeof(*host), GFP_KERNEL))) 1031da177e4SLinus Torvalds goto nohost; 1041da177e4SLinus Torvalds memset(host, 0, sizeof(*host)); 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds addr = sin->sin_addr.s_addr; 1071da177e4SLinus Torvalds sprintf(host->h_name, "%u.%u.%u.%u", NIPQUAD(addr)); 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds host->h_addr = *sin; 1101da177e4SLinus Torvalds host->h_addr.sin_port = 0; /* ouch! */ 1111da177e4SLinus Torvalds host->h_version = version; 1121da177e4SLinus Torvalds host->h_proto = proto; 1131da177e4SLinus Torvalds host->h_rpcclnt = NULL; 1141da177e4SLinus Torvalds init_MUTEX(&host->h_sema); 1151da177e4SLinus Torvalds host->h_nextrebind = jiffies + NLM_HOST_REBIND; 1161da177e4SLinus Torvalds host->h_expires = jiffies + NLM_HOST_EXPIRE; 1171da177e4SLinus Torvalds atomic_set(&host->h_count, 1); 1181da177e4SLinus Torvalds init_waitqueue_head(&host->h_gracewait); 1191da177e4SLinus Torvalds host->h_state = 0; /* pseudo NSM state */ 1201da177e4SLinus Torvalds host->h_nsmstate = 0; /* real NSM state */ 1211da177e4SLinus Torvalds host->h_server = server; 1221da177e4SLinus Torvalds host->h_next = nlm_hosts[hash]; 1231da177e4SLinus Torvalds nlm_hosts[hash] = host; 1241da177e4SLinus Torvalds INIT_LIST_HEAD(&host->h_lockowners); 1251da177e4SLinus Torvalds spin_lock_init(&host->h_lock); 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds if (++nrhosts > NLM_HOST_MAX) 1281da177e4SLinus Torvalds next_gc = 0; 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds nohost: 1311da177e4SLinus Torvalds up(&nlm_host_sema); 1321da177e4SLinus Torvalds return host; 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds struct nlm_host * 1361da177e4SLinus Torvalds nlm_find_client(void) 1371da177e4SLinus Torvalds { 1381da177e4SLinus Torvalds /* find a nlm_host for a client for which h_killed == 0. 1391da177e4SLinus Torvalds * and return it 1401da177e4SLinus Torvalds */ 1411da177e4SLinus Torvalds int hash; 1421da177e4SLinus Torvalds down(&nlm_host_sema); 1431da177e4SLinus Torvalds for (hash = 0 ; hash < NLM_HOST_NRHASH; hash++) { 1441da177e4SLinus Torvalds struct nlm_host *host, **hp; 1451da177e4SLinus Torvalds for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) { 1461da177e4SLinus Torvalds if (host->h_server && 1471da177e4SLinus Torvalds host->h_killed == 0) { 1481da177e4SLinus Torvalds nlm_get_host(host); 1491da177e4SLinus Torvalds up(&nlm_host_sema); 1501da177e4SLinus Torvalds return host; 1511da177e4SLinus Torvalds } 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds } 1541da177e4SLinus Torvalds up(&nlm_host_sema); 1551da177e4SLinus Torvalds return NULL; 1561da177e4SLinus Torvalds } 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds /* 1601da177e4SLinus Torvalds * Create the NLM RPC client for an NLM peer 1611da177e4SLinus Torvalds */ 1621da177e4SLinus Torvalds struct rpc_clnt * 1631da177e4SLinus Torvalds nlm_bind_host(struct nlm_host *host) 1641da177e4SLinus Torvalds { 1651da177e4SLinus Torvalds struct rpc_clnt *clnt; 1661da177e4SLinus Torvalds struct rpc_xprt *xprt; 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds dprintk("lockd: nlm_bind_host(%08x)\n", 1691da177e4SLinus Torvalds (unsigned)ntohl(host->h_addr.sin_addr.s_addr)); 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds /* Lock host handle */ 1721da177e4SLinus Torvalds down(&host->h_sema); 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds /* If we've already created an RPC client, check whether 1751da177e4SLinus Torvalds * RPC rebind is required 1761da177e4SLinus Torvalds */ 1771da177e4SLinus Torvalds if ((clnt = host->h_rpcclnt) != NULL) { 1781da177e4SLinus Torvalds xprt = clnt->cl_xprt; 179*43118c29SChuck Lever if (time_after_eq(jiffies, host->h_nextrebind)) { 1801da177e4SLinus Torvalds clnt->cl_port = 0; 1811da177e4SLinus Torvalds host->h_nextrebind = jiffies + NLM_HOST_REBIND; 1821da177e4SLinus Torvalds dprintk("lockd: next rebind in %ld jiffies\n", 1831da177e4SLinus Torvalds host->h_nextrebind - jiffies); 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds } else { 1861da177e4SLinus Torvalds xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL); 1871da177e4SLinus Torvalds if (IS_ERR(xprt)) 1881da177e4SLinus Torvalds goto forgetit; 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout); 1915ee0ed7dSTrond Myklebust xprt->nocong = 1; /* No congestion control for NLM */ 1925ee0ed7dSTrond Myklebust xprt->resvport = 1; /* NLM requires a reserved port */ 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds /* Existing NLM servers accept AUTH_UNIX only */ 1951da177e4SLinus Torvalds clnt = rpc_create_client(xprt, host->h_name, &nlm_program, 1961da177e4SLinus Torvalds host->h_version, RPC_AUTH_UNIX); 1975b616f5dSTrond Myklebust if (IS_ERR(clnt)) 1981da177e4SLinus Torvalds goto forgetit; 1991da177e4SLinus Torvalds clnt->cl_autobind = 1; /* turn on pmap queries */ 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds host->h_rpcclnt = clnt; 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds up(&host->h_sema); 2051da177e4SLinus Torvalds return clnt; 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds forgetit: 2081da177e4SLinus Torvalds printk("lockd: couldn't create RPC handle for %s\n", host->h_name); 2091da177e4SLinus Torvalds up(&host->h_sema); 2101da177e4SLinus Torvalds return NULL; 2111da177e4SLinus Torvalds } 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds /* 2141da177e4SLinus Torvalds * Force a portmap lookup of the remote lockd port 2151da177e4SLinus Torvalds */ 2161da177e4SLinus Torvalds void 2171da177e4SLinus Torvalds nlm_rebind_host(struct nlm_host *host) 2181da177e4SLinus Torvalds { 2191da177e4SLinus Torvalds dprintk("lockd: rebind host %s\n", host->h_name); 2201da177e4SLinus Torvalds if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) { 2211da177e4SLinus Torvalds host->h_rpcclnt->cl_port = 0; 2221da177e4SLinus Torvalds host->h_nextrebind = jiffies + NLM_HOST_REBIND; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds } 2251da177e4SLinus Torvalds 2261da177e4SLinus Torvalds /* 2271da177e4SLinus Torvalds * Increment NLM host count 2281da177e4SLinus Torvalds */ 2291da177e4SLinus Torvalds struct nlm_host * nlm_get_host(struct nlm_host *host) 2301da177e4SLinus Torvalds { 2311da177e4SLinus Torvalds if (host) { 2321da177e4SLinus Torvalds dprintk("lockd: get host %s\n", host->h_name); 2331da177e4SLinus Torvalds atomic_inc(&host->h_count); 2341da177e4SLinus Torvalds host->h_expires = jiffies + NLM_HOST_EXPIRE; 2351da177e4SLinus Torvalds } 2361da177e4SLinus Torvalds return host; 2371da177e4SLinus Torvalds } 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds /* 2401da177e4SLinus Torvalds * Release NLM host after use 2411da177e4SLinus Torvalds */ 2421da177e4SLinus Torvalds void nlm_release_host(struct nlm_host *host) 2431da177e4SLinus Torvalds { 2441da177e4SLinus Torvalds if (host != NULL) { 2451da177e4SLinus Torvalds dprintk("lockd: release host %s\n", host->h_name); 2461da177e4SLinus Torvalds atomic_dec(&host->h_count); 2471da177e4SLinus Torvalds BUG_ON(atomic_read(&host->h_count) < 0); 2481da177e4SLinus Torvalds } 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds /* 2521da177e4SLinus Torvalds * Shut down the hosts module. 2531da177e4SLinus Torvalds * Note that this routine is called only at server shutdown time. 2541da177e4SLinus Torvalds */ 2551da177e4SLinus Torvalds void 2561da177e4SLinus Torvalds nlm_shutdown_hosts(void) 2571da177e4SLinus Torvalds { 2581da177e4SLinus Torvalds struct nlm_host *host; 2591da177e4SLinus Torvalds int i; 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds dprintk("lockd: shutting down host module\n"); 2621da177e4SLinus Torvalds down(&nlm_host_sema); 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds /* First, make all hosts eligible for gc */ 2651da177e4SLinus Torvalds dprintk("lockd: nuking all hosts...\n"); 2661da177e4SLinus Torvalds for (i = 0; i < NLM_HOST_NRHASH; i++) { 2671da177e4SLinus Torvalds for (host = nlm_hosts[i]; host; host = host->h_next) 2681da177e4SLinus Torvalds host->h_expires = jiffies - 1; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds /* Then, perform a garbage collection pass */ 2721da177e4SLinus Torvalds nlm_gc_hosts(); 2731da177e4SLinus Torvalds up(&nlm_host_sema); 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds /* complain if any hosts are left */ 2761da177e4SLinus Torvalds if (nrhosts) { 2771da177e4SLinus Torvalds printk(KERN_WARNING "lockd: couldn't shutdown host module!\n"); 2781da177e4SLinus Torvalds dprintk("lockd: %d hosts left:\n", nrhosts); 2791da177e4SLinus Torvalds for (i = 0; i < NLM_HOST_NRHASH; i++) { 2801da177e4SLinus Torvalds for (host = nlm_hosts[i]; host; host = host->h_next) { 2811da177e4SLinus Torvalds dprintk(" %s (cnt %d use %d exp %ld)\n", 2821da177e4SLinus Torvalds host->h_name, atomic_read(&host->h_count), 2831da177e4SLinus Torvalds host->h_inuse, host->h_expires); 2841da177e4SLinus Torvalds } 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds /* 2901da177e4SLinus Torvalds * Garbage collect any unused NLM hosts. 2911da177e4SLinus Torvalds * This GC combines reference counting for async operations with 2921da177e4SLinus Torvalds * mark & sweep for resources held by remote clients. 2931da177e4SLinus Torvalds */ 2941da177e4SLinus Torvalds static void 2951da177e4SLinus Torvalds nlm_gc_hosts(void) 2961da177e4SLinus Torvalds { 2971da177e4SLinus Torvalds struct nlm_host **q, *host; 2981da177e4SLinus Torvalds struct rpc_clnt *clnt; 2991da177e4SLinus Torvalds int i; 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds dprintk("lockd: host garbage collection\n"); 3021da177e4SLinus Torvalds for (i = 0; i < NLM_HOST_NRHASH; i++) { 3031da177e4SLinus Torvalds for (host = nlm_hosts[i]; host; host = host->h_next) 3041da177e4SLinus Torvalds host->h_inuse = 0; 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds /* Mark all hosts that hold locks, blocks or shares */ 3081da177e4SLinus Torvalds nlmsvc_mark_resources(); 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds for (i = 0; i < NLM_HOST_NRHASH; i++) { 3111da177e4SLinus Torvalds q = &nlm_hosts[i]; 3121da177e4SLinus Torvalds while ((host = *q) != NULL) { 3131da177e4SLinus Torvalds if (atomic_read(&host->h_count) || host->h_inuse 3141da177e4SLinus Torvalds || time_before(jiffies, host->h_expires)) { 3151da177e4SLinus Torvalds dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n", 3161da177e4SLinus Torvalds host->h_name, atomic_read(&host->h_count), 3171da177e4SLinus Torvalds host->h_inuse, host->h_expires); 3181da177e4SLinus Torvalds q = &host->h_next; 3191da177e4SLinus Torvalds continue; 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds dprintk("lockd: delete host %s\n", host->h_name); 3221da177e4SLinus Torvalds *q = host->h_next; 3231da177e4SLinus Torvalds /* Don't unmonitor hosts that have been invalidated */ 3241da177e4SLinus Torvalds if (host->h_monitored && !host->h_killed) 3251da177e4SLinus Torvalds nsm_unmonitor(host); 3261da177e4SLinus Torvalds if ((clnt = host->h_rpcclnt) != NULL) { 3271da177e4SLinus Torvalds if (atomic_read(&clnt->cl_users)) { 3281da177e4SLinus Torvalds printk(KERN_WARNING 3291da177e4SLinus Torvalds "lockd: active RPC handle\n"); 3301da177e4SLinus Torvalds clnt->cl_dead = 1; 3311da177e4SLinus Torvalds } else { 3321da177e4SLinus Torvalds rpc_destroy_client(host->h_rpcclnt); 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds BUG_ON(!list_empty(&host->h_lockowners)); 3361da177e4SLinus Torvalds kfree(host); 3371da177e4SLinus Torvalds nrhosts--; 3381da177e4SLinus Torvalds } 3391da177e4SLinus Torvalds } 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds next_gc = jiffies + NLM_HOST_COLLECT; 3421da177e4SLinus Torvalds } 3431da177e4SLinus Torvalds 344