Print this page
11083 support NFS server in zone
Portions contributed by: Dan Kruchinin <dan.kruchinin@nexenta.com>
Portions contributed by: Stepan Zastupov <stepan.zastupov@gmail.com>
Portions contributed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Portions contributed by: Mike Zeller <mike@mikezeller.net>
Portions contributed by: Dan McDonald <danmcd@joyent.com>
Portions contributed by: Gordon Ross <gordon.w.ross@gmail.com>
Portions contributed by: Vitaliy Gusev <gusev.vitaliy@gmail.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Jason King <jbk@joyent.com>
Reviewed by: C Fraire <cfraire@me.com>
Change-Id: I22f289d357503f9b48a0bc2482cc4328a6d43d16
        
*** 18,31 ****
   *
   * CDDL HEADER END
   */
  
  /*
-  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
   * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
   * Copyright (c) 2015 by Delphix. All rights reserved.
   * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
   */
  
  #include <sys/param.h>
  #include <sys/errno.h>
  #include <sys/vfs.h>
--- 18,31 ----
   *
   * CDDL HEADER END
   */
  
  /*
   * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
   * Copyright (c) 2015 by Delphix. All rights reserved.
   * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
+  * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
   */
  
  #include <sys/param.h>
  #include <sys/errno.h>
  #include <sys/vfs.h>
*** 51,64 ****
--- 51,66 ----
  #include <nfs/nfs_clnt.h>
  #include <nfs/auth.h>
  
  static struct kmem_cache *exi_cache_handle;
  static void exi_cache_reclaim(void *);
+ static void exi_cache_reclaim_zone(nfs_globals_t *);
  static void exi_cache_trim(struct exportinfo *exi);
  
  extern pri_t minclsyspri;
  
+ /* NFS auth cache statistics */
  volatile uint_t nfsauth_cache_hit;
  volatile uint_t nfsauth_cache_miss;
  volatile uint_t nfsauth_cache_refresh;
  volatile uint_t nfsauth_cache_reclaim;
  volatile uint_t exi_cache_auth_reclaim_failed;
*** 118,258 ****
          char                    *ran_netid;
          list_node_t             ran_node;
  } refreshq_auth_node_t;
  
  /*
!  * Used to manipulate things on the refreshq_queue.
!  * Note that the refresh thread will effectively
!  * pop a node off of the queue, at which point it
   * will no longer need to hold the mutex.
   */
  static kmutex_t refreshq_lock;
  static list_t refreshq_queue;
  static kcondvar_t refreshq_cv;
  
  /*
!  * If there is ever a problem with loading the
!  * module, then nfsauth_fini() needs to be called
!  * to remove state. In that event, since the
!  * refreshq thread has been started, they need to
!  * work together to get rid of state.
   */
  typedef enum nfsauth_refreshq_thread_state {
          REFRESHQ_THREAD_RUNNING,
          REFRESHQ_THREAD_FINI_REQ,
!         REFRESHQ_THREAD_HALTED
  } nfsauth_refreshq_thread_state_t;
  
! nfsauth_refreshq_thread_state_t
! refreshq_thread_state = REFRESHQ_THREAD_HALTED;
  
  static void nfsauth_free_node(struct auth_cache *);
! static void nfsauth_refresh_thread(void);
  
  static int nfsauth_cache_compar(const void *, const void *);
  
! /*
!  * mountd is a server-side only daemon. This will need to be
!  * revisited if the NFS server is ever made zones-aware.
!  */
! kmutex_t        mountd_lock;
! door_handle_t   mountd_dh;
  
  void
  mountd_args(uint_t did)
  {
!         mutex_enter(&mountd_lock);
!         if (mountd_dh != NULL)
!                 door_ki_rele(mountd_dh);
!         mountd_dh = door_ki_lookup(did);
!         mutex_exit(&mountd_lock);
  }
  
  void
  nfsauth_init(void)
  {
!         /*
!          * mountd can be restarted by smf(5). We need to make sure
!          * the updated door handle will safely make it to mountd_dh
!          */
!         mutex_init(&mountd_lock, NULL, MUTEX_DEFAULT, NULL);
  
!         mutex_init(&refreshq_lock, NULL, MUTEX_DEFAULT, NULL);
!         list_create(&refreshq_queue, sizeof (refreshq_exi_node_t),
!             offsetof(refreshq_exi_node_t, ren_node));
  
!         cv_init(&refreshq_cv, NULL, CV_DEFAULT, NULL);
  
          /*
!          * Allocate nfsauth cache handle
           */
!         exi_cache_handle = kmem_cache_create("exi_cache_handle",
!             sizeof (struct auth_cache), 0, NULL, NULL,
!             exi_cache_reclaim, NULL, NULL, 0);
  
!         refreshq_thread_state = REFRESHQ_THREAD_RUNNING;
!         (void) zthread_create(NULL, 0, nfsauth_refresh_thread,
!             NULL, 0, minclsyspri);
  }
  
- /*
-  * Finalization routine for nfsauth. It is important to call this routine
-  * before destroying the exported_lock.
-  */
  void
! nfsauth_fini(void)
  {
          refreshq_exi_node_t     *ren;
  
!         /*
!          * Prevent the nfsauth_refresh_thread from getting new
!          * work.
!          */
!         mutex_enter(&refreshq_lock);
!         if (refreshq_thread_state != REFRESHQ_THREAD_HALTED) {
!                 refreshq_thread_state = REFRESHQ_THREAD_FINI_REQ;
!                 cv_broadcast(&refreshq_cv);
  
!                 /*
!                  * Also, wait for nfsauth_refresh_thread() to exit.
!                  */
!                 while (refreshq_thread_state != REFRESHQ_THREAD_HALTED) {
!                         cv_wait(&refreshq_cv, &refreshq_lock);
                  }
!         }
!         mutex_exit(&refreshq_lock);
  
          /*
           * Walk the exi_list and in turn, walk the auth_lists and free all
           * lists.  In addition, free INVALID auth_cache entries.
           */
!         while ((ren = list_remove_head(&refreshq_queue))) {
                  refreshq_auth_node_t *ran;
  
                  while ((ran = list_remove_head(&ren->ren_authlist)) != NULL) {
                          struct auth_cache *p = ran->ran_auth;
                          if (p->auth_state == NFS_AUTH_INVALID)
                                  nfsauth_free_node(p);
                          strfree(ran->ran_netid);
!                         kmem_free(ran, sizeof (refreshq_auth_node_t));
                  }
  
                  list_destroy(&ren->ren_authlist);
                  exi_rele(ren->ren_exi);
!                 kmem_free(ren, sizeof (refreshq_exi_node_t));
          }
!         list_destroy(&refreshq_queue);
  
!         cv_destroy(&refreshq_cv);
!         mutex_destroy(&refreshq_lock);
  
!         mutex_destroy(&mountd_lock);
  
!         /*
!          * Deallocate nfsauth cache handle
!          */
!         kmem_cache_destroy(exi_cache_handle);
  }
  
  /*
   * Convert the address in a netbuf to
   * a hash index for the auth_cache table.
--- 120,293 ----
          char                    *ran_netid;
          list_node_t             ran_node;
  } refreshq_auth_node_t;
  
  /*
!  * Used to manipulate things on the refreshq_queue.  Note that the refresh
!  * thread will effectively pop a node off of the queue, at which point it
   * will no longer need to hold the mutex.
   */
  static kmutex_t refreshq_lock;
  static list_t refreshq_queue;
  static kcondvar_t refreshq_cv;
  
  /*
!  * If there is ever a problem with loading the module, then nfsauth_fini()
!  * needs to be called to remove state.  In that event, since the refreshq
!  * thread has been started, they need to work together to get rid of state.
   */
  typedef enum nfsauth_refreshq_thread_state {
          REFRESHQ_THREAD_RUNNING,
          REFRESHQ_THREAD_FINI_REQ,
!         REFRESHQ_THREAD_HALTED,
!         REFRESHQ_THREAD_NEED_CREATE
  } nfsauth_refreshq_thread_state_t;
  
! typedef struct nfsauth_globals {
!         kmutex_t        mountd_lock;
!         door_handle_t   mountd_dh;
  
+         /*
+          * Used to manipulate things on the refreshq_queue.  Note that the
+          * refresh thread will effectively pop a node off of the queue,
+          * at which point it will no longer need to hold the mutex.
+          */
+         kmutex_t        refreshq_lock;
+         list_t          refreshq_queue;
+         kcondvar_t      refreshq_cv;
+ 
+         /*
+          * A list_t would be overkill.  These are auth_cache entries which are
+          * no longer linked to an exi.  It should be the case that all of their
+          * states are NFS_AUTH_INVALID, i.e., the only way to be put on this
+          * list is iff their state indicated that they had been placed on the
+          * refreshq_queue.
+          *
+          * Note that while there is no link from the exi or back to the exi,
+          * the exi can not go away until these entries are harvested.
+          */
+         struct auth_cache               *refreshq_dead_entries;
+         nfsauth_refreshq_thread_state_t refreshq_thread_state;
+ 
+ } nfsauth_globals_t;
+ 
  static void nfsauth_free_node(struct auth_cache *);
! static void nfsauth_refresh_thread(nfsauth_globals_t *);
  
  static int nfsauth_cache_compar(const void *, const void *);
  
! static nfsauth_globals_t *
! nfsauth_get_zg(void)
! {
!         nfs_globals_t *ng = nfs_srv_getzg();
!         nfsauth_globals_t *nag = ng->nfs_auth;
!         ASSERT(nag != NULL);
!         return (nag);
! }
  
  void
  mountd_args(uint_t did)
  {
!         nfsauth_globals_t *nag;
! 
!         nag = nfsauth_get_zg();
!         mutex_enter(&nag->mountd_lock);
!         if (nag->mountd_dh != NULL)
!                 door_ki_rele(nag->mountd_dh);
!         nag->mountd_dh = door_ki_lookup(did);
!         mutex_exit(&nag->mountd_lock);
  }
  
  void
  nfsauth_init(void)
  {
!         exi_cache_handle = kmem_cache_create("exi_cache_handle",
!             sizeof (struct auth_cache), 0, NULL, NULL,
!             exi_cache_reclaim, NULL, NULL, 0);
! }
  
! void
! nfsauth_fini(void)
! {
!         kmem_cache_destroy(exi_cache_handle);
! }
  
! void
! nfsauth_zone_init(nfs_globals_t *ng)
! {
!         nfsauth_globals_t *nag;
  
+         nag = kmem_zalloc(sizeof (*nag), KM_SLEEP);
+ 
          /*
!          * mountd can be restarted by smf(5).  We need to make sure
!          * the updated door handle will safely make it to mountd_dh.
           */
!         mutex_init(&nag->mountd_lock, NULL, MUTEX_DEFAULT, NULL);
!         mutex_init(&nag->refreshq_lock, NULL, MUTEX_DEFAULT, NULL);
!         list_create(&nag->refreshq_queue, sizeof (refreshq_exi_node_t),
!             offsetof(refreshq_exi_node_t, ren_node));
!         cv_init(&nag->refreshq_cv, NULL, CV_DEFAULT, NULL);
!         nag->refreshq_thread_state = REFRESHQ_THREAD_NEED_CREATE;
  
!         ng->nfs_auth = nag;
  }
  
  void
! nfsauth_zone_shutdown(nfs_globals_t *ng)
  {
          refreshq_exi_node_t     *ren;
+         nfsauth_globals_t       *nag = ng->nfs_auth;
  
!         /* Prevent the nfsauth_refresh_thread from getting new work */
!         mutex_enter(&nag->refreshq_lock);
!         if (nag->refreshq_thread_state == REFRESHQ_THREAD_RUNNING) {
!                 nag->refreshq_thread_state = REFRESHQ_THREAD_FINI_REQ;
!                 cv_broadcast(&nag->refreshq_cv);
  
!                 /* Wait for nfsauth_refresh_thread() to exit */
!                 while (nag->refreshq_thread_state != REFRESHQ_THREAD_HALTED)
!                         cv_wait(&nag->refreshq_cv, &nag->refreshq_lock);
          }
!         mutex_exit(&nag->refreshq_lock);
  
          /*
           * Walk the exi_list and in turn, walk the auth_lists and free all
           * lists.  In addition, free INVALID auth_cache entries.
           */
!         while ((ren = list_remove_head(&nag->refreshq_queue))) {
                  refreshq_auth_node_t *ran;
  
                  while ((ran = list_remove_head(&ren->ren_authlist)) != NULL) {
                          struct auth_cache *p = ran->ran_auth;
                          if (p->auth_state == NFS_AUTH_INVALID)
                                  nfsauth_free_node(p);
                          strfree(ran->ran_netid);
!                         kmem_free(ran, sizeof (*ran));
                  }
  
                  list_destroy(&ren->ren_authlist);
                  exi_rele(ren->ren_exi);
!                 kmem_free(ren, sizeof (*ren));
          }
! }
  
! void
! nfsauth_zone_fini(nfs_globals_t *ng)
! {
!         nfsauth_globals_t *nag = ng->nfs_auth;
  
!         ng->nfs_auth = NULL;
  
!         list_destroy(&nag->refreshq_queue);
!         cv_destroy(&nag->refreshq_cv);
!         mutex_destroy(&nag->refreshq_lock);
!         mutex_destroy(&nag->mountd_lock);
!         /* Extra cleanup. */
!         if (nag->mountd_dh != NULL)
!                 door_ki_rele(nag->mountd_dh);
!         kmem_free(nag, sizeof (*nag));
  }
  
  /*
   * Convert the address in a netbuf to
   * a hash index for the auth_cache table.
*** 340,352 ****
  
  /*
   * Callup to the mountd to get access information in the kernel.
   */
  static bool_t
! nfsauth_retrieve(struct exportinfo *exi, char *req_netid, int flavor,
!     struct netbuf *addr, int *access, cred_t *clnt_cred, uid_t *srv_uid,
!     gid_t *srv_gid, uint_t *srv_gids_cnt, gid_t **srv_gids)
  {
          varg_t                    varg = {0};
          nfsauth_res_t             res = {0};
          XDR                       xdrs;
          size_t                    absz;
--- 375,388 ----
  
  /*
   * Callup to the mountd to get access information in the kernel.
   */
  static bool_t
! nfsauth_retrieve(nfsauth_globals_t *nag, struct exportinfo *exi,
!     char *req_netid, int flavor, struct netbuf *addr, int *access,
!     cred_t *clnt_cred, uid_t *srv_uid, gid_t *srv_gid, uint_t *srv_gids_cnt,
!     gid_t **srv_gids)
  {
          varg_t                    varg = {0};
          nfsauth_res_t             res = {0};
          XDR                       xdrs;
          size_t                    absz;
*** 415,429 ****
          da.desc_num = 0;
          da.rbuf = NULL;
          da.rsize = 1;
  
  retry:
!         mutex_enter(&mountd_lock);
!         dh = mountd_dh;
          if (dh != NULL)
                  door_ki_hold(dh);
!         mutex_exit(&mountd_lock);
  
          if (dh == NULL) {
                  /*
                   * The rendezvous point has not been established yet!
                   * This could mean that either mountd(1m) has not yet
--- 451,465 ----
          da.desc_num = 0;
          da.rbuf = NULL;
          da.rsize = 1;
  
  retry:
!         mutex_enter(&nag->mountd_lock);
!         dh = nag->mountd_dh;
          if (dh != NULL)
                  door_ki_hold(dh);
!         mutex_exit(&nag->mountd_lock);
  
          if (dh == NULL) {
                  /*
                   * The rendezvous point has not been established yet!
                   * This could mean that either mountd(1m) has not yet
*** 489,504 ****
                                   * the (existing) door on us; we
                                   * want to wait to give smf(5) a
                                   * chance to restart mountd(1m)
                                   * and establish a new door handle.
                                   */
!                                 mutex_enter(&mountd_lock);
!                                 if (dh == mountd_dh) {
!                                         door_ki_rele(mountd_dh);
!                                         mountd_dh = NULL;
                                  }
!                                 mutex_exit(&mountd_lock);
                                  delay(hz);
                                  goto retry;
                          }
                          /*
                           * If the door was _not_ revoked on us,
--- 525,540 ----
                                   * the (existing) door on us; we
                                   * want to wait to give smf(5) a
                                   * chance to restart mountd(1m)
                                   * and establish a new door handle.
                                   */
!                                 mutex_enter(&nag->mountd_lock);
!                                 if (dh == nag->mountd_dh) {
!                                         door_ki_rele(nag->mountd_dh);
!                                         nag->mountd_dh = NULL;
                                  }
!                                 mutex_exit(&nag->mountd_lock);
                                  delay(hz);
                                  goto retry;
                          }
                          /*
                           * If the door was _not_ revoked on us,
*** 591,601 ****
  
          return (TRUE);
  }
  
  static void
! nfsauth_refresh_thread(void)
  {
          refreshq_exi_node_t     *ren;
          refreshq_auth_node_t    *ran;
  
          struct exportinfo       *exi;
--- 627,637 ----
  
          return (TRUE);
  }
  
  static void
! nfsauth_refresh_thread(nfsauth_globals_t *nag)
  {
          refreshq_exi_node_t     *ren;
          refreshq_auth_node_t    *ran;
  
          struct exportinfo       *exi;
*** 603,631 ****
          int                     access;
          bool_t                  retrieval;
  
          callb_cpr_t             cprinfo;
  
!         CALLB_CPR_INIT(&cprinfo, &refreshq_lock, callb_generic_cpr,
              "nfsauth_refresh");
  
          for (;;) {
!                 mutex_enter(&refreshq_lock);
!                 if (refreshq_thread_state != REFRESHQ_THREAD_RUNNING) {
                          /* Keep the hold on the lock! */
                          break;
                  }
  
!                 ren = list_remove_head(&refreshq_queue);
                  if (ren == NULL) {
                          CALLB_CPR_SAFE_BEGIN(&cprinfo);
!                         cv_wait(&refreshq_cv, &refreshq_lock);
!                         CALLB_CPR_SAFE_END(&cprinfo, &refreshq_lock);
!                         mutex_exit(&refreshq_lock);
                          continue;
                  }
!                 mutex_exit(&refreshq_lock);
  
                  exi = ren->ren_exi;
                  ASSERT(exi != NULL);
  
                  /*
--- 639,667 ----
          int                     access;
          bool_t                  retrieval;
  
          callb_cpr_t             cprinfo;
  
!         CALLB_CPR_INIT(&cprinfo, &nag->refreshq_lock, callb_generic_cpr,
              "nfsauth_refresh");
  
          for (;;) {
!                 mutex_enter(&nag->refreshq_lock);
!                 if (nag->refreshq_thread_state != REFRESHQ_THREAD_RUNNING) {
                          /* Keep the hold on the lock! */
                          break;
                  }
  
!                 ren = list_remove_head(&nag->refreshq_queue);
                  if (ren == NULL) {
                          CALLB_CPR_SAFE_BEGIN(&cprinfo);
!                         cv_wait(&nag->refreshq_cv, &nag->refreshq_lock);
!                         CALLB_CPR_SAFE_END(&cprinfo, &nag->refreshq_lock);
!                         mutex_exit(&nag->refreshq_lock);
                          continue;
                  }
!                 mutex_exit(&nag->refreshq_lock);
  
                  exi = ren->ren_exi;
                  ASSERT(exi != NULL);
  
                  /*
*** 668,678 ****
                           * will miss such advisory, nothing catastrophic
                           * happens: we will just spin longer here before the
                           * shutdown.
                           */
                          if (p->auth_state == NFS_AUTH_INVALID ||
!                             refreshq_thread_state != REFRESHQ_THREAD_RUNNING) {
                                  mutex_exit(&p->auth_lock);
  
                                  if (p->auth_state == NFS_AUTH_INVALID)
                                          nfsauth_free_node(p);
  
--- 704,715 ----
                           * will miss such advisory, nothing catastrophic
                           * happens: we will just spin longer here before the
                           * shutdown.
                           */
                          if (p->auth_state == NFS_AUTH_INVALID ||
!                             nag->refreshq_thread_state !=
!                             REFRESHQ_THREAD_RUNNING) {
                                  mutex_exit(&p->auth_lock);
  
                                  if (p->auth_state == NFS_AUTH_INVALID)
                                          nfsauth_free_node(p);
  
*** 703,713 ****
                           * the same netid. It doesn't matter. So
                           * when we refresh, we simply use the netid
                           * of the request which triggered the
                           * refresh attempt.
                           */
!                         retrieval = nfsauth_retrieve(exi, netid,
                              p->auth_flavor, &p->auth_clnt->authc_addr, &access,
                              p->auth_clnt_cred, &uid, &gid, &ngids, &gids);
  
                          /*
                           * This can only be set in one other place
--- 740,750 ----
                           * the same netid. It doesn't matter. So
                           * when we refresh, we simply use the netid
                           * of the request which triggered the
                           * refresh attempt.
                           */
!                         retrieval = nfsauth_retrieve(nag, exi, netid,
                              p->auth_flavor, &p->auth_clnt->authc_addr, &access,
                              p->auth_clnt_cred, &uid, &gid, &ngids, &gids);
  
                          /*
                           * This can only be set in one other place
*** 750,762 ****
                  list_destroy(&ren->ren_authlist);
                  exi_rele(ren->ren_exi);
                  kmem_free(ren, sizeof (refreshq_exi_node_t));
          }
  
!         refreshq_thread_state = REFRESHQ_THREAD_HALTED;
!         cv_broadcast(&refreshq_cv);
          CALLB_CPR_EXIT(&cprinfo);
          zthread_exit();
  }
  
  int
  nfsauth_cache_clnt_compar(const void *v1, const void *v2)
--- 787,800 ----
                  list_destroy(&ren->ren_authlist);
                  exi_rele(ren->ren_exi);
                  kmem_free(ren, sizeof (refreshq_exi_node_t));
          }
  
!         nag->refreshq_thread_state = REFRESHQ_THREAD_HALTED;
!         cv_broadcast(&nag->refreshq_cv);
          CALLB_CPR_EXIT(&cprinfo);
+         DTRACE_PROBE(nfsauth__nfsauth__refresh__thread__exit);
          zthread_exit();
  }
  
  int
  nfsauth_cache_clnt_compar(const void *v1, const void *v2)
*** 824,833 ****
--- 862,872 ----
   */
  static int
  nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
      cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
  {
+         nfsauth_globals_t       *nag;
          struct netbuf           *taddrmask;
          struct netbuf           addr;   /* temporary copy of client's address */
          const struct netbuf     *claddr;
          avl_tree_t              *tree;
          struct auth_cache       ac;     /* used as a template for avl_find() */
*** 843,852 ****
--- 882,894 ----
  
          avl_index_t             where;  /* used for avl_find()/avl_insert() */
  
          ASSERT(cr != NULL);
  
+         ASSERT3P(curzone->zone_id, ==, exi->exi_zoneid);
+         nag = nfsauth_get_zg();
+ 
          /*
           * Now check whether this client already
           * has an entry for this flavor in the cache
           * for this export.
           * Get the caller's address, mask off the
*** 855,866 ****
--- 897,912 ----
           * it to find the chain of cache entries.
           */
  
          claddr = svc_getrpccaller(req->rq_xprt);
          addr = *claddr;
+         if (claddr->len != 0) {
                  addr.buf = kmem_alloc(addr.maxlen, KM_SLEEP);
                  bcopy(claddr->buf, addr.buf, claddr->len);
+         } else {
+                 addr.buf = NULL;
+         }
  
          SVC_GETADDRMASK(req->rq_xprt, SVC_TATTR_ADDRMASK, (void **)&taddrmask);
          ASSERT(taddrmask != NULL);
          addrmask(&addr, taddrmask);
  
*** 1002,1013 ****
                  kmem_free(addr.buf, addr.maxlen);
                  addr = p->auth_clnt->authc_addr;
  
                  atomic_inc_uint(&nfsauth_cache_miss);
  
!                 res = nfsauth_retrieve(exi, svc_getnetid(req->rq_xprt), flavor,
!                     &addr, &access, cr, &tmpuid, &tmpgid, &tmpngids, &tmpgids);
  
                  p->auth_access = access;
                  p->auth_time = p->auth_freshness = gethrestime_sec();
  
                  if (res == TRUE) {
--- 1048,1060 ----
                  kmem_free(addr.buf, addr.maxlen);
                  addr = p->auth_clnt->authc_addr;
  
                  atomic_inc_uint(&nfsauth_cache_miss);
  
!                 res = nfsauth_retrieve(nag, exi, svc_getnetid(req->rq_xprt),
!                     flavor, &addr, &access, cr, &tmpuid, &tmpgid, &tmpngids,
!                     &tmpgids);
  
                  p->auth_access = access;
                  p->auth_time = p->auth_freshness = gethrestime_sec();
  
                  if (res == TRUE) {
*** 1088,1112 ****
                          ran = kmem_alloc(sizeof (refreshq_auth_node_t),
                              KM_SLEEP);
                          ran->ran_auth = p;
                          ran->ran_netid = strdup(svc_getnetid(req->rq_xprt));
  
!                         mutex_enter(&refreshq_lock);
                          /*
!                          * We should not add a work queue
!                          * item if the thread is not
!                          * accepting them.
                           */
!                         if (refreshq_thread_state == REFRESHQ_THREAD_RUNNING) {
                                  refreshq_exi_node_t *ren;
  
                                  /*
                                   * Is there an existing exi_list?
                                   */
!                                 for (ren = list_head(&refreshq_queue);
                                      ren != NULL;
!                                     ren = list_next(&refreshq_queue, ren)) {
                                          if (ren->ren_exi == exi) {
                                                  list_insert_tail(
                                                      &ren->ren_authlist, ran);
                                                  break;
                                          }
--- 1135,1171 ----
                          ran = kmem_alloc(sizeof (refreshq_auth_node_t),
                              KM_SLEEP);
                          ran->ran_auth = p;
                          ran->ran_netid = strdup(svc_getnetid(req->rq_xprt));
  
!                         mutex_enter(&nag->refreshq_lock);
! 
!                         if (nag->refreshq_thread_state ==
!                             REFRESHQ_THREAD_NEED_CREATE) {
!                                 /* Launch nfsauth refresh thread */
!                                 nag->refreshq_thread_state =
!                                     REFRESHQ_THREAD_RUNNING;
!                                 (void) zthread_create(NULL, 0,
!                                     nfsauth_refresh_thread, nag, 0,
!                                     minclsyspri);
!                         }
! 
                          /*
!                          * We should not add a work queue item if the thread
!                          * is not accepting them.
                           */
!                         if (nag->refreshq_thread_state ==
!                             REFRESHQ_THREAD_RUNNING) {
                                  refreshq_exi_node_t *ren;
  
                                  /*
                                   * Is there an existing exi_list?
                                   */
!                                 for (ren = list_head(&nag->refreshq_queue);
                                      ren != NULL;
!                                     ren = list_next(&nag->refreshq_queue,
!                                     ren)) {
                                          if (ren->ren_exi == exi) {
                                                  list_insert_tail(
                                                      &ren->ren_authlist, ran);
                                                  break;
                                          }
*** 1125,1144 ****
                                              offsetof(refreshq_auth_node_t,
                                              ran_node));
  
                                          list_insert_tail(&ren->ren_authlist,
                                              ran);
!                                         list_insert_tail(&refreshq_queue, ren);
                                  }
  
!                                 cv_broadcast(&refreshq_cv);
                          } else {
                                  strfree(ran->ran_netid);
                                  kmem_free(ran, sizeof (refreshq_auth_node_t));
                          }
  
!                         mutex_exit(&refreshq_lock);
                  } else {
                          mutex_exit(&p->auth_lock);
                  }
  
                  nach = atomic_inc_uint_nv(&nfsauth_cache_hit);
--- 1184,1204 ----
                                              offsetof(refreshq_auth_node_t,
                                              ran_node));
  
                                          list_insert_tail(&ren->ren_authlist,
                                              ran);
!                                         list_insert_tail(&nag->refreshq_queue,
!                                             ren);
                                  }
  
!                                 cv_broadcast(&nag->refreshq_cv);
                          } else {
                                  strfree(ran->ran_netid);
                                  kmem_free(ran, sizeof (refreshq_auth_node_t));
                          }
  
!                         mutex_exit(&nag->refreshq_lock);
                  } else {
                          mutex_exit(&p->auth_lock);
                  }
  
                  nach = atomic_inc_uint_nv(&nfsauth_cache_hit);
*** 1160,1171 ****
  
          ASSERT(p == NULL);
  
          atomic_inc_uint(&nfsauth_cache_miss);
  
!         if (nfsauth_retrieve(exi, svc_getnetid(req->rq_xprt), flavor, &addr,
!             &access, cr, &tmpuid, &tmpgid, &tmpngids, &tmpgids)) {
                  if (uid != NULL)
                          *uid = tmpuid;
                  if (gid != NULL)
                          *gid = tmpgid;
                  if (ngids != NULL && gids != NULL) {
--- 1220,1231 ----
  
          ASSERT(p == NULL);
  
          atomic_inc_uint(&nfsauth_cache_miss);
  
!         if (nfsauth_retrieve(nag, exi, svc_getnetid(req->rq_xprt), flavor,
!             &addr, &access, cr, &tmpuid, &tmpgid, &tmpngids, &tmpgids)) {
                  if (uid != NULL)
                          *uid = tmpuid;
                  if (gid != NULL)
                          *gid = tmpgid;
                  if (ngids != NULL && gids != NULL) {
*** 1408,1443 ****
                          nfsauth_free_clnt_node(node);
          }
  }
  
  /*
!  * Called by the kernel memory allocator when
!  * memory is low. Free unused cache entries.
!  * If that's not enough, the VM system will
!  * call again for some more.
   */
  /*ARGSUSED*/
  void
  exi_cache_reclaim(void *cdrarg)
  {
          int i;
          struct exportinfo *exi;
  
!         rw_enter(&exported_lock, RW_READER);
  
          for (i = 0; i < EXPTABLESIZE; i++) {
!                 for (exi = exptable[i]; exi; exi = exi->fid_hash.next) {
                          exi_cache_trim(exi);
                  }
-         }
  
!         rw_exit(&exported_lock);
  
          atomic_inc_uint(&nfsauth_cache_reclaim);
  }
  
! void
  exi_cache_trim(struct exportinfo *exi)
  {
          struct auth_cache_clnt *c;
          struct auth_cache_clnt *nextc;
          struct auth_cache *p;
--- 1468,1526 ----
                          nfsauth_free_clnt_node(node);
          }
  }
  
  /*
!  * Called by the kernel memory allocator when memory is low.
!  * Free unused cache entries. If that's not enough, the VM system
!  * will call again for some more.
!  *
!  * This needs to operate on all zones, so we take a reader lock
!  * on the list of zones and walk the list.  This is OK here
!  * becuase exi_cache_trim doesn't block or cause new objects
!  * to be allocated (basically just frees lots of stuff).
!  * Use care if nfssrv_globals_rwl is taken as reader in any
!  * other cases because it will block nfs_server_zone_init
!  * and nfs_server_zone_fini, which enter as writer.
   */
  /*ARGSUSED*/
  void
  exi_cache_reclaim(void *cdrarg)
  {
+         nfs_globals_t *ng;
+ 
+         rw_enter(&nfssrv_globals_rwl, RW_READER);
+ 
+         ng = list_head(&nfssrv_globals_list);
+         while (ng != NULL) {
+                 exi_cache_reclaim_zone(ng);
+                 ng = list_next(&nfssrv_globals_list, ng);
+         }
+ 
+         rw_exit(&nfssrv_globals_rwl);
+ }
+ 
+ static void
+ exi_cache_reclaim_zone(nfs_globals_t *ng)
+ {
          int i;
          struct exportinfo *exi;
+         nfs_export_t *ne = ng->nfs_export;
  
!         rw_enter(&ne->exported_lock, RW_READER);
  
          for (i = 0; i < EXPTABLESIZE; i++) {
!                 for (exi = ne->exptable[i]; exi; exi = exi->fid_hash.next)
                          exi_cache_trim(exi);
          }
  
!         rw_exit(&ne->exported_lock);
  
          atomic_inc_uint(&nfsauth_cache_reclaim);
  }
  
! static void
  exi_cache_trim(struct exportinfo *exi)
  {
          struct auth_cache_clnt *c;
          struct auth_cache_clnt *nextc;
          struct auth_cache *p;