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 }