xref: /csrg-svn/sys/nfs/nfs_srvcache.c (revision 68653)
139742Smckusick /*
263234Sbostic  * Copyright (c) 1989, 1993
363234Sbostic  *	The Regents of the University of California.  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*68653Smckusick  *	@(#)nfs_srvcache.c	8.3 (Berkeley) 03/30/95
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>
35*68653Smckusick #include <nfs/nfsproto.h>
3654874Smckusick #include <nfs/nfs.h>
3754874Smckusick #include <nfs/nfsrvcache.h>
3854874Smckusick #include <nfs/nqnfs.h>
3939742Smckusick 
40*68653Smckusick extern struct nfsstats nfsstats;
41*68653Smckusick extern int nfsv2_procid[NFS_NPROCS];
4255520Smckusick long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
4339742Smckusick 
4467708Smckusick #define	NFSRCHASH(xid) \
4567708Smckusick 	(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
4667708Smckusick LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl;
4767708Smckusick TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead;
4867708Smckusick u_long nfsrvhash;
4939742Smckusick 
5039742Smckusick #define TRUE	1
5139742Smckusick #define	FALSE	0
5239742Smckusick 
5352196Smckusick #define	NETFAMILY(rp) \
5452196Smckusick 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
5552196Smckusick 
5639742Smckusick /*
5739742Smckusick  * Static array that defines which nfs rpc's are nonidempotent
5839742Smckusick  */
5940118Smckusick int nonidempotent[NFS_NPROCS] = {
6039742Smckusick 	FALSE,
6139742Smckusick 	FALSE,
6239742Smckusick 	TRUE,
6339742Smckusick 	FALSE,
6439742Smckusick 	FALSE,
6539742Smckusick 	FALSE,
6639742Smckusick 	FALSE,
6739742Smckusick 	TRUE,
6839742Smckusick 	TRUE,
6939742Smckusick 	TRUE,
7039742Smckusick 	TRUE,
7139742Smckusick 	TRUE,
7239742Smckusick 	TRUE,
7339742Smckusick 	TRUE,
7439742Smckusick 	TRUE,
75*68653Smckusick 	TRUE,
7639742Smckusick 	FALSE,
7739742Smckusick 	FALSE,
7852196Smckusick 	FALSE,
7952196Smckusick 	FALSE,
8052196Smckusick 	FALSE,
8152196Smckusick 	FALSE,
8256362Smckusick 	FALSE,
83*68653Smckusick 	FALSE,
84*68653Smckusick 	FALSE,
85*68653Smckusick 	FALSE,
8639742Smckusick };
8739742Smckusick 
8839742Smckusick /* True iff the rpc reply is an nfs status ONLY! */
89*68653Smckusick static int nfsv2_repstat[NFS_NPROCS] = {
9039742Smckusick 	FALSE,
9139742Smckusick 	FALSE,
9239742Smckusick 	FALSE,
9339742Smckusick 	FALSE,
9439742Smckusick 	FALSE,
9539742Smckusick 	FALSE,
9639742Smckusick 	FALSE,
9739742Smckusick 	FALSE,
9839742Smckusick 	FALSE,
9939742Smckusick 	FALSE,
10039742Smckusick 	TRUE,
10139742Smckusick 	TRUE,
10239742Smckusick 	TRUE,
10339742Smckusick 	TRUE,
10439742Smckusick 	FALSE,
10539742Smckusick 	TRUE,
10639742Smckusick 	FALSE,
10739742Smckusick 	FALSE,
10839742Smckusick };
10939742Smckusick 
11039742Smckusick /*
11139742Smckusick  * Initialize the server request cache list
11239742Smckusick  */
113*68653Smckusick void
nfsrv_initcache()11439742Smckusick nfsrv_initcache()
11539742Smckusick {
11639742Smckusick 
11767708Smckusick 	nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
11867708Smckusick 	TAILQ_INIT(&nfsrvlruhead);
11939742Smckusick }
12039742Smckusick 
12139742Smckusick /*
12239742Smckusick  * Look for the request in the cache
12339742Smckusick  * If found then
12439742Smckusick  *    return action and optionally reply
12539742Smckusick  * else
12639742Smckusick  *    insert it in the cache
12739742Smckusick  *
12839742Smckusick  * The rules are as follows:
12939742Smckusick  * - if in progress, return DROP request
13039742Smckusick  * - if completed within DELAY of the current time, return DROP it
13139742Smckusick  * - if completed a longer time ago return REPLY if the reply was cached or
13239742Smckusick  *   return DOIT
13339742Smckusick  * Update/add new request at end of lru list
13439742Smckusick  */
135*68653Smckusick int
nfsrv_getcache(nd,slp,repp)136*68653Smckusick nfsrv_getcache(nd, slp, repp)
137*68653Smckusick 	register struct nfsrv_descript *nd;
138*68653Smckusick 	struct nfssvc_sock *slp;
13939742Smckusick 	struct mbuf **repp;
14039742Smckusick {
14167708Smckusick 	register struct nfsrvcache *rp;
14239742Smckusick 	struct mbuf *mb;
14352196Smckusick 	struct sockaddr_in *saddr;
14439742Smckusick 	caddr_t bpos;
14539742Smckusick 	int ret;
14639742Smckusick 
147*68653Smckusick 	/*
148*68653Smckusick 	 * Don't cache recent requests for reliable transport protocols.
149*68653Smckusick 	 * (Maybe we should for the case of a reconnect, but..)
150*68653Smckusick 	 */
151*68653Smckusick 	if (!nd->nd_nam2)
15252196Smckusick 		return (RC_DOIT);
15339742Smckusick loop:
15467708Smckusick 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
15567708Smckusick 	    rp = rp->rc_hash.le_next) {
15652196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
157*68653Smckusick 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
15839742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
15939742Smckusick 				rp->rc_flag |= RC_WANTED;
16043352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
16139742Smckusick 				goto loop;
16239742Smckusick 			}
16339742Smckusick 			rp->rc_flag |= RC_LOCKED;
16455520Smckusick 			/* If not at end of LRU chain, move it there */
16567708Smckusick 			if (rp->rc_lru.tqe_next) {
16667708Smckusick 				TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
16767708Smckusick 				TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
16852196Smckusick 			}
16939742Smckusick 			if (rp->rc_state == RC_UNUSED)
17039742Smckusick 				panic("nfsrv cache");
17160155Smckusick 			if (rp->rc_state == RC_INPROG) {
17239742Smckusick 				nfsstats.srvcache_inproghits++;
17339742Smckusick 				ret = RC_DROPIT;
17439742Smckusick 			} else if (rp->rc_flag & RC_REPSTATUS) {
17560155Smckusick 				nfsstats.srvcache_nonidemdonehits++;
176*68653Smckusick 				nfs_rephead(0, nd, slp, rp->rc_status,
17752196Smckusick 				   0, (u_quad_t *)0, repp, &mb, &bpos);
17839742Smckusick 				ret = RC_REPLY;
17939742Smckusick 			} else if (rp->rc_flag & RC_REPMBUF) {
18060155Smckusick 				nfsstats.srvcache_nonidemdonehits++;
18141901Smckusick 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
18239742Smckusick 						M_WAIT);
18339742Smckusick 				ret = RC_REPLY;
18439742Smckusick 			} else {
18560155Smckusick 				nfsstats.srvcache_idemdonehits++;
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 {
20567708Smckusick 		rp = nfsrvlruhead.tqh_first;
20655520Smckusick 		while ((rp->rc_flag & RC_LOCKED) != 0) {
20755520Smckusick 			rp->rc_flag |= RC_WANTED;
20855520Smckusick 			(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
20967708Smckusick 			rp = nfsrvlruhead.tqh_first;
21055520Smckusick 		}
21155520Smckusick 		rp->rc_flag |= RC_LOCKED;
21267708Smckusick 		LIST_REMOVE(rp, rc_hash);
21367708Smckusick 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
21455520Smckusick 		if (rp->rc_flag & RC_REPMBUF)
21555520Smckusick 			m_freem(rp->rc_reply);
21655520Smckusick 		if (rp->rc_flag & RC_NAM)
21755520Smckusick 			MFREE(rp->rc_nam, mb);
21855520Smckusick 		rp->rc_flag &= (RC_LOCKED | RC_WANTED);
21952196Smckusick 	}
22067708Smckusick 	TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru);
22139742Smckusick 	rp->rc_state = RC_INPROG;
22252196Smckusick 	rp->rc_xid = nd->nd_retxid;
223*68653Smckusick 	saddr = mtod(nd->nd_nam, struct sockaddr_in *);
22452196Smckusick 	switch (saddr->sin_family) {
22552196Smckusick 	case AF_INET:
22652196Smckusick 		rp->rc_flag |= RC_INETADDR;
22752196Smckusick 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
22852196Smckusick 		break;
22952196Smckusick 	case AF_ISO:
23052196Smckusick 	default:
23152196Smckusick 		rp->rc_flag |= RC_NAM;
232*68653Smckusick 		rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT);
23352196Smckusick 		break;
23452196Smckusick 	};
23552196Smckusick 	rp->rc_proc = nd->nd_procnum;
23667708Smckusick 	LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash);
23752196Smckusick 	rp->rc_flag &= ~RC_LOCKED;
23852196Smckusick 	if (rp->rc_flag & RC_WANTED) {
23952196Smckusick 		rp->rc_flag &= ~RC_WANTED;
24052196Smckusick 		wakeup((caddr_t)rp);
24152196Smckusick 	}
24239742Smckusick 	return (RC_DOIT);
24339742Smckusick }
24439742Smckusick 
24539742Smckusick /*
24639742Smckusick  * Update a request cache entry after the rpc has been done
24739742Smckusick  */
24852196Smckusick void
nfsrv_updatecache(nd,repvalid,repmbuf)249*68653Smckusick nfsrv_updatecache(nd, repvalid, repmbuf)
250*68653Smckusick 	register struct nfsrv_descript *nd;
25140253Smckusick 	int repvalid;
25239742Smckusick 	struct mbuf *repmbuf;
25339742Smckusick {
25439742Smckusick 	register struct nfsrvcache *rp;
25539742Smckusick 
256*68653Smckusick 	if (!nd->nd_nam2)
25752196Smckusick 		return;
25839742Smckusick loop:
25967708Smckusick 	for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0;
26067708Smckusick 	    rp = rp->rc_hash.le_next) {
26152196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
262*68653Smckusick 		netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) {
26339742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
26439742Smckusick 				rp->rc_flag |= RC_WANTED;
26543352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
26639742Smckusick 				goto loop;
26739742Smckusick 			}
26839742Smckusick 			rp->rc_flag |= RC_LOCKED;
26939742Smckusick 			rp->rc_state = RC_DONE;
27040253Smckusick 			/*
27140253Smckusick 			 * If we have a valid reply update status and save
27241901Smckusick 			 * the reply for non-idempotent rpc's.
27340253Smckusick 			 */
27460155Smckusick 			if (repvalid && nonidempotent[nd->nd_procnum]) {
275*68653Smckusick 				if ((nd->nd_flag & ND_NFSV3) == 0 &&
276*68653Smckusick 				  nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) {
27760155Smckusick 					rp->rc_status = nd->nd_repstat;
27860155Smckusick 					rp->rc_flag |= RC_REPSTATUS;
27960155Smckusick 				} else {
28060155Smckusick 					rp->rc_reply = m_copym(repmbuf,
28160155Smckusick 						0, M_COPYALL, M_WAIT);
28260155Smckusick 					rp->rc_flag |= RC_REPMBUF;
28339742Smckusick 				}
28439742Smckusick 			}
28539742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
28639742Smckusick 			if (rp->rc_flag & RC_WANTED) {
28739742Smckusick 				rp->rc_flag &= ~RC_WANTED;
28839742Smckusick 				wakeup((caddr_t)rp);
28939742Smckusick 			}
29039742Smckusick 			return;
29139742Smckusick 		}
29239742Smckusick 	}
29339742Smckusick }
29452196Smckusick 
29552196Smckusick /*
29652196Smckusick  * Clean out the cache. Called when the last nfsd terminates.
29752196Smckusick  */
29852196Smckusick void
nfsrv_cleancache()29952196Smckusick nfsrv_cleancache()
30052196Smckusick {
30155520Smckusick 	register struct nfsrvcache *rp, *nextrp;
30252196Smckusick 
30367708Smckusick 	for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) {
30467708Smckusick 		nextrp = rp->rc_lru.tqe_next;
30567708Smckusick 		LIST_REMOVE(rp, rc_hash);
30667708Smckusick 		TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru);
30755520Smckusick 		free(rp, M_NFSD);
30852196Smckusick 	}
30955520Smckusick 	numnfsrvcache = 0;
31052196Smckusick }
311