From 0194f347869fb092d216242f469c62649c11e304 Mon Sep 17 00:00:00 2001
From: Gordon Ross <gwr@nexenta.com>
Date: Thu, 10 Oct 2019 15:58:50 -0400
Subject: [PATCH 3/3] 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.)
---
 usr/src/uts/common/fs/nfs/nfs3_srv.c       |  32 ++--
 usr/src/uts/common/fs/nfs/nfs4_db.c        |   2 +-
 usr/src/uts/common/fs/nfs/nfs4_dispatch.c  |   6 +-
 usr/src/uts/common/fs/nfs/nfs4_srv.c       |  43 ++---
 usr/src/uts/common/fs/nfs/nfs4_srv_deleg.c |  14 +-
 usr/src/uts/common/fs/nfs/nfs4_srv_ns.c    |  12 ++
 usr/src/uts/common/fs/nfs/nfs4_state.c     |  74 +++++----
 usr/src/uts/common/fs/nfs/nfs_auth.c       |  78 +++++----
 usr/src/uts/common/fs/nfs/nfs_export.c     | 177 ++++++++++++++++-----
 usr/src/uts/common/fs/nfs/nfs_server.c     | 124 +++++++++++++--
 usr/src/uts/common/fs/nfs/nfs_srv.c        |  32 ++--
 usr/src/uts/common/nfs/export.h            |   9 +-
 usr/src/uts/common/nfs/nfs.h               |  31 +++-
 usr/src/uts/common/nfs/nfs4.h              |   4 +-
 14 files changed, 460 insertions(+), 178 deletions(-)

diff --git a/usr/src/uts/common/fs/nfs/nfs3_srv.c b/usr/src/uts/common/fs/nfs/nfs3_srv.c
index 1efd694178..7c63789325 100644
--- a/usr/src/uts/common/fs/nfs/nfs3_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs3_srv.c
@@ -91,7 +91,15 @@ static int	rdma_setup_read_data3(READ3args *, READ3resok *);
 extern int nfs_loaned_buffers;
 
 u_longlong_t nfs3_srv_caller_id;
-static zone_key_t rfs3_zone_key;
+
+static nfs3_srv_t *
+nfs3_get_srv(void)
+{
+	nfs_globals_t *ng = zone_getspecific(nfssrv_zone_key, curzone);
+	nfs3_srv_t *srv = ng->nfs3_srv;
+	ASSERT(srv != NULL);
+	return (srv);
+}
 
 /* ARGSUSED */
 void
@@ -1327,7 +1335,8 @@ rfs3_write(WRITE3args *args, WRITE3res *resp, struct exportinfo *exi,
 	}
 
 	ASSERT3P(curzone, ==, exi->exi_zone); /* exi is guaranteed non-NULL. */
-	ns = zone_getspecific(rfs3_zone_key, curzone);
+	ns = nfs3_get_srv();
+
 	if (is_system_labeled()) {
 		bslabel_t *clabel = req->rq_label;
 
@@ -4128,7 +4137,7 @@ rfs3_commit(COMMIT3args *args, COMMIT3res *resp, struct exportinfo *exi,
 	}
 
 	ASSERT3P(curzone, ==, exi->exi_zone); /* exi is guaranteed non-NULL. */
-	ns = zone_getspecific(rfs3_zone_key, curzone);
+	ns = nfs3_get_srv();
 	bva.va_mask = AT_ALL;
 	error = VOP_GETATTR(vp, &bva, 0, cr, NULL);
 
@@ -4381,9 +4390,8 @@ rdma_setup_read_data3(READ3args *args, READ3resok *rok)
 	return (TRUE);
 }
 
-/* ARGSUSED */
-static void *
-rfs3_zone_init(zoneid_t zoneid)
+void
+rfs3_srv_zone_init(nfs_globals_t *ng)
 {
 	nfs3_srv_t *ns;
 	struct rfs3_verf_overlay {
@@ -4428,14 +4436,15 @@ rfs3_zone_init(zoneid_t zoneid)
 	if (verfp->id == 0)
 		verfp->id = (uint_t)now.tv_nsec;
 
-	return (ns);
+	ng->nfs3_srv = ns;
 }
 
-/* ARGSUSED */
-static void
-rfs3_zone_fini(zoneid_t zoneid, void *data)
+void
+rfs3_srv_zone_fini(nfs_globals_t *ng)
 {
-	nfs3_srv_t *ns = data;
+	nfs3_srv_t *ns = ng->nfs3_srv;
+
+	ng->nfs3_srv = NULL;
 
 	kmem_free(ns, sizeof (*ns));
 }
@@ -4444,7 +4453,6 @@ void
 rfs3_srvrinit(void)
 {
 	nfs3_srv_caller_id = fs_new_caller_id();
-	zone_key_create(&rfs3_zone_key, rfs3_zone_init, NULL, rfs3_zone_fini);
 }
 
 void
diff --git a/usr/src/uts/common/fs/nfs/nfs4_db.c b/usr/src/uts/common/fs/nfs/nfs4_db.c
index 01639a78ec..845311d1c8 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_db.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_db.c
@@ -752,7 +752,7 @@ rfs4_cpr_callb(void *arg, int code)
 	rfs4_client_t *cp;
 	int i;
 
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 	rfs4_table_t *table = nsrv4->rfs4_client_tab;
 
 	/*
diff --git a/usr/src/uts/common/fs/nfs/nfs4_dispatch.c b/usr/src/uts/common/fs/nfs/nfs4_dispatch.c
index 6fc0026e26..268650597c 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_dispatch.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_dispatch.c
@@ -55,8 +55,6 @@ uint32_t nfs4_drc_hash = 541;
 
 static void rfs4_resource_err(struct svc_req *req, COMPOUND4args *argsp);
 
-extern zone_key_t rfs4_zone_key;
-
 /*
  * Initialize a duplicate request cache.
  */
@@ -97,7 +95,7 @@ rfs4_init_drc(uint32_t drc_size, uint32_t drc_hash_size)
 void
 rfs4_fini_drc(void)
 {
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 	rfs4_drc_t *drc = nsrv4->nfs4_drc;
 	rfs4_dupreq_t *drp, *drp_next;
 
@@ -387,7 +385,7 @@ rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req,
 	int		 dr_stat = NFS4_NOT_DUP;
 	rfs4_dupreq_t	*drp = NULL;
 	int		 rv;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 	rfs4_drc_t *nfs4_drc = nsrv4->nfs4_drc;
 
 	ASSERT(disp);
diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv.c b/usr/src/uts/common/fs/nfs/nfs4_srv.c
index 481b7a3aad..e628abd9eb 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv.c
@@ -153,7 +153,6 @@ static int rdma_setup_read_data4(READ4args *, READ4res *);
 #define	DIRENT64_TO_DIRCOUNT(dp) \
 	(3 * BYTES_PER_XDR_UNIT + DIRENT64_NAMELEN((dp)->d_reclen))
 
-zone_key_t	rfs4_zone_key;
 
 static sysid_t		lockt_sysid;	/* dummy sysid for all LOCKT calls */
 
@@ -501,9 +500,17 @@ static const fs_operation_def_t nfs4_wr_deleg_tmpl[] = {
 	NULL,			NULL
 };
 
-/* ARGSUSED */
-static void *
-rfs4_zone_init(zoneid_t zoneid)
+nfs4_srv_t *
+nfs4_get_srv(void)
+{
+	nfs_globals_t *ng = zone_getspecific(nfssrv_zone_key, curzone);
+	nfs4_srv_t *srv = ng->nfs4_srv;
+	ASSERT(srv != NULL);
+	return (srv);
+}
+
+void
+rfs4_srv_zone_init(nfs_globals_t *ng)
 {
 	nfs4_srv_t *nsrv4;
 	timespec32_t verf;
@@ -549,14 +556,15 @@ rfs4_zone_init(zoneid_t zoneid)
 	mutex_init(&nsrv4->servinst_lock, NULL, MUTEX_DEFAULT, NULL);
 	rw_init(&nsrv4->deleg_policy_lock, NULL, RW_DEFAULT, NULL);
 
-	return (nsrv4);
+	ng->nfs4_srv = nsrv4;
 }
 
-/* ARGSUSED */
-static void
-rfs4_zone_fini(zoneid_t zoneid, void *data)
+void
+rfs4_srv_zone_fini(nfs_globals_t *ng)
 {
-	nfs4_srv_t *nsrv4 = data;
+	nfs4_srv_t *nsrv4 = ng->nfs4_srv;
+
+	ng->nfs4_srv = NULL;
 
 	mutex_destroy(&nsrv4->deleg_lock);
 	mutex_destroy(&nsrv4->state_lock);
@@ -571,11 +579,8 @@ rfs4_srvrinit(void)
 {
 	extern void rfs4_attr_init();
 
-	zone_key_create(&rfs4_zone_key, rfs4_zone_init, NULL, rfs4_zone_fini);
-
 	rfs4_attr_init();
 
-
 	if (fem_create("deleg_rdops", nfs4_rd_deleg_tmpl, &deleg_rdops) != 0) {
 		rfs4_disable_delegation();
 	} else if (fem_create("deleg_wrops", nfs4_wr_deleg_tmpl,
@@ -602,15 +607,13 @@ rfs4_srvrfini(void)
 
 	fem_free(deleg_rdops);
 	fem_free(deleg_wrops);
-
-	(void) zone_key_delete(rfs4_zone_key);
 }
 
 void
 rfs4_do_server_start(int server_upordown,
     int srv_delegation, int cluster_booted)
 {
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	/* Is this a warm start? */
 	if (server_upordown == NFS_SERVER_QUIESCED) {
@@ -1517,7 +1520,7 @@ rfs4_op_commit(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
 		goto out;
 	}
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 	*cs->statusp = resp->status = NFS4_OK;
 	resp->writeverf = nsrv4->write4verf;
 out:
@@ -5664,7 +5667,7 @@ rfs4_op_write(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req,
 		goto out;
 	}
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 	if (args->data_len == 0) {
 		*cs->statusp = resp->status = NFS4_OK;
 		resp->count = 0;
@@ -5844,7 +5847,7 @@ rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi,
 	    KM_SLEEP);
 
 	cs.basecr = cr;
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 
 	DTRACE_NFSV4_2(compound__start, struct compound_state *, &cs,
 	    COMPOUND4args *, args);
@@ -6656,7 +6659,7 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs,
 			 * We are writing over an existing file.
 			 * Check to see if we need to recall a delegation.
 			 */
-			nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+			nsrv4 = nfs4_get_srv();
 			rfs4_hold_deleg_policy(nsrv4);
 			if ((fp = rfs4_findfile(vp, NULL, &create)) != NULL) {
 				if (rfs4_check_delegated_byfp(FWRITE, fp,
@@ -8233,7 +8236,7 @@ rfs4_op_setclientid_confirm(nfs_argop4 *argop, nfs_resop4 *resop,
 	    struct compound_state *, cs,
 	    SETCLIENTID_CONFIRM4args *, args);
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 	*cs->statusp = res->status = NFS4_OK;
 
 	cp = rfs4_findclient_by_id(args->clientid, TRUE);
diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_deleg.c b/usr/src/uts/common/fs/nfs/nfs4_srv_deleg.c
index 46fac9a42b..af44a447ce 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv_deleg.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv_deleg.c
@@ -160,7 +160,7 @@ rfs4_rele_deleg_policy(nfs4_srv_t *nsrv4)
 srv_deleg_policy_t
 nfs4_get_deleg_policy()
 {
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 	return (nsrv4->nfs4_deleg_policy);
 }
 
@@ -1270,7 +1270,7 @@ rfs4_grant_delegation(delegreq_t dreq, rfs4_state_t *sp, int *recall)
 	ASSERT(rfs4_dbe_islocked(sp->rs_dbe));
 	ASSERT(rfs4_dbe_islocked(fp->rf_dbe));
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 
 	/* Is the server even providing delegations? */
 	if (nsrv4->nfs4_deleg_policy == SRV_NEVER_DELEGATE || dreq == DELEG_NONE)
@@ -1449,7 +1449,7 @@ rfs4_check_delegated_byfp(int mode, rfs4_file_t *fp,
 {
 	rfs4_deleg_state_t *dsp;
 
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	/* Is delegation enabled? */
 	if (nsrv4->nfs4_deleg_policy == SRV_NEVER_DELEGATE)
@@ -1522,7 +1522,7 @@ rfs4_check_delegated(int mode, vnode_t *vp, bool_t trunc)
 	bool_t create = FALSE;
 	bool_t rc = FALSE;
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 	rfs4_hold_deleg_policy(nsrv4);
 
 	/* Is delegation enabled? */
@@ -1548,7 +1548,7 @@ rfs4_check_delegated(int mode, vnode_t *vp, bool_t trunc)
 void
 rfs4_clear_dont_grant(rfs4_file_t *fp)
 {
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	if (nsrv4->nfs4_deleg_policy == SRV_NEVER_DELEGATE)
 		return;
@@ -1888,7 +1888,7 @@ rfs4_disable_delegation(void)
 {
 	nfs4_srv_t *nsrv4;
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 	mutex_enter(&nsrv4->deleg_lock);
 	rfs4_deleg_disabled++;
 	mutex_exit(&nsrv4->deleg_lock);
@@ -1899,7 +1899,7 @@ rfs4_enable_delegation(void)
 {
 	nfs4_srv_t *nsrv4;
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 	mutex_enter(&nsrv4->deleg_lock);
 	ASSERT(rfs4_deleg_disabled > 0);
 	rfs4_deleg_disabled--;
diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
index c392e9cf4d..3c43aa3e0e 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
@@ -169,6 +169,7 @@ pseudo_exportfs(nfs_export_t *ne, vnode_t *vp, fid_t *fid,
 	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_zoneid = ne->ne_globals->nfs_zoneid;
 
 	/*
 	 * Build up the template fhandle
@@ -840,7 +841,18 @@ treeclimb_unexport(nfs_export_t *ne, struct exportinfo *exip)
 	ASSERT(RW_WRITE_HELD(&ne->exported_lock));
 	ASSERT(curzone == exip->exi_zone || curzone == global_zone);
 
+	/*
+	 * 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;
+	}
+
 	/*
 	 * The unshared exportinfo was unlinked in unexport().
 	 * Zeroing tree_exi ensures that we will skip it.
diff --git a/usr/src/uts/common/fs/nfs/nfs4_state.c b/usr/src/uts/common/fs/nfs/nfs4_state.c
index 85fa06f0ec..727c043c63 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_state.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_state.c
@@ -1133,7 +1133,7 @@ void
 rfs4_clear_client_state(struct nfs4clrst_args *clr)
 {
 	nfs4_srv_t *nsrv4;
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 	(void) rfs4_dbe_walk(nsrv4->rfs4_client_tab, rfs4_client_scrub, clr);
 }
 
@@ -1492,7 +1492,7 @@ rfs4_state_zone_fini()
 {
 	rfs4_database_t *dbp;
 	nfs4_srv_t *nsrv4;
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 
 	rfs4_set_deleg_policy(nsrv4, SRV_NEVER_DELEGATE);
 
@@ -1668,7 +1668,7 @@ rfs4_dss_remove_cpleaf(rfs4_client_t *cp)
 	 * from all server instances.
 	 */
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 	mutex_enter(&nsrv4->servinst_lock);
 	for (sip = nsrv4->nfs4_cur_servinst; sip != NULL; sip = sip->prev) {
 		/* remove the leaf file associated with this server instance */
@@ -1744,7 +1744,7 @@ rfs4_client_create(rfs4_entry_t u_entry, void *arg)
 	scid_confirm_verf *scvp;
 	nfs4_srv_t *nsrv4;
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 
 	/* Get a clientid to give to the client */
 	cidp = (cid *)&cp->rc_clientid;
@@ -1838,7 +1838,7 @@ rfs4_findclient(nfs_client_id4 *client, bool_t *create,	rfs4_client_t *oldcp)
 {
 	rfs4_client_t *cp;
 	nfs4_srv_t *nsrv4;
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 
 
 	if (oldcp) {
@@ -1865,7 +1865,7 @@ rfs4_findclient_by_id(clientid4 clientid, bool_t find_unconfirmed)
 	rfs4_client_t *cp;
 	bool_t create = FALSE;
 	cid *cidp = (cid *)&clientid;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	/* If we're a cluster and the nodeid isn't right, short-circuit */
 	if (cluster_bootflags & CLUSTER_BOOTED && foreign_clientid(cidp))
@@ -1986,7 +1986,7 @@ rfs4_find_clntip(struct sockaddr *addr, bool_t *create)
 	rfs4_clntip_t *cp;
 	nfs4_srv_t *nsrv4;
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 
 	rw_enter(&nsrv4->rfs4_findclient_lock, RW_READER);
 
@@ -2003,7 +2003,7 @@ rfs4_invalidate_clntip(struct sockaddr *addr)
 {
 	rfs4_clntip_t *cp;
 	bool_t create = FALSE;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	rw_enter(&nsrv4->rfs4_findclient_lock, RW_READER);
 
@@ -2164,7 +2164,7 @@ rfs4_openowner_create(rfs4_entry_t u_entry, void *arg)
 	seqid4 seqid = argp->ro_open_seqid;
 	rfs4_client_t *cp;
 	bool_t create = FALSE;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	rw_enter(&nsrv4->rfs4_findclient_lock, RW_READER);
 
@@ -2214,7 +2214,7 @@ rfs4_findopenowner(open_owner4 *openowner, bool_t *create, seqid4 seqid)
 {
 	rfs4_openowner_t *oo;
 	rfs4_openowner_t arg;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	arg.ro_owner = *openowner;
 	arg.ro_open_seqid = seqid;
@@ -2362,7 +2362,7 @@ rfs4_lockowner_create(rfs4_entry_t u_entry, void *arg)
 	lock_owner4 *lockowner = (lock_owner4 *)arg;
 	rfs4_client_t *cp;
 	bool_t create = FALSE;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	rw_enter(&nsrv4->rfs4_findclient_lock, RW_READER);
 
@@ -2391,7 +2391,7 @@ rfs4_lockowner_t *
 rfs4_findlockowner(lock_owner4 *lockowner, bool_t *create)
 {
 	rfs4_lockowner_t *lo;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	/* CSTYLED */
 	lo = (rfs4_lockowner_t *)rfs4_dbsearch(nsrv4->rfs4_lockowner_idx, lockowner,
@@ -2405,7 +2405,7 @@ rfs4_findlockowner_by_pid(pid_t pid)
 {
 	rfs4_lockowner_t *lo;
 	bool_t create = FALSE;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	lo = (rfs4_lockowner_t *)rfs4_dbsearch(nsrv4->rfs4_lockowner_pid_idx,
 	    (void *)(uintptr_t)pid, &create, NULL, RFS4_DBS_VALID);
@@ -2518,7 +2518,7 @@ rfs4_findfile(vnode_t *vp, nfs_fh4 *fh, bool_t *create)
 {
 	rfs4_file_t *fp;
 	rfs4_fcreate_arg arg;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	arg.vp = vp;
 	arg.fh = fh;
@@ -2560,7 +2560,7 @@ rfs4_findfile_withlock(vnode_t *vp, nfs_fh4 *fh, bool_t *create)
 	rfs4_file_t *fp;
 	rfs4_fcreate_arg arg;
 	bool_t screate = *create;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	if (screate == FALSE) {
 		mutex_enter(&vp->v_vsd_lock);
@@ -2747,7 +2747,7 @@ rfs4_findlo_state(stateid_t *id, bool_t lock_fp)
 {
 	rfs4_lo_state_t *lsp;
 	bool_t create = FALSE;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	lsp = (rfs4_lo_state_t *)rfs4_dbsearch(nsrv4->rfs4_lo_state_idx, id,
 	    &create, NULL, RFS4_DBS_VALID);
@@ -2788,7 +2788,7 @@ rfs4_findlo_state_by_owner(rfs4_lockowner_t *lo, rfs4_state_t *sp,
 {
 	rfs4_lo_state_t *lsp;
 	rfs4_lo_state_t arg;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	arg.rls_locker = lo;
 	arg.rls_state = sp;
@@ -2805,7 +2805,7 @@ get_stateid(id_t eid)
 	stateid_t id;
 	nfs4_srv_t *nsrv4;
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 
 	id.bits.boottime = nsrv4->rfs4_start_time;
 	id.bits.ident = eid;
@@ -3063,7 +3063,7 @@ rfs4_deleg_state_t *
 rfs4_finddeleg(rfs4_state_t *sp, bool_t *create)
 {
 	rfs4_deleg_state_t ds, *dsp;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	ds.rds_client = sp->rs_owner->ro_client;
 	ds.rds_finfo = sp->rs_finfo;
@@ -3079,7 +3079,7 @@ rfs4_finddelegstate(stateid_t *id)
 {
 	rfs4_deleg_state_t *dsp;
 	bool_t create = FALSE;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	dsp = (rfs4_deleg_state_t *)rfs4_dbsearch(nsrv4->rfs4_deleg_state_idx,
 	    id, &create, NULL, RFS4_DBS_VALID);
@@ -3201,7 +3201,7 @@ rfs4_findstate_by_owner_file(rfs4_openowner_t *oo, rfs4_file_t *fp,
 {
 	rfs4_state_t *sp;
 	rfs4_state_t key;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	key.rs_owner = oo;
 	key.rs_finfo = fp;
@@ -3217,7 +3217,7 @@ static rfs4_state_t *
 rfs4_findstate_by_file(rfs4_file_t *fp)
 {
 	bool_t create = FALSE;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	return ((rfs4_state_t *)rfs4_dbsearch(nsrv4->rfs4_state_file_idx, fp,
 	    &create, fp, RFS4_DBS_VALID));
@@ -3270,7 +3270,7 @@ rfs4_findstate(stateid_t *id, rfs4_dbsearch_type_t find_invalid, bool_t lock_fp)
 {
 	rfs4_state_t *sp;
 	bool_t create = FALSE;
-	nfs4_srv_t *nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nfs4_srv_t *nsrv4 = nfs4_get_srv();
 
 	sp = (rfs4_state_t *)rfs4_dbsearch(nsrv4->rfs4_state_idx, id,
 	    &create, NULL, find_invalid);
@@ -3342,7 +3342,7 @@ rfs4_check_clientid(clientid4 *cp, int setclid_confirm)
 	cid *cidp = (cid *) cp;
 	nfs4_srv_t *nsrv4;
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 
 	/*
 	 * If we are booted as a cluster node, check the embedded nodeid.
@@ -3374,7 +3374,7 @@ what_stateid_error(stateid_t *id, stateid_type_t type)
 {
 	nfs4_srv_t *nsrv4;
 
-	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+	nsrv4 = nfs4_get_srv();
 
 	/* If we are booted as a cluster node, was stateid locally generated? */
 	if ((cluster_bootflags & CLUSTER_BOOTED) && foreign_stateid(id))
@@ -4108,17 +4108,21 @@ rfs4_file_walk_callout(rfs4_entry_t u_entry, void *e)
  * state in the server that refers to objects residing underneath this
  * particular export.  The ordering of the release is important.
  * Lock_owner, then state and then file.
+ *
+ * NFS zones note: nfs_export.c:unexport() calls this from a
+ * thread in the global zone for NGZ data structures, so we
+ * CANNOT use zone_getspecific anywhere in this code path.
  */
 void
-rfs4_clean_state_exi(struct exportinfo *exi)
+rfs4_clean_state_exi(nfs_export_t *ne, struct exportinfo *exi)
 {
+	nfs_globals_t *ng;
 	nfs4_srv_t *nsrv4;
 
-	/* curzone mightn't be exi_zone, so use exi_zone instead. */
-	ASSERT(exi->exi_zone == curzone || curzone == global_zone);
-	nsrv4 = zone_getspecific(rfs4_zone_key, exi->exi_zone);
-	if (nsrv4 == NULL) /* NOTE: NFSv4 cleanup MAY have already happened. */
-		return;
+	ng = ne->ne_globals;
+	ASSERT(ng->nfs_zoneid == exi->exi_zoneid);
+	nsrv4 = ng->nfs4_srv;
+
 	mutex_enter(&nsrv4->state_lock);
 
 	if (nsrv4->nfs4_server_state == NULL) {
@@ -4126,11 +4130,11 @@ rfs4_clean_state_exi(struct exportinfo *exi)
 		return;
 	}
 
-	/* CSTYLED */
-	rfs4_dbe_walk(nsrv4->rfs4_lo_state_tab, rfs4_lo_state_walk_callout, exi);
+	rfs4_dbe_walk(nsrv4->rfs4_lo_state_tab,
+	    rfs4_lo_state_walk_callout, exi);
 	rfs4_dbe_walk(nsrv4->rfs4_state_tab, rfs4_state_walk_callout, exi);
-	/* CSTYLED */
-	rfs4_dbe_walk(nsrv4->rfs4_deleg_state_tab, rfs4_deleg_state_walk_callout, exi);
+	rfs4_dbe_walk(nsrv4->rfs4_deleg_state_tab,
+	    rfs4_deleg_state_walk_callout, exi);
 	rfs4_dbe_walk(nsrv4->rfs4_file_tab, rfs4_file_walk_callout, exi);
 
 	mutex_exit(&nsrv4->state_lock);
diff --git a/usr/src/uts/common/fs/nfs/nfs_auth.c b/usr/src/uts/common/fs/nfs/nfs_auth.c
index 7808bfcd6f..c8b07dbaea 100644
--- a/usr/src/uts/common/fs/nfs/nfs_auth.c
+++ b/usr/src/uts/common/fs/nfs/nfs_auth.c
@@ -53,10 +53,8 @@
 
 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);
-static void *nfsauth_zone_init(zoneid_t);
-static void nfsauth_zone_shutdown(zoneid_t zoneid, void *data);
-static void nfsauth_zone_fini(zoneid_t, void *);
 
 extern pri_t minclsyspri;
 
@@ -177,14 +175,21 @@ static void nfsauth_refresh_thread(nfsauth_globals_t *);
 
 static int nfsauth_cache_compar(const void *, const void *);
 
-static zone_key_t	nfsauth_zone_key;
+static nfsauth_globals_t *
+nfsauth_get_zg(void)
+{
+	nfs_globals_t *ng = zone_getspecific(nfssrv_zone_key, curzone);
+	nfsauth_globals_t *nag = ng->nfs_auth;
+	ASSERT(nag != NULL);
+	return (nag);
+}
 
 void
 mountd_args(uint_t did)
 {
 	nfsauth_globals_t *nag;
 
-	nag = zone_getspecific(nfsauth_zone_key, curzone);
+	nag = nfsauth_get_zg();
 	mutex_enter(&nag->mountd_lock);
 	if (nag->mountd_dh != NULL)
 		door_ki_rele(nag->mountd_dh);
@@ -195,9 +200,6 @@ mountd_args(uint_t did)
 void
 nfsauth_init(void)
 {
-	zone_key_create(&nfsauth_zone_key, nfsauth_zone_init,
-	    nfsauth_zone_shutdown, nfsauth_zone_fini);
-
 	exi_cache_handle = kmem_cache_create("exi_cache_handle",
 	    sizeof (struct auth_cache), 0, NULL, NULL,
 	    exi_cache_reclaim, NULL, NULL, 0);
@@ -209,9 +211,8 @@ nfsauth_fini(void)
 	kmem_cache_destroy(exi_cache_handle);
 }
 
-/*ARGSUSED*/
-static void *
-nfsauth_zone_init(zoneid_t zoneid)
+void
+nfsauth_zone_init(nfs_globals_t *ng)
 {
 	nfsauth_globals_t *nag;
 
@@ -228,15 +229,14 @@ nfsauth_zone_init(zoneid_t zoneid)
 	cv_init(&nag->refreshq_cv, NULL, CV_DEFAULT, NULL);
 	nag->refreshq_thread_state = REFRESHQ_THREAD_NEED_CREATE;
 
-	return (nag);
+	ng->nfs_auth = nag;
 }
 
-/*ARGSUSED*/
-static void
-nfsauth_zone_shutdown(zoneid_t zoneid, void *data)
+void
+nfsauth_zone_shutdown(nfs_globals_t *ng)
 {
 	refreshq_exi_node_t	*ren;
-	nfsauth_globals_t	*nag = data;
+	nfsauth_globals_t	*nag = ng->nfs_auth;
 
 	/* Prevent the nfsauth_refresh_thread from getting new work */
 	mutex_enter(&nag->refreshq_lock);
@@ -271,11 +271,12 @@ nfsauth_zone_shutdown(zoneid_t zoneid, void *data)
 	}
 }
 
-/*ARGSUSED*/
-static void
-nfsauth_zone_fini(zoneid_t zoneid, void *data)
+void
+nfsauth_zone_fini(nfs_globals_t *ng)
 {
-	nfsauth_globals_t *nag = data;
+	nfsauth_globals_t *nag = ng->nfs_auth;
+
+	ng->nfs_auth = NULL;
 
 	list_destroy(&nag->refreshq_queue);
 	cv_destroy(&nag->refreshq_cv);
@@ -884,7 +885,7 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
 	ASSERT(cr != NULL);
 
 	ASSERT3P(curzone, ==, exi->exi_zone);
-	nag = zone_getspecific(nfsauth_zone_key, curzone);
+	nag = nfsauth_get_zg();
 
 	/*
 	 * Now check whether this client already
@@ -1465,18 +1466,41 @@ nfsauth_cache_free(struct exportinfo *exi)
 }
 
 /*
- * 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.
+ * 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 = nfs_get_export();
+	nfs_export_t *ne = ng->nfs_export;
 
 	rw_enter(&ne->exported_lock, RW_READER);
 
@@ -1490,7 +1514,7 @@ exi_cache_reclaim(void *cdrarg)
 	atomic_inc_uint(&nfsauth_cache_reclaim);
 }
 
-void
+static void
 exi_cache_trim(struct exportinfo *exi)
 {
 	struct auth_cache_clnt *c;
diff --git a/usr/src/uts/common/fs/nfs/nfs_export.c b/usr/src/uts/common/fs/nfs/nfs_export.c
index 2ed2eae291..4b13116de4 100644
--- a/usr/src/uts/common/fs/nfs/nfs_export.c
+++ b/usr/src/uts/common/fs/nfs/nfs_export.c
@@ -68,8 +68,6 @@
 #include <nfs/lm.h>
 #include <sys/sunddi.h>
 
-static zone_key_t nfs_export_key;
-
 /*
  * exi_id support
  *
@@ -122,7 +120,10 @@ fhandle_t nullfh2;	/* for comparing V2 filehandles */
 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
@@ -722,10 +723,21 @@ 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;
@@ -804,6 +816,7 @@ 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);
@@ -855,9 +868,35 @@ exi_id_get_next()
 	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;
@@ -866,6 +905,8 @@ nfs_export_zone_init(zoneid_t zoneid)
 
 	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.
@@ -883,16 +924,14 @@ nfs_export_zone_init(zoneid_t zoneid)
 	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++) {
@@ -904,12 +943,7 @@ nfs_export_zone_init(zoneid_t zoneid)
 	}
 	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);
 
@@ -925,24 +959,79 @@ nfs_export_zone_init(zoneid_t zoneid)
 	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 */
@@ -960,14 +1049,24 @@ nfs_export_zone_fini(zoneid_t zoneid, void *data)
 	    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));
 }
@@ -987,9 +1086,6 @@ nfs_exportinit(void)
 	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();
 }
 
@@ -999,7 +1095,6 @@ nfs_exportinit(void)
 void
 nfs_exportfini(void)
 {
-	(void) zone_key_delete(nfs_export_key);
 	avl_destroy(&exi_id_tree);
 	mutex_destroy(&nfs_exi_id_lock);
 }
@@ -1843,7 +1938,7 @@ unexport(nfs_export_t *ne, struct exportinfo *exi)
 	 * 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
diff --git a/usr/src/uts/common/fs/nfs/nfs_server.c b/usr/src/uts/common/fs/nfs/nfs_server.c
index b61522a63b..d4955f5d08 100644
--- a/usr/src/uts/common/fs/nfs/nfs_server.c
+++ b/usr/src/uts/common/fs/nfs/nfs_server.c
@@ -115,7 +115,10 @@ static struct modlinkage modlinkage = {
 	MODREV_1, (void *)&modlmisc, NULL
 };
 
-zone_key_t nfssrv_zone_key;
+zone_key_t	nfssrv_zone_key;
+list_t		nfssrv_globals_list;
+krwlock_t	nfssrv_globals_rwl;
+
 kmem_cache_t *nfs_xuio_cache;
 int nfs_loaned_buffers = 0;
 
@@ -201,8 +204,9 @@ static char	*client_name(struct svc_req *req);
 static char	*client_addr(struct svc_req *req, char *buf);
 extern	int	sec_svc_getcred(struct svc_req *, cred_t *cr, char **, int *);
 extern	bool_t	sec_svc_inrootlist(int, caddr_t, int, caddr_t *);
-static void	*nfs_srv_zone_init(zoneid_t);
-static void	nfs_srv_zone_fini(zoneid_t, void *);
+static void	*nfs_server_zone_init(zoneid_t);
+static void	nfs_server_zone_fini(zoneid_t, void *);
+static void	nfs_server_zone_shutdown(zoneid_t, void *);
 
 #define	NFSLOG_COPY_NETBUF(exi, xprt, nb)	{		\
 	(nb)->maxlen = (xprt)->xp_rtaddr.maxlen;		\
@@ -263,6 +267,15 @@ nvlist_t *rfs4_dss_paths, *rfs4_dss_oldpaths;
 int rfs4_dispatch(struct rpcdisp *, struct svc_req *, SVCXPRT *, char *);
 bool_t rfs4_minorvers_mismatch(struct svc_req *, SVCXPRT *, void *);
 
+nfs_globals_t *
+nfs_srv_getzg(void)
+{
+	nfs_globals_t *ng;
+
+	ng = zone_getspecific(nfssrv_zone_key, curzone);
+	return (ng);
+}
+
 /*
  * Will be called at the point the server pool is being unregistered
  * from the pool list. From that point onwards, the pool is waiting
@@ -274,7 +287,7 @@ nfs_srv_offline(void)
 {
 	nfs_globals_t *ng;
 
-	ng = zone_getspecific(nfssrv_zone_key, curzone);
+	ng = nfs_srv_getzg();
 
 	mutex_enter(&ng->nfs_server_upordown_lock);
 	if (ng->nfs_server_upordown == NFS_SERVER_RUNNING) {
@@ -311,7 +324,7 @@ nfs_srv_quiesce_all(void)
 static void
 nfs_srv_shutdown_all(int quiesce)
 {
-	nfs_globals_t *ng = zone_getspecific(nfssrv_zone_key, curzone);
+	nfs_globals_t *ng = nfs_srv_getzg();
 
 	mutex_enter(&ng->nfs_server_upordown_lock);
 	if (quiesce) {
@@ -426,7 +439,7 @@ nfs_svc(struct nfs_svc_args *arg, model_t model)
 	model = model;		/* STRUCT macros don't always refer to it */
 #endif
 
-	ng = zone_getspecific(nfssrv_zone_key, curzone);
+	ng = nfs_srv_getzg();
 	STRUCT_SET_HANDLE(uap, model, arg);
 
 	/* Check privileges in nfssys() */
@@ -434,6 +447,10 @@ nfs_svc(struct nfs_svc_args *arg, model_t model)
 	if ((fp = getf(STRUCT_FGET(uap, fd))) == NULL)
 		return (EBADF);
 
+	/* Setup global file handle in nfs_export */
+	if ((error = nfs_export_get_rootfh(ng)) != 0)
+		return (error);
+
 	/*
 	 * Set read buffer size to rsize
 	 * and add room for RPC headers.
@@ -552,7 +569,7 @@ rdma_start(struct rdma_svc_args *rsa)
 		rsa->nfs_versmax = NFS_VERSMAX_DEFAULT;
 	}
 
-	ng = zone_getspecific(nfssrv_zone_key, curzone);
+	ng = nfs_srv_getzg();
 	ng->nfs_versmin = rsa->nfs_versmin;
 	ng->nfs_versmax = rsa->nfs_versmax;
 
@@ -2547,15 +2564,25 @@ client_addr(struct svc_req *req, char *buf)
 void
 nfs_srvinit(void)
 {
-	/* NFS server zone-specific global variables */
-	zone_key_create(&nfssrv_zone_key, nfs_srv_zone_init,
-	    NULL, nfs_srv_zone_fini);
 
+	/* Truly global stuff in this module (not per zone) */
+	rw_init(&nfssrv_globals_rwl, NULL, RW_DEFAULT, NULL);
+	list_create(&nfssrv_globals_list, sizeof (nfs_globals_t),
+	    offsetof (nfs_globals_t, nfs_g_link));
+
+	/* The order here is important */
 	nfs_exportinit();
 	rfs_srvrinit();
 	rfs3_srvrinit();
 	rfs4_srvrinit();
 	nfsauth_init();
+
+	/*
+	 * NFS server zone-specific global variables
+	 * Note the zone_init is called for the GZ here.
+	 */
+	zone_key_create(&nfssrv_zone_key, nfs_server_zone_init,
+	    nfs_server_zone_shutdown, nfs_server_zone_fini);
 }
 
 /*
@@ -2566,18 +2593,40 @@ nfs_srvinit(void)
 void
 nfs_srvfini(void)
 {
+
+	/*
+	 * NFS server zone-specific global variables
+	 * Note the zone_fini is called for the GZ here.
+	 */
+	(void) zone_key_delete(nfssrv_zone_key);
+
+	/* The order here is important (reverse of init) */
 	nfsauth_fini();
 	rfs4_srvrfini();
 	rfs3_srvrfini();
 	rfs_srvrfini();
 	nfs_exportfini();
 
-	(void) zone_key_delete(nfssrv_zone_key);
+	/* Truly global stuff in this module (not per zone) */
+	list_destroy(&nfssrv_globals_list);
+	rw_destroy(&nfssrv_globals_rwl);
 }
 
-/* ARGSUSED */
+/*
+ * Zone init, shutdown, fini functions for the NFS server
+ *
+ * This design is careful to create the entire hierarhcy of
+ * NFS server "globals" (including those created by various
+ * per-module *_zone_init functions, etc.) so that all these
+ * objects have exactly the same lifetime.
+ *
+ * These objects are also kept on a list for two reasons:
+ * 1: It makes finding these in  mdb _much_ easier.
+ * 2: It allows operating across all zone globals for
+ *    functions like nfs_auth.c:exi_cache_reclaim
+ */
 static void *
-nfs_srv_zone_init(zoneid_t zoneid)
+nfs_server_zone_init(zoneid_t zoneid)
 {
 	nfs_globals_t *ng;
 
@@ -2593,16 +2642,63 @@ nfs_srv_zone_init(zoneid_t zoneid)
 	mutex_init(&ng->rdma_wait_mutex, NULL, MUTEX_DEFAULT, NULL);
 	cv_init(&ng->rdma_wait_cv, NULL, CV_DEFAULT, NULL);
 
+	ng->nfs_zoneid = zoneid;
+
+	/*
+	 * Order here is important.
+	 * export init must precede srv init calls.
+	 */
+	nfs_export_zone_init(ng);
+	rfs_srv_zone_init(ng);
+	rfs3_srv_zone_init(ng);
+	rfs4_srv_zone_init(ng);
+	nfsauth_zone_init(ng);
+
+	rw_enter(&nfssrv_globals_rwl, RW_WRITER);
+	list_insert_tail(&nfssrv_globals_list, ng);
+	rw_exit(&nfssrv_globals_rwl);
+
 	return (ng);
 }
 
 /* ARGSUSED */
 static void
-nfs_srv_zone_fini(zoneid_t zoneid, void *data)
+nfs_server_zone_shutdown(zoneid_t zoneid, void *data)
+{
+	nfs_globals_t *ng;
+
+	ng = (nfs_globals_t *)data;
+
+	/*
+	 * Order is like _fini, but only
+	 * some modules need this hook.
+	 */
+	nfsauth_zone_shutdown(ng);
+	nfs_export_zone_shutdown(ng);
+}
+
+/* ARGSUSED */
+static void
+nfs_server_zone_fini(zoneid_t zoneid, void *data)
 {
 	nfs_globals_t *ng;
 
 	ng = (nfs_globals_t *)data;
+
+	rw_enter(&nfssrv_globals_rwl, RW_WRITER);
+	list_remove(&nfssrv_globals_list, ng);
+	rw_exit(&nfssrv_globals_rwl);
+
+	/*
+	 * Order here is important.
+	 * reverse order from init
+	 */
+	nfsauth_zone_fini(ng);
+	rfs4_srv_zone_fini(ng);
+	rfs3_srv_zone_fini(ng);
+	rfs_srv_zone_fini(ng);
+	nfs_export_zone_fini(ng);
+
 	mutex_destroy(&ng->nfs_server_upordown_lock);
 	cv_destroy(&ng->nfs_server_upordown_cv);
 	mutex_destroy(&ng->rdma_wait_mutex);
diff --git a/usr/src/uts/common/fs/nfs/nfs_srv.c b/usr/src/uts/common/fs/nfs/nfs_srv.c
index 131c97456f..719fd9e0b4 100644
--- a/usr/src/uts/common/fs/nfs/nfs_srv.c
+++ b/usr/src/uts/common/fs/nfs/nfs_srv.c
@@ -99,8 +99,6 @@ typedef struct nfs_srv {
 static int	sattr_to_vattr(struct nfssattr *, struct vattr *);
 static void	acl_perm(struct vnode *, struct exportinfo *, struct vattr *,
 			cred_t *);
-static void	*rfs_zone_init(zoneid_t zoneid);
-static void	rfs_zone_fini(zoneid_t zoneid, void *data);
 
 
 /*
@@ -113,7 +111,15 @@ static void	rfs_zone_fini(zoneid_t zoneid, void *data);
 #define	IFSOCK		0140000		/* socket */
 
 u_longlong_t nfs2_srv_caller_id;
-static zone_key_t rfs_zone_key;
+
+static nfs_srv_t *
+nfs_get_srv(void)
+{
+	nfs_globals_t *ng = zone_getspecific(nfssrv_zone_key, curzone);
+	nfs_srv_t *srv = ng->nfs_srv;
+	ASSERT(srv != NULL);
+	return (srv);
+}
 
 /*
  * Get file attributes.
@@ -1315,8 +1321,8 @@ rfs_write(struct nfswriteargs *wa, struct nfsattrstat *ns,
 	caller_context_t ct;
 	nfs_srv_t *nsrv;
 
-	ASSERT3P(curzone, ==, ((exi == NULL) ? curzone : exi->exi_zone));
-	nsrv = zone_getspecific(rfs_zone_key, curzone);
+	ASSERT(exi == NULL || exi->exi_zoneid == curzone->zone_id);
+	nsrv = nfs_get_srv();
 	if (!nsrv->write_async) {
 		rfs_write_sync(wa, ns, exi, req, cr, ro);
 		return;
@@ -3103,7 +3109,6 @@ void
 rfs_srvrinit(void)
 {
 	nfs2_srv_caller_id = fs_new_caller_id();
-	zone_key_create(&rfs_zone_key, rfs_zone_init, NULL, rfs_zone_fini);
 }
 
 void
@@ -3112,8 +3117,8 @@ rfs_srvrfini(void)
 }
 
 /* ARGSUSED */
-static void *
-rfs_zone_init(zoneid_t zoneid)
+void
+rfs_srv_zone_init(nfs_globals_t *ng)
 {
 	nfs_srv_t *ns;
 
@@ -3122,16 +3127,17 @@ rfs_zone_init(zoneid_t zoneid)
 	mutex_init(&ns->async_write_lock, NULL, MUTEX_DEFAULT, NULL);
 	ns->write_async = 1;
 
-	return (ns);
+	ng->nfs_srv = ns;
 }
 
 /* ARGSUSED */
-static void
-rfs_zone_fini(zoneid_t zoneid, void *data)
+void
+rfs_srv_zone_fini(nfs_globals_t *ng)
 {
-	nfs_srv_t *ns;
+	nfs_srv_t *ns = ng->nfs_srv;
+
+	ng->nfs_srv = NULL;
 
-	ns = (nfs_srv_t *)data;
 	mutex_destroy(&ns->async_write_lock);
 	kmem_free(ns, sizeof (*ns));
 }
diff --git a/usr/src/uts/common/nfs/export.h b/usr/src/uts/common/nfs/export.h
index 3a01abfad1..c68e5524ea 100644
--- a/usr/src/uts/common/nfs/export.h
+++ b/usr/src/uts/common/nfs/export.h
@@ -551,7 +551,7 @@ struct exportinfo {
 	uint32_t		exi_volatile_id;
 	struct ex_vol_rename	*exi_vol_rename;
 	kmutex_t		exi_vol_rename_lock;
-#endif /* VOLATILE_FH_TEST */
+#endif /* VOLATILE_FH_TEST -- keep last! */
 };
 #define	exi_zoneid exi_zone->zone_id
 
@@ -633,6 +633,10 @@ extern int	nfsauth_cache_clnt_compar(const void *, const void *);
 extern int	nfs_fhbcmp(char *, char *, int);
 extern void	nfs_exportinit(void);
 extern void	nfs_exportfini(void);
+extern void	nfs_export_zone_init(nfs_globals_t *);
+extern void	nfs_export_zone_fini(nfs_globals_t *);
+extern void	nfs_export_zone_shutdown(nfs_globals_t *);
+extern int	nfs_export_get_rootfh(nfs_globals_t *);
 extern int	chk_clnt_sec(struct exportinfo *, struct svc_req *);
 extern int	makefh(fhandle_t *, struct vnode *, struct exportinfo *);
 extern int	makefh_ol(fhandle_t *, struct exportinfo *, uint_t);
@@ -659,6 +663,8 @@ typedef struct nfs_export {
 	/* Root of nfs pseudo namespace */
 	treenode_t *ns_root;
 
+	nfs_globals_t		*ne_globals;	/* "up" pointer */
+
 	struct exportinfo *exptable_path_hash[PKP_HASH_SIZE];
 	struct exportinfo *exptable[EXPTABLESIZE];
 
@@ -698,6 +704,7 @@ extern int	nfs4_vget_pseudo(struct exportinfo *, vnode_t **, fid_t *);
 extern bool_t	nfs_visible_change(struct exportinfo *, vnode_t *,
 		    timespec_t *);
 extern void	tree_update_change(nfs_export_t *, treenode_t *, timespec_t *);
+extern void	rfs4_clean_state_exi(nfs_export_t *, struct exportinfo *);
 
 /*
  * Functions that handle the NFSv4 server namespace security flavors
diff --git a/usr/src/uts/common/nfs/nfs.h b/usr/src/uts/common/nfs/nfs.h
index 285b0a8a48..c705aee47c 100644
--- a/usr/src/uts/common/nfs/nfs.h
+++ b/usr/src/uts/common/nfs/nfs.h
@@ -27,8 +27,8 @@
 /*	  All Rights Reserved	*/
 
 /*
- * Copyright 2018 Nexenta Systems, Inc.
  * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
  */
 
 #ifndef	_NFS_NFS_H
@@ -85,10 +85,19 @@ typedef enum {
 	NFS_SERVER_OFFLINE	/* server pool offline */
 } nfs_server_running_t;
 
+/* Forward declarations for nfs_globals */
+struct nfs_export;
+struct nfs_srv;
+struct nfs3_srv;
+struct nfs4_srv;
+struct nfsauth_globals;
+
 /*
  * Zone globals variables of NFS server
  */
 typedef struct nfs_globals {
+	list_node_t		nfs_g_link;	/* all globals list */
+
 	rpcvers_t		nfs_versmin;
 	rpcvers_t		nfs_versmax;
 
@@ -100,6 +109,14 @@ typedef struct nfs_globals {
 	/* RDMA wait variables */
 	kcondvar_t		rdma_wait_cv;
 	kmutex_t		rdma_wait_mutex;
+
+	zoneid_t		nfs_zoneid;
+	/* Per-zone data structures private to each module */
+	struct nfs_export	*nfs_export;	/* nfs_export.c */
+	struct nfs_srv		*nfs_srv;	/* nfs_srv.c */
+	struct nfs3_srv		*nfs3_srv;	/* nfs3_srv.c */
+	struct nfs4_srv		*nfs4_srv;	/* nfs4_srv.c */
+	struct nfsauth_globals	*nfs_auth;	/* nfs_auth.c */
 } nfs_globals_t;
 
 /*
@@ -899,6 +916,8 @@ extern void	rfs_statfs(fhandle_t *, struct nfsstatfs *, struct exportinfo *,
 extern void	*rfs_statfs_getfh(fhandle_t *);
 extern void	rfs_srvrinit(void);
 extern void	rfs_srvrfini(void);
+extern void	rfs_srv_zone_init(nfs_globals_t *);
+extern void	rfs_srv_zone_fini(nfs_globals_t *);
 
 /*
  * flags to define path types during Multi Component Lookups
@@ -969,10 +988,14 @@ extern int	nfsauth_access(struct exportinfo *, struct svc_req *, cred_t *,
     uid_t *, gid_t *, uint_t *, gid_t **);
 extern void	nfsauth_init(void);
 extern void	nfsauth_fini(void);
+extern void	nfsauth_zone_init(nfs_globals_t *);
+extern void	nfsauth_zone_fini(nfs_globals_t *);
+extern void	nfsauth_zone_shutdown(nfs_globals_t *);
 extern int	nfs_setopts(vnode_t *, model_t, struct nfs_args *);
 extern int	nfs_mount_label_policy(vfs_t *, struct netbuf *,
     struct knetconfig *, cred_t *);
 extern boolean_t nfs_has_ctty(void);
+extern nfs_globals_t *nfs_srv_getzg(void);
 extern void	nfs_srv_stop_all(void);
 extern void	nfs_srv_quiesce_all(void);
 extern int	rfs4_dss_setpaths(char *, size_t);
@@ -984,8 +1007,10 @@ extern nvlist_t	*rfs4_dss_paths, *rfs4_dss_oldpaths;
 
 extern kstat_named_t	*global_svstat_ptr[];
 
-extern zone_key_t	rfs4_zone_key;
 extern zone_key_t	nfssrv_zone_key;
+extern list_t		nfssrv_globals_list;
+extern krwlock_t	nfssrv_globals_rwl;
+
 extern krwlock_t	rroklock;
 extern vtype_t		nf_to_vt[];
 extern kstat_named_t	*rfsproccnt_v2_ptr;
@@ -2277,6 +2302,8 @@ extern void	rfs3_commit(COMMIT3args *, COMMIT3res *, struct exportinfo *,
 extern void	*rfs3_commit_getfh(COMMIT3args *);
 extern void	rfs3_srvrinit(void);
 extern void	rfs3_srvrfini(void);
+extern void	rfs3_srv_zone_init(nfs_globals_t *);
+extern void	rfs3_srv_zone_fini(nfs_globals_t *);
 
 extern int	nfs3_validate_caches(vnode_t *, cred_t *);
 extern void	nfs3_cache_post_op_attr(vnode_t *, post_op_attr *, hrtime_t,
diff --git a/usr/src/uts/common/nfs/nfs4.h b/usr/src/uts/common/nfs/nfs4.h
index d17515343c..ce21ce999b 100644
--- a/usr/src/uts/common/nfs/nfs4.h
+++ b/usr/src/uts/common/nfs/nfs4.h
@@ -900,7 +900,6 @@ extern void		rfs4_dss_readstate(nfs4_srv_t *, int, char **);
  * Various interfaces to manipulate the state structures introduced
  * above
  */
-extern	void		rfs4_clean_state_exi(struct exportinfo *exi);
 extern	void		rfs4_free_reply(nfs_resop4 *);
 extern	void		rfs4_copy_reply(nfs_resop4 *, nfs_resop4 *);
 
@@ -1494,10 +1493,13 @@ extern void	rfs4_compound_flagproc(COMPOUND4args *, int *);
 
 extern void	rfs4_srvrinit(void);
 extern void	rfs4_srvrfini(void);
+extern void	rfs4_srv_zone_init(nfs_globals_t *);
+extern void	rfs4_srv_zone_fini(nfs_globals_t *);
 extern void	rfs4_state_g_init(void);
 extern void	rfs4_state_zone_init(nfs4_srv_t *);
 extern void	rfs4_state_g_fini(void);
 extern void	rfs4_state_zone_fini(void);
+extern nfs4_srv_t *nfs4_get_srv(void);
 
 #endif
 #ifdef	__cplusplus
-- 
2.21.0 (Apple Git-122)

