xref: /csrg-svn/sys/nfs/nfs_srvcache.c (revision 58365)
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*58365Smckusick  *	@(#)nfs_srvcache.c	7.20 (Berkeley) 03/02/93
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>
2555520Smckusick #include <sys/malloc.h>
2654874Smckusick #include <sys/socket.h>
2754874Smckusick #include <sys/socketvar.h>
2856535Sbostic 
2954874Smckusick #include <netinet/in.h>
3052196Smckusick #ifdef ISO
3154874Smckusick #include <netiso/iso.h>
3252196Smckusick #endif
3354874Smckusick #include <nfs/nfsm_subs.h>
3454874Smckusick #include <nfs/rpcv2.h>
3554874Smckusick #include <nfs/nfsv2.h>
3654874Smckusick #include <nfs/nfs.h>
3754874Smckusick #include <nfs/nfsrvcache.h>
3854874Smckusick #include <nfs/nqnfs.h>
3939742Smckusick 
4055520Smckusick long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
4139742Smckusick 
4255520Smckusick #define	NFSRCHASH(xid)		(((xid) + ((xid) >> 16)) & rheadhash)
4355520Smckusick static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead;
4455520Smckusick static struct nfsrvcache **rheadhtbl;
4555520Smckusick static u_long rheadhash;
4639742Smckusick 
4739742Smckusick #define TRUE	1
4839742Smckusick #define	FALSE	0
4939742Smckusick 
5052196Smckusick #define	NETFAMILY(rp) \
5152196Smckusick 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
5252196Smckusick 
5339742Smckusick /*
5439742Smckusick  * Static array that defines which nfs rpc's are nonidempotent
5539742Smckusick  */
5640118Smckusick int nonidempotent[NFS_NPROCS] = {
5739742Smckusick 	FALSE,
5839742Smckusick 	FALSE,
5939742Smckusick 	TRUE,
6039742Smckusick 	FALSE,
6139742Smckusick 	FALSE,
6239742Smckusick 	FALSE,
6339742Smckusick 	FALSE,
6439742Smckusick 	FALSE,
6539742Smckusick 	TRUE,
6639742Smckusick 	TRUE,
6739742Smckusick 	TRUE,
6839742Smckusick 	TRUE,
6939742Smckusick 	TRUE,
7039742Smckusick 	TRUE,
7139742Smckusick 	TRUE,
7239742Smckusick 	TRUE,
7339742Smckusick 	FALSE,
7439742Smckusick 	FALSE,
7552196Smckusick 	FALSE,
7652196Smckusick 	FALSE,
7752196Smckusick 	FALSE,
7852196Smckusick 	FALSE,
7956362Smckusick 	FALSE,
8039742Smckusick };
8139742Smckusick 
8239742Smckusick /* True iff the rpc reply is an nfs status ONLY! */
8339742Smckusick static int repliesstatus[NFS_NPROCS] = {
8439742Smckusick 	FALSE,
8539742Smckusick 	FALSE,
8639742Smckusick 	FALSE,
8739742Smckusick 	FALSE,
8839742Smckusick 	FALSE,
8939742Smckusick 	FALSE,
9039742Smckusick 	FALSE,
9139742Smckusick 	FALSE,
9239742Smckusick 	FALSE,
9339742Smckusick 	FALSE,
9439742Smckusick 	TRUE,
9539742Smckusick 	TRUE,
9639742Smckusick 	TRUE,
9739742Smckusick 	TRUE,
9839742Smckusick 	FALSE,
9939742Smckusick 	TRUE,
10039742Smckusick 	FALSE,
10139742Smckusick 	FALSE,
10252196Smckusick 	FALSE,
10352196Smckusick 	FALSE,
10452196Smckusick 	FALSE,
10552196Smckusick 	FALSE,
10656362Smckusick 	TRUE,
10739742Smckusick };
10839742Smckusick 
10939742Smckusick /*
11039742Smckusick  * Initialize the server request cache list
11139742Smckusick  */
11239742Smckusick nfsrv_initcache()
11339742Smckusick {
11439742Smckusick 
11555520Smckusick 	rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash);
11639742Smckusick }
11739742Smckusick 
11839742Smckusick /*
11939742Smckusick  * Look for the request in the cache
12039742Smckusick  * If found then
12139742Smckusick  *    return action and optionally reply
12239742Smckusick  * else
12339742Smckusick  *    insert it in the cache
12439742Smckusick  *
12539742Smckusick  * The rules are as follows:
12639742Smckusick  * - if in progress, return DROP request
12739742Smckusick  * - if completed within DELAY of the current time, return DROP it
12839742Smckusick  * - if completed a longer time ago return REPLY if the reply was cached or
12939742Smckusick  *   return DOIT
13039742Smckusick  * Update/add new request at end of lru list
13139742Smckusick  */
13252196Smckusick nfsrv_getcache(nam, nd, repp)
13339742Smckusick 	struct mbuf *nam;
13452196Smckusick 	register struct nfsd *nd;
13539742Smckusick 	struct mbuf **repp;
13639742Smckusick {
13755520Smckusick 	register struct nfsrvcache *rp, *rq, **rpp;
13839742Smckusick 	struct mbuf *mb;
13952196Smckusick 	struct sockaddr_in *saddr;
14039742Smckusick 	caddr_t bpos;
14139742Smckusick 	int ret;
14239742Smckusick 
14352196Smckusick 	if (nd->nd_nqlflag != NQL_NOVAL)
14452196Smckusick 		return (RC_DOIT);
14555520Smckusick 	rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)];
14639742Smckusick loop:
14755520Smckusick 	for (rp = *rpp; rp; rp = rp->rc_forw) {
14852196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
14956362Smckusick 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
15039742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
15139742Smckusick 				rp->rc_flag |= RC_WANTED;
15243352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
15339742Smckusick 				goto loop;
15439742Smckusick 			}
15539742Smckusick 			rp->rc_flag |= RC_LOCKED;
15655520Smckusick 			/* If not at end of LRU chain, move it there */
15755520Smckusick 			if (rp->rc_next) {
15855520Smckusick 				/* remove from LRU chain */
15955520Smckusick 				*rp->rc_prev = rp->rc_next;
16052196Smckusick 				rp->rc_next->rc_prev = rp->rc_prev;
16155520Smckusick 				/* and replace at end of it */
16255520Smckusick 				rp->rc_next = NULL;
16355520Smckusick 				rp->rc_prev = nfsrvlrutail;
16455520Smckusick 				*nfsrvlrutail = rp;
16555520Smckusick 				nfsrvlrutail = &rp->rc_next;
16652196Smckusick 			}
16739742Smckusick 			if (rp->rc_state == RC_UNUSED)
16839742Smckusick 				panic("nfsrv cache");
16939742Smckusick 			if (rp->rc_state == RC_INPROG ||
17039742Smckusick 			   (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
17139742Smckusick 				nfsstats.srvcache_inproghits++;
17239742Smckusick 				ret = RC_DROPIT;
17339742Smckusick 			} else if (rp->rc_flag & RC_REPSTATUS) {
17439742Smckusick 				nfsstats.srvcache_idemdonehits++;
17552196Smckusick 				nfs_rephead(0, nd, rp->rc_status,
17652196Smckusick 				   0, (u_quad_t *)0, repp, &mb, &bpos);
17739742Smckusick 				rp->rc_timestamp = time.tv_sec;
17839742Smckusick 				ret = RC_REPLY;
17939742Smckusick 			} else if (rp->rc_flag & RC_REPMBUF) {
18039742Smckusick 				nfsstats.srvcache_idemdonehits++;
18141901Smckusick 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
18239742Smckusick 						M_WAIT);
18339742Smckusick 				rp->rc_timestamp = time.tv_sec;
18439742Smckusick 				ret = RC_REPLY;
18539742Smckusick 			} else {
18639742Smckusick 				nfsstats.srvcache_nonidemdonehits++;
18739742Smckusick 				rp->rc_state = RC_INPROG;
18839742Smckusick 				ret = RC_DOIT;
18939742Smckusick 			}
19039742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
19139742Smckusick 			if (rp->rc_flag & RC_WANTED) {
19239742Smckusick 				rp->rc_flag &= ~RC_WANTED;
19339742Smckusick 				wakeup((caddr_t)rp);
19439742Smckusick 			}
19539742Smckusick 			return (ret);
19639742Smckusick 		}
19739742Smckusick 	}
19839742Smckusick 	nfsstats.srvcache_misses++;
19955520Smckusick 	if (numnfsrvcache < desirednfsrvcache) {
20055520Smckusick 		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
20155520Smckusick 		    M_NFSD, M_WAITOK);
20255520Smckusick 		bzero((char *)rp, sizeof *rp);
20355520Smckusick 		numnfsrvcache++;
20455520Smckusick 		rp->rc_flag = RC_LOCKED;
20555520Smckusick 	} else {
20655520Smckusick 		rp = nfsrvlruhead;
20755520Smckusick 		while ((rp->rc_flag & RC_LOCKED) != 0) {
20855520Smckusick 			rp->rc_flag |= RC_WANTED;
20955520Smckusick 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
21055520Smckusick 			rp = nfsrvlruhead;
21155520Smckusick 		}
21255520Smckusick 		rp->rc_flag |= RC_LOCKED;
21355520Smckusick 		/* remove from hash chain */
21455520Smckusick 		if (rq = rp->rc_forw)
21555520Smckusick 			rq->rc_back = rp->rc_back;
21655520Smckusick 		*rp->rc_back = rq;
21755520Smckusick 		/* remove from LRU chain */
21855520Smckusick 		*rp->rc_prev = rp->rc_next;
21952196Smckusick 		rp->rc_next->rc_prev = rp->rc_prev;
22055520Smckusick 		if (rp->rc_flag & RC_REPMBUF)
22155520Smckusick 			m_freem(rp->rc_reply);
22255520Smckusick 		if (rp->rc_flag & RC_NAM)
22355520Smckusick 			MFREE(rp->rc_nam, mb);
22455520Smckusick 		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
22552196Smckusick 	}
22655520Smckusick 	/* place at end of LRU list */
22755520Smckusick 	rp->rc_next = NULL;
22855520Smckusick 	rp->rc_prev = nfsrvlrutail;
22955520Smckusick 	*nfsrvlrutail = rp;
23055520Smckusick 	nfsrvlrutail = &rp->rc_next;
23139742Smckusick 	rp->rc_state = RC_INPROG;
23252196Smckusick 	rp->rc_xid = nd->nd_retxid;
23352196Smckusick 	saddr = mtod(nam, struct sockaddr_in *);
23452196Smckusick 	switch (saddr->sin_family) {
23552196Smckusick 	case AF_INET:
23652196Smckusick 		rp->rc_flag |= RC_INETADDR;
23752196Smckusick 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
23852196Smckusick 		break;
23952196Smckusick 	case AF_ISO:
24052196Smckusick 	default:
24152196Smckusick 		rp->rc_flag |= RC_NAM;
24252196Smckusick 		rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
24352196Smckusick 		break;
24452196Smckusick 	};
24552196Smckusick 	rp->rc_proc = nd->nd_procnum;
24655520Smckusick 	/* insert into hash chain */
24755520Smckusick 	if (rq = *rpp)
24855520Smckusick 		rq->rc_back = &rp->rc_forw;
24956275Smckusick 	rp->rc_forw = rq;
25055520Smckusick 	rp->rc_back = rpp;
25155520Smckusick 	*rpp = rp;
25252196Smckusick 	rp->rc_flag &= ~RC_LOCKED;
25352196Smckusick 	if (rp->rc_flag & RC_WANTED) {
25452196Smckusick 		rp->rc_flag &= ~RC_WANTED;
25552196Smckusick 		wakeup((caddr_t)rp);
25652196Smckusick 	}
25739742Smckusick 	return (RC_DOIT);
25839742Smckusick }
25939742Smckusick 
26039742Smckusick /*
26139742Smckusick  * Update a request cache entry after the rpc has been done
26239742Smckusick  */
26352196Smckusick void
26452196Smckusick nfsrv_updatecache(nam, nd, repvalid, repmbuf)
26539742Smckusick 	struct mbuf *nam;
26652196Smckusick 	register struct nfsd *nd;
26740253Smckusick 	int repvalid;
26839742Smckusick 	struct mbuf *repmbuf;
26939742Smckusick {
27039742Smckusick 	register struct nfsrvcache *rp;
27139742Smckusick 
27252196Smckusick 	if (nd->nd_nqlflag != NQL_NOVAL)
27352196Smckusick 		return;
27439742Smckusick loop:
27555520Smckusick 	for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) {
27652196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
27756362Smckusick 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
27839742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
27939742Smckusick 				rp->rc_flag |= RC_WANTED;
28043352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
28139742Smckusick 				goto loop;
28239742Smckusick 			}
28339742Smckusick 			rp->rc_flag |= RC_LOCKED;
28439742Smckusick 			rp->rc_state = RC_DONE;
28540253Smckusick 			/*
28640253Smckusick 			 * If we have a valid reply update status and save
28741901Smckusick 			 * the reply for non-idempotent rpc's.
28841901Smckusick 			 * Otherwise invalidate entry by setting the timestamp
28941901Smckusick 			 * to nil.
29040253Smckusick 			 */
29140253Smckusick 			if (repvalid) {
29240253Smckusick 				rp->rc_timestamp = time.tv_sec;
29352196Smckusick 				if (nonidempotent[nd->nd_procnum]) {
29452196Smckusick 					if (repliesstatus[nd->nd_procnum]) {
29552196Smckusick 						rp->rc_status = nd->nd_repstat;
29640253Smckusick 						rp->rc_flag |= RC_REPSTATUS;
29740253Smckusick 					} else {
29841901Smckusick 						rp->rc_reply = m_copym(repmbuf,
29940253Smckusick 							0, M_COPYALL, M_WAIT);
30040253Smckusick 						rp->rc_flag |= RC_REPMBUF;
30140253Smckusick 					}
30239742Smckusick 				}
30340253Smckusick 			} else {
30440253Smckusick 				rp->rc_timestamp = 0;
30539742Smckusick 			}
30639742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
30739742Smckusick 			if (rp->rc_flag & RC_WANTED) {
30839742Smckusick 				rp->rc_flag &= ~RC_WANTED;
30939742Smckusick 				wakeup((caddr_t)rp);
31039742Smckusick 			}
31139742Smckusick 			return;
31239742Smckusick 		}
31339742Smckusick 	}
31439742Smckusick }
31552196Smckusick 
31652196Smckusick /*
31752196Smckusick  * Clean out the cache. Called when the last nfsd terminates.
31852196Smckusick  */
31952196Smckusick void
32052196Smckusick nfsrv_cleancache()
32152196Smckusick {
32255520Smckusick 	register struct nfsrvcache *rp, *nextrp;
32352196Smckusick 
32455520Smckusick 	for (rp = nfsrvlruhead; rp; rp = nextrp) {
32555520Smckusick 		nextrp = rp->rc_next;
32655520Smckusick 		free(rp, M_NFSD);
32752196Smckusick 	}
32855520Smckusick 	bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *));
329*58365Smckusick 	nfsrvlruhead = NULL;
330*58365Smckusick 	nfsrvlrutail = &nfsrvlruhead;
33155520Smckusick 	numnfsrvcache = 0;
33252196Smckusick }
333