xref: /openbmc/linux/fs/lockd/host.c (revision e868d61272caa648214046a096e5a6bfc068dc8c)
1 /*
2  * linux/fs/lockd/host.c
3  *
4  * Management for NLM peer hosts. The nlm_host struct is shared
5  * between client and server implementation. The only reason to
6  * do so is to reduce code bloat.
7  *
8  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
9  */
10 
11 #include <linux/types.h>
12 #include <linux/slab.h>
13 #include <linux/in.h>
14 #include <linux/sunrpc/clnt.h>
15 #include <linux/sunrpc/svc.h>
16 #include <linux/lockd/lockd.h>
17 #include <linux/lockd/sm_inter.h>
18 #include <linux/mutex.h>
19 
20 
21 #define NLMDBG_FACILITY		NLMDBG_HOSTCACHE
22 #define NLM_HOST_MAX		64
23 #define NLM_HOST_NRHASH		32
24 #define NLM_ADDRHASH(addr)	(ntohl(addr) & (NLM_HOST_NRHASH-1))
25 #define NLM_HOST_REBIND		(60 * HZ)
26 #define NLM_HOST_EXPIRE		((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ)
27 #define NLM_HOST_COLLECT	((nrhosts > NLM_HOST_MAX)? 120 * HZ :  60 * HZ)
28 
29 static struct hlist_head	nlm_hosts[NLM_HOST_NRHASH];
30 static unsigned long		next_gc;
31 static int			nrhosts;
32 static DEFINE_MUTEX(nlm_host_mutex);
33 
34 
35 static void			nlm_gc_hosts(void);
36 static struct nsm_handle *	__nsm_find(const struct sockaddr_in *,
37 					const char *, int, int);
38 static struct nsm_handle *	nsm_find(const struct sockaddr_in *sin,
39 					 const char *hostname,
40 					 int hostname_len);
41 
42 /*
43  * Common host lookup routine for server & client
44  */
45 static struct nlm_host *
46 nlm_lookup_host(int server, const struct sockaddr_in *sin,
47 					int proto, int version,
48 					const char *hostname,
49 					int hostname_len)
50 {
51 	struct hlist_head *chain;
52 	struct hlist_node *pos;
53 	struct nlm_host	*host;
54 	struct nsm_handle *nsm = NULL;
55 	int		hash;
56 
57 	dprintk("lockd: nlm_lookup_host(%u.%u.%u.%u, p=%d, v=%d, my role=%s, name=%.*s)\n",
58 			NIPQUAD(sin->sin_addr.s_addr), proto, version,
59 			server? "server" : "client",
60 			hostname_len,
61 			hostname? hostname : "<none>");
62 
63 
64 	hash = NLM_ADDRHASH(sin->sin_addr.s_addr);
65 
66 	/* Lock hash table */
67 	mutex_lock(&nlm_host_mutex);
68 
69 	if (time_after_eq(jiffies, next_gc))
70 		nlm_gc_hosts();
71 
72 	/* We may keep several nlm_host objects for a peer, because each
73 	 * nlm_host is identified by
74 	 * (address, protocol, version, server/client)
75 	 * We could probably simplify this a little by putting all those
76 	 * different NLM rpc_clients into one single nlm_host object.
77 	 * This would allow us to have one nlm_host per address.
78 	 */
79 	chain = &nlm_hosts[hash];
80 	hlist_for_each_entry(host, pos, chain, h_hash) {
81 		if (!nlm_cmp_addr(&host->h_addr, sin))
82 			continue;
83 
84 		/* See if we have an NSM handle for this client */
85 		if (!nsm)
86 			nsm = host->h_nsmhandle;
87 
88 		if (host->h_proto != proto)
89 			continue;
90 		if (host->h_version != version)
91 			continue;
92 		if (host->h_server != server)
93 			continue;
94 
95 		/* Move to head of hash chain. */
96 		hlist_del(&host->h_hash);
97 		hlist_add_head(&host->h_hash, chain);
98 
99 		nlm_get_host(host);
100 		goto out;
101 	}
102 	if (nsm)
103 		atomic_inc(&nsm->sm_count);
104 
105 	host = NULL;
106 
107 	/* Sadly, the host isn't in our hash table yet. See if
108 	 * we have an NSM handle for it. If not, create one.
109 	 */
110 	if (!nsm && !(nsm = nsm_find(sin, hostname, hostname_len)))
111 		goto out;
112 
113 	host = kzalloc(sizeof(*host), GFP_KERNEL);
114 	if (!host) {
115 		nsm_release(nsm);
116 		goto out;
117 	}
118 	host->h_name	   = nsm->sm_name;
119 	host->h_addr       = *sin;
120 	host->h_addr.sin_port = 0;	/* ouch! */
121 	host->h_version    = version;
122 	host->h_proto      = proto;
123 	host->h_rpcclnt    = NULL;
124 	mutex_init(&host->h_mutex);
125 	host->h_nextrebind = jiffies + NLM_HOST_REBIND;
126 	host->h_expires    = jiffies + NLM_HOST_EXPIRE;
127 	atomic_set(&host->h_count, 1);
128 	init_waitqueue_head(&host->h_gracewait);
129 	init_rwsem(&host->h_rwsem);
130 	host->h_state      = 0;			/* pseudo NSM state */
131 	host->h_nsmstate   = 0;			/* real NSM state */
132 	host->h_nsmhandle  = nsm;
133 	host->h_server	   = server;
134 	hlist_add_head(&host->h_hash, chain);
135 	INIT_LIST_HEAD(&host->h_lockowners);
136 	spin_lock_init(&host->h_lock);
137 	INIT_LIST_HEAD(&host->h_granted);
138 	INIT_LIST_HEAD(&host->h_reclaim);
139 
140 	if (++nrhosts > NLM_HOST_MAX)
141 		next_gc = 0;
142 
143 out:
144 	mutex_unlock(&nlm_host_mutex);
145 	return host;
146 }
147 
148 /*
149  * Destroy a host
150  */
151 static void
152 nlm_destroy_host(struct nlm_host *host)
153 {
154 	struct rpc_clnt	*clnt;
155 
156 	BUG_ON(!list_empty(&host->h_lockowners));
157 	BUG_ON(atomic_read(&host->h_count));
158 
159 	/*
160 	 * Release NSM handle and unmonitor host.
161 	 */
162 	nsm_unmonitor(host);
163 
164 	if ((clnt = host->h_rpcclnt) != NULL) {
165 		if (atomic_read(&clnt->cl_users)) {
166 			printk(KERN_WARNING
167 				"lockd: active RPC handle\n");
168 			clnt->cl_dead = 1;
169 		} else {
170 			rpc_destroy_client(host->h_rpcclnt);
171 		}
172 	}
173 	kfree(host);
174 }
175 
176 /*
177  * Find an NLM server handle in the cache. If there is none, create it.
178  */
179 struct nlm_host *
180 nlmclnt_lookup_host(const struct sockaddr_in *sin, int proto, int version,
181 			const char *hostname, int hostname_len)
182 {
183 	return nlm_lookup_host(0, sin, proto, version,
184 			       hostname, hostname_len);
185 }
186 
187 /*
188  * Find an NLM client handle in the cache. If there is none, create it.
189  */
190 struct nlm_host *
191 nlmsvc_lookup_host(struct svc_rqst *rqstp,
192 			const char *hostname, int hostname_len)
193 {
194 	return nlm_lookup_host(1, svc_addr_in(rqstp),
195 			       rqstp->rq_prot, rqstp->rq_vers,
196 			       hostname, hostname_len);
197 }
198 
199 /*
200  * Create the NLM RPC client for an NLM peer
201  */
202 struct rpc_clnt *
203 nlm_bind_host(struct nlm_host *host)
204 {
205 	struct rpc_clnt	*clnt;
206 
207 	dprintk("lockd: nlm_bind_host(%08x)\n",
208 			(unsigned)ntohl(host->h_addr.sin_addr.s_addr));
209 
210 	/* Lock host handle */
211 	mutex_lock(&host->h_mutex);
212 
213 	/* If we've already created an RPC client, check whether
214 	 * RPC rebind is required
215 	 */
216 	if ((clnt = host->h_rpcclnt) != NULL) {
217 		if (time_after_eq(jiffies, host->h_nextrebind)) {
218 			rpc_force_rebind(clnt);
219 			host->h_nextrebind = jiffies + NLM_HOST_REBIND;
220 			dprintk("lockd: next rebind in %ld jiffies\n",
221 					host->h_nextrebind - jiffies);
222 		}
223 	} else {
224 		unsigned long increment = nlmsvc_timeout * HZ;
225 		struct rpc_timeout timeparms = {
226 			.to_initval	= increment,
227 			.to_increment	= increment,
228 			.to_maxval	= increment * 6UL,
229 			.to_retries	= 5U,
230 		};
231 		struct rpc_create_args args = {
232 			.protocol	= host->h_proto,
233 			.address	= (struct sockaddr *)&host->h_addr,
234 			.addrsize	= sizeof(host->h_addr),
235 			.timeout	= &timeparms,
236 			.servername	= host->h_name,
237 			.program	= &nlm_program,
238 			.version	= host->h_version,
239 			.authflavor	= RPC_AUTH_UNIX,
240 			.flags		= (RPC_CLNT_CREATE_HARDRTRY |
241 					   RPC_CLNT_CREATE_AUTOBIND),
242 		};
243 
244 		clnt = rpc_create(&args);
245 		if (!IS_ERR(clnt))
246 			host->h_rpcclnt = clnt;
247 		else {
248 			printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
249 			clnt = NULL;
250 		}
251 	}
252 
253 	mutex_unlock(&host->h_mutex);
254 	return clnt;
255 }
256 
257 /*
258  * Force a portmap lookup of the remote lockd port
259  */
260 void
261 nlm_rebind_host(struct nlm_host *host)
262 {
263 	dprintk("lockd: rebind host %s\n", host->h_name);
264 	if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
265 		rpc_force_rebind(host->h_rpcclnt);
266 		host->h_nextrebind = jiffies + NLM_HOST_REBIND;
267 	}
268 }
269 
270 /*
271  * Increment NLM host count
272  */
273 struct nlm_host * nlm_get_host(struct nlm_host *host)
274 {
275 	if (host) {
276 		dprintk("lockd: get host %s\n", host->h_name);
277 		atomic_inc(&host->h_count);
278 		host->h_expires = jiffies + NLM_HOST_EXPIRE;
279 	}
280 	return host;
281 }
282 
283 /*
284  * Release NLM host after use
285  */
286 void nlm_release_host(struct nlm_host *host)
287 {
288 	if (host != NULL) {
289 		dprintk("lockd: release host %s\n", host->h_name);
290 		BUG_ON(atomic_read(&host->h_count) < 0);
291 		if (atomic_dec_and_test(&host->h_count)) {
292 			BUG_ON(!list_empty(&host->h_lockowners));
293 			BUG_ON(!list_empty(&host->h_granted));
294 			BUG_ON(!list_empty(&host->h_reclaim));
295 		}
296 	}
297 }
298 
299 /*
300  * We were notified that the host indicated by address &sin
301  * has rebooted.
302  * Release all resources held by that peer.
303  */
304 void nlm_host_rebooted(const struct sockaddr_in *sin,
305 				const char *hostname, int hostname_len,
306 				u32 new_state)
307 {
308 	struct hlist_head *chain;
309 	struct hlist_node *pos;
310 	struct nsm_handle *nsm;
311 	struct nlm_host	*host;
312 
313 	dprintk("lockd: nlm_host_rebooted(%s, %u.%u.%u.%u)\n",
314 			hostname, NIPQUAD(sin->sin_addr));
315 
316 	/* Find the NSM handle for this peer */
317 	if (!(nsm = __nsm_find(sin, hostname, hostname_len, 0)))
318 		return;
319 
320 	/* When reclaiming locks on this peer, make sure that
321 	 * we set up a new notification */
322 	nsm->sm_monitored = 0;
323 
324 	/* Mark all hosts tied to this NSM state as having rebooted.
325 	 * We run the loop repeatedly, because we drop the host table
326 	 * lock for this.
327 	 * To avoid processing a host several times, we match the nsmstate.
328 	 */
329 again:	mutex_lock(&nlm_host_mutex);
330 	for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
331 		hlist_for_each_entry(host, pos, chain, h_hash) {
332 			if (host->h_nsmhandle == nsm
333 			 && host->h_nsmstate != new_state) {
334 				host->h_nsmstate = new_state;
335 				host->h_state++;
336 
337 				nlm_get_host(host);
338 				mutex_unlock(&nlm_host_mutex);
339 
340 				if (host->h_server) {
341 					/* We're server for this guy, just ditch
342 					 * all the locks he held. */
343 					nlmsvc_free_host_resources(host);
344 				} else {
345 					/* He's the server, initiate lock recovery. */
346 					nlmclnt_recovery(host);
347 				}
348 
349 				nlm_release_host(host);
350 				goto again;
351 			}
352 		}
353 	}
354 
355 	mutex_unlock(&nlm_host_mutex);
356 }
357 
358 /*
359  * Shut down the hosts module.
360  * Note that this routine is called only at server shutdown time.
361  */
362 void
363 nlm_shutdown_hosts(void)
364 {
365 	struct hlist_head *chain;
366 	struct hlist_node *pos;
367 	struct nlm_host	*host;
368 
369 	dprintk("lockd: shutting down host module\n");
370 	mutex_lock(&nlm_host_mutex);
371 
372 	/* First, make all hosts eligible for gc */
373 	dprintk("lockd: nuking all hosts...\n");
374 	for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
375 		hlist_for_each_entry(host, pos, chain, h_hash)
376 			host->h_expires = jiffies - 1;
377 	}
378 
379 	/* Then, perform a garbage collection pass */
380 	nlm_gc_hosts();
381 	mutex_unlock(&nlm_host_mutex);
382 
383 	/* complain if any hosts are left */
384 	if (nrhosts) {
385 		printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
386 		dprintk("lockd: %d hosts left:\n", nrhosts);
387 		for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
388 			hlist_for_each_entry(host, pos, chain, h_hash) {
389 				dprintk("       %s (cnt %d use %d exp %ld)\n",
390 					host->h_name, atomic_read(&host->h_count),
391 					host->h_inuse, host->h_expires);
392 			}
393 		}
394 	}
395 }
396 
397 /*
398  * Garbage collect any unused NLM hosts.
399  * This GC combines reference counting for async operations with
400  * mark & sweep for resources held by remote clients.
401  */
402 static void
403 nlm_gc_hosts(void)
404 {
405 	struct hlist_head *chain;
406 	struct hlist_node *pos, *next;
407 	struct nlm_host	*host;
408 
409 	dprintk("lockd: host garbage collection\n");
410 	for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
411 		hlist_for_each_entry(host, pos, chain, h_hash)
412 			host->h_inuse = 0;
413 	}
414 
415 	/* Mark all hosts that hold locks, blocks or shares */
416 	nlmsvc_mark_resources();
417 
418 	for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
419 		hlist_for_each_entry_safe(host, pos, next, chain, h_hash) {
420 			if (atomic_read(&host->h_count) || host->h_inuse
421 			 || time_before(jiffies, host->h_expires)) {
422 				dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n",
423 					host->h_name, atomic_read(&host->h_count),
424 					host->h_inuse, host->h_expires);
425 				continue;
426 			}
427 			dprintk("lockd: delete host %s\n", host->h_name);
428 			hlist_del_init(&host->h_hash);
429 
430 			nlm_destroy_host(host);
431 			nrhosts--;
432 		}
433 	}
434 
435 	next_gc = jiffies + NLM_HOST_COLLECT;
436 }
437 
438 
439 /*
440  * Manage NSM handles
441  */
442 static LIST_HEAD(nsm_handles);
443 static DEFINE_MUTEX(nsm_mutex);
444 
445 static struct nsm_handle *
446 __nsm_find(const struct sockaddr_in *sin,
447 		const char *hostname, int hostname_len,
448 		int create)
449 {
450 	struct nsm_handle *nsm = NULL;
451 	struct list_head *pos;
452 
453 	if (!sin)
454 		return NULL;
455 
456 	if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
457 		if (printk_ratelimit()) {
458 			printk(KERN_WARNING "Invalid hostname \"%.*s\" "
459 					    "in NFS lock request\n",
460 				hostname_len, hostname);
461 		}
462 		return NULL;
463 	}
464 
465 	mutex_lock(&nsm_mutex);
466 	list_for_each(pos, &nsm_handles) {
467 		nsm = list_entry(pos, struct nsm_handle, sm_link);
468 
469 		if (hostname && nsm_use_hostnames) {
470 			if (strlen(nsm->sm_name) != hostname_len
471 			 || memcmp(nsm->sm_name, hostname, hostname_len))
472 				continue;
473 		} else if (!nlm_cmp_addr(&nsm->sm_addr, sin))
474 			continue;
475 		atomic_inc(&nsm->sm_count);
476 		goto out;
477 	}
478 
479 	if (!create) {
480 		nsm = NULL;
481 		goto out;
482 	}
483 
484 	nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL);
485 	if (nsm != NULL) {
486 		nsm->sm_addr = *sin;
487 		nsm->sm_name = (char *) (nsm + 1);
488 		memcpy(nsm->sm_name, hostname, hostname_len);
489 		nsm->sm_name[hostname_len] = '\0';
490 		atomic_set(&nsm->sm_count, 1);
491 
492 		list_add(&nsm->sm_link, &nsm_handles);
493 	}
494 
495 out:
496 	mutex_unlock(&nsm_mutex);
497 	return nsm;
498 }
499 
500 static struct nsm_handle *
501 nsm_find(const struct sockaddr_in *sin, const char *hostname, int hostname_len)
502 {
503 	return __nsm_find(sin, hostname, hostname_len, 1);
504 }
505 
506 /*
507  * Release an NSM handle
508  */
509 void
510 nsm_release(struct nsm_handle *nsm)
511 {
512 	if (!nsm)
513 		return;
514 	if (atomic_dec_and_test(&nsm->sm_count)) {
515 		mutex_lock(&nsm_mutex);
516 		if (atomic_read(&nsm->sm_count) == 0) {
517 			list_del(&nsm->sm_link);
518 			kfree(nsm);
519 		}
520 		mutex_unlock(&nsm_mutex);
521 	}
522 }
523