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*60155Smckusick * @(#)nfs_srvcache.c 7.21 (Berkeley) 05/20/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 42*60155Smckusick #define NFSRCHASH(xid) (((xid) + ((xid) >> 24)) & 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"); 169*60155Smckusick if (rp->rc_state == RC_INPROG) { 17039742Smckusick nfsstats.srvcache_inproghits++; 17139742Smckusick ret = RC_DROPIT; 17239742Smckusick } else if (rp->rc_flag & RC_REPSTATUS) { 173*60155Smckusick nfsstats.srvcache_nonidemdonehits++; 17452196Smckusick nfs_rephead(0, nd, rp->rc_status, 17552196Smckusick 0, (u_quad_t *)0, repp, &mb, &bpos); 17639742Smckusick ret = RC_REPLY; 17739742Smckusick } else if (rp->rc_flag & RC_REPMBUF) { 178*60155Smckusick nfsstats.srvcache_nonidemdonehits++; 17941901Smckusick *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 18039742Smckusick M_WAIT); 18139742Smckusick ret = RC_REPLY; 18239742Smckusick } else { 183*60155Smckusick nfsstats.srvcache_idemdonehits++; 18439742Smckusick rp->rc_state = RC_INPROG; 18539742Smckusick ret = RC_DOIT; 18639742Smckusick } 18739742Smckusick rp->rc_flag &= ~RC_LOCKED; 18839742Smckusick if (rp->rc_flag & RC_WANTED) { 18939742Smckusick rp->rc_flag &= ~RC_WANTED; 19039742Smckusick wakeup((caddr_t)rp); 19139742Smckusick } 19239742Smckusick return (ret); 19339742Smckusick } 19439742Smckusick } 19539742Smckusick nfsstats.srvcache_misses++; 19655520Smckusick if (numnfsrvcache < desirednfsrvcache) { 19755520Smckusick rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp, 19855520Smckusick M_NFSD, M_WAITOK); 19955520Smckusick bzero((char *)rp, sizeof *rp); 20055520Smckusick numnfsrvcache++; 20155520Smckusick rp->rc_flag = RC_LOCKED; 20255520Smckusick } else { 20355520Smckusick rp = nfsrvlruhead; 20455520Smckusick while ((rp->rc_flag & RC_LOCKED) != 0) { 20555520Smckusick rp->rc_flag |= RC_WANTED; 20655520Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 20755520Smckusick rp = nfsrvlruhead; 20855520Smckusick } 20955520Smckusick rp->rc_flag |= RC_LOCKED; 21055520Smckusick /* remove from hash chain */ 21155520Smckusick if (rq = rp->rc_forw) 21255520Smckusick rq->rc_back = rp->rc_back; 21355520Smckusick *rp->rc_back = rq; 21455520Smckusick /* remove from LRU chain */ 21555520Smckusick *rp->rc_prev = rp->rc_next; 21652196Smckusick rp->rc_next->rc_prev = rp->rc_prev; 21755520Smckusick if (rp->rc_flag & RC_REPMBUF) 21855520Smckusick m_freem(rp->rc_reply); 21955520Smckusick if (rp->rc_flag & RC_NAM) 22055520Smckusick MFREE(rp->rc_nam, mb); 22155520Smckusick rp->rc_flag &= (RC_LOCKED | RC_WANTED); 22252196Smckusick } 22355520Smckusick /* place at end of LRU list */ 22455520Smckusick rp->rc_next = NULL; 22555520Smckusick rp->rc_prev = nfsrvlrutail; 22655520Smckusick *nfsrvlrutail = rp; 22755520Smckusick nfsrvlrutail = &rp->rc_next; 22839742Smckusick rp->rc_state = RC_INPROG; 22952196Smckusick rp->rc_xid = nd->nd_retxid; 23052196Smckusick saddr = mtod(nam, struct sockaddr_in *); 23152196Smckusick switch (saddr->sin_family) { 23252196Smckusick case AF_INET: 23352196Smckusick rp->rc_flag |= RC_INETADDR; 23452196Smckusick rp->rc_inetaddr = saddr->sin_addr.s_addr; 23552196Smckusick break; 23652196Smckusick case AF_ISO: 23752196Smckusick default: 23852196Smckusick rp->rc_flag |= RC_NAM; 23952196Smckusick rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT); 24052196Smckusick break; 24152196Smckusick }; 24252196Smckusick rp->rc_proc = nd->nd_procnum; 24355520Smckusick /* insert into hash chain */ 24455520Smckusick if (rq = *rpp) 24555520Smckusick rq->rc_back = &rp->rc_forw; 24656275Smckusick rp->rc_forw = rq; 24755520Smckusick rp->rc_back = rpp; 24855520Smckusick *rpp = rp; 24952196Smckusick rp->rc_flag &= ~RC_LOCKED; 25052196Smckusick if (rp->rc_flag & RC_WANTED) { 25152196Smckusick rp->rc_flag &= ~RC_WANTED; 25252196Smckusick wakeup((caddr_t)rp); 25352196Smckusick } 25439742Smckusick return (RC_DOIT); 25539742Smckusick } 25639742Smckusick 25739742Smckusick /* 25839742Smckusick * Update a request cache entry after the rpc has been done 25939742Smckusick */ 26052196Smckusick void 26152196Smckusick nfsrv_updatecache(nam, nd, repvalid, repmbuf) 26239742Smckusick struct mbuf *nam; 26352196Smckusick register struct nfsd *nd; 26440253Smckusick int repvalid; 26539742Smckusick struct mbuf *repmbuf; 26639742Smckusick { 26739742Smckusick register struct nfsrvcache *rp; 26839742Smckusick 26952196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 27052196Smckusick return; 27139742Smckusick loop: 27255520Smckusick for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) { 27352196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 27456362Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) { 27539742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 27639742Smckusick rp->rc_flag |= RC_WANTED; 27743352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 27839742Smckusick goto loop; 27939742Smckusick } 28039742Smckusick rp->rc_flag |= RC_LOCKED; 28139742Smckusick rp->rc_state = RC_DONE; 28240253Smckusick /* 28340253Smckusick * If we have a valid reply update status and save 28441901Smckusick * the reply for non-idempotent rpc's. 28540253Smckusick */ 286*60155Smckusick if (repvalid && nonidempotent[nd->nd_procnum]) { 287*60155Smckusick if (repliesstatus[nd->nd_procnum]) { 288*60155Smckusick rp->rc_status = nd->nd_repstat; 289*60155Smckusick rp->rc_flag |= RC_REPSTATUS; 290*60155Smckusick } else { 291*60155Smckusick rp->rc_reply = m_copym(repmbuf, 292*60155Smckusick 0, M_COPYALL, M_WAIT); 293*60155Smckusick rp->rc_flag |= RC_REPMBUF; 29439742Smckusick } 29539742Smckusick } 29639742Smckusick rp->rc_flag &= ~RC_LOCKED; 29739742Smckusick if (rp->rc_flag & RC_WANTED) { 29839742Smckusick rp->rc_flag &= ~RC_WANTED; 29939742Smckusick wakeup((caddr_t)rp); 30039742Smckusick } 30139742Smckusick return; 30239742Smckusick } 30339742Smckusick } 30439742Smckusick } 30552196Smckusick 30652196Smckusick /* 30752196Smckusick * Clean out the cache. Called when the last nfsd terminates. 30852196Smckusick */ 30952196Smckusick void 31052196Smckusick nfsrv_cleancache() 31152196Smckusick { 31255520Smckusick register struct nfsrvcache *rp, *nextrp; 31352196Smckusick 31455520Smckusick for (rp = nfsrvlruhead; rp; rp = nextrp) { 31555520Smckusick nextrp = rp->rc_next; 31655520Smckusick free(rp, M_NFSD); 31752196Smckusick } 31855520Smckusick bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *)); 31958365Smckusick nfsrvlruhead = NULL; 32058365Smckusick nfsrvlrutail = &nfsrvlruhead; 32155520Smckusick numnfsrvcache = 0; 32252196Smckusick } 323