1 /* 2 * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ 3 * Authors: Doug Rabson <dfr@rabson.org> 4 * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* 29 * Copyright 2026 Edgecast Cloud LLC. 30 * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 31 * Copyright (c) 2012 by Delphix. All rights reserved. 32 */ 33 34 /* 35 * NFS LockManager, start/stop, support functions, etc. 36 * Most of the interesting code is here. 37 * 38 * Source code derived from FreeBSD nlm_prot_impl.c 39 */ 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/thread.h> 44 #include <sys/fcntl.h> 45 #include <sys/flock.h> 46 #include <sys/mount.h> 47 #include <sys/priv.h> 48 #include <sys/proc.h> 49 #include <sys/share.h> 50 #include <sys/socket.h> 51 #include <sys/syscall.h> 52 #include <sys/syslog.h> 53 #include <sys/systm.h> 54 #include <sys/class.h> 55 #include <sys/unistd.h> 56 #include <sys/vnode.h> 57 #include <sys/vfs.h> 58 #include <sys/queue.h> 59 #include <sys/bitmap.h> 60 #include <sys/sdt.h> 61 #include <netinet/in.h> 62 63 #include <rpc/rpc.h> 64 #include <rpc/xdr.h> 65 #include <rpc/pmap_prot.h> 66 #include <rpc/pmap_clnt.h> 67 #include <rpc/rpcb_prot.h> 68 69 #include <rpcsvc/nlm_prot.h> 70 #include <rpcsvc/sm_inter.h> 71 #include <rpcsvc/nsm_addr.h> 72 73 #include <nfs/nfs.h> 74 #include <nfs/nfs_clnt.h> 75 #include <nfs/export.h> 76 #include <nfs/rnode.h> 77 #include <nfs/lm.h> 78 79 #include "nlm_impl.h" 80 81 struct nlm_knc { 82 struct knetconfig n_knc; 83 const char *n_netid; 84 }; 85 86 /* 87 * Number of attempts NLM tries to obtain RPC binding 88 * of local statd. 89 */ 90 #define NLM_NSM_RPCBIND_RETRIES 10 91 92 /* 93 * Timeout (in seconds) NLM waits before making another 94 * attempt to obtain RPC binding of local statd. 95 */ 96 #define NLM_NSM_RPCBIND_TIMEOUT 5 97 98 /* 99 * Total number of sysids in NLM sysid bitmap 100 */ 101 #define NLM_BMAP_NITEMS (LM_SYSID_MAX + 1) 102 103 /* 104 * Number of ulong_t words in bitmap that is used 105 * for allocation of sysid numbers. 106 */ 107 #define NLM_BMAP_WORDS (NLM_BMAP_NITEMS / BT_NBIPUL) 108 109 /* 110 * Given an integer x, the macro returns 111 * -1 if x is negative, 112 * 0 if x is zero 113 * 1 if x is positive 114 */ 115 #define SIGN(x) (((x) > 0) - ((x) < 0)) 116 117 #define ARRSIZE(arr) (sizeof (arr) / sizeof ((arr)[0])) 118 #define NLM_KNCS ARRSIZE(nlm_netconfigs) 119 120 krwlock_t lm_lck; 121 122 /* 123 * Zero timeout for asynchronous NLM RPC operations 124 */ 125 static const struct timeval nlm_rpctv_zero = { 0, 0 }; 126 127 /* 128 * List of all Zone globals nlm_globals instences 129 * linked together. 130 */ 131 static struct nlm_globals_list nlm_zones_list; /* (g) */ 132 133 /* 134 * NLM kmem caches 135 */ 136 static struct kmem_cache *nlm_hosts_cache = NULL; 137 static struct kmem_cache *nlm_vhold_cache = NULL; 138 139 /* 140 * A bitmap for allocation of new sysids. 141 * Sysid is a unique number between LM_SYSID 142 * and LM_SYSID_MAX. Sysid represents unique remote 143 * host that does file locks on the given host. 144 */ 145 static ulong_t nlm_sysid_bmap[NLM_BMAP_WORDS]; /* (g) */ 146 static int nlm_sysid_nidx; /* (g) */ 147 148 /* 149 * RPC service registration for all transports 150 */ 151 static SVC_CALLOUT nlm_svcs[] = { 152 { NLM_PROG, 4, 4, nlm_prog_4 }, /* NLM4_VERS */ 153 { NLM_PROG, 1, 3, nlm_prog_3 } /* NLM_VERS - NLM_VERSX */ 154 }; 155 156 static SVC_CALLOUT_TABLE nlm_sct = { 157 ARRSIZE(nlm_svcs), 158 FALSE, 159 nlm_svcs 160 }; 161 162 /* 163 * Static table of all netid/knetconfig network 164 * lock manager can work with. nlm_netconfigs table 165 * is used when we need to get valid knetconfig by 166 * netid and vice versa. 167 * 168 * Knetconfigs are activated either by the call from 169 * user-space lockd daemon (server side) or by taking 170 * knetconfig from NFS mountinfo (client side) 171 */ 172 static struct nlm_knc nlm_netconfigs[] = { /* (g) */ 173 /* UDP */ 174 { 175 { NC_TPI_CLTS, NC_INET, NC_UDP, NODEV }, 176 "udp", 177 }, 178 /* TCP */ 179 { 180 { NC_TPI_COTS_ORD, NC_INET, NC_TCP, NODEV }, 181 "tcp", 182 }, 183 /* UDP over IPv6 */ 184 { 185 { NC_TPI_CLTS, NC_INET6, NC_UDP, NODEV }, 186 "udp6", 187 }, 188 /* TCP over IPv6 */ 189 { 190 { NC_TPI_COTS_ORD, NC_INET6, NC_TCP, NODEV }, 191 "tcp6", 192 }, 193 /* ticlts (loopback over UDP) */ 194 { 195 { NC_TPI_CLTS, NC_LOOPBACK, NC_NOPROTO, NODEV }, 196 "ticlts", 197 }, 198 /* ticotsord (loopback over TCP) */ 199 { 200 { NC_TPI_COTS_ORD, NC_LOOPBACK, NC_NOPROTO, NODEV }, 201 "ticotsord", 202 }, 203 }; 204 205 /* 206 * NLM misc. function 207 */ 208 static void nlm_copy_netbuf(struct netbuf *, struct netbuf *); 209 static int nlm_netbuf_addrs_cmp(struct netbuf *, struct netbuf *); 210 static void nlm_kmem_reclaim(void *); 211 static void nlm_pool_shutdown(void); 212 static void nlm_suspend_zone(struct nlm_globals *); 213 static void nlm_resume_zone(struct nlm_globals *); 214 static void nlm_nsm_clnt_init(CLIENT *, struct nlm_nsm *); 215 static void nlm_netbuf_to_netobj(struct netbuf *, int *, netobj *); 216 217 /* 218 * NLM thread functions 219 */ 220 static void nlm_gc(struct nlm_globals *); 221 static void nlm_reclaimer(struct nlm_host *); 222 223 /* 224 * NLM NSM functions 225 */ 226 static int nlm_init_local_knc(struct knetconfig *); 227 static int nlm_nsm_init_local(struct nlm_nsm *); 228 static int nlm_nsm_init(struct nlm_nsm *, struct knetconfig *, struct netbuf *); 229 static void nlm_nsm_fini(struct nlm_nsm *); 230 static enum clnt_stat nlm_nsm_simu_crash(struct nlm_nsm *); 231 static enum clnt_stat nlm_nsm_stat(struct nlm_nsm *, int32_t *); 232 static enum clnt_stat nlm_nsm_mon(struct nlm_nsm *, char *, uint16_t); 233 static enum clnt_stat nlm_nsm_unmon(struct nlm_nsm *, char *); 234 235 /* 236 * NLM host functions 237 */ 238 static int nlm_host_ctor(void *, void *, int); 239 static void nlm_host_dtor(void *, void *); 240 static void nlm_host_destroy(struct nlm_host *); 241 static struct nlm_host *nlm_host_create(char *, const char *, 242 struct knetconfig *, struct netbuf *, struct netbuf *); 243 static struct nlm_host *nlm_host_find_locked(struct nlm_globals *, 244 const char *, struct netbuf *, avl_index_t *); 245 static void nlm_host_unregister(struct nlm_globals *, struct nlm_host *); 246 static void nlm_host_gc_vholds(struct nlm_host *); 247 static bool_t nlm_host_has_srv_locks(struct nlm_host *); 248 static bool_t nlm_host_has_cli_locks(struct nlm_host *); 249 static bool_t nlm_host_has_locks(struct nlm_host *); 250 251 /* 252 * NLM vhold functions 253 */ 254 static int nlm_vhold_ctor(void *, void *, int); 255 static void nlm_vhold_dtor(void *, void *); 256 static void nlm_vhold_destroy(struct nlm_host *, 257 struct nlm_vhold *); 258 static bool_t nlm_vhold_busy(struct nlm_host *, struct nlm_vhold *); 259 static void nlm_vhold_clean(struct nlm_vhold *, int); 260 261 /* 262 * NLM client/server sleeping locks/share reservation functions 263 */ 264 struct nlm_slreq *nlm_slreq_find_locked(struct nlm_host *, 265 struct nlm_vhold *, struct flock64 *); 266 static struct nlm_shres *nlm_shres_create_item(struct shrlock *, vnode_t *); 267 static void nlm_shres_destroy_item(struct nlm_shres *); 268 static bool_t nlm_shres_equal(struct shrlock *, struct shrlock *); 269 270 /* 271 * NLM initialization functions. 272 */ 273 void 274 nlm_init(void) 275 { 276 nlm_hosts_cache = kmem_cache_create("nlm_host_cache", 277 sizeof (struct nlm_host), 0, nlm_host_ctor, nlm_host_dtor, 278 nlm_kmem_reclaim, NULL, NULL, 0); 279 280 nlm_vhold_cache = kmem_cache_create("nlm_vhold_cache", 281 sizeof (struct nlm_vhold), 0, nlm_vhold_ctor, nlm_vhold_dtor, 282 NULL, NULL, NULL, 0); 283 284 nlm_rpc_init(); 285 TAILQ_INIT(&nlm_zones_list); 286 287 /* initialize sysids bitmap */ 288 bzero(nlm_sysid_bmap, sizeof (nlm_sysid_bmap)); 289 nlm_sysid_nidx = 1; 290 291 /* 292 * Reserv the sysid #0, because it's associated 293 * with local locks only. Don't let to allocate 294 * it for remote locks. 295 */ 296 BT_SET(nlm_sysid_bmap, 0); 297 } 298 299 void 300 nlm_globals_register(struct nlm_globals *g) 301 { 302 rw_enter(&lm_lck, RW_WRITER); 303 TAILQ_INSERT_TAIL(&nlm_zones_list, g, nlm_link); 304 rw_exit(&lm_lck); 305 } 306 307 void 308 nlm_globals_unregister(struct nlm_globals *g) 309 { 310 rw_enter(&lm_lck, RW_WRITER); 311 TAILQ_REMOVE(&nlm_zones_list, g, nlm_link); 312 rw_exit(&lm_lck); 313 } 314 315 /* ARGSUSED */ 316 static void 317 nlm_kmem_reclaim(void *cdrarg) 318 { 319 struct nlm_globals *g; 320 321 rw_enter(&lm_lck, RW_READER); 322 TAILQ_FOREACH(g, &nlm_zones_list, nlm_link) 323 cv_broadcast(&g->nlm_gc_sched_cv); 324 325 rw_exit(&lm_lck); 326 } 327 328 /* 329 * NLM garbage collector thread (GC). 330 * 331 * NLM GC periodically checks whether there're any host objects 332 * that can be cleaned up. It also releases stale vnodes that 333 * live on the server side (under protection of vhold objects). 334 * 335 * NLM host objects are cleaned up from GC thread because 336 * operations helping us to determine whether given host has 337 * any locks can be quite expensive and it's not good to call 338 * them every time the very last reference to the host is dropped. 339 * Thus we use "lazy" approach for hosts cleanup. 340 * 341 * The work of GC is to release stale vnodes on the server side 342 * and destroy hosts that haven't any locks and any activity for 343 * some time (i.e. idle hosts). 344 */ 345 static void 346 nlm_gc(struct nlm_globals *g) 347 { 348 struct nlm_host *hostp; 349 clock_t now, idle_period; 350 351 idle_period = SEC_TO_TICK(g->cn_idle_tmo); 352 mutex_enter(&g->lock); 353 for (;;) { 354 /* 355 * GC thread can be explicitly scheduled from 356 * memory reclamation function. 357 */ 358 (void) cv_timedwait(&g->nlm_gc_sched_cv, &g->lock, 359 ddi_get_lbolt() + idle_period); 360 361 /* 362 * NLM is shutting down, time to die. 363 */ 364 if (g->run_status == NLM_ST_STOPPING) 365 break; 366 367 now = ddi_get_lbolt(); 368 DTRACE_PROBE2(gc__start, struct nlm_globals *, g, 369 clock_t, now); 370 371 /* 372 * Find all obviously unused vholds and destroy them. 373 */ 374 for (hostp = avl_first(&g->nlm_hosts_tree); hostp != NULL; 375 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp)) { 376 struct nlm_vhold *nvp; 377 378 mutex_enter(&hostp->nh_lock); 379 380 nvp = TAILQ_FIRST(&hostp->nh_vholds_list); 381 while (nvp != NULL) { 382 struct nlm_vhold *new_nvp; 383 384 new_nvp = TAILQ_NEXT(nvp, nv_link); 385 386 /* 387 * If these conditions are met, the vhold is 388 * obviously unused and we will destroy it. In 389 * a case either v_filocks and/or v_shrlocks is 390 * non-NULL the vhold might still be unused by 391 * the host, but it is expensive to check that. 392 * We defer such check until the host is idle. 393 * The expensive check is done below without 394 * the global lock held. 395 */ 396 if (nvp->nv_refcnt == 0 && 397 nvp->nv_vp->v_filocks == NULL && 398 nvp->nv_vp->v_shrlocks == NULL) { 399 nlm_vhold_destroy(hostp, nvp); 400 } 401 402 nvp = new_nvp; 403 } 404 405 mutex_exit(&hostp->nh_lock); 406 } 407 408 /* 409 * Handle all hosts that are unused at the moment 410 * until we meet one with idle timeout in future. 411 */ 412 while ((hostp = TAILQ_FIRST(&g->nlm_idle_hosts)) != NULL) { 413 bool_t has_locks; 414 415 if (hostp->nh_idle_timeout > now) 416 break; 417 418 /* 419 * Drop global lock while doing expensive work 420 * on this host. We'll re-check any conditions 421 * that might change after retaking the global 422 * lock. 423 */ 424 mutex_exit(&g->lock); 425 mutex_enter(&hostp->nh_lock); 426 427 /* 428 * nlm_globals lock was dropped earlier because 429 * garbage collecting of vholds and checking whether 430 * host has any locks/shares are expensive operations. 431 */ 432 nlm_host_gc_vholds(hostp); 433 has_locks = nlm_host_has_locks(hostp); 434 435 mutex_exit(&hostp->nh_lock); 436 mutex_enter(&g->lock); 437 438 /* 439 * While we were doing expensive operations 440 * outside of nlm_globals critical section, 441 * somebody could take the host and remove it 442 * from the idle list. Whether its been 443 * reinserted or not, our information about 444 * the host is outdated, and we should take no 445 * further action. 446 */ 447 if ((hostp->nh_flags & NLM_NH_INIDLE) == 0 || 448 hostp->nh_idle_timeout > now) 449 continue; 450 451 /* 452 * If the host has locks we have to renew the 453 * host's timeout and put it at the end of LRU 454 * list. 455 */ 456 if (has_locks) { 457 TAILQ_REMOVE(&g->nlm_idle_hosts, 458 hostp, nh_link); 459 hostp->nh_idle_timeout = now + idle_period; 460 TAILQ_INSERT_TAIL(&g->nlm_idle_hosts, 461 hostp, nh_link); 462 continue; 463 } 464 465 /* 466 * We're here if all the following conditions hold: 467 * 1) Host hasn't any locks or share reservations 468 * 2) Host is unused 469 * 3) Host wasn't touched by anyone at least for 470 * g->cn_idle_tmo seconds. 471 * 472 * So, now we can destroy it. 473 */ 474 nlm_host_unregister(g, hostp); 475 mutex_exit(&g->lock); 476 477 nlm_host_unmonitor(g, hostp); 478 nlm_host_destroy(hostp); 479 mutex_enter(&g->lock); 480 if (g->run_status == NLM_ST_STOPPING) 481 break; 482 483 } 484 485 DTRACE_PROBE(gc__end); 486 } 487 488 DTRACE_PROBE1(gc__exit, struct nlm_globals *, g); 489 490 /* Let others know that GC has died */ 491 g->nlm_gc_thread = NULL; 492 mutex_exit(&g->lock); 493 494 cv_broadcast(&g->nlm_gc_finish_cv); 495 zthread_exit(); 496 } 497 498 /* 499 * Thread reclaim locks/shares acquired by the client side 500 * on the given server represented by hostp. 501 */ 502 static void 503 nlm_reclaimer(struct nlm_host *hostp) 504 { 505 struct nlm_globals *g; 506 507 mutex_enter(&hostp->nh_lock); 508 hostp->nh_reclaimer = curthread; 509 mutex_exit(&hostp->nh_lock); 510 511 g = zone_getspecific(nlm_zone_key, curzone); 512 nlm_reclaim_client(g, hostp); 513 514 mutex_enter(&hostp->nh_lock); 515 hostp->nh_flags &= ~NLM_NH_RECLAIM; 516 hostp->nh_reclaimer = NULL; 517 cv_broadcast(&hostp->nh_recl_cv); 518 mutex_exit(&hostp->nh_lock); 519 520 /* 521 * Host was explicitly referenced before 522 * nlm_reclaim() was called, release it 523 * here. 524 */ 525 nlm_host_release(g, hostp); 526 zthread_exit(); 527 } 528 529 /* 530 * Copy a struct netobj. (see xdr.h) 531 */ 532 void 533 nlm_copy_netobj(struct netobj *dst, struct netobj *src) 534 { 535 dst->n_len = src->n_len; 536 dst->n_bytes = kmem_alloc(src->n_len, KM_SLEEP); 537 bcopy(src->n_bytes, dst->n_bytes, src->n_len); 538 } 539 540 /* 541 * An NLM specificw replacement for clnt_call(). 542 * nlm_clnt_call() is used by all RPC functions generated 543 * from nlm_prot.x specification. The function is aware 544 * about some pitfalls of NLM RPC procedures and has a logic 545 * that handles them properly. 546 */ 547 enum clnt_stat 548 nlm_clnt_call(CLIENT *clnt, rpcproc_t procnum, xdrproc_t xdr_args, 549 caddr_t argsp, xdrproc_t xdr_result, caddr_t resultp, struct timeval wait) 550 { 551 k_sigset_t oldmask; 552 enum clnt_stat stat; 553 bool_t sig_blocked = FALSE; 554 555 /* 556 * If NLM RPC procnum is one of the NLM _RES procedures 557 * that are used to reply to asynchronous NLM RPC 558 * (MSG calls), explicitly set RPC timeout to zero. 559 * Client doesn't send a reply to RES procedures, so 560 * we don't need to wait anything. 561 * 562 * NOTE: we ignore NLM4_*_RES procnums because they are 563 * equal to NLM_*_RES numbers. 564 */ 565 if (procnum >= NLM_TEST_RES && procnum <= NLM_GRANTED_RES) 566 wait = nlm_rpctv_zero; 567 568 /* 569 * We need to block signals in case of NLM_CANCEL RPC 570 * in order to prevent interruption of network RPC 571 * calls. 572 */ 573 if (procnum == NLM_CANCEL) { 574 k_sigset_t newmask; 575 576 sigfillset(&newmask); 577 sigreplace(&newmask, &oldmask); 578 sig_blocked = TRUE; 579 } 580 581 stat = clnt_call(clnt, procnum, xdr_args, 582 argsp, xdr_result, resultp, wait); 583 584 /* 585 * Restore signal mask back if signals were blocked 586 */ 587 if (sig_blocked) 588 sigreplace(&oldmask, (k_sigset_t *)NULL); 589 590 return (stat); 591 } 592 593 /* 594 * Suspend NLM client/server in the given zone. 595 * 596 * During suspend operation we mark those hosts 597 * that have any locks with NLM_NH_SUSPEND flags, 598 * so that they can be checked later, when resume 599 * operation occurs. 600 */ 601 static void 602 nlm_suspend_zone(struct nlm_globals *g) 603 { 604 struct nlm_host *hostp; 605 struct nlm_host_list all_hosts; 606 607 /* 608 * Note that while we're doing suspend, GC thread is active 609 * and it can destroy some hosts while we're walking through 610 * the hosts tree. To prevent that and make suspend logic 611 * a bit more simple we put all hosts to local "all_hosts" 612 * list and increment reference counter of each host. 613 * This guaranties that no hosts will be released while 614 * we're doing suspend. 615 * NOTE: reference of each host must be dropped during 616 * resume operation. 617 */ 618 TAILQ_INIT(&all_hosts); 619 mutex_enter(&g->lock); 620 for (hostp = avl_first(&g->nlm_hosts_tree); hostp != NULL; 621 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp)) { 622 /* 623 * If host is idle, remove it from idle list and 624 * clear idle flag. That is done to prevent GC 625 * from touching this host. 626 */ 627 if (hostp->nh_flags & NLM_NH_INIDLE) { 628 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link); 629 hostp->nh_flags &= ~NLM_NH_INIDLE; 630 } 631 632 hostp->nh_refs++; 633 TAILQ_INSERT_TAIL(&all_hosts, hostp, nh_link); 634 } 635 636 /* 637 * Now we can walk through all hosts on the system 638 * with zone globals lock released. The fact the 639 * we have taken a reference to each host guaranties 640 * that no hosts can be destroyed during that process. 641 */ 642 mutex_exit(&g->lock); 643 while ((hostp = TAILQ_FIRST(&all_hosts)) != NULL) { 644 mutex_enter(&hostp->nh_lock); 645 if (nlm_host_has_locks(hostp)) 646 hostp->nh_flags |= NLM_NH_SUSPEND; 647 648 mutex_exit(&hostp->nh_lock); 649 TAILQ_REMOVE(&all_hosts, hostp, nh_link); 650 } 651 } 652 653 /* 654 * Resume NLM hosts for the given zone. 655 * 656 * nlm_resume_zone() is called after hosts were suspended 657 * (see nlm_suspend_zone) and its main purpose to check 658 * whether remote locks owned by hosts are still in consistent 659 * state. If they aren't, resume function tries to reclaim 660 * locks (for client side hosts) and clean locks (for 661 * server side hosts). 662 */ 663 static void 664 nlm_resume_zone(struct nlm_globals *g) 665 { 666 struct nlm_host *hostp, *h_next; 667 668 mutex_enter(&g->lock); 669 hostp = avl_first(&g->nlm_hosts_tree); 670 671 /* 672 * In nlm_suspend_zone() the reference counter of each 673 * host was incremented, so we can safely iterate through 674 * all hosts without worrying that any host we touch will 675 * be removed at the moment. 676 */ 677 while (hostp != NULL) { 678 struct nlm_nsm nsm; 679 enum clnt_stat stat; 680 int32_t sm_state; 681 int error; 682 bool_t resume_failed = FALSE; 683 684 h_next = AVL_NEXT(&g->nlm_hosts_tree, hostp); 685 mutex_exit(&g->lock); 686 687 DTRACE_PROBE1(resume__host, struct nlm_host *, hostp); 688 689 /* 690 * Suspend operation marked that the host doesn't 691 * have any locks. Skip it. 692 */ 693 if (!(hostp->nh_flags & NLM_NH_SUSPEND)) 694 goto cycle_end; 695 696 error = nlm_nsm_init(&nsm, &hostp->nh_knc, &hostp->nh_addr); 697 if (error != 0) { 698 NLM_ERR("Resume: Failed to contact to NSM of host %s " 699 "[error=%d]\n", hostp->nh_name, error); 700 resume_failed = TRUE; 701 goto cycle_end; 702 } 703 704 stat = nlm_nsm_stat(&nsm, &sm_state); 705 if (stat != RPC_SUCCESS) { 706 NLM_ERR("Resume: Failed to call SM_STAT operation for " 707 "host %s [stat=%d]\n", hostp->nh_name, stat); 708 resume_failed = TRUE; 709 nlm_nsm_fini(&nsm); 710 goto cycle_end; 711 } 712 713 if (sm_state != hostp->nh_state) { 714 /* 715 * Current SM state of the host isn't equal 716 * to the one host had when it was suspended. 717 * Probably it was rebooted. Try to reclaim 718 * locks if the host has any on its client side. 719 * Also try to clean up its server side locks 720 * (if the host has any). 721 */ 722 nlm_host_notify_client(hostp, sm_state); 723 nlm_host_notify_server(hostp, sm_state); 724 } 725 726 nlm_nsm_fini(&nsm); 727 728 cycle_end: 729 if (resume_failed) { 730 /* 731 * Resume failed for the given host. 732 * Just clean up all resources it owns. 733 */ 734 nlm_host_notify_server(hostp, 0); 735 nlm_client_cancel_all(g, hostp); 736 } 737 738 hostp->nh_flags &= ~NLM_NH_SUSPEND; 739 nlm_host_release(g, hostp); 740 hostp = h_next; 741 mutex_enter(&g->lock); 742 } 743 744 mutex_exit(&g->lock); 745 } 746 747 /* 748 * NLM functions responsible for operations on NSM handle. 749 */ 750 751 /* 752 * Initialize knetconfig that is used for communication 753 * with local statd via loopback interface. 754 */ 755 static int 756 nlm_init_local_knc(struct knetconfig *knc) 757 { 758 int error; 759 vnode_t *vp; 760 761 bzero(knc, sizeof (*knc)); 762 error = lookupname("/dev/tcp", UIO_SYSSPACE, 763 FOLLOW, NULLVPP, &vp); 764 if (error != 0) 765 return (error); 766 767 knc->knc_semantics = NC_TPI_COTS; 768 knc->knc_protofmly = NC_INET; 769 knc->knc_proto = NC_TCP; 770 knc->knc_rdev = vp->v_rdev; 771 VN_RELE(vp); 772 773 774 return (0); 775 } 776 777 /* 778 * Initialize NSM handle that will be used to talk 779 * to local statd via loopback interface. 780 */ 781 static int 782 nlm_nsm_init_local(struct nlm_nsm *nsm) 783 { 784 int error; 785 struct knetconfig knc; 786 struct sockaddr_in sin; 787 struct netbuf nb; 788 789 error = nlm_init_local_knc(&knc); 790 if (error != 0) 791 return (error); 792 793 bzero(&sin, sizeof (sin)); 794 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 795 sin.sin_family = AF_INET; 796 797 nb.buf = (char *)&sin; 798 nb.len = nb.maxlen = sizeof (sin); 799 800 return (nlm_nsm_init(nsm, &knc, &nb)); 801 } 802 803 /* 804 * Initialize NSM handle used for talking to statd 805 */ 806 static int 807 nlm_nsm_init(struct nlm_nsm *nsm, struct knetconfig *knc, struct netbuf *nb) 808 { 809 enum clnt_stat stat; 810 int error, retries; 811 812 bzero(nsm, sizeof (*nsm)); 813 nsm->ns_knc = *knc; 814 nlm_copy_netbuf(&nsm->ns_addr, nb); 815 816 /* 817 * Try several times to get the port of statd service, 818 * If rpcbind_getaddr returns RPC_PROGNOTREGISTERED, 819 * retry an attempt, but wait for NLM_NSM_RPCBIND_TIMEOUT 820 * seconds berofore. 821 */ 822 for (retries = 0; retries < NLM_NSM_RPCBIND_RETRIES; retries++) { 823 stat = rpcbind_getaddr(&nsm->ns_knc, SM_PROG, 824 SM_VERS, &nsm->ns_addr); 825 if (stat != RPC_SUCCESS) { 826 if (stat == RPC_PROGNOTREGISTERED) { 827 delay(SEC_TO_TICK(NLM_NSM_RPCBIND_TIMEOUT)); 828 continue; 829 } 830 } 831 832 break; 833 } 834 835 if (stat != RPC_SUCCESS) { 836 DTRACE_PROBE2(rpcbind__error, enum clnt_stat, stat, 837 int, retries); 838 error = ENOENT; 839 goto error; 840 } 841 842 /* 843 * Create an RPC handle that'll be used for communication with local 844 * statd using the status monitor protocol. 845 */ 846 error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, SM_PROG, SM_VERS, 847 0, NLM_RPC_RETRIES, zone_kcred(), &nsm->ns_handle); 848 if (error != 0) 849 goto error; 850 851 /* 852 * Create an RPC handle that'll be used for communication with the 853 * local statd using the address registration protocol. 854 */ 855 error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, NSM_ADDR_PROGRAM, 856 NSM_ADDR_V1, 0, NLM_RPC_RETRIES, zone_kcred(), 857 &nsm->ns_addr_handle); 858 if (error != 0) 859 goto error; 860 861 sema_init(&nsm->ns_sem, 1, NULL, SEMA_DEFAULT, NULL); 862 return (0); 863 864 error: 865 kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen); 866 if (nsm->ns_handle) { 867 ASSERT(nsm->ns_handle->cl_auth != NULL); 868 auth_destroy(nsm->ns_handle->cl_auth); 869 CLNT_DESTROY(nsm->ns_handle); 870 } 871 872 return (error); 873 } 874 875 static void 876 nlm_nsm_fini(struct nlm_nsm *nsm) 877 { 878 kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen); 879 if (nsm->ns_addr_handle->cl_auth != NULL) 880 auth_destroy(nsm->ns_addr_handle->cl_auth); 881 CLNT_DESTROY(nsm->ns_addr_handle); 882 nsm->ns_addr_handle = NULL; 883 if (nsm->ns_handle->cl_auth != NULL) 884 auth_destroy(nsm->ns_handle->cl_auth); 885 CLNT_DESTROY(nsm->ns_handle); 886 nsm->ns_handle = NULL; 887 sema_destroy(&nsm->ns_sem); 888 } 889 890 static enum clnt_stat 891 nlm_nsm_simu_crash(struct nlm_nsm *nsm) 892 { 893 enum clnt_stat stat; 894 895 sema_p(&nsm->ns_sem); 896 nlm_nsm_clnt_init(nsm->ns_handle, nsm); 897 stat = sm_simu_crash_1(NULL, NULL, nsm->ns_handle); 898 sema_v(&nsm->ns_sem); 899 900 return (stat); 901 } 902 903 static enum clnt_stat 904 nlm_nsm_stat(struct nlm_nsm *nsm, int32_t *out_stat) 905 { 906 struct sm_name args; 907 struct sm_stat_res res; 908 enum clnt_stat stat; 909 910 args.mon_name = uts_nodename(); 911 bzero(&res, sizeof (res)); 912 913 sema_p(&nsm->ns_sem); 914 nlm_nsm_clnt_init(nsm->ns_handle, nsm); 915 stat = sm_stat_1(&args, &res, nsm->ns_handle); 916 sema_v(&nsm->ns_sem); 917 918 if (stat == RPC_SUCCESS) 919 *out_stat = res.state; 920 921 return (stat); 922 } 923 924 static enum clnt_stat 925 nlm_nsm_mon(struct nlm_nsm *nsm, char *hostname, uint16_t priv) 926 { 927 struct mon args; 928 struct sm_stat_res res; 929 enum clnt_stat stat; 930 931 bzero(&args, sizeof (args)); 932 bzero(&res, sizeof (res)); 933 934 args.mon_id.mon_name = hostname; 935 args.mon_id.my_id.my_name = uts_nodename(); 936 args.mon_id.my_id.my_prog = NLM_PROG; 937 args.mon_id.my_id.my_vers = NLM_SM; 938 args.mon_id.my_id.my_proc = NLM_SM_NOTIFY1; 939 bcopy(&priv, args.priv, sizeof (priv)); 940 941 sema_p(&nsm->ns_sem); 942 nlm_nsm_clnt_init(nsm->ns_handle, nsm); 943 stat = sm_mon_1(&args, &res, nsm->ns_handle); 944 sema_v(&nsm->ns_sem); 945 946 return (stat); 947 } 948 949 static enum clnt_stat 950 nlm_nsm_unmon(struct nlm_nsm *nsm, char *hostname) 951 { 952 struct mon_id args; 953 struct sm_stat res; 954 enum clnt_stat stat; 955 956 bzero(&args, sizeof (args)); 957 bzero(&res, sizeof (res)); 958 959 args.mon_name = hostname; 960 args.my_id.my_name = uts_nodename(); 961 args.my_id.my_prog = NLM_PROG; 962 args.my_id.my_vers = NLM_SM; 963 args.my_id.my_proc = NLM_SM_NOTIFY1; 964 965 sema_p(&nsm->ns_sem); 966 nlm_nsm_clnt_init(nsm->ns_handle, nsm); 967 stat = sm_unmon_1(&args, &res, nsm->ns_handle); 968 sema_v(&nsm->ns_sem); 969 970 return (stat); 971 } 972 973 static enum clnt_stat 974 nlm_nsmaddr_reg(struct nlm_nsm *nsm, char *name, int family, netobj *address) 975 { 976 struct reg1args args = { 0 }; 977 struct reg1res res = { 0 }; 978 enum clnt_stat stat; 979 980 args.family = family; 981 args.name = name; 982 args.address = *address; 983 984 sema_p(&nsm->ns_sem); 985 nlm_nsm_clnt_init(nsm->ns_addr_handle, nsm); 986 stat = nsmaddrproc1_reg_1(&args, &res, nsm->ns_addr_handle); 987 sema_v(&nsm->ns_sem); 988 989 return (stat); 990 } 991 992 /* 993 * Get NLM vhold object corresponding to vnode "vp". 994 * If no such object was found, create a new one. 995 * 996 * The purpose of this function is to associate vhold 997 * object with given vnode, so that: 998 * 1) vnode is hold (VN_HOLD) while vhold object is alive. 999 * 2) host has a track of all vnodes it touched by lock 1000 * or share operations. These vnodes are accessible 1001 * via collection of vhold objects. 1002 */ 1003 struct nlm_vhold * 1004 nlm_vhold_get(struct nlm_host *hostp, vnode_t *vp) 1005 { 1006 struct nlm_vhold *nvp, *new_nvp = NULL; 1007 1008 mutex_enter(&hostp->nh_lock); 1009 nvp = nlm_vhold_find_locked(hostp, vp); 1010 if (nvp != NULL) 1011 goto out; 1012 1013 /* nlm_vhold wasn't found, then create a new one */ 1014 mutex_exit(&hostp->nh_lock); 1015 new_nvp = kmem_cache_alloc(nlm_vhold_cache, KM_SLEEP); 1016 1017 /* 1018 * Check if another thread has already 1019 * created the same nlm_vhold. 1020 */ 1021 mutex_enter(&hostp->nh_lock); 1022 nvp = nlm_vhold_find_locked(hostp, vp); 1023 if (nvp == NULL) { 1024 nvp = new_nvp; 1025 new_nvp = NULL; 1026 1027 TAILQ_INIT(&nvp->nv_slreqs); 1028 nvp->nv_vp = vp; 1029 nvp->nv_refcnt = 1; 1030 VN_HOLD(nvp->nv_vp); 1031 1032 VERIFY(mod_hash_insert(hostp->nh_vholds_by_vp, 1033 (mod_hash_key_t)vp, (mod_hash_val_t)nvp) == 0); 1034 TAILQ_INSERT_TAIL(&hostp->nh_vholds_list, nvp, nv_link); 1035 } 1036 1037 out: 1038 mutex_exit(&hostp->nh_lock); 1039 if (new_nvp != NULL) 1040 kmem_cache_free(nlm_vhold_cache, new_nvp); 1041 1042 return (nvp); 1043 } 1044 1045 /* 1046 * Drop a reference to vhold object nvp. 1047 */ 1048 void 1049 nlm_vhold_release(struct nlm_host *hostp, struct nlm_vhold *nvp) 1050 { 1051 if (nvp == NULL) 1052 return; 1053 1054 mutex_enter(&hostp->nh_lock); 1055 ASSERT(nvp->nv_refcnt > 0); 1056 nvp->nv_refcnt--; 1057 1058 /* 1059 * If these conditions are met, the vhold is obviously unused and we 1060 * will destroy it. In a case either v_filocks and/or v_shrlocks is 1061 * non-NULL the vhold might still be unused by the host, but it is 1062 * expensive to check that. We defer such check until the host is 1063 * idle. The expensive check is done in the NLM garbage collector. 1064 */ 1065 if (nvp->nv_refcnt == 0 && 1066 nvp->nv_vp->v_filocks == NULL && 1067 nvp->nv_vp->v_shrlocks == NULL) { 1068 nlm_vhold_destroy(hostp, nvp); 1069 } 1070 1071 mutex_exit(&hostp->nh_lock); 1072 } 1073 1074 /* 1075 * Clean all locks and share reservations on the 1076 * given vhold object that were acquired by the 1077 * given sysid 1078 */ 1079 static void 1080 nlm_vhold_clean(struct nlm_vhold *nvp, int sysid) 1081 { 1082 cleanlocks(nvp->nv_vp, IGN_PID, sysid); 1083 cleanshares_by_sysid(nvp->nv_vp, sysid); 1084 } 1085 1086 static void 1087 nlm_vhold_destroy(struct nlm_host *hostp, struct nlm_vhold *nvp) 1088 { 1089 ASSERT(MUTEX_HELD(&hostp->nh_lock)); 1090 1091 ASSERT(nvp->nv_refcnt == 0); 1092 ASSERT(TAILQ_EMPTY(&nvp->nv_slreqs)); 1093 1094 VERIFY(mod_hash_remove(hostp->nh_vholds_by_vp, 1095 (mod_hash_key_t)nvp->nv_vp, 1096 (mod_hash_val_t)&nvp) == 0); 1097 1098 TAILQ_REMOVE(&hostp->nh_vholds_list, nvp, nv_link); 1099 VN_RELE(nvp->nv_vp); 1100 nvp->nv_vp = NULL; 1101 1102 kmem_cache_free(nlm_vhold_cache, nvp); 1103 } 1104 1105 /* 1106 * Return TRUE if the given vhold is busy. 1107 * Vhold object is considered to be "busy" when 1108 * all the following conditions hold: 1109 * 1) No one uses it at the moment; 1110 * 2) It hasn't any locks; 1111 * 3) It hasn't any share reservations; 1112 */ 1113 static bool_t 1114 nlm_vhold_busy(struct nlm_host *hostp, struct nlm_vhold *nvp) 1115 { 1116 vnode_t *vp; 1117 int sysid; 1118 1119 ASSERT(MUTEX_HELD(&hostp->nh_lock)); 1120 1121 if (nvp->nv_refcnt > 0) 1122 return (TRUE); 1123 1124 vp = nvp->nv_vp; 1125 sysid = hostp->nh_sysid; 1126 if (flk_has_remote_locks_for_sysid(vp, sysid) || 1127 shr_has_remote_shares(vp, sysid)) 1128 return (TRUE); 1129 1130 return (FALSE); 1131 } 1132 1133 /* ARGSUSED */ 1134 static int 1135 nlm_vhold_ctor(void *datap, void *cdrarg, int kmflags) 1136 { 1137 struct nlm_vhold *nvp = (struct nlm_vhold *)datap; 1138 1139 bzero(nvp, sizeof (*nvp)); 1140 return (0); 1141 } 1142 1143 /* ARGSUSED */ 1144 static void 1145 nlm_vhold_dtor(void *datap, void *cdrarg) 1146 { 1147 struct nlm_vhold *nvp = (struct nlm_vhold *)datap; 1148 1149 ASSERT(nvp->nv_refcnt == 0); 1150 ASSERT(TAILQ_EMPTY(&nvp->nv_slreqs)); 1151 ASSERT(nvp->nv_vp == NULL); 1152 } 1153 1154 struct nlm_vhold * 1155 nlm_vhold_find_locked(struct nlm_host *hostp, const vnode_t *vp) 1156 { 1157 struct nlm_vhold *nvp = NULL; 1158 1159 ASSERT(MUTEX_HELD(&hostp->nh_lock)); 1160 (void) mod_hash_find(hostp->nh_vholds_by_vp, 1161 (mod_hash_key_t)vp, 1162 (mod_hash_val_t)&nvp); 1163 1164 if (nvp != NULL) 1165 nvp->nv_refcnt++; 1166 1167 return (nvp); 1168 } 1169 1170 /* 1171 * NLM host functions 1172 */ 1173 static void 1174 nlm_copy_netbuf(struct netbuf *dst, struct netbuf *src) 1175 { 1176 ASSERT(src->len <= src->maxlen); 1177 1178 dst->maxlen = src->maxlen; 1179 dst->len = src->len; 1180 dst->buf = kmem_zalloc(src->maxlen, KM_SLEEP); 1181 bcopy(src->buf, dst->buf, src->len); 1182 } 1183 1184 /* ARGSUSED */ 1185 static int 1186 nlm_host_ctor(void *datap, void *cdrarg, int kmflags) 1187 { 1188 struct nlm_host *hostp = (struct nlm_host *)datap; 1189 1190 bzero(hostp, sizeof (*hostp)); 1191 return (0); 1192 } 1193 1194 /* ARGSUSED */ 1195 static void 1196 nlm_host_dtor(void *datap, void *cdrarg) 1197 { 1198 struct nlm_host *hostp = (struct nlm_host *)datap; 1199 ASSERT(hostp->nh_refs == 0); 1200 } 1201 1202 static void 1203 nlm_host_unregister(struct nlm_globals *g, struct nlm_host *hostp) 1204 { 1205 ASSERT(hostp->nh_refs == 0); 1206 ASSERT(hostp->nh_flags & NLM_NH_INIDLE); 1207 1208 avl_remove(&g->nlm_hosts_tree, hostp); 1209 VERIFY(mod_hash_remove(g->nlm_hosts_hash, 1210 (mod_hash_key_t)(uintptr_t)hostp->nh_sysid, 1211 (mod_hash_val_t)&hostp) == 0); 1212 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link); 1213 hostp->nh_flags &= ~NLM_NH_INIDLE; 1214 } 1215 1216 /* 1217 * Free resources used by a host. This is called after the reference 1218 * count has reached zero so it doesn't need to worry about locks. 1219 */ 1220 static void 1221 nlm_host_destroy(struct nlm_host *hostp) 1222 { 1223 ASSERT(hostp->nh_name != NULL); 1224 ASSERT(hostp->nh_netid != NULL); 1225 ASSERT(TAILQ_EMPTY(&hostp->nh_vholds_list)); 1226 1227 strfree(hostp->nh_name); 1228 strfree(hostp->nh_netid); 1229 kmem_free(hostp->nh_addr.buf, hostp->nh_addr.maxlen); 1230 if (hostp->nh_laddr.buf != NULL) 1231 kmem_free(hostp->nh_laddr.buf, hostp->nh_laddr.maxlen); 1232 1233 if (hostp->nh_sysid != LM_NOSYSID) 1234 nlm_sysid_free(hostp->nh_sysid); 1235 1236 nlm_rpc_cache_destroy(hostp); 1237 1238 ASSERT(TAILQ_EMPTY(&hostp->nh_vholds_list)); 1239 mod_hash_destroy_ptrhash(hostp->nh_vholds_by_vp); 1240 1241 mutex_destroy(&hostp->nh_lock); 1242 cv_destroy(&hostp->nh_rpcb_cv); 1243 cv_destroy(&hostp->nh_recl_cv); 1244 1245 kmem_cache_free(nlm_hosts_cache, hostp); 1246 } 1247 1248 /* 1249 * Cleanup SERVER-side state after a client restarts, 1250 * or becomes unresponsive, or whatever. 1251 * 1252 * We unlock any active locks owned by the host. 1253 * When rpc.lockd is shutting down, 1254 * this function is called with newstate set to zero 1255 * which allows us to cancel any pending async locks 1256 * and clear the locking state. 1257 * 1258 * When "state" is 0, we don't update host's state, 1259 * but cleanup all remote locks on the host. 1260 * It's useful to call this function for resources 1261 * cleanup. 1262 */ 1263 void 1264 nlm_host_notify_server(struct nlm_host *hostp, int32_t state) 1265 { 1266 struct nlm_vhold *nvp; 1267 struct nlm_slreq *slr; 1268 struct nlm_slreq_list slreqs2free; 1269 1270 TAILQ_INIT(&slreqs2free); 1271 mutex_enter(&hostp->nh_lock); 1272 if (state != 0) 1273 hostp->nh_state = state; 1274 1275 TAILQ_FOREACH(nvp, &hostp->nh_vholds_list, nv_link) { 1276 1277 /* cleanup sleeping requests at first */ 1278 while ((slr = TAILQ_FIRST(&nvp->nv_slreqs)) != NULL) { 1279 TAILQ_REMOVE(&nvp->nv_slreqs, slr, nsr_link); 1280 1281 /* 1282 * Instead of freeing cancelled sleeping request 1283 * here, we add it to the linked list created 1284 * on the stack in order to do all frees outside 1285 * the critical section. 1286 */ 1287 TAILQ_INSERT_TAIL(&slreqs2free, slr, nsr_link); 1288 } 1289 1290 nvp->nv_refcnt++; 1291 mutex_exit(&hostp->nh_lock); 1292 1293 nlm_vhold_clean(nvp, hostp->nh_sysid); 1294 1295 mutex_enter(&hostp->nh_lock); 1296 nvp->nv_refcnt--; 1297 } 1298 1299 mutex_exit(&hostp->nh_lock); 1300 while ((slr = TAILQ_FIRST(&slreqs2free)) != NULL) { 1301 TAILQ_REMOVE(&slreqs2free, slr, nsr_link); 1302 kmem_free(slr, sizeof (*slr)); 1303 } 1304 } 1305 1306 /* 1307 * Cleanup CLIENT-side state after a server restarts, 1308 * or becomes unresponsive, or whatever. 1309 * 1310 * This is called by the local NFS statd when we receive a 1311 * host state change notification. (also nlm_svc_stopping) 1312 * 1313 * Deal with a server restart. If we are stopping the 1314 * NLM service, we'll have newstate == 0, and will just 1315 * cancel all our client-side lock requests. Otherwise, 1316 * start the "recovery" process to reclaim any locks 1317 * we hold on this server. 1318 */ 1319 void 1320 nlm_host_notify_client(struct nlm_host *hostp, int32_t state) 1321 { 1322 mutex_enter(&hostp->nh_lock); 1323 hostp->nh_state = state; 1324 if (hostp->nh_flags & NLM_NH_RECLAIM) { 1325 /* 1326 * Either host's state is up to date or 1327 * host is already in recovery. 1328 */ 1329 mutex_exit(&hostp->nh_lock); 1330 return; 1331 } 1332 1333 hostp->nh_flags |= NLM_NH_RECLAIM; 1334 1335 /* 1336 * Host will be released by the recovery thread, 1337 * thus we need to increment refcount. 1338 */ 1339 hostp->nh_refs++; 1340 mutex_exit(&hostp->nh_lock); 1341 1342 (void) zthread_create(NULL, 0, nlm_reclaimer, 1343 hostp, 0, minclsyspri); 1344 } 1345 1346 /* 1347 * The function is called when NLM client detects that 1348 * server has entered in grace period and client needs 1349 * to wait until reclamation process (if any) does 1350 * its job. 1351 */ 1352 int 1353 nlm_host_wait_grace(struct nlm_host *hostp) 1354 { 1355 struct nlm_globals *g; 1356 int error = 0; 1357 1358 g = zone_getspecific(nlm_zone_key, curzone); 1359 mutex_enter(&hostp->nh_lock); 1360 1361 do { 1362 int rc; 1363 1364 rc = cv_timedwait_sig(&hostp->nh_recl_cv, 1365 &hostp->nh_lock, ddi_get_lbolt() + 1366 SEC_TO_TICK(g->retrans_tmo)); 1367 1368 if (rc == 0) { 1369 error = EINTR; 1370 break; 1371 } 1372 } while (hostp->nh_flags & NLM_NH_RECLAIM); 1373 1374 mutex_exit(&hostp->nh_lock); 1375 return (error); 1376 } 1377 1378 /* 1379 * Create a new NLM host. 1380 * 1381 * NOTE: The in-kernel RPC (kRPC) subsystem uses TLI/XTI, 1382 * which needs both a knetconfig and an address when creating 1383 * endpoints. Thus host object stores both knetconfig and 1384 * netid. 1385 */ 1386 static struct nlm_host * 1387 nlm_host_create(char *name, const char *netid, 1388 struct knetconfig *knc, struct netbuf *naddr, struct netbuf *laddr) 1389 { 1390 struct nlm_host *host; 1391 1392 host = kmem_cache_alloc(nlm_hosts_cache, KM_SLEEP); 1393 1394 mutex_init(&host->nh_lock, NULL, MUTEX_DEFAULT, NULL); 1395 cv_init(&host->nh_rpcb_cv, NULL, CV_DEFAULT, NULL); 1396 cv_init(&host->nh_recl_cv, NULL, CV_DEFAULT, NULL); 1397 1398 host->nh_sysid = LM_NOSYSID; 1399 host->nh_refs = 1; 1400 host->nh_name = strdup(name); 1401 host->nh_netid = strdup(netid); 1402 host->nh_knc = *knc; 1403 nlm_copy_netbuf(&host->nh_addr, naddr); 1404 if (laddr != NULL) { 1405 nlm_copy_netbuf(&host->nh_laddr, laddr); 1406 } else { 1407 bzero(&host->nh_laddr, sizeof (host->nh_laddr)); 1408 } 1409 1410 host->nh_state = 0; 1411 host->nh_rpcb_state = NRPCB_NEED_UPDATE; 1412 host->nh_flags = 0; 1413 1414 host->nh_vholds_by_vp = mod_hash_create_ptrhash("nlm vholds hash", 1415 32, mod_hash_null_valdtor, sizeof (vnode_t)); 1416 1417 TAILQ_INIT(&host->nh_vholds_list); 1418 TAILQ_INIT(&host->nh_rpchc); 1419 1420 return (host); 1421 } 1422 1423 /* 1424 * Cancel all client side sleeping locks owned by given host. 1425 */ 1426 void 1427 nlm_host_cancel_slocks(struct nlm_globals *g, struct nlm_host *hostp) 1428 { 1429 struct nlm_slock *nslp; 1430 1431 mutex_enter(&g->lock); 1432 TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) { 1433 if (nslp->nsl_host == hostp) { 1434 nslp->nsl_state = NLM_SL_CANCELLED; 1435 cv_broadcast(&nslp->nsl_cond); 1436 } 1437 } 1438 1439 mutex_exit(&g->lock); 1440 } 1441 1442 /* 1443 * Garbage collect stale vhold objects. 1444 * 1445 * In other words check whether vnodes that are 1446 * held by vhold objects still have any locks 1447 * or shares or still in use. If they aren't, 1448 * just destroy them. 1449 */ 1450 static void 1451 nlm_host_gc_vholds(struct nlm_host *hostp) 1452 { 1453 struct nlm_vhold *nvp; 1454 1455 ASSERT(MUTEX_HELD(&hostp->nh_lock)); 1456 1457 nvp = TAILQ_FIRST(&hostp->nh_vholds_list); 1458 while (nvp != NULL) { 1459 struct nlm_vhold *nvp_tmp; 1460 1461 if (nlm_vhold_busy(hostp, nvp)) { 1462 nvp = TAILQ_NEXT(nvp, nv_link); 1463 continue; 1464 } 1465 1466 nvp_tmp = TAILQ_NEXT(nvp, nv_link); 1467 nlm_vhold_destroy(hostp, nvp); 1468 nvp = nvp_tmp; 1469 } 1470 } 1471 1472 /* 1473 * Check whether the given host has any 1474 * server side locks or share reservations. 1475 */ 1476 static bool_t 1477 nlm_host_has_srv_locks(struct nlm_host *hostp) 1478 { 1479 /* 1480 * It's cheap and simple: if server has 1481 * any locks/shares there must be vhold 1482 * object storing the affected vnode. 1483 * 1484 * NOTE: We don't need to check sleeping 1485 * locks on the server side, because if 1486 * server side sleeping lock is alive, 1487 * there must be a vhold object corresponding 1488 * to target vnode. 1489 */ 1490 ASSERT(MUTEX_HELD(&hostp->nh_lock)); 1491 if (!TAILQ_EMPTY(&hostp->nh_vholds_list)) 1492 return (TRUE); 1493 1494 return (FALSE); 1495 } 1496 1497 /* 1498 * Check whether the given host has any client side 1499 * locks or share reservations. 1500 */ 1501 static bool_t 1502 nlm_host_has_cli_locks(struct nlm_host *hostp) 1503 { 1504 ASSERT(MUTEX_HELD(&hostp->nh_lock)); 1505 1506 /* 1507 * XXX: It's not the way I'd like to do the check, 1508 * because flk_sysid_has_locks() can be very 1509 * expensive by design. Unfortunatelly it iterates 1510 * through all locks on the system, doesn't matter 1511 * were they made on remote system via NLM or 1512 * on local system via reclock. To understand the 1513 * problem, consider that there're dozens of thousands 1514 * of locks that are made on some ZFS dataset. And there's 1515 * another dataset shared by NFS where NLM client had locks 1516 * some time ago, but doesn't have them now. 1517 * In this case flk_sysid_has_locks() will iterate 1518 * thrught dozens of thousands locks until it returns us 1519 * FALSE. 1520 * Oh, I hope that in shiny future somebody will make 1521 * local lock manager (os/flock.c) better, so that 1522 * it'd be more friedly to remote locks and 1523 * flk_sysid_has_locks() wouldn't be so expensive. 1524 */ 1525 if (flk_sysid_has_locks(hostp->nh_sysid | 1526 LM_SYSID_CLIENT, FLK_QUERY_ACTIVE)) 1527 return (TRUE); 1528 1529 /* 1530 * Check whether host has any share reservations 1531 * registered on the client side. 1532 */ 1533 if (hostp->nh_shrlist != NULL) 1534 return (TRUE); 1535 1536 return (FALSE); 1537 } 1538 1539 /* 1540 * Determine whether the given host owns any 1541 * locks or share reservations. 1542 */ 1543 static bool_t 1544 nlm_host_has_locks(struct nlm_host *hostp) 1545 { 1546 if (nlm_host_has_srv_locks(hostp)) 1547 return (TRUE); 1548 1549 return (nlm_host_has_cli_locks(hostp)); 1550 } 1551 1552 /* 1553 * This function compares only addresses of two netbufs 1554 * that belong to NC_TCP[6] or NC_UDP[6] protofamily. 1555 * Port part of netbuf is ignored. 1556 * 1557 * Return values: 1558 * -1: nb1's address is "smaller" than nb2's 1559 * 0: addresses are equal 1560 * 1: nb1's address is "greater" than nb2's 1561 */ 1562 static int 1563 nlm_netbuf_addrs_cmp(struct netbuf *nb1, struct netbuf *nb2) 1564 { 1565 union nlm_addr { 1566 struct sockaddr sa; 1567 struct sockaddr_in sin; 1568 struct sockaddr_in6 sin6; 1569 } *na1, *na2; 1570 int res; 1571 1572 /* LINTED E_BAD_PTR_CAST_ALIGN */ 1573 na1 = (union nlm_addr *)nb1->buf; 1574 /* LINTED E_BAD_PTR_CAST_ALIGN */ 1575 na2 = (union nlm_addr *)nb2->buf; 1576 1577 if (na1->sa.sa_family < na2->sa.sa_family) 1578 return (-1); 1579 if (na1->sa.sa_family > na2->sa.sa_family) 1580 return (1); 1581 1582 switch (na1->sa.sa_family) { 1583 case AF_INET: 1584 res = memcmp(&na1->sin.sin_addr, &na2->sin.sin_addr, 1585 sizeof (na1->sin.sin_addr)); 1586 break; 1587 case AF_INET6: 1588 res = memcmp(&na1->sin6.sin6_addr, &na2->sin6.sin6_addr, 1589 sizeof (na1->sin6.sin6_addr)); 1590 break; 1591 default: 1592 VERIFY(0); 1593 return (0); 1594 } 1595 1596 return (SIGN(res)); 1597 } 1598 1599 /* 1600 * Compare two nlm hosts. 1601 * Return values: 1602 * -1: host1 is "smaller" than host2 1603 * 0: host1 is equal to host2 1604 * 1: host1 is "greater" than host2 1605 */ 1606 int 1607 nlm_host_cmp(const void *p1, const void *p2) 1608 { 1609 struct nlm_host *h1 = (struct nlm_host *)p1; 1610 struct nlm_host *h2 = (struct nlm_host *)p2; 1611 int res; 1612 1613 res = strcmp(h1->nh_netid, h2->nh_netid); 1614 if (res != 0) 1615 return (SIGN(res)); 1616 1617 res = nlm_netbuf_addrs_cmp(&h1->nh_addr, &h2->nh_addr); 1618 return (res); 1619 } 1620 1621 /* 1622 * Find the host specified by... (see below) 1623 * If found, increment the ref count. 1624 */ 1625 static struct nlm_host * 1626 nlm_host_find_locked(struct nlm_globals *g, const char *netid, 1627 struct netbuf *naddr, avl_index_t *wherep) 1628 { 1629 struct nlm_host *hostp, key; 1630 avl_index_t pos; 1631 1632 ASSERT(MUTEX_HELD(&g->lock)); 1633 1634 key.nh_netid = (char *)netid; 1635 key.nh_addr.buf = naddr->buf; 1636 key.nh_addr.len = naddr->len; 1637 key.nh_addr.maxlen = naddr->maxlen; 1638 1639 hostp = avl_find(&g->nlm_hosts_tree, &key, &pos); 1640 1641 if (hostp != NULL) { 1642 /* 1643 * Host is inuse now. Remove it from idle 1644 * hosts list if needed. 1645 */ 1646 if (hostp->nh_flags & NLM_NH_INIDLE) { 1647 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link); 1648 hostp->nh_flags &= ~NLM_NH_INIDLE; 1649 } 1650 1651 hostp->nh_refs++; 1652 } 1653 if (wherep != NULL) 1654 *wherep = pos; 1655 1656 return (hostp); 1657 } 1658 1659 /* 1660 * Find NLM host for the given name and address. 1661 */ 1662 struct nlm_host * 1663 nlm_host_find(struct nlm_globals *g, const char *netid, 1664 struct netbuf *addr) 1665 { 1666 struct nlm_host *hostp = NULL; 1667 1668 mutex_enter(&g->lock); 1669 if (g->run_status != NLM_ST_UP) 1670 goto out; 1671 1672 hostp = nlm_host_find_locked(g, netid, addr, NULL); 1673 1674 out: 1675 mutex_exit(&g->lock); 1676 return (hostp); 1677 } 1678 1679 1680 /* 1681 * Find or create an NLM host for the given name and address. 1682 * 1683 * The remote host is determined by all of: name, netid, address. 1684 * Note that the netid is whatever nlm_svc_add_ep() gave to 1685 * svc_tli_kcreate() for the service binding. If any of these 1686 * are different, allocate a new host (new sysid). 1687 */ 1688 struct nlm_host * 1689 nlm_host_findcreate(struct nlm_globals *g, char *name, 1690 const char *netid, struct netbuf *addr, struct netbuf *laddr) 1691 { 1692 int err; 1693 struct nlm_host *host, *newhost = NULL; 1694 struct knetconfig knc; 1695 avl_index_t where; 1696 1697 mutex_enter(&g->lock); 1698 if (g->run_status != NLM_ST_UP) { 1699 mutex_exit(&g->lock); 1700 return (NULL); 1701 } 1702 1703 host = nlm_host_find_locked(g, netid, addr, NULL); 1704 mutex_exit(&g->lock); 1705 if (host != NULL) { 1706 if ((&host->nh_laddr)->len != 0 && 1707 (laddr == NULL || laddr->len == 0)) { 1708 cmn_err(CE_NOTE, "nlm_host_findcreate: " 1709 "Incoming laddr is absent but " 1710 "host has a recorded nh_laddr.\n"); 1711 } else if ((&host->nh_laddr)->len == 0 && 1712 laddr != NULL && laddr->len != 0) { 1713 cmn_err(CE_NOTE, "nlm_host_findcreate: " 1714 "Incoming laddr is present but " 1715 "host has no recorded nh_laddr.\n"); 1716 } else if (laddr != NULL && 1717 (((&host->nh_laddr)->len != laddr->len) || 1718 bcmp((&host->nh_laddr)->buf, laddr->buf, 1719 (size_t)laddr->len) != 0)) { 1720 cmn_err(CE_NOTE, "nlm_host_findcreate: received " 1721 "laddr different from recorded nh_laddr.\n"); 1722 } 1723 1724 return (host); 1725 } 1726 1727 err = nlm_knc_from_netid(netid, &knc); 1728 if (err != 0) 1729 return (NULL); 1730 /* 1731 * Do allocations (etc.) outside of mutex, 1732 * and then check again before inserting. 1733 */ 1734 newhost = nlm_host_create(name, netid, &knc, addr, laddr); 1735 newhost->nh_sysid = nlm_sysid_alloc(); 1736 if (newhost->nh_sysid == LM_NOSYSID) 1737 goto out; 1738 1739 mutex_enter(&g->lock); 1740 host = nlm_host_find_locked(g, netid, addr, &where); 1741 if (host == NULL) { 1742 host = newhost; 1743 newhost = NULL; 1744 1745 /* 1746 * Insert host to the hosts AVL tree that is 1747 * used to lookup by <netid, address> pair. 1748 */ 1749 avl_insert(&g->nlm_hosts_tree, host, where); 1750 1751 /* 1752 * Insert host to the hosts hash table that is 1753 * used to lookup host by sysid. 1754 */ 1755 VERIFY(mod_hash_insert(g->nlm_hosts_hash, 1756 (mod_hash_key_t)(uintptr_t)host->nh_sysid, 1757 (mod_hash_val_t)host) == 0); 1758 } 1759 1760 mutex_exit(&g->lock); 1761 1762 out: 1763 if (newhost != NULL) { 1764 /* 1765 * We do not need the preallocated nlm_host 1766 * so decrement the reference counter 1767 * and destroy it. 1768 */ 1769 newhost->nh_refs--; 1770 nlm_host_destroy(newhost); 1771 } 1772 1773 return (host); 1774 } 1775 1776 /* 1777 * Find the NLM host that matches the value of 'sysid'. 1778 * If found, return it with a new ref, 1779 * else return NULL. 1780 */ 1781 struct nlm_host * 1782 nlm_host_find_by_sysid(struct nlm_globals *g, sysid_t sysid) 1783 { 1784 struct nlm_host *hostp = NULL; 1785 1786 mutex_enter(&g->lock); 1787 if (g->run_status != NLM_ST_UP) 1788 goto out; 1789 1790 (void) mod_hash_find(g->nlm_hosts_hash, 1791 (mod_hash_key_t)(uintptr_t)sysid, 1792 (mod_hash_val_t)&hostp); 1793 1794 if (hostp == NULL) 1795 goto out; 1796 1797 /* 1798 * Host is inuse now. Remove it 1799 * from idle hosts list if needed. 1800 */ 1801 if (hostp->nh_flags & NLM_NH_INIDLE) { 1802 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link); 1803 hostp->nh_flags &= ~NLM_NH_INIDLE; 1804 } 1805 1806 hostp->nh_refs++; 1807 1808 out: 1809 mutex_exit(&g->lock); 1810 return (hostp); 1811 } 1812 1813 /* 1814 * Release the given host. 1815 * I.e. drop a reference that was taken earlier by one of 1816 * the following functions: nlm_host_findcreate(), nlm_host_find(), 1817 * nlm_host_find_by_sysid(). 1818 * 1819 * When the very last reference is dropped, host is moved to 1820 * so-called "idle state". All hosts that are in idle state 1821 * have an idle timeout. If timeout is expired, GC thread 1822 * checks whether hosts have any locks and if they heven't 1823 * any, it removes them. 1824 * NOTE: only unused hosts can be in idle state. 1825 */ 1826 static void 1827 nlm_host_release_locked(struct nlm_globals *g, struct nlm_host *hostp) 1828 { 1829 if (hostp == NULL) 1830 return; 1831 1832 ASSERT(MUTEX_HELD(&g->lock)); 1833 ASSERT(hostp->nh_refs > 0); 1834 1835 hostp->nh_refs--; 1836 if (hostp->nh_refs != 0) 1837 return; 1838 1839 /* 1840 * The very last reference to the host was dropped, 1841 * thus host is unused now. Set its idle timeout 1842 * and move it to the idle hosts LRU list. 1843 */ 1844 hostp->nh_idle_timeout = ddi_get_lbolt() + 1845 SEC_TO_TICK(g->cn_idle_tmo); 1846 1847 ASSERT((hostp->nh_flags & NLM_NH_INIDLE) == 0); 1848 TAILQ_INSERT_TAIL(&g->nlm_idle_hosts, hostp, nh_link); 1849 hostp->nh_flags |= NLM_NH_INIDLE; 1850 } 1851 1852 void 1853 nlm_host_release(struct nlm_globals *g, struct nlm_host *hostp) 1854 { 1855 if (hostp == NULL) 1856 return; 1857 1858 mutex_enter(&g->lock); 1859 nlm_host_release_locked(g, hostp); 1860 mutex_exit(&g->lock); 1861 } 1862 1863 /* 1864 * Unregister this NLM host (NFS client) with the local statd 1865 * due to idleness (no locks held for a while). 1866 */ 1867 void 1868 nlm_host_unmonitor(struct nlm_globals *g, struct nlm_host *host) 1869 { 1870 enum clnt_stat stat; 1871 1872 VERIFY(host->nh_refs == 0); 1873 if (!(host->nh_flags & NLM_NH_MONITORED)) 1874 return; 1875 1876 host->nh_flags &= ~NLM_NH_MONITORED; 1877 stat = nlm_nsm_unmon(&g->nlm_nsm, host->nh_name); 1878 if (stat != RPC_SUCCESS) { 1879 NLM_WARN("NLM: Failed to contact statd, stat=%d\n", stat); 1880 return; 1881 } 1882 } 1883 1884 /* 1885 * Ask the local NFS statd to begin monitoring this host. 1886 * It will call us back when that host restarts, using the 1887 * prog,vers,proc specified below, i.e. NLM_SM_NOTIFY1, 1888 * which is handled in nlm_do_notify1(). 1889 */ 1890 void 1891 nlm_host_monitor(struct nlm_globals *g, struct nlm_host *host, int state) 1892 { 1893 int family; 1894 netobj obj; 1895 enum clnt_stat stat; 1896 1897 if (state != 0 && host->nh_state == 0) { 1898 /* 1899 * This is the first time we have seen an NSM state 1900 * Value for this host. We record it here to help 1901 * detect host reboots. 1902 */ 1903 host->nh_state = state; 1904 } 1905 1906 mutex_enter(&host->nh_lock); 1907 if (host->nh_flags & NLM_NH_MONITORED) { 1908 mutex_exit(&host->nh_lock); 1909 return; 1910 } 1911 1912 host->nh_flags |= NLM_NH_MONITORED; 1913 mutex_exit(&host->nh_lock); 1914 1915 /* 1916 * Before we begin monitoring the host register the network address 1917 * associated with this hostname. 1918 */ 1919 nlm_netbuf_to_netobj(&host->nh_addr, &family, &obj); 1920 stat = nlm_nsmaddr_reg(&g->nlm_nsm, host->nh_name, family, &obj); 1921 if (stat != RPC_SUCCESS) { 1922 NLM_WARN("Failed to register address, stat=%d\n", stat); 1923 mutex_enter(&g->lock); 1924 host->nh_flags &= ~NLM_NH_MONITORED; 1925 mutex_exit(&g->lock); 1926 1927 return; 1928 } 1929 1930 /* 1931 * Tell statd how to call us with status updates for 1932 * this host. Updates arrive via nlm_do_notify1(). 1933 * 1934 * We put our assigned system ID value in the priv field to 1935 * make it simpler to find the host if we are notified of a 1936 * host restart. 1937 */ 1938 stat = nlm_nsm_mon(&g->nlm_nsm, host->nh_name, host->nh_sysid); 1939 if (stat != RPC_SUCCESS) { 1940 NLM_WARN("Failed to contact local NSM, stat=%d\n", stat); 1941 mutex_enter(&g->lock); 1942 host->nh_flags &= ~NLM_NH_MONITORED; 1943 mutex_exit(&g->lock); 1944 1945 return; 1946 } 1947 } 1948 1949 int 1950 nlm_host_get_state(struct nlm_host *hostp) 1951 { 1952 1953 return (hostp->nh_state); 1954 } 1955 1956 /* 1957 * NLM client/server sleeping locks 1958 */ 1959 1960 /* 1961 * Register client side sleeping lock. 1962 * 1963 * Our client code calls this to keep information 1964 * about sleeping lock somewhere. When it receives 1965 * grant callback from server or when it just 1966 * needs to remove all sleeping locks from vnode, 1967 * it uses this information for remove/apply lock 1968 * properly. 1969 */ 1970 struct nlm_slock * 1971 nlm_slock_register( 1972 struct nlm_globals *g, 1973 struct nlm_host *host, 1974 struct nlm4_lock *lock, 1975 struct vnode *vp) 1976 { 1977 struct nlm_slock *nslp; 1978 1979 nslp = kmem_zalloc(sizeof (*nslp), KM_SLEEP); 1980 cv_init(&nslp->nsl_cond, NULL, CV_DEFAULT, NULL); 1981 nslp->nsl_lock = *lock; 1982 nlm_copy_netobj(&nslp->nsl_fh, &nslp->nsl_lock.fh); 1983 nslp->nsl_state = NLM_SL_BLOCKED; 1984 nslp->nsl_host = host; 1985 nslp->nsl_vp = vp; 1986 1987 mutex_enter(&g->lock); 1988 TAILQ_INSERT_TAIL(&g->nlm_slocks, nslp, nsl_link); 1989 mutex_exit(&g->lock); 1990 1991 return (nslp); 1992 } 1993 1994 /* 1995 * Remove this lock from the wait list and destroy it. 1996 */ 1997 void 1998 nlm_slock_unregister(struct nlm_globals *g, struct nlm_slock *nslp) 1999 { 2000 mutex_enter(&g->lock); 2001 TAILQ_REMOVE(&g->nlm_slocks, nslp, nsl_link); 2002 mutex_exit(&g->lock); 2003 2004 kmem_free(nslp->nsl_fh.n_bytes, nslp->nsl_fh.n_len); 2005 cv_destroy(&nslp->nsl_cond); 2006 kmem_free(nslp, sizeof (*nslp)); 2007 } 2008 2009 /* 2010 * Wait for a granted callback or cancellation event 2011 * for a sleeping lock. 2012 * 2013 * If a signal interrupted the wait or if the lock 2014 * was cancelled, return EINTR - the caller must arrange to send 2015 * a cancellation to the server. 2016 * 2017 * If timeout occurred, return ETIMEDOUT - the caller must 2018 * resend the lock request to the server. 2019 * 2020 * On success return 0. 2021 */ 2022 int 2023 nlm_slock_wait(struct nlm_globals *g, 2024 struct nlm_slock *nslp, uint_t timeo_secs) 2025 { 2026 clock_t timeo_ticks; 2027 int cv_res, error; 2028 2029 /* 2030 * If the granted message arrived before we got here, 2031 * nslp->nsl_state will be NLM_SL_GRANTED - in that case don't sleep. 2032 */ 2033 cv_res = 1; 2034 timeo_ticks = ddi_get_lbolt() + SEC_TO_TICK(timeo_secs); 2035 2036 mutex_enter(&g->lock); 2037 while (nslp->nsl_state == NLM_SL_BLOCKED && cv_res > 0) { 2038 cv_res = cv_timedwait_sig(&nslp->nsl_cond, 2039 &g->lock, timeo_ticks); 2040 } 2041 2042 /* 2043 * No matter why we wake up, if the lock was 2044 * cancelled, let the function caller to know 2045 * about it by returning EINTR. 2046 */ 2047 if (nslp->nsl_state == NLM_SL_CANCELLED) { 2048 error = EINTR; 2049 goto out; 2050 } 2051 2052 if (cv_res <= 0) { 2053 /* We were woken up either by timeout or by interrupt */ 2054 error = (cv_res < 0) ? ETIMEDOUT : EINTR; 2055 2056 /* 2057 * The granted message may arrive after the 2058 * interrupt/timeout but before we manage to lock the 2059 * mutex. Detect this by examining nslp. 2060 */ 2061 if (nslp->nsl_state == NLM_SL_GRANTED) 2062 error = 0; 2063 } else { /* Awaken via cv_signal()/cv_broadcast() or didn't block */ 2064 error = 0; 2065 VERIFY(nslp->nsl_state == NLM_SL_GRANTED); 2066 } 2067 2068 out: 2069 mutex_exit(&g->lock); 2070 return (error); 2071 } 2072 2073 /* 2074 * Mark client side sleeping lock as granted 2075 * and wake up a process blocked on the lock. 2076 * Called from server side NLM_GRANT handler. 2077 * 2078 * If sleeping lock is found return 0, otherwise 2079 * return ENOENT. 2080 */ 2081 int 2082 nlm_slock_grant(struct nlm_globals *g, 2083 struct nlm_host *hostp, struct nlm4_lock *alock) 2084 { 2085 struct nlm_slock *nslp; 2086 int error = ENOENT; 2087 2088 mutex_enter(&g->lock); 2089 TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) { 2090 if ((nslp->nsl_state != NLM_SL_BLOCKED) || 2091 (nslp->nsl_host != hostp)) 2092 continue; 2093 2094 if (alock->svid == nslp->nsl_lock.svid && 2095 alock->l_offset == nslp->nsl_lock.l_offset && 2096 alock->l_len == nslp->nsl_lock.l_len && 2097 alock->fh.n_len == nslp->nsl_lock.fh.n_len && 2098 bcmp(alock->fh.n_bytes, nslp->nsl_lock.fh.n_bytes, 2099 nslp->nsl_lock.fh.n_len) == 0) { 2100 nslp->nsl_state = NLM_SL_GRANTED; 2101 cv_broadcast(&nslp->nsl_cond); 2102 error = 0; 2103 break; 2104 } 2105 } 2106 2107 mutex_exit(&g->lock); 2108 return (error); 2109 } 2110 2111 /* 2112 * Register sleeping lock request corresponding to 2113 * flp on the given vhold object. 2114 * On success function returns 0, otherwise (if 2115 * lock request with the same flp is already 2116 * registered) function returns EEXIST. 2117 */ 2118 int 2119 nlm_slreq_register(struct nlm_host *hostp, struct nlm_vhold *nvp, 2120 struct flock64 *flp) 2121 { 2122 struct nlm_slreq *slr, *new_slr = NULL; 2123 int ret = EEXIST; 2124 2125 mutex_enter(&hostp->nh_lock); 2126 slr = nlm_slreq_find_locked(hostp, nvp, flp); 2127 if (slr != NULL) 2128 goto out; 2129 2130 mutex_exit(&hostp->nh_lock); 2131 new_slr = kmem_zalloc(sizeof (*slr), KM_SLEEP); 2132 bcopy(flp, &new_slr->nsr_fl, sizeof (*flp)); 2133 2134 mutex_enter(&hostp->nh_lock); 2135 slr = nlm_slreq_find_locked(hostp, nvp, flp); 2136 if (slr == NULL) { 2137 slr = new_slr; 2138 new_slr = NULL; 2139 ret = 0; 2140 2141 TAILQ_INSERT_TAIL(&nvp->nv_slreqs, slr, nsr_link); 2142 } 2143 2144 out: 2145 mutex_exit(&hostp->nh_lock); 2146 if (new_slr != NULL) 2147 kmem_free(new_slr, sizeof (*new_slr)); 2148 2149 return (ret); 2150 } 2151 2152 /* 2153 * Unregister sleeping lock request corresponding 2154 * to flp from the given vhold object. 2155 * On success function returns 0, otherwise (if 2156 * lock request corresponding to flp isn't found 2157 * on the given vhold) function returns ENOENT. 2158 */ 2159 int 2160 nlm_slreq_unregister(struct nlm_host *hostp, struct nlm_vhold *nvp, 2161 struct flock64 *flp) 2162 { 2163 struct nlm_slreq *slr; 2164 2165 mutex_enter(&hostp->nh_lock); 2166 slr = nlm_slreq_find_locked(hostp, nvp, flp); 2167 if (slr == NULL) { 2168 mutex_exit(&hostp->nh_lock); 2169 return (ENOENT); 2170 } 2171 2172 TAILQ_REMOVE(&nvp->nv_slreqs, slr, nsr_link); 2173 mutex_exit(&hostp->nh_lock); 2174 2175 kmem_free(slr, sizeof (*slr)); 2176 return (0); 2177 } 2178 2179 /* 2180 * Find sleeping lock request on the given vhold object by flp. 2181 */ 2182 struct nlm_slreq * 2183 nlm_slreq_find_locked(struct nlm_host *hostp, struct nlm_vhold *nvp, 2184 struct flock64 *flp) 2185 { 2186 struct nlm_slreq *slr = NULL; 2187 2188 ASSERT(MUTEX_HELD(&hostp->nh_lock)); 2189 TAILQ_FOREACH(slr, &nvp->nv_slreqs, nsr_link) { 2190 if (slr->nsr_fl.l_start == flp->l_start && 2191 slr->nsr_fl.l_len == flp->l_len && 2192 slr->nsr_fl.l_pid == flp->l_pid && 2193 slr->nsr_fl.l_type == flp->l_type) 2194 break; 2195 } 2196 2197 return (slr); 2198 } 2199 2200 /* 2201 * NLM tracks active share reservations made on the client side. 2202 * It needs to have a track of share reservations for two purposes 2203 * 1) to determine if nlm_host is busy (if it has active locks and/or 2204 * share reservations, it is) 2205 * 2) to recover active share reservations when NLM server reports 2206 * that it has rebooted. 2207 * 2208 * Unfortunately Illumos local share reservations manager (see os/share.c) 2209 * doesn't have an ability to lookup all reservations on the system 2210 * by sysid (like local lock manager) or get all reservations by sysid. 2211 * It tracks reservations per vnode and is able to get/looup them 2212 * on particular vnode. It's not what NLM needs. Thus it has that ugly 2213 * share reservations tracking scheme. 2214 */ 2215 2216 void 2217 nlm_shres_track(struct nlm_host *hostp, vnode_t *vp, struct shrlock *shrp) 2218 { 2219 struct nlm_shres *nsp, *nsp_new; 2220 2221 /* 2222 * NFS code must fill the s_owner, so that 2223 * s_own_len is never 0. 2224 */ 2225 ASSERT(shrp->s_own_len > 0); 2226 nsp_new = nlm_shres_create_item(shrp, vp); 2227 2228 mutex_enter(&hostp->nh_lock); 2229 for (nsp = hostp->nh_shrlist; nsp != NULL; nsp = nsp->ns_next) 2230 if (nsp->ns_vp == vp && nlm_shres_equal(shrp, nsp->ns_shr)) 2231 break; 2232 2233 if (nsp != NULL) { 2234 /* 2235 * Found a duplicate. Do nothing. 2236 */ 2237 2238 goto out; 2239 } 2240 2241 nsp = nsp_new; 2242 nsp_new = NULL; 2243 nsp->ns_next = hostp->nh_shrlist; 2244 hostp->nh_shrlist = nsp; 2245 2246 out: 2247 mutex_exit(&hostp->nh_lock); 2248 if (nsp_new != NULL) 2249 nlm_shres_destroy_item(nsp_new); 2250 } 2251 2252 void 2253 nlm_shres_untrack(struct nlm_host *hostp, vnode_t *vp, struct shrlock *shrp) 2254 { 2255 struct nlm_shres *nsp, *nsp_prev = NULL; 2256 2257 mutex_enter(&hostp->nh_lock); 2258 nsp = hostp->nh_shrlist; 2259 while (nsp != NULL) { 2260 if (nsp->ns_vp == vp && nlm_shres_equal(shrp, nsp->ns_shr)) { 2261 struct nlm_shres *nsp_del; 2262 2263 nsp_del = nsp; 2264 nsp = nsp->ns_next; 2265 if (nsp_prev != NULL) 2266 nsp_prev->ns_next = nsp; 2267 else 2268 hostp->nh_shrlist = nsp; 2269 2270 nlm_shres_destroy_item(nsp_del); 2271 continue; 2272 } 2273 2274 nsp_prev = nsp; 2275 nsp = nsp->ns_next; 2276 } 2277 2278 mutex_exit(&hostp->nh_lock); 2279 } 2280 2281 /* 2282 * Get a _copy_ of the list of all active share reservations 2283 * made by the given host. 2284 * NOTE: the list function returns _must_ be released using 2285 * nlm_free_shrlist(). 2286 */ 2287 struct nlm_shres * 2288 nlm_get_active_shres(struct nlm_host *hostp) 2289 { 2290 struct nlm_shres *nsp, *nslist = NULL; 2291 2292 mutex_enter(&hostp->nh_lock); 2293 for (nsp = hostp->nh_shrlist; nsp != NULL; nsp = nsp->ns_next) { 2294 struct nlm_shres *nsp_new; 2295 2296 nsp_new = nlm_shres_create_item(nsp->ns_shr, nsp->ns_vp); 2297 nsp_new->ns_next = nslist; 2298 nslist = nsp_new; 2299 } 2300 2301 mutex_exit(&hostp->nh_lock); 2302 return (nslist); 2303 } 2304 2305 /* 2306 * Free memory allocated for the active share reservations 2307 * list created by nlm_get_active_shres() function. 2308 */ 2309 void 2310 nlm_free_shrlist(struct nlm_shres *nslist) 2311 { 2312 struct nlm_shres *nsp; 2313 2314 while (nslist != NULL) { 2315 nsp = nslist; 2316 nslist = nslist->ns_next; 2317 2318 nlm_shres_destroy_item(nsp); 2319 } 2320 } 2321 2322 static bool_t 2323 nlm_shres_equal(struct shrlock *shrp1, struct shrlock *shrp2) 2324 { 2325 if (shrp1->s_sysid == shrp2->s_sysid && 2326 shrp1->s_pid == shrp2->s_pid && 2327 shrp1->s_own_len == shrp2->s_own_len && 2328 bcmp(shrp1->s_owner, shrp2->s_owner, 2329 shrp1->s_own_len) == 0) 2330 return (TRUE); 2331 2332 return (FALSE); 2333 } 2334 2335 static struct nlm_shres * 2336 nlm_shres_create_item(struct shrlock *shrp, vnode_t *vp) 2337 { 2338 struct nlm_shres *nsp; 2339 2340 nsp = kmem_alloc(sizeof (*nsp), KM_SLEEP); 2341 nsp->ns_shr = kmem_alloc(sizeof (*shrp), KM_SLEEP); 2342 bcopy(shrp, nsp->ns_shr, sizeof (*shrp)); 2343 nsp->ns_shr->s_owner = kmem_alloc(shrp->s_own_len, KM_SLEEP); 2344 bcopy(shrp->s_owner, nsp->ns_shr->s_owner, shrp->s_own_len); 2345 nsp->ns_vp = vp; 2346 2347 return (nsp); 2348 } 2349 2350 static void 2351 nlm_shres_destroy_item(struct nlm_shres *nsp) 2352 { 2353 kmem_free(nsp->ns_shr->s_owner, 2354 nsp->ns_shr->s_own_len); 2355 kmem_free(nsp->ns_shr, sizeof (struct shrlock)); 2356 kmem_free(nsp, sizeof (*nsp)); 2357 } 2358 2359 /* 2360 * Called by klmmod.c when lockd adds a network endpoint 2361 * on which we should begin RPC services. 2362 */ 2363 int 2364 nlm_svc_add_ep(struct file *fp, const char *netid, struct knetconfig *knc) 2365 { 2366 SVCMASTERXPRT *xprt = NULL; 2367 int error; 2368 2369 error = svc_tli_kcreate(fp, 0, (char *)netid, NULL, &xprt, 2370 &nlm_sct, NULL, NLM_SVCPOOL_ID, FALSE); 2371 if (error != 0) 2372 return (error); 2373 2374 (void) nlm_knc_to_netid(knc); 2375 return (0); 2376 } 2377 2378 /* 2379 * Start NLM service. 2380 */ 2381 int 2382 nlm_svc_starting(struct nlm_globals *g, struct file *fp, 2383 const char *netid, struct knetconfig *knc) 2384 { 2385 int error; 2386 enum clnt_stat stat; 2387 2388 VERIFY(g->run_status == NLM_ST_STARTING); 2389 VERIFY(g->nlm_gc_thread == NULL); 2390 2391 error = nlm_nsm_init_local(&g->nlm_nsm); 2392 if (error != 0) { 2393 NLM_ERR("Failed to initialize NSM handler " 2394 "(error=%d)\n", error); 2395 g->run_status = NLM_ST_DOWN; 2396 return (error); 2397 } 2398 2399 error = EIO; 2400 2401 /* 2402 * Create an NLM garbage collector thread that will 2403 * clean up stale vholds and hosts objects. 2404 */ 2405 g->nlm_gc_thread = zthread_create(NULL, 0, nlm_gc, 2406 g, 0, minclsyspri); 2407 2408 /* 2409 * Send SIMU_CRASH to local statd to report that 2410 * NLM started, so that statd can report other hosts 2411 * about NLM state change. 2412 */ 2413 2414 stat = nlm_nsm_simu_crash(&g->nlm_nsm); 2415 if (stat != RPC_SUCCESS) { 2416 NLM_ERR("Failed to connect to local statd " 2417 "(rpcerr=%d)\n", stat); 2418 goto shutdown_lm; 2419 } 2420 2421 stat = nlm_nsm_stat(&g->nlm_nsm, &g->nsm_state); 2422 if (stat != RPC_SUCCESS) { 2423 NLM_ERR("Failed to get the status of local statd " 2424 "(rpcerr=%d)\n", stat); 2425 goto shutdown_lm; 2426 } 2427 2428 g->grace_threshold = ddi_get_lbolt() + 2429 SEC_TO_TICK(g->grace_period); 2430 2431 /* Register endpoint used for communications with local NLM */ 2432 error = nlm_svc_add_ep(fp, netid, knc); 2433 if (error != 0) 2434 goto shutdown_lm; 2435 2436 (void) svc_pool_control(NLM_SVCPOOL_ID, 2437 SVCPSET_SHUTDOWN_PROC, (void *)nlm_pool_shutdown); 2438 g->run_status = NLM_ST_UP; 2439 return (0); 2440 2441 shutdown_lm: 2442 mutex_enter(&g->lock); 2443 g->run_status = NLM_ST_STOPPING; 2444 mutex_exit(&g->lock); 2445 2446 nlm_svc_stopping(g); 2447 return (error); 2448 } 2449 2450 /* 2451 * Called when the server pool is destroyed, so that 2452 * all transports are closed and no any server threads 2453 * exist. 2454 * 2455 * Just call lm_shutdown() to shut NLM down properly. 2456 */ 2457 static void 2458 nlm_pool_shutdown(void) 2459 { 2460 (void) lm_shutdown(); 2461 } 2462 2463 /* 2464 * Stop NLM service, cleanup all resources 2465 * NLM owns at the moment. 2466 * 2467 * NOTE: NFS code can call NLM while it's 2468 * stopping or even if it's shut down. Any attempt 2469 * to lock file either on client or on the server 2470 * will fail if NLM isn't in NLM_ST_UP state. 2471 */ 2472 void 2473 nlm_svc_stopping(struct nlm_globals *g) 2474 { 2475 mutex_enter(&g->lock); 2476 ASSERT(g->run_status == NLM_ST_STOPPING); 2477 2478 /* 2479 * Ask NLM GC thread to exit and wait until it dies. 2480 */ 2481 cv_signal(&g->nlm_gc_sched_cv); 2482 while (g->nlm_gc_thread != NULL) 2483 cv_wait(&g->nlm_gc_finish_cv, &g->lock); 2484 2485 mutex_exit(&g->lock); 2486 2487 /* 2488 * Cleanup locks owned by NLM hosts. 2489 * NOTE: New hosts won't be created while 2490 * NLM is stopping. 2491 */ 2492 while (!avl_is_empty(&g->nlm_hosts_tree)) { 2493 struct nlm_host *hostp; 2494 int busy_hosts = 0; 2495 2496 /* 2497 * Iterate through all NLM hosts in the system 2498 * and drop the locks they own by force. 2499 */ 2500 hostp = avl_first(&g->nlm_hosts_tree); 2501 while (hostp != NULL) { 2502 /* Cleanup all client and server side locks */ 2503 nlm_client_cancel_all(g, hostp); 2504 nlm_host_notify_server(hostp, 0); 2505 2506 mutex_enter(&hostp->nh_lock); 2507 nlm_host_gc_vholds(hostp); 2508 if (hostp->nh_refs > 0 || nlm_host_has_locks(hostp)) { 2509 /* 2510 * Oh, it seems the host is still busy, let 2511 * it some time to release and go to the 2512 * next one. 2513 */ 2514 2515 mutex_exit(&hostp->nh_lock); 2516 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp); 2517 busy_hosts++; 2518 continue; 2519 } 2520 2521 mutex_exit(&hostp->nh_lock); 2522 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp); 2523 } 2524 2525 /* 2526 * All hosts go to nlm_idle_hosts list after 2527 * all locks they own are cleaned up and last refereces 2528 * were dropped. Just destroy all hosts in nlm_idle_hosts 2529 * list, they can not be removed from there while we're 2530 * in stopping state. 2531 */ 2532 while ((hostp = TAILQ_FIRST(&g->nlm_idle_hosts)) != NULL) { 2533 nlm_host_unregister(g, hostp); 2534 nlm_host_destroy(hostp); 2535 } 2536 2537 if (busy_hosts > 0) { 2538 /* 2539 * There're some hosts that weren't cleaned 2540 * up. Probably they're in resource cleanup 2541 * process. Give them some time to do drop 2542 * references. 2543 */ 2544 delay(MSEC_TO_TICK(500)); 2545 } 2546 } 2547 2548 ASSERT(TAILQ_EMPTY(&g->nlm_slocks)); 2549 2550 nlm_nsm_fini(&g->nlm_nsm); 2551 g->lockd_pid = 0; 2552 g->run_status = NLM_ST_DOWN; 2553 } 2554 2555 /* 2556 * Returns TRUE if the given vnode has 2557 * any active or sleeping locks. 2558 */ 2559 int 2560 nlm_vp_active(const vnode_t *vp) 2561 { 2562 struct nlm_globals *g; 2563 struct nlm_host *hostp; 2564 struct nlm_vhold *nvp; 2565 int active = 0; 2566 2567 g = zone_getspecific(nlm_zone_key, curzone); 2568 2569 /* 2570 * Server side NLM has locks on the given vnode 2571 * if there exist a vhold object that holds 2572 * the given vnode "vp" in one of NLM hosts. 2573 */ 2574 mutex_enter(&g->lock); 2575 hostp = avl_first(&g->nlm_hosts_tree); 2576 while (hostp != NULL) { 2577 mutex_enter(&hostp->nh_lock); 2578 nvp = nlm_vhold_find_locked(hostp, vp); 2579 mutex_exit(&hostp->nh_lock); 2580 if (nvp != NULL) { 2581 active = 1; 2582 break; 2583 } 2584 2585 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp); 2586 } 2587 2588 mutex_exit(&g->lock); 2589 return (active); 2590 } 2591 2592 /* 2593 * Called right before NFS export is going to 2594 * dissapear. The function finds all vnodes 2595 * belonging to the given export and cleans 2596 * all remote locks and share reservations 2597 * on them. 2598 */ 2599 void 2600 nlm_zone_unexport(struct nlm_globals *g, struct exportinfo *exi) 2601 { 2602 struct nlm_host *hostp; 2603 2604 mutex_enter(&g->lock); 2605 if (g->run_status != NLM_ST_UP) { 2606 /* nothing to do */ 2607 mutex_exit(&g->lock); 2608 return; 2609 } 2610 2611 hostp = avl_first(&g->nlm_hosts_tree); 2612 while (hostp != NULL) { 2613 struct nlm_vhold *nvp; 2614 2615 if (hostp->nh_flags & NLM_NH_INIDLE) { 2616 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link); 2617 hostp->nh_flags &= ~NLM_NH_INIDLE; 2618 } 2619 hostp->nh_refs++; 2620 2621 mutex_exit(&g->lock); 2622 2623 mutex_enter(&hostp->nh_lock); 2624 TAILQ_FOREACH(nvp, &hostp->nh_vholds_list, nv_link) { 2625 vnode_t *vp; 2626 2627 nvp->nv_refcnt++; 2628 mutex_exit(&hostp->nh_lock); 2629 2630 vp = nvp->nv_vp; 2631 2632 if (!EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid)) 2633 goto next_iter; 2634 2635 /* 2636 * Ok, it we found out that vnode vp is under 2637 * control by the exportinfo exi, now we need 2638 * to drop all locks from this vnode, let's 2639 * do it. 2640 */ 2641 nlm_vhold_clean(nvp, hostp->nh_sysid); 2642 2643 next_iter: 2644 mutex_enter(&hostp->nh_lock); 2645 nvp->nv_refcnt--; 2646 } 2647 mutex_exit(&hostp->nh_lock); 2648 2649 mutex_enter(&g->lock); 2650 nlm_host_release_locked(g, hostp); 2651 2652 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp); 2653 } 2654 2655 mutex_exit(&g->lock); 2656 } 2657 2658 void 2659 nlm_unexport(struct exportinfo *exi) 2660 { 2661 struct nlm_globals *g; 2662 2663 rw_enter(&lm_lck, RW_READER); 2664 TAILQ_FOREACH(g, &nlm_zones_list, nlm_link) { 2665 if (g->nlm_zoneid == exi->exi_zoneid) { 2666 /* 2667 * NOTE: If we want to drop lm_lock before 2668 * calling nlm_zone_unexport(), we should break, 2669 * and have a post-rw_exit() snippit like: 2670 * if (g != NULL) 2671 * nlm_zone_unexport(g, exi); 2672 */ 2673 nlm_zone_unexport(g, exi); 2674 break; /* Only going to match once! */ 2675 } 2676 } 2677 rw_exit(&lm_lck); 2678 } 2679 2680 /* 2681 * Allocate new unique sysid. 2682 * In case of failure (no available sysids) 2683 * return LM_NOSYSID. 2684 */ 2685 sysid_t 2686 nlm_sysid_alloc(void) 2687 { 2688 sysid_t ret_sysid = LM_NOSYSID; 2689 2690 rw_enter(&lm_lck, RW_WRITER); 2691 if (nlm_sysid_nidx > LM_SYSID_MAX) 2692 nlm_sysid_nidx = LM_SYSID; 2693 2694 if (!BT_TEST(nlm_sysid_bmap, nlm_sysid_nidx)) { 2695 BT_SET(nlm_sysid_bmap, nlm_sysid_nidx); 2696 ret_sysid = nlm_sysid_nidx++; 2697 } else { 2698 index_t id; 2699 2700 id = bt_availbit(nlm_sysid_bmap, NLM_BMAP_NITEMS); 2701 if (id > 0) { 2702 nlm_sysid_nidx = id + 1; 2703 ret_sysid = id; 2704 BT_SET(nlm_sysid_bmap, id); 2705 } 2706 } 2707 2708 rw_exit(&lm_lck); 2709 return (ret_sysid); 2710 } 2711 2712 void 2713 nlm_sysid_free(sysid_t sysid) 2714 { 2715 ASSERT(sysid >= LM_SYSID && sysid <= LM_SYSID_MAX); 2716 2717 rw_enter(&lm_lck, RW_WRITER); 2718 ASSERT(BT_TEST(nlm_sysid_bmap, sysid)); 2719 BT_CLEAR(nlm_sysid_bmap, sysid); 2720 rw_exit(&lm_lck); 2721 } 2722 2723 /* 2724 * Return true if the request came from a local caller. 2725 * By necessity, this "knows" the netid names invented 2726 * in lm_svc() and nlm_netid_from_knetconfig(). 2727 */ 2728 bool_t 2729 nlm_caller_is_local(SVCXPRT *transp) 2730 { 2731 char *netid; 2732 struct netbuf *rtaddr; 2733 2734 netid = svc_getnetid(transp); 2735 rtaddr = svc_getrpccaller(transp); 2736 2737 if (netid == NULL) 2738 return (FALSE); 2739 2740 if (strcmp(netid, "ticlts") == 0 || 2741 strcmp(netid, "ticotsord") == 0) 2742 return (TRUE); 2743 2744 if (strcmp(netid, "tcp") == 0 || strcmp(netid, "udp") == 0) { 2745 struct sockaddr_in *sin = (void *)rtaddr->buf; 2746 if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) 2747 return (TRUE); 2748 } 2749 if (strcmp(netid, "tcp6") == 0 || strcmp(netid, "udp6") == 0) { 2750 struct sockaddr_in6 *sin6 = (void *)rtaddr->buf; 2751 if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) 2752 return (TRUE); 2753 } 2754 2755 return (FALSE); /* unknown transport */ 2756 } 2757 2758 /* 2759 * Get netid string correspondig to the given knetconfig. 2760 * If not done already, save knc->knc_rdev in our table. 2761 */ 2762 const char * 2763 nlm_knc_to_netid(struct knetconfig *knc) 2764 { 2765 int i; 2766 dev_t rdev; 2767 struct nlm_knc *nc; 2768 const char *netid = NULL; 2769 2770 rw_enter(&lm_lck, RW_READER); 2771 for (i = 0; i < NLM_KNCS; i++) { 2772 nc = &nlm_netconfigs[i]; 2773 2774 if (nc->n_knc.knc_semantics == knc->knc_semantics && 2775 strcmp(nc->n_knc.knc_protofmly, 2776 knc->knc_protofmly) == 0) { 2777 netid = nc->n_netid; 2778 rdev = nc->n_knc.knc_rdev; 2779 break; 2780 } 2781 } 2782 rw_exit(&lm_lck); 2783 2784 if (netid != NULL && rdev == NODEV) { 2785 rw_enter(&lm_lck, RW_WRITER); 2786 if (nc->n_knc.knc_rdev == NODEV) 2787 nc->n_knc.knc_rdev = knc->knc_rdev; 2788 rw_exit(&lm_lck); 2789 } 2790 2791 return (netid); 2792 } 2793 2794 /* 2795 * Get a knetconfig corresponding to the given netid. 2796 * If there's no knetconfig for this netid, ENOENT 2797 * is returned. 2798 */ 2799 int 2800 nlm_knc_from_netid(const char *netid, struct knetconfig *knc) 2801 { 2802 int i, ret; 2803 2804 ret = ENOENT; 2805 for (i = 0; i < NLM_KNCS; i++) { 2806 struct nlm_knc *nknc; 2807 2808 nknc = &nlm_netconfigs[i]; 2809 if (strcmp(netid, nknc->n_netid) == 0 && 2810 nknc->n_knc.knc_rdev != NODEV) { 2811 *knc = nknc->n_knc; 2812 ret = 0; 2813 break; 2814 } 2815 } 2816 2817 return (ret); 2818 } 2819 2820 void 2821 nlm_cprsuspend(void) 2822 { 2823 struct nlm_globals *g; 2824 2825 rw_enter(&lm_lck, RW_READER); 2826 TAILQ_FOREACH(g, &nlm_zones_list, nlm_link) 2827 nlm_suspend_zone(g); 2828 2829 rw_exit(&lm_lck); 2830 } 2831 2832 void 2833 nlm_cprresume(void) 2834 { 2835 struct nlm_globals *g; 2836 2837 rw_enter(&lm_lck, RW_READER); 2838 TAILQ_FOREACH(g, &nlm_zones_list, nlm_link) 2839 nlm_resume_zone(g); 2840 2841 rw_exit(&lm_lck); 2842 } 2843 2844 static void 2845 nlm_nsm_clnt_init(CLIENT *clnt, struct nlm_nsm *nsm) 2846 { 2847 (void) clnt_tli_kinit(clnt, &nsm->ns_knc, &nsm->ns_addr, 0, 2848 NLM_RPC_RETRIES, zone_kcred()); 2849 } 2850 2851 static void 2852 nlm_netbuf_to_netobj(struct netbuf *addr, int *family, netobj *obj) 2853 { 2854 /* LINTED pointer alignment */ 2855 struct sockaddr *sa = (struct sockaddr *)addr->buf; 2856 2857 *family = sa->sa_family; 2858 2859 switch (sa->sa_family) { 2860 case AF_INET: { 2861 /* LINTED pointer alignment */ 2862 struct sockaddr_in *sin = (struct sockaddr_in *)sa; 2863 2864 obj->n_len = sizeof (sin->sin_addr); 2865 obj->n_bytes = (char *)&sin->sin_addr; 2866 break; 2867 } 2868 2869 case AF_INET6: { 2870 /* LINTED pointer alignment */ 2871 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 2872 2873 obj->n_len = sizeof (sin6->sin6_addr); 2874 obj->n_bytes = (char *)&sin6->sin6_addr; 2875 break; 2876 } 2877 2878 default: 2879 VERIFY(0); 2880 break; 2881 } 2882 } 2883