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