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.
*/