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