git: b4805d577787 - stable/13 - nfsd: Prepare the NFS server code to run in a vnet prison

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Mon, 15 May 2023 23:42:18 UTC
The branch stable/13 has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=b4805d5777871ee62400737ceddec5e0e5e05941

commit b4805d5777871ee62400737ceddec5e0e5e05941
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2023-02-11 23:51:19 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2023-05-15 23:39:04 +0000

    nfsd: Prepare the NFS server code to run in a vnet prison
    
    This patch defines null macros that can be used to apply
    the vnet macros for global variables and SYSCTL flags.
    It also applies these macros to many of the global variables
    and some of the SYSCTLs.  Since the macros do nothing, these
    changes should not result in semantics changes, although the
    changes are large in number.
    
    The patch does change several global variables that were
    arrays or structures to pointers to same.  For these variables,
    modified initialization and cleanup code malloc's and free's
    the arrays/structures.  This was done so that the vnet footprint
    would be about 300bytes when the macros are defined as vnet macros,
    allowing nfsd.ko to load dynamically.
    
    I believe the comments in D37519 have been addressed, although
    it has never been reviewed, due in part to the large size of the patch.
    This is the first of a series of patches that will put D37519 in main.
    
    Once everything is in main, the macros will be defined as front
    end macros to the vnet ones.
    
    (cherry picked from commit 7e44856e3a6deb194c2c376e886854b256360c40)
---
 sys/fs/nfs/nfs_var.h              |   2 +-
 sys/fs/nfs/nfsport.h              |  14 ++
 sys/fs/nfsserver/nfs_fha_new.c    |   5 +-
 sys/fs/nfsserver/nfs_nfsdcache.c  | 155 +++++++++++---------
 sys/fs/nfsserver/nfs_nfsdkrpc.c   | 100 ++++++++-----
 sys/fs/nfsserver/nfs_nfsdport.c   | 292 +++++++++++++++++++++++++-------------
 sys/fs/nfsserver/nfs_nfsdsocket.c |  70 +++++----
 sys/fs/nfsserver/nfs_nfsdstate.c  | 186 +++++++++++++-----------
 sys/fs/nfsserver/nfs_nfsdsubs.c   |  45 +++---
 9 files changed, 522 insertions(+), 347 deletions(-)

diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index 9dd3be3d5c42..ef7d0050e1a3 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -122,7 +122,7 @@ int nfsrv_delegupdate(struct nfsrv_descript *, nfsquad_t, nfsv4stateid_t *,
 int nfsrv_releaselckown(struct nfsstate *, nfsquad_t, NFSPROC_T *);
 void nfsrv_zapclient(struct nfsclient *, NFSPROC_T *);
 int nfssvc_idname(struct nfsd_idargs *);
-void nfsrv_servertimer(void);
+void nfsrv_servertimer(void * __unused);
 int nfsrv_getclientipaddr(struct nfsrv_descript *, struct nfsclient *);
 void nfsrv_setupstable(NFSPROC_T *);
 void nfsrv_updatestable(NFSPROC_T *);
diff --git a/sys/fs/nfs/nfsport.h b/sys/fs/nfs/nfsport.h
index 2955ae43de85..0a0e68787f65 100644
--- a/sys/fs/nfs/nfsport.h
+++ b/sys/fs/nfs/nfsport.h
@@ -181,6 +181,20 @@
  */
 #define	NFSMUTEX_T		struct mtx
 
+/* Define the NFSD_VNET macros similar to !VIMAGE. */
+#define	NFSD_VNET_NAME(n)		n
+#define	NFSD_VNET_DECLARE(t, n)		extern t n
+#define	NFSD_VNET_DEFINE(t, n)		t n
+#define	NFSD_VNET_DEFINE_STATIC(t, n)	static t n
+#define	NFSD_VNET(n)			(n)
+
+#define	CTLFLAG_NFSD_VNET		0
+
+#define	NFSD_CURVNET_SET(n)
+#define	NFSD_CURVNET_SET_QUIET(n)
+#define	NFSD_CURVNET_RESTORE()
+#define	NFSD_TD_TO_VNET(n)		NULL
+
 #endif	/* _KERNEL */
 
 /*
diff --git a/sys/fs/nfsserver/nfs_fha_new.c b/sys/fs/nfsserver/nfs_fha_new.c
index 1f66842da5b8..59933ef01bd4 100644
--- a/sys/fs/nfsserver/nfs_fha_new.c
+++ b/sys/fs/nfsserver/nfs_fha_new.c
@@ -61,7 +61,8 @@ static struct fha_params fhanew_softc;
 SYSCTL_DECL(_vfs_nfsd);
 
 extern int newnfs_nfsv3_procid[];
-extern SVCPOOL	*nfsrvd_pool;
+
+NFSD_VNET_DECLARE(SVCPOOL *, nfsrvd_pool);
 
 SYSINIT(nfs_fhanew, SI_SUB_ROOT_CONF, SI_ORDER_ANY, fhanew_init, NULL);
 SYSUNINIT(nfs_fhanew, SI_SUB_ROOT_CONF, SI_ORDER_ANY, fhanew_uninit, NULL);
@@ -79,7 +80,7 @@ fhanew_init(void *foo)
 	snprintf(softc->server_name, sizeof(softc->server_name),
 	    FHANEW_SERVER_NAME);
 
-	softc->pool = &nfsrvd_pool;
+	softc->pool = &NFSD_VNET(nfsrvd_pool);
 
 	/*
 	 * Initialize the sysctl context list for the fha module.
diff --git a/sys/fs/nfsserver/nfs_nfsdcache.c b/sys/fs/nfsserver/nfs_nfsdcache.c
index 5efcb90411e0..e94d58481286 100644
--- a/sys/fs/nfsserver/nfs_nfsdcache.c
+++ b/sys/fs/nfsserver/nfs_nfsdcache.c
@@ -160,11 +160,15 @@ __FBSDID("$FreeBSD$");
  */
 #include <fs/nfs/nfsport.h>
 
-extern struct nfsstatsv1 nfsstatsv1;
 extern struct mtx nfsrc_udpmtx;
-extern struct nfsrchash_bucket nfsrchash_table[NFSRVCACHE_HASHSIZE];
-extern struct nfsrchash_bucket nfsrcahash_table[NFSRVCACHE_HASHSIZE];
-int nfsrc_floodlevel = NFSRVCACHE_FLOODLEVEL, nfsrc_tcpsavedreplies = 0;
+
+NFSD_VNET_DECLARE(struct nfsrvhashhead *, nfsrvudphashtbl);
+NFSD_VNET_DECLARE(struct nfsrchash_bucket *, nfsrchash_table);
+NFSD_VNET_DECLARE(struct nfsrchash_bucket *, nfsrcahash_table);
+NFSD_VNET_DECLARE(struct nfsstatsv1 *, nfsstatsv1_p);
+
+NFSD_VNET_DEFINE(int, nfsrc_floodlevel) = NFSRVCACHE_FLOODLEVEL;
+NFSD_VNET_DEFINE(int, nfsrc_tcpsavedreplies) = 0;
 
 SYSCTL_DECL(_vfs_nfsd);
 
@@ -180,8 +184,8 @@ sysctl_tcphighwater(SYSCTL_HANDLER_ARGS)
 		return (error);
 	if (newhighwater < 0)
 		return (EINVAL);
-	if (newhighwater >= nfsrc_floodlevel)
-		nfsrc_floodlevel = newhighwater + newhighwater / 5;
+	if (newhighwater >= NFSD_VNET(nfsrc_floodlevel))
+		NFSD_VNET(nfsrc_floodlevel) = newhighwater + newhighwater / 5;
 	nfsrc_tcphighwater = newhighwater;
 	return (0);
 }
@@ -202,9 +206,8 @@ SYSCTL_UINT(_vfs_nfsd, OID_AUTO, cachetcp, CTLFLAG_RW,
     &nfsrc_tcpnonidempotent, 0,
     "Enable the DRC for NFS over TCP");
 
-static int nfsrc_udpcachesize = 0;
-static TAILQ_HEAD(, nfsrvcache) nfsrvudplru;
-static struct nfsrvhashhead nfsrvudphashtbl[NFSRVCACHE_HASHSIZE];
+NFSD_VNET_DEFINE_STATIC(int, nfsrc_udpcachesize) = 0;
+NFSD_VNET_DEFINE_STATIC(TAILQ_HEAD(, nfsrvcache), nfsrvudplru);
 
 /*
  * and the reverse mapping from generic to Version 2 procedure numbers
@@ -236,10 +239,10 @@ static int newnfsv2_procid[NFS_V3NPROCS] = {
 
 #define	nfsrc_hash(xid)	(((xid) + ((xid) >> 24)) % NFSRVCACHE_HASHSIZE)
 #define	NFSRCUDPHASH(xid) \
-	(&nfsrvudphashtbl[nfsrc_hash(xid)])
+	(&NFSD_VNET(nfsrvudphashtbl)[nfsrc_hash(xid)])
 #define	NFSRCHASH(xid) \
-	(&nfsrchash_table[nfsrc_hash(xid)].tbl)
-#define	NFSRCAHASH(xid) (&nfsrcahash_table[nfsrc_hash(xid)])
+	(&NFSD_VNET(nfsrchash_table)[nfsrc_hash(xid)].tbl)
+#define	NFSRCAHASH(xid) (&NFSD_VNET(nfsrcahash_table)[nfsrc_hash(xid)])
 #define	TRUE	1
 #define	FALSE	0
 #define	NFSRVCACHE_CHECKLEN	100
@@ -295,7 +298,7 @@ nfsrc_cachemutex(struct nfsrvcache *rp)
 
 	if ((rp->rc_flag & RC_UDP) != 0)
 		return (&nfsrc_udpmtx);
-	return (&nfsrchash_table[nfsrc_hash(rp->rc_xid)].mtx);
+	return (&NFSD_VNET(nfsrchash_table)[nfsrc_hash(rp->rc_xid)].mtx);
 }
 
 /*
@@ -305,21 +308,27 @@ void
 nfsrvd_initcache(void)
 {
 	int i;
-	static int inited = 0;
 
-	if (inited)
-		return;
-	inited = 1;
+	NFSD_VNET(nfsrvudphashtbl) = malloc(sizeof(struct nfsrvhashhead) *
+	    NFSRVCACHE_HASHSIZE, M_NFSRVCACHE, M_WAITOK | M_ZERO);
+	NFSD_VNET(nfsrchash_table) = malloc(sizeof(struct nfsrchash_bucket) *
+	    NFSRVCACHE_HASHSIZE, M_NFSRVCACHE, M_WAITOK | M_ZERO);
+	NFSD_VNET(nfsrcahash_table) = malloc(sizeof(struct nfsrchash_bucket) *
+	    NFSRVCACHE_HASHSIZE, M_NFSRVCACHE, M_WAITOK | M_ZERO);
+	for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) {
+		mtx_init(&NFSD_VNET(nfsrchash_table)[i].mtx, "nfsrtc", NULL,
+		    MTX_DEF);
+		mtx_init(&NFSD_VNET(nfsrcahash_table)[i].mtx, "nfsrtca", NULL,
+		    MTX_DEF);
+	}
 	for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) {
-		LIST_INIT(&nfsrvudphashtbl[i]);
-		LIST_INIT(&nfsrchash_table[i].tbl);
-		LIST_INIT(&nfsrcahash_table[i].tbl);
+		LIST_INIT(&NFSD_VNET(nfsrvudphashtbl)[i]);
+		LIST_INIT(&NFSD_VNET(nfsrchash_table)[i].tbl);
+		LIST_INIT(&NFSD_VNET(nfsrcahash_table)[i].tbl);
 	}
-	TAILQ_INIT(&nfsrvudplru);
-	nfsrc_tcpsavedreplies = 0;
-	nfsrc_udpcachesize = 0;
-	nfsstatsv1.srvcache_tcppeak = 0;
-	nfsstatsv1.srvcache_size = 0;
+	TAILQ_INIT(&NFSD_VNET(nfsrvudplru));
+	NFSD_VNET(nfsrc_tcpsavedreplies) = 0;
+	NFSD_VNET(nfsrc_udpcachesize) = 0;
 }
 
 /*
@@ -392,17 +401,17 @@ loop:
 			if (rp->rc_flag == 0)
 				panic("nfs udp cache0");
 			rp->rc_flag |= RC_LOCKED;
-			TAILQ_REMOVE(&nfsrvudplru, rp, rc_lru);
-			TAILQ_INSERT_TAIL(&nfsrvudplru, rp, rc_lru);
+			TAILQ_REMOVE(&NFSD_VNET(nfsrvudplru), rp, rc_lru);
+			TAILQ_INSERT_TAIL(&NFSD_VNET(nfsrvudplru), rp, rc_lru);
 			if (rp->rc_flag & RC_INPROG) {
-				nfsstatsv1.srvcache_inproghits++;
+				nfsstatsv1_p->srvcache_inproghits++;
 				mtx_unlock(mutex);
 				ret = RC_DROPIT;
 			} else if (rp->rc_flag & RC_REPSTATUS) {
 				/*
 				 * V2 only.
 				 */
-				nfsstatsv1.srvcache_nonidemdonehits++;
+				nfsstatsv1_p->srvcache_nonidemdonehits++;
 				mtx_unlock(mutex);
 				nfsrvd_rephead(nd);
 				*(nd->nd_errp) = rp->rc_status;
@@ -410,7 +419,7 @@ loop:
 				rp->rc_timestamp = NFSD_MONOSEC +
 					NFSRVCACHE_UDPTIMEOUT;
 			} else if (rp->rc_flag & RC_REPMBUF) {
-				nfsstatsv1.srvcache_nonidemdonehits++;
+				nfsstatsv1_p->srvcache_nonidemdonehits++;
 				mtx_unlock(mutex);
 				nd->nd_mreq = m_copym(rp->rc_reply, 0,
 					M_COPYALL, M_WAITOK);
@@ -425,9 +434,9 @@ loop:
 			goto out;
 		}
 	}
-	nfsstatsv1.srvcache_misses++;
-	atomic_add_int(&nfsstatsv1.srvcache_size, 1);
-	nfsrc_udpcachesize++;
+	nfsstatsv1_p->srvcache_misses++;
+	atomic_add_int(&nfsstatsv1_p->srvcache_size, 1);
+	NFSD_VNET(nfsrc_udpcachesize)++;
 
 	newrp->rc_flag |= RC_INPROG;
 	saddr = NFSSOCKADDR(nd->nd_nam, struct sockaddr_in *);
@@ -440,7 +449,7 @@ loop:
 		newrp->rc_flag |= RC_INETIPV6;
 	}
 	LIST_INSERT_HEAD(hp, newrp, rc_hash);
-	TAILQ_INSERT_TAIL(&nfsrvudplru, newrp, rc_lru);
+	TAILQ_INSERT_TAIL(&NFSD_VNET(nfsrvudplru), newrp, rc_lru);
 	mtx_unlock(mutex);
 	nd->nd_rp = newrp;
 	ret = RC_DOIT;
@@ -472,15 +481,15 @@ nfsrvd_updatecache(struct nfsrv_descript *nd)
 		panic("nfsrvd_updatecache not inprog");
 	rp->rc_flag &= ~RC_INPROG;
 	if (rp->rc_flag & RC_UDP) {
-		TAILQ_REMOVE(&nfsrvudplru, rp, rc_lru);
-		TAILQ_INSERT_TAIL(&nfsrvudplru, rp, rc_lru);
+		TAILQ_REMOVE(&NFSD_VNET(nfsrvudplru), rp, rc_lru);
+		TAILQ_INSERT_TAIL(&NFSD_VNET(nfsrvudplru), rp, rc_lru);
 	}
 
 	/*
 	 * Reply from cache is a special case returned by nfsrv_checkseqid().
 	 */
 	if (nd->nd_repstat == NFSERR_REPLYFROMCACHE) {
-		nfsstatsv1.srvcache_nonidemdonehits++;
+		nfsstatsv1_p->srvcache_nonidemdonehits++;
 		mtx_unlock(mutex);
 		nd->nd_repstat = 0;
 		if (nd->nd_mreq)
@@ -503,7 +512,7 @@ nfsrvd_updatecache(struct nfsrv_descript *nd)
 	    (rp->rc_refcnt > 0 ||
 	     ((nd->nd_flag & ND_SAVEREPLY) && (rp->rc_flag & RC_UDP)) ||
 	     ((nd->nd_flag & ND_SAVEREPLY) && !(rp->rc_flag & RC_UDP) &&
-	      nfsrc_tcpsavedreplies <= nfsrc_floodlevel &&
+	      NFSD_VNET(nfsrc_tcpsavedreplies) <= NFSD_VNET(nfsrc_floodlevel) &&
 	      nfsrc_tcpnonidempotent))) {
 		if (rp->rc_refcnt > 0) {
 			if (!(rp->rc_flag & RC_NFSV4))
@@ -517,11 +526,12 @@ nfsrvd_updatecache(struct nfsrv_descript *nd)
 			mtx_unlock(mutex);
 		} else {
 			if (!(rp->rc_flag & RC_UDP)) {
-			    atomic_add_int(&nfsrc_tcpsavedreplies, 1);
-			    if (nfsrc_tcpsavedreplies >
-				nfsstatsv1.srvcache_tcppeak)
-				nfsstatsv1.srvcache_tcppeak =
-				    nfsrc_tcpsavedreplies;
+			    atomic_add_int(&NFSD_VNET(nfsrc_tcpsavedreplies),
+				1);
+			    if (NFSD_VNET(nfsrc_tcpsavedreplies) >
+				nfsstatsv1_p->srvcache_tcppeak)
+				nfsstatsv1_p->srvcache_tcppeak =
+				    NFSD_VNET(nfsrc_tcpsavedreplies);
 			}
 			mtx_unlock(mutex);
 			m = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK);
@@ -678,7 +688,7 @@ tryagain:
 			panic("nfs tcp cache0");
 		rp->rc_flag |= RC_LOCKED;
 		if (rp->rc_flag & RC_INPROG) {
-			nfsstatsv1.srvcache_inproghits++;
+			nfsstatsv1_p->srvcache_inproghits++;
 			mtx_unlock(mutex);
 			if (newrp->rc_sockref == rp->rc_sockref)
 				nfsrc_marksametcpconn(rp->rc_sockref);
@@ -687,7 +697,7 @@ tryagain:
 			/*
 			 * V2 only.
 			 */
-			nfsstatsv1.srvcache_nonidemdonehits++;
+			nfsstatsv1_p->srvcache_nonidemdonehits++;
 			mtx_unlock(mutex);
 			if (newrp->rc_sockref == rp->rc_sockref)
 				nfsrc_marksametcpconn(rp->rc_sockref);
@@ -696,7 +706,7 @@ tryagain:
 			*(nd->nd_errp) = rp->rc_status;
 			rp->rc_timestamp = NFSD_MONOSEC + nfsrc_tcptimeout;
 		} else if (rp->rc_flag & RC_REPMBUF) {
-			nfsstatsv1.srvcache_nonidemdonehits++;
+			nfsstatsv1_p->srvcache_nonidemdonehits++;
 			mtx_unlock(mutex);
 			if (newrp->rc_sockref == rp->rc_sockref)
 				nfsrc_marksametcpconn(rp->rc_sockref);
@@ -711,8 +721,8 @@ tryagain:
 		free(newrp, M_NFSRVCACHE);
 		goto out;
 	}
-	nfsstatsv1.srvcache_misses++;
-	atomic_add_int(&nfsstatsv1.srvcache_size, 1);
+	nfsstatsv1_p->srvcache_misses++;
+	atomic_add_int(&nfsstatsv1_p->srvcache_size, 1);
 
 	/*
 	 * For TCP, multiple entries for a key are allowed, so don't
@@ -785,8 +795,8 @@ nfsrc_freecache(struct nfsrvcache *rp)
 
 	LIST_REMOVE(rp, rc_hash);
 	if (rp->rc_flag & RC_UDP) {
-		TAILQ_REMOVE(&nfsrvudplru, rp, rc_lru);
-		nfsrc_udpcachesize--;
+		TAILQ_REMOVE(&NFSD_VNET(nfsrvudplru), rp, rc_lru);
+		NFSD_VNET(nfsrc_udpcachesize)--;
 	} else if (rp->rc_acked != RC_NO_SEQ) {
 		hbp = NFSRCAHASH(rp->rc_sockref);
 		mtx_lock(&hbp->mtx);
@@ -798,10 +808,10 @@ nfsrc_freecache(struct nfsrvcache *rp)
 	if (rp->rc_flag & RC_REPMBUF) {
 		m_freem(rp->rc_reply);
 		if (!(rp->rc_flag & RC_UDP))
-			atomic_add_int(&nfsrc_tcpsavedreplies, -1);
+			atomic_add_int(&NFSD_VNET(nfsrc_tcpsavedreplies), -1);
 	}
 	free(rp, M_NFSRVCACHE);
-	atomic_add_int(&nfsstatsv1.srvcache_size, -1);
+	atomic_add_int(&nfsstatsv1_p->srvcache_size, -1);
 }
 
 /*
@@ -814,20 +824,22 @@ nfsrvd_cleancache(void)
 	int i;
 
 	for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) {
-		mtx_lock(&nfsrchash_table[i].mtx);
-		LIST_FOREACH_SAFE(rp, &nfsrchash_table[i].tbl, rc_hash, nextrp)
+		mtx_lock(&NFSD_VNET(nfsrchash_table)[i].mtx);
+		LIST_FOREACH_SAFE(rp, &NFSD_VNET(nfsrchash_table)[i].tbl,
+		    rc_hash, nextrp)
 			nfsrc_freecache(rp);
-		mtx_unlock(&nfsrchash_table[i].mtx);
+		mtx_unlock(&NFSD_VNET(nfsrchash_table)[i].mtx);
 	}
 	mtx_lock(&nfsrc_udpmtx);
 	for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) {
-		LIST_FOREACH_SAFE(rp, &nfsrvudphashtbl[i], rc_hash, nextrp) {
+		LIST_FOREACH_SAFE(rp, &NFSD_VNET(nfsrvudphashtbl)[i], rc_hash,
+		    nextrp) {
 			nfsrc_freecache(rp);
 		}
 	}
-	nfsstatsv1.srvcache_size = 0;
+	nfsstatsv1_p->srvcache_size = 0;
 	mtx_unlock(&nfsrc_udpmtx);
-	nfsrc_tcpsavedreplies = 0;
+	NFSD_VNET(nfsrc_tcpsavedreplies) = 0;
 }
 
 #define HISTSIZE	16
@@ -864,25 +876,28 @@ nfsrc_trimcache(u_int64_t sockref, uint32_t snd_una, int final)
 	if (atomic_cmpset_acq_int(&onethread, 0, 1) == 0)
 		return;
 	if (NFSD_MONOSEC != udp_lasttrim ||
-	    nfsrc_udpcachesize >= (nfsrc_udphighwater +
+	    NFSD_VNET(nfsrc_udpcachesize) >= (nfsrc_udphighwater +
 	    nfsrc_udphighwater / 2)) {
 		mtx_lock(&nfsrc_udpmtx);
 		udp_lasttrim = NFSD_MONOSEC;
-		TAILQ_FOREACH_SAFE(rp, &nfsrvudplru, rc_lru, nextrp) {
+		TAILQ_FOREACH_SAFE(rp, &NFSD_VNET(nfsrvudplru), rc_lru,
+		    nextrp) {
 			if (!(rp->rc_flag & (RC_INPROG|RC_LOCKED|RC_WANTED))
 			     && rp->rc_refcnt == 0
 			     && ((rp->rc_flag & RC_REFCNT) ||
 				 udp_lasttrim > rp->rc_timestamp ||
-				 nfsrc_udpcachesize > nfsrc_udphighwater))
+				 NFSD_VNET(nfsrc_udpcachesize) >
+				 nfsrc_udphighwater))
 				nfsrc_freecache(rp);
 		}
 		mtx_unlock(&nfsrc_udpmtx);
 	}
 	if (NFSD_MONOSEC != tcp_lasttrim ||
-	    nfsrc_tcpsavedreplies >= nfsrc_tcphighwater) {
+	    NFSD_VNET(nfsrc_tcpsavedreplies) >= nfsrc_tcphighwater) {
 		force = nfsrc_tcphighwater / 4;
 		if (force > 0 &&
-		    nfsrc_tcpsavedreplies + force >= nfsrc_tcphighwater) {
+		    NFSD_VNET(nfsrc_tcpsavedreplies) + force >=
+		    nfsrc_tcphighwater) {
 			for (i = 0; i < HISTSIZE; i++)
 				time_histo[i] = 0;
 			i = 0;
@@ -901,8 +916,9 @@ nfsrc_trimcache(u_int64_t sockref, uint32_t snd_una, int final)
 		tto = nfsrc_tcptimeout;
 		tcp_lasttrim = NFSD_MONOSEC;
 		for (; i <= lastslot; i++) {
-			mtx_lock(&nfsrchash_table[i].mtx);
-			LIST_FOREACH_SAFE(rp, &nfsrchash_table[i].tbl, rc_hash,
+			mtx_lock(&NFSD_VNET(nfsrchash_table)[i].mtx);
+			LIST_FOREACH_SAFE(rp,
+			    &NFSD_VNET(nfsrchash_table)[i].tbl, rc_hash,
 			    nextrp) {
 				if (!(rp->rc_flag &
 				     (RC_INPROG|RC_LOCKED|RC_WANTED))
@@ -932,7 +948,7 @@ nfsrc_trimcache(u_int64_t sockref, uint32_t snd_una, int final)
 					time_histo[j]++;
 				}
 			}
-			mtx_unlock(&nfsrchash_table[i].mtx);
+			mtx_unlock(&NFSD_VNET(nfsrchash_table)[i].mtx);
 		}
 		if (force) {
 			/*
@@ -951,8 +967,9 @@ nfsrc_trimcache(u_int64_t sockref, uint32_t snd_una, int final)
 				k = 1;
 			thisstamp = tcp_lasttrim + k;
 			for (i = 0; i < NFSRVCACHE_HASHSIZE; i++) {
-				mtx_lock(&nfsrchash_table[i].mtx);
-				LIST_FOREACH_SAFE(rp, &nfsrchash_table[i].tbl,
+				mtx_lock(&NFSD_VNET(nfsrchash_table)[i].mtx);
+				LIST_FOREACH_SAFE(rp,
+				    &NFSD_VNET(nfsrchash_table)[i].tbl,
 				    rc_hash, nextrp) {
 					if (!(rp->rc_flag &
 					     (RC_INPROG|RC_LOCKED|RC_WANTED))
@@ -962,7 +979,7 @@ nfsrc_trimcache(u_int64_t sockref, uint32_t snd_una, int final)
 						 rp->rc_acked == RC_ACK))
 						nfsrc_freecache(rp);
 				}
-				mtx_unlock(&nfsrchash_table[i].mtx);
+				mtx_unlock(&NFSD_VNET(nfsrchash_table)[i].mtx);
 			}
 		}
 	}
diff --git a/sys/fs/nfsserver/nfs_nfsdkrpc.c b/sys/fs/nfsserver/nfs_nfsdkrpc.c
index 44f585ff0beb..7b0f84c397bc 100644
--- a/sys/fs/nfsserver/nfs_nfsdkrpc.c
+++ b/sys/fs/nfsserver/nfs_nfsdkrpc.c
@@ -52,7 +52,6 @@ __FBSDID("$FreeBSD$");
 
 NFSDLOCKMUTEX;
 NFSV4ROOTLOCKMUTEX;
-struct nfsv4lock nfsd_suspend_lock;
 char *nfsrv_zeropnfsdat = NULL;
 
 /*
@@ -85,32 +84,39 @@ int newnfs_nfsv3_procid[NFS_V3NPROCS] = {
 
 SYSCTL_DECL(_vfs_nfsd);
 
-SVCPOOL		*nfsrvd_pool;
-
-static int	nfs_privport = 0;
-SYSCTL_INT(_vfs_nfsd, OID_AUTO, nfs_privport, CTLFLAG_RWTUN,
-    &nfs_privport, 0,
+NFSD_VNET_DEFINE_STATIC(int, nfs_privport) = 0;
+SYSCTL_INT(_vfs_nfsd, OID_AUTO, nfs_privport, CTLFLAG_NFSD_VNET | CTLFLAG_RWTUN,
+    &NFSD_VNET_NAME(nfs_privport), 0,
     "Only allow clients using a privileged port for NFSv2, 3 and 4");
 
-static int	nfs_minvers = NFS_VER2;
-SYSCTL_INT(_vfs_nfsd, OID_AUTO, server_min_nfsvers, CTLFLAG_RWTUN,
-    &nfs_minvers, 0, "The lowest version of NFS handled by the server");
+NFSD_VNET_DEFINE_STATIC(int, nfs_minvers) = NFS_VER2;
+SYSCTL_INT(_vfs_nfsd, OID_AUTO, server_min_nfsvers,
+    CTLFLAG_NFSD_VNET | CTLFLAG_RWTUN, &NFSD_VNET_NAME(nfs_minvers), 0,
+    "The lowest version of NFS handled by the server");
 
-static int	nfs_maxvers = NFS_VER4;
-SYSCTL_INT(_vfs_nfsd, OID_AUTO, server_max_nfsvers, CTLFLAG_RWTUN,
-    &nfs_maxvers, 0, "The highest version of NFS handled by the server");
+NFSD_VNET_DEFINE_STATIC(int, nfs_maxvers) = NFS_VER4;
+SYSCTL_INT(_vfs_nfsd, OID_AUTO, server_max_nfsvers,
+    CTLFLAG_NFSD_VNET | CTLFLAG_RWTUN, &NFSD_VNET_NAME(nfs_maxvers), 0,
+    "The highest version of NFS handled by the server");
 
 static int nfs_proc(struct nfsrv_descript *, u_int32_t, SVCXPRT *xprt,
     struct nfsrvcache **);
 
 extern u_long sb_max_adj;
 extern int newnfs_numnfsd;
-extern struct proc *nfsd_master_proc;
 extern time_t nfsdev_time;
 extern int nfsrv_writerpc[NFS_NPROCS];
 extern volatile int nfsrv_devidcnt;
 extern struct nfsv4_opflag nfsv4_opflag[NFSV42_NOPS];
 
+NFSD_VNET_DECLARE(struct proc *, nfsd_master_proc);
+
+NFSD_VNET_DEFINE(SVCPOOL *, nfsrvd_pool);
+NFSD_VNET_DEFINE(int, nfsrv_numnfsd) = 0;
+NFSD_VNET_DEFINE(struct nfsv4lock, nfsd_suspend_lock);
+
+NFSD_VNET_DEFINE_STATIC(bool, nfsrvd_inited) = false;
+
 /*
  * NFS server system calls
  */
@@ -125,6 +131,7 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
 	u_int maxlen;
 #endif
 
+	NFSD_CURVNET_SET_QUIET(NFSD_TD_TO_VNET(curthread));
 	memset(&nd, 0, sizeof(nd));
 	if (rqst->rq_vers == NFS_VER2) {
 		if (rqst->rq_proc > NFSV2PROC_STATFS ||
@@ -169,7 +176,7 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
 	nd.nd_mreq = NULL;
 	nd.nd_cred = NULL;
 
-	if (nfs_privport != 0) {
+	if (NFSD_VNET(nfs_privport) != 0) {
 		/* Check if source port is privileged */
 		u_short port;
 		struct sockaddr *nam = nd.nd_nam;
@@ -261,17 +268,17 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
 		 * nfsv4root exports by nfsvno_v4rootexport().
 		 */
 		NFSLOCKV4ROOTMUTEX();
-		nfsv4_lock(&nfsd_suspend_lock, 0, NULL, NFSV4ROOTLOCKMUTEXPTR,
-		    NULL);
-		nfsv4_getref(&nfsd_suspend_lock, NULL, NFSV4ROOTLOCKMUTEXPTR,
-		    NULL);
+		nfsv4_lock(&NFSD_VNET(nfsd_suspend_lock), 0, NULL,
+		    NFSV4ROOTLOCKMUTEXPTR, NULL);
+		nfsv4_getref(&NFSD_VNET(nfsd_suspend_lock), NULL,
+		    NFSV4ROOTLOCKMUTEXPTR, NULL);
 		NFSUNLOCKV4ROOTMUTEX();
 
 		if ((nd.nd_flag & ND_NFSV4) != 0) {
 			nd.nd_repstat = nfsvno_v4rootexport(&nd);
 			if (nd.nd_repstat != 0) {
 				NFSLOCKV4ROOTMUTEX();
-				nfsv4_relref(&nfsd_suspend_lock);
+				nfsv4_relref(&NFSD_VNET(nfsd_suspend_lock));
 				NFSUNLOCKV4ROOTMUTEX();
 				svcerr_weakauth(rqst);
 				svc_freereq(rqst);
@@ -287,7 +294,7 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
 #endif
 		cacherep = nfs_proc(&nd, rqst->rq_xid, xprt, &rp);
 		NFSLOCKV4ROOTMUTEX();
-		nfsv4_relref(&nfsd_suspend_lock);
+		nfsv4_relref(&NFSD_VNET(nfsd_suspend_lock));
 		NFSUNLOCKV4ROOTMUTEX();
 	} else {
 		NFSMGET(nd.nd_mreq);
@@ -327,6 +334,7 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
 	svc_freereq(rqst);
 
 out:
+	NFSD_CURVNET_RESTORE();
 	td_softdep_cleanup(curthread);
 	NFSEXITCODE(0);
 }
@@ -440,7 +448,9 @@ nfssvc_loss(SVCXPRT *xprt)
 
 	ack = 0;
 	SVC_ACK(xprt, &ack);
+	NFSD_CURVNET_SET(NFSD_TD_TO_VNET(curthread));
 	nfsrc_trimcache(xprt->xp_sockref, ack, 1);
+	NFSD_CURVNET_RESTORE();
 }
 
 /*
@@ -467,26 +477,28 @@ nfsrvd_addsock(struct file *fp)
 	 * unexpectedly.
 	 */
 	if (so->so_type == SOCK_DGRAM)
-		xprt = svc_dg_create(nfsrvd_pool, so, 0, 0);
+		xprt = svc_dg_create(NFSD_VNET(nfsrvd_pool), so, 0, 0);
 	else
-		xprt = svc_vc_create(nfsrvd_pool, so, 0, 0);
+		xprt = svc_vc_create(NFSD_VNET(nfsrvd_pool), so, 0, 0);
 	if (xprt) {
 		fp->f_ops = &badfileops;
 		fp->f_data = NULL;
 		xprt->xp_sockref = ++sockref;
-		if (nfs_minvers == NFS_VER2)
+		if (NFSD_VNET(nfs_minvers) == NFS_VER2)
 			svc_reg(xprt, NFS_PROG, NFS_VER2, nfssvc_program,
 			    NULL);
-		if (nfs_minvers <= NFS_VER3 && nfs_maxvers >= NFS_VER3)
+		if (NFSD_VNET(nfs_minvers) <= NFS_VER3 &&
+		    NFSD_VNET(nfs_maxvers) >= NFS_VER3)
 			svc_reg(xprt, NFS_PROG, NFS_VER3, nfssvc_program,
 			    NULL);
-		if (nfs_maxvers >= NFS_VER4)
+		if (NFSD_VNET(nfs_maxvers) >= NFS_VER4)
 			svc_reg(xprt, NFS_PROG, NFS_VER4, nfssvc_program,
 			    NULL);
 		if (so->so_type == SOCK_STREAM)
 			svc_loss_reg(xprt, nfssvc_loss);
 		SVC_RELEASE(xprt);
-	}
+	} else
+		error = EPERM;
 
 out:
 	NFSEXITCODE(error);
@@ -518,13 +530,15 @@ nfsrvd_nfsd(struct thread *td, struct nfsd_nfsd_args *args)
 	 * use.
 	 */
 	NFSD_LOCK();
-	if (newnfs_numnfsd == 0) {
+	if (NFSD_VNET(nfsrv_numnfsd) == 0) {
+		nfsrvd_init(0);
 		nfsdev_time = time_second;
 		p = td->td_proc;
 		PROC_LOCK(p);
 		p->p_flag2 |= P2_AST_SU;
 		PROC_UNLOCK(p);
-		newnfs_numnfsd++;
+		newnfs_numnfsd++;	/* Total num for all vnets. */
+		NFSD_VNET(nfsrv_numnfsd)++;	/* Num for this vnet. */
 
 		NFSD_UNLOCK();
 		error = nfsrv_createdevids(args, td);
@@ -542,12 +556,15 @@ nfsrvd_nfsd(struct thread *td, struct nfsd_nfsd_args *args)
 				    NFS_VER4);
 
 				if (!ret2 || !ret3 || !ret4)
-					printf(
-					    "nfsd: can't register svc name\n");
+					printf("nfsd: can't register svc "
+					    "name %s jid:%d\n", principal,
+					    td->td_ucred->cr_prison->pr_id);
 			}
 
-			nfsrvd_pool->sp_minthreads = args->minthreads;
-			nfsrvd_pool->sp_maxthreads = args->maxthreads;
+			NFSD_VNET(nfsrvd_pool)->sp_minthreads =
+			    args->minthreads;
+			NFSD_VNET(nfsrvd_pool)->sp_maxthreads =
+			    args->maxthreads;
 				
 			/*
 			 * If this is a pNFS service, make Getattr do a
@@ -558,7 +575,7 @@ nfsrvd_nfsd(struct thread *td, struct nfsd_nfsd_args *args)
 				nfsv4_opflag[NFSV4OP_GETATTR].modifyfs = 1;
 			}
 
-			svc_run(nfsrvd_pool);
+			svc_run(NFSD_VNET(nfsrvd_pool));
 
 			/* Reset Getattr to not do a vn_start_write(). */
 			nfsrv_writerpc[NFSPROC_GETATTR] = 0;
@@ -572,6 +589,7 @@ nfsrvd_nfsd(struct thread *td, struct nfsd_nfsd_args *args)
 		}
 		NFSD_LOCK();
 		newnfs_numnfsd--;
+		NFSD_VNET(nfsrv_numnfsd)--;
 		nfsrvd_init(1);
 		PROC_LOCK(p);
 		p->p_flag2 &= ~P2_AST_SU;
@@ -596,21 +614,25 @@ nfsrvd_init(int terminating)
 	NFSD_LOCK_ASSERT();
 
 	if (terminating) {
-		nfsd_master_proc = NULL;
+		NFSD_VNET(nfsd_master_proc) = NULL;
 		NFSD_UNLOCK();
 		nfsrv_freealllayoutsanddevids();
 		nfsrv_freeallbackchannel_xprts();
-		svcpool_close(nfsrvd_pool);
+		svcpool_close(NFSD_VNET(nfsrvd_pool));
 		free(nfsrv_zeropnfsdat, M_TEMP);
 		nfsrv_zeropnfsdat = NULL;
 		NFSD_LOCK();
 	} else {
+		/* Initialize per-vnet globals once per vnet. */
+		if (NFSD_VNET(nfsrvd_inited))
+			return;
+		NFSD_VNET(nfsrvd_inited) = true;
 		NFSD_UNLOCK();
-		nfsrvd_pool = svcpool_create("nfsd",
+		NFSD_VNET(nfsrvd_pool) = svcpool_create("nfsd",
 		    SYSCTL_STATIC_CHILDREN(_vfs_nfsd));
-		nfsrvd_pool->sp_rcache = NULL;
-		nfsrvd_pool->sp_assign = fhanew_assign;
-		nfsrvd_pool->sp_done = fhanew_nd_complete;
+		NFSD_VNET(nfsrvd_pool)->sp_rcache = NULL;
+		NFSD_VNET(nfsrvd_pool)->sp_assign = fhanew_assign;
+		NFSD_VNET(nfsrvd_pool)->sp_done = fhanew_nd_complete;
 		NFSD_LOCK();
 	}
 }
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index 407987aa767f..a58df7b69420 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/callout.h>
 #include <sys/filio.h>
 #include <sys/hash.h>
+#include <sys/osd.h>
 #include <sys/sysctl.h>
 #include <nlm/nlm_prot.h>
 #include <nlm/nlm.h>
@@ -59,45 +60,57 @@ FEATURE(nfsd, "NFSv4 server");
 extern u_int32_t newnfs_true, newnfs_false, newnfs_xdrneg1;
 extern int nfsrv_useacl;
 extern int newnfs_numnfsd;
-extern struct mount nfsv4root_mnt;
-extern struct nfsrv_stablefirst nfsrv_stablefirst;
-extern SVCPOOL	*nfsrvd_pool;
-extern struct nfsv4lock nfsd_suspend_lock;
-extern struct nfsclienthashhead *nfsclienthash;
-extern struct nfslockhashhead *nfslockhash;
-extern struct nfssessionhash *nfssessionhash;
 extern int nfsrv_sessionhashsize;
 extern struct nfsstatsv1 nfsstatsv1;
 extern struct nfslayouthash *nfslayouthash;
 extern int nfsrv_layouthashsize;
 extern struct mtx nfsrv_dslock_mtx;
 extern int nfs_pnfsiothreads;
-extern struct nfsdontlisthead nfsrv_dontlisthead;
-extern volatile int nfsrv_dontlistlen;
 extern volatile int nfsrv_devidcnt;
 extern int nfsrv_maxpnfsmirror;
 extern uint32_t nfs_srvmaxio;
 extern int nfs_bufpackets;
 extern u_long sb_max_adj;
-struct vfsoptlist nfsv4root_opt, nfsv4root_newopt;
+
+NFSD_VNET_DECLARE(int, nfsrv_numnfsd);
+NFSD_VNET_DECLARE(struct nfsrv_stablefirst, nfsrv_stablefirst);
+NFSD_VNET_DECLARE(SVCPOOL *, nfsrvd_pool);
+NFSD_VNET_DECLARE(struct nfsclienthashhead *, nfsclienthash);
+NFSD_VNET_DECLARE(struct nfslockhashhead *, nfslockhash);
+NFSD_VNET_DECLARE(struct nfssessionhash *, nfssessionhash);
+NFSD_VNET_DECLARE(struct nfsv4lock, nfsd_suspend_lock);
+
 NFSDLOCKMUTEX;
 NFSSTATESPINLOCK;
-struct nfsrchash_bucket nfsrchash_table[NFSRVCACHE_HASHSIZE];
-struct nfsrchash_bucket nfsrcahash_table[NFSRVCACHE_HASHSIZE];
 struct mtx nfsrc_udpmtx;
 struct mtx nfs_v4root_mutex;
 struct mtx nfsrv_dontlistlock_mtx;
 struct mtx nfsrv_recalllock_mtx;
-struct nfsrvfh nfs_rootfh, nfs_pubfh;
-int nfs_pubfhset = 0, nfs_rootfhset = 0;
-struct proc *nfsd_master_proc = NULL;
+struct nfsrvfh nfs_pubfh;
+int nfs_pubfhset = 0;
 int nfsd_debuglevel = 0;
 static pid_t nfsd_master_pid = (pid_t)-1;
 static char nfsd_master_comm[MAXCOMLEN + 1];
 static struct timeval nfsd_master_start;
 static uint32_t nfsv4_sysid = 0;
 static fhandle_t zerofh;
-struct callout nfsd_callout;
+static int nfsrv_osd_jail_slot;
+
+NFSD_VNET_DEFINE(struct proc *, nfsd_master_proc) = NULL;
+NFSD_VNET_DEFINE(struct nfsrvhashhead *, nfsrvudphashtbl);
+NFSD_VNET_DEFINE(struct nfsrchash_bucket *, nfsrchash_table);
+NFSD_VNET_DEFINE(struct nfsrchash_bucket *, nfsrcahash_table);
+NFSD_VNET_DEFINE(struct nfsrvfh, nfs_rootfh);
+NFSD_VNET_DEFINE(int, nfs_rootfhset) = 0;
+NFSD_VNET_DEFINE(struct callout, nfsd_callout);
+NFSD_VNET_DEFINE(struct nfsstatsv1 *, nfsstatsv1_p);
+NFSD_VNET_DEFINE_STATIC(struct mount *, nfsv4root_mnt);
+NFSD_VNET_DEFINE_STATIC(struct vfsoptlist, nfsv4root_opt);
+NFSD_VNET_DEFINE_STATIC(struct vfsoptlist, nfsv4root_newopt);
+NFSD_VNET_DEFINE_STATIC(bool, nfsrv_suspend_nfsd) = false;
+NFSD_VNET_DEFINE_STATIC(bool, nfsrv_mntinited) = false;
+
+static void nfsrv_cleanup(struct prison *);
 
 static int nfssvc_srvcall(struct thread *, struct nfssvc_args *,
     struct ucred *);
@@ -108,7 +121,6 @@ static int nfs_commit_blks;
 static int nfs_commit_miss;
 extern int nfsrv_issuedelegs;
 extern int nfsrv_dolocallocks;
-extern int nfsd_enable_stringtouid;
 extern struct nfsdevicehead nfsrv_devidhead;
 
 static int nfsrv_createiovec(int, struct mbuf **, struct mbuf **,
@@ -168,8 +180,10 @@ SYSCTL_INT(_vfs_nfsd, OID_AUTO, enable_locallocks, CTLFLAG_RW,
     &nfsrv_dolocallocks, 0, "Enable nfsd to acquire local locks on files");
 SYSCTL_INT(_vfs_nfsd, OID_AUTO, debuglevel, CTLFLAG_RW, &nfsd_debuglevel,
     0, "Debug level for NFS server");
-SYSCTL_INT(_vfs_nfsd, OID_AUTO, enable_stringtouid, CTLFLAG_RW,
-    &nfsd_enable_stringtouid, 0, "Enable nfsd to accept numeric owner_names");
+NFSD_VNET_DECLARE(int, nfsd_enable_stringtouid);
+SYSCTL_INT(_vfs_nfsd, OID_AUTO, enable_stringtouid,
+    CTLFLAG_NFSD_VNET | CTLFLAG_RW, &NFSD_VNET_NAME(nfsd_enable_stringtouid),
+    0, "Enable nfsd to accept numeric owner_names");
 static int nfsrv_pnfsgetdsattr = 1;
 SYSCTL_INT(_vfs_nfsd, OID_AUTO, pnfsgetdsattr, CTLFLAG_RW,
     &nfsrv_pnfsgetdsattr, 0, "When set getattr gets DS attributes via RPC");
@@ -1025,7 +1039,7 @@ nfsvno_read(struct vnode *vp, off_t off, int cnt, struct ucred *cred,
 	nh = nfsrv_sequential_heuristic(uiop, vp);
 	ioflag |= nh->nh_seqcount << IO_SEQSHIFT;
 	/* XXX KDM make this more systematic? */
-	nfsstatsv1.srvbytes[NFSV4OP_READ] += uiop->uio_resid;
+	nfsstatsv1_p->srvbytes[NFSV4OP_READ] += uiop->uio_resid;
 	error = VOP_READ(vp, uiop, IO_NODELOCKED | ioflag, cred);
 	free(iv, M_TEMP);
 	if (error) {
@@ -1150,7 +1164,7 @@ nfsvno_write(struct vnode *vp, off_t off, int retlen, int *stable,
 	nh = nfsrv_sequential_heuristic(uiop, vp);
 	ioflags |= nh->nh_seqcount << IO_SEQSHIFT;
 	/* XXX KDM make this more systematic? */
-	nfsstatsv1.srvbytes[NFSV4OP_WRITE] += uiop->uio_resid;
+	nfsstatsv1_p->srvbytes[NFSV4OP_WRITE] += uiop->uio_resid;
 	error = VOP_WRITE(vp, uiop, ioflags, cred);
 	if (error == 0)
 		nh->nh_nextoff = uiop->uio_offset;
@@ -3263,7 +3277,7 @@ nfsvno_checkexp(struct mount *mp, struct sockaddr *nam, struct nfsexstuff *exp,
 	error = VFS_CHECKEXP(mp, nam, &exp->nes_exflag, credp,
 	    &exp->nes_numsecflavor, exp->nes_secflavors);
 	if (error) {
-		if (nfs_rootfhset) {
+		if (NFSD_VNET(nfs_rootfhset)) {
 			exp->nes_exflag = 0;
 			exp->nes_numsecflavor = 0;
 			error = 0;
@@ -3298,7 +3312,7 @@ nfsvno_fhtovp(struct mount *mp, fhandle_t *fhp, struct sockaddr *nam,
 		error = VFS_CHECKEXP(mp, nam, &exp->nes_exflag, credp,
 		    &exp->nes_numsecflavor, exp->nes_secflavors);
 		if (error) {
-			if (nfs_rootfhset) {
+			if (NFSD_VNET(nfs_rootfhset)) {
 				exp->nes_exflag = 0;
 				exp->nes_numsecflavor = 0;
 				error = 0;
@@ -3466,9 +3480,9 @@ nfsrv_v4rootexport(void *argp, struct ucred *cred, struct thread *p)
 	struct nameidata nd;
 	fhandle_t fh;
 
-	error = vfs_export(&nfsv4root_mnt, &nfsexargp->export);
+	error = vfs_export(NFSD_VNET(nfsv4root_mnt), &nfsexargp->export);
 	if ((nfsexargp->export.ex_flags & MNT_DELEXPORT) != 0)
-		nfs_rootfhset = 0;
+		NFSD_VNET(nfs_rootfhset) = 0;
 	else if (error == 0) {
 		if (nfsexargp->fspec == NULL) {
 			error = EPERM;
@@ -3484,11 +3498,11 @@ nfsrv_v4rootexport(void *argp, struct ucred *cred, struct thread *p)
 		error = nfsvno_getfh(nd.ni_vp, &fh, p);
 		vrele(nd.ni_vp);
 		if (!error) {
-			nfs_rootfh.nfsrvfh_len = NFSX_MYFH;
+			NFSD_VNET(nfs_rootfh).nfsrvfh_len = NFSX_MYFH;
 			NFSBCOPY((caddr_t)&fh,
-			    nfs_rootfh.nfsrvfh_data,
+			    NFSD_VNET(nfs_rootfh).nfsrvfh_data,
 			    sizeof (fhandle_t));
-			nfs_rootfhset = 1;
+			NFSD_VNET(nfs_rootfhset) = 1;
 		}
 	}
 
@@ -3524,29 +3538,53 @@ nfsrv_mallocmget_limit(void)
 void
 nfsd_mntinit(void)
 {
-	static int inited = 0;
 
-	if (inited)
+	NFSD_LOCK();
+	if (NFSD_VNET(nfsrv_mntinited)) {
+		NFSD_UNLOCK();
 		return;
-	inited = 1;
-	nfsv4root_mnt.mnt_flag = (MNT_RDONLY | MNT_EXPORTED);
-	TAILQ_INIT(&nfsv4root_mnt.mnt_nvnodelist);
-	TAILQ_INIT(&nfsv4root_mnt.mnt_lazyvnodelist);
-	nfsv4root_mnt.mnt_export = NULL;
-	TAILQ_INIT(&nfsv4root_opt);
-	TAILQ_INIT(&nfsv4root_newopt);
-	nfsv4root_mnt.mnt_opt = &nfsv4root_opt;
-	nfsv4root_mnt.mnt_optnew = &nfsv4root_newopt;
-	nfsv4root_mnt.mnt_nvnodelistsize = 0;
-	nfsv4root_mnt.mnt_lazyvnodelistsize = 0;
+	}
+	NFSD_VNET(nfsrv_mntinited) = true;
+	NFSD_UNLOCK();
+
+	if (curthread->td_ucred->cr_prison == &prison0)
+		NFSD_VNET(nfsstatsv1_p) = &nfsstatsv1;
+	else
+		NFSD_VNET(nfsstatsv1_p) = malloc(sizeof(struct nfsstatsv1),
+		    M_TEMP, M_WAITOK | M_ZERO);
+	nfsstatsv1_p->srvcache_tcppeak = 0;
+	nfsstatsv1_p->srvcache_size = 0;
+	NFSD_VNET(nfsv4root_mnt) = malloc(sizeof(struct mount), M_TEMP,
+	    M_WAITOK | M_ZERO);
+	NFSD_VNET(nfsv4root_mnt)->mnt_flag = (MNT_RDONLY | MNT_EXPORTED);
+	mtx_init(&NFSD_VNET(nfsv4root_mnt)->mnt_mtx, "nfs4mnt", NULL, MTX_DEF);
+	lockinit(&NFSD_VNET(nfsv4root_mnt)->mnt_explock, PVFS, "explock", 0, 0);
+	TAILQ_INIT(&NFSD_VNET(nfsv4root_mnt)->mnt_nvnodelist);
+	TAILQ_INIT(&NFSD_VNET(nfsv4root_mnt)->mnt_lazyvnodelist);
+	NFSD_VNET(nfsv4root_mnt)->mnt_export = NULL;
+	TAILQ_INIT(&NFSD_VNET(nfsv4root_opt));
+	TAILQ_INIT(&NFSD_VNET(nfsv4root_newopt));
+	NFSD_VNET(nfsv4root_mnt)->mnt_opt = &NFSD_VNET(nfsv4root_opt);
+	NFSD_VNET(nfsv4root_mnt)->mnt_optnew = &NFSD_VNET(nfsv4root_newopt);
+	NFSD_VNET(nfsv4root_mnt)->mnt_nvnodelistsize = 0;
+	NFSD_VNET(nfsv4root_mnt)->mnt_lazyvnodelistsize = 0;
+	callout_init(&NFSD_VNET(nfsd_callout), 1);
+
+	nfsrvd_initcache();
+	nfsd_init();
 }
 
 static void
 nfsd_timer(void *arg)
 {
-
-	nfsrv_servertimer();
-	callout_reset_sbt(&nfsd_callout, SBT_1S, SBT_1S, nfsd_timer, NULL, 0);
+	struct vnet *vnetp;
+
+	vnetp = (struct vnet *)arg;
+	NFSD_CURVNET_SET_QUIET(vnetp);
+	nfsrv_servertimer(vnetp);
+	callout_reset_sbt(&NFSD_VNET(nfsd_callout), SBT_1S, SBT_1S, nfsd_timer,
+	    arg, 0);
+	NFSD_CURVNET_RESTORE();
 }
 
 /*
@@ -3628,7 +3666,7 @@ nfsvno_v4rootexport(struct nfsrv_descript *nd)
 	int error = 0, numsecflavor, secflavors[MAXSECFLAVORS], i;
 	uint64_t exflags;
 
-	error = vfs_stdcheckexp(&nfsv4root_mnt, nd->nd_nam, &exflags,
+	error = vfs_stdcheckexp(NFSD_VNET(nfsv4root_mnt), nd->nd_nam, &exflags,
 	    &credanon, &numsecflavor, secflavors);
 	if (error) {
 		error = NFSERR_PROGUNAVAIL;
@@ -3661,6 +3699,18 @@ out:
 	return (error);
*** 1205 LINES SKIPPED ***