host.c 15.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * linux/fs/lockd/host.c
 *
 * Management for NLM peer hosts. The nlm_host struct is shared
 * between client and server implementation. The only reason to
 * do so is to reduce code bloat.
 *
 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
 */

#include <linux/types.h>
#include <linux/slab.h>
#include <linux/in.h>
14
#include <linux/in6.h>
L
Linus Torvalds 已提交
15 16 17 18
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h>
#include <linux/lockd/sm_inter.h>
I
Ingo Molnar 已提交
19
#include <linux/mutex.h>
L
Linus Torvalds 已提交
20

21
#include <net/ipv6.h>
L
Linus Torvalds 已提交
22 23 24 25

#define NLMDBG_FACILITY		NLMDBG_HOSTCACHE
#define NLM_HOST_NRHASH		32
#define NLM_HOST_REBIND		(60 * HZ)
26 27
#define NLM_HOST_EXPIRE		(300 * HZ)
#define NLM_HOST_COLLECT	(120 * HZ)
L
Linus Torvalds 已提交
28

29
static struct hlist_head	nlm_hosts[NLM_HOST_NRHASH];
L
Linus Torvalds 已提交
30 31
static unsigned long		next_gc;
static int			nrhosts;
I
Ingo Molnar 已提交
32
static DEFINE_MUTEX(nlm_host_mutex);
L
Linus Torvalds 已提交
33 34

static void			nlm_gc_hosts(void);
35 36 37 38
static struct nsm_handle	*nsm_find(const struct sockaddr_in *sin,
						const char *hostname,
						const size_t hostname_len,
						const int create);
L
Linus Torvalds 已提交
39

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
/*
 * Hash function must work well on big- and little-endian platforms
 */
static unsigned int __nlm_hash32(const __be32 n)
{
	unsigned int hash = (__force u32)n ^ ((__force u32)n >> 16);
	return hash ^ (hash >> 8);
}

static unsigned int __nlm_hash_addr4(const struct sockaddr *sap)
{
	const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
	return __nlm_hash32(sin->sin_addr.s_addr);
}

static unsigned int __nlm_hash_addr6(const struct sockaddr *sap)
{
	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
	const struct in6_addr addr = sin6->sin6_addr;
	return __nlm_hash32(addr.s6_addr32[0]) ^
	       __nlm_hash32(addr.s6_addr32[1]) ^
	       __nlm_hash32(addr.s6_addr32[2]) ^
	       __nlm_hash32(addr.s6_addr32[3]);
}

static unsigned int nlm_hash_address(const struct sockaddr *sap)
{
	unsigned int hash;

	switch (sap->sa_family) {
	case AF_INET:
		hash = __nlm_hash_addr4(sap);
		break;
	case AF_INET6:
		hash = __nlm_hash_addr6(sap);
		break;
	default:
		hash = 0;
	}
	return hash & (NLM_HOST_NRHASH - 1);
}

82 83 84 85 86 87 88 89 90 91 92 93
static void nlm_clear_port(struct sockaddr *sap)
{
	switch (sap->sa_family) {
	case AF_INET:
		((struct sockaddr_in *)sap)->sin_port = 0;
		break;
	case AF_INET6:
		((struct sockaddr_in6 *)sap)->sin6_port = 0;
		break;
	}
}

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
static void nlm_display_address(const struct sockaddr *sap,
				char *buf, const size_t len)
{
	const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
	const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;

	switch (sap->sa_family) {
	case AF_UNSPEC:
		snprintf(buf, len, "unspecified");
		break;
	case AF_INET:
		snprintf(buf, len, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr));
		break;
	case AF_INET6:
		if (ipv6_addr_v4mapped(&sin6->sin6_addr))
			snprintf(buf, len, NIPQUAD_FMT,
				 NIPQUAD(sin6->sin6_addr.s6_addr32[3]));
		else
			snprintf(buf, len, NIP6_FMT, NIP6(sin6->sin6_addr));
		break;
	default:
		snprintf(buf, len, "unsupported address family");
		break;
	}
}

L
Linus Torvalds 已提交
120 121 122
/*
 * Common host lookup routine for server & client
 */
123 124 125 126 127 128
static struct nlm_host *nlm_lookup_host(int server,
					const struct sockaddr_in *sin,
					int proto, u32 version,
					const char *hostname,
					unsigned int hostname_len,
					const struct sockaddr_in *ssin)
L
Linus Torvalds 已提交
129
{
130 131 132
	struct hlist_head *chain;
	struct hlist_node *pos;
	struct nlm_host	*host;
133
	struct nsm_handle *nsm = NULL;
L
Linus Torvalds 已提交
134

135 136 137 138
	dprintk("lockd: nlm_lookup_host(proto=%d, vers=%u,"
			" my role is %s, hostname=%.*s)\n",
			proto, version, server ? "server" : "client",
			hostname_len, hostname ? hostname : "<none>");
L
Linus Torvalds 已提交
139

I
Ingo Molnar 已提交
140
	mutex_lock(&nlm_host_mutex);
L
Linus Torvalds 已提交
141 142 143 144

	if (time_after_eq(jiffies, next_gc))
		nlm_gc_hosts();

145 146 147 148 149 150 151
	/* We may keep several nlm_host objects for a peer, because each
	 * nlm_host is identified by
	 * (address, protocol, version, server/client)
	 * We could probably simplify this a little by putting all those
	 * different NLM rpc_clients into one single nlm_host object.
	 * This would allow us to have one nlm_host per address.
	 */
152
	chain = &nlm_hosts[nlm_hash_address((struct sockaddr *)sin)];
153
	hlist_for_each_entry(host, pos, chain, h_hash) {
154
		if (!nlm_cmp_addr(nlm_addr(host), (struct sockaddr *)sin))
155 156 157
			continue;

		/* See if we have an NSM handle for this client */
158 159
		if (!nsm)
			nsm = host->h_nsmhandle;
160

L
Linus Torvalds 已提交
161 162 163 164 165 166
		if (host->h_proto != proto)
			continue;
		if (host->h_version != version)
			continue;
		if (host->h_server != server)
			continue;
167
		if (!nlm_cmp_addr(nlm_srcaddr(host), (struct sockaddr *)ssin))
168
			continue;
L
Linus Torvalds 已提交
169

170 171 172 173
		/* Move to head of hash chain. */
		hlist_del(&host->h_hash);
		hlist_add_head(&host->h_hash, chain);

174
		nlm_get_host(host);
175 176
		dprintk("lockd: nlm_lookup_host found host %s (%s)\n",
				host->h_name, host->h_addrbuf);
177
		goto out;
L
Linus Torvalds 已提交
178 179
	}

180 181 182
	/*
	 * The host wasn't in our hash table.  If we don't
	 * have an NSM handle for it yet, create one.
183
	 */
184 185 186 187
	if (nsm)
		atomic_inc(&nsm->sm_count);
	else {
		host = NULL;
188
		nsm = nsm_find(sin, hostname, hostname_len, 1);
189 190 191
		if (!nsm) {
			dprintk("lockd: nlm_lookup_host failed; "
				"no nsm handle\n");
192
			goto out;
193
		}
194
	}
L
Linus Torvalds 已提交
195

196
	host = kzalloc(sizeof(*host), GFP_KERNEL);
197 198
	if (!host) {
		nsm_release(nsm);
199
		dprintk("lockd: nlm_lookup_host failed; no memory\n");
200 201 202
		goto out;
	}
	host->h_name	   = nsm->sm_name;
203 204 205
	memcpy(nlm_addr(host), sin, sizeof(*sin));
	host->h_addrlen = sizeof(*sin);
	nlm_clear_port(nlm_addr(host));
206
	memcpy(nlm_srcaddr(host), ssin, sizeof(*ssin));
L
Linus Torvalds 已提交
207 208 209
	host->h_version    = version;
	host->h_proto      = proto;
	host->h_rpcclnt    = NULL;
T
Trond Myklebust 已提交
210
	mutex_init(&host->h_mutex);
L
Linus Torvalds 已提交
211 212 213 214
	host->h_nextrebind = jiffies + NLM_HOST_REBIND;
	host->h_expires    = jiffies + NLM_HOST_EXPIRE;
	atomic_set(&host->h_count, 1);
	init_waitqueue_head(&host->h_gracewait);
T
Trond Myklebust 已提交
215
	init_rwsem(&host->h_rwsem);
L
Linus Torvalds 已提交
216 217
	host->h_state      = 0;			/* pseudo NSM state */
	host->h_nsmstate   = 0;			/* real NSM state */
218
	host->h_nsmhandle  = nsm;
L
Linus Torvalds 已提交
219
	host->h_server	   = server;
220
	hlist_add_head(&host->h_hash, chain);
L
Linus Torvalds 已提交
221 222
	INIT_LIST_HEAD(&host->h_lockowners);
	spin_lock_init(&host->h_lock);
223 224
	INIT_LIST_HEAD(&host->h_granted);
	INIT_LIST_HEAD(&host->h_reclaim);
L
Linus Torvalds 已提交
225

226
	nrhosts++;
227 228 229

	nlm_display_address((struct sockaddr *)&host->h_addr,
				host->h_addrbuf, sizeof(host->h_addrbuf));
230 231
	nlm_display_address((struct sockaddr *)&host->h_srcaddr,
				host->h_srcaddrbuf, sizeof(host->h_srcaddrbuf));
232 233 234 235

	dprintk("lockd: nlm_lookup_host created host %s\n",
			host->h_name);

236
out:
I
Ingo Molnar 已提交
237
	mutex_unlock(&nlm_host_mutex);
L
Linus Torvalds 已提交
238 239 240
	return host;
}

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
/*
 * Destroy a host
 */
static void
nlm_destroy_host(struct nlm_host *host)
{
	struct rpc_clnt	*clnt;

	BUG_ON(!list_empty(&host->h_lockowners));
	BUG_ON(atomic_read(&host->h_count));

	/*
	 * Release NSM handle and unmonitor host.
	 */
	nsm_unmonitor(host);

257 258 259
	clnt = host->h_rpcclnt;
	if (clnt != NULL)
		rpc_shutdown_client(clnt);
260 261 262
	kfree(host);
}

263 264 265
/*
 * Find an NLM server handle in the cache. If there is none, create it.
 */
266 267 268 269
struct nlm_host *nlmclnt_lookup_host(const struct sockaddr_in *sin,
				     int proto, u32 version,
				     const char *hostname,
				     unsigned int hostname_len)
270
{
271 272 273
	const struct sockaddr_in source = {
		.sin_family	= AF_UNSPEC,
	};
274

275
	return nlm_lookup_host(0, sin, proto, version,
276
			       hostname, hostname_len, &source);
277 278 279 280 281 282 283
}

/*
 * Find an NLM client handle in the cache. If there is none, create it.
 */
struct nlm_host *
nlmsvc_lookup_host(struct svc_rqst *rqstp,
284
			const char *hostname, unsigned int hostname_len)
285
{
286 287 288 289
	const struct sockaddr_in source = {
		.sin_family	= AF_INET,
		.sin_addr	= rqstp->rq_daddr.addr,
	};
290

291
	return nlm_lookup_host(1, svc_addr_in(rqstp),
292
			       rqstp->rq_prot, rqstp->rq_vers,
293
			       hostname, hostname_len, &source);
294 295
}

L
Linus Torvalds 已提交
296 297 298 299 300 301 302 303
/*
 * Create the NLM RPC client for an NLM peer
 */
struct rpc_clnt *
nlm_bind_host(struct nlm_host *host)
{
	struct rpc_clnt	*clnt;

304
	dprintk("lockd: nlm_bind_host %s (%s), my addr=%s\n",
305
			host->h_name, host->h_addrbuf, host->h_srcaddrbuf);
L
Linus Torvalds 已提交
306 307

	/* Lock host handle */
T
Trond Myklebust 已提交
308
	mutex_lock(&host->h_mutex);
L
Linus Torvalds 已提交
309 310 311 312 313

	/* If we've already created an RPC client, check whether
	 * RPC rebind is required
	 */
	if ((clnt = host->h_rpcclnt) != NULL) {
314
		if (time_after_eq(jiffies, host->h_nextrebind)) {
315
			rpc_force_rebind(clnt);
L
Linus Torvalds 已提交
316
			host->h_nextrebind = jiffies + NLM_HOST_REBIND;
317
			dprintk("lockd: next rebind in %lu jiffies\n",
L
Linus Torvalds 已提交
318 319 320
					host->h_nextrebind - jiffies);
		}
	} else {
321
		unsigned long increment = nlmsvc_timeout;
322 323 324 325 326 327 328 329
		struct rpc_timeout timeparms = {
			.to_initval	= increment,
			.to_increment	= increment,
			.to_maxval	= increment * 6UL,
			.to_retries	= 5U,
		};
		struct rpc_create_args args = {
			.protocol	= host->h_proto,
330 331
			.address	= nlm_addr(host),
			.addrsize	= host->h_addrlen,
332
			.saddress	= nlm_srcaddr(host),
333 334 335 336 337
			.timeout	= &timeparms,
			.servername	= host->h_name,
			.program	= &nlm_program,
			.version	= host->h_version,
			.authflavor	= RPC_AUTH_UNIX,
338
			.flags		= (RPC_CLNT_CREATE_NOPING |
339 340 341
					   RPC_CLNT_CREATE_AUTOBIND),
		};

342 343 344 345 346 347 348 349
		/*
		 * lockd retries server side blocks automatically so we want
		 * those to be soft RPC calls. Client side calls need to be
		 * hard RPC tasks.
		 */
		if (!host->h_server)
			args.flags |= RPC_CLNT_CREATE_HARDRTRY;

350 351 352 353 354 355 356
		clnt = rpc_create(&args);
		if (!IS_ERR(clnt))
			host->h_rpcclnt = clnt;
		else {
			printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
			clnt = NULL;
		}
L
Linus Torvalds 已提交
357 358
	}

T
Trond Myklebust 已提交
359
	mutex_unlock(&host->h_mutex);
L
Linus Torvalds 已提交
360 361 362 363 364 365 366 367 368 369 370
	return clnt;
}

/*
 * Force a portmap lookup of the remote lockd port
 */
void
nlm_rebind_host(struct nlm_host *host)
{
	dprintk("lockd: rebind host %s\n", host->h_name);
	if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
371
		rpc_force_rebind(host->h_rpcclnt);
L
Linus Torvalds 已提交
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
		host->h_nextrebind = jiffies + NLM_HOST_REBIND;
	}
}

/*
 * Increment NLM host count
 */
struct nlm_host * nlm_get_host(struct nlm_host *host)
{
	if (host) {
		dprintk("lockd: get host %s\n", host->h_name);
		atomic_inc(&host->h_count);
		host->h_expires = jiffies + NLM_HOST_EXPIRE;
	}
	return host;
}

/*
 * Release NLM host after use
 */
void nlm_release_host(struct nlm_host *host)
{
	if (host != NULL) {
		dprintk("lockd: release host %s\n", host->h_name);
		BUG_ON(atomic_read(&host->h_count) < 0);
397 398 399 400 401
		if (atomic_dec_and_test(&host->h_count)) {
			BUG_ON(!list_empty(&host->h_lockowners));
			BUG_ON(!list_empty(&host->h_granted));
			BUG_ON(!list_empty(&host->h_reclaim));
		}
L
Linus Torvalds 已提交
402 403 404
	}
}

405 406 407 408 409
/*
 * We were notified that the host indicated by address &sin
 * has rebooted.
 * Release all resources held by that peer.
 */
410
void nlm_host_rebooted(const struct sockaddr_in *sin,
411 412
				const char *hostname,
				unsigned int hostname_len,
413
				u32 new_state)
414
{
415 416
	struct hlist_head *chain;
	struct hlist_node *pos;
417
	struct nsm_handle *nsm;
418
	struct nlm_host	*host;
419

420
	nsm = nsm_find(sin, hostname, hostname_len, 0);
421 422 423
	if (nsm == NULL) {
		dprintk("lockd: never saw rebooted peer '%.*s' before\n",
				hostname_len, hostname);
424
		return;
425 426 427 428
	}

	dprintk("lockd: nlm_host_rebooted(%.*s, %s)\n",
			hostname_len, hostname, nsm->sm_addrbuf);
429

430 431 432 433 434 435 436 437 438 439
	/* When reclaiming locks on this peer, make sure that
	 * we set up a new notification */
	nsm->sm_monitored = 0;

	/* Mark all hosts tied to this NSM state as having rebooted.
	 * We run the loop repeatedly, because we drop the host table
	 * lock for this.
	 * To avoid processing a host several times, we match the nsmstate.
	 */
again:	mutex_lock(&nlm_host_mutex);
440 441
	for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
		hlist_for_each_entry(host, pos, chain, h_hash) {
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
			if (host->h_nsmhandle == nsm
			 && host->h_nsmstate != new_state) {
				host->h_nsmstate = new_state;
				host->h_state++;

				nlm_get_host(host);
				mutex_unlock(&nlm_host_mutex);

				if (host->h_server) {
					/* We're server for this guy, just ditch
					 * all the locks he held. */
					nlmsvc_free_host_resources(host);
				} else {
					/* He's the server, initiate lock recovery. */
					nlmclnt_recovery(host);
				}

				nlm_release_host(host);
				goto again;
			}
		}
463
	}
464 465

	mutex_unlock(&nlm_host_mutex);
466 467
}

L
Linus Torvalds 已提交
468 469 470 471 472 473 474
/*
 * Shut down the hosts module.
 * Note that this routine is called only at server shutdown time.
 */
void
nlm_shutdown_hosts(void)
{
475 476
	struct hlist_head *chain;
	struct hlist_node *pos;
L
Linus Torvalds 已提交
477 478 479
	struct nlm_host	*host;

	dprintk("lockd: shutting down host module\n");
I
Ingo Molnar 已提交
480
	mutex_lock(&nlm_host_mutex);
L
Linus Torvalds 已提交
481 482 483

	/* First, make all hosts eligible for gc */
	dprintk("lockd: nuking all hosts...\n");
484
	for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
485
		hlist_for_each_entry(host, pos, chain, h_hash) {
L
Linus Torvalds 已提交
486
			host->h_expires = jiffies - 1;
487 488 489 490 491
			if (host->h_rpcclnt) {
				rpc_shutdown_client(host->h_rpcclnt);
				host->h_rpcclnt = NULL;
			}
		}
L
Linus Torvalds 已提交
492 493 494 495
	}

	/* Then, perform a garbage collection pass */
	nlm_gc_hosts();
I
Ingo Molnar 已提交
496
	mutex_unlock(&nlm_host_mutex);
L
Linus Torvalds 已提交
497 498 499 500 501

	/* complain if any hosts are left */
	if (nrhosts) {
		printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
		dprintk("lockd: %d hosts left:\n", nrhosts);
502 503
		for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
			hlist_for_each_entry(host, pos, chain, h_hash) {
L
Linus Torvalds 已提交
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
				dprintk("       %s (cnt %d use %d exp %ld)\n",
					host->h_name, atomic_read(&host->h_count),
					host->h_inuse, host->h_expires);
			}
		}
	}
}

/*
 * Garbage collect any unused NLM hosts.
 * This GC combines reference counting for async operations with
 * mark & sweep for resources held by remote clients.
 */
static void
nlm_gc_hosts(void)
{
520 521 522
	struct hlist_head *chain;
	struct hlist_node *pos, *next;
	struct nlm_host	*host;
L
Linus Torvalds 已提交
523 524

	dprintk("lockd: host garbage collection\n");
525 526
	for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
		hlist_for_each_entry(host, pos, chain, h_hash)
L
Linus Torvalds 已提交
527 528 529 530 531 532
			host->h_inuse = 0;
	}

	/* Mark all hosts that hold locks, blocks or shares */
	nlmsvc_mark_resources();

533 534
	for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
		hlist_for_each_entry_safe(host, pos, next, chain, h_hash) {
L
Linus Torvalds 已提交
535 536 537 538 539 540 541 542
			if (atomic_read(&host->h_count) || host->h_inuse
			 || time_before(jiffies, host->h_expires)) {
				dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n",
					host->h_name, atomic_read(&host->h_count),
					host->h_inuse, host->h_expires);
				continue;
			}
			dprintk("lockd: delete host %s\n", host->h_name);
543
			hlist_del_init(&host->h_hash);
544

545
			nlm_destroy_host(host);
L
Linus Torvalds 已提交
546 547 548 549 550 551 552
			nrhosts--;
		}
	}

	next_gc = jiffies + NLM_HOST_COLLECT;
}

553 554 555 556 557

/*
 * Manage NSM handles
 */
static LIST_HEAD(nsm_handles);
558
static DEFINE_SPINLOCK(nsm_lock);
559

560 561 562 563
static struct nsm_handle *nsm_find(const struct sockaddr_in *sin,
				   const char *hostname,
				   const size_t hostname_len,
				   const int create)
564 565
{
	struct nsm_handle *nsm = NULL;
J
J. Bruce Fields 已提交
566
	struct nsm_handle *pos;
567 568 569 570 571 572 573 574

	if (!sin)
		return NULL;

	if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
		if (printk_ratelimit()) {
			printk(KERN_WARNING "Invalid hostname \"%.*s\" "
					    "in NFS lock request\n",
575
				(int)hostname_len, hostname);
576 577 578 579
		}
		return NULL;
	}

580 581
retry:
	spin_lock(&nsm_lock);
J
J. Bruce Fields 已提交
582
	list_for_each_entry(pos, &nsm_handles, sm_link) {
583

584
		if (hostname && nsm_use_hostnames) {
J
J. Bruce Fields 已提交
585 586
			if (strlen(pos->sm_name) != hostname_len
			 || memcmp(pos->sm_name, hostname, hostname_len))
587
				continue;
588
		} else if (!nlm_cmp_addr(nsm_addr(pos), (struct sockaddr *)sin))
589
			continue;
J
J. Bruce Fields 已提交
590
		atomic_inc(&pos->sm_count);
591
		kfree(nsm);
J
J. Bruce Fields 已提交
592
		nsm = pos;
593
		goto found;
594
	}
595 596 597
	if (nsm) {
		list_add(&nsm->sm_link, &nsm_handles);
		goto found;
598
	}
599 600 601 602
	spin_unlock(&nsm_lock);

	if (!create)
		return NULL;
603 604

	nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL);
J
J. Bruce Fields 已提交
605
	if (nsm == NULL)
606 607
		return NULL;

608 609
	memcpy(nsm_addr(nsm), sin, sizeof(*sin));
	nsm->sm_addrlen = sizeof(*sin);
J
J. Bruce Fields 已提交
610 611 612
	nsm->sm_name = (char *) (nsm + 1);
	memcpy(nsm->sm_name, hostname, hostname_len);
	nsm->sm_name[hostname_len] = '\0';
613 614
	nlm_display_address((struct sockaddr *)&nsm->sm_addr,
				nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf));
J
J. Bruce Fields 已提交
615
	atomic_set(&nsm->sm_count, 1);
616
	goto retry;
J
J. Bruce Fields 已提交
617

618 619
found:
	spin_unlock(&nsm_lock);
620 621 622 623 624 625 626 627 628 629 630
	return nsm;
}

/*
 * Release an NSM handle
 */
void
nsm_release(struct nsm_handle *nsm)
{
	if (!nsm)
		return;
631
	if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) {
632
		list_del(&nsm->sm_link);
633
		spin_unlock(&nsm_lock);
634
		kfree(nsm);
635 636
	}
}