--- old/usr/src/uts/common/fs/nfs/nfs_srv.c	Mon Feb  3 11:23:30 2020
+++ new/usr/src/uts/common/fs/nfs/nfs_srv.c	Mon Feb  3 11:23:30 2020
@@ -30,6 +30,11 @@
  *	All rights reserved.
  */
 
+/*
+ * Copyright 2018 Nexenta Systems, Inc.
+ * Copyright (c) 2016 by Delphix. All rights reserved.
+ */
+
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/systm.h>
@@ -70,7 +75,22 @@
 
 #include <sys/strsubr.h>
 
+struct rfs_async_write_list;
+
 /*
+ * Zone globals of NFSv2 server
+ */
+typedef struct nfs_srv {
+	kmutex_t			async_write_lock;
+	struct rfs_async_write_list	*async_write_head;
+
+	/*
+	 * enables write clustering if == 1
+	 */
+	int		write_async;
+} nfs_srv_t;
+
+/*
  * These are the interface routines for the server side of the
  * Network File System.  See the NFS version 2 protocol specification
  * for a description of this interface.
@@ -80,6 +100,7 @@
 static void	acl_perm(struct vnode *, struct exportinfo *, struct vattr *,
 			cred_t *);
 
+
 /*
  * Some "over the wire" UNIX file types.  These are encoded
  * into the mode.  This needs to be fixed in the next rev.
@@ -91,6 +112,15 @@
 
 u_longlong_t nfs2_srv_caller_id;
 
+static nfs_srv_t *
+nfs_get_srv(void)
+{
+	nfs_globals_t *ng = nfs_srv_getzg();
+	nfs_srv_t *srv = ng->nfs_srv;
+	ASSERT(srv != NULL);
+	return (srv);
+}
+
 /*
  * Get file attributes.
  * Returns the current attributes of the file with the given fhandle.
@@ -386,11 +416,13 @@
 {
 	struct exportinfo *exi;
 	vnode_t *dvp = *dvpp;
+	vnode_t *zone_rootvp;
 
-	ASSERT(dvp->v_flag & VROOT);
+	zone_rootvp = (*exip)->exi_ne->exi_root->exi_vp;
+	ASSERT((dvp->v_flag & VROOT) || VN_CMP(zone_rootvp, dvp));
 
 	VN_HOLD(dvp);
-	dvp = untraverse(dvp);
+	dvp = untraverse(dvp, zone_rootvp);
 	exi = nfs_vptoexi(NULL, dvp, cr, NULL, NULL, FALSE);
 	if (exi == NULL) {
 		VN_RELE(dvp);
@@ -397,6 +429,7 @@
 		return (-1);
 	}
 
+	ASSERT3U(exi->exi_zoneid, ==, (*exip)->exi_zoneid);
 	exi_rele(*exip);
 	*exip = exi;
 	VN_RELE(*dvpp);
@@ -446,7 +479,7 @@
 	 * location of the public filehandle.
 	 */
 	if (exi != NULL && (exi->exi_export.ex_flags & EX_PUBLIC)) {
-		dvp = rootdir;
+		dvp = ZONE_ROOTVP();
 		VN_HOLD(dvp);
 	} else {
 		dvp = nfs_fhtovp(fhp, exi);
@@ -457,6 +490,7 @@
 	}
 
 	exi_hold(exi);
+	ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
 
 	/*
 	 * Not allow lookup beyond root.
@@ -466,7 +500,7 @@
 	if (strcmp(da->da_name, "..") == 0 &&
 	    EQFID(&exi->exi_fid, (fid_t *)&fhp->fh_len)) {
 		if ((exi->exi_export.ex_flags & EX_NOHIDE) &&
-		    (dvp->v_flag & VROOT)) {
+		    ((dvp->v_flag & VROOT) || VN_IS_CURZONEROOT(dvp))) {
 			/*
 			 * special case for ".." and 'nohide'exported root
 			 */
@@ -502,6 +536,7 @@
 		publicfh_flag = TRUE;
 
 		exi_rele(exi);
+		exi = NULL;
 
 		error = rfs_publicfh_mclookup(name, dvp, cr, &vp, &exi,
 		    &sec);
@@ -635,10 +670,12 @@
 	if (is_referral) {
 		char *s;
 		size_t strsz;
+		kstat_named_t *stat =
+		    exi->exi_ne->ne_globals->svstat[NFS_VERSION];
 
 		/* Get an artificial symlink based on a referral */
 		s = build_symlink(vp, cr, &strsz);
-		global_svstat_ptr[2][NFS_REFERLINKS].value.ui64++;
+		stat[NFS_REFERLINKS].value.ui64++;
 		DTRACE_PROBE2(nfs2serv__func__referral__reflink,
 		    vnode_t *, vp, char *, s);
 		if (s == NULL)
@@ -775,6 +812,8 @@
 
 	/* check if a monitor detected a delegation conflict */
 	if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
+		if (in_crit)
+			nbl_end_crit(vp);
 		VN_RELE(vp);
 		/* mark as wouldblock so response is dropped */
 		curthread->t_flag |= T_WOULDBLOCK;
@@ -1100,10 +1139,7 @@
 
 	/* check if a monitor detected a delegation conflict */
 	if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
-		VN_RELE(vp);
-		/* mark as wouldblock so response is dropped */
-		curthread->t_flag |= T_WOULDBLOCK;
-		return;
+		goto out;
 	}
 
 	if (wa->wa_data || wa->wa_rlist) {
@@ -1143,6 +1179,7 @@
 		error = VOP_WRITE(vp, &uio, FSYNC, cr, &ct);
 		curthread->t_cred = savecred;
 	} else {
+
 		iovcnt = 0;
 		for (m = wa->wa_mblk; m != NULL; m = m->b_cont)
 			iovcnt++;
@@ -1286,8 +1323,11 @@
 	cred_t *savecred;
 	int in_crit = 0;
 	caller_context_t ct;
+	nfs_srv_t *nsrv;
 
-	if (!rfs_write_async) {
+	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;
 	}
@@ -1312,8 +1352,8 @@
 	 * Look to see if there is already a cluster started
 	 * for this file.
 	 */
-	mutex_enter(&rfs_async_write_lock);
-	for (lp = rfs_async_write_head; lp != NULL; lp = lp->next) {
+	mutex_enter(&nsrv->async_write_lock);
+	for (lp = nsrv->async_write_head; lp != NULL; lp = lp->next) {
 		if (bcmp(&wa->wa_fhandle, lp->fhp,
 		    sizeof (fhandle_t)) == 0)
 			break;
@@ -1339,8 +1379,8 @@
 		else
 			trp->list = nrp;
 		while (nrp->ns->ns_status == RFSWRITE_INITVAL)
-			cv_wait(&lp->cv, &rfs_async_write_lock);
-		mutex_exit(&rfs_async_write_lock);
+			cv_wait(&lp->cv, &nsrv->async_write_lock);
+		mutex_exit(&nsrv->async_write_lock);
 
 		return;
 	}
@@ -1357,15 +1397,15 @@
 	nlp->list = nrp;
 	nlp->next = NULL;
 
-	if (rfs_async_write_head == NULL) {
-		rfs_async_write_head = nlp;
+	if (nsrv->async_write_head == NULL) {
+		nsrv->async_write_head = nlp;
 	} else {
-		lp = rfs_async_write_head;
+		lp = nsrv->async_write_head;
 		while (lp->next != NULL)
 			lp = lp->next;
 		lp->next = nlp;
 	}
-	mutex_exit(&rfs_async_write_lock);
+	mutex_exit(&nsrv->async_write_lock);
 
 	/*
 	 * Convert the file handle common to all of the requests
@@ -1373,11 +1413,11 @@
 	 */
 	vp = nfs_fhtovp(&wa->wa_fhandle, exi);
 	if (vp == NULL) {
-		mutex_enter(&rfs_async_write_lock);
-		if (rfs_async_write_head == nlp)
-			rfs_async_write_head = nlp->next;
+		mutex_enter(&nsrv->async_write_lock);
+		if (nsrv->async_write_head == nlp)
+			nsrv->async_write_head = nlp->next;
 		else {
-			lp = rfs_async_write_head;
+			lp = nsrv->async_write_head;
 			while (lp->next != nlp)
 				lp = lp->next;
 			lp->next = nlp->next;
@@ -1388,7 +1428,7 @@
 			rp->thread->t_flag |= t_flag;
 		}
 		cv_broadcast(&nlp->cv);
-		mutex_exit(&rfs_async_write_lock);
+		mutex_exit(&nsrv->async_write_lock);
 
 		return;
 	}
@@ -1399,11 +1439,11 @@
 	 */
 	if (vp->v_type != VREG) {
 		VN_RELE(vp);
-		mutex_enter(&rfs_async_write_lock);
-		if (rfs_async_write_head == nlp)
-			rfs_async_write_head = nlp->next;
+		mutex_enter(&nsrv->async_write_lock);
+		if (nsrv->async_write_head == nlp)
+			nsrv->async_write_head = nlp->next;
 		else {
-			lp = rfs_async_write_head;
+			lp = nsrv->async_write_head;
 			while (lp->next != nlp)
 				lp = lp->next;
 			lp->next = nlp->next;
@@ -1414,7 +1454,7 @@
 			rp->thread->t_flag |= t_flag;
 		}
 		cv_broadcast(&nlp->cv);
-		mutex_exit(&rfs_async_write_lock);
+		mutex_exit(&nsrv->async_write_lock);
 
 		return;
 	}
@@ -1446,11 +1486,11 @@
 		VN_RELE(vp);
 		/* mark as wouldblock so response is dropped */
 		curthread->t_flag |= T_WOULDBLOCK;
-		mutex_enter(&rfs_async_write_lock);
-		if (rfs_async_write_head == nlp)
-			rfs_async_write_head = nlp->next;
+		mutex_enter(&nsrv->async_write_lock);
+		if (nsrv->async_write_head == nlp)
+			nsrv->async_write_head = nlp->next;
 		else {
-			lp = rfs_async_write_head;
+			lp = nsrv->async_write_head;
 			while (lp->next != nlp)
 				lp = lp->next;
 			lp->next = nlp->next;
@@ -1462,7 +1502,7 @@
 			}
 		}
 		cv_broadcast(&nlp->cv);
-		mutex_exit(&rfs_async_write_lock);
+		mutex_exit(&nsrv->async_write_lock);
 
 		return;
 	}
@@ -1484,16 +1524,16 @@
 	 * will allow more requests to be clustered in this
 	 * second cluster.
 	 */
-	mutex_enter(&rfs_async_write_lock);
-	if (rfs_async_write_head == nlp)
-		rfs_async_write_head = nlp->next;
+	mutex_enter(&nsrv->async_write_lock);
+	if (nsrv->async_write_head == nlp)
+		nsrv->async_write_head = nlp->next;
 	else {
-		lp = rfs_async_write_head;
+		lp = nsrv->async_write_head;
 		while (lp->next != nlp)
 			lp = lp->next;
 		lp->next = nlp->next;
 	}
-	mutex_exit(&rfs_async_write_lock);
+	mutex_exit(&nsrv->async_write_lock);
 
 	/*
 	 * Step through the list of requests in this cluster.
@@ -1738,7 +1778,7 @@
 	VN_RELE(vp);
 
 	t_flag = curthread->t_flag & T_WOULDBLOCK;
-	mutex_enter(&rfs_async_write_lock);
+	mutex_enter(&nsrv->async_write_lock);
 	for (rp = nlp->list; rp != NULL; rp = rp->list) {
 		if (rp->ns->ns_status == RFSWRITE_INITVAL) {
 			rp->ns->ns_status = puterrno(error);
@@ -1746,7 +1786,7 @@
 		}
 	}
 	cv_broadcast(&nlp->cv);
-	mutex_exit(&rfs_async_write_lock);
+	mutex_exit(&nsrv->async_write_lock);
 
 }
 
@@ -2211,7 +2251,7 @@
 
 	/* Check for delegation on the file being renamed over, if it exists */
 
-	if (rfs4_deleg_policy != SRV_NEVER_DELEGATE &&
+	if (nfs4_get_deleg_policy() != SRV_NEVER_DELEGATE &&
 	    VOP_LOOKUP(tovp, args->rna_to.da_name, &targvp, NULL, 0, NULL, cr,
 	    NULL, NULL, NULL) == 0) {
 
@@ -2578,7 +2618,7 @@
 	 * supplying a vnode known to exist and illegal to
 	 * remove.
 	 */
-	error = VOP_RMDIR(vp, da->da_name, rootdir, cr, NULL, 0);
+	error = VOP_RMDIR(vp, da->da_name, ZONE_ROOTVP(), cr, NULL, 0);
 
 	/*
 	 * Force modified data and metadata out to stable storage.
@@ -2853,7 +2893,7 @@
 	return (0);
 }
 
-static enum nfsftype vt_to_nf[] = {
+static const enum nfsftype vt_to_nf[] = {
 	0, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, 0, 0, 0, NFSOC, 0
 };
 
@@ -3072,7 +3112,6 @@
 void
 rfs_srvrinit(void)
 {
-	mutex_init(&rfs_async_write_lock, NULL, MUTEX_DEFAULT, NULL);
 	nfs2_srv_caller_id = fs_new_caller_id();
 }
 
@@ -3079,9 +3118,34 @@
 void
 rfs_srvrfini(void)
 {
-	mutex_destroy(&rfs_async_write_lock);
 }
 
+/* ARGSUSED */
+void
+rfs_srv_zone_init(nfs_globals_t *ng)
+{
+	nfs_srv_t *ns;
+
+	ns = kmem_zalloc(sizeof (*ns), KM_SLEEP);
+
+	mutex_init(&ns->async_write_lock, NULL, MUTEX_DEFAULT, NULL);
+	ns->write_async = 1;
+
+	ng->nfs_srv = ns;
+}
+
+/* ARGSUSED */
+void
+rfs_srv_zone_fini(nfs_globals_t *ng)
+{
+	nfs_srv_t *ns = ng->nfs_srv;
+
+	ng->nfs_srv = NULL;
+
+	mutex_destroy(&ns->async_write_lock);
+	kmem_free(ns, sizeof (*ns));
+}
+
 static int
 rdma_setup_read_data2(struct nfsreadargs *ra, struct nfsrdresult *rr)
 {
