Print this page
NEX-19996 exi_id_get_next() calls should be WRITER locked
NEX-20014 NFS v4 state lock mutex exited before entered (on error path)
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
NEX-15279 support NFS server in zone
NEX-15520 online NFS shares cause zoneadm halt to hang in nfs_export_zone_fini
Portions contributed by: Dan Kruchinin dan.kruchinin@nexenta.com
Portions contributed by: Stepan Zastupov stepan.zastupov@gmail.com
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-9275 Got "bad mutex" panic when run IO to nfs share from clients
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
NEX-6778 NFS kstats leak and cause system to hang
Revert "NEX-4261 Per-client NFS server IOPS, bandwidth, and latency kstats"
This reverts commit 586c3ab1927647487f01c337ddc011c642575a52.
Revert "NEX-5354 Aggregated IOPS, bandwidth, and latency kstats for NFS server"
This reverts commit c91d7614da8618ef48018102b077f60ecbbac8c2.
Revert "NEX-5667 nfssrv_stats_flags does not work for aggregated kstats"
This reverts commit 3dcf42618be7dd5f408c327f429c81e07ca08e74.
Revert "NEX-5750 Time values for aggregated NFS server kstats should be normalized"
This reverts commit 1f4d4f901153b0191027969fa4a8064f9d3b9ee1.
Revert "NEX-5942 Panic in rfs4_minorvers_mismatch() with NFSv4.1 client"
This reverts commit 40766417094a162f5e4cc8786c0fa0a7e5871cd9.
Revert "NEX-5752 NFS server: namespace collision in kstats"
This reverts commit ae81e668db86050da8e483264acb0cce0444a132.
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-4261 Per-client NFS server IOPS, bandwidth, and latency kstats
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-3097 IOPS, bandwidth, and latency kstats for NFS server
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
NEX-2345 nfsauth_cache_get() could spend a lot of time walking exi_cache
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
        
*** 18,36 ****
   *
   * CDDL HEADER END
   */
  
  /*
-  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
   * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
   */
  
  /*
   *      Copyright 1983, 1984, 1985, 1986, 1987, 1988, 1989  AT&T.
   *              All rights reserved.
   */
  
  
  #include <sys/types.h>
  #include <sys/param.h>
  #include <sys/time.h>
  #include <sys/vfs.h>
--- 18,38 ----
   *
   * CDDL HEADER END
   */
  
  /*
   * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
   */
  
  /*
   *      Copyright 1983, 1984, 1985, 1986, 1987, 1988, 1989  AT&T.
   *              All rights reserved.
   */
  
+ /*
+  * Copyright 2018 Nexenta Systems, Inc.
+  */
  
  #include <sys/types.h>
  #include <sys/param.h>
  #include <sys/time.h>
  #include <sys/vfs.h>
*** 63,80 ****
  #include <nfs/nfs_clnt.h>
  #include <nfs/nfs_acl.h>
  #include <nfs/nfs_log.h>
  #include <nfs/lm.h>
  #include <sys/sunddi.h>
- #include <sys/pkp_hash.h>
  
! treenode_t *ns_root;
  
! struct exportinfo *exptable_path_hash[PKP_HASH_SIZE];
! struct exportinfo *exptable[EXPTABLESIZE];
  
! static int      unexport(exportinfo_t *);
  static void     exportfree(exportinfo_t *);
  static int      loadindex(exportdata_t *);
  
  extern void     nfsauth_cache_free(exportinfo_t *);
  extern int      sec_svc_loadrootnames(int, int, caddr_t **, model_t);
--- 65,95 ----
  #include <nfs/nfs_clnt.h>
  #include <nfs/nfs_acl.h>
  #include <nfs/nfs_log.h>
  #include <nfs/lm.h>
  #include <sys/sunddi.h>
  
! static zone_key_t nfs_export_key;
  
! /*
!  * exi_id support
!  *
!  * exi_id_next          The next exi_id available.
!  * exi_id_overflow      The exi_id_next already overflowed, so we should
!  *                      thoroughly check for duplicates.
!  * exi_id_tree          AVL tree indexed by exi_id.
!  * nfs_exi_id_lock      Lock to protect the export ID list
!  *
!  * All exi_id_next, exi_id_overflow, and exi_id_tree are protected by
!  * nfs_exi_id_lock.
!  */
! static int exi_id_next;
! static bool_t exi_id_overflow;
! avl_tree_t exi_id_tree;
! kmutex_t nfs_exi_id_lock;
  
! static int      unexport(nfs_export_t *, exportinfo_t *);
  static void     exportfree(exportinfo_t *);
  static int      loadindex(exportdata_t *);
  
  extern void     nfsauth_cache_free(exportinfo_t *);
  extern int      sec_svc_loadrootnames(int, int, caddr_t **, model_t);
*** 81,112 ****
  extern void     sec_svc_freerootnames(int, int, caddr_t *);
  
  static int build_seclist_nodups(exportdata_t *, secinfo_t *, int);
  static void srv_secinfo_add(secinfo_t **, int *, secinfo_t *, int, int);
  static void srv_secinfo_remove(secinfo_t **, int *, secinfo_t *, int);
! static void srv_secinfo_treeclimb(exportinfo_t *, secinfo_t *, int, bool_t);
  
  #ifdef VOLATILE_FH_TEST
  static struct ex_vol_rename *find_volrnm_fh(exportinfo_t *, nfs_fh4 *);
  static uint32_t find_volrnm_fh_id(exportinfo_t *, nfs_fh4 *);
  static void free_volrnm_list(exportinfo_t *);
  #endif /* VOLATILE_FH_TEST */
  
- /*
-  * exported_lock        Read/Write lock that protects the exportinfo list.
-  *                      This lock must be held when searching or modifiying
-  *                      the exportinfo list.
-  */
- krwlock_t exported_lock;
- 
- /*
-  * "public" and default (root) location for public filehandle
-  */
- struct exportinfo *exi_public, *exi_root;
- 
- fid_t exi_rootfid;      /* for checking the default public file handle */
- 
  fhandle_t nullfh2;      /* for comparing V2 filehandles */
  
  /*
   * macro for static dtrace probes to trace server namespace ref count mods.
   */
--- 96,114 ----
  extern void     sec_svc_freerootnames(int, int, caddr_t *);
  
  static int      build_seclist_nodups(exportdata_t *, secinfo_t *, int);
  static void     srv_secinfo_add(secinfo_t **, int *, secinfo_t *, int, int);
  static void     srv_secinfo_remove(secinfo_t **, int *, secinfo_t *, int);
! static void     srv_secinfo_treeclimb(nfs_export_t *, exportinfo_t *,
!                     secinfo_t *, int, bool_t);
  
  #ifdef VOLATILE_FH_TEST
  static struct ex_vol_rename *find_volrnm_fh(exportinfo_t *, nfs_fh4 *);
  static uint32_t find_volrnm_fh_id(exportinfo_t *, nfs_fh4 *);
  static void     free_volrnm_list(exportinfo_t *);
  #endif /* VOLATILE_FH_TEST */
  
  fhandle_t nullfh2;      /* for comparing V2 filehandles */
  
  /*
   * macro for static dtrace probes to trace server namespace ref count mods.
   */
*** 115,124 ****
--- 117,132 ----
                  char *, (tag), int, (int)(flav), int, (int)(aftcnt))
  
  
  #define exptablehash(fsid, fid) (nfs_fhhash((fsid), (fid)) & (EXPTABLESIZE - 1))
  
+ extern nfs_export_t *
+ nfs_get_export(void)
+ {
+         return (zone_getspecific(nfs_export_key, curzone));
+ }
+ 
  static uint8_t
  xor_hash(uint8_t *data, int len)
  {
          uint8_t h = 0;
  
*** 701,716 ****
   * For NFS V4.
   * Add or remove the newly exported or unexported security flavors of the
   * given exportinfo from its ancestors upto the system root.
   */
  void
! srv_secinfo_treeclimb(exportinfo_t *exip, secinfo_t *sec, int seccnt,
!     bool_t isadd)
  {
          treenode_t *tnode = exip->exi_tree;
  
!         ASSERT(RW_WRITE_HELD(&exported_lock));
          ASSERT(tnode != NULL);
  
          if (seccnt == 0)
                  return;
  
--- 709,724 ----
   * For NFS V4.
   * Add or remove the newly exported or unexported security flavors of the
   * given exportinfo from its ancestors upto the system root.
   */
  void
! srv_secinfo_treeclimb(nfs_export_t *ne, exportinfo_t *exip, secinfo_t *sec,
!     int seccnt, bool_t isadd)
  {
          treenode_t *tnode = exip->exi_tree;
  
!         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
          ASSERT(tnode != NULL);
  
          if (seccnt == 0)
                  return;
  
*** 780,894 ****
          if ((exi)->hash_name.next) \
                  (exi)->hash_name.next->hash_name.prev = (exi); \
          *(bucket) = (exi);
  
  void
! export_link(exportinfo_t *exi)
  {
          exportinfo_t **bckt;
  
!         bckt = &exptable[exptablehash(&exi->exi_fsid, &exi->exi_fid)];
          exp_hash_link(exi, fid_hash, bckt);
  
!         bckt = &exptable_path_hash[pkp_tab_hash(exi->exi_export.ex_path,
              strlen(exi->exi_export.ex_path))];
          exp_hash_link(exi, path_hash, bckt);
  }
  
  /*
!  * Initialization routine for export routines. Should only be called once.
   */
  int
! nfs_exportinit(void)
  {
!         int error;
          int i;
  
!         rw_init(&exported_lock, NULL, RW_DEFAULT, NULL);
  
          /*
           * Allocate the place holder for the public file handle, which
           * is all zeroes. It is initially set to the root filesystem.
           */
!         exi_root = kmem_zalloc(sizeof (*exi_root), KM_SLEEP);
!         exi_public = exi_root;
  
!         exi_root->exi_export.ex_flags = EX_PUBLIC;
!         exi_root->exi_export.ex_pathlen = 1;    /* length of "/" */
!         exi_root->exi_export.ex_path =
!             kmem_alloc(exi_root->exi_export.ex_pathlen + 1, KM_SLEEP);
!         exi_root->exi_export.ex_path[0] = '/';
!         exi_root->exi_export.ex_path[1] = '\0';
  
!         exi_root->exi_count = 1;
!         mutex_init(&exi_root->exi_lock, NULL, MUTEX_DEFAULT, NULL);
  
!         exi_root->exi_vp = rootdir;
!         exi_rootfid.fid_len = MAXFIDSZ;
!         error = vop_fid_pseudo(exi_root->exi_vp, &exi_rootfid);
!         if (error) {
!                 mutex_destroy(&exi_root->exi_lock);
!                 kmem_free(exi_root, sizeof (*exi_root));
!                 return (error);
          }
  
!         /*
!          * Initialize auth cache and auth cache lock
!          */
          for (i = 0; i < AUTH_TABLESIZE; i++) {
!                 exi_root->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t),
                      KM_SLEEP);
!                 avl_create(exi_root->exi_cache[i], nfsauth_cache_clnt_compar,
!                     sizeof (struct auth_cache_clnt),
                      offsetof(struct auth_cache_clnt, authc_link));
          }
!         rw_init(&exi_root->exi_cache_lock, NULL, RW_DEFAULT, NULL);
  
!         /* setup the fhandle template */
!         exi_root->exi_fh.fh_fsid = rootdir->v_vfsp->vfs_fsid;
!         exi_root->exi_fh.fh_xlen = exi_rootfid.fid_len;
!         bcopy(exi_rootfid.fid_data, exi_root->exi_fh.fh_xdata,
!             exi_rootfid.fid_len);
!         exi_root->exi_fh.fh_len = sizeof (exi_root->exi_fh.fh_data);
  
!         /*
!          * Publish the exportinfo in the hash table
!          */
!         export_link(exi_root);
  
!         nfslog_init();
!         ns_root = NULL;
  
!         return (0);
  }
  
! /*
!  * Finalization routine for export routines. Called to cleanup previously
!  * initialization work when the NFS server module could not be loaded correctly.
!  */
! void
! nfs_exportfini(void)
  {
          int i;
  
!         /*
!          * Deallocate the place holder for the public file handle.
!          */
!         srv_secinfo_list_free(exi_root->exi_export.ex_secinfo,
!             exi_root->exi_export.ex_seccnt);
!         mutex_destroy(&exi_root->exi_lock);
!         rw_destroy(&exi_root->exi_cache_lock);
          for (i = 0; i < AUTH_TABLESIZE; i++) {
!                 avl_destroy(exi_root->exi_cache[i]);
!                 kmem_free(exi_root->exi_cache[i], sizeof (avl_tree_t));
          }
-         kmem_free(exi_root, sizeof (*exi_root));
  
!         rw_destroy(&exported_lock);
  }
  
  /*
   *  Check if 2 gss mechanism identifiers are the same.
   *
   *  return FALSE if not the same.
   *  return TRUE if the same.
   */
--- 788,1007 ----
          if ((exi)->hash_name.next) \
                  (exi)->hash_name.next->hash_name.prev = (exi); \
          *(bucket) = (exi);
  
  void
! export_link(nfs_export_t *ne, exportinfo_t *exi)
  {
          exportinfo_t **bckt;
  
!         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
! 
!         bckt = &ne->exptable[exptablehash(&exi->exi_fsid, &exi->exi_fid)];
          exp_hash_link(exi, fid_hash, bckt);
  
!         bckt = &ne->exptable_path_hash[pkp_tab_hash(exi->exi_export.ex_path,
              strlen(exi->exi_export.ex_path))];
          exp_hash_link(exi, path_hash, bckt);
  }
  
  /*
!  * Helper functions for exi_id handling
   */
+ static int
+ exi_id_compar(const void *v1, const void *v2)
+ {
+         const struct exportinfo *e1 = v1;
+         const struct exportinfo *e2 = v2;
+ 
+         if (e1->exi_id < e2->exi_id)
+                 return (-1);
+         if (e1->exi_id > e2->exi_id)
+                 return (1);
+ 
+         return (0);
+ }
+ 
  int
! exi_id_get_next()
  {
!         struct exportinfo e;
!         int ret = exi_id_next;
! 
!         ASSERT(MUTEX_HELD(&nfs_exi_id_lock));
! 
!         do {
!                 exi_id_next++;
!                 if (exi_id_next == 0)
!                         exi_id_overflow = TRUE;
! 
!                 if (!exi_id_overflow)
!                         break;
! 
!                 if (exi_id_next == ret)
!                         cmn_err(CE_PANIC, "exi_id exhausted");
! 
!                 e.exi_id = exi_id_next;
!         } while (avl_find(&exi_id_tree, &e, NULL) != NULL);
! 
!         return (ret);
! }
! 
! /*ARGSUSED*/
! static void *
! nfs_export_zone_init(zoneid_t zoneid)
! {
          int i;
+         nfs_export_t *ne;
  
!         ne = kmem_zalloc(sizeof (*ne), KM_SLEEP);
  
+         rw_init(&ne->exported_lock, NULL, RW_DEFAULT, NULL);
+ 
          /*
           * Allocate the place holder for the public file handle, which
           * is all zeroes. It is initially set to the root filesystem.
           */
!         ne->exi_root = kmem_zalloc(sizeof (*ne->exi_root), KM_SLEEP);
!         ne->exi_public = ne->exi_root;
  
!         ne->exi_root->exi_export.ex_flags = EX_PUBLIC;
!         ne->exi_root->exi_export.ex_pathlen = 1;        /* length of "/" */
!         ne->exi_root->exi_export.ex_path =
!             kmem_alloc(ne->exi_root->exi_export.ex_pathlen + 1, KM_SLEEP);
!         ne->exi_root->exi_export.ex_path[0] = '/';
!         ne->exi_root->exi_export.ex_path[1] = '\0';
  
!         ne->exi_root->exi_count = 1;
!         mutex_init(&ne->exi_root->exi_lock, NULL, MUTEX_DEFAULT, NULL);
  
!         ne->exi_root->exi_vp = ZONE_ROOTVP();
!         ne->exi_rootfid.fid_len = MAXFIDSZ;
!         if (vop_fid_pseudo(ne->exi_root->exi_vp, &ne->exi_rootfid) != 0) {
!                 mutex_destroy(&ne->exi_root->exi_lock);
!                 kmem_free(ne->exi_root->exi_export.ex_path,
!                     ne->exi_root->exi_export.ex_pathlen + 1);
!                 kmem_free(ne->exi_root, sizeof (*ne->exi_root));
!                 return (NULL);
          }
  
!         /* Initialize auth cache and auth cache lock */
          for (i = 0; i < AUTH_TABLESIZE; i++) {
!                 ne->exi_root->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t),
                      KM_SLEEP);
!                 avl_create(ne->exi_root->exi_cache[i],
!                     nfsauth_cache_clnt_compar, sizeof (struct auth_cache_clnt),
                      offsetof(struct auth_cache_clnt, authc_link));
          }
!         rw_init(&ne->exi_root->exi_cache_lock, NULL, RW_DEFAULT, NULL);
  
!         /* Setup the fhandle template */
!         ne->exi_root->exi_fh.fh_fsid = rootdir->v_vfsp->vfs_fsid;
!         ne->exi_root->exi_fh.fh_xlen = ne->exi_rootfid.fid_len;
!         bcopy(ne->exi_rootfid.fid_data, ne->exi_root->exi_fh.fh_xdata,
!             ne->exi_rootfid.fid_len);
!         ne->exi_root->exi_fh.fh_len = sizeof (ne->exi_root->exi_fh.fh_data);
  
!         rw_enter(&ne->exported_lock, RW_WRITER);
  
!         /* Publish the exportinfo in the hash table */
!         export_link(ne, ne->exi_root);
  
!         /* Initialize exi_id and exi_kstats */
!         mutex_enter(&nfs_exi_id_lock);
!         ne->exi_root->exi_id = exi_id_get_next();
!         avl_add(&exi_id_tree, ne->exi_root);
!         mutex_exit(&nfs_exi_id_lock);
!         ne->exi_root->exi_kstats = exp_kstats_init(zoneid,
!             ne->exi_root->exi_id, ne->exi_root->exi_export.ex_path,
!             ne->exi_root->exi_export.ex_pathlen, FALSE);
! 
!         rw_exit(&ne->exported_lock);
!         ne->ns_root = NULL;
! 
!         return (ne);
  }
  
! /*ARGSUSED*/
! static void
! nfs_export_zone_fini(zoneid_t zoneid, void *data)
  {
          int i;
+         nfs_export_t *ne = data;
+         struct exportinfo *exi;
  
!         rw_enter(&ne->exported_lock, RW_WRITER);
!         mutex_enter(&nfs_exi_id_lock);
! 
!         exp_kstats_delete(ne->exi_root->exi_kstats);
!         avl_remove(&exi_id_tree, ne->exi_root);
!         export_unlink(ne, ne->exi_root);
! 
!         mutex_exit(&nfs_exi_id_lock);
!         rw_exit(&ne->exported_lock);
! 
!         /* Deallocate the place holder for the public file handle */
!         srv_secinfo_list_free(ne->exi_root->exi_export.ex_secinfo,
!             ne->exi_root->exi_export.ex_seccnt);
!         mutex_destroy(&ne->exi_root->exi_lock);
! 
!         rw_destroy(&ne->exi_root->exi_cache_lock);
          for (i = 0; i < AUTH_TABLESIZE; i++) {
!                 avl_destroy(ne->exi_root->exi_cache[i]);
!                 kmem_free(ne->exi_root->exi_cache[i], sizeof (avl_tree_t));
          }
  
!         exp_kstats_fini(ne->exi_root->exi_kstats);
!         kmem_free(ne->exi_root->exi_export.ex_path,
!             ne->exi_root->exi_export.ex_pathlen + 1);
!         kmem_free(ne->exi_root, sizeof (*ne->exi_root));
! 
!         exi = avl_first(&exi_id_tree);
!         while (exi != NULL) {
!                 struct exportinfo *nexi = AVL_NEXT(&exi_id_tree, exi);
!                 if (zoneid == exi->exi_zoneid)
!                         (void) unexport(ne, exi);
!                 exi = nexi;
!         }
! 
!         rw_destroy(&ne->exported_lock);
!         kmem_free(ne, sizeof (*ne));
  }
  
  /*
+  * Initialization routine for export routines.
+  * Should only be called once.
+  */
+ void
+ nfs_exportinit(void)
+ {
+         mutex_init(&nfs_exi_id_lock, NULL, MUTEX_DEFAULT, NULL);
+ 
+         /* exi_id handling initialization */
+         exi_id_next = 0;
+         exi_id_overflow = FALSE;
+         avl_create(&exi_id_tree, exi_id_compar, sizeof (struct exportinfo),
+             offsetof(struct exportinfo, exi_id_link));
+ 
+         zone_key_create(&nfs_export_key, nfs_export_zone_init,
+             NULL, nfs_export_zone_fini);
+ 
+         nfslog_init();
+ }
+ 
+ /*
+  * Finalization routine for export routines.
+  */
+ void
+ nfs_exportfini(void)
+ {
+         (void) zone_key_delete(nfs_export_key);
+         avl_destroy(&exi_id_tree);
+         mutex_destroy(&nfs_exi_id_lock);
+ }
+ 
+ /*
   *  Check if 2 gss mechanism identifiers are the same.
   *
   *  return FALSE if not the same.
   *  return TRUE if the same.
   */
*** 920,929 ****
--- 1033,1043 ----
      rpc_gss_lock_t *lock, void **cookie)
  {
          int i, j;
          rpc_gss_rawcred_t *raw_cred;
          struct exportinfo *exi;
+         nfs_export_t *ne = nfs_get_export();
  
          /*
           * We don't deal with delegated credentials.
           */
          if (deleg != GSS_C_NO_CREDENTIAL)
*** 930,942 ****
                  return (FALSE);
  
          raw_cred = lock->raw_cred;
          *cookie = NULL;
  
!         rw_enter(&exported_lock, RW_READER);
          for (i = 0; i < EXPTABLESIZE; i++) {
!                 exi = exptable[i];
                  while (exi) {
                          if (exi->exi_export.ex_seccnt > 0) {
                                  struct secinfo *secp;
                                  seconfig_t *se;
                                  int seccnt;
--- 1044,1057 ----
                  return (FALSE);
  
          raw_cred = lock->raw_cred;
          *cookie = NULL;
  
!         rw_enter(&ne->exported_lock, RW_READER);
! 
          for (i = 0; i < EXPTABLESIZE; i++) {
!                 exi = ne->exptable[i];
                  while (exi) {
                          if (exi->exi_export.ex_seccnt > 0) {
                                  struct secinfo *secp;
                                  seconfig_t *se;
                                  int seccnt;
*** 972,982 ****
                          }
                          exi = exi->fid_hash.next;
                  }
          }
  done:
!         rw_exit(&exported_lock);
  
          /*
           * If no nfs pseudo number mapping can be found in the export
           * table, assign the nfsflavor to NFS_FLAVOR_NOMAP. In V4, we may
           * recover the flavor mismatch from NFS layer (NFS4ERR_WRONGSEC).
--- 1087,1097 ----
                          }
                          exi = exi->fid_hash.next;
                  }
          }
  done:
!         rw_exit(&ne->exported_lock);
  
          /*
           * If no nfs pseudo number mapping can be found in the export
           * table, assign the nfsflavor to NFS_FLAVOR_NOMAP. In V4, we may
           * recover the flavor mismatch from NFS layer (NFS4ERR_WRONGSEC).
*** 1039,1076 ****
          int newcnt;
          struct secinfo oldsec[MAX_FLAVORS];
          int oldcnt;
          int i;
          struct pathname lookpn;
  
          STRUCT_SET_HANDLE(uap, model, args);
  
          /* Read in pathname from userspace */
          if (error = pn_get(STRUCT_FGETP(uap, dname), UIO_USERSPACE, &lookpn))
                  return (error);
  
          /* Walk the export list looking for that pathname */
!         rw_enter(&exported_lock, RW_READER);
          DTRACE_PROBE(nfss__i__exported_lock1_start);
!         for (ex1 = exptable_path_hash[pkp_tab_hash(lookpn.pn_path,
              strlen(lookpn.pn_path))]; ex1; ex1 = ex1->path_hash.next) {
!                 if (ex1 != exi_root && 0 ==
                      strcmp(ex1->exi_export.ex_path, lookpn.pn_path)) {
                          exi_hold(ex1);
                          break;
                  }
          }
          DTRACE_PROBE(nfss__i__exported_lock1_stop);
!         rw_exit(&exported_lock);
  
          /* Is this an unshare? */
          if (STRUCT_FGETP(uap, uex) == NULL) {
                  pn_free(&lookpn);
                  if (ex1 == NULL)
                          return (EINVAL);
!                 error = unexport(ex1);
!                 exi_rele(ex1);
                  return (error);
          }
  
          /* It is a share or a re-share */
          error = lookupname(STRUCT_FGETP(uap, dname), UIO_USERSPACE,
--- 1154,1192 ----
          int newcnt;
          struct secinfo oldsec[MAX_FLAVORS];
          int oldcnt;
          int i;
          struct pathname lookpn;
+         nfs_export_t *ne = nfs_get_export();
  
          STRUCT_SET_HANDLE(uap, model, args);
  
          /* Read in pathname from userspace */
          if (error = pn_get(STRUCT_FGETP(uap, dname), UIO_USERSPACE, &lookpn))
                  return (error);
  
          /* Walk the export list looking for that pathname */
!         rw_enter(&ne->exported_lock, RW_READER);
          DTRACE_PROBE(nfss__i__exported_lock1_start);
!         for (ex1 = ne->exptable_path_hash[pkp_tab_hash(lookpn.pn_path,
              strlen(lookpn.pn_path))]; ex1; ex1 = ex1->path_hash.next) {
!                 if (ex1 != ne->exi_root && 0 ==
                      strcmp(ex1->exi_export.ex_path, lookpn.pn_path)) {
                          exi_hold(ex1);
                          break;
                  }
          }
          DTRACE_PROBE(nfss__i__exported_lock1_stop);
!         rw_exit(&ne->exported_lock);
  
          /* Is this an unshare? */
          if (STRUCT_FGETP(uap, uex) == NULL) {
                  pn_free(&lookpn);
                  if (ex1 == NULL)
                          return (EINVAL);
!                 error = unexport(ne, ex1);
!                 exi_rele(&ex1);
                  return (error);
          }
  
          /* It is a share or a re-share */
          error = lookupname(STRUCT_FGETP(uap, dname), UIO_USERSPACE,
*** 1092,1102 ****
                  error = ENOENT;
          }
          if (error) {
                  pn_free(&lookpn);
                  if (ex1)
!                         exi_rele(ex1);
                  return (error);
          }
  
          /*
           * 'vp' may be an AUTOFS node, so we perform a
--- 1208,1218 ----
                  error = ENOENT;
          }
          if (error) {
                  pn_free(&lookpn);
                  if (ex1)
!                         exi_rele(&ex1);
                  return (error);
          }
  
          /*
           * 'vp' may be an AUTOFS node, so we perform a
*** 1118,1128 ****
                          VN_RELE(vp);
                          if (dvp != NULL)
                                  VN_RELE(dvp);
                          pn_free(&lookpn);
                          if (ex1)
!                                 exi_rele(ex1);
                          return (error);
                  }
          }
  
          /* Do not allow sharing another vnode for already shared path */
--- 1234,1244 ----
                          VN_RELE(vp);
                          if (dvp != NULL)
                                  VN_RELE(dvp);
                          pn_free(&lookpn);
                          if (ex1)
!                                 exi_rele(&ex1);
                          return (error);
                  }
          }
  
          /* Do not allow sharing another vnode for already shared path */
*** 1129,1143 ****
          if (ex1 && !PSEUDO(ex1) && !VN_CMP(ex1->exi_vp, vp)) {
                  VN_RELE(vp);
                  if (dvp != NULL)
                          VN_RELE(dvp);
                  pn_free(&lookpn);
!                 exi_rele(ex1);
                  return (EEXIST);
          }
          if (ex1)
!                 exi_rele(ex1);
  
          /*
           * Get the vfs id
           */
          bzero(&fid, sizeof (fid));
--- 1245,1259 ----
          if (ex1 && !PSEUDO(ex1) && !VN_CMP(ex1->exi_vp, vp)) {
                  VN_RELE(vp);
                  if (dvp != NULL)
                          VN_RELE(dvp);
                  pn_free(&lookpn);
!                 exi_rele(&ex1);
                  return (EEXIST);
          }
          if (ex1)
!                 exi_rele(&ex1);
  
          /*
           * Get the vfs id
           */
          bzero(&fid, sizeof (fid));
*** 1161,1195 ****
  
          /*
           * Do not allow re-sharing a shared vnode under a different path
           * PSEUDO export has ex_path fabricated, e.g. "/tmp (pseudo)", skip it.
           */
!         rw_enter(&exported_lock, RW_READER);
          DTRACE_PROBE(nfss__i__exported_lock2_start);
!         for (ex2 = exptable[exptablehash(&fsid, &fid)]; ex2;
              ex2 = ex2->fid_hash.next) {
!                 if (ex2 != exi_root && !PSEUDO(ex2) &&
                      VN_CMP(ex2->exi_vp, vp) &&
                      strcmp(ex2->exi_export.ex_path, lookpn.pn_path) != 0) {
                          DTRACE_PROBE(nfss__i__exported_lock2_stop);
!                         rw_exit(&exported_lock);
                          VN_RELE(vp);
                          if (dvp != NULL)
                                  VN_RELE(dvp);
                          pn_free(&lookpn);
                          return (EEXIST);
                  }
          }
          DTRACE_PROBE(nfss__i__exported_lock2_stop);
!         rw_exit(&exported_lock);
          pn_free(&lookpn);
  
          exi = kmem_zalloc(sizeof (*exi), KM_SLEEP);
          exi->exi_fsid = fsid;
          exi->exi_fid = fid;
          exi->exi_vp = vp;
          exi->exi_count = 1;
          exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
              VSW_VOLATILEDEV) ? 1 : 0;
          mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
          exi->exi_dvp = dvp;
  
--- 1277,1312 ----
  
          /*
           * Do not allow re-sharing a shared vnode under a different path
           * PSEUDO export has ex_path fabricated, e.g. "/tmp (pseudo)", skip it.
           */
!         rw_enter(&ne->exported_lock, RW_READER);
          DTRACE_PROBE(nfss__i__exported_lock2_start);
!         for (ex2 = ne->exptable[exptablehash(&fsid, &fid)]; ex2;
              ex2 = ex2->fid_hash.next) {
!                 if (ex2 != ne->exi_root && !PSEUDO(ex2) &&
                      VN_CMP(ex2->exi_vp, vp) &&
                      strcmp(ex2->exi_export.ex_path, lookpn.pn_path) != 0) {
                          DTRACE_PROBE(nfss__i__exported_lock2_stop);
!                         rw_exit(&ne->exported_lock);
                          VN_RELE(vp);
                          if (dvp != NULL)
                                  VN_RELE(dvp);
                          pn_free(&lookpn);
                          return (EEXIST);
                  }
          }
          DTRACE_PROBE(nfss__i__exported_lock2_stop);
!         rw_exit(&ne->exported_lock);
          pn_free(&lookpn);
  
          exi = kmem_zalloc(sizeof (*exi), KM_SLEEP);
          exi->exi_fsid = fsid;
          exi->exi_fid = fid;
          exi->exi_vp = vp;
          exi->exi_count = 1;
+         exi->exi_zoneid = crgetzoneid(cr);
          exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
              VSW_VOLATILEDEV) ? 1 : 0;
          mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
          exi->exi_dvp = dvp;
  
*** 1459,1499 ****
          }
  
          /*
           * Insert the new entry at the front of the export list
           */
!         rw_enter(&exported_lock, RW_WRITER);
          DTRACE_PROBE(nfss__i__exported_lock3_start);
  
!         export_link(exi);
  
          /*
           * Check the rest of the list for an old entry for the fs.
           * If one is found then unlink it, wait until this is the
           * only reference and then free it.
           */
          for (ex = exi->fid_hash.next; ex != NULL; ex = ex->fid_hash.next) {
!                 if (ex != exi_root && VN_CMP(ex->exi_vp, vp)) {
!                         export_unlink(ex);
                          break;
                  }
          }
  
          /*
           * If the public filehandle is pointing at the
           * old entry, then point it back at the root.
           */
!         if (ex != NULL && ex == exi_public)
!                 exi_public = exi_root;
  
          /*
           * If the public flag is on, make the global exi_public
           * point to this entry and turn off the public bit so that
           * we can distinguish it from the place holder export.
           */
          if (kex->ex_flags & EX_PUBLIC) {
!                 exi_public = exi;
                  kex->ex_flags &= ~EX_PUBLIC;
          }
  
  #ifdef VOLATILE_FH_TEST
          /*
--- 1576,1619 ----
          }
  
          /*
           * Insert the new entry at the front of the export list
           */
!         rw_enter(&ne->exported_lock, RW_WRITER);
          DTRACE_PROBE(nfss__i__exported_lock3_start);
  
!         export_link(ne, exi);
  
          /*
           * Check the rest of the list for an old entry for the fs.
           * If one is found then unlink it, wait until this is the
           * only reference and then free it.
           */
          for (ex = exi->fid_hash.next; ex != NULL; ex = ex->fid_hash.next) {
!                 if (ex != ne->exi_root && VN_CMP(ex->exi_vp, vp)) {
!                         mutex_enter(&nfs_exi_id_lock);
!                         avl_remove(&exi_id_tree, ex);
!                         mutex_exit(&nfs_exi_id_lock);
!                         export_unlink(ne, ex);
                          break;
                  }
          }
  
          /*
           * If the public filehandle is pointing at the
           * old entry, then point it back at the root.
           */
!         if (ex != NULL && ex == ne->exi_public)
!                 ne->exi_public = ne->exi_root;
  
          /*
           * If the public flag is on, make the global exi_public
           * point to this entry and turn off the public bit so that
           * we can distinguish it from the place holder export.
           */
          if (kex->ex_flags & EX_PUBLIC) {
!                 ne->exi_public = exi;
                  kex->ex_flags &= ~EX_PUBLIC;
          }
  
  #ifdef VOLATILE_FH_TEST
          /*
*** 1521,1541 ****
                  /* If it's a re-export update namespace tree */
                  exi->exi_tree = ex->exi_tree;
                  exi->exi_tree->tree_exi = exi;
  
                  /* Update the change timestamp */
!                 tree_update_change(exi->exi_tree, NULL);
          }
  
          /*
           * build a unique flavor list from the flavors specified
           * in the share cmd.  unique means that each flavor only
           * appears once in the secinfo list -- no duplicates allowed.
           */
          newcnt = build_seclist_nodups(&exi->exi_export, newsec, FALSE);
  
!         srv_secinfo_treeclimb(exi, newsec, newcnt, TRUE);
  
          /*
           * If re-sharing an old export entry, update the secinfo data
           * depending on if the old entry is a pseudo node or not.
           */
--- 1641,1661 ----
                  /* If it's a re-export update namespace tree */
                  exi->exi_tree = ex->exi_tree;
                  exi->exi_tree->tree_exi = exi;
  
                  /* Update the change timestamp */
!                 tree_update_change(ne, exi->exi_tree, NULL);
          }
  
          /*
           * build a unique flavor list from the flavors specified
           * in the share cmd.  unique means that each flavor only
           * appears once in the secinfo list -- no duplicates allowed.
           */
          newcnt = build_seclist_nodups(&exi->exi_export, newsec, FALSE);
  
!         srv_secinfo_treeclimb(ne, exi, newsec, newcnt, TRUE);
  
          /*
           * If re-sharing an old export entry, update the secinfo data
           * depending on if the old entry is a pseudo node or not.
           */
*** 1556,1566 ****
                          /*
                           * First transfer implicit flavor refs to new export.
                           * Remove old flavor refs last.
                           */
                          srv_secinfo_exp2exp(&exi->exi_export, oldsec, oldcnt);
!                         srv_secinfo_treeclimb(ex, oldsec, oldcnt, FALSE);
                  }
          }
  
          /*
           * If it's a re-export and the old entry has a pseudonode list,
--- 1676,1686 ----
                          /*
                           * First transfer implicit flavor refs to new export.
                           * Remove old flavor refs last.
                           */
                          srv_secinfo_exp2exp(&exi->exi_export, oldsec, oldcnt);
!                         srv_secinfo_treeclimb(ne, ex, oldsec, oldcnt, FALSE);
                  }
          }
  
          /*
           * If it's a re-export and the old entry has a pseudonode list,
*** 1569,1598 ****
          if (ex != NULL && (ex->exi_visible != NULL)) {
                  exi->exi_visible = ex->exi_visible;
                  ex->exi_visible = NULL;
          }
  
          DTRACE_PROBE(nfss__i__exported_lock3_stop);
!         rw_exit(&exported_lock);
  
!         if (exi_public == exi || kex->ex_flags & EX_LOG) {
                  /*
                   * Log share operation to this buffer only.
                   */
                  nfslog_share_record(exi, cr);
          }
  
          if (ex != NULL)
!                 exi_rele(ex);
  
          return (0);
  
  out7:
          /* Unlink the new export in exptable. */
!         export_unlink(exi);
          DTRACE_PROBE(nfss__i__exported_lock3_stop);
!         rw_exit(&exported_lock);
  out6:
          if (kex->ex_flags & EX_INDEX)
                  kmem_free(kex->ex_index, strlen(kex->ex_index) + 1);
  out5:
          /* free partially completed allocation */
--- 1689,1738 ----
          if (ex != NULL && (ex->exi_visible != NULL)) {
                  exi->exi_visible = ex->exi_visible;
                  ex->exi_visible = NULL;
          }
  
+         /*
+          * Initialize exi_id and exi_kstats
+          */
+         if (ex != NULL) {
+                 exi->exi_id = ex->exi_id;
+                 exi->exi_kstats = ex->exi_kstats;
+                 ex->exi_kstats = NULL;
+                 exp_kstats_reset(exi->exi_kstats, kex->ex_path,
+                     kex->ex_pathlen, FALSE);
+         } else {
+                 mutex_enter(&nfs_exi_id_lock);
+                 exi->exi_id = exi_id_get_next();
+                 mutex_exit(&nfs_exi_id_lock);
+                 exi->exi_kstats = exp_kstats_init(crgetzoneid(cr), exi->exi_id,
+                     kex->ex_path, kex->ex_pathlen, FALSE);
+         }
+         mutex_enter(&nfs_exi_id_lock);
+         avl_add(&exi_id_tree, exi);
+         mutex_exit(&nfs_exi_id_lock);
+ 
          DTRACE_PROBE(nfss__i__exported_lock3_stop);
!         rw_exit(&ne->exported_lock);
  
!         if (ne->exi_public == exi || kex->ex_flags & EX_LOG) {
                  /*
                   * Log share operation to this buffer only.
                   */
                  nfslog_share_record(exi, cr);
          }
  
          if (ex != NULL)
!                 exi_rele(&ex);
  
          return (0);
  
  out7:
          /* Unlink the new export in exptable. */
!         export_unlink(ne, exi);
          DTRACE_PROBE(nfss__i__exported_lock3_stop);
!         rw_exit(&ne->exported_lock);
  out6:
          if (kex->ex_flags & EX_INDEX)
                  kmem_free(kex->ex_index, strlen(kex->ex_index) + 1);
  out5:
          /* free partially completed allocation */
*** 1632,1699 ****
  
  /*
   * Remove the exportinfo from the export list
   */
  void
! export_unlink(struct exportinfo *exi)
  {
!         ASSERT(RW_WRITE_HELD(&exported_lock));
  
          exp_hash_unlink(exi, fid_hash);
          exp_hash_unlink(exi, path_hash);
  }
  
  /*
   * Unexport an exported filesystem
   */
  static int
! unexport(struct exportinfo *exi)
  {
          struct secinfo cursec[MAX_FLAVORS];
          int curcnt;
  
!         rw_enter(&exported_lock, RW_WRITER);
  
          /* Check if exi is still linked in the export table */
          if (!EXP_LINKED(exi) || PSEUDO(exi)) {
!                 rw_exit(&exported_lock);
                  return (EINVAL);
          }
  
!         export_unlink(exi);
  
          /*
           * Remove security flavors before treeclimb_unexport() is called
           * because srv_secinfo_treeclimb needs the namespace tree
           */
          curcnt = build_seclist_nodups(&exi->exi_export, cursec, TRUE);
  
-         srv_secinfo_treeclimb(exi, cursec, curcnt, FALSE);
- 
          /*
           * If there's a visible list, then need to leave
           * a pseudo export here to retain the visible list
           * for paths to exports below.
           */
          if (exi->exi_visible != NULL) {
                  struct exportinfo *newexi;
  
!                 newexi = pseudo_exportfs(exi->exi_vp, &exi->exi_fid,
                      exi->exi_visible, &exi->exi_export);
                  exi->exi_visible = NULL;
  
                  /* interconnect the existing treenode with the new exportinfo */
                  newexi->exi_tree = exi->exi_tree;
                  newexi->exi_tree->tree_exi = newexi;
  
                  /* Update the change timestamp */
!                 tree_update_change(exi->exi_tree, NULL);
          } else {
!                 treeclimb_unexport(exi);
          }
  
!         rw_exit(&exported_lock);
  
          /*
           * Need to call into the NFSv4 server and release all data
           * held on this particular export.  This is important since
           * the v4 server may be holding file locks or vnodes under
--- 1772,1842 ----
  
  /*
   * Remove the exportinfo from the export list
   */
  void
! export_unlink(nfs_export_t *ne, struct exportinfo *exi)
  {
!         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
  
          exp_hash_unlink(exi, fid_hash);
          exp_hash_unlink(exi, path_hash);
  }
  
  /*
   * Unexport an exported filesystem
   */
  static int
! unexport(nfs_export_t *ne, struct exportinfo *exi)
  {
          struct secinfo cursec[MAX_FLAVORS];
          int curcnt;
  
!         rw_enter(&ne->exported_lock, RW_WRITER);
  
          /* Check if exi is still linked in the export table */
          if (!EXP_LINKED(exi) || PSEUDO(exi)) {
!                 rw_exit(&ne->exported_lock);
                  return (EINVAL);
          }
  
!         exp_kstats_delete(exi->exi_kstats);
!         mutex_enter(&nfs_exi_id_lock);
!         avl_remove(&exi_id_tree, exi);
!         mutex_exit(&nfs_exi_id_lock);
!         export_unlink(ne, exi);
  
          /*
           * Remove security flavors before treeclimb_unexport() is called
           * because srv_secinfo_treeclimb needs the namespace tree
           */
          curcnt = build_seclist_nodups(&exi->exi_export, cursec, TRUE);
+         srv_secinfo_treeclimb(ne, exi, cursec, curcnt, FALSE);
  
          /*
           * If there's a visible list, then need to leave
           * a pseudo export here to retain the visible list
           * for paths to exports below.
           */
          if (exi->exi_visible != NULL) {
                  struct exportinfo *newexi;
  
!                 newexi = pseudo_exportfs(ne, exi->exi_vp, &exi->exi_fid,
                      exi->exi_visible, &exi->exi_export);
                  exi->exi_visible = NULL;
  
                  /* interconnect the existing treenode with the new exportinfo */
                  newexi->exi_tree = exi->exi_tree;
                  newexi->exi_tree->tree_exi = newexi;
  
                  /* Update the change timestamp */
!                 tree_update_change(ne, exi->exi_tree, NULL);
          } else {
!                 treeclimb_unexport(ne, exi);
          }
  
!         rw_exit(&ne->exported_lock);
  
          /*
           * Need to call into the NFSv4 server and release all data
           * held on this particular export.  This is important since
           * the v4 server may be holding file locks or vnodes under
*** 1709,1729 ****
  
          /*
           * If this was a public export, restore
           * the public filehandle to the root.
           */
!         if (exi == exi_public) {
!                 exi_public = exi_root;
  
!                 nfslog_share_record(exi_public, CRED());
          }
  
!         if (exi->exi_export.ex_flags & EX_LOG) {
                  nfslog_unshare_record(exi, CRED());
-         }
  
!         exi_rele(exi);
          return (0);
  }
  
  /*
   * Get file handle system call.
--- 1852,1871 ----
  
          /*
           * If this was a public export, restore
           * the public filehandle to the root.
           */
!         if (exi == ne->exi_public) {
!                 ne->exi_public = ne->exi_root;
  
!                 nfslog_share_record(ne->exi_public, CRED());
          }
  
!         if (exi->exi_export.ex_flags & EX_LOG)
                  nfslog_unshare_record(exi, CRED());
  
!         exi_rele(&exi);
          return (0);
  }
  
  /*
   * Get file handle system call.
*** 1875,1885 ****
                  }
                  if (!error && exi->exi_export.ex_flags & EX_LOG) {
                          nfslog_getfh(exi, (fhandle_t *)logptr,
                              STRUCT_FGETP(uap, fname), UIO_USERSPACE, cr);
                  }
!                 exi_rele(exi);
                  if (!error) {
                          if (copyout(&l, STRUCT_FGETP(uap, lenp), sizeof (int)))
                                  error = EFAULT;
                          if (copyout(buf, STRUCT_FGETP(uap, fhp), l))
                                  error = EFAULT;
--- 2017,2027 ----
                  }
                  if (!error && exi->exi_export.ex_flags & EX_LOG) {
                          nfslog_getfh(exi, (fhandle_t *)logptr,
                              STRUCT_FGETP(uap, fname), UIO_USERSPACE, cr);
                  }
!                 exi_rele(&exi);
                  if (!error) {
                          if (copyout(&l, STRUCT_FGETP(uap, lenp), sizeof (int)))
                                  error = EFAULT;
                          if (copyout(buf, STRUCT_FGETP(uap, fhp), l))
                                  error = EFAULT;
*** 2444,2456 ****
   */
  struct exportinfo *
  checkexport(fsid_t *fsid, fid_t *fid)
  {
          struct exportinfo *exi;
  
!         rw_enter(&exported_lock, RW_READER);
!         for (exi = exptable[exptablehash(fsid, fid)];
              exi != NULL;
              exi = exi->fid_hash.next) {
                  if (exportmatch(exi, fsid, fid)) {
                          /*
                           * If this is the place holder for the
--- 2586,2599 ----
   */
  struct exportinfo *
  checkexport(fsid_t *fsid, fid_t *fid)
  {
          struct exportinfo *exi;
+         nfs_export_t *ne = nfs_get_export();
  
!         rw_enter(&ne->exported_lock, RW_READER);
!         for (exi = ne->exptable[exptablehash(fsid, fid)];
              exi != NULL;
              exi = exi->fid_hash.next) {
                  if (exportmatch(exi, fsid, fid)) {
                          /*
                           * If this is the place holder for the
*** 2457,2475 ****
                           * public file handle, then return the
                           * real export entry for the public file
                           * handle.
                           */
                          if (exi->exi_export.ex_flags & EX_PUBLIC) {
!                                 exi = exi_public;
                          }
  
                          exi_hold(exi);
!                         rw_exit(&exported_lock);
                          return (exi);
                  }
          }
!         rw_exit(&exported_lock);
          return (NULL);
  }
  
  
  /*
--- 2600,2618 ----
                           * public file handle, then return the
                           * real export entry for the public file
                           * handle.
                           */
                          if (exi->exi_export.ex_flags & EX_PUBLIC) {
!                                 exi = ne->exi_public;
                          }
  
                          exi_hold(exi);
!                         rw_exit(&ne->exported_lock);
                          return (exi);
                  }
          }
!         rw_exit(&ne->exported_lock);
          return (NULL);
  }
  
  
  /*
*** 2481,2494 ****
   */
  struct exportinfo *
  checkexport4(fsid_t *fsid, fid_t *fid, vnode_t *vp)
  {
          struct exportinfo *exi;
  
!         ASSERT(RW_LOCK_HELD(&exported_lock));
  
!         for (exi = exptable[exptablehash(fsid, fid)];
              exi != NULL;
              exi = exi->fid_hash.next) {
                  if (exportmatch(exi, fsid, fid)) {
                          /*
                           * If this is the place holder for the
--- 2624,2638 ----
   */
  struct exportinfo *
  checkexport4(fsid_t *fsid, fid_t *fid, vnode_t *vp)
  {
          struct exportinfo *exi;
+         nfs_export_t *ne = nfs_get_export();
  
!         ASSERT(RW_LOCK_HELD(&ne->exported_lock));
  
!         for (exi = ne->exptable[exptablehash(fsid, fid)];
              exi != NULL;
              exi = exi->fid_hash.next) {
                  if (exportmatch(exi, fsid, fid)) {
                          /*
                           * If this is the place holder for the
*** 2495,2505 ****
                           * public file handle, then return the
                           * real export entry for the public file
                           * handle.
                           */
                          if (exi->exi_export.ex_flags & EX_PUBLIC) {
!                                 exi = exi_public;
                          }
  
                          /*
                           * If vp is given, check if vp is the
                           * same vnode as the exported node.
--- 2639,2649 ----
                           * public file handle, then return the
                           * real export entry for the public file
                           * handle.
                           */
                          if (exi->exi_export.ex_flags & EX_PUBLIC) {
!                                 exi = ne->exi_public;
                          }
  
                          /*
                           * If vp is given, check if vp is the
                           * same vnode as the exported node.
*** 2518,2528 ****
  }
  
  /*
   * Free an entire export list node
   */
! void
  exportfree(struct exportinfo *exi)
  {
          struct exportdata *ex;
          struct charset_cache *cache;
          int i;
--- 2662,2672 ----
  }
  
  /*
   * Free an entire export list node
   */
! static void
  exportfree(struct exportinfo *exi)
  {
          struct exportdata *ex;
          struct charset_cache *cache;
          int i;
*** 2581,2590 ****
--- 2725,2736 ----
          for (i = 0; i < AUTH_TABLESIZE; i++) {
                  avl_destroy(exi->exi_cache[i]);
                  kmem_free(exi->exi_cache[i], sizeof (avl_tree_t));
          }
  
+         exp_kstats_fini(exi->exi_kstats);
+ 
          kmem_free(exi, sizeof (*exi));
  }
  
  /*
   * load the index file from user space into kernel space.
*** 2622,2640 ****
   * When a thread completes using exi, it should call exi_rele().
   * exi_rele() decrements exi_count. It releases exi if exi_count == 0, i.e.
   * if this is the last user of exi and exi is not on exportinfo list anymore
   */
  void
! exi_rele(struct exportinfo *exi)
  {
!         mutex_enter(&exi->exi_lock);
!         exi->exi_count--;
!         if (exi->exi_count == 0) {
!                 mutex_exit(&exi->exi_lock);
!                 exportfree(exi);
          } else
!                 mutex_exit(&exi->exi_lock);
  }
  
  #ifdef VOLATILE_FH_TEST
  /*
   * Test for volatile fh's - add file handle to list and set its volatile id
--- 2768,2795 ----
   * When a thread completes using exi, it should call exi_rele().
   * exi_rele() decrements exi_count. It releases exi if exi_count == 0, i.e.
   * if this is the last user of exi and exi is not on exportinfo list anymore
   */
  void
! exi_rele(struct exportinfo **exi)
  {
!         struct exportinfo *exip = *exi;
!         mutex_enter(&exip->exi_lock);
!         exip->exi_count--;
!         if (exip->exi_count == 0) {
!                 mutex_exit(&exip->exi_lock);
!                 /*
!                  * The exportinfo structure needs to be cleared here
!                  * since the control point, for when we free the structure,
!                  * is in this function and is triggered by the reference
!                  * count. The caller does not necessarily know when that
!                  * will be the case.
!                  */
!                 *exi = NULL;
!                 exportfree(exip);
          } else
!                 mutex_exit(&exip->exi_lock);
  }
  
  #ifdef VOLATILE_FH_TEST
  /*
   * Test for volatile fh's - add file handle to list and set its volatile id