xref: /netbsd-src/sys/nfs/nfs_srvcache.c (revision b8817e4aed928ed0e10796459301187172baedc8)
1*b8817e4aScegger /*	$NetBSD: nfs_srvcache.c,v 1.45 2009/03/15 17:20:10 cegger Exp $	*/
2fccfa11aScgd 
361f28255Scgd /*
4cde1d475Smycroft  * Copyright (c) 1989, 1993
5cde1d475Smycroft  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Rick Macklem at The University of Guelph.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18aad01611Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  *
345ac7df1cSfvdl  *	@(#)nfs_srvcache.c	8.3 (Berkeley) 3/30/95
3561f28255Scgd  */
3661f28255Scgd 
3761f28255Scgd /*
3861f28255Scgd  * Reference: Chet Juszczak, "Improving the Performance and Correctness
3961f28255Scgd  *		of an NFS Server", in Proc. Winter 1989 USENIX Conference,
4061f28255Scgd  *		pages 53-63. San Diego, February 1989.
4161f28255Scgd  */
420ffad693Slukem 
430ffad693Slukem #include <sys/cdefs.h>
44*b8817e4aScegger __KERNEL_RCSID(0, "$NetBSD: nfs_srvcache.c,v 1.45 2009/03/15 17:20:10 cegger Exp $");
455c0c5dd0Sjonathan 
4695b048b5Smycroft #include <sys/param.h>
4795b048b5Smycroft #include <sys/vnode.h>
4805aaff39Syamt #include <sys/condvar.h>
4995b048b5Smycroft #include <sys/mount.h>
5095b048b5Smycroft #include <sys/kernel.h>
5195b048b5Smycroft #include <sys/systm.h>
5213278252Syamt #include <sys/lock.h>
53cde1d475Smycroft #include <sys/proc.h>
548ee3b648Syamt #include <sys/pool.h>
5595b048b5Smycroft #include <sys/mbuf.h>
5605aaff39Syamt #include <sys/mutex.h>
5795b048b5Smycroft #include <sys/socket.h>
5895b048b5Smycroft #include <sys/socketvar.h>
5961f28255Scgd 
6095b048b5Smycroft #include <netinet/in.h>
61cde1d475Smycroft #include <nfs/nfsm_subs.h>
62cde1d475Smycroft #include <nfs/rpcv2.h>
635ac7df1cSfvdl #include <nfs/nfsproto.h>
64cde1d475Smycroft #include <nfs/nfs.h>
65cde1d475Smycroft #include <nfs/nfsrvcache.h>
66e4c93ec8Schristos #include <nfs/nfs_var.h>
6761f28255Scgd 
685ac7df1cSfvdl extern struct nfsstats nfsstats;
699bd0cc2bSmatt extern const int nfsv2_procid[NFS_NPROCS];
70cde1d475Smycroft long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
718ee3b648Syamt struct pool nfs_reqcache_pool;
7224e91b07Sglass 
737b8734f3Smycroft #define	NFSRCHASH(xid) \
747b8734f3Smycroft 	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
757b8734f3Smycroft LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
767b8734f3Smycroft TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
7705aaff39Syamt kmutex_t nfsrv_reqcache_lock;
787b8734f3Smycroft u_long nfsrvhash;
7961f28255Scgd 
80cb2e7bafSyamt #if defined(MBUFTRACE)
81cb2e7bafSyamt static struct mowner nfsd_cache_mowner = MOWNER_INIT("nfsd", "cache");
82cb2e7bafSyamt #endif /* defined(MBUFTRACE) */
83cb2e7bafSyamt 
84cde1d475Smycroft #define	NETFAMILY(rp) \
8592ce8c6aSad 		(((rp)->rc_flags & RC_INETADDR) ? AF_INET : -1)
86cde1d475Smycroft 
8713278252Syamt static struct nfsrvcache *nfsrv_lookupcache(struct nfsrv_descript *nd);
8813278252Syamt static void nfsrv_unlockcache(struct nfsrvcache *rp);
8913278252Syamt 
90cde1d475Smycroft /*
91cde1d475Smycroft  * Static array that defines which nfs rpc's are nonidempotent
92cde1d475Smycroft  */
93522f5698Sjdolecek const int nonidempotent[NFS_NPROCS] = {
94b3667adaSthorpej 	false,	/* NULL */
95b3667adaSthorpej 	false,	/* GETATTR */
96b3667adaSthorpej 	true,	/* SETATTR */
97b3667adaSthorpej 	false,	/* LOOKUP */
98b3667adaSthorpej 	false,	/* ACCESS */
99b3667adaSthorpej 	false,	/* READLINK */
100b3667adaSthorpej 	false,	/* READ */
101b3667adaSthorpej 	true,	/* WRITE */
102b3667adaSthorpej 	true,	/* CREATE */
103b3667adaSthorpej 	true,	/* MKDIR */
104b3667adaSthorpej 	true,	/* SYMLINK */
105b3667adaSthorpej 	true,	/* MKNOD */
106b3667adaSthorpej 	true,	/* REMOVE */
107b3667adaSthorpej 	true,	/* RMDIR */
108b3667adaSthorpej 	true,	/* RENAME */
109b3667adaSthorpej 	true,	/* LINK */
110b3667adaSthorpej 	false,	/* READDIR */
111b3667adaSthorpej 	false,	/* READDIRPLUS */
112b3667adaSthorpej 	false,	/* FSSTAT */
113b3667adaSthorpej 	false,	/* FSINFO */
114b3667adaSthorpej 	false,	/* PATHCONF */
115b3667adaSthorpej 	false,	/* COMMIT */
116b3667adaSthorpej 	false,	/* NOOP */
117cde1d475Smycroft };
11861f28255Scgd 
11961f28255Scgd /* True iff the rpc reply is an nfs status ONLY! */
120522f5698Sjdolecek static const int nfsv2_repstat[NFS_NPROCS] = {
121b3667adaSthorpej 	false,	/* NULL */
122b3667adaSthorpej 	false,	/* GETATTR */
123b3667adaSthorpej 	false,	/* SETATTR */
124b3667adaSthorpej 	false,	/* NOOP */
125b3667adaSthorpej 	false,	/* LOOKUP */
126b3667adaSthorpej 	false,	/* READLINK */
127b3667adaSthorpej 	false,	/* READ */
128b3667adaSthorpej 	false,	/* Obsolete WRITECACHE */
129b3667adaSthorpej 	false,	/* WRITE */
130b3667adaSthorpej 	false,	/* CREATE */
131b3667adaSthorpej 	true,	/* REMOVE */
132b3667adaSthorpej 	true,	/* RENAME */
133b3667adaSthorpej 	true,	/* LINK */
134b3667adaSthorpej 	true,	/* SYMLINK */
135b3667adaSthorpej 	false,	/* MKDIR */
136b3667adaSthorpej 	true,	/* RMDIR */
137b3667adaSthorpej 	false,	/* READDIR */
138b3667adaSthorpej 	false,	/* STATFS */
13961f28255Scgd };
14061f28255Scgd 
1413be095f6Syamt static void
cleanentry(struct nfsrvcache * rp)1423be095f6Syamt cleanentry(struct nfsrvcache *rp)
1433be095f6Syamt {
1443be095f6Syamt 
1451ed3981cSyamt 	if ((rp->rc_flags & RC_REPMBUF) != 0) {
1463be095f6Syamt 		m_freem(rp->rc_reply);
1473be095f6Syamt 	}
1481ed3981cSyamt 	if ((rp->rc_flags & RC_NAM) != 0) {
1493be095f6Syamt 		m_free(rp->rc_nam);
1503be095f6Syamt 	}
1511ed3981cSyamt 	rp->rc_flags &= ~(RC_REPSTATUS|RC_REPMBUF);
1523be095f6Syamt }
1533be095f6Syamt 
15461f28255Scgd /*
15561f28255Scgd  * Initialize the server request cache list
15661f28255Scgd  */
157e4c93ec8Schristos void
nfsrv_initcache(void)158*b8817e4aScegger nfsrv_initcache(void)
15961f28255Scgd {
16061f28255Scgd 
1611ed3981cSyamt 	mutex_init(&nfsrv_reqcache_lock, MUTEX_DEFAULT, IPL_NONE);
162e071d39cSad 	nfsrvhashtbl = hashinit(desirednfsrvcache, HASH_LIST, true,
163e071d39cSad 	    &nfsrvhash);
1647b8734f3Smycroft 	TAILQ_INIT(&nfsrvlruhead);
1658ee3b648Syamt 	pool_init(&nfs_reqcache_pool, sizeof(struct nfsrvcache), 0, 0, 0,
16659d979c5Sad 	    "nfsreqcachepl", &pool_allocator_nointr, IPL_NONE);
167cb2e7bafSyamt 	MOWNER_ATTACH(&nfsd_cache_mowner);
16861f28255Scgd }
16961f28255Scgd 
17092ce8c6aSad void
nfsrv_finicache(void)171*b8817e4aScegger nfsrv_finicache(void)
17292ce8c6aSad {
17392ce8c6aSad 
17492ce8c6aSad 	nfsrv_cleancache();
17592ce8c6aSad 	KASSERT(TAILQ_EMPTY(&nfsrvlruhead));
17692ce8c6aSad 	pool_destroy(&nfs_reqcache_pool);
17792ce8c6aSad 	hashdone(nfsrvhashtbl, HASH_LIST, nfsrvhash);
17892ce8c6aSad 	MOWNER_DETACH(&nfsd_cache_mowner);
17992ce8c6aSad 	mutex_destroy(&nfsrv_reqcache_lock);
18092ce8c6aSad }
18192ce8c6aSad 
18261f28255Scgd /*
18313278252Syamt  * Lookup a cache and lock it
18413278252Syamt  */
18513278252Syamt static struct nfsrvcache *
nfsrv_lookupcache(struct nfsrv_descript * nd)186454af1c0Sdsl nfsrv_lookupcache(struct nfsrv_descript *nd)
18713278252Syamt {
18813278252Syamt 	struct nfsrvcache *rp;
18913278252Syamt 
19005aaff39Syamt 	KASSERT(mutex_owned(&nfsrv_reqcache_lock));
19113278252Syamt 
19213278252Syamt loop:
19313278252Syamt 	LIST_FOREACH(rp, NFSRCHASH(nd->nd_retxid), rc_hash) {
19413278252Syamt 		if (nd->nd_retxid == rp->rc_xid &&
19513278252Syamt 		    nd->nd_procnum == rp->rc_proc &&
19613278252Syamt 		    netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
1971ed3981cSyamt 			if ((rp->rc_gflags & RC_G_LOCKED) != 0) {
19805aaff39Syamt 				cv_wait(&rp->rc_cv, &nfsrv_reqcache_lock);
19913278252Syamt 				goto loop;
20013278252Syamt 			}
2011ed3981cSyamt 			rp->rc_gflags |= RC_G_LOCKED;
20213278252Syamt 			break;
20313278252Syamt 		}
20413278252Syamt 	}
20513278252Syamt 
20613278252Syamt 	return rp;
20713278252Syamt }
20813278252Syamt 
20913278252Syamt /*
21013278252Syamt  * Unlock a cache
21113278252Syamt  */
21213278252Syamt static void
nfsrv_unlockcache(struct nfsrvcache * rp)213454af1c0Sdsl nfsrv_unlockcache(struct nfsrvcache *rp)
21413278252Syamt {
21513278252Syamt 
21605aaff39Syamt 	KASSERT(mutex_owned(&nfsrv_reqcache_lock));
21713278252Syamt 
2181ed3981cSyamt 	KASSERT((rp->rc_gflags & RC_G_LOCKED) != 0);
2191ed3981cSyamt 	rp->rc_gflags &= ~RC_G_LOCKED;
22005aaff39Syamt 	cv_broadcast(&rp->rc_cv);
22113278252Syamt }
22213278252Syamt 
22313278252Syamt /*
22461f28255Scgd  * Look for the request in the cache
22561f28255Scgd  * If found then
22661f28255Scgd  *    return action and optionally reply
22761f28255Scgd  * else
22861f28255Scgd  *    insert it in the cache
22961f28255Scgd  *
23061f28255Scgd  * The rules are as follows:
23161f28255Scgd  * - if in progress, return DROP request
23261f28255Scgd  * - if completed within DELAY of the current time, return DROP it
23361f28255Scgd  * - if completed a longer time ago return REPLY if the reply was cached or
23461f28255Scgd  *   return DOIT
23561f28255Scgd  * Update/add new request at end of lru list
23661f28255Scgd  */
237e4c93ec8Schristos int
nfsrv_getcache(struct nfsrv_descript * nd,struct nfssvc_sock * slp,struct mbuf ** repp)238454af1c0Sdsl nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp, struct mbuf **repp)
23961f28255Scgd {
2400b47e1b2Syamt 	struct nfsrvcache *rp, *rpdup;
24161f28255Scgd 	struct mbuf *mb;
242cde1d475Smycroft 	struct sockaddr_in *saddr;
24353524e44Schristos 	char *bpos;
24461f28255Scgd 	int ret;
24561f28255Scgd 
24605aaff39Syamt 	mutex_enter(&nfsrv_reqcache_lock);
24713278252Syamt 	rp = nfsrv_lookupcache(nd);
24813278252Syamt 	if (rp) {
24905aaff39Syamt 		mutex_exit(&nfsrv_reqcache_lock);
25013278252Syamt found:
251cde1d475Smycroft 		/* If not at end of LRU chain, move it there */
25213278252Syamt 		if (TAILQ_NEXT(rp, rc_lru)) { /* racy but ok */
25305aaff39Syamt 			mutex_enter(&nfsrv_reqcache_lock);
2547b8734f3Smycroft 			TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
2557b8734f3Smycroft 			TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
25605aaff39Syamt 			mutex_exit(&nfsrv_reqcache_lock);
257cde1d475Smycroft 		}
25861f28255Scgd 		if (rp->rc_state == RC_UNUSED)
25961f28255Scgd 			panic("nfsrv cache");
260cde1d475Smycroft 		if (rp->rc_state == RC_INPROG) {
26161f28255Scgd 			nfsstats.srvcache_inproghits++;
26261f28255Scgd 			ret = RC_DROPIT;
2631ed3981cSyamt 		} else if (rp->rc_flags & RC_REPSTATUS) {
264cde1d475Smycroft 			nfsstats.srvcache_nonidemdonehits++;
2655ac7df1cSfvdl 			nfs_rephead(0, nd, slp, rp->rc_status,
266cde1d475Smycroft 			   0, (u_quad_t *)0, repp, &mb, &bpos);
26761f28255Scgd 			ret = RC_REPLY;
2681ed3981cSyamt 		} else if (rp->rc_flags & RC_REPMBUF) {
269cde1d475Smycroft 			nfsstats.srvcache_nonidemdonehits++;
27061f28255Scgd 			*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
27161f28255Scgd 					M_WAIT);
27261f28255Scgd 			ret = RC_REPLY;
27361f28255Scgd 		} else {
274cde1d475Smycroft 			nfsstats.srvcache_idemdonehits++;
27561f28255Scgd 			rp->rc_state = RC_INPROG;
27661f28255Scgd 			ret = RC_DOIT;
27761f28255Scgd 		}
27805aaff39Syamt 		mutex_enter(&nfsrv_reqcache_lock);
27913278252Syamt 		nfsrv_unlockcache(rp);
28005aaff39Syamt 		mutex_exit(&nfsrv_reqcache_lock);
281a055ad0fSyamt 		return ret;
28261f28255Scgd 	}
28361f28255Scgd 	nfsstats.srvcache_misses++;
284cde1d475Smycroft 	if (numnfsrvcache < desirednfsrvcache) {
285cde1d475Smycroft 		numnfsrvcache++;
28605aaff39Syamt 		mutex_exit(&nfsrv_reqcache_lock);
28713278252Syamt 		rp = pool_get(&nfs_reqcache_pool, PR_WAITOK);
28813278252Syamt 		memset(rp, 0, sizeof *rp);
28905aaff39Syamt 		cv_init(&rp->rc_cv, "nfsdrc");
2901ed3981cSyamt 		rp->rc_gflags = RC_G_LOCKED;
291cde1d475Smycroft 	} else {
292a14f4443Syamt 		rp = TAILQ_FIRST(&nfsrvlruhead);
2931ed3981cSyamt 		while ((rp->rc_gflags & RC_G_LOCKED) != 0) {
29405aaff39Syamt 			cv_wait(&rp->rc_cv, &nfsrv_reqcache_lock);
295a14f4443Syamt 			rp = TAILQ_FIRST(&nfsrvlruhead);
29661f28255Scgd 		}
2971ed3981cSyamt 		rp->rc_gflags |= RC_G_LOCKED;
2987b8734f3Smycroft 		LIST_REMOVE(rp, rc_hash);
2997b8734f3Smycroft 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
30005aaff39Syamt 		mutex_exit(&nfsrv_reqcache_lock);
3013be095f6Syamt 		cleanentry(rp);
3021ed3981cSyamt 		rp->rc_flags = 0;
303cde1d475Smycroft 	}
30461f28255Scgd 	rp->rc_state = RC_INPROG;
305cde1d475Smycroft 	rp->rc_xid = nd->nd_retxid;
3065ac7df1cSfvdl 	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
307cde1d475Smycroft 	switch (saddr->sin_family) {
308cde1d475Smycroft 	case AF_INET:
3091ed3981cSyamt 		rp->rc_flags |= RC_INETADDR;
310cde1d475Smycroft 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
311cde1d475Smycroft 		break;
312cde1d475Smycroft 	default:
3131ed3981cSyamt 		rp->rc_flags |= RC_NAM;
3145ac7df1cSfvdl 		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
315cb2e7bafSyamt 		m_claimm(rp->rc_nam, &nfsd_cache_mowner);
316cde1d475Smycroft 		break;
317cde1d475Smycroft 	};
318cde1d475Smycroft 	rp->rc_proc = nd->nd_procnum;
31905aaff39Syamt 	mutex_enter(&nfsrv_reqcache_lock);
3200b47e1b2Syamt 	rpdup = nfsrv_lookupcache(nd);
3210b47e1b2Syamt 	if (rpdup != NULL) {
32213278252Syamt 		/*
32313278252Syamt 		 * other thread made duplicate cache entry.
32413278252Syamt 		 */
3251ed3981cSyamt 		KASSERT(numnfsrvcache > 0);
3261ed3981cSyamt 		numnfsrvcache--;
32705aaff39Syamt 		mutex_exit(&nfsrv_reqcache_lock);
3281ed3981cSyamt 		cleanentry(rp);
32905aaff39Syamt 		cv_destroy(&rp->rc_cv);
33013278252Syamt 		pool_put(&nfs_reqcache_pool, rp);
3310b47e1b2Syamt 		rp = rpdup;
33213278252Syamt 		goto found;
333cde1d475Smycroft 	}
33413278252Syamt 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
33513278252Syamt 	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
33613278252Syamt 	nfsrv_unlockcache(rp);
33705aaff39Syamt 	mutex_exit(&nfsrv_reqcache_lock);
338a055ad0fSyamt 	return RC_DOIT;
33961f28255Scgd }
34061f28255Scgd 
34161f28255Scgd /*
34261f28255Scgd  * Update a request cache entry after the rpc has been done
34361f28255Scgd  */
344cde1d475Smycroft void
nfsrv_updatecache(struct nfsrv_descript * nd,int repvalid,struct mbuf * repmbuf)345454af1c0Sdsl nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf)
34661f28255Scgd {
3478529438fSaugustss 	struct nfsrvcache *rp;
34861f28255Scgd 
34905aaff39Syamt 	mutex_enter(&nfsrv_reqcache_lock);
35013278252Syamt 	rp = nfsrv_lookupcache(nd);
35105aaff39Syamt 	mutex_exit(&nfsrv_reqcache_lock);
35213278252Syamt 	if (rp) {
3533be095f6Syamt 		cleanentry(rp);
35461f28255Scgd 		rp->rc_state = RC_DONE;
35561f28255Scgd 		/*
35661f28255Scgd 		 * If we have a valid reply update status and save
35761f28255Scgd 		 * the reply for non-idempotent rpc's.
35861f28255Scgd 		 */
359cde1d475Smycroft 		if (repvalid && nonidempotent[nd->nd_procnum]) {
3605ac7df1cSfvdl 			if ((nd->nd_flag & ND_NFSV3) == 0 &&
3615ac7df1cSfvdl 			  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
362cde1d475Smycroft 				rp->rc_status = nd->nd_repstat;
3631ed3981cSyamt 				rp->rc_flags |= RC_REPSTATUS;
36461f28255Scgd 			} else {
36561f28255Scgd 				rp->rc_reply = m_copym(repmbuf,
36661f28255Scgd 					0, M_COPYALL, M_WAIT);
367cb2e7bafSyamt 				m_claimm(rp->rc_reply, &nfsd_cache_mowner);
3681ed3981cSyamt 				rp->rc_flags |= RC_REPMBUF;
36961f28255Scgd 			}
37061f28255Scgd 		}
37105aaff39Syamt 		mutex_enter(&nfsrv_reqcache_lock);
37213278252Syamt 		nfsrv_unlockcache(rp);
37305aaff39Syamt 		mutex_exit(&nfsrv_reqcache_lock);
37461f28255Scgd 	}
37561f28255Scgd }
376cde1d475Smycroft 
377cde1d475Smycroft /*
378cde1d475Smycroft  * Clean out the cache. Called when the last nfsd terminates.
379cde1d475Smycroft  */
380cde1d475Smycroft void
nfsrv_cleancache(void)381*b8817e4aScegger nfsrv_cleancache(void)
382cde1d475Smycroft {
3831ed3981cSyamt 	struct nfsrvcache *rp;
384cde1d475Smycroft 
38505aaff39Syamt 	mutex_enter(&nfsrv_reqcache_lock);
3861ed3981cSyamt 	while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) {
3871ed3981cSyamt 		KASSERT((rp->rc_gflags & RC_G_LOCKED) == 0);
3887b8734f3Smycroft 		LIST_REMOVE(rp, rc_hash);
3897b8734f3Smycroft 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
3901ed3981cSyamt 		KASSERT(numnfsrvcache > 0);
3911ed3981cSyamt 		numnfsrvcache--;
3921ed3981cSyamt 		mutex_exit(&nfsrv_reqcache_lock);
3933be095f6Syamt 		cleanentry(rp);
39405aaff39Syamt 		cv_destroy(&rp->rc_cv);
3958ee3b648Syamt 		pool_put(&nfs_reqcache_pool, rp);
3961ed3981cSyamt 		mutex_enter(&nfsrv_reqcache_lock);
397cde1d475Smycroft 	}
3981ed3981cSyamt 	KASSERT(numnfsrvcache == 0);
39905aaff39Syamt 	mutex_exit(&nfsrv_reqcache_lock);
400cde1d475Smycroft }
401