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*54874Smckusick * @(#)nfs_srvcache.c 7.15 (Berkeley) 07/09/92 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 */ 18*54874Smckusick #include <sys/param.h> 19*54874Smckusick #include <sys/vnode.h> 20*54874Smckusick #include <sys/mount.h> 21*54874Smckusick #include <sys/kernel.h> 22*54874Smckusick #include <sys/systm.h> 23*54874Smckusick #include <sys/proc.h> 24*54874Smckusick #include <sys/mbuf.h> 25*54874Smckusick #include <sys/socket.h> 26*54874Smckusick #include <sys/socketvar.h> 27*54874Smckusick #include <netinet/in.h> 2852196Smckusick #ifdef ISO 29*54874Smckusick #include <netiso/iso.h> 3052196Smckusick #endif 31*54874Smckusick #include <nfs/nfsm_subs.h> 32*54874Smckusick #include <nfs/rpcv2.h> 33*54874Smckusick #include <nfs/nfsv2.h> 34*54874Smckusick #include <nfs/nfs.h> 35*54874Smckusick #include <nfs/nfsrvcache.h> 36*54874Smckusick #include <nfs/nqnfs.h> 3739742Smckusick 3839742Smckusick #if ((NFSRCHSZ&(NFSRCHSZ-1)) == 0) 3939742Smckusick #define NFSRCHASH(xid) (((xid)+((xid)>>16))&(NFSRCHSZ-1)) 4039742Smckusick #else 4139742Smckusick #define NFSRCHASH(xid) (((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ) 4239742Smckusick #endif 4339742Smckusick 4439742Smckusick union rhead { 4539742Smckusick union rhead *rh_head[2]; 4639742Smckusick struct nfsrvcache *rh_chain[2]; 4739742Smckusick } rhead[NFSRCHSZ]; 4839742Smckusick 4939742Smckusick static struct nfsrvcache nfsrvcachehead; 5039742Smckusick static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ]; 5139742Smckusick 5239742Smckusick #define TRUE 1 5339742Smckusick #define FALSE 0 5439742Smckusick 5552196Smckusick #define NETFAMILY(rp) \ 5652196Smckusick (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO) 5752196Smckusick 5839742Smckusick /* 5939742Smckusick * Static array that defines which nfs rpc's are nonidempotent 6039742Smckusick */ 6140118Smckusick int nonidempotent[NFS_NPROCS] = { 6239742Smckusick FALSE, 6339742Smckusick FALSE, 6439742Smckusick TRUE, 6539742Smckusick FALSE, 6639742Smckusick FALSE, 6739742Smckusick FALSE, 6839742Smckusick FALSE, 6939742Smckusick FALSE, 7039742Smckusick TRUE, 7139742Smckusick TRUE, 7239742Smckusick TRUE, 7339742Smckusick TRUE, 7439742Smckusick TRUE, 7539742Smckusick TRUE, 7639742Smckusick TRUE, 7739742Smckusick TRUE, 7839742Smckusick FALSE, 7939742Smckusick FALSE, 8052196Smckusick FALSE, 8152196Smckusick FALSE, 8252196Smckusick FALSE, 8352196Smckusick FALSE, 8439742Smckusick }; 8539742Smckusick 8639742Smckusick /* True iff the rpc reply is an nfs status ONLY! */ 8739742Smckusick static int repliesstatus[NFS_NPROCS] = { 8839742Smckusick FALSE, 8939742Smckusick FALSE, 9039742Smckusick FALSE, 9139742Smckusick FALSE, 9239742Smckusick FALSE, 9339742Smckusick FALSE, 9439742Smckusick FALSE, 9539742Smckusick FALSE, 9639742Smckusick FALSE, 9739742Smckusick FALSE, 9839742Smckusick TRUE, 9939742Smckusick TRUE, 10039742Smckusick TRUE, 10139742Smckusick TRUE, 10239742Smckusick FALSE, 10339742Smckusick TRUE, 10439742Smckusick FALSE, 10539742Smckusick FALSE, 10652196Smckusick FALSE, 10752196Smckusick FALSE, 10852196Smckusick FALSE, 10952196Smckusick FALSE, 11039742Smckusick }; 11139742Smckusick 11239742Smckusick /* 11339742Smckusick * Initialize the server request cache list 11439742Smckusick */ 11539742Smckusick nfsrv_initcache() 11639742Smckusick { 11739742Smckusick register int i; 11839742Smckusick register struct nfsrvcache *rp = nfsrvcache; 11939742Smckusick register struct nfsrvcache *hp = &nfsrvcachehead; 12039742Smckusick register union rhead *rh = rhead; 12139742Smckusick 12239742Smckusick for (i = NFSRCHSZ; --i >= 0; rh++) { 12339742Smckusick rh->rh_head[0] = rh; 12439742Smckusick rh->rh_head[1] = rh; 12539742Smckusick } 12639742Smckusick hp->rc_next = hp->rc_prev = hp; 12739742Smckusick for (i = NFSRVCACHESIZ; i-- > 0; ) { 12839742Smckusick rp->rc_state = RC_UNUSED; 12939742Smckusick rp->rc_flag = 0; 13039742Smckusick rp->rc_forw = rp; 13139742Smckusick rp->rc_back = rp; 13239742Smckusick rp->rc_next = hp->rc_next; 13339742Smckusick hp->rc_next->rc_prev = rp; 13439742Smckusick rp->rc_prev = hp; 13539742Smckusick hp->rc_next = rp; 13639742Smckusick rp++; 13739742Smckusick } 13839742Smckusick } 13939742Smckusick 14039742Smckusick /* 14139742Smckusick * Look for the request in the cache 14239742Smckusick * If found then 14339742Smckusick * return action and optionally reply 14439742Smckusick * else 14539742Smckusick * insert it in the cache 14639742Smckusick * 14739742Smckusick * The rules are as follows: 14839742Smckusick * - if in progress, return DROP request 14939742Smckusick * - if completed within DELAY of the current time, return DROP it 15039742Smckusick * - if completed a longer time ago return REPLY if the reply was cached or 15139742Smckusick * return DOIT 15239742Smckusick * Update/add new request at end of lru list 15339742Smckusick */ 15452196Smckusick nfsrv_getcache(nam, nd, repp) 15539742Smckusick struct mbuf *nam; 15652196Smckusick register struct nfsd *nd; 15739742Smckusick struct mbuf **repp; 15839742Smckusick { 15939742Smckusick register struct nfsrvcache *rp; 16039742Smckusick register union rhead *rh; 16139742Smckusick struct mbuf *mb; 16252196Smckusick struct sockaddr_in *saddr; 16339742Smckusick caddr_t bpos; 16439742Smckusick int ret; 16539742Smckusick 16652196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 16752196Smckusick return (RC_DOIT); 16852196Smckusick rh = &rhead[NFSRCHASH(nd->nd_retxid)]; 16939742Smckusick loop: 17039742Smckusick for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 17152196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 17254613Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) { 17339742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 17439742Smckusick rp->rc_flag |= RC_WANTED; 17543352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 17639742Smckusick goto loop; 17739742Smckusick } 17839742Smckusick rp->rc_flag |= RC_LOCKED; 17952196Smckusick if (rp->rc_prev != &nfsrvcachehead) { 18052196Smckusick rp->rc_next->rc_prev = rp->rc_prev; 18152196Smckusick rp->rc_prev->rc_next = rp->rc_next; 18252196Smckusick rp->rc_next = nfsrvcachehead.rc_next; 18352196Smckusick nfsrvcachehead.rc_next = rp; 18452196Smckusick rp->rc_prev = &nfsrvcachehead; 18552196Smckusick rp->rc_next->rc_prev = rp; 18652196Smckusick } 18739742Smckusick if (rp->rc_state == RC_UNUSED) 18839742Smckusick panic("nfsrv cache"); 18939742Smckusick if (rp->rc_state == RC_INPROG || 19039742Smckusick (time.tv_sec - rp->rc_timestamp) < RC_DELAY) { 19139742Smckusick nfsstats.srvcache_inproghits++; 19239742Smckusick ret = RC_DROPIT; 19339742Smckusick } else if (rp->rc_flag & RC_REPSTATUS) { 19439742Smckusick nfsstats.srvcache_idemdonehits++; 19552196Smckusick nfs_rephead(0, nd, rp->rc_status, 19652196Smckusick 0, (u_quad_t *)0, repp, &mb, &bpos); 19739742Smckusick rp->rc_timestamp = time.tv_sec; 19839742Smckusick ret = RC_REPLY; 19939742Smckusick } else if (rp->rc_flag & RC_REPMBUF) { 20039742Smckusick nfsstats.srvcache_idemdonehits++; 20141901Smckusick *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 20239742Smckusick M_WAIT); 20339742Smckusick rp->rc_timestamp = time.tv_sec; 20439742Smckusick ret = RC_REPLY; 20539742Smckusick } else { 20639742Smckusick nfsstats.srvcache_nonidemdonehits++; 20739742Smckusick rp->rc_state = RC_INPROG; 20839742Smckusick ret = RC_DOIT; 20939742Smckusick } 21039742Smckusick rp->rc_flag &= ~RC_LOCKED; 21139742Smckusick if (rp->rc_flag & RC_WANTED) { 21239742Smckusick rp->rc_flag &= ~RC_WANTED; 21339742Smckusick wakeup((caddr_t)rp); 21439742Smckusick } 21539742Smckusick return (ret); 21639742Smckusick } 21739742Smckusick } 21839742Smckusick nfsstats.srvcache_misses++; 21939742Smckusick rp = nfsrvcachehead.rc_prev; 22039742Smckusick while ((rp->rc_flag & RC_LOCKED) != 0) { 22139742Smckusick rp->rc_flag |= RC_WANTED; 22243352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 22352196Smckusick rp = nfsrvcachehead.rc_prev; 22439742Smckusick } 22552196Smckusick rp->rc_flag |= RC_LOCKED; 22639742Smckusick remque(rp); 22752196Smckusick if (rp->rc_prev != &nfsrvcachehead) { 22852196Smckusick rp->rc_next->rc_prev = rp->rc_prev; 22952196Smckusick rp->rc_prev->rc_next = rp->rc_next; 23052196Smckusick rp->rc_next = nfsrvcachehead.rc_next; 23152196Smckusick nfsrvcachehead.rc_next = rp; 23252196Smckusick rp->rc_prev = &nfsrvcachehead; 23352196Smckusick rp->rc_next->rc_prev = rp; 23452196Smckusick } 23539742Smckusick if (rp->rc_flag & RC_REPMBUF) 23652196Smckusick m_freem(rp->rc_reply); 23752196Smckusick if (rp->rc_flag & RC_NAM) 23852196Smckusick MFREE(rp->rc_nam, mb); 23952196Smckusick rp->rc_flag &= (RC_LOCKED | RC_WANTED); 24039742Smckusick rp->rc_state = RC_INPROG; 24152196Smckusick rp->rc_xid = nd->nd_retxid; 24252196Smckusick saddr = mtod(nam, struct sockaddr_in *); 24352196Smckusick switch (saddr->sin_family) { 24452196Smckusick case AF_INET: 24552196Smckusick rp->rc_flag |= RC_INETADDR; 24652196Smckusick rp->rc_inetaddr = saddr->sin_addr.s_addr; 24752196Smckusick break; 24852196Smckusick case AF_ISO: 24952196Smckusick default: 25052196Smckusick rp->rc_flag |= RC_NAM; 25152196Smckusick rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT); 25252196Smckusick break; 25352196Smckusick }; 25452196Smckusick rp->rc_proc = nd->nd_procnum; 25539742Smckusick insque(rp, rh); 25652196Smckusick rp->rc_flag &= ~RC_LOCKED; 25752196Smckusick if (rp->rc_flag & RC_WANTED) { 25852196Smckusick rp->rc_flag &= ~RC_WANTED; 25952196Smckusick wakeup((caddr_t)rp); 26052196Smckusick } 26139742Smckusick return (RC_DOIT); 26239742Smckusick } 26339742Smckusick 26439742Smckusick /* 26539742Smckusick * Update a request cache entry after the rpc has been done 26639742Smckusick */ 26752196Smckusick void 26852196Smckusick nfsrv_updatecache(nam, nd, repvalid, repmbuf) 26939742Smckusick struct mbuf *nam; 27052196Smckusick register struct nfsd *nd; 27140253Smckusick int repvalid; 27239742Smckusick struct mbuf *repmbuf; 27339742Smckusick { 27439742Smckusick register struct nfsrvcache *rp; 27539742Smckusick register union rhead *rh; 27639742Smckusick 27752196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 27852196Smckusick return; 27952196Smckusick rh = &rhead[NFSRCHASH(nd->nd_retxid)]; 28039742Smckusick loop: 28139742Smckusick for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 28252196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 28354613Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) { 28439742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 28539742Smckusick rp->rc_flag |= RC_WANTED; 28643352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 28739742Smckusick goto loop; 28839742Smckusick } 28939742Smckusick rp->rc_flag |= RC_LOCKED; 29039742Smckusick rp->rc_state = RC_DONE; 29140253Smckusick /* 29240253Smckusick * If we have a valid reply update status and save 29341901Smckusick * the reply for non-idempotent rpc's. 29441901Smckusick * Otherwise invalidate entry by setting the timestamp 29541901Smckusick * to nil. 29640253Smckusick */ 29740253Smckusick if (repvalid) { 29840253Smckusick rp->rc_timestamp = time.tv_sec; 29952196Smckusick if (nonidempotent[nd->nd_procnum]) { 30052196Smckusick if (repliesstatus[nd->nd_procnum]) { 30152196Smckusick rp->rc_status = nd->nd_repstat; 30240253Smckusick rp->rc_flag |= RC_REPSTATUS; 30340253Smckusick } else { 30441901Smckusick rp->rc_reply = m_copym(repmbuf, 30540253Smckusick 0, M_COPYALL, M_WAIT); 30640253Smckusick rp->rc_flag |= RC_REPMBUF; 30740253Smckusick } 30839742Smckusick } 30940253Smckusick } else { 31040253Smckusick rp->rc_timestamp = 0; 31139742Smckusick } 31239742Smckusick rp->rc_flag &= ~RC_LOCKED; 31339742Smckusick if (rp->rc_flag & RC_WANTED) { 31439742Smckusick rp->rc_flag &= ~RC_WANTED; 31539742Smckusick wakeup((caddr_t)rp); 31639742Smckusick } 31739742Smckusick return; 31839742Smckusick } 31939742Smckusick } 32039742Smckusick } 32152196Smckusick 32252196Smckusick /* 32352196Smckusick * Clean out the cache. Called when the last nfsd terminates. 32452196Smckusick */ 32552196Smckusick void 32652196Smckusick nfsrv_cleancache() 32752196Smckusick { 32852196Smckusick register int i; 32952196Smckusick register struct nfsrvcache *rp = nfsrvcache; 33052196Smckusick register struct nfsrvcache *hp = &nfsrvcachehead; 33152196Smckusick register union rhead *rh = rhead; 33252196Smckusick 33352196Smckusick for (i = NFSRCHSZ; --i >= 0; rh++) { 33452196Smckusick rh->rh_head[0] = rh; 33552196Smckusick rh->rh_head[1] = rh; 33652196Smckusick } 33752196Smckusick hp->rc_next = hp->rc_prev = hp; 33852196Smckusick for (i = NFSRVCACHESIZ; i-- > 0; ) { 33952196Smckusick if (rp->rc_flag & RC_REPMBUF) 34052196Smckusick m_freem(rp->rc_reply); 34152196Smckusick if (rp->rc_flag & RC_NAM) 34252196Smckusick m_freem(rp->rc_nam); 34352196Smckusick rp->rc_state = RC_UNUSED; 34452196Smckusick rp->rc_flag = 0; 34552196Smckusick rp->rc_forw = rp; 34652196Smckusick rp->rc_back = rp; 34752196Smckusick rp->rc_next = hp->rc_next; 34852196Smckusick hp->rc_next->rc_prev = rp; 34952196Smckusick rp->rc_prev = hp; 35052196Smckusick hp->rc_next = rp; 35152196Smckusick rp++; 35252196Smckusick } 35352196Smckusick } 354