xref: /csrg-svn/sys/nfs/nfs_srvcache.c (revision 43431)
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  *
839742Smckusick  * Redistribution and use in source and binary forms are permitted
939742Smckusick  * provided that the above copyright notice and this paragraph are
1039742Smckusick  * duplicated in all such forms and that any documentation,
1139742Smckusick  * advertising materials, and other materials related to such
1239742Smckusick  * distribution and use acknowledge that the software was developed
1339742Smckusick  * by the University of California, Berkeley.  The name of the
1439742Smckusick  * University may not be used to endorse or promote products derived
1539742Smckusick  * from this software without specific prior written permission.
1639742Smckusick  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1739742Smckusick  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1839742Smckusick  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1939742Smckusick  *
20*43431Smckusick  *	@(#)nfs_srvcache.c	7.8 (Berkeley) 06/22/90
2139742Smckusick  */
2239742Smckusick 
23*43431Smckusick /*
24*43431Smckusick  * Reference: Chet Juszczak, "Improving the Performance and Correctness
25*43431Smckusick  *            of an NFS Server", in Proc. Winter 1989 USENIX Conference,
26*43431Smckusick  *            pages 53-63. San Diego, February 1989.
27*43431Smckusick  */
28*43431Smckusick 
2939742Smckusick #include "param.h"
3039742Smckusick #include "user.h"
3139742Smckusick #include "vnode.h"
3239742Smckusick #include "mount.h"
3339742Smckusick #include "kernel.h"
3439759Smckusick #include "systm.h"
3539742Smckusick #include "mbuf.h"
3639742Smckusick #include "socket.h"
3739742Smckusick #include "socketvar.h"
3840152Smckusick #include "../netinet/in.h"
3939742Smckusick #include "nfsm_subs.h"
4039742Smckusick #include "nfsv2.h"
4139742Smckusick #include "nfsrvcache.h"
4239742Smckusick #include "nfs.h"
4339742Smckusick 
4439742Smckusick #if	((NFSRCHSZ&(NFSRCHSZ-1)) == 0)
4539742Smckusick #define	NFSRCHASH(xid)		(((xid)+((xid)>>16))&(NFSRCHSZ-1))
4639742Smckusick #else
4739742Smckusick #define	NFSRCHASH(xid)		(((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ)
4839742Smckusick #endif
4939742Smckusick 
5039742Smckusick union rhead {
5139742Smckusick 	union  rhead *rh_head[2];
5239742Smckusick 	struct nfsrvcache *rh_chain[2];
5339742Smckusick } rhead[NFSRCHSZ];
5439742Smckusick 
5539742Smckusick static struct nfsrvcache nfsrvcachehead;
5639742Smckusick static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ];
5739742Smckusick 
5839742Smckusick #define TRUE	1
5939742Smckusick #define	FALSE	0
6039742Smckusick 
6139742Smckusick /*
6239742Smckusick  * Static array that defines which nfs rpc's are nonidempotent
6339742Smckusick  */
6440118Smckusick int nonidempotent[NFS_NPROCS] = {
6539742Smckusick 	FALSE,
6639742Smckusick 	FALSE,
6739742Smckusick 	TRUE,
6839742Smckusick 	FALSE,
6939742Smckusick 	FALSE,
7039742Smckusick 	FALSE,
7139742Smckusick 	FALSE,
7239742Smckusick 	FALSE,
7339742Smckusick 	TRUE,
7439742Smckusick 	TRUE,
7539742Smckusick 	TRUE,
7639742Smckusick 	TRUE,
7739742Smckusick 	TRUE,
7839742Smckusick 	TRUE,
7939742Smckusick 	TRUE,
8039742Smckusick 	TRUE,
8139742Smckusick 	FALSE,
8239742Smckusick 	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,
10539742Smckusick };
10639742Smckusick 
10739742Smckusick /*
10839742Smckusick  * Initialize the server request cache list
10939742Smckusick  */
11039742Smckusick nfsrv_initcache()
11139742Smckusick {
11239742Smckusick 	register int i;
11339742Smckusick 	register struct nfsrvcache *rp = nfsrvcache;
11439742Smckusick 	register struct nfsrvcache *hp = &nfsrvcachehead;
11539742Smckusick 	register union  rhead *rh = rhead;
11639742Smckusick 
11739742Smckusick 	for (i = NFSRCHSZ; --i >= 0; rh++) {
11839742Smckusick 		rh->rh_head[0] = rh;
11939742Smckusick 		rh->rh_head[1] = rh;
12039742Smckusick 	}
12139742Smckusick 	hp->rc_next = hp->rc_prev = hp;
12239742Smckusick 	for (i = NFSRVCACHESIZ; i-- > 0; ) {
12339742Smckusick 		rp->rc_state = RC_UNUSED;
12439742Smckusick 		rp->rc_flag = 0;
12539742Smckusick 		rp->rc_forw = rp;
12639742Smckusick 		rp->rc_back = rp;
12739742Smckusick 		rp->rc_next = hp->rc_next;
12839742Smckusick 		hp->rc_next->rc_prev = rp;
12939742Smckusick 		rp->rc_prev = hp;
13039742Smckusick 		hp->rc_next = rp;
13139742Smckusick 		rp++;
13239742Smckusick 	}
13339742Smckusick }
13439742Smckusick 
13539742Smckusick /*
13639742Smckusick  * Look for the request in the cache
13739742Smckusick  * If found then
13839742Smckusick  *    return action and optionally reply
13939742Smckusick  * else
14039742Smckusick  *    insert it in the cache
14139742Smckusick  *
14239742Smckusick  * The rules are as follows:
14339742Smckusick  * - if in progress, return DROP request
14439742Smckusick  * - if completed within DELAY of the current time, return DROP it
14539742Smckusick  * - if completed a longer time ago return REPLY if the reply was cached or
14639742Smckusick  *   return DOIT
14739742Smckusick  * Update/add new request at end of lru list
14839742Smckusick  */
14939742Smckusick nfsrv_getcache(nam, xid, proc, repp)
15039742Smckusick 	struct mbuf *nam;
15139742Smckusick 	u_long xid;
15239742Smckusick 	int proc;
15339742Smckusick 	struct mbuf **repp;
15439742Smckusick {
15539742Smckusick 	register struct nfsrvcache *rp;
15639742Smckusick 	register union  rhead *rh;
15739742Smckusick 	struct mbuf *mb;
15839742Smckusick 	caddr_t bpos;
15939742Smckusick 	int ret;
16039742Smckusick 
16139742Smckusick 	rh = &rhead[NFSRCHASH(xid)];
16239742Smckusick loop:
16339742Smckusick 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
16441901Smckusick 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
16541901Smckusick 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
16639742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
16739742Smckusick 				rp->rc_flag |= RC_WANTED;
16843352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
16939742Smckusick 				goto loop;
17039742Smckusick 			}
17139742Smckusick 			rp->rc_flag |= RC_LOCKED;
17239742Smckusick 			put_at_head(rp);
17339742Smckusick 			if (rp->rc_state == RC_UNUSED)
17439742Smckusick 				panic("nfsrv cache");
17539742Smckusick 			if (rp->rc_state == RC_INPROG ||
17639742Smckusick 			   (time.tv_sec - rp->rc_timestamp) < RC_DELAY) {
17739742Smckusick 				nfsstats.srvcache_inproghits++;
17839742Smckusick 				ret = RC_DROPIT;
17939742Smckusick 			} else if (rp->rc_flag & RC_REPSTATUS) {
18039742Smckusick 				nfsstats.srvcache_idemdonehits++;
18139742Smckusick 				nfs_rephead(0, xid, rp->rc_status, repp, &mb,
18239742Smckusick 					&bpos);
18339742Smckusick 				rp->rc_timestamp = time.tv_sec;
18439742Smckusick 				ret = RC_REPLY;
18539742Smckusick 			} else if (rp->rc_flag & RC_REPMBUF) {
18639742Smckusick 				nfsstats.srvcache_idemdonehits++;
18741901Smckusick 				*repp = m_copym(rp->rc_reply, 0, M_COPYALL,
18839742Smckusick 						M_WAIT);
18939742Smckusick 				rp->rc_timestamp = time.tv_sec;
19039742Smckusick 				ret = RC_REPLY;
19139742Smckusick 			} else {
19239742Smckusick 				nfsstats.srvcache_nonidemdonehits++;
19339742Smckusick 				rp->rc_state = RC_INPROG;
19439742Smckusick 				ret = RC_DOIT;
19539742Smckusick 			}
19639742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
19739742Smckusick 			if (rp->rc_flag & RC_WANTED) {
19839742Smckusick 				rp->rc_flag &= ~RC_WANTED;
19939742Smckusick 				wakeup((caddr_t)rp);
20039742Smckusick 			}
20139742Smckusick 			return (ret);
20239742Smckusick 		}
20339742Smckusick 	}
20439742Smckusick 	nfsstats.srvcache_misses++;
20539742Smckusick 	rp = nfsrvcachehead.rc_prev;
20639742Smckusick 	while ((rp->rc_flag & RC_LOCKED) != 0) {
20739742Smckusick 		rp->rc_flag |= RC_WANTED;
20843352Smckusick 		(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
20939742Smckusick 	}
21039742Smckusick 	remque(rp);
21139742Smckusick 	put_at_head(rp);
21239742Smckusick 	if (rp->rc_flag & RC_REPMBUF)
21339742Smckusick 		mb = rp->rc_reply;
21439742Smckusick 	else
21539742Smckusick 		mb = (struct mbuf *)0;
21639742Smckusick 	rp->rc_flag = 0;
21739742Smckusick 	rp->rc_state = RC_INPROG;
21839742Smckusick 	rp->rc_xid = xid;
21941901Smckusick 	bcopy((caddr_t)nam, (caddr_t)&rp->rc_nam, sizeof (struct mbuf));
22039742Smckusick 	rp->rc_proc = proc;
22139742Smckusick 	insque(rp, rh);
22239742Smckusick 	if (mb)
22339742Smckusick 		m_freem(mb);
22439742Smckusick 	return (RC_DOIT);
22539742Smckusick }
22639742Smckusick 
22739742Smckusick /*
22839742Smckusick  * Update a request cache entry after the rpc has been done
22939742Smckusick  */
23040253Smckusick nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf)
23139742Smckusick 	struct mbuf *nam;
23239742Smckusick 	u_long xid;
23339742Smckusick 	int proc;
23440253Smckusick 	int repvalid;
23539742Smckusick 	int repstat;
23639742Smckusick 	struct mbuf *repmbuf;
23739742Smckusick {
23839742Smckusick 	register struct nfsrvcache *rp;
23939742Smckusick 	register union	rhead *rh;
24039742Smckusick 
24139742Smckusick 	rh = &rhead[NFSRCHASH(xid)];
24239742Smckusick loop:
24339742Smckusick 	for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) {
24441901Smckusick 		if (xid == rp->rc_xid && proc == rp->rc_proc &&
24541901Smckusick 		    nfs_netaddr_match(nam, &rp->rc_nam)) {
24639742Smckusick 			if ((rp->rc_flag & RC_LOCKED) != 0) {
24739742Smckusick 				rp->rc_flag |= RC_WANTED;
24843352Smckusick 				(void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
24939742Smckusick 				goto loop;
25039742Smckusick 			}
25139742Smckusick 			rp->rc_flag |= RC_LOCKED;
25239742Smckusick 			rp->rc_state = RC_DONE;
25340253Smckusick 			/*
25440253Smckusick 			 * If we have a valid reply update status and save
25541901Smckusick 			 * the reply for non-idempotent rpc's.
25641901Smckusick 			 * Otherwise invalidate entry by setting the timestamp
25741901Smckusick 			 * to nil.
25840253Smckusick 			 */
25940253Smckusick 			if (repvalid) {
26040253Smckusick 				rp->rc_timestamp = time.tv_sec;
26140253Smckusick 				if (nonidempotent[proc]) {
26240253Smckusick 					if (repliesstatus[proc]) {
26340253Smckusick 						rp->rc_status = repstat;
26440253Smckusick 						rp->rc_flag |= RC_REPSTATUS;
26540253Smckusick 					} else {
26641901Smckusick 						rp->rc_reply = m_copym(repmbuf,
26740253Smckusick 							0, M_COPYALL, M_WAIT);
26840253Smckusick 						rp->rc_flag |= RC_REPMBUF;
26940253Smckusick 					}
27039742Smckusick 				}
27140253Smckusick 			} else {
27240253Smckusick 				rp->rc_timestamp = 0;
27339742Smckusick 			}
27439742Smckusick 			rp->rc_flag &= ~RC_LOCKED;
27539742Smckusick 			if (rp->rc_flag & RC_WANTED) {
27639742Smckusick 				rp->rc_flag &= ~RC_WANTED;
27739742Smckusick 				wakeup((caddr_t)rp);
27839742Smckusick 			}
27939742Smckusick 			return;
28039742Smckusick 		}
28139742Smckusick 	}
28239742Smckusick }
283