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,12 +66,10 @@
 #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

@@ -120,11 +118,14 @@
 #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));
+        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,15 +721,26 @@
  */
 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;
+        treenode_t *tnode;
 
         ASSERT(RW_WRITE_HELD(&ne->exported_lock));
-        ASSERT(tnode != NULL);
 
+        /*
+         * 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,10 +814,11 @@
 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,21 +866,49 @@
         } while (avl_find(&exi_id_tree, &e, NULL) != NULL);
 
         return (ret);
 }
 
-/*ARGSUSED*/
-static void *
-nfs_export_zone_init(zoneid_t zoneid)
+/*
+ * 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,21 +922,19 @@
         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);
-        }
+        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,16 +941,11 @@
                     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);
+        /* 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,28 +957,83 @@
         mutex_exit(&nfs_exi_id_lock);
 
         rw_exit(&ne->exported_lock);
         ne->ns_root = NULL;
 
-        return (ne);
+        ng->nfs_export = ne;
 }
 
-/*ARGSUSED*/
-static void
-nfs_export_zone_fini(zoneid_t zoneid, void *data)
+/*
+ * 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 = data;
+        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);
 
+        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);
 
-        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);

@@ -958,18 +1047,28 @@
 
         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) {
-                struct exportinfo *nexi = AVL_NEXT(&exi_id_tree, exi);
-                if (zoneid == exi->exi_zoneid)
-                        (void) unexport(ne, exi);
-                exi = nexi;
+                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,23 +1084,19 @@
         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);
 }
 
 /*

@@ -1841,11 +1936,11 @@
          * 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);
+        rfs4_clean_state_exi(ne, exi);
 
         /*
          * Notify the lock manager that the filesystem is being
          * unexported.
          */