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