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*56362Smckusick * @(#)nfs_srvcache.c 7.18 (Berkeley) 09/30/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 */ 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> 2854874Smckusick #include <netinet/in.h> 2952196Smckusick #ifdef ISO 3054874Smckusick #include <netiso/iso.h> 3152196Smckusick #endif 3254874Smckusick #include <nfs/nfsm_subs.h> 3354874Smckusick #include <nfs/rpcv2.h> 3454874Smckusick #include <nfs/nfsv2.h> 3554874Smckusick #include <nfs/nfs.h> 3654874Smckusick #include <nfs/nfsrvcache.h> 3754874Smckusick #include <nfs/nqnfs.h> 3839742Smckusick 3955520Smckusick long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ; 4039742Smckusick 4155520Smckusick #define NFSRCHASH(xid) (((xid) + ((xid) >> 16)) & rheadhash) 4255520Smckusick static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead; 4355520Smckusick static struct nfsrvcache **rheadhtbl; 4455520Smckusick static u_long rheadhash; 4539742Smckusick 4639742Smckusick #define TRUE 1 4739742Smckusick #define FALSE 0 4839742Smckusick 4952196Smckusick #define NETFAMILY(rp) \ 5052196Smckusick (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO) 5152196Smckusick 5239742Smckusick /* 5339742Smckusick * Static array that defines which nfs rpc's are nonidempotent 5439742Smckusick */ 5540118Smckusick int nonidempotent[NFS_NPROCS] = { 5639742Smckusick FALSE, 5739742Smckusick FALSE, 5839742Smckusick TRUE, 5939742Smckusick FALSE, 6039742Smckusick FALSE, 6139742Smckusick FALSE, 6239742Smckusick FALSE, 6339742Smckusick FALSE, 6439742Smckusick TRUE, 6539742Smckusick TRUE, 6639742Smckusick TRUE, 6739742Smckusick TRUE, 6839742Smckusick TRUE, 6939742Smckusick TRUE, 7039742Smckusick TRUE, 7139742Smckusick TRUE, 7239742Smckusick FALSE, 7339742Smckusick FALSE, 7452196Smckusick FALSE, 7552196Smckusick FALSE, 7652196Smckusick FALSE, 7752196Smckusick FALSE, 78*56362Smckusick FALSE, 7939742Smckusick }; 8039742Smckusick 8139742Smckusick /* True iff the rpc reply is an nfs status ONLY! */ 8239742Smckusick static int repliesstatus[NFS_NPROCS] = { 8339742Smckusick FALSE, 8439742Smckusick FALSE, 8539742Smckusick FALSE, 8639742Smckusick FALSE, 8739742Smckusick FALSE, 8839742Smckusick FALSE, 8939742Smckusick FALSE, 9039742Smckusick FALSE, 9139742Smckusick FALSE, 9239742Smckusick FALSE, 9339742Smckusick TRUE, 9439742Smckusick TRUE, 9539742Smckusick TRUE, 9639742Smckusick TRUE, 9739742Smckusick FALSE, 9839742Smckusick TRUE, 9939742Smckusick FALSE, 10039742Smckusick FALSE, 10152196Smckusick FALSE, 10252196Smckusick FALSE, 10352196Smckusick FALSE, 10452196Smckusick FALSE, 105*56362Smckusick TRUE, 10639742Smckusick }; 10739742Smckusick 10839742Smckusick /* 10939742Smckusick * Initialize the server request cache list 11039742Smckusick */ 11139742Smckusick nfsrv_initcache() 11239742Smckusick { 11339742Smckusick 11455520Smckusick rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash); 11539742Smckusick } 11639742Smckusick 11739742Smckusick /* 11839742Smckusick * Look for the request in the cache 11939742Smckusick * If found then 12039742Smckusick * return action and optionally reply 12139742Smckusick * else 12239742Smckusick * insert it in the cache 12339742Smckusick * 12439742Smckusick * The rules are as follows: 12539742Smckusick * - if in progress, return DROP request 12639742Smckusick * - if completed within DELAY of the current time, return DROP it 12739742Smckusick * - if completed a longer time ago return REPLY if the reply was cached or 12839742Smckusick * return DOIT 12939742Smckusick * Update/add new request at end of lru list 13039742Smckusick */ 13152196Smckusick nfsrv_getcache(nam, nd, repp) 13239742Smckusick struct mbuf *nam; 13352196Smckusick register struct nfsd *nd; 13439742Smckusick struct mbuf **repp; 13539742Smckusick { 13655520Smckusick register struct nfsrvcache *rp, *rq, **rpp; 13739742Smckusick struct mbuf *mb; 13852196Smckusick struct sockaddr_in *saddr; 13939742Smckusick caddr_t bpos; 14039742Smckusick int ret; 14139742Smckusick 14252196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 14352196Smckusick return (RC_DOIT); 14455520Smckusick rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)]; 14539742Smckusick loop: 14655520Smckusick for (rp = *rpp; rp; rp = rp->rc_forw) { 14752196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 148*56362Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) { 14939742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 15039742Smckusick rp->rc_flag |= RC_WANTED; 15143352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 15239742Smckusick goto loop; 15339742Smckusick } 15439742Smckusick rp->rc_flag |= RC_LOCKED; 15555520Smckusick /* If not at end of LRU chain, move it there */ 15655520Smckusick if (rp->rc_next) { 15755520Smckusick /* remove from LRU chain */ 15855520Smckusick *rp->rc_prev = rp->rc_next; 15952196Smckusick rp->rc_next->rc_prev = rp->rc_prev; 16055520Smckusick /* and replace at end of it */ 16155520Smckusick rp->rc_next = NULL; 16255520Smckusick rp->rc_prev = nfsrvlrutail; 16355520Smckusick *nfsrvlrutail = rp; 16455520Smckusick nfsrvlrutail = &rp->rc_next; 16552196Smckusick } 16639742Smckusick if (rp->rc_state == RC_UNUSED) 16739742Smckusick panic("nfsrv cache"); 16839742Smckusick if (rp->rc_state == RC_INPROG || 16939742Smckusick (time.tv_sec - rp->rc_timestamp) < RC_DELAY) { 17039742Smckusick nfsstats.srvcache_inproghits++; 17139742Smckusick ret = RC_DROPIT; 17239742Smckusick } else if (rp->rc_flag & RC_REPSTATUS) { 17339742Smckusick nfsstats.srvcache_idemdonehits++; 17452196Smckusick nfs_rephead(0, nd, rp->rc_status, 17552196Smckusick 0, (u_quad_t *)0, repp, &mb, &bpos); 17639742Smckusick rp->rc_timestamp = time.tv_sec; 17739742Smckusick ret = RC_REPLY; 17839742Smckusick } else if (rp->rc_flag & RC_REPMBUF) { 17939742Smckusick nfsstats.srvcache_idemdonehits++; 18041901Smckusick *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 18139742Smckusick M_WAIT); 18239742Smckusick rp->rc_timestamp = time.tv_sec; 18339742Smckusick ret = RC_REPLY; 18439742Smckusick } else { 18539742Smckusick nfsstats.srvcache_nonidemdonehits++; 18639742Smckusick rp->rc_state = RC_INPROG; 18739742Smckusick ret = RC_DOIT; 18839742Smckusick } 18939742Smckusick rp->rc_flag &= ~RC_LOCKED; 19039742Smckusick if (rp->rc_flag & RC_WANTED) { 19139742Smckusick rp->rc_flag &= ~RC_WANTED; 19239742Smckusick wakeup((caddr_t)rp); 19339742Smckusick } 19439742Smckusick return (ret); 19539742Smckusick } 19639742Smckusick } 19739742Smckusick nfsstats.srvcache_misses++; 19855520Smckusick if (numnfsrvcache < desirednfsrvcache) { 19955520Smckusick rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp, 20055520Smckusick M_NFSD, M_WAITOK); 20155520Smckusick bzero((char *)rp, sizeof *rp); 20255520Smckusick numnfsrvcache++; 20355520Smckusick rp->rc_flag = RC_LOCKED; 20455520Smckusick } else { 20555520Smckusick rp = nfsrvlruhead; 20655520Smckusick while ((rp->rc_flag & RC_LOCKED) != 0) { 20755520Smckusick rp->rc_flag |= RC_WANTED; 20855520Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 20955520Smckusick rp = nfsrvlruhead; 21055520Smckusick } 21155520Smckusick rp->rc_flag |= RC_LOCKED; 21255520Smckusick /* remove from hash chain */ 21355520Smckusick if (rq = rp->rc_forw) 21455520Smckusick rq->rc_back = rp->rc_back; 21555520Smckusick *rp->rc_back = rq; 21655520Smckusick /* remove from LRU chain */ 21755520Smckusick *rp->rc_prev = rp->rc_next; 21852196Smckusick rp->rc_next->rc_prev = rp->rc_prev; 21955520Smckusick if (rp->rc_flag & RC_REPMBUF) 22055520Smckusick m_freem(rp->rc_reply); 22155520Smckusick if (rp->rc_flag & RC_NAM) 22255520Smckusick MFREE(rp->rc_nam, mb); 22355520Smckusick rp->rc_flag &= (RC_LOCKED | RC_WANTED); 22452196Smckusick } 22555520Smckusick /* place at end of LRU list */ 22655520Smckusick rp->rc_next = NULL; 22755520Smckusick rp->rc_prev = nfsrvlrutail; 22855520Smckusick *nfsrvlrutail = rp; 22955520Smckusick nfsrvlrutail = &rp->rc_next; 23039742Smckusick rp->rc_state = RC_INPROG; 23152196Smckusick rp->rc_xid = nd->nd_retxid; 23252196Smckusick saddr = mtod(nam, struct sockaddr_in *); 23352196Smckusick switch (saddr->sin_family) { 23452196Smckusick case AF_INET: 23552196Smckusick rp->rc_flag |= RC_INETADDR; 23652196Smckusick rp->rc_inetaddr = saddr->sin_addr.s_addr; 23752196Smckusick break; 23852196Smckusick case AF_ISO: 23952196Smckusick default: 24052196Smckusick rp->rc_flag |= RC_NAM; 24152196Smckusick rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT); 24252196Smckusick break; 24352196Smckusick }; 24452196Smckusick rp->rc_proc = nd->nd_procnum; 24555520Smckusick /* insert into hash chain */ 24655520Smckusick if (rq = *rpp) 24755520Smckusick rq->rc_back = &rp->rc_forw; 24856275Smckusick rp->rc_forw = rq; 24955520Smckusick rp->rc_back = rpp; 25055520Smckusick *rpp = rp; 25152196Smckusick rp->rc_flag &= ~RC_LOCKED; 25252196Smckusick if (rp->rc_flag & RC_WANTED) { 25352196Smckusick rp->rc_flag &= ~RC_WANTED; 25452196Smckusick wakeup((caddr_t)rp); 25552196Smckusick } 25639742Smckusick return (RC_DOIT); 25739742Smckusick } 25839742Smckusick 25939742Smckusick /* 26039742Smckusick * Update a request cache entry after the rpc has been done 26139742Smckusick */ 26252196Smckusick void 26352196Smckusick nfsrv_updatecache(nam, nd, repvalid, repmbuf) 26439742Smckusick struct mbuf *nam; 26552196Smckusick register struct nfsd *nd; 26640253Smckusick int repvalid; 26739742Smckusick struct mbuf *repmbuf; 26839742Smckusick { 26939742Smckusick register struct nfsrvcache *rp; 27039742Smckusick 27152196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 27252196Smckusick return; 27339742Smckusick loop: 27455520Smckusick for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) { 27552196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 276*56362Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) { 27739742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 27839742Smckusick rp->rc_flag |= RC_WANTED; 27943352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 28039742Smckusick goto loop; 28139742Smckusick } 28239742Smckusick rp->rc_flag |= RC_LOCKED; 28339742Smckusick rp->rc_state = RC_DONE; 28440253Smckusick /* 28540253Smckusick * If we have a valid reply update status and save 28641901Smckusick * the reply for non-idempotent rpc's. 28741901Smckusick * Otherwise invalidate entry by setting the timestamp 28841901Smckusick * to nil. 28940253Smckusick */ 29040253Smckusick if (repvalid) { 29140253Smckusick rp->rc_timestamp = time.tv_sec; 29252196Smckusick if (nonidempotent[nd->nd_procnum]) { 29352196Smckusick if (repliesstatus[nd->nd_procnum]) { 29452196Smckusick rp->rc_status = nd->nd_repstat; 29540253Smckusick rp->rc_flag |= RC_REPSTATUS; 29640253Smckusick } else { 29741901Smckusick rp->rc_reply = m_copym(repmbuf, 29840253Smckusick 0, M_COPYALL, M_WAIT); 29940253Smckusick rp->rc_flag |= RC_REPMBUF; 30040253Smckusick } 30139742Smckusick } 30240253Smckusick } else { 30340253Smckusick rp->rc_timestamp = 0; 30439742Smckusick } 30539742Smckusick rp->rc_flag &= ~RC_LOCKED; 30639742Smckusick if (rp->rc_flag & RC_WANTED) { 30739742Smckusick rp->rc_flag &= ~RC_WANTED; 30839742Smckusick wakeup((caddr_t)rp); 30939742Smckusick } 31039742Smckusick return; 31139742Smckusick } 31239742Smckusick } 31339742Smckusick } 31452196Smckusick 31552196Smckusick /* 31652196Smckusick * Clean out the cache. Called when the last nfsd terminates. 31752196Smckusick */ 31852196Smckusick void 31952196Smckusick nfsrv_cleancache() 32052196Smckusick { 32155520Smckusick register struct nfsrvcache *rp, *nextrp; 32252196Smckusick 32355520Smckusick for (rp = nfsrvlruhead; rp; rp = nextrp) { 32455520Smckusick nextrp = rp->rc_next; 32555520Smckusick free(rp, M_NFSD); 32652196Smckusick } 32755520Smckusick bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *)); 32855520Smckusick numnfsrvcache = 0; 32952196Smckusick } 330