xref: /csrg-svn/sys/nfs/nfs_srvcache.c (revision 52196)
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*52196Smckusick  *	@(#)nfs_srvcache.c	7.12 (Berkeley) 01/14/92
1139742Smckusick  */
1239742Smckusick 
1343431Smckusick /*
1443431Smckusick  * Reference: Chet Juszczak, "Improving the Performance and Correctness
15*52196Smckusick  *		of an NFS Server", in Proc. Winter 1989 USENIX Conference,
16*52196Smckusick  *		pages 53-63. San Diego, February 1989.
1743431Smckusick  */
1839742Smckusick #include "param.h"
1939742Smckusick #include "vnode.h"
2039742Smckusick #include "mount.h"
2139742Smckusick #include "kernel.h"
2239759Smckusick #include "systm.h"
2339742Smckusick #include "mbuf.h"
2439742Smckusick #include "socket.h"
2539742Smckusick #include "socketvar.h"
26*52196Smckusick #include "netinet/in.h"
27*52196Smckusick #ifdef ISO
28*52196Smckusick #include "netiso/iso.h"
29*52196Smckusick #endif
3039742Smckusick #include "nfsm_subs.h"
31*52196Smckusick #include "rpcv2.h"
3239742Smckusick #include "nfsv2.h"
3339742Smckusick #include "nfsrvcache.h"
3439742Smckusick #include "nfs.h"
35*52196Smckusick #include "nqnfs.h"
3639742Smckusick 
3739742Smckusick #if	((NFSRCHSZ&(NFSRCHSZ-1)) == 0)
3839742Smckusick #define	NFSRCHASH(xid)		(((xid)+((xid)>>16))&(NFSRCHSZ-1))
3939742Smckusick #else
4039742Smckusick #define	NFSRCHASH(xid)		(((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ)
4139742Smckusick #endif
4239742Smckusick 
4339742Smckusick union rhead {
4439742Smckusick 	union  rhead *rh_head[2];
4539742Smckusick 	struct nfsrvcache *rh_chain[2];
4639742Smckusick } rhead[NFSRCHSZ];
4739742Smckusick 
4839742Smckusick static struct nfsrvcache nfsrvcachehead;
4939742Smckusick static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ];
5039742Smckusick 
5139742Smckusick #define TRUE	1
5239742Smckusick #define	FALSE	0
5339742Smckusick 
54*52196Smckusick #define	NETFAMILY(rp) \
55*52196Smckusick 		(((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
56*52196Smckusick 
5739742Smckusick /*
5839742Smckusick  * Static array that defines which nfs rpc's are nonidempotent
5939742Smckusick  */
6040118Smckusick int nonidempotent[NFS_NPROCS] = {
6139742Smckusick 	FALSE,
6239742Smckusick 	FALSE,
6339742Smckusick 	TRUE,
6439742Smckusick 	FALSE,
6539742Smckusick 	FALSE,
6639742Smckusick 	FALSE,
6739742Smckusick 	FALSE,
6839742Smckusick 	FALSE,
6939742Smckusick 	TRUE,
7039742Smckusick 	TRUE,
7139742Smckusick 	TRUE,
7239742Smckusick 	TRUE,
7339742Smckusick 	TRUE,
7439742Smckusick 	TRUE,
7539742Smckusick 	TRUE,
7639742Smckusick 	TRUE,
7739742Smckusick 	FALSE,
7839742Smckusick 	FALSE,
79*52196Smckusick 	FALSE,
80*52196Smckusick 	FALSE,
81*52196Smckusick 	FALSE,
82*52196Smckusick 	FALSE,
8339742Smckusick };
8439742Smckusick 
8539742Smckusick /* True iff the rpc reply is an nfs status ONLY! */
8639742Smckusick static int repliesstatus[NFS_NPROCS] = {
8739742Smckusick 	FALSE,
8839742Smckusick 	FALSE,
8939742Smckusick 	FALSE,
9039742Smckusick 	FALSE,
9139742Smckusick 	FALSE,
9239742Smckusick 	FALSE,
9339742Smckusick 	FALSE,
9439742Smckusick 	FALSE,
9539742Smckusick 	FALSE,
9639742Smckusick 	FALSE,
9739742Smckusick 	TRUE,
9839742Smckusick 	TRUE,
9939742Smckusick 	TRUE,
10039742Smckusick 	TRUE,
10139742Smckusick 	FALSE,
10239742Smckusick 	TRUE,
10339742Smckusick 	FALSE,
10439742Smckusick 	FALSE,
105*52196Smckusick 	FALSE,
106*52196Smckusick 	FALSE,
107*52196Smckusick 	FALSE,
108*52196Smckusick 	FALSE,
10939742Smckusick };
11039742Smckusick 
11139742Smckusick /*
11239742Smckusick  * Initialize the server request cache list
11339742Smckusick  */
11439742Smckusick nfsrv_initcache()
11539742Smckusick {
11639742Smckusick 	register int i;
11739742Smckusick 	register struct nfsrvcache *rp = nfsrvcache;
11839742Smckusick 	register struct nfsrvcache *hp = &nfsrvcachehead;
11939742Smckusick 	register union  rhead *rh = rhead;
12039742Smckusick 
12139742Smckusick 	for (i = NFSRCHSZ; --i >= 0; rh++) {
12239742Smckusick 		rh->rh_head[0] = rh;
12339742Smckusick 		rh->rh_head[1] = rh;
12439742Smckusick 	}
12539742Smckusick 	hp->rc_next = hp->rc_prev = hp;
12639742Smckusick 	for (i = NFSRVCACHESIZ; i-- > 0; ) {
12739742Smckusick 		rp->rc_state = RC_UNUSED;
12839742Smckusick 		rp->rc_flag = 0;
12939742Smckusick 		rp->rc_forw = rp;
13039742Smckusick 		rp->rc_back = rp;
13139742Smckusick 		rp->rc_next = hp->rc_next;
13239742Smckusick 		hp->rc_next->rc_prev = rp;
13339742Smckusick 		rp->rc_prev = hp;
13439742Smckusick 		hp->rc_next = rp;
13539742Smckusick 		rp++;
13639742Smckusick 	}
13739742Smckusick }
13839742Smckusick 
13939742Smckusick /*
14039742Smckusick  * Look for the request in the cache
14139742Smckusick  * If found then
14239742Smckusick  *    return action and optionally reply
14339742Smckusick  * else
14439742Smckusick  *    insert it in the cache
14539742Smckusick  *
14639742Smckusick  * The rules are as follows:
14739742Smckusick  * - if in progress, return DROP request
14839742Smckusick  * - if completed within DELAY of the current time, return DROP it
14939742Smckusick  * - if completed a longer time ago return REPLY if the reply was cached or
15039742Smckusick  *   return DOIT
15139742Smckusick  * Update/add new request at end of lru list
15239742Smckusick  */
153*52196Smckusick nfsrv_getcache(nam, nd, repp)
15439742Smckusick 	struct mbuf *nam;
155*52196Smckusick 	register struct nfsd *nd;
15639742Smckusick 	struct mbuf **repp;
15739742Smckusick {
15839742Smckusick 	register struct nfsrvcache *rp;
15939742Smckusick 	register union  rhead *rh;
16039742Smckusick 	struct mbuf *mb;
161*52196Smckusick 	struct sockaddr_in *saddr;
16239742Smckusick 	caddr_t bpos;
16339742Smckusick 	int ret;
16439742Smckusick 
165*52196Smckusick 	if (nd->nd_nqlflag != NQL_NOVAL)
166*52196Smckusick 		return (RC_DOIT);
167*52196Smckusick 	rh = &rhead[NFSRCHASH(nd->nd_retxid)];
16839742Smckusick loop:
16939742Smckusick 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
170*52196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
171*52196Smckusick 		nfs_netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) {
17239742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
17339742Smckusick 				rp->rc_flag |= RC_WANTED;
17443352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
17539742Smckusick 				goto loop;
17639742Smckusick 			}
17739742Smckusick 			rp->rc_flag |= RC_LOCKED;
178*52196Smckusick 			if (rp->rc_prev != &nfsrvcachehead) {
179*52196Smckusick 				rp->rc_next->rc_prev = rp->rc_prev;
180*52196Smckusick 				rp->rc_prev->rc_next = rp->rc_next;
181*52196Smckusick 				rp->rc_next = nfsrvcachehead.rc_next;
182*52196Smckusick 				nfsrvcachehead.rc_next = rp;
183*52196Smckusick 				rp->rc_prev = &nfsrvcachehead;
184*52196Smckusick 				rp->rc_next->rc_prev = rp;
185*52196Smckusick 			}
18639742Smckusick 			if (rp->rc_state == RC_UNUSED)
18739742Smckusick 				panic("nfsrv cache");
18839742Smckusick 			if (rp->rc_state == RC_INPROG ||
18939742Smckusick 			   (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
19039742Smckusick 				nfsstats.srvcache_inproghits++;
19139742Smckusick 				ret = RC_DROPIT;
19239742Smckusick 			} else if (rp->rc_flag & RC_REPSTATUS) {
19339742Smckusick 				nfsstats.srvcache_idemdonehits++;
194*52196Smckusick 				nfs_rephead(0, nd, rp->rc_status,
195*52196Smckusick 				   0, (u_quad_t *)0, repp, &mb, &bpos);
19639742Smckusick 				rp->rc_timestamp = time.tv_sec;
19739742Smckusick 				ret = RC_REPLY;
19839742Smckusick 			} else if (rp->rc_flag & RC_REPMBUF) {
19939742Smckusick 				nfsstats.srvcache_idemdonehits++;
20041901Smckusick 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
20139742Smckusick 						M_WAIT);
20239742Smckusick 				rp->rc_timestamp = time.tv_sec;
20339742Smckusick 				ret = RC_REPLY;
20439742Smckusick 			} else {
20539742Smckusick 				nfsstats.srvcache_nonidemdonehits++;
20639742Smckusick 				rp->rc_state = RC_INPROG;
20739742Smckusick 				ret = RC_DOIT;
20839742Smckusick 			}
20939742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
21039742Smckusick 			if (rp->rc_flag & RC_WANTED) {
21139742Smckusick 				rp->rc_flag &= ~RC_WANTED;
21239742Smckusick 				wakeup((caddr_t)rp);
21339742Smckusick 			}
21439742Smckusick 			return (ret);
21539742Smckusick 		}
21639742Smckusick 	}
21739742Smckusick 	nfsstats.srvcache_misses++;
21839742Smckusick 	rp = nfsrvcachehead.rc_prev;
21939742Smckusick 	while ((rp->rc_flag & RC_LOCKED) != 0) {
22039742Smckusick 		rp->rc_flag |= RC_WANTED;
22143352Smckusick 		(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
222*52196Smckusick 		rp = nfsrvcachehead.rc_prev;
22339742Smckusick 	}
224*52196Smckusick 	rp->rc_flag |= RC_LOCKED;
22539742Smckusick 	remque(rp);
226*52196Smckusick 	if (rp->rc_prev != &nfsrvcachehead) {
227*52196Smckusick 		rp->rc_next->rc_prev = rp->rc_prev;
228*52196Smckusick 		rp->rc_prev->rc_next = rp->rc_next;
229*52196Smckusick 		rp->rc_next = nfsrvcachehead.rc_next;
230*52196Smckusick 		nfsrvcachehead.rc_next = rp;
231*52196Smckusick 		rp->rc_prev = &nfsrvcachehead;
232*52196Smckusick 		rp->rc_next->rc_prev = rp;
233*52196Smckusick 	}
23439742Smckusick 	if (rp->rc_flag & RC_REPMBUF)
235*52196Smckusick 		m_freem(rp->rc_reply);
236*52196Smckusick 	if (rp->rc_flag & RC_NAM)
237*52196Smckusick 		MFREE(rp->rc_nam, mb);
238*52196Smckusick 	rp->rc_flag &= (RC_LOCKED | RC_WANTED);
23939742Smckusick 	rp->rc_state = RC_INPROG;
240*52196Smckusick 	rp->rc_xid = nd->nd_retxid;
241*52196Smckusick 	saddr = mtod(nam, struct sockaddr_in *);
242*52196Smckusick 	switch (saddr->sin_family) {
243*52196Smckusick 	case AF_INET:
244*52196Smckusick 		rp->rc_flag |= RC_INETADDR;
245*52196Smckusick 		rp->rc_inetaddr = saddr->sin_addr.s_addr;
246*52196Smckusick 		break;
247*52196Smckusick 	case AF_ISO:
248*52196Smckusick 	default:
249*52196Smckusick 		rp->rc_flag |= RC_NAM;
250*52196Smckusick 		rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
251*52196Smckusick 		break;
252*52196Smckusick 	};
253*52196Smckusick 	rp->rc_proc = nd->nd_procnum;
25439742Smckusick 	insque(rp, rh);
255*52196Smckusick 	rp->rc_flag &= ~RC_LOCKED;
256*52196Smckusick 	if (rp->rc_flag & RC_WANTED) {
257*52196Smckusick 		rp->rc_flag &= ~RC_WANTED;
258*52196Smckusick 		wakeup((caddr_t)rp);
259*52196Smckusick 	}
26039742Smckusick 	return (RC_DOIT);
26139742Smckusick }
26239742Smckusick 
26339742Smckusick /*
26439742Smckusick  * Update a request cache entry after the rpc has been done
26539742Smckusick  */
266*52196Smckusick void
267*52196Smckusick nfsrv_updatecache(nam, nd, repvalid, repmbuf)
26839742Smckusick 	struct mbuf *nam;
269*52196Smckusick 	register struct nfsd *nd;
27040253Smckusick 	int repvalid;
27139742Smckusick 	struct mbuf *repmbuf;
27239742Smckusick {
27339742Smckusick 	register struct nfsrvcache *rp;
27439742Smckusick 	register union	rhead *rh;
27539742Smckusick 
276*52196Smckusick 	if (nd->nd_nqlflag != NQL_NOVAL)
277*52196Smckusick 		return;
278*52196Smckusick 	rh = &rhead[NFSRCHASH(nd->nd_retxid)];
27939742Smckusick loop:
28039742Smckusick 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
281*52196Smckusick 	    if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
282*52196Smckusick 		nfs_netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) {
28339742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
28439742Smckusick 				rp->rc_flag |= RC_WANTED;
28543352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
28639742Smckusick 				goto loop;
28739742Smckusick 			}
28839742Smckusick 			rp->rc_flag |= RC_LOCKED;
28939742Smckusick 			rp->rc_state = RC_DONE;
29040253Smckusick 			/*
29140253Smckusick 			 * If we have a valid reply update status and save
29241901Smckusick 			 * the reply for non-idempotent rpc's.
29341901Smckusick 			 * Otherwise invalidate entry by setting the timestamp
29441901Smckusick 			 * to nil.
29540253Smckusick 			 */
29640253Smckusick 			if (repvalid) {
29740253Smckusick 				rp->rc_timestamp = time.tv_sec;
298*52196Smckusick 				if (nonidempotent[nd->nd_procnum]) {
299*52196Smckusick 					if (repliesstatus[nd->nd_procnum]) {
300*52196Smckusick 						rp->rc_status = nd->nd_repstat;
30140253Smckusick 						rp->rc_flag |= RC_REPSTATUS;
30240253Smckusick 					} else {
30341901Smckusick 						rp->rc_reply = m_copym(repmbuf,
30440253Smckusick 							0, M_COPYALL, M_WAIT);
30540253Smckusick 						rp->rc_flag |= RC_REPMBUF;
30640253Smckusick 					}
30739742Smckusick 				}
30840253Smckusick 			} else {
30940253Smckusick 				rp->rc_timestamp = 0;
31039742Smckusick 			}
31139742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
31239742Smckusick 			if (rp->rc_flag & RC_WANTED) {
31339742Smckusick 				rp->rc_flag &= ~RC_WANTED;
31439742Smckusick 				wakeup((caddr_t)rp);
31539742Smckusick 			}
31639742Smckusick 			return;
31739742Smckusick 		}
31839742Smckusick 	}
31939742Smckusick }
320*52196Smckusick 
321*52196Smckusick /*
322*52196Smckusick  * Clean out the cache. Called when the last nfsd terminates.
323*52196Smckusick  */
324*52196Smckusick void
325*52196Smckusick nfsrv_cleancache()
326*52196Smckusick {
327*52196Smckusick 	register int i;
328*52196Smckusick 	register struct nfsrvcache *rp = nfsrvcache;
329*52196Smckusick 	register struct nfsrvcache *hp = &nfsrvcachehead;
330*52196Smckusick 	register union  rhead *rh = rhead;
331*52196Smckusick 
332*52196Smckusick 	for (i = NFSRCHSZ; --i >= 0; rh++) {
333*52196Smckusick 		rh->rh_head[0] = rh;
334*52196Smckusick 		rh->rh_head[1] = rh;
335*52196Smckusick 	}
336*52196Smckusick 	hp->rc_next = hp->rc_prev = hp;
337*52196Smckusick 	for (i = NFSRVCACHESIZ; i-- > 0; ) {
338*52196Smckusick 		if (rp->rc_flag & RC_REPMBUF)
339*52196Smckusick 			m_freem(rp->rc_reply);
340*52196Smckusick 		if (rp->rc_flag & RC_NAM)
341*52196Smckusick 			m_freem(rp->rc_nam);
342*52196Smckusick 		rp->rc_state = RC_UNUSED;
343*52196Smckusick 		rp->rc_flag = 0;
344*52196Smckusick 		rp->rc_forw = rp;
345*52196Smckusick 		rp->rc_back = rp;
346*52196Smckusick 		rp->rc_next = hp->rc_next;
347*52196Smckusick 		hp->rc_next->rc_prev = rp;
348*52196Smckusick 		rp->rc_prev = hp;
349*52196Smckusick 		hp->rc_next = rp;
350*52196Smckusick 		rp++;
351*52196Smckusick 	}
352*52196Smckusick }
353