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*58365Smckusick * @(#)nfs_srvcache.c 7.20 (Berkeley) 03/02/93 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 */ 1854874Smckusick #include <sys/param.h> 1954874Smckusick #include <sys/vnode.h> 2054874Smckusick #include <sys/mount.h> 2154874Smckusick #include <sys/kernel.h> 2254874Smckusick #include <sys/systm.h> 2354874Smckusick #include <sys/proc.h> 2454874Smckusick #include <sys/mbuf.h> 2555520Smckusick #include <sys/malloc.h> 2654874Smckusick #include <sys/socket.h> 2754874Smckusick #include <sys/socketvar.h> 2856535Sbostic 2954874Smckusick #include <netinet/in.h> 3052196Smckusick #ifdef ISO 3154874Smckusick #include <netiso/iso.h> 3252196Smckusick #endif 3354874Smckusick #include <nfs/nfsm_subs.h> 3454874Smckusick #include <nfs/rpcv2.h> 3554874Smckusick #include <nfs/nfsv2.h> 3654874Smckusick #include <nfs/nfs.h> 3754874Smckusick #include <nfs/nfsrvcache.h> 3854874Smckusick #include <nfs/nqnfs.h> 3939742Smckusick 4055520Smckusick long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ; 4139742Smckusick 4255520Smckusick #define NFSRCHASH(xid) (((xid) + ((xid) >> 16)) & rheadhash) 4355520Smckusick static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead; 4455520Smckusick static struct nfsrvcache **rheadhtbl; 4555520Smckusick static u_long rheadhash; 4639742Smckusick 4739742Smckusick #define TRUE 1 4839742Smckusick #define FALSE 0 4939742Smckusick 5052196Smckusick #define NETFAMILY(rp) \ 5152196Smckusick (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO) 5252196Smckusick 5339742Smckusick /* 5439742Smckusick * Static array that defines which nfs rpc's are nonidempotent 5539742Smckusick */ 5640118Smckusick int nonidempotent[NFS_NPROCS] = { 5739742Smckusick FALSE, 5839742Smckusick FALSE, 5939742Smckusick TRUE, 6039742Smckusick FALSE, 6139742Smckusick FALSE, 6239742Smckusick FALSE, 6339742Smckusick FALSE, 6439742Smckusick FALSE, 6539742Smckusick TRUE, 6639742Smckusick TRUE, 6739742Smckusick TRUE, 6839742Smckusick TRUE, 6939742Smckusick TRUE, 7039742Smckusick TRUE, 7139742Smckusick TRUE, 7239742Smckusick TRUE, 7339742Smckusick FALSE, 7439742Smckusick FALSE, 7552196Smckusick FALSE, 7652196Smckusick FALSE, 7752196Smckusick FALSE, 7852196Smckusick FALSE, 7956362Smckusick FALSE, 8039742Smckusick }; 8139742Smckusick 8239742Smckusick /* True iff the rpc reply is an nfs status ONLY! */ 8339742Smckusick static int repliesstatus[NFS_NPROCS] = { 8439742Smckusick FALSE, 8539742Smckusick FALSE, 8639742Smckusick FALSE, 8739742Smckusick FALSE, 8839742Smckusick FALSE, 8939742Smckusick FALSE, 9039742Smckusick FALSE, 9139742Smckusick FALSE, 9239742Smckusick FALSE, 9339742Smckusick FALSE, 9439742Smckusick TRUE, 9539742Smckusick TRUE, 9639742Smckusick TRUE, 9739742Smckusick TRUE, 9839742Smckusick FALSE, 9939742Smckusick TRUE, 10039742Smckusick FALSE, 10139742Smckusick FALSE, 10252196Smckusick FALSE, 10352196Smckusick FALSE, 10452196Smckusick FALSE, 10552196Smckusick FALSE, 10656362Smckusick TRUE, 10739742Smckusick }; 10839742Smckusick 10939742Smckusick /* 11039742Smckusick * Initialize the server request cache list 11139742Smckusick */ 11239742Smckusick nfsrv_initcache() 11339742Smckusick { 11439742Smckusick 11555520Smckusick rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash); 11639742Smckusick } 11739742Smckusick 11839742Smckusick /* 11939742Smckusick * Look for the request in the cache 12039742Smckusick * If found then 12139742Smckusick * return action and optionally reply 12239742Smckusick * else 12339742Smckusick * insert it in the cache 12439742Smckusick * 12539742Smckusick * The rules are as follows: 12639742Smckusick * - if in progress, return DROP request 12739742Smckusick * - if completed within DELAY of the current time, return DROP it 12839742Smckusick * - if completed a longer time ago return REPLY if the reply was cached or 12939742Smckusick * return DOIT 13039742Smckusick * Update/add new request at end of lru list 13139742Smckusick */ 13252196Smckusick nfsrv_getcache(nam, nd, repp) 13339742Smckusick struct mbuf *nam; 13452196Smckusick register struct nfsd *nd; 13539742Smckusick struct mbuf **repp; 13639742Smckusick { 13755520Smckusick register struct nfsrvcache *rp, *rq, **rpp; 13839742Smckusick struct mbuf *mb; 13952196Smckusick struct sockaddr_in *saddr; 14039742Smckusick caddr_t bpos; 14139742Smckusick int ret; 14239742Smckusick 14352196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 14452196Smckusick return (RC_DOIT); 14555520Smckusick rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)]; 14639742Smckusick loop: 14755520Smckusick for (rp = *rpp; rp; rp = rp->rc_forw) { 14852196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 14956362Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) { 15039742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 15139742Smckusick rp->rc_flag |= RC_WANTED; 15243352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 15339742Smckusick goto loop; 15439742Smckusick } 15539742Smckusick rp->rc_flag |= RC_LOCKED; 15655520Smckusick /* If not at end of LRU chain, move it there */ 15755520Smckusick if (rp->rc_next) { 15855520Smckusick /* remove from LRU chain */ 15955520Smckusick *rp->rc_prev = rp->rc_next; 16052196Smckusick rp->rc_next->rc_prev = rp->rc_prev; 16155520Smckusick /* and replace at end of it */ 16255520Smckusick rp->rc_next = NULL; 16355520Smckusick rp->rc_prev = nfsrvlrutail; 16455520Smckusick *nfsrvlrutail = rp; 16555520Smckusick nfsrvlrutail = &rp->rc_next; 16652196Smckusick } 16739742Smckusick if (rp->rc_state == RC_UNUSED) 16839742Smckusick panic("nfsrv cache"); 16939742Smckusick if (rp->rc_state == RC_INPROG || 17039742Smckusick (time.tv_sec - rp->rc_timestamp) < RC_DELAY) { 17139742Smckusick nfsstats.srvcache_inproghits++; 17239742Smckusick ret = RC_DROPIT; 17339742Smckusick } else if (rp->rc_flag & RC_REPSTATUS) { 17439742Smckusick nfsstats.srvcache_idemdonehits++; 17552196Smckusick nfs_rephead(0, nd, rp->rc_status, 17652196Smckusick 0, (u_quad_t *)0, repp, &mb, &bpos); 17739742Smckusick rp->rc_timestamp = time.tv_sec; 17839742Smckusick ret = RC_REPLY; 17939742Smckusick } else if (rp->rc_flag & RC_REPMBUF) { 18039742Smckusick nfsstats.srvcache_idemdonehits++; 18141901Smckusick *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 18239742Smckusick M_WAIT); 18339742Smckusick rp->rc_timestamp = time.tv_sec; 18439742Smckusick ret = RC_REPLY; 18539742Smckusick } else { 18639742Smckusick nfsstats.srvcache_nonidemdonehits++; 18739742Smckusick rp->rc_state = RC_INPROG; 18839742Smckusick ret = RC_DOIT; 18939742Smckusick } 19039742Smckusick rp->rc_flag &= ~RC_LOCKED; 19139742Smckusick if (rp->rc_flag & RC_WANTED) { 19239742Smckusick rp->rc_flag &= ~RC_WANTED; 19339742Smckusick wakeup((caddr_t)rp); 19439742Smckusick } 19539742Smckusick return (ret); 19639742Smckusick } 19739742Smckusick } 19839742Smckusick nfsstats.srvcache_misses++; 19955520Smckusick if (numnfsrvcache < desirednfsrvcache) { 20055520Smckusick rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp, 20155520Smckusick M_NFSD, M_WAITOK); 20255520Smckusick bzero((char *)rp, sizeof *rp); 20355520Smckusick numnfsrvcache++; 20455520Smckusick rp->rc_flag = RC_LOCKED; 20555520Smckusick } else { 20655520Smckusick rp = nfsrvlruhead; 20755520Smckusick while ((rp->rc_flag & RC_LOCKED) != 0) { 20855520Smckusick rp->rc_flag |= RC_WANTED; 20955520Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 21055520Smckusick rp = nfsrvlruhead; 21155520Smckusick } 21255520Smckusick rp->rc_flag |= RC_LOCKED; 21355520Smckusick /* remove from hash chain */ 21455520Smckusick if (rq = rp->rc_forw) 21555520Smckusick rq->rc_back = rp->rc_back; 21655520Smckusick *rp->rc_back = rq; 21755520Smckusick /* remove from LRU chain */ 21855520Smckusick *rp->rc_prev = rp->rc_next; 21952196Smckusick rp->rc_next->rc_prev = rp->rc_prev; 22055520Smckusick if (rp->rc_flag & RC_REPMBUF) 22155520Smckusick m_freem(rp->rc_reply); 22255520Smckusick if (rp->rc_flag & RC_NAM) 22355520Smckusick MFREE(rp->rc_nam, mb); 22455520Smckusick rp->rc_flag &= (RC_LOCKED | RC_WANTED); 22552196Smckusick } 22655520Smckusick /* place at end of LRU list */ 22755520Smckusick rp->rc_next = NULL; 22855520Smckusick rp->rc_prev = nfsrvlrutail; 22955520Smckusick *nfsrvlrutail = rp; 23055520Smckusick nfsrvlrutail = &rp->rc_next; 23139742Smckusick rp->rc_state = RC_INPROG; 23252196Smckusick rp->rc_xid = nd->nd_retxid; 23352196Smckusick saddr = mtod(nam, struct sockaddr_in *); 23452196Smckusick switch (saddr->sin_family) { 23552196Smckusick case AF_INET: 23652196Smckusick rp->rc_flag |= RC_INETADDR; 23752196Smckusick rp->rc_inetaddr = saddr->sin_addr.s_addr; 23852196Smckusick break; 23952196Smckusick case AF_ISO: 24052196Smckusick default: 24152196Smckusick rp->rc_flag |= RC_NAM; 24252196Smckusick rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT); 24352196Smckusick break; 24452196Smckusick }; 24552196Smckusick rp->rc_proc = nd->nd_procnum; 24655520Smckusick /* insert into hash chain */ 24755520Smckusick if (rq = *rpp) 24855520Smckusick rq->rc_back = &rp->rc_forw; 24956275Smckusick rp->rc_forw = rq; 25055520Smckusick rp->rc_back = rpp; 25155520Smckusick *rpp = rp; 25252196Smckusick rp->rc_flag &= ~RC_LOCKED; 25352196Smckusick if (rp->rc_flag & RC_WANTED) { 25452196Smckusick rp->rc_flag &= ~RC_WANTED; 25552196Smckusick wakeup((caddr_t)rp); 25652196Smckusick } 25739742Smckusick return (RC_DOIT); 25839742Smckusick } 25939742Smckusick 26039742Smckusick /* 26139742Smckusick * Update a request cache entry after the rpc has been done 26239742Smckusick */ 26352196Smckusick void 26452196Smckusick nfsrv_updatecache(nam, nd, repvalid, repmbuf) 26539742Smckusick struct mbuf *nam; 26652196Smckusick register struct nfsd *nd; 26740253Smckusick int repvalid; 26839742Smckusick struct mbuf *repmbuf; 26939742Smckusick { 27039742Smckusick register struct nfsrvcache *rp; 27139742Smckusick 27252196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 27352196Smckusick return; 27439742Smckusick loop: 27555520Smckusick for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) { 27652196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 27756362Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) { 27839742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 27939742Smckusick rp->rc_flag |= RC_WANTED; 28043352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 28139742Smckusick goto loop; 28239742Smckusick } 28339742Smckusick rp->rc_flag |= RC_LOCKED; 28439742Smckusick rp->rc_state = RC_DONE; 28540253Smckusick /* 28640253Smckusick * If we have a valid reply update status and save 28741901Smckusick * the reply for non-idempotent rpc's. 28841901Smckusick * Otherwise invalidate entry by setting the timestamp 28941901Smckusick * to nil. 29040253Smckusick */ 29140253Smckusick if (repvalid) { 29240253Smckusick rp->rc_timestamp = time.tv_sec; 29352196Smckusick if (nonidempotent[nd->nd_procnum]) { 29452196Smckusick if (repliesstatus[nd->nd_procnum]) { 29552196Smckusick rp->rc_status = nd->nd_repstat; 29640253Smckusick rp->rc_flag |= RC_REPSTATUS; 29740253Smckusick } else { 29841901Smckusick rp->rc_reply = m_copym(repmbuf, 29940253Smckusick 0, M_COPYALL, M_WAIT); 30040253Smckusick rp->rc_flag |= RC_REPMBUF; 30140253Smckusick } 30239742Smckusick } 30340253Smckusick } else { 30440253Smckusick rp->rc_timestamp = 0; 30539742Smckusick } 30639742Smckusick rp->rc_flag &= ~RC_LOCKED; 30739742Smckusick if (rp->rc_flag & RC_WANTED) { 30839742Smckusick rp->rc_flag &= ~RC_WANTED; 30939742Smckusick wakeup((caddr_t)rp); 31039742Smckusick } 31139742Smckusick return; 31239742Smckusick } 31339742Smckusick } 31439742Smckusick } 31552196Smckusick 31652196Smckusick /* 31752196Smckusick * Clean out the cache. Called when the last nfsd terminates. 31852196Smckusick */ 31952196Smckusick void 32052196Smckusick nfsrv_cleancache() 32152196Smckusick { 32255520Smckusick register struct nfsrvcache *rp, *nextrp; 32352196Smckusick 32455520Smckusick for (rp = nfsrvlruhead; rp; rp = nextrp) { 32555520Smckusick nextrp = rp->rc_next; 32655520Smckusick free(rp, M_NFSD); 32752196Smckusick } 32855520Smckusick bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *)); 329*58365Smckusick nfsrvlruhead = NULL; 330*58365Smckusick nfsrvlrutail = &nfsrvlruhead; 33155520Smckusick numnfsrvcache = 0; 33252196Smckusick } 333