Print this page
Fix NFS design problems re. multiple zone keys
Make NFS server zone-specific data all have the same lifetime
Fix rfs4_clean_state_exi
Fix exi_cache_reclaim
Fix mistakes in zone keys work
More fixes re. exi_zoneid and exi_tree
(danmcd -> Keep some ASSERT()s around for readability.)
        
*** 66,77 ****
  #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
--- 66,75 ----
*** 120,130 ****
  #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)
  {
--- 118,131 ----
  #define exptablehash(fsid, fid) (nfs_fhhash((fsid), (fid)) & (EXPTABLESIZE - 1))
  
  extern nfs_export_t *
  nfs_get_export(void)
  {
!         nfs_globals_t *ng = zone_getspecific(nfssrv_zone_key, curzone);
!         nfs_export_t *ne = ng->nfs_export;
!         ASSERT(ne != NULL);
!         return (ne);
  }
  
  static uint8_t
  xor_hash(uint8_t *data, int len)
  {
*** 720,734 ****
   */
  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;
  
          /*
           * If flavors are being added and the new export root isn't
--- 721,746 ----
   */
  void
  srv_secinfo_treeclimb(nfs_export_t *ne, exportinfo_t *exip, secinfo_t *sec,
      int seccnt, bool_t isadd)
  {
!         treenode_t *tnode;
  
          ASSERT(RW_WRITE_HELD(&ne->exported_lock));
  
+         /*
+          * exi_tree can be null for the zone root
+          * which means we're already at the "top"
+          * and there's nothing more to "climb".
+          */
+         tnode = exip->exi_tree;
+         if (tnode == NULL) {
+                 /* Should only happen for... */
+                 ASSERT(exip == ne->exi_root);
+                 return;
+         }
+ 
          if (seccnt == 0)
                  return;
  
          /*
           * If flavors are being added and the new export root isn't
*** 802,811 ****
--- 814,824 ----
  export_link(nfs_export_t *ne, exportinfo_t *exi)
  {
          exportinfo_t **bckt;
  
          ASSERT(RW_WRITE_HELD(&ne->exported_lock));
+         ASSERT(exi->exi_zoneid == ne->ne_globals->nfs_zoneid);
  
          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,
*** 853,873 ****
          } 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);
--- 866,914 ----
          } while (avl_find(&exi_id_tree, &e, NULL) != NULL);
  
          return (ret);
  }
  
! /*
!  * Get the root file handle for this zone.
!  * Called when nfs_svc() starts
!  */
! int
! nfs_export_get_rootfh(nfs_globals_t *g)
  {
+         nfs_export_t *ne = g->nfs_export;
+         int err;
+ 
+         ne->exi_rootfid.fid_len = MAXFIDSZ;
+         err = vop_fid_pseudo(ne->exi_root->exi_vp, &ne->exi_rootfid);
+         if (err != 0) {
+                 ne->exi_rootfid.fid_len = 0;
+                 return (err);
+         }
+ 
+         /* Setup the fhandle template exi_fh */
+         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);
+ 
+         return (0);
+ }
+ 
+ void
+ nfs_export_zone_init(nfs_globals_t *ng)
+ {
          int i;
          nfs_export_t *ne;
  
          ne = kmem_zalloc(sizeof (*ne), KM_SLEEP);
  
          rw_init(&ne->exported_lock, NULL, RW_DEFAULT, NULL);
  
+         ne->ne_globals = ng; /* "up" pointer */
+ 
          /*
           * 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);
*** 881,901 ****
          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_zone = zone_find_by_id_nolock(zoneid);
!         ne->exi_root->exi_vp = ne->exi_root->exi_zone->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],
--- 922,940 ----
          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);
  
!         ASSERT(curzone->zone_id == ng->nfs_zoneid);
!         ne->exi_root->exi_vp = ZONE_ROOTVP();
!         ne->exi_root->exi_zoneid = ng->nfs_zoneid;
  
+         /*
+          * Fill in ne->exi_rootfid later, in nfs_export_get_rootfid
+          * because we can't correctly return errors here.
+          */
+ 
          /* 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],
*** 902,917 ****
                      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);
--- 941,951 ----
                      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 exi_fh later, in nfs_export_get_rootfid */
  
          rw_enter(&ne->exported_lock, RW_WRITER);
  
          /* Publish the exportinfo in the hash table */
          export_link(ne, ne->exi_root);
*** 923,950 ****
          mutex_exit(&nfs_exi_id_lock);
  
          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);
  
          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);
--- 957,1039 ----
          mutex_exit(&nfs_exi_id_lock);
  
          rw_exit(&ne->exported_lock);
          ne->ns_root = NULL;
  
!         ng->nfs_export = ne;
  }
  
! /*
!  * During zone shutdown, remove exports
!  */
! void
! nfs_export_zone_shutdown(nfs_globals_t *ng)
  {
+         nfs_export_t *ne = ng->nfs_export;
+         struct exportinfo *exi, *nexi;
+         int i, errors;
+ 
+         rw_enter(&ne->exported_lock, RW_READER);
+ 
+         errors = 0;
+         for (i = 0; i < EXPTABLESIZE; i++) {
+ 
+                 exi = ne->exptable[i];
+                 if (exi != NULL)
+                         exi_hold(exi);
+ 
+                 while (exi != NULL) {
+ 
+                         /*
+                          * Get and hold next export before
+                          * dropping the rwlock and unexport
+                          */
+                         nexi = exi->fid_hash.next;
+                         if (nexi != NULL)
+                                 exi_hold(nexi);
+ 
+                         rw_exit(&ne->exported_lock);
+ 
+                         /*
+                          * Skip ne->exi_root which gets special
+                          * create/destroy handling.
+                          */
+                         if (exi != ne->exi_root &&
+                             unexport(ne, exi) != 0)
+                                 errors++;
+                         exi_rele(exi);
+ 
+                         rw_enter(&ne->exported_lock, RW_READER);
+                         exi = nexi;
+                 }
+         }
+         if (errors > 0) {
+                 cmn_err(CE_NOTE,
+                     "NFS: failed un-exports in zone %d",
+                     (int) ng->nfs_zoneid);
+         }
+ 
+         rw_exit(&ne->exported_lock);
+ }
+ 
+ void
+ nfs_export_zone_fini(nfs_globals_t *ng)
+ {
          int i;
!         nfs_export_t *ne = ng->nfs_export;
          struct exportinfo *exi;
  
+         ng->nfs_export = NULL;
+ 
          rw_enter(&ne->exported_lock, RW_WRITER);
  
+         mutex_enter(&nfs_exi_id_lock);
          avl_remove(&exi_id_tree, ne->exi_root);
+         mutex_exit(&nfs_exi_id_lock);
+ 
          export_unlink(ne, ne->exi_root);
  
          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);
*** 958,975 ****
  
          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));
  }
  
  /*
--- 1047,1074 ----
  
          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));
  
+         /*
+          * The shutdown hook should have left the exi_id_tree
+          * with nothing belonging to this zone.
+          */
+         mutex_enter(&nfs_exi_id_lock);
+         i = 0;
          exi = avl_first(&exi_id_tree);
          while (exi != NULL) {
!                 if (exi->exi_zoneid == ng->nfs_zoneid)
!                         i++;
!                 exi = AVL_NEXT(&exi_id_tree, exi);
          }
!         mutex_exit(&nfs_exi_id_lock);
!         if (i > 0) {
!                 cmn_err(CE_NOTE,
!                     "NFS: zone %d has %d export IDs left after shutdown",
!                     (int) ng->nfs_zoneid, i);
!         }
          rw_destroy(&ne->exported_lock);
          kmem_free(ne, sizeof (*ne));
  }
  
  /*
*** 985,1007 ****
          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);
  }
  
  /*
--- 1084,1102 ----
*** 1841,1851 ****
           * 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
           * this export.
           */
!         rfs4_clean_state_exi(exi);
  
          /*
           * Notify the lock manager that the filesystem is being
           * unexported.
           */
--- 1936,1946 ----
           * 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
           * this export.
           */
!         rfs4_clean_state_exi(ne, exi);
  
          /*
           * Notify the lock manager that the filesystem is being
           * unexported.
           */