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