--- old/usr/src/uts/common/fs/nfs/nfs_server.c	Mon Aug  5 17:26:06 2019
+++ new/usr/src/uts/common/fs/nfs/nfs_server.c	Mon Aug  5 17:26:06 2019
@@ -18,12 +18,9 @@
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
- * Copyright (c) 2013 by Delphix. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
- * Copyright (c) 2017 Joyent Inc
  */
 
 /*
@@ -32,6 +29,13 @@
  *	Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.
+ * Copyright (c) 2017 Joyent Inc
+ */
+
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/systm.h>
@@ -83,7 +87,6 @@
 #include <nfs/nfs_clnt.h>
 #include <nfs/nfs_acl.h>
 #include <nfs/nfs_log.h>
-#include <nfs/nfs_cmd.h>
 #include <nfs/lm.h>
 #include <nfs/nfs_dispatch.h>
 #include <nfs/nfs4_drc.h>
@@ -109,6 +112,7 @@
 	MODREV_1, (void *)&modlmisc, NULL
 };
 
+zone_key_t nfssrv_zone_key;
 kmem_cache_t *nfs_xuio_cache;
 int nfs_loaned_buffers = 0;
 
@@ -117,10 +121,7 @@
 {
 	int status;
 
-	if ((status = nfs_srvinit()) != 0) {
-		cmn_err(CE_WARN, "_init: nfs_srvinit failed");
-		return (status);
-	}
+	nfs_srvinit();
 
 	status = mod_install((struct modlinkage *)&modlinkage);
 	if (status != 0) {
@@ -177,14 +178,14 @@
  * supports RPC_PUBLICFH_OK, and if the filesystem is explicitly exported
  * public (i.e., not the placeholder).
  */
-#define	PUBLICFH_CHECK(disp, exi, fsid, xfid) \
+#define	PUBLICFH_CHECK(ne, disp, exi, fsid, xfid) \
 		((disp->dis_flags & RPC_PUBLICFH_OK) && \
 		((exi->exi_export.ex_flags & EX_PUBLIC) || \
-		(exi == exi_public && exportmatch(exi_root, \
+		(exi == ne->exi_public && exportmatch(ne->exi_root, \
 		fsid, xfid))))
 
 static void	nfs_srv_shutdown_all(int);
-static void	rfs4_server_start(int);
+static void	rfs4_server_start(nfs_globals_t *, int);
 static void	nullfree(void);
 static void	rfs_dispatch(struct svc_req *, SVCXPRT *);
 static void	acl_dispatch(struct svc_req *, SVCXPRT *);
@@ -191,7 +192,6 @@
 static void	common_dispatch(struct svc_req *, SVCXPRT *,
 		rpcvers_t, rpcvers_t, char *,
 		struct rpc_disptable *);
-static void	hanfsv4_failover(void);
 static	int	checkauth(struct exportinfo *, struct svc_req *, cred_t *, int,
 		bool_t, bool_t *);
 static char	*client_name(struct svc_req *req);
@@ -198,6 +198,8 @@
 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 *);
 
 #define	NFSLOG_COPY_NETBUF(exi, xprt, nb)	{		\
 	(nb)->maxlen = (xprt)->xp_rtaddr.maxlen;		\
@@ -248,41 +250,18 @@
 static SVC_CALLOUT_TABLE nfs_sct_rdma = {
 	sizeof (__nfs_sc_rdma) / sizeof (__nfs_sc_rdma[0]), FALSE, __nfs_sc_rdma
 };
-rpcvers_t nfs_versmin = NFS_VERSMIN_DEFAULT;
-rpcvers_t nfs_versmax = NFS_VERSMAX_DEFAULT;
 
 /*
- * Used to track the state of the server so that initialization
- * can be done properly.
- */
-typedef enum {
-	NFS_SERVER_STOPPED,	/* server state destroyed */
-	NFS_SERVER_STOPPING,	/* server state being destroyed */
-	NFS_SERVER_RUNNING,
-	NFS_SERVER_QUIESCED,	/* server state preserved */
-	NFS_SERVER_OFFLINE	/* server pool offline */
-} nfs_server_running_t;
-
-static nfs_server_running_t nfs_server_upordown;
-static kmutex_t nfs_server_upordown_lock;
-static	kcondvar_t nfs_server_upordown_cv;
-
-/*
  * DSS: distributed stable storage
  * lists of all DSS paths: current, and before last warmstart
  */
 nvlist_t *rfs4_dss_paths, *rfs4_dss_oldpaths;
 
-int rfs4_dispatch(struct rpcdisp *, struct svc_req *, SVCXPRT *, char *);
+int rfs4_dispatch(struct rpcdisp *, struct svc_req *, SVCXPRT *, char *,
+    size_t *);
 bool_t rfs4_minorvers_mismatch(struct svc_req *, SVCXPRT *, void *);
 
 /*
- * RDMA wait variables.
- */
-static kcondvar_t rdma_wait_cv;
-static kmutex_t rdma_wait_mutex;
-
-/*
  * Will be called at the point the server pool is being unregistered
  * from the pool list. From that point onwards, the pool is waiting
  * to be drained and as such the server state is stale and pertains
@@ -291,11 +270,15 @@
 void
 nfs_srv_offline(void)
 {
-	mutex_enter(&nfs_server_upordown_lock);
-	if (nfs_server_upordown == NFS_SERVER_RUNNING) {
-		nfs_server_upordown = NFS_SERVER_OFFLINE;
+	nfs_globals_t *ng;
+
+	ng = zone_getspecific(nfssrv_zone_key, curzone);
+
+	mutex_enter(&ng->nfs_server_upordown_lock);
+	if (ng->nfs_server_upordown == NFS_SERVER_RUNNING) {
+		ng->nfs_server_upordown = NFS_SERVER_OFFLINE;
 	}
-	mutex_exit(&nfs_server_upordown_lock);
+	mutex_exit(&ng->nfs_server_upordown_lock);
 }
 
 /*
@@ -324,13 +307,16 @@
 }
 
 static void
-nfs_srv_shutdown_all(int quiesce) {
-	mutex_enter(&nfs_server_upordown_lock);
+nfs_srv_shutdown_all(int quiesce)
+{
+	nfs_globals_t *ng = zone_getspecific(nfssrv_zone_key, curzone);
+
+	mutex_enter(&ng->nfs_server_upordown_lock);
 	if (quiesce) {
-		if (nfs_server_upordown == NFS_SERVER_RUNNING ||
-			nfs_server_upordown == NFS_SERVER_OFFLINE) {
-			nfs_server_upordown = NFS_SERVER_QUIESCED;
-			cv_signal(&nfs_server_upordown_cv);
+		if (ng->nfs_server_upordown == NFS_SERVER_RUNNING ||
+		    ng->nfs_server_upordown == NFS_SERVER_OFFLINE) {
+			ng->nfs_server_upordown = NFS_SERVER_QUIESCED;
+			cv_signal(&ng->nfs_server_upordown_cv);
 
 			/* reset DSS state, for subsequent warm restart */
 			rfs4_dss_numnewpaths = 0;
@@ -340,22 +326,22 @@
 			    "NFSv4 state has been preserved");
 		}
 	} else {
-		if (nfs_server_upordown == NFS_SERVER_OFFLINE) {
-			nfs_server_upordown = NFS_SERVER_STOPPING;
-			mutex_exit(&nfs_server_upordown_lock);
-			rfs4_state_fini();
-			rfs4_fini_drc(nfs4_drc);
-			mutex_enter(&nfs_server_upordown_lock);
-			nfs_server_upordown = NFS_SERVER_STOPPED;
-			cv_signal(&nfs_server_upordown_cv);
+		if (ng->nfs_server_upordown == NFS_SERVER_OFFLINE) {
+			ng->nfs_server_upordown = NFS_SERVER_STOPPING;
+			mutex_exit(&ng->nfs_server_upordown_lock);
+			rfs4_state_zone_fini();
+			rfs4_fini_drc();
+			mutex_enter(&ng->nfs_server_upordown_lock);
+			ng->nfs_server_upordown = NFS_SERVER_STOPPED;
+			cv_signal(&ng->nfs_server_upordown_cv);
 		}
 	}
-	mutex_exit(&nfs_server_upordown_lock);
+	mutex_exit(&ng->nfs_server_upordown_lock);
 }
 
 static int
 nfs_srv_set_sc_versions(struct file *fp, SVC_CALLOUT_TABLE **sctpp,
-			rpcvers_t versmin, rpcvers_t versmax)
+    rpcvers_t versmin, rpcvers_t versmax)
 {
 	struct strioctl strioc;
 	struct T_info_ack tinfo;
@@ -418,6 +404,7 @@
 int
 nfs_svc(struct nfs_svc_args *arg, model_t model)
 {
+	nfs_globals_t *ng;
 	file_t *fp;
 	SVCMASTERXPRT *xprt;
 	int error;
@@ -432,6 +419,7 @@
 	model = model;		/* STRUCT macros don't always refer to it */
 #endif
 
+	ng = zone_getspecific(nfssrv_zone_key, curzone);
 	STRUCT_SET_HANDLE(uap, model, arg);
 
 	/* Check privileges in nfssys() */
@@ -465,19 +453,19 @@
 		return (error);
 	}
 
-	nfs_versmin = STRUCT_FGET(uap, versmin);
-	nfs_versmax = STRUCT_FGET(uap, versmax);
+	ng->nfs_versmin = STRUCT_FGET(uap, versmin);
+	ng->nfs_versmax = STRUCT_FGET(uap, versmax);
 
 	/* Double check the vers min/max ranges */
-	if ((nfs_versmin > nfs_versmax) ||
-	    (nfs_versmin < NFS_VERSMIN) ||
-	    (nfs_versmax > NFS_VERSMAX)) {
-		nfs_versmin = NFS_VERSMIN_DEFAULT;
-		nfs_versmax = NFS_VERSMAX_DEFAULT;
+	if ((ng->nfs_versmin > ng->nfs_versmax) ||
+	    (ng->nfs_versmin < NFS_VERSMIN) ||
+	    (ng->nfs_versmax > NFS_VERSMAX)) {
+		ng->nfs_versmin = NFS_VERSMIN_DEFAULT;
+		ng->nfs_versmax = NFS_VERSMAX_DEFAULT;
 	}
 
-	if (error =
-	    nfs_srv_set_sc_versions(fp, &sctp, nfs_versmin, nfs_versmax)) {
+	if (error = nfs_srv_set_sc_versions(fp, &sctp, ng->nfs_versmin,
+	    ng->nfs_versmax)) {
 		releasef(STRUCT_FGET(uap, fd));
 		kmem_free(addrmask.buf, addrmask.maxlen);
 		return (error);
@@ -484,8 +472,8 @@
 	}
 
 	/* Initialize nfsv4 server */
-	if (nfs_versmax == (rpcvers_t)NFS_V4)
-		rfs4_server_start(STRUCT_FGET(uap, delegation));
+	if (ng->nfs_versmax == (rpcvers_t)NFS_V4)
+		rfs4_server_start(ng, STRUCT_FGET(uap, delegation));
 
 	/* Create a transport handle. */
 	error = svc_tli_kcreate(fp, readsize, buf, &addrmask, &xprt,
@@ -504,59 +492,36 @@
 }
 
 static void
-rfs4_server_start(int nfs4_srv_delegation)
+rfs4_server_start(nfs_globals_t *ng, int nfs4_srv_delegation)
 {
 	/*
 	 * Determine if the server has previously been "started" and
 	 * if not, do the per instance initialization
 	 */
-	mutex_enter(&nfs_server_upordown_lock);
+	mutex_enter(&ng->nfs_server_upordown_lock);
 
-	if (nfs_server_upordown != NFS_SERVER_RUNNING) {
+	if (ng->nfs_server_upordown != NFS_SERVER_RUNNING) {
 		/* Do we need to stop and wait on the previous server? */
-		while (nfs_server_upordown == NFS_SERVER_STOPPING ||
-		    nfs_server_upordown == NFS_SERVER_OFFLINE)
-			cv_wait(&nfs_server_upordown_cv,
-			    &nfs_server_upordown_lock);
+		while (ng->nfs_server_upordown == NFS_SERVER_STOPPING ||
+		    ng->nfs_server_upordown == NFS_SERVER_OFFLINE)
+			cv_wait(&ng->nfs_server_upordown_cv,
+			    &ng->nfs_server_upordown_lock);
 
-		if (nfs_server_upordown != NFS_SERVER_RUNNING) {
+		if (ng->nfs_server_upordown != NFS_SERVER_RUNNING) {
 			(void) svc_pool_control(NFS_SVCPOOL_ID,
 			    SVCPSET_UNREGISTER_PROC, (void *)&nfs_srv_offline);
 			(void) svc_pool_control(NFS_SVCPOOL_ID,
 			    SVCPSET_SHUTDOWN_PROC, (void *)&nfs_srv_stop_all);
 
-			/* is this an nfsd warm start? */
-			if (nfs_server_upordown == NFS_SERVER_QUIESCED) {
-				cmn_err(CE_NOTE, "nfs_server: "
-				    "server was previously quiesced; "
-				    "existing NFSv4 state will be re-used");
+			rfs4_do_server_start(ng->nfs_server_upordown,
+			    nfs4_srv_delegation,
+			    cluster_bootflags & CLUSTER_BOOTED);
 
-				/*
-				 * HA-NFSv4: this is also the signal
-				 * that a Resource Group failover has
-				 * occurred.
-				 */
-				if (cluster_bootflags & CLUSTER_BOOTED)
-					hanfsv4_failover();
-			} else {
-				/* cold start */
-				rfs4_state_init();
-				nfs4_drc = rfs4_init_drc(nfs4_drc_max,
-				    nfs4_drc_hash);
-			}
-
-			/*
-			 * Check to see if delegation is to be
-			 * enabled at the server
-			 */
-			if (nfs4_srv_delegation != FALSE)
-				rfs4_set_deleg_policy(SRV_NORMAL_DELEGATE);
-
-			nfs_server_upordown = NFS_SERVER_RUNNING;
+			ng->nfs_server_upordown = NFS_SERVER_RUNNING;
 		}
-		cv_signal(&nfs_server_upordown_cv);
+		cv_signal(&ng->nfs_server_upordown_cv);
 	}
-	mutex_exit(&nfs_server_upordown_lock);
+	mutex_exit(&ng->nfs_server_upordown_lock);
 }
 
 /*
@@ -566,6 +531,7 @@
 int
 rdma_start(struct rdma_svc_args *rsa)
 {
+	nfs_globals_t *ng;
 	int error;
 	rdma_xprt_group_t started_rdma_xprts;
 	rdma_stat stat;
@@ -578,9 +544,11 @@
 		rsa->nfs_versmin = NFS_VERSMIN_DEFAULT;
 		rsa->nfs_versmax = NFS_VERSMAX_DEFAULT;
 	}
-	nfs_versmin = rsa->nfs_versmin;
-	nfs_versmax = rsa->nfs_versmax;
 
+	ng = zone_getspecific(nfssrv_zone_key, curzone);
+	ng->nfs_versmin = rsa->nfs_versmin;
+	ng->nfs_versmax = rsa->nfs_versmax;
+
 	/* Set the versions in the callout table */
 	__nfs_sc_rdma[0].sc_versmin = rsa->nfs_versmin;
 	__nfs_sc_rdma[0].sc_versmax = rsa->nfs_versmax;
@@ -593,7 +561,7 @@
 
 	/* Initialize nfsv4 server */
 	if (rsa->nfs_versmax == (rpcvers_t)NFS_V4)
-		rfs4_server_start(rsa->delegation);
+		rfs4_server_start(ng, rsa->delegation);
 
 	started_rdma_xprts.rtg_count = 0;
 	started_rdma_xprts.rtg_listhead = NULL;
@@ -610,7 +578,7 @@
 		/*
 		 * wait till either interrupted by a signal on
 		 * nfs service stop/restart or signalled by a
-		 * rdma plugin attach/detatch.
+		 * rdma attach/detatch.
 		 */
 
 		stat = rdma_kwait();
@@ -661,10 +629,10 @@
 rpc_null_v3(caddr_t *argp, caddr_t *resp, struct exportinfo *exi,
     struct svc_req *req, cred_t *cr, bool_t ro)
 {
-	DTRACE_NFSV3_3(op__null__start, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, NULL);
-	DTRACE_NFSV3_3(op__null__done, struct svc_req *, req,
-	    cred_t *, cr, vnode_t *, NULL);
+	DTRACE_NFSV3_4(op__null__start, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, NULL, struct exportinfo *, exi);
+	DTRACE_NFSV3_4(op__null__done, struct svc_req *, req,
+	    cred_t *, cr, vnode_t *, NULL, struct exportinfo *, exi);
 }
 
 /* ARGSUSED */
@@ -1342,13 +1310,13 @@
 static struct rpc_disptable rfs_disptable[] = {
 	{sizeof (rfsdisptab_v2) / sizeof (rfsdisptab_v2[0]),
 	    rfscallnames_v2,
-	    &rfsproccnt_v2_ptr, rfsdisptab_v2},
+	    &rfsproccnt_v2_ptr, &rfsprocio_v2_ptr, rfsdisptab_v2},
 	{sizeof (rfsdisptab_v3) / sizeof (rfsdisptab_v3[0]),
 	    rfscallnames_v3,
-	    &rfsproccnt_v3_ptr, rfsdisptab_v3},
+	    &rfsproccnt_v3_ptr, &rfsprocio_v3_ptr, rfsdisptab_v3},
 	{sizeof (rfsdisptab_v4) / sizeof (rfsdisptab_v4[0]),
 	    rfscallnames_v4,
-	    &rfsproccnt_v4_ptr, rfsdisptab_v4},
+	    &rfsproccnt_v4_ptr, &rfsprocio_v4_ptr, rfsdisptab_v4},
 };
 
 /*
@@ -1360,7 +1328,7 @@
  * is it transport-dependent, it's laughably easy to spoof.  If you're
  * really interested in security, you must start with secure RPC instead.
  */
-static int nfs_portmon = 0;
+volatile int nfs_portmon = 0;
 
 #ifdef DEBUG
 static int cred_hits = 0;
@@ -1367,7 +1335,6 @@
 static int cred_misses = 0;
 #endif
 
-
 #ifdef DEBUG
 /*
  * Debug code to allow disabling of rfs_dispatch() use of
@@ -1474,8 +1441,7 @@
 
 static void
 common_dispatch(struct svc_req *req, SVCXPRT *xprt, rpcvers_t min_vers,
-		rpcvers_t max_vers, char *pgmname,
-		struct rpc_disptable *disptable)
+    rpcvers_t max_vers, char *pgmname, struct rpc_disptable *disptable)
 {
 	int which;
 	rpcvers_t vers;
@@ -1508,6 +1474,12 @@
 	char **procnames;
 	char cbuf[INET6_ADDRSTRLEN];	/* to hold both IPv4 and IPv6 addr */
 	bool_t ro = FALSE;
+	kstat_t *ksp = NULL;
+	kstat_t *exi_ksp = NULL;
+	size_t pos;			/* request size */
+	size_t rlen;			/* reply size */
+	bool_t rsent = FALSE;		/* reply was sent successfully */
+	nfs_export_t *ne = nfs_get_export();
 
 	vers = req->rq_vers;
 
@@ -1528,6 +1500,14 @@
 
 	(*(disptable[(int)vers].dis_proccntp))[which].value.ui64++;
 
+	ksp = (*(disptable[(int)vers].dis_prociop))[which];
+	if (ksp != NULL) {
+		mutex_enter(ksp->ks_lock);
+		kstat_runq_enter(KSTAT_IO_PTR(ksp));
+		mutex_exit(ksp->ks_lock);
+	}
+	pos = XDR_GETPOS(&xprt->xp_xdrin);
+
 	disp = &disptable[(int)vers].dis_table[which];
 	procnames = disptable[(int)vers].dis_procnames;
 
@@ -1571,7 +1551,9 @@
 	 * If Version 4 use that specific dispatch function.
 	 */
 	if (req->rq_vers == 4) {
-		error += rfs4_dispatch(disp, req, xprt, args);
+		error += rfs4_dispatch(disp, req, xprt, args, &rlen);
+		if (error == 0)
+			rsent = TRUE;
 		goto done;
 	}
 
@@ -1632,13 +1614,15 @@
 		cr = xprt->xp_cred;
 		ASSERT(cr != NULL);
 #ifdef DEBUG
-		if (crgetref(cr) != 1) {
-			crfree(cr);
-			cr = crget();
-			xprt->xp_cred = cr;
-			cred_misses++;
-		} else
-			cred_hits++;
+		{
+			if (crgetref(cr) != 1) {
+				crfree(cr);
+				cr = crget();
+				xprt->xp_cred = cr;
+				cred_misses++;
+			} else
+				cred_hits++;
+		}
 #else
 		if (crgetref(cr) != 1) {
 			crfree(cr);
@@ -1650,8 +1634,34 @@
 		exi = checkexport(fsid, xfid);
 
 		if (exi != NULL) {
-			publicfh_ok = PUBLICFH_CHECK(disp, exi, fsid, xfid);
+			rw_enter(&ne->exported_lock, RW_READER);
+			exi_ksp = NULL;
 
+			if (exi->exi_kstats != NULL) {
+				switch (req->rq_vers) {
+				case NFS_VERSION:
+					exi_ksp = exp_kstats_v2(exi->exi_kstats,
+					    which);
+					break;
+				case NFS_V3:
+					exi_ksp = exp_kstats_v3(exi->exi_kstats,
+					    which);
+					break;
+				default:
+					ASSERT(0);
+					break;
+				}
+			}
+
+			if (exi_ksp != NULL) {
+				mutex_enter(exi_ksp->ks_lock);
+				kstat_runq_enter(KSTAT_IO_PTR(exi_ksp));
+				mutex_exit(exi_ksp->ks_lock);
+			} else {
+				rw_exit(&ne->exported_lock);
+			}
+
+			publicfh_ok = PUBLICFH_CHECK(ne, disp, exi, fsid, xfid);
 			/*
 			 * Don't allow non-V4 clients access
 			 * to pseudo exports
@@ -1763,7 +1773,7 @@
 	 * file system.
 	 */
 	if (nfslog_buffer_list != NULL) {
-		nfslog_exi = nfslog_get_exi(exi, req, res, &nfslog_rec_id);
+		nfslog_exi = nfslog_get_exi(ne, exi, req, res, &nfslog_rec_id);
 		/*
 		 * Is logging enabled?
 		 */
@@ -1800,6 +1810,9 @@
 			cmn_err(CE_NOTE, "%s: bad sendreply", pgmname);
 			svcerr_systemerr(xprt);
 			error++;
+		} else {
+			rlen = xdr_sizeof(disp->dis_fastxdrres, res);
+			rsent = TRUE;
 		}
 	} else {
 		if (!svc_sendreply(xprt, disp->dis_xdrres, res)) {
@@ -1806,6 +1819,9 @@
 			cmn_err(CE_NOTE, "%s: bad sendreply", pgmname);
 			svcerr_systemerr(xprt);
 			error++;
+		} else {
+			rlen = xdr_sizeof(disp->dis_xdrres, res);
+			rsent = TRUE;
 		}
 	}
 
@@ -1815,7 +1831,7 @@
 	if (logging_enabled) {
 		nfslog_write_record(nfslog_exi, req, args, (char *)&res_buf,
 		    cr, &nb, nfslog_rec_id, NFSLOG_ONE_BUFFER);
-		exi_rele(nfslog_exi);
+		exi_rele(&nfslog_exi);
 		kmem_free((&nb)->buf, (&nb)->len);
 	}
 
@@ -1828,6 +1844,10 @@
 	}
 
 done:
+	if (ksp != NULL || exi_ksp != NULL) {
+		pos = XDR_GETPOS(&xprt->xp_xdrin) - pos;
+	}
+
 	/*
 	 * Free arguments struct
 	 */
@@ -1843,9 +1863,35 @@
 		}
 	}
 
+	if (exi_ksp != NULL) {
+		mutex_enter(exi_ksp->ks_lock);
+		KSTAT_IO_PTR(exi_ksp)->nwritten += pos;
+		KSTAT_IO_PTR(exi_ksp)->writes++;
+		if (rsent) {
+			KSTAT_IO_PTR(exi_ksp)->nread += rlen;
+			KSTAT_IO_PTR(exi_ksp)->reads++;
+		}
+		kstat_runq_exit(KSTAT_IO_PTR(exi_ksp));
+		mutex_exit(exi_ksp->ks_lock);
+
+		rw_exit(&ne->exported_lock);
+	}
+
 	if (exi != NULL)
-		exi_rele(exi);
+		exi_rele(&exi);
 
+	if (ksp != NULL) {
+		mutex_enter(ksp->ks_lock);
+		KSTAT_IO_PTR(ksp)->nwritten += pos;
+		KSTAT_IO_PTR(ksp)->writes++;
+		if (rsent) {
+			KSTAT_IO_PTR(ksp)->nread += rlen;
+			KSTAT_IO_PTR(ksp)->reads++;
+		}
+		kstat_runq_exit(KSTAT_IO_PTR(ksp));
+		mutex_exit(ksp->ks_lock);
+	}
+
 	global_svstat_ptr[req->rq_vers][NFS_BADCALLS].value.ui64 += error;
 
 	global_svstat_ptr[req->rq_vers][NFS_CALLS].value.ui64++;
@@ -1971,10 +2017,10 @@
 static struct rpc_disptable acl_disptable[] = {
 	{sizeof (acldisptab_v2) / sizeof (acldisptab_v2[0]),
 		aclcallnames_v2,
-		&aclproccnt_v2_ptr, acldisptab_v2},
+		&aclproccnt_v2_ptr, &aclprocio_v2_ptr, acldisptab_v2},
 	{sizeof (acldisptab_v3) / sizeof (acldisptab_v3[0]),
 		aclcallnames_v3,
-		&aclproccnt_v3_ptr, acldisptab_v3},
+		&aclproccnt_v3_ptr, &aclprocio_v3_ptr, acldisptab_v3},
 };
 
 static void
@@ -2568,31 +2614,18 @@
  *	- Initialize all locks
  *	- initialize the version 3 write verifier
  */
-int
+void
 nfs_srvinit(void)
 {
-	int error;
+	/* NFS server zone-specific global variables */
+	zone_key_create(&nfssrv_zone_key, nfs_srv_zone_init,
+	    NULL, nfs_srv_zone_fini);
 
-	error = nfs_exportinit();
-	if (error != 0)
-		return (error);
-	error = rfs4_srvrinit();
-	if (error != 0) {
-		nfs_exportfini();
-		return (error);
-	}
+	nfs_exportinit();
 	rfs_srvrinit();
 	rfs3_srvrinit();
+	rfs4_srvrinit();
 	nfsauth_init();
-
-	/* Init the stuff to control start/stop */
-	nfs_server_upordown = NFS_SERVER_STOPPED;
-	mutex_init(&nfs_server_upordown_lock, NULL, MUTEX_DEFAULT, NULL);
-	cv_init(&nfs_server_upordown_cv, NULL, CV_DEFAULT, NULL);
-	mutex_init(&rdma_wait_mutex, NULL, MUTEX_DEFAULT, NULL);
-	cv_init(&rdma_wait_cv, NULL, CV_DEFAULT, NULL);
-
-	return (0);
 }
 
 /*
@@ -2604,20 +2637,53 @@
 nfs_srvfini(void)
 {
 	nfsauth_fini();
+	rfs4_srvrfini();
 	rfs3_srvrfini();
 	rfs_srvrfini();
 	nfs_exportfini();
 
-	mutex_destroy(&nfs_server_upordown_lock);
-	cv_destroy(&nfs_server_upordown_cv);
-	mutex_destroy(&rdma_wait_mutex);
-	cv_destroy(&rdma_wait_cv);
+	(void) zone_key_delete(nfssrv_zone_key);
 }
 
+/* ARGSUSED */
+static void *
+nfs_srv_zone_init(zoneid_t zoneid)
+{
+	nfs_globals_t *ng;
+
+	ng = kmem_zalloc(sizeof (*ng), KM_SLEEP);
+
+	ng->nfs_versmin = NFS_VERSMIN_DEFAULT;
+	ng->nfs_versmax = NFS_VERSMAX_DEFAULT;
+
+	/* Init the stuff to control start/stop */
+	ng->nfs_server_upordown = NFS_SERVER_STOPPED;
+	mutex_init(&ng->nfs_server_upordown_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&ng->nfs_server_upordown_cv, NULL, CV_DEFAULT, NULL);
+	mutex_init(&ng->rdma_wait_mutex, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&ng->rdma_wait_cv, NULL, CV_DEFAULT, NULL);
+
+	return (ng);
+}
+
+/* ARGSUSED */
+static void
+nfs_srv_zone_fini(zoneid_t zoneid, void *data)
+{
+	nfs_globals_t *ng;
+
+	ng = (nfs_globals_t *)data;
+	mutex_destroy(&ng->nfs_server_upordown_lock);
+	cv_destroy(&ng->nfs_server_upordown_cv);
+	mutex_destroy(&ng->rdma_wait_mutex);
+	cv_destroy(&ng->rdma_wait_cv);
+
+	kmem_free(ng, sizeof (*ng));
+}
+
 /*
  * Set up an iovec array of up to cnt pointers.
  */
-
 void
 mblk_to_iov(mblk_t *m, int cnt, struct iovec *iovp)
 {
@@ -2854,7 +2920,7 @@
 
 			/* Release the reference on the old exi value */
 			ASSERT(*exi != NULL);
-			exi_rele(*exi);
+			exi_rele(exi);
 
 			if (error = nfs_check_vpexi(mc_dvp, *vpp, kcred, exi)) {
 				VN_RELE(*vpp);
@@ -2893,7 +2959,7 @@
 		while (*path == '/')
 			path++;
 
-		startdvp = rootdir;
+		startdvp = ZONE_ROOTVP();
 	}
 
 	error = pn_get_buf(path, UIO_SYSSPACE, &pn, namebuf, sizeof (namebuf));
@@ -2916,7 +2982,7 @@
 		}
 		VN_HOLD(startdvp);
 		error = lookuppnvp(&pn, NULL, NO_FOLLOW, dirvpp, compvpp,
-		    rootdir, startdvp, cr);
+		    ZONE_ROOTVP(), startdvp, cr);
 	}
 	if (error == ENAMETOOLONG) {
 		/*
@@ -2933,7 +2999,7 @@
 		}
 		VN_HOLD(startdvp);
 		error = lookuppnvp(&pn, NULL, NO_FOLLOW, dirvpp, compvpp,
-		    rootdir, startdvp, cr);
+		    ZONE_ROOTVP(), startdvp, cr);
 		pn_free(&pn);
 	}
 
@@ -3037,168 +3103,6 @@
 	return (error);
 }
 
-/*
- * Do the main work of handling HA-NFSv4 Resource Group failover on
- * Sun Cluster.
- * We need to detect whether any RG admin paths have been added or removed,
- * and adjust resources accordingly.
- * Currently we're using a very inefficient algorithm, ~ 2 * O(n**2). In
- * order to scale, the list and array of paths need to be held in more
- * suitable data structures.
- */
-static void
-hanfsv4_failover(void)
-{
-	int i, start_grace, numadded_paths = 0;
-	char **added_paths = NULL;
-	rfs4_dss_path_t *dss_path;
-
-	/*
-	 * Note: currently, rfs4_dss_pathlist cannot be NULL, since
-	 * it will always include an entry for NFS4_DSS_VAR_DIR. If we
-	 * make the latter dynamically specified too, the following will
-	 * need to be adjusted.
-	 */
-
-	/*
-	 * First, look for removed paths: RGs that have been failed-over
-	 * away from this node.
-	 * Walk the "currently-serving" rfs4_dss_pathlist and, for each
-	 * path, check if it is on the "passed-in" rfs4_dss_newpaths array
-	 * from nfsd. If not, that RG path has been removed.
-	 *
-	 * Note that nfsd has sorted rfs4_dss_newpaths for us, and removed
-	 * any duplicates.
-	 */
-	dss_path = rfs4_dss_pathlist;
-	do {
-		int found = 0;
-		char *path = dss_path->path;
-
-		/* used only for non-HA so may not be removed */
-		if (strcmp(path, NFS4_DSS_VAR_DIR) == 0) {
-			dss_path = dss_path->next;
-			continue;
-		}
-
-		for (i = 0; i < rfs4_dss_numnewpaths; i++) {
-			int cmpret;
-			char *newpath = rfs4_dss_newpaths[i];
-
-			/*
-			 * Since nfsd has sorted rfs4_dss_newpaths for us,
-			 * once the return from strcmp is negative we know
-			 * we've passed the point where "path" should be,
-			 * and can stop searching: "path" has been removed.
-			 */
-			cmpret = strcmp(path, newpath);
-			if (cmpret < 0)
-				break;
-			if (cmpret == 0) {
-				found = 1;
-				break;
-			}
-		}
-
-		if (found == 0) {
-			unsigned index = dss_path->index;
-			rfs4_servinst_t *sip = dss_path->sip;
-			rfs4_dss_path_t *path_next = dss_path->next;
-
-			/*
-			 * This path has been removed.
-			 * We must clear out the servinst reference to
-			 * it, since it's now owned by another
-			 * node: we should not attempt to touch it.
-			 */
-			ASSERT(dss_path == sip->dss_paths[index]);
-			sip->dss_paths[index] = NULL;
-
-			/* remove from "currently-serving" list, and destroy */
-			remque(dss_path);
-			/* allow for NUL */
-			kmem_free(dss_path->path, strlen(dss_path->path) + 1);
-			kmem_free(dss_path, sizeof (rfs4_dss_path_t));
-
-			dss_path = path_next;
-		} else {
-			/* path was found; not removed */
-			dss_path = dss_path->next;
-		}
-	} while (dss_path != rfs4_dss_pathlist);
-
-	/*
-	 * Now, look for added paths: RGs that have been failed-over
-	 * to this node.
-	 * Walk the "passed-in" rfs4_dss_newpaths array from nfsd and,
-	 * for each path, check if it is on the "currently-serving"
-	 * rfs4_dss_pathlist. If not, that RG path has been added.
-	 *
-	 * Note: we don't do duplicate detection here; nfsd does that for us.
-	 *
-	 * Note: numadded_paths <= rfs4_dss_numnewpaths, which gives us
-	 * an upper bound for the size needed for added_paths[numadded_paths].
-	 */
-
-	/* probably more space than we need, but guaranteed to be enough */
-	if (rfs4_dss_numnewpaths > 0) {
-		size_t sz = rfs4_dss_numnewpaths * sizeof (char *);
-		added_paths = kmem_zalloc(sz, KM_SLEEP);
-	}
-
-	/* walk the "passed-in" rfs4_dss_newpaths array from nfsd */
-	for (i = 0; i < rfs4_dss_numnewpaths; i++) {
-		int found = 0;
-		char *newpath = rfs4_dss_newpaths[i];
-
-		dss_path = rfs4_dss_pathlist;
-		do {
-			char *path = dss_path->path;
-
-			/* used only for non-HA */
-			if (strcmp(path, NFS4_DSS_VAR_DIR) == 0) {
-				dss_path = dss_path->next;
-				continue;
-			}
-
-			if (strncmp(path, newpath, strlen(path)) == 0) {
-				found = 1;
-				break;
-			}
-
-			dss_path = dss_path->next;
-		} while (dss_path != rfs4_dss_pathlist);
-
-		if (found == 0) {
-			added_paths[numadded_paths] = newpath;
-			numadded_paths++;
-		}
-	}
-
-	/* did we find any added paths? */
-	if (numadded_paths > 0) {
-		/* create a new server instance, and start its grace period */
-		start_grace = 1;
-		rfs4_servinst_create(start_grace, numadded_paths, added_paths);
-
-		/* read in the stable storage state from these paths */
-		rfs4_dss_readstate(numadded_paths, added_paths);
-
-		/*
-		 * Multiple failovers during a grace period will cause
-		 * clients of the same resource group to be partitioned
-		 * into different server instances, with different
-		 * grace periods.  Since clients of the same resource
-		 * group must be subject to the same grace period,
-		 * we need to reset all currently active grace periods.
-		 */
-		rfs4_grace_reset_all();
-	}
-
-	if (rfs4_dss_numnewpaths > 0)
-		kmem_free(added_paths, rfs4_dss_numnewpaths * sizeof (char *));
-}
-
 /*
  * Used by NFSv3 and NFSv4 server to query label of
  * a pathname component during lookup/access ops.
