xref: /csrg-svn/sys/nfs/nfs_srvcache.c (revision 54874)
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*54874Smckusick  *	@(#)nfs_srvcache.c	7.15 (Berkeley) 07/09/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  */
18*54874Smckusick #include <sys/param.h>
19*54874Smckusick #include <sys/vnode.h>
20*54874Smckusick #include <sys/mount.h>
21*54874Smckusick #include <sys/kernel.h>
22*54874Smckusick #include <sys/systm.h>
23*54874Smckusick #include <sys/proc.h>
24*54874Smckusick #include <sys/mbuf.h>
25*54874Smckusick #include <sys/socket.h>
26*54874Smckusick #include <sys/socketvar.h>
27*54874Smckusick #include <netinet/in.h>
2852196Smckusick #ifdef ISO
29*54874Smckusick #include <netiso/iso.h>
3052196Smckusick #endif
31*54874Smckusick #include <nfs/nfsm_subs.h>
32*54874Smckusick #include <nfs/rpcv2.h>
33*54874Smckusick #include <nfs/nfsv2.h>
34*54874Smckusick #include <nfs/nfs.h>
35*54874Smckusick #include <nfs/nfsrvcache.h>
36*54874Smckusick #include <nfs/nqnfs.h>
3739742Smckusick 
3839742Smckusick #if	((NFSRCHSZ&(NFSRCHSZ-1)) == 0)
3939742Smckusick #define	NFSRCHASH(xid)		(((xid)+((xid)>>16))&(NFSRCHSZ-1))
4039742Smckusick #else
4139742Smckusick #define	NFSRCHASH(xid)		(((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ)
4239742Smckusick #endif
4339742Smckusick 
4439742Smckusick union rhead {
4539742Smckusick 	union  rhead *rh_head[2];
4639742Smckusick 	struct nfsrvcache *rh_chain[2];
4739742Smckusick } rhead[NFSRCHSZ];
4839742Smckusick 
4939742Smckusick static struct nfsrvcache nfsrvcachehead;
5039742Smckusick static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ];
5139742Smckusick 
5239742Smckusick #define TRUE	1
5339742Smckusick #define	FALSE	0
5439742Smckusick 
5552196Smckusick #define	NETFAMILY(rp) \
5652196Smckusick 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
5752196Smckusick 
5839742Smckusick /*
5939742Smckusick  * Static array that defines which nfs rpc's are nonidempotent
6039742Smckusick  */
6140118Smckusick int nonidempotent[NFS_NPROCS] = {
6239742Smckusick 	FALSE,
6339742Smckusick 	FALSE,
6439742Smckusick 	TRUE,
6539742Smckusick 	FALSE,
6639742Smckusick 	FALSE,
6739742Smckusick 	FALSE,
6839742Smckusick 	FALSE,
6939742Smckusick 	FALSE,
7039742Smckusick 	TRUE,
7139742Smckusick 	TRUE,
7239742Smckusick 	TRUE,
7339742Smckusick 	TRUE,
7439742Smckusick 	TRUE,
7539742Smckusick 	TRUE,
7639742Smckusick 	TRUE,
7739742Smckusick 	TRUE,
7839742Smckusick 	FALSE,
7939742Smckusick 	FALSE,
8052196Smckusick 	FALSE,
8152196Smckusick 	FALSE,
8252196Smckusick 	FALSE,
8352196Smckusick 	FALSE,
8439742Smckusick };
8539742Smckusick 
8639742Smckusick /* True iff the rpc reply is an nfs status ONLY! */
8739742Smckusick static int repliesstatus[NFS_NPROCS] = {
8839742Smckusick 	FALSE,
8939742Smckusick 	FALSE,
9039742Smckusick 	FALSE,
9139742Smckusick 	FALSE,
9239742Smckusick 	FALSE,
9339742Smckusick 	FALSE,
9439742Smckusick 	FALSE,
9539742Smckusick 	FALSE,
9639742Smckusick 	FALSE,
9739742Smckusick 	FALSE,
9839742Smckusick 	TRUE,
9939742Smckusick 	TRUE,
10039742Smckusick 	TRUE,
10139742Smckusick 	TRUE,
10239742Smckusick 	FALSE,
10339742Smckusick 	TRUE,
10439742Smckusick 	FALSE,
10539742Smckusick 	FALSE,
10652196Smckusick 	FALSE,
10752196Smckusick 	FALSE,
10852196Smckusick 	FALSE,
10952196Smckusick 	FALSE,
11039742Smckusick };
11139742Smckusick 
11239742Smckusick /*
11339742Smckusick  * Initialize the server request cache list
11439742Smckusick  */
11539742Smckusick nfsrv_initcache()
11639742Smckusick {
11739742Smckusick 	register int i;
11839742Smckusick 	register struct nfsrvcache *rp = nfsrvcache;
11939742Smckusick 	register struct nfsrvcache *hp = &nfsrvcachehead;
12039742Smckusick 	register union  rhead *rh = rhead;
12139742Smckusick 
12239742Smckusick 	for (i = NFSRCHSZ; --i >= 0; rh++) {
12339742Smckusick 		rh->rh_head[0] = rh;
12439742Smckusick 		rh->rh_head[1] = rh;
12539742Smckusick 	}
12639742Smckusick 	hp->rc_next = hp->rc_prev = hp;
12739742Smckusick 	for (i = NFSRVCACHESIZ; i-- > 0; ) {
12839742Smckusick 		rp->rc_state = RC_UNUSED;
12939742Smckusick 		rp->rc_flag = 0;
13039742Smckusick 		rp->rc_forw = rp;
13139742Smckusick 		rp->rc_back = rp;
13239742Smckusick 		rp->rc_next = hp->rc_next;
13339742Smckusick 		hp->rc_next->rc_prev = rp;
13439742Smckusick 		rp->rc_prev = hp;
13539742Smckusick 		hp->rc_next = rp;
13639742Smckusick 		rp++;
13739742Smckusick 	}
13839742Smckusick }
13939742Smckusick 
14039742Smckusick /*
14139742Smckusick  * Look for the request in the cache
14239742Smckusick  * If found then
14339742Smckusick  *    return action and optionally reply
14439742Smckusick  * else
14539742Smckusick  *    insert it in the cache
14639742Smckusick  *
14739742Smckusick  * The rules are as follows:
14839742Smckusick  * - if in progress, return DROP request
14939742Smckusick  * - if completed within DELAY of the current time, return DROP it
15039742Smckusick  * - if completed a longer time ago return REPLY if the reply was cached or
15139742Smckusick  *   return DOIT
15239742Smckusick  * Update/add new request at end of lru list
15339742Smckusick  */
15452196Smckusick nfsrv_getcache(nam, nd, repp)
15539742Smckusick 	struct mbuf *nam;
15652196Smckusick 	register struct nfsd *nd;
15739742Smckusick 	struct mbuf **repp;
15839742Smckusick {
15939742Smckusick 	register struct nfsrvcache *rp;
16039742Smckusick 	register union  rhead *rh;
16139742Smckusick 	struct mbuf *mb;
16252196Smckusick 	struct sockaddr_in *saddr;
16339742Smckusick 	caddr_t bpos;
16439742Smckusick 	int ret;
16539742Smckusick 
16652196Smckusick 	if (nd->nd_nqlflag != NQL_NOVAL)
16752196Smckusick 		return (RC_DOIT);
16852196Smckusick 	rh = &rhead[NFSRCHASH(nd->nd_retxid)];
16939742Smckusick loop:
17039742Smckusick 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
17152196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
17254613Smckusick 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) {
17339742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
17439742Smckusick 				rp->rc_flag |= RC_WANTED;
17543352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
17639742Smckusick 				goto loop;
17739742Smckusick 			}
17839742Smckusick 			rp->rc_flag |= RC_LOCKED;
17952196Smckusick 			if (rp->rc_prev != &nfsrvcachehead) {
18052196Smckusick 				rp->rc_next->rc_prev = rp->rc_prev;
18152196Smckusick 				rp->rc_prev->rc_next = rp->rc_next;
18252196Smckusick 				rp->rc_next = nfsrvcachehead.rc_next;
18352196Smckusick 				nfsrvcachehead.rc_next = rp;
18452196Smckusick 				rp->rc_prev = &nfsrvcachehead;
18552196Smckusick 				rp->rc_next->rc_prev = rp;
18652196Smckusick 			}
18739742Smckusick 			if (rp->rc_state == RC_UNUSED)
18839742Smckusick 				panic("nfsrv cache");
18939742Smckusick 			if (rp->rc_state == RC_INPROG ||
19039742Smckusick 			   (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
19139742Smckusick 				nfsstats.srvcache_inproghits++;
19239742Smckusick 				ret = RC_DROPIT;
19339742Smckusick 			} else if (rp->rc_flag & RC_REPSTATUS) {
19439742Smckusick 				nfsstats.srvcache_idemdonehits++;
19552196Smckusick 				nfs_rephead(0, nd, rp->rc_status,
19652196Smckusick 				   0, (u_quad_t *)0, repp, &mb, &bpos);
19739742Smckusick 				rp->rc_timestamp = time.tv_sec;
19839742Smckusick 				ret = RC_REPLY;
19939742Smckusick 			} else if (rp->rc_flag & RC_REPMBUF) {
20039742Smckusick 				nfsstats.srvcache_idemdonehits++;
20141901Smckusick 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
20239742Smckusick 						M_WAIT);
20339742Smckusick 				rp->rc_timestamp = time.tv_sec;
20439742Smckusick 				ret = RC_REPLY;
20539742Smckusick 			} else {
20639742Smckusick 				nfsstats.srvcache_nonidemdonehits++;
20739742Smckusick 				rp->rc_state = RC_INPROG;
20839742Smckusick 				ret = RC_DOIT;
20939742Smckusick 			}
21039742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
21139742Smckusick 			if (rp->rc_flag & RC_WANTED) {
21239742Smckusick 				rp->rc_flag &= ~RC_WANTED;
21339742Smckusick 				wakeup((caddr_t)rp);
21439742Smckusick 			}
21539742Smckusick 			return (ret);
21639742Smckusick 		}
21739742Smckusick 	}
21839742Smckusick 	nfsstats.srvcache_misses++;
21939742Smckusick 	rp = nfsrvcachehead.rc_prev;
22039742Smckusick 	while ((rp->rc_flag & RC_LOCKED) != 0) {
22139742Smckusick 		rp->rc_flag |= RC_WANTED;
22243352Smckusick 		(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
22352196Smckusick 		rp = nfsrvcachehead.rc_prev;
22439742Smckusick 	}
22552196Smckusick 	rp->rc_flag |= RC_LOCKED;
22639742Smckusick 	remque(rp);
22752196Smckusick 	if (rp->rc_prev != &nfsrvcachehead) {
22852196Smckusick 		rp->rc_next->rc_prev = rp->rc_prev;
22952196Smckusick 		rp->rc_prev->rc_next = rp->rc_next;
23052196Smckusick 		rp->rc_next = nfsrvcachehead.rc_next;
23152196Smckusick 		nfsrvcachehead.rc_next = rp;
23252196Smckusick 		rp->rc_prev = &nfsrvcachehead;
23352196Smckusick 		rp->rc_next->rc_prev = rp;
23452196Smckusick 	}
23539742Smckusick 	if (rp->rc_flag & RC_REPMBUF)
23652196Smckusick 		m_freem(rp->rc_reply);
23752196Smckusick 	if (rp->rc_flag & RC_NAM)
23852196Smckusick 		MFREE(rp->rc_nam, mb);
23952196Smckusick 	rp->rc_flag &= (RC_LOCKED | RC_WANTED);
24039742Smckusick 	rp->rc_state = RC_INPROG;
24152196Smckusick 	rp->rc_xid = nd->nd_retxid;
24252196Smckusick 	saddr = mtod(nam, struct sockaddr_in *);
24352196Smckusick 	switch (saddr->sin_family) {
24452196Smckusick 	case AF_INET:
24552196Smckusick 		rp->rc_flag |= RC_INETADDR;
24652196Smckusick 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
24752196Smckusick 		break;
24852196Smckusick 	case AF_ISO:
24952196Smckusick 	default:
25052196Smckusick 		rp->rc_flag |= RC_NAM;
25152196Smckusick 		rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
25252196Smckusick 		break;
25352196Smckusick 	};
25452196Smckusick 	rp->rc_proc = nd->nd_procnum;
25539742Smckusick 	insque(rp, rh);
25652196Smckusick 	rp->rc_flag &= ~RC_LOCKED;
25752196Smckusick 	if (rp->rc_flag & RC_WANTED) {
25852196Smckusick 		rp->rc_flag &= ~RC_WANTED;
25952196Smckusick 		wakeup((caddr_t)rp);
26052196Smckusick 	}
26139742Smckusick 	return (RC_DOIT);
26239742Smckusick }
26339742Smckusick 
26439742Smckusick /*
26539742Smckusick  * Update a request cache entry after the rpc has been done
26639742Smckusick  */
26752196Smckusick void
26852196Smckusick nfsrv_updatecache(nam, nd, repvalid, repmbuf)
26939742Smckusick 	struct mbuf *nam;
27052196Smckusick 	register struct nfsd *nd;
27140253Smckusick 	int repvalid;
27239742Smckusick 	struct mbuf *repmbuf;
27339742Smckusick {
27439742Smckusick 	register struct nfsrvcache *rp;
27539742Smckusick 	register union	rhead *rh;
27639742Smckusick 
27752196Smckusick 	if (nd->nd_nqlflag != NQL_NOVAL)
27852196Smckusick 		return;
27952196Smckusick 	rh = &rhead[NFSRCHASH(nd->nd_retxid)];
28039742Smckusick loop:
28139742Smckusick 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
28252196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
28354613Smckusick 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) {
28439742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
28539742Smckusick 				rp->rc_flag |= RC_WANTED;
28643352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
28739742Smckusick 				goto loop;
28839742Smckusick 			}
28939742Smckusick 			rp->rc_flag |= RC_LOCKED;
29039742Smckusick 			rp->rc_state = RC_DONE;
29140253Smckusick 			/*
29240253Smckusick 			 * If we have a valid reply update status and save
29341901Smckusick 			 * the reply for non-idempotent rpc's.
29441901Smckusick 			 * Otherwise invalidate entry by setting the timestamp
29541901Smckusick 			 * to nil.
29640253Smckusick 			 */
29740253Smckusick 			if (repvalid) {
29840253Smckusick 				rp->rc_timestamp = time.tv_sec;
29952196Smckusick 				if (nonidempotent[nd->nd_procnum]) {
30052196Smckusick 					if (repliesstatus[nd->nd_procnum]) {
30152196Smckusick 						rp->rc_status = nd->nd_repstat;
30240253Smckusick 						rp->rc_flag |= RC_REPSTATUS;
30340253Smckusick 					} else {
30441901Smckusick 						rp->rc_reply = m_copym(repmbuf,
30540253Smckusick 							0, M_COPYALL, M_WAIT);
30640253Smckusick 						rp->rc_flag |= RC_REPMBUF;
30740253Smckusick 					}
30839742Smckusick 				}
30940253Smckusick 			} else {
31040253Smckusick 				rp->rc_timestamp = 0;
31139742Smckusick 			}
31239742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
31339742Smckusick 			if (rp->rc_flag & RC_WANTED) {
31439742Smckusick 				rp->rc_flag &= ~RC_WANTED;
31539742Smckusick 				wakeup((caddr_t)rp);
31639742Smckusick 			}
31739742Smckusick 			return;
31839742Smckusick 		}
31939742Smckusick 	}
32039742Smckusick }
32152196Smckusick 
32252196Smckusick /*
32352196Smckusick  * Clean out the cache. Called when the last nfsd terminates.
32452196Smckusick  */
32552196Smckusick void
32652196Smckusick nfsrv_cleancache()
32752196Smckusick {
32852196Smckusick 	register int i;
32952196Smckusick 	register struct nfsrvcache *rp = nfsrvcache;
33052196Smckusick 	register struct nfsrvcache *hp = &nfsrvcachehead;
33152196Smckusick 	register union  rhead *rh = rhead;
33252196Smckusick 
33352196Smckusick 	for (i = NFSRCHSZ; --i >= 0; rh++) {
33452196Smckusick 		rh->rh_head[0] = rh;
33552196Smckusick 		rh->rh_head[1] = rh;
33652196Smckusick 	}
33752196Smckusick 	hp->rc_next = hp->rc_prev = hp;
33852196Smckusick 	for (i = NFSRVCACHESIZ; i-- > 0; ) {
33952196Smckusick 		if (rp->rc_flag & RC_REPMBUF)
34052196Smckusick 			m_freem(rp->rc_reply);
34152196Smckusick 		if (rp->rc_flag & RC_NAM)
34252196Smckusick 			m_freem(rp->rc_nam);
34352196Smckusick 		rp->rc_state = RC_UNUSED;
34452196Smckusick 		rp->rc_flag = 0;
34552196Smckusick 		rp->rc_forw = rp;
34652196Smckusick 		rp->rc_back = rp;
34752196Smckusick 		rp->rc_next = hp->rc_next;
34852196Smckusick 		hp->rc_next->rc_prev = rp;
34952196Smckusick 		rp->rc_prev = hp;
35052196Smckusick 		hp->rc_next = rp;
35152196Smckusick 		rp++;
35252196Smckusick 	}
35352196Smckusick }
354