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*55520Smckusick * @(#)nfs_srvcache.c 7.16 (Berkeley) 07/22/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> 25*55520Smckusick #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 39*55520Smckusick long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ; 4039742Smckusick 41*55520Smckusick #define NFSRCHASH(xid) (((xid) + ((xid) >> 16)) & rheadhash) 42*55520Smckusick static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead; 43*55520Smckusick static struct nfsrvcache **rheadhtbl; 44*55520Smckusick 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, 7839742Smckusick }; 7939742Smckusick 8039742Smckusick /* True iff the rpc reply is an nfs status ONLY! */ 8139742Smckusick static int repliesstatus[NFS_NPROCS] = { 8239742Smckusick FALSE, 8339742Smckusick FALSE, 8439742Smckusick FALSE, 8539742Smckusick FALSE, 8639742Smckusick FALSE, 8739742Smckusick FALSE, 8839742Smckusick FALSE, 8939742Smckusick FALSE, 9039742Smckusick FALSE, 9139742Smckusick FALSE, 9239742Smckusick TRUE, 9339742Smckusick TRUE, 9439742Smckusick TRUE, 9539742Smckusick TRUE, 9639742Smckusick FALSE, 9739742Smckusick TRUE, 9839742Smckusick FALSE, 9939742Smckusick FALSE, 10052196Smckusick FALSE, 10152196Smckusick FALSE, 10252196Smckusick FALSE, 10352196Smckusick FALSE, 10439742Smckusick }; 10539742Smckusick 10639742Smckusick /* 10739742Smckusick * Initialize the server request cache list 10839742Smckusick */ 10939742Smckusick nfsrv_initcache() 11039742Smckusick { 11139742Smckusick 112*55520Smckusick rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash); 11339742Smckusick } 11439742Smckusick 11539742Smckusick /* 11639742Smckusick * Look for the request in the cache 11739742Smckusick * If found then 11839742Smckusick * return action and optionally reply 11939742Smckusick * else 12039742Smckusick * insert it in the cache 12139742Smckusick * 12239742Smckusick * The rules are as follows: 12339742Smckusick * - if in progress, return DROP request 12439742Smckusick * - if completed within DELAY of the current time, return DROP it 12539742Smckusick * - if completed a longer time ago return REPLY if the reply was cached or 12639742Smckusick * return DOIT 12739742Smckusick * Update/add new request at end of lru list 12839742Smckusick */ 12952196Smckusick nfsrv_getcache(nam, nd, repp) 13039742Smckusick struct mbuf *nam; 13152196Smckusick register struct nfsd *nd; 13239742Smckusick struct mbuf **repp; 13339742Smckusick { 134*55520Smckusick register struct nfsrvcache *rp, *rq, **rpp; 13539742Smckusick struct mbuf *mb; 13652196Smckusick struct sockaddr_in *saddr; 13739742Smckusick caddr_t bpos; 13839742Smckusick int ret; 13939742Smckusick 14052196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 14152196Smckusick return (RC_DOIT); 142*55520Smckusick rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)]; 14339742Smckusick loop: 144*55520Smckusick for (rp = *rpp; rp; rp = rp->rc_forw) { 14552196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 14654613Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) { 14739742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 14839742Smckusick rp->rc_flag |= RC_WANTED; 14943352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 15039742Smckusick goto loop; 15139742Smckusick } 15239742Smckusick rp->rc_flag |= RC_LOCKED; 153*55520Smckusick /* If not at end of LRU chain, move it there */ 154*55520Smckusick if (rp->rc_next) { 155*55520Smckusick /* remove from LRU chain */ 156*55520Smckusick *rp->rc_prev = rp->rc_next; 15752196Smckusick rp->rc_next->rc_prev = rp->rc_prev; 158*55520Smckusick /* and replace at end of it */ 159*55520Smckusick rp->rc_next = NULL; 160*55520Smckusick rp->rc_prev = nfsrvlrutail; 161*55520Smckusick *nfsrvlrutail = rp; 162*55520Smckusick nfsrvlrutail = &rp->rc_next; 16352196Smckusick } 16439742Smckusick if (rp->rc_state == RC_UNUSED) 16539742Smckusick panic("nfsrv cache"); 16639742Smckusick if (rp->rc_state == RC_INPROG || 16739742Smckusick (time.tv_sec - rp->rc_timestamp) < RC_DELAY) { 16839742Smckusick nfsstats.srvcache_inproghits++; 16939742Smckusick ret = RC_DROPIT; 17039742Smckusick } else if (rp->rc_flag & RC_REPSTATUS) { 17139742Smckusick nfsstats.srvcache_idemdonehits++; 17252196Smckusick nfs_rephead(0, nd, rp->rc_status, 17352196Smckusick 0, (u_quad_t *)0, repp, &mb, &bpos); 17439742Smckusick rp->rc_timestamp = time.tv_sec; 17539742Smckusick ret = RC_REPLY; 17639742Smckusick } else if (rp->rc_flag & RC_REPMBUF) { 17739742Smckusick nfsstats.srvcache_idemdonehits++; 17841901Smckusick *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 17939742Smckusick M_WAIT); 18039742Smckusick rp->rc_timestamp = time.tv_sec; 18139742Smckusick ret = RC_REPLY; 18239742Smckusick } else { 18339742Smckusick nfsstats.srvcache_nonidemdonehits++; 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++; 196*55520Smckusick if (numnfsrvcache < desirednfsrvcache) { 197*55520Smckusick rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp, 198*55520Smckusick M_NFSD, M_WAITOK); 199*55520Smckusick bzero((char *)rp, sizeof *rp); 200*55520Smckusick numnfsrvcache++; 201*55520Smckusick rp->rc_flag = RC_LOCKED; 202*55520Smckusick } else { 203*55520Smckusick rp = nfsrvlruhead; 204*55520Smckusick while ((rp->rc_flag & RC_LOCKED) != 0) { 205*55520Smckusick rp->rc_flag |= RC_WANTED; 206*55520Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 207*55520Smckusick rp = nfsrvlruhead; 208*55520Smckusick } 209*55520Smckusick rp->rc_flag |= RC_LOCKED; 210*55520Smckusick /* remove from hash chain */ 211*55520Smckusick if (rq = rp->rc_forw) 212*55520Smckusick rq->rc_back = rp->rc_back; 213*55520Smckusick *rp->rc_back = rq; 214*55520Smckusick /* remove from LRU chain */ 215*55520Smckusick *rp->rc_prev = rp->rc_next; 21652196Smckusick rp->rc_next->rc_prev = rp->rc_prev; 217*55520Smckusick if (rp->rc_flag & RC_REPMBUF) 218*55520Smckusick m_freem(rp->rc_reply); 219*55520Smckusick if (rp->rc_flag & RC_NAM) 220*55520Smckusick MFREE(rp->rc_nam, mb); 221*55520Smckusick rp->rc_flag &= (RC_LOCKED | RC_WANTED); 22252196Smckusick } 223*55520Smckusick /* place at end of LRU list */ 224*55520Smckusick rp->rc_next = NULL; 225*55520Smckusick rp->rc_prev = nfsrvlrutail; 226*55520Smckusick *nfsrvlrutail = rp; 227*55520Smckusick 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; 243*55520Smckusick /* insert into hash chain */ 244*55520Smckusick if (rq = *rpp) 245*55520Smckusick rq->rc_back = &rp->rc_forw; 246*55520Smckusick rp->rc_next = rq; 247*55520Smckusick rp->rc_back = rpp; 248*55520Smckusick *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: 272*55520Smckusick 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 && 27454613Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, 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. 28541901Smckusick * Otherwise invalidate entry by setting the timestamp 28641901Smckusick * to nil. 28740253Smckusick */ 28840253Smckusick if (repvalid) { 28940253Smckusick rp->rc_timestamp = time.tv_sec; 29052196Smckusick if (nonidempotent[nd->nd_procnum]) { 29152196Smckusick if (repliesstatus[nd->nd_procnum]) { 29252196Smckusick rp->rc_status = nd->nd_repstat; 29340253Smckusick rp->rc_flag |= RC_REPSTATUS; 29440253Smckusick } else { 29541901Smckusick rp->rc_reply = m_copym(repmbuf, 29640253Smckusick 0, M_COPYALL, M_WAIT); 29740253Smckusick rp->rc_flag |= RC_REPMBUF; 29840253Smckusick } 29939742Smckusick } 30040253Smckusick } else { 30140253Smckusick rp->rc_timestamp = 0; 30239742Smckusick } 30339742Smckusick rp->rc_flag &= ~RC_LOCKED; 30439742Smckusick if (rp->rc_flag & RC_WANTED) { 30539742Smckusick rp->rc_flag &= ~RC_WANTED; 30639742Smckusick wakeup((caddr_t)rp); 30739742Smckusick } 30839742Smckusick return; 30939742Smckusick } 31039742Smckusick } 31139742Smckusick } 31252196Smckusick 31352196Smckusick /* 31452196Smckusick * Clean out the cache. Called when the last nfsd terminates. 31552196Smckusick */ 31652196Smckusick void 31752196Smckusick nfsrv_cleancache() 31852196Smckusick { 319*55520Smckusick register struct nfsrvcache *rp, *nextrp; 32052196Smckusick 321*55520Smckusick for (rp = nfsrvlruhead; rp; rp = nextrp) { 322*55520Smckusick nextrp = rp->rc_next; 323*55520Smckusick free(rp, M_NFSD); 32452196Smckusick } 325*55520Smckusick bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *)); 326*55520Smckusick numnfsrvcache = 0; 32752196Smckusick } 328