From 32a06c8783cbd00dc2cb3d2a16350ee37d5dd9f8 Mon Sep 17 00:00:00 2001
From: Evan Layton <evan.layton@nexenta.com>
Date: Tue, 19 Mar 2019 13:54:39 -0600
Subject: [PATCH 20/30] 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>

---
 usr/src/uts/common/fs/nfs/nfs4_srv_ns.c | 14 +++++++++++
 usr/src/uts/common/fs/nfs/nfs4_state.c  |  4 ++--
 usr/src/uts/common/fs/nfs/nfs_export.c  | 32 ++++++++++++++-----------
 usr/src/uts/common/nfs/export.h         |  2 +-
 4 files changed, 35 insertions(+), 17 deletions(-)

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 c76a6c5cd9..6d027797c0 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c
@@ -210,6 +210,14 @@ pseudo_exportfs(nfs_export_t *ne, vnode_t *vp, fid_t *fid, struct exp_visible *v
 	 */
 	export_link(ne, exi);
 
+	/*
+	 * Initialize exi_id and exi_kstats
+	 */
+	mutex_enter(&nfs_exi_id_lock);
+	exi->exi_id = exi_id_get_next();
+	avl_add(&exi_id_tree, exi);
+	mutex_exit(&nfs_exi_id_lock);
+
 	return (exi);
 }
 
@@ -793,6 +801,9 @@ treeclimb_export(struct exportinfo *exip)
 			exportinfo_t *e  = tree_head->tree_exi;
 			/* exip will be freed in exportfs() */
 			if (e && e != exip) {
+				mutex_enter(&nfs_exi_id_lock);
+				avl_remove(&exi_id_tree, e);
+				mutex_exit(&nfs_exi_id_lock);
 				export_unlink(ne, e);
 				exi_rele(e);
 			}
@@ -844,6 +855,9 @@ treeclimb_unexport(nfs_export_t *ne, struct exportinfo *exip)
 		/* Release pseudo export if it has no child */
 		if (TREE_ROOT(tnode) && !TREE_EXPORTED(tnode) &&
 		    tnode->tree_child_first == NULL) {
+			mutex_enter(&nfs_exi_id_lock);
+			avl_remove(&exi_id_tree, tnode->tree_exi);
+			mutex_exit(&nfs_exi_id_lock);
 			export_unlink(ne, tnode->tree_exi);
 			exi_rele(tnode->tree_exi);
 		}
diff --git a/usr/src/uts/common/fs/nfs/nfs4_state.c b/usr/src/uts/common/fs/nfs/nfs4_state.c
index 000081631d..fcd8f5b4ad 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_state.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_state.c
@@ -1479,13 +1479,13 @@ rfs4_state_zone_fini()
 
 	rfs4_set_deleg_policy(nsrv4, SRV_NEVER_DELEGATE);
 
+	mutex_enter(&nsrv4->state_lock);
+
 	if (nsrv4->nfs4_server_state == NULL) {
 		mutex_exit(&nsrv4->state_lock);
 		return;
 	}
 
-	mutex_enter(&nsrv4->state_lock);
-
 	/* destroy server instances and current instance ptr */
 	rfs4_servinst_destroy_all(nsrv4);
 
diff --git a/usr/src/uts/common/fs/nfs/nfs_export.c b/usr/src/uts/common/fs/nfs/nfs_export.c
index a345753f4e..5467a676a8 100644
--- a/usr/src/uts/common/fs/nfs/nfs_export.c
+++ b/usr/src/uts/common/fs/nfs/nfs_export.c
@@ -85,7 +85,7 @@ static zone_key_t nfs_export_key;
 static int exi_id_next;
 static bool_t exi_id_overflow;
 avl_tree_t exi_id_tree;
-krwlock_t nfs_exi_id_lock;
+kmutex_t nfs_exi_id_lock;
 
 static int	unexport(nfs_export_t *, exportinfo_t *);
 static void	exportfree(exportinfo_t *);
@@ -827,7 +827,7 @@ exi_id_get_next()
 	struct exportinfo e;
 	int ret = exi_id_next;
 
-	ASSERT(RW_LOCK_HELD(&nfs_exi_id_lock));
+	ASSERT(MUTEX_HELD(&nfs_exi_id_lock));
 
 	do {
 		exi_id_next++;
@@ -907,10 +907,10 @@ nfs_export_zone_init(zoneid_t zoneid)
 	export_link(ne, ne->exi_root);
 
 	/* Initialize exi_id and exi_kstats */
-	rw_enter(&nfs_exi_id_lock, RW_WRITER);
+	mutex_enter(&nfs_exi_id_lock);
 	ne->exi_root->exi_id = exi_id_get_next();
 	avl_add(&exi_id_tree, ne->exi_root);
-	rw_exit(&nfs_exi_id_lock);
+	mutex_exit(&nfs_exi_id_lock);
 
 	rw_exit(&ne->exported_lock);
 	ne->ns_root = NULL;
@@ -927,11 +927,12 @@ nfs_export_zone_fini(zoneid_t zoneid, void *data)
 	struct exportinfo *exi;
 
 	rw_enter(&ne->exported_lock, RW_WRITER);
-	rw_enter(&nfs_exi_id_lock, RW_WRITER);
+	mutex_enter(&nfs_exi_id_lock);
+
 	avl_remove(&exi_id_tree, ne->exi_root);
 	export_unlink(ne, ne->exi_root);
 
-	rw_exit(&nfs_exi_id_lock);
+	mutex_exit(&nfs_exi_id_lock);
 	rw_exit(&ne->exported_lock);
 
 	/* Deallocate the place holder for the public file handle */
@@ -968,7 +969,7 @@ nfs_export_zone_fini(zoneid_t zoneid, void *data)
 void
 nfs_exportinit(void)
 {
-	rw_init(&nfs_exi_id_lock, NULL, RW_DEFAULT, NULL);
+	mutex_init(&nfs_exi_id_lock, NULL, MUTEX_DEFAULT, NULL);
 
 	/* exi_id handling initialization */
 	exi_id_next = 0;
@@ -990,7 +991,7 @@ nfs_exportfini(void)
 {
 	(void) zone_key_delete(nfs_export_key);
 	avl_destroy(&exi_id_tree);
-	rw_destroy(&nfs_exi_id_lock);
+	mutex_destroy(&nfs_exi_id_lock);
 }
 
 /*
@@ -1584,9 +1585,9 @@ exportfs(struct exportfs_args *args, model_t model, cred_t *cr)
 	 */
 	for (ex = exi->fid_hash.next; ex != NULL; ex = ex->fid_hash.next) {
 		if (ex != ne->exi_root && VN_CMP(ex->exi_vp, vp)) {
-			rw_enter(&nfs_exi_id_lock, RW_WRITER);
+			mutex_enter(&nfs_exi_id_lock);
 			avl_remove(&exi_id_tree, ex);
-			rw_exit(&nfs_exi_id_lock);
+			mutex_exit(&nfs_exi_id_lock);
 			export_unlink(ne, ex);
 			break;
 		}
@@ -1691,13 +1692,13 @@ exportfs(struct exportfs_args *args, model_t model, cred_t *cr)
 	if (ex != NULL) {
 		exi->exi_id = ex->exi_id;
 	} else {
-		rw_enter(&nfs_exi_id_lock, RW_READER);
+		mutex_enter(&nfs_exi_id_lock);
 		exi->exi_id = exi_id_get_next();
-		rw_exit(&nfs_exi_id_lock);
+		mutex_exit(&nfs_exi_id_lock);
 	}
-	rw_enter(&nfs_exi_id_lock, RW_WRITER);
+	mutex_enter(&nfs_exi_id_lock);
 	avl_add(&exi_id_tree, exi);
-	rw_exit(&nfs_exi_id_lock);
+	mutex_exit(&nfs_exi_id_lock);
 
 	DTRACE_PROBE(nfss__i__exported_lock3_stop);
 	rw_exit(&ne->exported_lock);
@@ -1787,6 +1788,9 @@ unexport(nfs_export_t *ne, struct exportinfo *exi)
 		return (EINVAL);
 	}
 
+	mutex_enter(&nfs_exi_id_lock);
+	avl_remove(&exi_id_tree, exi);
+	mutex_exit(&nfs_exi_id_lock);
 	export_unlink(ne, exi);
 
 	/*
diff --git a/usr/src/uts/common/nfs/export.h b/usr/src/uts/common/nfs/export.h
index 20f33ad080..9f11b02ae7 100644
--- a/usr/src/uts/common/nfs/export.h
+++ b/usr/src/uts/common/nfs/export.h
@@ -695,7 +695,7 @@ extern void	export_unlink(nfs_export_t *, struct exportinfo *);
 /*
  * exi_id support
  */
-extern krwlock_t  nfs_exi_id_lock;
+extern kmutex_t  nfs_exi_id_lock;
 extern avl_tree_t exi_id_tree;
 extern int exi_id_get_next(void);
 
-- 
2.21.0

