xref: /csrg-svn/sys/nfs/nfs_srvcache.c (revision 60155)
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*60155Smckusick  *	@(#)nfs_srvcache.c	7.21 (Berkeley) 05/20/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 
42*60155Smckusick #define	NFSRCHASH(xid)		(((xid) + ((xid) >> 24)) & 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");
169*60155Smckusick 			if (rp->rc_state == RC_INPROG) {
17039742Smckusick 				nfsstats.srvcache_inproghits++;
17139742Smckusick 				ret = RC_DROPIT;
17239742Smckusick 			} else if (rp->rc_flag & RC_REPSTATUS) {
173*60155Smckusick 				nfsstats.srvcache_nonidemdonehits++;
17452196Smckusick 				nfs_rephead(0, nd, rp->rc_status,
17552196Smckusick 				   0, (u_quad_t *)0, repp, &mb, &bpos);
17639742Smckusick 				ret = RC_REPLY;
17739742Smckusick 			} else if (rp->rc_flag & RC_REPMBUF) {
178*60155Smckusick 				nfsstats.srvcache_nonidemdonehits++;
17941901Smckusick 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
18039742Smckusick 						M_WAIT);
18139742Smckusick 				ret = RC_REPLY;
18239742Smckusick 			} else {
183*60155Smckusick 				nfsstats.srvcache_idemdonehits++;
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++;
19655520Smckusick 	if (numnfsrvcache < desirednfsrvcache) {
19755520Smckusick 		rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
19855520Smckusick 		    M_NFSD, M_WAITOK);
19955520Smckusick 		bzero((char *)rp, sizeof *rp);
20055520Smckusick 		numnfsrvcache++;
20155520Smckusick 		rp->rc_flag = RC_LOCKED;
20255520Smckusick 	} else {
20355520Smckusick 		rp = nfsrvlruhead;
20455520Smckusick 		while ((rp->rc_flag & RC_LOCKED) != 0) {
20555520Smckusick 			rp->rc_flag |= RC_WANTED;
20655520Smckusick 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
20755520Smckusick 			rp = nfsrvlruhead;
20855520Smckusick 		}
20955520Smckusick 		rp->rc_flag |= RC_LOCKED;
21055520Smckusick 		/* remove from hash chain */
21155520Smckusick 		if (rq = rp->rc_forw)
21255520Smckusick 			rq->rc_back = rp->rc_back;
21355520Smckusick 		*rp->rc_back = rq;
21455520Smckusick 		/* remove from LRU chain */
21555520Smckusick 		*rp->rc_prev = rp->rc_next;
21652196Smckusick 		rp->rc_next->rc_prev = rp->rc_prev;
21755520Smckusick 		if (rp->rc_flag & RC_REPMBUF)
21855520Smckusick 			m_freem(rp->rc_reply);
21955520Smckusick 		if (rp->rc_flag & RC_NAM)
22055520Smckusick 			MFREE(rp->rc_nam, mb);
22155520Smckusick 		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
22252196Smckusick 	}
22355520Smckusick 	/* place at end of LRU list */
22455520Smckusick 	rp->rc_next = NULL;
22555520Smckusick 	rp->rc_prev = nfsrvlrutail;
22655520Smckusick 	*nfsrvlrutail = rp;
22755520Smckusick 	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;
24355520Smckusick 	/* insert into hash chain */
24455520Smckusick 	if (rq = *rpp)
24555520Smckusick 		rq->rc_back = &rp->rc_forw;
24656275Smckusick 	rp->rc_forw = rq;
24755520Smckusick 	rp->rc_back = rpp;
24855520Smckusick 	*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:
27255520Smckusick 	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 &&
27456362Smckusick 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, 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.
28540253Smckusick 			 */
286*60155Smckusick 			if (repvalid && nonidempotent[nd->nd_procnum]) {
287*60155Smckusick 				if (repliesstatus[nd->nd_procnum]) {
288*60155Smckusick 					rp->rc_status = nd->nd_repstat;
289*60155Smckusick 					rp->rc_flag |= RC_REPSTATUS;
290*60155Smckusick 				} else {
291*60155Smckusick 					rp->rc_reply = m_copym(repmbuf,
292*60155Smckusick 						0, M_COPYALL, M_WAIT);
293*60155Smckusick 					rp->rc_flag |= RC_REPMBUF;
29439742Smckusick 				}
29539742Smckusick 			}
29639742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
29739742Smckusick 			if (rp->rc_flag & RC_WANTED) {
29839742Smckusick 				rp->rc_flag &= ~RC_WANTED;
29939742Smckusick 				wakeup((caddr_t)rp);
30039742Smckusick 			}
30139742Smckusick 			return;
30239742Smckusick 		}
30339742Smckusick 	}
30439742Smckusick }
30552196Smckusick 
30652196Smckusick /*
30752196Smckusick  * Clean out the cache. Called when the last nfsd terminates.
30852196Smckusick  */
30952196Smckusick void
31052196Smckusick nfsrv_cleancache()
31152196Smckusick {
31255520Smckusick 	register struct nfsrvcache *rp, *nextrp;
31352196Smckusick 
31455520Smckusick 	for (rp = nfsrvlruhead; rp; rp = nextrp) {
31555520Smckusick 		nextrp = rp->rc_next;
31655520Smckusick 		free(rp, M_NFSD);
31752196Smckusick 	}
31855520Smckusick 	bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *));
31958365Smckusick 	nfsrvlruhead = NULL;
32058365Smckusick 	nfsrvlrutail = &nfsrvlruhead;
32155520Smckusick 	numnfsrvcache = 0;
32252196Smckusick }
323