xref: /csrg-svn/sys/nfs/nfs_srvcache.c (revision 55520)
139742Smckusick /*
239742Smckusick  * Copyright (c) 1989 The Regents of the University of California.
339742Smckusick  * All rights reserved.
439742Smckusick  *
539742Smckusick  * This code is derived from software contributed to Berkeley by
639742Smckusick  * Rick Macklem at The University of Guelph.
739742Smckusick  *
844512Sbostic  * %sccs.include.redist.c%
939742Smckusick  *
10*55520Smckusick  *	@(#)nfs_srvcache.c	7.16 (Berkeley) 07/22/92
1139742Smckusick  */
1239742Smckusick 
1343431Smckusick /*
1443431Smckusick  * Reference: Chet Juszczak, "Improving the Performance and Correctness
1552196Smckusick  *		of an NFS Server", in Proc. Winter 1989 USENIX Conference,
1652196Smckusick  *		pages 53-63. San Diego, February 1989.
1743431Smckusick  */
1854874Smckusick #include <sys/param.h>
1954874Smckusick #include <sys/vnode.h>
2054874Smckusick #include <sys/mount.h>
2154874Smckusick #include <sys/kernel.h>
2254874Smckusick #include <sys/systm.h>
2354874Smckusick #include <sys/proc.h>
2454874Smckusick #include <sys/mbuf.h>
25*55520Smckusick #include <sys/malloc.h>
2654874Smckusick #include <sys/socket.h>
2754874Smckusick #include <sys/socketvar.h>
2854874Smckusick #include <netinet/in.h>
2952196Smckusick #ifdef ISO
3054874Smckusick #include <netiso/iso.h>
3152196Smckusick #endif
3254874Smckusick #include <nfs/nfsm_subs.h>
3354874Smckusick #include <nfs/rpcv2.h>
3454874Smckusick #include <nfs/nfsv2.h>
3554874Smckusick #include <nfs/nfs.h>
3654874Smckusick #include <nfs/nfsrvcache.h>
3754874Smckusick #include <nfs/nqnfs.h>
3839742Smckusick 
39*55520Smckusick long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
4039742Smckusick 
41*55520Smckusick #define	NFSRCHASH(xid)		(((xid) + ((xid) >> 16)) & rheadhash)
42*55520Smckusick static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead;
43*55520Smckusick static struct nfsrvcache **rheadhtbl;
44*55520Smckusick static u_long rheadhash;
4539742Smckusick 
4639742Smckusick #define TRUE	1
4739742Smckusick #define	FALSE	0
4839742Smckusick 
4952196Smckusick #define	NETFAMILY(rp) \
5052196Smckusick 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
5152196Smckusick 
5239742Smckusick /*
5339742Smckusick  * Static array that defines which nfs rpc's are nonidempotent
5439742Smckusick  */
5540118Smckusick int nonidempotent[NFS_NPROCS] = {
5639742Smckusick 	FALSE,
5739742Smckusick 	FALSE,
5839742Smckusick 	TRUE,
5939742Smckusick 	FALSE,
6039742Smckusick 	FALSE,
6139742Smckusick 	FALSE,
6239742Smckusick 	FALSE,
6339742Smckusick 	FALSE,
6439742Smckusick 	TRUE,
6539742Smckusick 	TRUE,
6639742Smckusick 	TRUE,
6739742Smckusick 	TRUE,
6839742Smckusick 	TRUE,
6939742Smckusick 	TRUE,
7039742Smckusick 	TRUE,
7139742Smckusick 	TRUE,
7239742Smckusick 	FALSE,
7339742Smckusick 	FALSE,
7452196Smckusick 	FALSE,
7552196Smckusick 	FALSE,
7652196Smckusick 	FALSE,
7752196Smckusick 	FALSE,
7839742Smckusick };
7939742Smckusick 
8039742Smckusick /* True iff the rpc reply is an nfs status ONLY! */
8139742Smckusick static int repliesstatus[NFS_NPROCS] = {
8239742Smckusick 	FALSE,
8339742Smckusick 	FALSE,
8439742Smckusick 	FALSE,
8539742Smckusick 	FALSE,
8639742Smckusick 	FALSE,
8739742Smckusick 	FALSE,
8839742Smckusick 	FALSE,
8939742Smckusick 	FALSE,
9039742Smckusick 	FALSE,
9139742Smckusick 	FALSE,
9239742Smckusick 	TRUE,
9339742Smckusick 	TRUE,
9439742Smckusick 	TRUE,
9539742Smckusick 	TRUE,
9639742Smckusick 	FALSE,
9739742Smckusick 	TRUE,
9839742Smckusick 	FALSE,
9939742Smckusick 	FALSE,
10052196Smckusick 	FALSE,
10152196Smckusick 	FALSE,
10252196Smckusick 	FALSE,
10352196Smckusick 	FALSE,
10439742Smckusick };
10539742Smckusick 
10639742Smckusick /*
10739742Smckusick  * Initialize the server request cache list
10839742Smckusick  */
10939742Smckusick nfsrv_initcache()
11039742Smckusick {
11139742Smckusick 
112*55520Smckusick 	rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash);
11339742Smckusick }
11439742Smckusick 
11539742Smckusick /*
11639742Smckusick  * Look for the request in the cache
11739742Smckusick  * If found then
11839742Smckusick  *    return action and optionally reply
11939742Smckusick  * else
12039742Smckusick  *    insert it in the cache
12139742Smckusick  *
12239742Smckusick  * The rules are as follows:
12339742Smckusick  * - if in progress, return DROP request
12439742Smckusick  * - if completed within DELAY of the current time, return DROP it
12539742Smckusick  * - if completed a longer time ago return REPLY if the reply was cached or
12639742Smckusick  *   return DOIT
12739742Smckusick  * Update/add new request at end of lru list
12839742Smckusick  */
12952196Smckusick nfsrv_getcache(nam, nd, repp)
13039742Smckusick 	struct mbuf *nam;
13152196Smckusick 	register struct nfsd *nd;
13239742Smckusick 	struct mbuf **repp;
13339742Smckusick {
134*55520Smckusick 	register struct nfsrvcache *rp, *rq, **rpp;
13539742Smckusick 	struct mbuf *mb;
13652196Smckusick 	struct sockaddr_in *saddr;
13739742Smckusick 	caddr_t bpos;
13839742Smckusick 	int ret;
13939742Smckusick 
14052196Smckusick 	if (nd->nd_nqlflag != NQL_NOVAL)
14152196Smckusick 		return (RC_DOIT);
142*55520Smckusick 	rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)];
14339742Smckusick loop:
144*55520Smckusick 	for (rp = *rpp; rp; rp = rp->rc_forw) {
14552196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
14654613Smckusick 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) {
14739742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
14839742Smckusick 				rp->rc_flag |= RC_WANTED;
14943352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
15039742Smckusick 				goto loop;
15139742Smckusick 			}
15239742Smckusick 			rp->rc_flag |= RC_LOCKED;
153*55520Smckusick 			/* If not at end of LRU chain, move it there */
154*55520Smckusick 			if (rp->rc_next) {
155*55520Smckusick 				/* remove from LRU chain */
156*55520Smckusick 				*rp->rc_prev = rp->rc_next;
15752196Smckusick 				rp->rc_next->rc_prev = rp->rc_prev;
158*55520Smckusick 				/* and replace at end of it */
159*55520Smckusick 				rp->rc_next = NULL;
160*55520Smckusick 				rp->rc_prev = nfsrvlrutail;
161*55520Smckusick 				*nfsrvlrutail = rp;
162*55520Smckusick 				nfsrvlrutail = &rp->rc_next;
16352196Smckusick 			}
16439742Smckusick 			if (rp->rc_state == RC_UNUSED)
16539742Smckusick 				panic("nfsrv cache");
16639742Smckusick 			if (rp->rc_state == RC_INPROG ||
16739742Smckusick 			   (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
16839742Smckusick 				nfsstats.srvcache_inproghits++;
16939742Smckusick 				ret = RC_DROPIT;
17039742Smckusick 			} else if (rp->rc_flag & RC_REPSTATUS) {
17139742Smckusick 				nfsstats.srvcache_idemdonehits++;
17252196Smckusick 				nfs_rephead(0, nd, rp->rc_status,
17352196Smckusick 				   0, (u_quad_t *)0, repp, &mb, &bpos);
17439742Smckusick 				rp->rc_timestamp = time.tv_sec;
17539742Smckusick 				ret = RC_REPLY;
17639742Smckusick 			} else if (rp->rc_flag & RC_REPMBUF) {
17739742Smckusick 				nfsstats.srvcache_idemdonehits++;
17841901Smckusick 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
17939742Smckusick 						M_WAIT);
18039742Smckusick 				rp->rc_timestamp = time.tv_sec;
18139742Smckusick 				ret = RC_REPLY;
18239742Smckusick 			} else {
18339742Smckusick 				nfsstats.srvcache_nonidemdonehits++;
18439742Smckusick 				rp->rc_state = RC_INPROG;
18539742Smckusick 				ret = RC_DOIT;
18639742Smckusick 			}
18739742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
18839742Smckusick 			if (rp->rc_flag & RC_WANTED) {
18939742Smckusick 				rp->rc_flag &= ~RC_WANTED;
19039742Smckusick 				wakeup((caddr_t)rp);
19139742Smckusick 			}
19239742Smckusick 			return (ret);
19339742Smckusick 		}
19439742Smckusick 	}
19539742Smckusick 	nfsstats.srvcache_misses++;
196*55520Smckusick 	if (numnfsrvcache < desirednfsrvcache) {
197*55520Smckusick 		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
198*55520Smckusick 		    M_NFSD, M_WAITOK);
199*55520Smckusick 		bzero((char *)rp, sizeof *rp);
200*55520Smckusick 		numnfsrvcache++;
201*55520Smckusick 		rp->rc_flag = RC_LOCKED;
202*55520Smckusick 	} else {
203*55520Smckusick 		rp = nfsrvlruhead;
204*55520Smckusick 		while ((rp->rc_flag & RC_LOCKED) != 0) {
205*55520Smckusick 			rp->rc_flag |= RC_WANTED;
206*55520Smckusick 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
207*55520Smckusick 			rp = nfsrvlruhead;
208*55520Smckusick 		}
209*55520Smckusick 		rp->rc_flag |= RC_LOCKED;
210*55520Smckusick 		/* remove from hash chain */
211*55520Smckusick 		if (rq = rp->rc_forw)
212*55520Smckusick 			rq->rc_back = rp->rc_back;
213*55520Smckusick 		*rp->rc_back = rq;
214*55520Smckusick 		/* remove from LRU chain */
215*55520Smckusick 		*rp->rc_prev = rp->rc_next;
21652196Smckusick 		rp->rc_next->rc_prev = rp->rc_prev;
217*55520Smckusick 		if (rp->rc_flag & RC_REPMBUF)
218*55520Smckusick 			m_freem(rp->rc_reply);
219*55520Smckusick 		if (rp->rc_flag & RC_NAM)
220*55520Smckusick 			MFREE(rp->rc_nam, mb);
221*55520Smckusick 		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
22252196Smckusick 	}
223*55520Smckusick 	/* place at end of LRU list */
224*55520Smckusick 	rp->rc_next = NULL;
225*55520Smckusick 	rp->rc_prev = nfsrvlrutail;
226*55520Smckusick 	*nfsrvlrutail = rp;
227*55520Smckusick 	nfsrvlrutail = &rp->rc_next;
22839742Smckusick 	rp->rc_state = RC_INPROG;
22952196Smckusick 	rp->rc_xid = nd->nd_retxid;
23052196Smckusick 	saddr = mtod(nam, struct sockaddr_in *);
23152196Smckusick 	switch (saddr->sin_family) {
23252196Smckusick 	case AF_INET:
23352196Smckusick 		rp->rc_flag |= RC_INETADDR;
23452196Smckusick 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
23552196Smckusick 		break;
23652196Smckusick 	case AF_ISO:
23752196Smckusick 	default:
23852196Smckusick 		rp->rc_flag |= RC_NAM;
23952196Smckusick 		rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
24052196Smckusick 		break;
24152196Smckusick 	};
24252196Smckusick 	rp->rc_proc = nd->nd_procnum;
243*55520Smckusick 	/* insert into hash chain */
244*55520Smckusick 	if (rq = *rpp)
245*55520Smckusick 		rq->rc_back = &rp->rc_forw;
246*55520Smckusick 	rp->rc_next = rq;
247*55520Smckusick 	rp->rc_back = rpp;
248*55520Smckusick 	*rpp = rp;
24952196Smckusick 	rp->rc_flag &= ~RC_LOCKED;
25052196Smckusick 	if (rp->rc_flag & RC_WANTED) {
25152196Smckusick 		rp->rc_flag &= ~RC_WANTED;
25252196Smckusick 		wakeup((caddr_t)rp);
25352196Smckusick 	}
25439742Smckusick 	return (RC_DOIT);
25539742Smckusick }
25639742Smckusick 
25739742Smckusick /*
25839742Smckusick  * Update a request cache entry after the rpc has been done
25939742Smckusick  */
26052196Smckusick void
26152196Smckusick nfsrv_updatecache(nam, nd, repvalid, repmbuf)
26239742Smckusick 	struct mbuf *nam;
26352196Smckusick 	register struct nfsd *nd;
26440253Smckusick 	int repvalid;
26539742Smckusick 	struct mbuf *repmbuf;
26639742Smckusick {
26739742Smckusick 	register struct nfsrvcache *rp;
26839742Smckusick 
26952196Smckusick 	if (nd->nd_nqlflag != NQL_NOVAL)
27052196Smckusick 		return;
27139742Smckusick loop:
272*55520Smckusick 	for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) {
27352196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
27454613Smckusick 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) {
27539742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
27639742Smckusick 				rp->rc_flag |= RC_WANTED;
27743352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
27839742Smckusick 				goto loop;
27939742Smckusick 			}
28039742Smckusick 			rp->rc_flag |= RC_LOCKED;
28139742Smckusick 			rp->rc_state = RC_DONE;
28240253Smckusick 			/*
28340253Smckusick 			 * If we have a valid reply update status and save
28441901Smckusick 			 * the reply for non-idempotent rpc's.
28541901Smckusick 			 * Otherwise invalidate entry by setting the timestamp
28641901Smckusick 			 * to nil.
28740253Smckusick 			 */
28840253Smckusick 			if (repvalid) {
28940253Smckusick 				rp->rc_timestamp = time.tv_sec;
29052196Smckusick 				if (nonidempotent[nd->nd_procnum]) {
29152196Smckusick 					if (repliesstatus[nd->nd_procnum]) {
29252196Smckusick 						rp->rc_status = nd->nd_repstat;
29340253Smckusick 						rp->rc_flag |= RC_REPSTATUS;
29440253Smckusick 					} else {
29541901Smckusick 						rp->rc_reply = m_copym(repmbuf,
29640253Smckusick 							0, M_COPYALL, M_WAIT);
29740253Smckusick 						rp->rc_flag |= RC_REPMBUF;
29840253Smckusick 					}
29939742Smckusick 				}
30040253Smckusick 			} else {
30140253Smckusick 				rp->rc_timestamp = 0;
30239742Smckusick 			}
30339742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
30439742Smckusick 			if (rp->rc_flag & RC_WANTED) {
30539742Smckusick 				rp->rc_flag &= ~RC_WANTED;
30639742Smckusick 				wakeup((caddr_t)rp);
30739742Smckusick 			}
30839742Smckusick 			return;
30939742Smckusick 		}
31039742Smckusick 	}
31139742Smckusick }
31252196Smckusick 
31352196Smckusick /*
31452196Smckusick  * Clean out the cache. Called when the last nfsd terminates.
31552196Smckusick  */
31652196Smckusick void
31752196Smckusick nfsrv_cleancache()
31852196Smckusick {
319*55520Smckusick 	register struct nfsrvcache *rp, *nextrp;
32052196Smckusick 
321*55520Smckusick 	for (rp = nfsrvlruhead; rp; rp = nextrp) {
322*55520Smckusick 		nextrp = rp->rc_next;
323*55520Smckusick 		free(rp, M_NFSD);
32452196Smckusick 	}
325*55520Smckusick 	bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *));
326*55520Smckusick 	numnfsrvcache = 0;
32752196Smckusick }
328