--- old/usr/src/uts/common/fs/nfs/nfs4_srv.c	Mon Aug  5 17:26:00 2019
+++ new/usr/src/uts/common/fs/nfs/nfs4_srv.c	Mon Aug  5 17:26:00 2019
@@ -20,9 +20,7 @@
  */
 
 /*
- * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
  */
 
 /*
@@ -30,6 +28,11 @@
  *	All Rights Reserved
  */
 
+/*
+ * Copyright 2019 Nexenta Systems, Inc.
+ * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
+ */
+
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/systm.h>
@@ -57,6 +60,7 @@
 #include <sys/sdt.h>
 #include <sys/ddi.h>
 #include <sys/zone.h>
+#include <sys/kstat.h>
 
 #include <fs/fs_reparse.h>
 
@@ -66,10 +70,12 @@
 #include <rpc/svc.h>
 
 #include <nfs/nfs.h>
+#include <nfs/nfssys.h>
 #include <nfs/export.h>
 #include <nfs/nfs_cmd.h>
 #include <nfs/lm.h>
 #include <nfs/nfs4.h>
+#include <nfs/nfs4_drc.h>
 
 #include <sys/strsubr.h>
 #include <sys/strsun.h>
@@ -147,109 +153,107 @@
 #define	DIRENT64_TO_DIRCOUNT(dp) \
 	(3 * BYTES_PER_XDR_UNIT + DIRENT64_NAMELEN((dp)->d_reclen))
 
-time_t rfs4_start_time;			/* Initialized in rfs4_srvrinit */
+zone_key_t	rfs4_zone_key;
 
-static sysid_t lockt_sysid;		/* dummy sysid for all LOCKT calls */
+static sysid_t		lockt_sysid;	/* dummy sysid for all LOCKT calls */
 
 u_longlong_t	nfs4_srv_caller_id;
 uint_t		nfs4_srv_vkey = 0;
 
-verifier4	Write4verf;
-verifier4	Readdir4verf;
-
 void	rfs4_init_compound_state(struct compound_state *);
 
 static void	nullfree(caddr_t);
 static void	rfs4_op_inval(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_access(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_close(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_commit(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_create(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_create_free(nfs_resop4 *resop);
 static void	rfs4_op_delegreturn(nfs_argop4 *, nfs_resop4 *,
-			struct svc_req *, struct compound_state *);
+		    struct svc_req *, struct compound_state *);
 static void	rfs4_op_delegpurge(nfs_argop4 *, nfs_resop4 *,
-			struct svc_req *, struct compound_state *);
+		    struct svc_req *, struct compound_state *);
 static void	rfs4_op_getattr(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_getattr_free(nfs_resop4 *);
 static void	rfs4_op_getfh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_getfh_free(nfs_resop4 *);
 static void	rfs4_op_illegal(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_link(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_lock(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	lock_denied_free(nfs_resop4 *);
 static void	rfs4_op_locku(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_lockt(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_lookup(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_lookupp(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_openattr(nfs_argop4 *argop, nfs_resop4 *resop,
-				struct svc_req *req, struct compound_state *cs);
+		    struct svc_req *req, struct compound_state *cs);
 static void	rfs4_op_nverify(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_open(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_open_confirm(nfs_argop4 *, nfs_resop4 *,
-			struct svc_req *, struct compound_state *);
+		    struct svc_req *, struct compound_state *);
 static void	rfs4_op_open_downgrade(nfs_argop4 *, nfs_resop4 *,
-			struct svc_req *, struct compound_state *);
+		    struct svc_req *, struct compound_state *);
 static void	rfs4_op_putfh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_putpubfh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_putrootfh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_read(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_read_free(nfs_resop4 *);
 static void	rfs4_op_readdir_free(nfs_resop4 *resop);
 static void	rfs4_op_readlink(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_readlink_free(nfs_resop4 *);
 static void	rfs4_op_release_lockowner(nfs_argop4 *, nfs_resop4 *,
-			struct svc_req *, struct compound_state *);
+		    struct svc_req *, struct compound_state *);
 static void	rfs4_op_remove(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_rename(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_renew(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_restorefh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_savefh(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_setattr(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_verify(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_write(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_setclientid(nfs_argop4 *, nfs_resop4 *,
-			struct svc_req *, struct compound_state *);
+		    struct svc_req *, struct compound_state *);
 static void	rfs4_op_setclientid_confirm(nfs_argop4 *, nfs_resop4 *,
-			struct svc_req *req, struct compound_state *);
+		    struct svc_req *req, struct compound_state *);
 static void	rfs4_op_secinfo(nfs_argop4 *, nfs_resop4 *, struct svc_req *,
-			struct compound_state *);
+		    struct compound_state *);
 static void	rfs4_op_secinfo_free(nfs_resop4 *);
 
-static nfsstat4 check_open_access(uint32_t,
-				struct compound_state *, struct svc_req *);
-nfsstat4 rfs4_client_sysid(rfs4_client_t *, sysid_t *);
-void rfs4_ss_clid(rfs4_client_t *);
+static nfsstat4 check_open_access(uint32_t, struct compound_state *,
+		    struct svc_req *);
+nfsstat4	rfs4_client_sysid(rfs4_client_t *, sysid_t *);
+void		rfs4_ss_clid(nfs4_srv_t *, rfs4_client_t *);
 
+
 /*
  * translation table for attrs
  */
@@ -262,19 +266,17 @@
 
 static void	nfs4_ntov_table_init(struct nfs4_ntov_table *ntovp);
 static void	nfs4_ntov_table_free(struct nfs4_ntov_table *ntovp,
-				    struct nfs4_svgetit_arg *sargp);
+		    struct nfs4_svgetit_arg *sargp);
 
 static nfsstat4	do_rfs4_set_attrs(bitmap4 *resp, fattr4 *fattrp,
 		    struct compound_state *cs, struct nfs4_svgetit_arg *sargp,
 		    struct nfs4_ntov_table *ntovp, nfs4_attr_cmd_t cmd);
 
+static void	hanfsv4_failover(nfs4_srv_t *);
+
 fem_t		*deleg_rdops;
 fem_t		*deleg_wrops;
 
-rfs4_servinst_t *rfs4_cur_servinst = NULL;	/* current server instance */
-kmutex_t	rfs4_servinst_lock;	/* protects linked list */
-int		rfs4_seen_first_compound;	/* set first time we see one */
-
 /*
  * NFS4 op dispatch table
  */
@@ -283,8 +285,40 @@
 	void	(*dis_proc)();		/* proc to call */
 	void	(*dis_resfree)();	/* frees space allocated by proc */
 	int	dis_flags;		/* RPC_IDEMPOTENT, etc... */
+	int	op_type;		/* operation type, see below */
 };
 
+/*
+ * operation types; used primarily for the per-exportinfo kstat implementation
+ */
+#define	NFS4_OP_NOFH	0	/* The operation does not operate with any */
+				/* particular filehandle; we cannot associate */
+				/* it with any exportinfo. */
+
+#define	NFS4_OP_CFH	1	/* The operation works with the current */
+				/* filehandle; we associate the operation */
+				/* with the exportinfo related to the current */
+				/* filehandle (as set before the operation is */
+				/* executed). */
+
+#define	NFS4_OP_SFH	2	/* The operation works with the saved */
+				/* filehandle; we associate the operation */
+				/* with the exportinfo related to the saved */
+				/* filehandle (as set before the operation is */
+				/* executed). */
+
+#define	NFS4_OP_POSTCFH	3	/* The operation ignores the current */
+				/* filehandle, but sets the new current */
+				/* filehandle instead; we associate the */
+				/* operation with the exportinfo related to */
+				/* the current filehandle as set after the */
+				/* operation is successfuly executed.  Since */
+				/* we do not know the particular exportinfo */
+				/* (and thus the kstat) before the operation */
+				/* is done, there is no simple way how to */
+				/* update some I/O kstat statistics related */
+				/* to kstat_queue(9F). */
+
 static struct rfsv4disp rfsv4disptab[] = {
 	/*
 	 * NFS VERSION 4
@@ -291,124 +325,126 @@
 	 */
 
 	/* RFS_NULL = 0 */
-	{rfs4_op_illegal, nullfree, 0},
+	{rfs4_op_illegal, nullfree, 0, NFS4_OP_NOFH},
 
 	/* UNUSED = 1 */
-	{rfs4_op_illegal, nullfree, 0},
+	{rfs4_op_illegal, nullfree, 0, NFS4_OP_NOFH},
 
 	/* UNUSED = 2 */
-	{rfs4_op_illegal, nullfree, 0},
+	{rfs4_op_illegal, nullfree, 0, NFS4_OP_NOFH},
 
 	/* OP_ACCESS = 3 */
-	{rfs4_op_access, nullfree, RPC_IDEMPOTENT},
+	{rfs4_op_access, nullfree, RPC_IDEMPOTENT, NFS4_OP_CFH},
 
 	/* OP_CLOSE = 4 */
-	{rfs4_op_close, nullfree, 0},
+	{rfs4_op_close, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_COMMIT = 5 */
-	{rfs4_op_commit, nullfree, RPC_IDEMPOTENT},
+	{rfs4_op_commit, nullfree, RPC_IDEMPOTENT, NFS4_OP_CFH},
 
 	/* OP_CREATE = 6 */
-	{rfs4_op_create, nullfree, 0},
+	{rfs4_op_create, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_DELEGPURGE = 7 */
-	{rfs4_op_delegpurge, nullfree, 0},
+	{rfs4_op_delegpurge, nullfree, 0, NFS4_OP_NOFH},
 
 	/* OP_DELEGRETURN = 8 */
-	{rfs4_op_delegreturn, nullfree, 0},
+	{rfs4_op_delegreturn, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_GETATTR = 9 */
-	{rfs4_op_getattr, rfs4_op_getattr_free, RPC_IDEMPOTENT},
+	{rfs4_op_getattr, rfs4_op_getattr_free, RPC_IDEMPOTENT, NFS4_OP_CFH},
 
 	/* OP_GETFH = 10 */
-	{rfs4_op_getfh, rfs4_op_getfh_free, RPC_ALL},
+	{rfs4_op_getfh, rfs4_op_getfh_free, RPC_ALL, NFS4_OP_CFH},
 
 	/* OP_LINK = 11 */
-	{rfs4_op_link, nullfree, 0},
+	{rfs4_op_link, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_LOCK = 12 */
-	{rfs4_op_lock, lock_denied_free, 0},
+	{rfs4_op_lock, lock_denied_free, 0, NFS4_OP_CFH},
 
 	/* OP_LOCKT = 13 */
-	{rfs4_op_lockt, lock_denied_free, 0},
+	{rfs4_op_lockt, lock_denied_free, 0, NFS4_OP_CFH},
 
 	/* OP_LOCKU = 14 */
-	{rfs4_op_locku, nullfree, 0},
+	{rfs4_op_locku, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_LOOKUP = 15 */
-	{rfs4_op_lookup, nullfree, (RPC_IDEMPOTENT | RPC_PUBLICFH_OK)},
+	{rfs4_op_lookup, nullfree, (RPC_IDEMPOTENT | RPC_PUBLICFH_OK),
+	    NFS4_OP_CFH},
 
 	/* OP_LOOKUPP = 16 */
-	{rfs4_op_lookupp, nullfree, (RPC_IDEMPOTENT | RPC_PUBLICFH_OK)},
+	{rfs4_op_lookupp, nullfree, (RPC_IDEMPOTENT | RPC_PUBLICFH_OK),
+	    NFS4_OP_CFH},
 
 	/* OP_NVERIFY = 17 */
-	{rfs4_op_nverify, nullfree, RPC_IDEMPOTENT},
+	{rfs4_op_nverify, nullfree, RPC_IDEMPOTENT, NFS4_OP_CFH},
 
 	/* OP_OPEN = 18 */
-	{rfs4_op_open, rfs4_free_reply, 0},
+	{rfs4_op_open, rfs4_free_reply, 0, NFS4_OP_CFH},
 
 	/* OP_OPENATTR = 19 */
-	{rfs4_op_openattr, nullfree, 0},
+	{rfs4_op_openattr, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_OPEN_CONFIRM = 20 */
-	{rfs4_op_open_confirm, nullfree, 0},
+	{rfs4_op_open_confirm, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_OPEN_DOWNGRADE = 21 */
-	{rfs4_op_open_downgrade, nullfree, 0},
+	{rfs4_op_open_downgrade, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_OPEN_PUTFH = 22 */
-	{rfs4_op_putfh, nullfree, RPC_ALL},
+	{rfs4_op_putfh, nullfree, RPC_ALL, NFS4_OP_POSTCFH},
 
 	/* OP_PUTPUBFH = 23 */
-	{rfs4_op_putpubfh, nullfree, RPC_ALL},
+	{rfs4_op_putpubfh, nullfree, RPC_ALL, NFS4_OP_POSTCFH},
 
 	/* OP_PUTROOTFH = 24 */
-	{rfs4_op_putrootfh, nullfree, RPC_ALL},
+	{rfs4_op_putrootfh, nullfree, RPC_ALL, NFS4_OP_POSTCFH},
 
 	/* OP_READ = 25 */
-	{rfs4_op_read, rfs4_op_read_free, RPC_IDEMPOTENT},
+	{rfs4_op_read, rfs4_op_read_free, RPC_IDEMPOTENT, NFS4_OP_CFH},
 
 	/* OP_READDIR = 26 */
-	{rfs4_op_readdir, rfs4_op_readdir_free, RPC_IDEMPOTENT},
+	{rfs4_op_readdir, rfs4_op_readdir_free, RPC_IDEMPOTENT, NFS4_OP_CFH},
 
 	/* OP_READLINK = 27 */
-	{rfs4_op_readlink, rfs4_op_readlink_free, RPC_IDEMPOTENT},
+	{rfs4_op_readlink, rfs4_op_readlink_free, RPC_IDEMPOTENT, NFS4_OP_CFH},
 
 	/* OP_REMOVE = 28 */
-	{rfs4_op_remove, nullfree, 0},
+	{rfs4_op_remove, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_RENAME = 29 */
-	{rfs4_op_rename, nullfree, 0},
+	{rfs4_op_rename, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_RENEW = 30 */
-	{rfs4_op_renew, nullfree, 0},
+	{rfs4_op_renew, nullfree, 0, NFS4_OP_NOFH},
 
 	/* OP_RESTOREFH = 31 */
-	{rfs4_op_restorefh, nullfree, RPC_ALL},
+	{rfs4_op_restorefh, nullfree, RPC_ALL, NFS4_OP_SFH},
 
 	/* OP_SAVEFH = 32 */
-	{rfs4_op_savefh, nullfree, RPC_ALL},
+	{rfs4_op_savefh, nullfree, RPC_ALL, NFS4_OP_CFH},
 
 	/* OP_SECINFO = 33 */
-	{rfs4_op_secinfo, rfs4_op_secinfo_free, 0},
+	{rfs4_op_secinfo, rfs4_op_secinfo_free, 0, NFS4_OP_CFH},
 
 	/* OP_SETATTR = 34 */
-	{rfs4_op_setattr, nullfree, 0},
+	{rfs4_op_setattr, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_SETCLIENTID = 35 */
-	{rfs4_op_setclientid, nullfree, 0},
+	{rfs4_op_setclientid, nullfree, 0, NFS4_OP_NOFH},
 
 	/* OP_SETCLIENTID_CONFIRM = 36 */
-	{rfs4_op_setclientid_confirm, nullfree, 0},
+	{rfs4_op_setclientid_confirm, nullfree, 0, NFS4_OP_NOFH},
 
 	/* OP_VERIFY = 37 */
-	{rfs4_op_verify, nullfree, RPC_IDEMPOTENT},
+	{rfs4_op_verify, nullfree, RPC_IDEMPOTENT, NFS4_OP_CFH},
 
 	/* OP_WRITE = 38 */
-	{rfs4_op_write, nullfree, 0},
+	{rfs4_op_write, nullfree, 0, NFS4_OP_CFH},
 
 	/* OP_RELEASE_LOCKOWNER = 39 */
-	{rfs4_op_release_lockowner, nullfree, 0},
+	{rfs4_op_release_lockowner, nullfree, 0, NFS4_OP_NOFH},
 };
 
 static uint_t rfsv4disp_cnt = sizeof (rfsv4disptab) / sizeof (rfsv4disptab[0]);
@@ -466,7 +502,7 @@
 };
 #endif
 
-void	rfs4_ss_chkclid(rfs4_client_t *);
+void	rfs4_ss_chkclid(nfs4_srv_t *, rfs4_client_t *);
 
 extern size_t   strlcpy(char *dst, const char *src, size_t dstsize);
 
@@ -499,14 +535,15 @@
 	NULL,			NULL
 };
 
-int
-rfs4_srvrinit(void)
+/* ARGSUSED */
+static void *
+rfs4_zone_init(zoneid_t zoneid)
 {
+	nfs4_srv_t *nsrv4;
 	timespec32_t verf;
-	int error;
-	extern void rfs4_attr_init();
-	extern krwlock_t rfs4_deleg_policy_lock;
 
+	nsrv4 = kmem_zalloc(sizeof (*nsrv4), KM_SLEEP);
+
 	/*
 	 * The following algorithm attempts to find a unique verifier
 	 * to be used as the write verifier returned from the server
@@ -535,61 +572,107 @@
 		verf.tv_sec = (time_t)tverf.tv_sec;
 		verf.tv_nsec = tverf.tv_nsec;
 	}
+	nsrv4->write4verf = *(uint64_t *)&verf;
 
-	Write4verf = *(uint64_t *)&verf;
+	/* Used to manage create/destroy of server state */
+	nsrv4->nfs4_server_state = NULL;
+	nsrv4->nfs4_cur_servinst = NULL;
+	nsrv4->nfs4_deleg_policy = SRV_NEVER_DELEGATE;
+	mutex_init(&nsrv4->deleg_lock, NULL, MUTEX_DEFAULT, NULL);
+	mutex_init(&nsrv4->state_lock, NULL, MUTEX_DEFAULT, NULL);
+	mutex_init(&nsrv4->servinst_lock, NULL, MUTEX_DEFAULT, NULL);
+	rw_init(&nsrv4->deleg_policy_lock, NULL, RW_DEFAULT, NULL);
 
-	rfs4_attr_init();
-	mutex_init(&rfs4_deleg_lock, NULL, MUTEX_DEFAULT, NULL);
+	return (nsrv4);
+}
 
-	/* Used to manage create/destroy of server state */
-	mutex_init(&rfs4_state_lock, NULL, MUTEX_DEFAULT, NULL);
+/* ARGSUSED */
+static void
+rfs4_zone_fini(zoneid_t zoneid, void *data)
+{
+	nfs4_srv_t *nsrv4 = data;
 
-	/* Used to manage access to server instance linked list */
-	mutex_init(&rfs4_servinst_lock, NULL, MUTEX_DEFAULT, NULL);
+	mutex_destroy(&nsrv4->deleg_lock);
+	mutex_destroy(&nsrv4->state_lock);
+	mutex_destroy(&nsrv4->servinst_lock);
+	rw_destroy(&nsrv4->deleg_policy_lock);
 
-	/* Used to manage access to rfs4_deleg_policy */
-	rw_init(&rfs4_deleg_policy_lock, NULL, RW_DEFAULT, NULL);
+	kmem_free(nsrv4, sizeof (*nsrv4));
+}
 
-	error = fem_create("deleg_rdops", nfs4_rd_deleg_tmpl, &deleg_rdops);
-	if (error != 0) {
+void
+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 {
-		error = fem_create("deleg_wrops", nfs4_wr_deleg_tmpl,
-		    &deleg_wrops);
-		if (error != 0) {
-			rfs4_disable_delegation();
-			fem_free(deleg_rdops);
-		}
+	} else if (fem_create("deleg_wrops", nfs4_wr_deleg_tmpl,
+	    &deleg_wrops) != 0) {
+		rfs4_disable_delegation();
+		fem_free(deleg_rdops);
 	}
 
 	nfs4_srv_caller_id = fs_new_caller_id();
-
 	lockt_sysid = lm_alloc_sysidt();
-
 	vsd_create(&nfs4_srv_vkey, NULL);
-
-	return (0);
+	rfs4_state_g_init();
 }
 
 void
 rfs4_srvrfini(void)
 {
-	extern krwlock_t rfs4_deleg_policy_lock;
-
 	if (lockt_sysid != LM_NOSYSID) {
 		lm_free_sysidt(lockt_sysid);
 		lockt_sysid = LM_NOSYSID;
 	}
 
-	mutex_destroy(&rfs4_deleg_lock);
-	mutex_destroy(&rfs4_state_lock);
-	rw_destroy(&rfs4_deleg_policy_lock);
+	rfs4_state_g_fini();
 
 	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);
+
+	/* Is this a warm start? */
+	if (server_upordown == NFS_SERVER_QUIESCED) {
+		cmn_err(CE_NOTE, "nfs4_srv: "
+		    "server was previously quiesced; "
+		    "existing NFSv4 state will be re-used");
+
+		/*
+		 * HA-NFSv4: this is also the signal
+		 * that a Resource Group failover has
+		 * occurred.
+		 */
+		if (cluster_booted)
+			hanfsv4_failover(nsrv4);
+	} else {
+		/* Cold start */
+		nsrv4->rfs4_start_time = 0;
+		rfs4_state_zone_init(nsrv4);
+		nsrv4->nfs4_drc = rfs4_init_drc(nfs4_drc_max,
+		    nfs4_drc_hash);
+	}
+
+	/* Check if delegation is to be enabled */
+	if (srv_delegation != FALSE)
+		rfs4_set_deleg_policy(nsrv4, SRV_NORMAL_DELEGATE);
+}
+
+void
 rfs4_init_compound_state(struct compound_state *cs)
 {
 	bzero(cs, sizeof (*cs));
@@ -598,6 +681,7 @@
 	cs->deleg = FALSE;
 	cs->mandlock = FALSE;
 	cs->fh.nfs_fh4_val = cs->fhbuf;
+	cs->statusp = NULL;
 }
 
 void
@@ -652,15 +736,15 @@
  * reset all currently active grace periods
  */
 void
-rfs4_grace_reset_all(void)
+rfs4_grace_reset_all(nfs4_srv_t *nsrv4)
 {
 	rfs4_servinst_t *sip;
 
-	mutex_enter(&rfs4_servinst_lock);
-	for (sip = rfs4_cur_servinst; sip != NULL; sip = sip->prev)
+	mutex_enter(&nsrv4->servinst_lock);
+	for (sip = nsrv4->nfs4_cur_servinst; sip != NULL; sip = sip->prev)
 		if (rfs4_servinst_in_grace(sip))
 			rfs4_grace_start(sip);
-	mutex_exit(&rfs4_servinst_lock);
+	mutex_exit(&nsrv4->servinst_lock);
 }
 
 /*
@@ -667,19 +751,20 @@
  * start any new instances' grace periods
  */
 void
-rfs4_grace_start_new(void)
+rfs4_grace_start_new(nfs4_srv_t *nsrv4)
 {
 	rfs4_servinst_t *sip;
 
-	mutex_enter(&rfs4_servinst_lock);
-	for (sip = rfs4_cur_servinst; sip != NULL; sip = sip->prev)
+	mutex_enter(&nsrv4->servinst_lock);
+	for (sip = nsrv4->nfs4_cur_servinst; sip != NULL; sip = sip->prev)
 		if (rfs4_servinst_grace_new(sip))
 			rfs4_grace_start(sip);
-	mutex_exit(&rfs4_servinst_lock);
+	mutex_exit(&nsrv4->servinst_lock);
 }
 
 static rfs4_dss_path_t *
-rfs4_dss_newpath(rfs4_servinst_t *sip, char *path, unsigned index)
+rfs4_dss_newpath(nfs4_srv_t *nsrv4, rfs4_servinst_t *sip,
+    char *path, unsigned index)
 {
 	size_t len;
 	rfs4_dss_path_t *dss_path;
@@ -703,15 +788,15 @@
 	 * Add to list of served paths.
 	 * No locking required, as we're only ever called at startup.
 	 */
-	if (rfs4_dss_pathlist == NULL) {
+	if (nsrv4->dss_pathlist == NULL) {
 		/* this is the first dss_path_t */
 
 		/* needed for insque/remque */
 		dss_path->next = dss_path->prev = dss_path;
 
-		rfs4_dss_pathlist = dss_path;
+		nsrv4->dss_pathlist = dss_path;
 	} else {
-		insque(dss_path, rfs4_dss_pathlist);
+		insque(dss_path, nsrv4->dss_pathlist);
 	}
 
 	return (dss_path);
@@ -723,7 +808,8 @@
  * recovery window.
  */
 void
-rfs4_servinst_create(int start_grace, int dss_npaths, char **dss_paths)
+rfs4_servinst_create(nfs4_srv_t *nsrv4, int start_grace,
+    int dss_npaths, char **dss_paths)
 {
 	unsigned i;
 	rfs4_servinst_t *sip;
@@ -754,21 +840,22 @@
 	    sizeof (rfs4_dss_path_t *), KM_SLEEP);
 
 	for (i = 0; i < dss_npaths; i++) {
-		sip->dss_paths[i] = rfs4_dss_newpath(sip, dss_paths[i], i);
+		/* CSTYLED */
+		sip->dss_paths[i] = rfs4_dss_newpath(nsrv4, sip, dss_paths[i], i);
 	}
 
-	mutex_enter(&rfs4_servinst_lock);
-	if (rfs4_cur_servinst != NULL) {
+	mutex_enter(&nsrv4->servinst_lock);
+	if (nsrv4->nfs4_cur_servinst != NULL) {
 		/* add to linked list */
-		sip->prev = rfs4_cur_servinst;
-		rfs4_cur_servinst->next = sip;
+		sip->prev = nsrv4->nfs4_cur_servinst;
+		nsrv4->nfs4_cur_servinst->next = sip;
 	}
 	if (start_grace)
 		rfs4_grace_start(sip);
 	/* make the new instance "current" */
-	rfs4_cur_servinst = sip;
+	nsrv4->nfs4_cur_servinst = sip;
 
-	mutex_exit(&rfs4_servinst_lock);
+	mutex_exit(&nsrv4->servinst_lock);
 }
 
 /*
@@ -776,7 +863,7 @@
  * all instances directly.
  */
 void
-rfs4_servinst_destroy_all(void)
+rfs4_servinst_destroy_all(nfs4_srv_t *nsrv4)
 {
 	rfs4_servinst_t *sip, *prev, *current;
 #ifdef DEBUG
@@ -783,10 +870,10 @@
 	int n = 0;
 #endif
 
-	mutex_enter(&rfs4_servinst_lock);
-	ASSERT(rfs4_cur_servinst != NULL);
-	current = rfs4_cur_servinst;
-	rfs4_cur_servinst = NULL;
+	mutex_enter(&nsrv4->servinst_lock);
+	ASSERT(nsrv4->nfs4_cur_servinst != NULL);
+	current = nsrv4->nfs4_cur_servinst;
+	nsrv4->nfs4_cur_servinst = NULL;
 	for (sip = current; sip != NULL; sip = prev) {
 		prev = sip->prev;
 		rw_destroy(&sip->rwlock);
@@ -800,7 +887,7 @@
 		n++;
 #endif
 	}
-	mutex_exit(&rfs4_servinst_lock);
+	mutex_exit(&nsrv4->servinst_lock);
 }
 
 /*
@@ -808,7 +895,8 @@
  * Should be called with cp->rc_dbe held.
  */
 void
-rfs4_servinst_assign(rfs4_client_t *cp, rfs4_servinst_t *sip)
+rfs4_servinst_assign(nfs4_srv_t *nsrv4, rfs4_client_t *cp,
+    rfs4_servinst_t *sip)
 {
 	ASSERT(rfs4_dbe_refcnt(cp->rc_dbe) > 0);
 
@@ -816,9 +904,9 @@
 	 * The lock ensures that if the current instance is in the process
 	 * of changing, we will see the new one.
 	 */
-	mutex_enter(&rfs4_servinst_lock);
+	mutex_enter(&nsrv4->servinst_lock);
 	cp->rc_server_instance = sip;
-	mutex_exit(&rfs4_servinst_lock);
+	mutex_exit(&nsrv4->servinst_lock);
 }
 
 rfs4_servinst_t *
@@ -879,6 +967,7 @@
 	seconfig_t *si;
 	bool_t did_traverse = FALSE;
 	int dotdot, walk;
+	nfs_export_t *ne = nfs_get_export();
 
 	dvp = cs->vp;
 	dotdot = (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0');
@@ -900,7 +989,7 @@
 			 * If at the system root, then can
 			 * go up no further.
 			 */
-			if (VN_CMP(dvp, rootdir))
+			if (VN_CMP(dvp, ZONE_ROOTVP()))
 				return (puterrno4(ENOENT));
 
 			/*
@@ -1017,7 +1106,7 @@
 	 * For a real export node, return the flavor that the client
 	 * has access with.
 	 */
-	ASSERT(RW_LOCK_HELD(&exported_lock));
+	ASSERT(RW_LOCK_HELD(&ne->exported_lock));
 	if (PSEUDO(exi)) {
 		count = exi->exi_export.ex_seccnt; /* total sec count */
 		resok_val = kmem_alloc(count * sizeof (secinfo4), KM_SLEEP);
@@ -1380,6 +1469,7 @@
 	vnode_t *vp = cs->vp;
 	cred_t *cr = cs->cr;
 	vattr_t va;
+	nfs4_srv_t *nsrv4;
 
 	DTRACE_NFSV4_2(op__commit__start, struct compound_state *, cs,
 	    COMMIT4args *, args);
@@ -1436,8 +1526,9 @@
 		goto out;
 	}
 
+	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
 	*cs->statusp = resp->status = NFS4_OK;
-	resp->writeverf = Write4verf;
+	resp->writeverf = nsrv4->write4verf;
 out:
 	DTRACE_NFSV4_2(op__commit__done, struct compound_state *, cs,
 	    COMMIT4res *, resp);
@@ -2645,7 +2736,7 @@
 			 * If at the system root, then can
 			 * go up no further.
 			 */
-			if (VN_CMP(cs->vp, rootdir))
+			if (VN_CMP(cs->vp, ZONE_ROOTVP()))
 				return (puterrno4(ENOENT));
 
 			/*
@@ -3409,6 +3500,7 @@
 	vnode_t		*vp;
 	struct exportinfo *exi, *sav_exi;
 	nfs_fh4_fmt_t	*fh_fmtp;
+	nfs_export_t *ne = nfs_get_export();
 
 	DTRACE_NFSV4_1(op__putpubfh__start, struct compound_state *, cs);
 
@@ -3422,19 +3514,19 @@
 
 	cs->cr = crdup(cs->basecr);
 
-	vp = exi_public->exi_vp;
+	vp = ne->exi_public->exi_vp;
 	if (vp == NULL) {
 		*cs->statusp = resp->status = NFS4ERR_SERVERFAULT;
 		goto out;
 	}
 
-	error = makefh4(&cs->fh, vp, exi_public);
+	error = makefh4(&cs->fh, vp, ne->exi_public);
 	if (error != 0) {
 		*cs->statusp = resp->status = puterrno4(error);
 		goto out;
 	}
 	sav_exi = cs->exi;
-	if (exi_public == exi_root) {
+	if (ne->exi_public == ne->exi_root) {
 		/*
 		 * No filesystem is actually shared public, so we default
 		 * to exi_root. In this case, we must check whether root
@@ -3449,12 +3541,12 @@
 		 */
 		exi = checkexport4(&fh_fmtp->fh4_fsid,
 		    (fid_t *)&fh_fmtp->fh4_xlen, NULL);
-		cs->exi = ((exi != NULL) ? exi : exi_public);
+		cs->exi = ((exi != NULL) ? exi : ne->exi_public);
 	} else {
 		/*
 		 * it's a properly shared filesystem
 		 */
-		cs->exi = exi_public;
+		cs->exi = ne->exi_public;
 	}
 
 	if (is_system_labeled()) {
@@ -3529,7 +3621,6 @@
 		cs->cr = NULL;
 	}
 
-
 	if (args->object.nfs_fh4_len < NFS_FH4_LEN) {
 		*cs->statusp = resp->status = NFS4ERR_BADHANDLE;
 		goto out;
@@ -3596,7 +3687,7 @@
 	 */
 	bzero(&fid, sizeof (fid));
 	fid.fid_len = MAXFIDSZ;
-	error = vop_fid_pseudo(rootdir, &fid);
+	error = vop_fid_pseudo(ZONE_ROOTVP(), &fid);
 	if (error != 0) {
 		*cs->statusp = resp->status = puterrno4(error);
 		goto out;
@@ -3610,7 +3701,7 @@
 	 * one or more exports further down in the server's
 	 * file tree.
 	 */
-	exi = checkexport4(&rootdir->v_vfsp->vfs_fsid, &fid, NULL);
+	exi = checkexport4(&ZONE_ROOTVP()->v_vfsp->vfs_fsid, &fid, NULL);
 	if (exi == NULL || exi->exi_export.ex_flags & EX_PUBLIC) {
 		NFS4_DEBUG(rfs4_debug,
 		    (CE_WARN, "rfs4_op_putrootfh: export check failure"));
@@ -3622,7 +3713,7 @@
 	 * Now make a filehandle based on the root
 	 * export and root vnode.
 	 */
-	error = makefh4(&cs->fh, rootdir, exi);
+	error = makefh4(&cs->fh, ZONE_ROOTVP(), exi);
 	if (error != 0) {
 		*cs->statusp = resp->status = puterrno4(error);
 		goto out;
@@ -3631,11 +3722,11 @@
 	sav_exi = cs->exi;
 	cs->exi = exi;
 
-	VN_HOLD(rootdir);
-	cs->vp = rootdir;
+	VN_HOLD(ZONE_ROOTVP());
+	cs->vp = ZONE_ROOTVP();
 
 	if ((resp->status = call_checkauth4(cs, req)) != NFS4_OK) {
-		VN_RELE(rootdir);
+		VN_RELE(cs->vp);
 		cs->vp = NULL;
 		cs->exi = sav_exi;
 		goto out;
@@ -4246,7 +4337,7 @@
 			 * NFS4ERR_EXIST to NFS4ERR_NOTEMPTY to
 			 * transmit over the wire.
 			 */
-			if ((error = VOP_RMDIR(dvp, name, rootdir, cs->cr,
+			if ((error = VOP_RMDIR(dvp, name, ZONE_ROOTVP(), cs->cr,
 			    NULL, 0)) == EEXIST)
 				error = ENOTEMPTY;
 		}
@@ -4358,7 +4449,7 @@
 	int error;
 	vnode_t *odvp;
 	vnode_t *ndvp;
-	vnode_t *srcvp, *targvp;
+	vnode_t *srcvp, *targvp, *tvp;
 	struct vattr obdva, oidva, oadva;
 	struct vattr nbdva, nidva, nadva;
 	char *onm, *nnm;
@@ -4366,6 +4457,7 @@
 	rfs4_file_t *fp, *sfp;
 	int in_crit_src, in_crit_targ;
 	int fp_rele_grant_hold, sfp_rele_grant_hold;
+	int unlinked;
 	bslabel_t *clabel;
 	struct sockaddr *ca;
 	char *converted_onm = NULL;
@@ -4376,9 +4468,10 @@
 	    RENAME4args *, args);
 
 	fp = sfp = NULL;
-	srcvp = targvp = NULL;
+	srcvp = targvp = tvp = NULL;
 	in_crit_src = in_crit_targ = 0;
 	fp_rele_grant_hold = sfp_rele_grant_hold = 0;
+	unlinked = 0;
 
 	/* CURRENT_FH: target directory */
 	ndvp = cs->vp;
@@ -4551,7 +4644,6 @@
 	}
 	fp_rele_grant_hold = 1;
 
-
 	/* Check for NBMAND lock on both source and target */
 	if (nbl_need_check(srcvp)) {
 		nbl_start_crit(srcvp, RW_READER);
@@ -4586,11 +4678,15 @@
 	NFS4_SET_FATTR4_CHANGE(resp->source_cinfo.before, obdva.va_ctime)
 	NFS4_SET_FATTR4_CHANGE(resp->target_cinfo.before, nbdva.va_ctime)
 
-	if ((error = VOP_RENAME(odvp, converted_onm, ndvp, converted_nnm,
-	    cs->cr, NULL, 0)) == 0 && fp != NULL) {
-		struct vattr va;
-		vnode_t *tvp;
+	error = VOP_RENAME(odvp, converted_onm, ndvp, converted_nnm, cs->cr,
+	    NULL, 0);
 
+	/*
+	 * If target existed and was unlinked by VOP_RENAME, state will need
+	 * closed. To avoid deadlock, rfs4_close_all_state will be done after
+	 * any necessary nbl_end_crit on srcvp and tgtvp.
+	 */
+	if (error == 0 && fp != NULL) {
 		rfs4_dbe_lock(fp->rf_dbe);
 		tvp = fp->rf_vp;
 		if (tvp)
@@ -4598,17 +4694,23 @@
 		rfs4_dbe_unlock(fp->rf_dbe);
 
 		if (tvp) {
+			struct vattr va;
 			va.va_mask = AT_NLINK;
+
 			if (!VOP_GETATTR(tvp, &va, 0, cs->cr, NULL) &&
 			    va.va_nlink == 0) {
-				/* The file is gone and so should the state */
-				if (in_crit_targ) {
-					nbl_end_crit(targvp);
-					in_crit_targ = 0;
+				unlinked = 1;
+
+				/* DEBUG data */
+				if ((srcvp == targvp) || (tvp != targvp)) {
+					cmn_err(CE_WARN, "rfs4_op_rename: "
+					    "srcvp %p, targvp: %p, tvp: %p",
+					    (void *)srcvp, (void *)targvp,
+					    (void *)tvp);
 				}
-				rfs4_close_all_state(fp);
+			} else {
+				VN_RELE(tvp);
 			}
-			VN_RELE(tvp);
 		}
 	}
 	if (error == 0)
@@ -4623,6 +4725,21 @@
 	if (targvp)
 		VN_RELE(targvp);
 
+	if (unlinked) {
+		ASSERT(fp != NULL);
+		ASSERT(tvp != NULL);
+
+		/* DEBUG data */
+		if (RW_READ_HELD(&tvp->v_nbllock)) {
+			cmn_err(CE_WARN, "rfs4_op_rename: "
+			    "RW_READ_HELD(%p)", (void *)tvp);
+		}
+
+		/* The file is gone and so should the state */
+		rfs4_close_all_state(fp);
+		VN_RELE(tvp);
+	}
+
 	if (sfp) {
 		rfs4_clear_dont_grant(sfp);
 		rfs4_file_rele(sfp);
@@ -5559,6 +5676,7 @@
 	nfsstat4 stat;
 	int in_crit = 0;
 	caller_context_t ct;
+	nfs4_srv_t *nsrv4;
 
 	DTRACE_NFSV4_2(op__write__start, struct compound_state *, cs,
 	    WRITE4args *, args);
@@ -5629,11 +5747,12 @@
 		goto out;
 	}
 
+	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
 	if (args->data_len == 0) {
 		*cs->statusp = resp->status = NFS4_OK;
 		resp->count = 0;
 		resp->committed = args->stable;
-		resp->writeverf = Write4verf;
+		resp->writeverf = nsrv4->write4verf;
 		goto out;
 	}
 
@@ -5729,7 +5848,7 @@
 	else
 		resp->committed = FILE_SYNC4;
 
-	resp->writeverf = Write4verf;
+	resp->writeverf = nsrv4->write4verf;
 
 out:
 	if (in_crit)
@@ -5749,6 +5868,8 @@
 {
 	uint_t i;
 	struct compound_state cs;
+	nfs4_srv_t *nsrv4;
+	nfs_export_t *ne = nfs_get_export();
 
 	if (rv != NULL)
 		*rv = 0;
@@ -5806,6 +5927,7 @@
 	    KM_SLEEP);
 
 	cs.basecr = cr;
+	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
 
 	DTRACE_NFSV4_2(compound__start, struct compound_state *, &cs,
 	    COMPOUND4args *, args);
@@ -5820,20 +5942,20 @@
 	 * ops along with unexport.  This lock will be removed as
 	 * part of the NFSv4 phase 2 namespace redesign work.
 	 */
-	rw_enter(&exported_lock, RW_READER);
+	rw_enter(&ne->exported_lock, RW_READER);
 
 	/*
 	 * If this is the first compound we've seen, we need to start all
 	 * new instances' grace periods.
 	 */
-	if (rfs4_seen_first_compound == 0) {
-		rfs4_grace_start_new();
+	if (nsrv4->seen_first_compound == 0) {
+		rfs4_grace_start_new(nsrv4);
 		/*
 		 * This must be set after rfs4_grace_start_new(), otherwise
 		 * another thread could proceed past here before the former
 		 * is finished.
 		 */
-		rfs4_seen_first_compound = 1;
+		nsrv4->seen_first_compound = 1;
 	}
 
 	for (i = 0; i < args->array_len && cs.cont; i++) {
@@ -5847,6 +5969,9 @@
 		op = (uint_t)resop->resop;
 
 		if (op < rfsv4disp_cnt) {
+			kstat_t *ksp = rfsprocio_v4_ptr[op];
+			kstat_t *exi_ksp = NULL;
+
 			/*
 			 * Count the individual ops here; NULL and COMPOUND
 			 * are counted in common_dispatch()
@@ -5853,6 +5978,37 @@
 			 */
 			rfsproccnt_v4_ptr[op].value.ui64++;
 
+			if (ksp != NULL) {
+				mutex_enter(ksp->ks_lock);
+				kstat_runq_enter(KSTAT_IO_PTR(ksp));
+				mutex_exit(ksp->ks_lock);
+			}
+
+			switch (rfsv4disptab[op].op_type) {
+			case NFS4_OP_CFH:
+				resop->exi = cs.exi;
+				break;
+			case NFS4_OP_SFH:
+				resop->exi = cs.saved_exi;
+				break;
+			default:
+				ASSERT(resop->exi == NULL);
+				break;
+			}
+
+			if (resop->exi != NULL) {
+				exi_ksp = NULL;
+				if (resop->exi->exi_kstats != NULL) {
+					exi_ksp = exp_kstats_v4(
+					    resop->exi->exi_kstats, op);
+				}
+				if (exi_ksp != NULL) {
+					mutex_enter(exi_ksp->ks_lock);
+					kstat_runq_enter(KSTAT_IO_PTR(exi_ksp));
+					mutex_exit(exi_ksp->ks_lock);
+				}
+			}
+
 			NFS4_DEBUG(rfs4_debug > 1,
 			    (CE_NOTE, "Executing %s", rfs4_op_string[op]));
 			(*rfsv4disptab[op].dis_proc)(argop, resop, req, &cs);
@@ -5860,6 +6016,34 @@
 			    rfs4_op_string[op], *cs.statusp));
 			if (*cs.statusp != NFS4_OK)
 				cs.cont = FALSE;
+
+			if (rfsv4disptab[op].op_type == NFS4_OP_POSTCFH &&
+			    *cs.statusp == NFS4_OK &&
+			    (resop->exi = cs.exi) != NULL) {
+				exi_ksp = NULL;
+				if (resop->exi->exi_kstats != NULL) {
+					exi_ksp = exp_kstats_v4(
+					    resop->exi->exi_kstats, op);
+				}
+			}
+
+			if (exi_ksp != NULL) {
+				mutex_enter(exi_ksp->ks_lock);
+				KSTAT_IO_PTR(exi_ksp)->nwritten +=
+				    argop->opsize;
+				KSTAT_IO_PTR(exi_ksp)->writes++;
+				if (rfsv4disptab[op].op_type != NFS4_OP_POSTCFH)
+					kstat_runq_exit(KSTAT_IO_PTR(exi_ksp));
+				mutex_exit(exi_ksp->ks_lock);
+			} else {
+				resop->exi = NULL;
+			}
+
+			if (ksp != NULL) {
+				mutex_enter(ksp->ks_lock);
+				kstat_runq_exit(KSTAT_IO_PTR(ksp));
+				mutex_exit(ksp->ks_lock);
+			}
 		} else {
 			/*
 			 * This is effectively dead code since XDR code
@@ -5875,31 +6059,46 @@
 		}
 
 		/*
+		 * The exi saved in the resop to be used for kstats update
+		 * once the opsize is calculated during XDR response encoding.
+		 * Put a hold on resop->exi so that it can't be destroyed.
+		 */
+		if (resop->exi != NULL)
+			exi_hold(resop->exi);
+
+		/*
 		 * If not at last op, and if we are to stop, then
 		 * compact the results array.
 		 */
 		if ((i + 1) < args->array_len && !cs.cont) {
 			nfs_resop4 *new_res = kmem_alloc(
-			    (i+1) * sizeof (nfs_resop4), KM_SLEEP);
+			    (i + 1) * sizeof (nfs_resop4), KM_SLEEP);
 			bcopy(resp->array,
-			    new_res, (i+1) * sizeof (nfs_resop4));
+			    new_res, (i + 1) * sizeof (nfs_resop4));
 			kmem_free(resp->array,
 			    args->array_len * sizeof (nfs_resop4));
 
-			resp->array_len =  i + 1;
+			resp->array_len = i + 1;
 			resp->array = new_res;
 		}
 	}
 
-	rw_exit(&exported_lock);
+	rw_exit(&ne->exported_lock);
 
-	DTRACE_NFSV4_2(compound__done, struct compound_state *, &cs,
-	    COMPOUND4res *, resp);
-
+	/*
+	 * clear exportinfo and vnode fields from compound_state before dtrace
+	 * probe, to avoid tracing residual values for path and share path.
+	 */
 	if (cs.vp)
 		VN_RELE(cs.vp);
 	if (cs.saved_vp)
 		VN_RELE(cs.saved_vp);
+	cs.exi = cs.saved_exi = NULL;
+	cs.vp = cs.saved_vp = NULL;
+
+	DTRACE_NFSV4_2(compound__done, struct compound_state *, &cs,
+	    COMPOUND4res *, resp);
+
 	if (cs.saved_fh.nfs_fh4_val)
 		kmem_free(cs.saved_fh.nfs_fh4_val, NFS4_FHSIZE);
 
@@ -5969,6 +6168,93 @@
 	*flagp = flag;
 }
 
+/*
+ * Update the kstats for the received requests.
+ * Note: writes/nwritten are used to hold count and nbytes of requests received.
+ *
+ * Per export request statistics need to be updated during the compound request
+ * processing (rfs4_compound()) as that is where it is known which exportinfo to
+ * associate the kstats with.
+ */
+void
+rfs4_compound_kstat_args(COMPOUND4args *args)
+{
+	int i;
+
+	for (i = 0; i < args->array_len; i++) {
+		uint_t op = (uint_t)args->array[i].argop;
+
+		if (op < rfsv4disp_cnt) {
+			kstat_t *ksp = rfsprocio_v4_ptr[op];
+
+			if (ksp != NULL) {
+				mutex_enter(ksp->ks_lock);
+				KSTAT_IO_PTR(ksp)->nwritten +=
+				    args->array[i].opsize;
+				KSTAT_IO_PTR(ksp)->writes++;
+				mutex_exit(ksp->ks_lock);
+			}
+		}
+	}
+}
+
+/*
+ * Update the kstats for the sent responses.
+ * Note: reads/nread are used to hold count and nbytes of responses sent.
+ *
+ * Per export response statistics cannot be updated until here, after the
+ * response send has generated the opsize (bytes sent) in the XDR encoding.
+ * The exportinfo with which the kstats should be associated is thus saved
+ * in the response structure (by rfs4_compound()) for use here. A hold is
+ * placed on the exi to ensure it cannot be deleted before use. This hold
+ * is released, and the exi set to NULL, here.
+ */
+void
+rfs4_compound_kstat_res(COMPOUND4res *res)
+{
+	int i;
+	nfs_export_t *ne = nfs_get_export();
+
+	for (i = 0; i < res->array_len; i++) {
+		uint_t op = (uint_t)res->array[i].resop;
+
+		if (op < rfsv4disp_cnt) {
+			kstat_t *ksp = rfsprocio_v4_ptr[op];
+			struct exportinfo *exi = res->array[i].exi;
+
+			if (ksp != NULL) {
+				mutex_enter(ksp->ks_lock);
+				KSTAT_IO_PTR(ksp)->nread +=
+				    res->array[i].opsize;
+				KSTAT_IO_PTR(ksp)->reads++;
+				mutex_exit(ksp->ks_lock);
+			}
+
+			if (exi != NULL) {
+				kstat_t *exi_ksp = NULL;
+
+				rw_enter(&ne->exported_lock, RW_READER);
+
+				if (exi->exi_kstats != NULL) {
+					/*CSTYLED*/
+					exi_ksp = exp_kstats_v4(exi->exi_kstats, op);
+				}
+				if (exi_ksp != NULL) {
+					mutex_enter(exi_ksp->ks_lock);
+					KSTAT_IO_PTR(exi_ksp)->nread +=
+					    res->array[i].opsize;
+					KSTAT_IO_PTR(exi_ksp)->reads++;
+					mutex_exit(exi_ksp->ks_lock);
+				}
+
+				exi_rele(&exi);
+				res->array[i].exi = NULL;
+				rw_exit(&ne->exported_lock);
+			}
+		}
+	}
+}
+
 nfsstat4
 rfs4_client_sysid(rfs4_client_t *cp, sysid_t *sp)
 {
@@ -6603,6 +6889,7 @@
 		if (trunc) {
 			int in_crit = 0;
 			rfs4_file_t *fp;
+			nfs4_srv_t *nsrv4;
 			bool_t create = FALSE;
 
 			/*
@@ -6609,12 +6896,13 @@
 			 * We are writing over an existing file.
 			 * Check to see if we need to recall a delegation.
 			 */
-			rfs4_hold_deleg_policy();
+			nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
+			rfs4_hold_deleg_policy(nsrv4);
 			if ((fp = rfs4_findfile(vp, NULL, &create)) != NULL) {
 				if (rfs4_check_delegated_byfp(FWRITE, fp,
 				    (reqsize == 0), FALSE, FALSE, &clientid)) {
 					rfs4_file_rele(fp);
-					rfs4_rele_deleg_policy();
+					rfs4_rele_deleg_policy(nsrv4);
 					VN_RELE(vp);
 					*attrset = 0;
 					return (NFS4ERR_DELAY);
@@ -6621,7 +6909,7 @@
 				}
 				rfs4_file_rele(fp);
 			}
-			rfs4_rele_deleg_policy();
+			rfs4_rele_deleg_policy(nsrv4);
 
 			if (nbl_need_check(vp)) {
 				in_crit = 1;
@@ -8179,11 +8467,13 @@
 	SETCLIENTID_CONFIRM4res *res =
 	    &resop->nfs_resop4_u.opsetclientid_confirm;
 	rfs4_client_t *cp, *cptoclose = NULL;
+	nfs4_srv_t *nsrv4;
 
 	DTRACE_NFSV4_2(op__setclientid__confirm__start,
 	    struct compound_state *, cs,
 	    SETCLIENTID_CONFIRM4args *, args);
 
+	nsrv4 = zone_getspecific(rfs4_zone_key, curzone);
 	*cs->statusp = res->status = NFS4_OK;
 
 	cp = rfs4_findclient_by_id(args->clientid, TRUE);
@@ -8219,14 +8509,14 @@
 	 * Update the client's associated server instance, if it's changed
 	 * since the client was created.
 	 */
-	if (rfs4_servinst(cp) != rfs4_cur_servinst)
-		rfs4_servinst_assign(cp, rfs4_cur_servinst);
+	if (rfs4_servinst(cp) != nsrv4->nfs4_cur_servinst)
+		rfs4_servinst_assign(nsrv4, cp, nsrv4->nfs4_cur_servinst);
 
 	/*
 	 * Record clientid in stable storage.
 	 * Must be done after server instance has been assigned.
 	 */
-	rfs4_ss_clid(cp);
+	rfs4_ss_clid(nsrv4, cp);
 
 	rfs4_dbe_unlock(cp->rc_dbe);
 
@@ -8241,7 +8531,7 @@
 	/*
 	 * Check to see if client can perform reclaims
 	 */
-	rfs4_ss_chkclid(cp);
+	rfs4_ss_chkclid(nsrv4, cp);
 
 	rfs4_client_rele(cp);
 
@@ -9885,4 +10175,168 @@
 	is_downrev = ci->ri_no_referrals;
 	rfs4_dbe_rele(ci->ri_dbe);
 	return (is_downrev);
+}
+
+/*
+ * 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(nfs4_srv_t *nsrv4)
+{
+	int i, start_grace, numadded_paths = 0;
+	char **added_paths = NULL;
+	rfs4_dss_path_t *dss_path;
+
+	/*
+	 * Note: currently, 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" 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 = nsrv4->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 != nsrv4->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"
+	 * 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 = nsrv4->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 != nsrv4->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;
+		/* CSTYLED */
+		rfs4_servinst_create(nsrv4, start_grace, numadded_paths, added_paths);
+
+		/* read in the stable storage state from these paths */
+		rfs4_dss_readstate(nsrv4, 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(nsrv4);
+	}
+
+	if (rfs4_dss_numnewpaths > 0)
+		kmem_free(added_paths, rfs4_dss_numnewpaths * sizeof (char *));
 }
