xref: /linux/fs/lockd/host.c (revision 43118c29dea2b23798bd42a147015cceee7fa885)
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