xref: /csrg-svn/sys/nfs/nfs_srvcache.c (revision 44512)
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  *
8*44512Sbostic  * %sccs.include.redist.c%
939742Smckusick  *
10*44512Sbostic  *	@(#)nfs_srvcache.c	7.9 (Berkeley) 06/28/90
1139742Smckusick  */
1239742Smckusick 
1343431Smckusick /*
1443431Smckusick  * Reference: Chet Juszczak, "Improving the Performance and Correctness
1543431Smckusick  *            of an NFS Server", in Proc. Winter 1989 USENIX Conference,
1643431Smckusick  *            pages 53-63. San Diego, February 1989.
1743431Smckusick  */
1843431Smckusick 
1939742Smckusick #include "param.h"
2039742Smckusick #include "user.h"
2139742Smckusick #include "vnode.h"
2239742Smckusick #include "mount.h"
2339742Smckusick #include "kernel.h"
2439759Smckusick #include "systm.h"
2539742Smckusick #include "mbuf.h"
2639742Smckusick #include "socket.h"
2739742Smckusick #include "socketvar.h"
2840152Smckusick #include "../netinet/in.h"
2939742Smckusick #include "nfsm_subs.h"
3039742Smckusick #include "nfsv2.h"
3139742Smckusick #include "nfsrvcache.h"
3239742Smckusick #include "nfs.h"
3339742Smckusick 
3439742Smckusick #if	((NFSRCHSZ&(NFSRCHSZ-1)) == 0)
3539742Smckusick #define	NFSRCHASH(xid)		(((xid)+((xid)>>16))&(NFSRCHSZ-1))
3639742Smckusick #else
3739742Smckusick #define	NFSRCHASH(xid)		(((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ)
3839742Smckusick #endif
3939742Smckusick 
4039742Smckusick union rhead {
4139742Smckusick 	union  rhead *rh_head[2];
4239742Smckusick 	struct nfsrvcache *rh_chain[2];
4339742Smckusick } rhead[NFSRCHSZ];
4439742Smckusick 
4539742Smckusick static struct nfsrvcache nfsrvcachehead;
4639742Smckusick static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ];
4739742Smckusick 
4839742Smckusick #define TRUE	1
4939742Smckusick #define	FALSE	0
5039742Smckusick 
5139742Smckusick /*
5239742Smckusick  * Static array that defines which nfs rpc's are nonidempotent
5339742Smckusick  */
5440118Smckusick int nonidempotent[NFS_NPROCS] = {
5539742Smckusick 	FALSE,
5639742Smckusick 	FALSE,
5739742Smckusick 	TRUE,
5839742Smckusick 	FALSE,
5939742Smckusick 	FALSE,
6039742Smckusick 	FALSE,
6139742Smckusick 	FALSE,
6239742Smckusick 	FALSE,
6339742Smckusick 	TRUE,
6439742Smckusick 	TRUE,
6539742Smckusick 	TRUE,
6639742Smckusick 	TRUE,
6739742Smckusick 	TRUE,
6839742Smckusick 	TRUE,
6939742Smckusick 	TRUE,
7039742Smckusick 	TRUE,
7139742Smckusick 	FALSE,
7239742Smckusick 	FALSE,
7339742Smckusick };
7439742Smckusick 
7539742Smckusick /* True iff the rpc reply is an nfs status ONLY! */
7639742Smckusick static int repliesstatus[NFS_NPROCS] = {
7739742Smckusick 	FALSE,
7839742Smckusick 	FALSE,
7939742Smckusick 	FALSE,
8039742Smckusick 	FALSE,
8139742Smckusick 	FALSE,
8239742Smckusick 	FALSE,
8339742Smckusick 	FALSE,
8439742Smckusick 	FALSE,
8539742Smckusick 	FALSE,
8639742Smckusick 	FALSE,
8739742Smckusick 	TRUE,
8839742Smckusick 	TRUE,
8939742Smckusick 	TRUE,
9039742Smckusick 	TRUE,
9139742Smckusick 	FALSE,
9239742Smckusick 	TRUE,
9339742Smckusick 	FALSE,
9439742Smckusick 	FALSE,
9539742Smckusick };
9639742Smckusick 
9739742Smckusick /*
9839742Smckusick  * Initialize the server request cache list
9939742Smckusick  */
10039742Smckusick nfsrv_initcache()
10139742Smckusick {
10239742Smckusick 	register int i;
10339742Smckusick 	register struct nfsrvcache *rp = nfsrvcache;
10439742Smckusick 	register struct nfsrvcache *hp = &nfsrvcachehead;
10539742Smckusick 	register union  rhead *rh = rhead;
10639742Smckusick 
10739742Smckusick 	for (i = NFSRCHSZ; --i >= 0; rh++) {
10839742Smckusick 		rh->rh_head[0] = rh;
10939742Smckusick 		rh->rh_head[1] = rh;
11039742Smckusick 	}
11139742Smckusick 	hp->rc_next = hp->rc_prev = hp;
11239742Smckusick 	for (i = NFSRVCACHESIZ; i-- > 0; ) {
11339742Smckusick 		rp->rc_state = RC_UNUSED;
11439742Smckusick 		rp->rc_flag = 0;
11539742Smckusick 		rp->rc_forw = rp;
11639742Smckusick 		rp->rc_back = rp;
11739742Smckusick 		rp->rc_next = hp->rc_next;
11839742Smckusick 		hp->rc_next->rc_prev = rp;
11939742Smckusick 		rp->rc_prev = hp;
12039742Smckusick 		hp->rc_next = rp;
12139742Smckusick 		rp++;
12239742Smckusick 	}
12339742Smckusick }
12439742Smckusick 
12539742Smckusick /*
12639742Smckusick  * Look for the request in the cache
12739742Smckusick  * If found then
12839742Smckusick  *    return action and optionally reply
12939742Smckusick  * else
13039742Smckusick  *    insert it in the cache
13139742Smckusick  *
13239742Smckusick  * The rules are as follows:
13339742Smckusick  * - if in progress, return DROP request
13439742Smckusick  * - if completed within DELAY of the current time, return DROP it
13539742Smckusick  * - if completed a longer time ago return REPLY if the reply was cached or
13639742Smckusick  *   return DOIT
13739742Smckusick  * Update/add new request at end of lru list
13839742Smckusick  */
13939742Smckusick nfsrv_getcache(nam, xid, proc, repp)
14039742Smckusick 	struct mbuf *nam;
14139742Smckusick 	u_long xid;
14239742Smckusick 	int proc;
14339742Smckusick 	struct mbuf **repp;
14439742Smckusick {
14539742Smckusick 	register struct nfsrvcache *rp;
14639742Smckusick 	register union  rhead *rh;
14739742Smckusick 	struct mbuf *mb;
14839742Smckusick 	caddr_t bpos;
14939742Smckusick 	int ret;
15039742Smckusick 
15139742Smckusick 	rh = &rhead[NFSRCHASH(xid)];
15239742Smckusick loop:
15339742Smckusick 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
15441901Smckusick 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
15541901Smckusick 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
15639742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
15739742Smckusick 				rp->rc_flag |= RC_WANTED;
15843352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
15939742Smckusick 				goto loop;
16039742Smckusick 			}
16139742Smckusick 			rp->rc_flag |= RC_LOCKED;
16239742Smckusick 			put_at_head(rp);
16339742Smckusick 			if (rp->rc_state == RC_UNUSED)
16439742Smckusick 				panic("nfsrv cache");
16539742Smckusick 			if (rp->rc_state == RC_INPROG ||
16639742Smckusick 			   (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
16739742Smckusick 				nfsstats.srvcache_inproghits++;
16839742Smckusick 				ret = RC_DROPIT;
16939742Smckusick 			} else if (rp->rc_flag & RC_REPSTATUS) {
17039742Smckusick 				nfsstats.srvcache_idemdonehits++;
17139742Smckusick 				nfs_rephead(0, xid, rp->rc_status, repp, &mb,
17239742Smckusick 					&bpos);
17339742Smckusick 				rp->rc_timestamp = time.tv_sec;
17439742Smckusick 				ret = RC_REPLY;
17539742Smckusick 			} else if (rp->rc_flag & RC_REPMBUF) {
17639742Smckusick 				nfsstats.srvcache_idemdonehits++;
17741901Smckusick 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
17839742Smckusick 						M_WAIT);
17939742Smckusick 				rp->rc_timestamp = time.tv_sec;
18039742Smckusick 				ret = RC_REPLY;
18139742Smckusick 			} else {
18239742Smckusick 				nfsstats.srvcache_nonidemdonehits++;
18339742Smckusick 				rp->rc_state = RC_INPROG;
18439742Smckusick 				ret = RC_DOIT;
18539742Smckusick 			}
18639742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
18739742Smckusick 			if (rp->rc_flag & RC_WANTED) {
18839742Smckusick 				rp->rc_flag &= ~RC_WANTED;
18939742Smckusick 				wakeup((caddr_t)rp);
19039742Smckusick 			}
19139742Smckusick 			return (ret);
19239742Smckusick 		}
19339742Smckusick 	}
19439742Smckusick 	nfsstats.srvcache_misses++;
19539742Smckusick 	rp = nfsrvcachehead.rc_prev;
19639742Smckusick 	while ((rp->rc_flag & RC_LOCKED) != 0) {
19739742Smckusick 		rp->rc_flag |= RC_WANTED;
19843352Smckusick 		(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
19939742Smckusick 	}
20039742Smckusick 	remque(rp);
20139742Smckusick 	put_at_head(rp);
20239742Smckusick 	if (rp->rc_flag & RC_REPMBUF)
20339742Smckusick 		mb = rp->rc_reply;
20439742Smckusick 	else
20539742Smckusick 		mb = (struct mbuf *)0;
20639742Smckusick 	rp->rc_flag = 0;
20739742Smckusick 	rp->rc_state = RC_INPROG;
20839742Smckusick 	rp->rc_xid = xid;
20941901Smckusick 	bcopy((caddr_t)nam, (caddr_t)&rp->rc_nam, sizeof (struct mbuf));
21039742Smckusick 	rp->rc_proc = proc;
21139742Smckusick 	insque(rp, rh);
21239742Smckusick 	if (mb)
21339742Smckusick 		m_freem(mb);
21439742Smckusick 	return (RC_DOIT);
21539742Smckusick }
21639742Smckusick 
21739742Smckusick /*
21839742Smckusick  * Update a request cache entry after the rpc has been done
21939742Smckusick  */
22040253Smckusick nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf)
22139742Smckusick 	struct mbuf *nam;
22239742Smckusick 	u_long xid;
22339742Smckusick 	int proc;
22440253Smckusick 	int repvalid;
22539742Smckusick 	int repstat;
22639742Smckusick 	struct mbuf *repmbuf;
22739742Smckusick {
22839742Smckusick 	register struct nfsrvcache *rp;
22939742Smckusick 	register union	rhead *rh;
23039742Smckusick 
23139742Smckusick 	rh = &rhead[NFSRCHASH(xid)];
23239742Smckusick loop:
23339742Smckusick 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
23441901Smckusick 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
23541901Smckusick 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
23639742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
23739742Smckusick 				rp->rc_flag |= RC_WANTED;
23843352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
23939742Smckusick 				goto loop;
24039742Smckusick 			}
24139742Smckusick 			rp->rc_flag |= RC_LOCKED;
24239742Smckusick 			rp->rc_state = RC_DONE;
24340253Smckusick 			/*
24440253Smckusick 			 * If we have a valid reply update status and save
24541901Smckusick 			 * the reply for non-idempotent rpc's.
24641901Smckusick 			 * Otherwise invalidate entry by setting the timestamp
24741901Smckusick 			 * to nil.
24840253Smckusick 			 */
24940253Smckusick 			if (repvalid) {
25040253Smckusick 				rp->rc_timestamp = time.tv_sec;
25140253Smckusick 				if (nonidempotent[proc]) {
25240253Smckusick 					if (repliesstatus[proc]) {
25340253Smckusick 						rp->rc_status = repstat;
25440253Smckusick 						rp->rc_flag |= RC_REPSTATUS;
25540253Smckusick 					} else {
25641901Smckusick 						rp->rc_reply = m_copym(repmbuf,
25740253Smckusick 							0, M_COPYALL, M_WAIT);
25840253Smckusick 						rp->rc_flag |= RC_REPMBUF;
25940253Smckusick 					}
26039742Smckusick 				}
26140253Smckusick 			} else {
26240253Smckusick 				rp->rc_timestamp = 0;
26339742Smckusick 			}
26439742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
26539742Smckusick 			if (rp->rc_flag & RC_WANTED) {
26639742Smckusick 				rp->rc_flag &= ~RC_WANTED;
26739742Smckusick 				wakeup((caddr_t)rp);
26839742Smckusick 			}
26939742Smckusick 			return;
27039742Smckusick 		}
27139742Smckusick 	}
27239742Smckusick }
273