xref: /csrg-svn/sys/nfs/nfs_srvcache.c (revision 56362)
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*56362Smckusick  *	@(#)nfs_srvcache.c	7.18 (Berkeley) 09/30/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>
2555520Smckusick #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 
3955520Smckusick long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
4039742Smckusick 
4155520Smckusick #define	NFSRCHASH(xid)		(((xid) + ((xid) >> 16)) & rheadhash)
4255520Smckusick static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead;
4355520Smckusick static struct nfsrvcache **rheadhtbl;
4455520Smckusick 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,
78*56362Smckusick 	FALSE,
7939742Smckusick };
8039742Smckusick 
8139742Smckusick /* True iff the rpc reply is an nfs status ONLY! */
8239742Smckusick static int repliesstatus[NFS_NPROCS] = {
8339742Smckusick 	FALSE,
8439742Smckusick 	FALSE,
8539742Smckusick 	FALSE,
8639742Smckusick 	FALSE,
8739742Smckusick 	FALSE,
8839742Smckusick 	FALSE,
8939742Smckusick 	FALSE,
9039742Smckusick 	FALSE,
9139742Smckusick 	FALSE,
9239742Smckusick 	FALSE,
9339742Smckusick 	TRUE,
9439742Smckusick 	TRUE,
9539742Smckusick 	TRUE,
9639742Smckusick 	TRUE,
9739742Smckusick 	FALSE,
9839742Smckusick 	TRUE,
9939742Smckusick 	FALSE,
10039742Smckusick 	FALSE,
10152196Smckusick 	FALSE,
10252196Smckusick 	FALSE,
10352196Smckusick 	FALSE,
10452196Smckusick 	FALSE,
105*56362Smckusick 	TRUE,
10639742Smckusick };
10739742Smckusick 
10839742Smckusick /*
10939742Smckusick  * Initialize the server request cache list
11039742Smckusick  */
11139742Smckusick nfsrv_initcache()
11239742Smckusick {
11339742Smckusick 
11455520Smckusick 	rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash);
11539742Smckusick }
11639742Smckusick 
11739742Smckusick /*
11839742Smckusick  * Look for the request in the cache
11939742Smckusick  * If found then
12039742Smckusick  *    return action and optionally reply
12139742Smckusick  * else
12239742Smckusick  *    insert it in the cache
12339742Smckusick  *
12439742Smckusick  * The rules are as follows:
12539742Smckusick  * - if in progress, return DROP request
12639742Smckusick  * - if completed within DELAY of the current time, return DROP it
12739742Smckusick  * - if completed a longer time ago return REPLY if the reply was cached or
12839742Smckusick  *   return DOIT
12939742Smckusick  * Update/add new request at end of lru list
13039742Smckusick  */
13152196Smckusick nfsrv_getcache(nam, nd, repp)
13239742Smckusick 	struct mbuf *nam;
13352196Smckusick 	register struct nfsd *nd;
13439742Smckusick 	struct mbuf **repp;
13539742Smckusick {
13655520Smckusick 	register struct nfsrvcache *rp, *rq, **rpp;
13739742Smckusick 	struct mbuf *mb;
13852196Smckusick 	struct sockaddr_in *saddr;
13939742Smckusick 	caddr_t bpos;
14039742Smckusick 	int ret;
14139742Smckusick 
14252196Smckusick 	if (nd->nd_nqlflag != NQL_NOVAL)
14352196Smckusick 		return (RC_DOIT);
14455520Smckusick 	rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)];
14539742Smckusick loop:
14655520Smckusick 	for (rp = *rpp; rp; rp = rp->rc_forw) {
14752196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
148*56362Smckusick 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
14939742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
15039742Smckusick 				rp->rc_flag |= RC_WANTED;
15143352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
15239742Smckusick 				goto loop;
15339742Smckusick 			}
15439742Smckusick 			rp->rc_flag |= RC_LOCKED;
15555520Smckusick 			/* If not at end of LRU chain, move it there */
15655520Smckusick 			if (rp->rc_next) {
15755520Smckusick 				/* remove from LRU chain */
15855520Smckusick 				*rp->rc_prev = rp->rc_next;
15952196Smckusick 				rp->rc_next->rc_prev = rp->rc_prev;
16055520Smckusick 				/* and replace at end of it */
16155520Smckusick 				rp->rc_next = NULL;
16255520Smckusick 				rp->rc_prev = nfsrvlrutail;
16355520Smckusick 				*nfsrvlrutail = rp;
16455520Smckusick 				nfsrvlrutail = &rp->rc_next;
16552196Smckusick 			}
16639742Smckusick 			if (rp->rc_state == RC_UNUSED)
16739742Smckusick 				panic("nfsrv cache");
16839742Smckusick 			if (rp->rc_state == RC_INPROG ||
16939742Smckusick 			   (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
17039742Smckusick 				nfsstats.srvcache_inproghits++;
17139742Smckusick 				ret = RC_DROPIT;
17239742Smckusick 			} else if (rp->rc_flag & RC_REPSTATUS) {
17339742Smckusick 				nfsstats.srvcache_idemdonehits++;
17452196Smckusick 				nfs_rephead(0, nd, rp->rc_status,
17552196Smckusick 				   0, (u_quad_t *)0, repp, &mb, &bpos);
17639742Smckusick 				rp->rc_timestamp = time.tv_sec;
17739742Smckusick 				ret = RC_REPLY;
17839742Smckusick 			} else if (rp->rc_flag & RC_REPMBUF) {
17939742Smckusick 				nfsstats.srvcache_idemdonehits++;
18041901Smckusick 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
18139742Smckusick 						M_WAIT);
18239742Smckusick 				rp->rc_timestamp = time.tv_sec;
18339742Smckusick 				ret = RC_REPLY;
18439742Smckusick 			} else {
18539742Smckusick 				nfsstats.srvcache_nonidemdonehits++;
18639742Smckusick 				rp->rc_state = RC_INPROG;
18739742Smckusick 				ret = RC_DOIT;
18839742Smckusick 			}
18939742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
19039742Smckusick 			if (rp->rc_flag & RC_WANTED) {
19139742Smckusick 				rp->rc_flag &= ~RC_WANTED;
19239742Smckusick 				wakeup((caddr_t)rp);
19339742Smckusick 			}
19439742Smckusick 			return (ret);
19539742Smckusick 		}
19639742Smckusick 	}
19739742Smckusick 	nfsstats.srvcache_misses++;
19855520Smckusick 	if (numnfsrvcache < desirednfsrvcache) {
19955520Smckusick 		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
20055520Smckusick 		    M_NFSD, M_WAITOK);
20155520Smckusick 		bzero((char *)rp, sizeof *rp);
20255520Smckusick 		numnfsrvcache++;
20355520Smckusick 		rp->rc_flag = RC_LOCKED;
20455520Smckusick 	} else {
20555520Smckusick 		rp = nfsrvlruhead;
20655520Smckusick 		while ((rp->rc_flag & RC_LOCKED) != 0) {
20755520Smckusick 			rp->rc_flag |= RC_WANTED;
20855520Smckusick 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
20955520Smckusick 			rp = nfsrvlruhead;
21055520Smckusick 		}
21155520Smckusick 		rp->rc_flag |= RC_LOCKED;
21255520Smckusick 		/* remove from hash chain */
21355520Smckusick 		if (rq = rp->rc_forw)
21455520Smckusick 			rq->rc_back = rp->rc_back;
21555520Smckusick 		*rp->rc_back = rq;
21655520Smckusick 		/* remove from LRU chain */
21755520Smckusick 		*rp->rc_prev = rp->rc_next;
21852196Smckusick 		rp->rc_next->rc_prev = rp->rc_prev;
21955520Smckusick 		if (rp->rc_flag & RC_REPMBUF)
22055520Smckusick 			m_freem(rp->rc_reply);
22155520Smckusick 		if (rp->rc_flag & RC_NAM)
22255520Smckusick 			MFREE(rp->rc_nam, mb);
22355520Smckusick 		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
22452196Smckusick 	}
22555520Smckusick 	/* place at end of LRU list */
22655520Smckusick 	rp->rc_next = NULL;
22755520Smckusick 	rp->rc_prev = nfsrvlrutail;
22855520Smckusick 	*nfsrvlrutail = rp;
22955520Smckusick 	nfsrvlrutail = &rp->rc_next;
23039742Smckusick 	rp->rc_state = RC_INPROG;
23152196Smckusick 	rp->rc_xid = nd->nd_retxid;
23252196Smckusick 	saddr = mtod(nam, struct sockaddr_in *);
23352196Smckusick 	switch (saddr->sin_family) {
23452196Smckusick 	case AF_INET:
23552196Smckusick 		rp->rc_flag |= RC_INETADDR;
23652196Smckusick 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
23752196Smckusick 		break;
23852196Smckusick 	case AF_ISO:
23952196Smckusick 	default:
24052196Smckusick 		rp->rc_flag |= RC_NAM;
24152196Smckusick 		rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
24252196Smckusick 		break;
24352196Smckusick 	};
24452196Smckusick 	rp->rc_proc = nd->nd_procnum;
24555520Smckusick 	/* insert into hash chain */
24655520Smckusick 	if (rq = *rpp)
24755520Smckusick 		rq->rc_back = &rp->rc_forw;
24856275Smckusick 	rp->rc_forw = rq;
24955520Smckusick 	rp->rc_back = rpp;
25055520Smckusick 	*rpp = rp;
25152196Smckusick 	rp->rc_flag &= ~RC_LOCKED;
25252196Smckusick 	if (rp->rc_flag & RC_WANTED) {
25352196Smckusick 		rp->rc_flag &= ~RC_WANTED;
25452196Smckusick 		wakeup((caddr_t)rp);
25552196Smckusick 	}
25639742Smckusick 	return (RC_DOIT);
25739742Smckusick }
25839742Smckusick 
25939742Smckusick /*
26039742Smckusick  * Update a request cache entry after the rpc has been done
26139742Smckusick  */
26252196Smckusick void
26352196Smckusick nfsrv_updatecache(nam, nd, repvalid, repmbuf)
26439742Smckusick 	struct mbuf *nam;
26552196Smckusick 	register struct nfsd *nd;
26640253Smckusick 	int repvalid;
26739742Smckusick 	struct mbuf *repmbuf;
26839742Smckusick {
26939742Smckusick 	register struct nfsrvcache *rp;
27039742Smckusick 
27152196Smckusick 	if (nd->nd_nqlflag != NQL_NOVAL)
27252196Smckusick 		return;
27339742Smckusick loop:
27455520Smckusick 	for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) {
27552196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
276*56362Smckusick 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
27739742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
27839742Smckusick 				rp->rc_flag |= RC_WANTED;
27943352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
28039742Smckusick 				goto loop;
28139742Smckusick 			}
28239742Smckusick 			rp->rc_flag |= RC_LOCKED;
28339742Smckusick 			rp->rc_state = RC_DONE;
28440253Smckusick 			/*
28540253Smckusick 			 * If we have a valid reply update status and save
28641901Smckusick 			 * the reply for non-idempotent rpc's.
28741901Smckusick 			 * Otherwise invalidate entry by setting the timestamp
28841901Smckusick 			 * to nil.
28940253Smckusick 			 */
29040253Smckusick 			if (repvalid) {
29140253Smckusick 				rp->rc_timestamp = time.tv_sec;
29252196Smckusick 				if (nonidempotent[nd->nd_procnum]) {
29352196Smckusick 					if (repliesstatus[nd->nd_procnum]) {
29452196Smckusick 						rp->rc_status = nd->nd_repstat;
29540253Smckusick 						rp->rc_flag |= RC_REPSTATUS;
29640253Smckusick 					} else {
29741901Smckusick 						rp->rc_reply = m_copym(repmbuf,
29840253Smckusick 							0, M_COPYALL, M_WAIT);
29940253Smckusick 						rp->rc_flag |= RC_REPMBUF;
30040253Smckusick 					}
30139742Smckusick 				}
30240253Smckusick 			} else {
30340253Smckusick 				rp->rc_timestamp = 0;
30439742Smckusick 			}
30539742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
30639742Smckusick 			if (rp->rc_flag & RC_WANTED) {
30739742Smckusick 				rp->rc_flag &= ~RC_WANTED;
30839742Smckusick 				wakeup((caddr_t)rp);
30939742Smckusick 			}
31039742Smckusick 			return;
31139742Smckusick 		}
31239742Smckusick 	}
31339742Smckusick }
31452196Smckusick 
31552196Smckusick /*
31652196Smckusick  * Clean out the cache. Called when the last nfsd terminates.
31752196Smckusick  */
31852196Smckusick void
31952196Smckusick nfsrv_cleancache()
32052196Smckusick {
32155520Smckusick 	register struct nfsrvcache *rp, *nextrp;
32252196Smckusick 
32355520Smckusick 	for (rp = nfsrvlruhead; rp; rp = nextrp) {
32455520Smckusick 		nextrp = rp->rc_next;
32555520Smckusick 		free(rp, M_NFSD);
32652196Smckusick 	}
32755520Smckusick 	bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *));
32855520Smckusick 	numnfsrvcache = 0;
32952196Smckusick }
330