139742Smckusick /* 263234Sbostic * Copyright (c) 1989, 1993 363234Sbostic * The Regents of the University of California. 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*67708Smckusick * @(#)nfs_srvcache.c 8.2 (Berkeley) 08/18/94 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*67708Smckusick #define NFSRCHASH(xid) \ 43*67708Smckusick (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash]) 44*67708Smckusick LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl; 45*67708Smckusick TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead; 46*67708Smckusick u_long nfsrvhash; 4739742Smckusick 4839742Smckusick #define TRUE 1 4939742Smckusick #define FALSE 0 5039742Smckusick 5152196Smckusick #define NETFAMILY(rp) \ 5252196Smckusick (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO) 5352196Smckusick 5439742Smckusick /* 5539742Smckusick * Static array that defines which nfs rpc's are nonidempotent 5639742Smckusick */ 5740118Smckusick int nonidempotent[NFS_NPROCS] = { 5839742Smckusick FALSE, 5939742Smckusick FALSE, 6039742Smckusick TRUE, 6139742Smckusick FALSE, 6239742Smckusick FALSE, 6339742Smckusick FALSE, 6439742Smckusick FALSE, 6539742Smckusick FALSE, 6639742Smckusick TRUE, 6739742Smckusick TRUE, 6839742Smckusick TRUE, 6939742Smckusick TRUE, 7039742Smckusick TRUE, 7139742Smckusick TRUE, 7239742Smckusick TRUE, 7339742Smckusick TRUE, 7439742Smckusick FALSE, 7539742Smckusick FALSE, 7652196Smckusick FALSE, 7752196Smckusick FALSE, 7852196Smckusick FALSE, 7952196Smckusick FALSE, 8056362Smckusick FALSE, 8139742Smckusick }; 8239742Smckusick 8339742Smckusick /* True iff the rpc reply is an nfs status ONLY! */ 8439742Smckusick static int repliesstatus[NFS_NPROCS] = { 8539742Smckusick FALSE, 8639742Smckusick FALSE, 8739742Smckusick FALSE, 8839742Smckusick FALSE, 8939742Smckusick FALSE, 9039742Smckusick FALSE, 9139742Smckusick FALSE, 9239742Smckusick FALSE, 9339742Smckusick FALSE, 9439742Smckusick FALSE, 9539742Smckusick TRUE, 9639742Smckusick TRUE, 9739742Smckusick TRUE, 9839742Smckusick TRUE, 9939742Smckusick FALSE, 10039742Smckusick TRUE, 10139742Smckusick FALSE, 10239742Smckusick FALSE, 10352196Smckusick FALSE, 10452196Smckusick FALSE, 10552196Smckusick FALSE, 10652196Smckusick FALSE, 10756362Smckusick TRUE, 10839742Smckusick }; 10939742Smckusick 11039742Smckusick /* 11139742Smckusick * Initialize the server request cache list 11239742Smckusick */ 11339742Smckusick nfsrv_initcache() 11439742Smckusick { 11539742Smckusick 116*67708Smckusick nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash); 117*67708Smckusick TAILQ_INIT(&nfsrvlruhead); 11839742Smckusick } 11939742Smckusick 12039742Smckusick /* 12139742Smckusick * Look for the request in the cache 12239742Smckusick * If found then 12339742Smckusick * return action and optionally reply 12439742Smckusick * else 12539742Smckusick * insert it in the cache 12639742Smckusick * 12739742Smckusick * The rules are as follows: 12839742Smckusick * - if in progress, return DROP request 12939742Smckusick * - if completed within DELAY of the current time, return DROP it 13039742Smckusick * - if completed a longer time ago return REPLY if the reply was cached or 13139742Smckusick * return DOIT 13239742Smckusick * Update/add new request at end of lru list 13339742Smckusick */ 13452196Smckusick nfsrv_getcache(nam, nd, repp) 13539742Smckusick struct mbuf *nam; 13652196Smckusick register struct nfsd *nd; 13739742Smckusick struct mbuf **repp; 13839742Smckusick { 139*67708Smckusick register struct nfsrvcache *rp; 14039742Smckusick struct mbuf *mb; 14152196Smckusick struct sockaddr_in *saddr; 14239742Smckusick caddr_t bpos; 14339742Smckusick int ret; 14439742Smckusick 14552196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 14652196Smckusick return (RC_DOIT); 14739742Smckusick loop: 148*67708Smckusick for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0; 149*67708Smckusick rp = rp->rc_hash.le_next) { 15052196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 15156362Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) { 15239742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 15339742Smckusick rp->rc_flag |= RC_WANTED; 15443352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 15539742Smckusick goto loop; 15639742Smckusick } 15739742Smckusick rp->rc_flag |= RC_LOCKED; 15855520Smckusick /* If not at end of LRU chain, move it there */ 159*67708Smckusick if (rp->rc_lru.tqe_next) { 160*67708Smckusick TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 161*67708Smckusick TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 16252196Smckusick } 16339742Smckusick if (rp->rc_state == RC_UNUSED) 16439742Smckusick panic("nfsrv cache"); 16560155Smckusick if (rp->rc_state == RC_INPROG) { 16639742Smckusick nfsstats.srvcache_inproghits++; 16739742Smckusick ret = RC_DROPIT; 16839742Smckusick } else if (rp->rc_flag & RC_REPSTATUS) { 16960155Smckusick nfsstats.srvcache_nonidemdonehits++; 17052196Smckusick nfs_rephead(0, nd, rp->rc_status, 17152196Smckusick 0, (u_quad_t *)0, repp, &mb, &bpos); 17239742Smckusick ret = RC_REPLY; 17339742Smckusick } else if (rp->rc_flag & RC_REPMBUF) { 17460155Smckusick nfsstats.srvcache_nonidemdonehits++; 17541901Smckusick *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 17639742Smckusick M_WAIT); 17739742Smckusick ret = RC_REPLY; 17839742Smckusick } else { 17960155Smckusick nfsstats.srvcache_idemdonehits++; 18039742Smckusick rp->rc_state = RC_INPROG; 18139742Smckusick ret = RC_DOIT; 18239742Smckusick } 18339742Smckusick rp->rc_flag &= ~RC_LOCKED; 18439742Smckusick if (rp->rc_flag & RC_WANTED) { 18539742Smckusick rp->rc_flag &= ~RC_WANTED; 18639742Smckusick wakeup((caddr_t)rp); 18739742Smckusick } 18839742Smckusick return (ret); 18939742Smckusick } 19039742Smckusick } 19139742Smckusick nfsstats.srvcache_misses++; 19255520Smckusick if (numnfsrvcache < desirednfsrvcache) { 19355520Smckusick rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp, 19455520Smckusick M_NFSD, M_WAITOK); 19555520Smckusick bzero((char *)rp, sizeof *rp); 19655520Smckusick numnfsrvcache++; 19755520Smckusick rp->rc_flag = RC_LOCKED; 19855520Smckusick } else { 199*67708Smckusick rp = nfsrvlruhead.tqh_first; 20055520Smckusick while ((rp->rc_flag & RC_LOCKED) != 0) { 20155520Smckusick rp->rc_flag |= RC_WANTED; 20255520Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 203*67708Smckusick rp = nfsrvlruhead.tqh_first; 20455520Smckusick } 20555520Smckusick rp->rc_flag |= RC_LOCKED; 206*67708Smckusick LIST_REMOVE(rp, rc_hash); 207*67708Smckusick TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 20855520Smckusick if (rp->rc_flag & RC_REPMBUF) 20955520Smckusick m_freem(rp->rc_reply); 21055520Smckusick if (rp->rc_flag & RC_NAM) 21155520Smckusick MFREE(rp->rc_nam, mb); 21255520Smckusick rp->rc_flag &= (RC_LOCKED | RC_WANTED); 21352196Smckusick } 214*67708Smckusick TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 21539742Smckusick rp->rc_state = RC_INPROG; 21652196Smckusick rp->rc_xid = nd->nd_retxid; 21752196Smckusick saddr = mtod(nam, struct sockaddr_in *); 21852196Smckusick switch (saddr->sin_family) { 21952196Smckusick case AF_INET: 22052196Smckusick rp->rc_flag |= RC_INETADDR; 22152196Smckusick rp->rc_inetaddr = saddr->sin_addr.s_addr; 22252196Smckusick break; 22352196Smckusick case AF_ISO: 22452196Smckusick default: 22552196Smckusick rp->rc_flag |= RC_NAM; 22652196Smckusick rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT); 22752196Smckusick break; 22852196Smckusick }; 22952196Smckusick rp->rc_proc = nd->nd_procnum; 230*67708Smckusick LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash); 23152196Smckusick rp->rc_flag &= ~RC_LOCKED; 23252196Smckusick if (rp->rc_flag & RC_WANTED) { 23352196Smckusick rp->rc_flag &= ~RC_WANTED; 23452196Smckusick wakeup((caddr_t)rp); 23552196Smckusick } 23639742Smckusick return (RC_DOIT); 23739742Smckusick } 23839742Smckusick 23939742Smckusick /* 24039742Smckusick * Update a request cache entry after the rpc has been done 24139742Smckusick */ 24252196Smckusick void 24352196Smckusick nfsrv_updatecache(nam, nd, repvalid, repmbuf) 24439742Smckusick struct mbuf *nam; 24552196Smckusick register struct nfsd *nd; 24640253Smckusick int repvalid; 24739742Smckusick struct mbuf *repmbuf; 24839742Smckusick { 24939742Smckusick register struct nfsrvcache *rp; 25039742Smckusick 25152196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 25252196Smckusick return; 25339742Smckusick loop: 254*67708Smckusick for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0; 255*67708Smckusick rp = rp->rc_hash.le_next) { 25652196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 25756362Smckusick netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) { 25839742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 25939742Smckusick rp->rc_flag |= RC_WANTED; 26043352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 26139742Smckusick goto loop; 26239742Smckusick } 26339742Smckusick rp->rc_flag |= RC_LOCKED; 26439742Smckusick rp->rc_state = RC_DONE; 26540253Smckusick /* 26640253Smckusick * If we have a valid reply update status and save 26741901Smckusick * the reply for non-idempotent rpc's. 26840253Smckusick */ 26960155Smckusick if (repvalid && nonidempotent[nd->nd_procnum]) { 27060155Smckusick if (repliesstatus[nd->nd_procnum]) { 27160155Smckusick rp->rc_status = nd->nd_repstat; 27260155Smckusick rp->rc_flag |= RC_REPSTATUS; 27360155Smckusick } else { 27460155Smckusick rp->rc_reply = m_copym(repmbuf, 27560155Smckusick 0, M_COPYALL, M_WAIT); 27660155Smckusick rp->rc_flag |= RC_REPMBUF; 27739742Smckusick } 27839742Smckusick } 27939742Smckusick rp->rc_flag &= ~RC_LOCKED; 28039742Smckusick if (rp->rc_flag & RC_WANTED) { 28139742Smckusick rp->rc_flag &= ~RC_WANTED; 28239742Smckusick wakeup((caddr_t)rp); 28339742Smckusick } 28439742Smckusick return; 28539742Smckusick } 28639742Smckusick } 28739742Smckusick } 28852196Smckusick 28952196Smckusick /* 29052196Smckusick * Clean out the cache. Called when the last nfsd terminates. 29152196Smckusick */ 29252196Smckusick void 29352196Smckusick nfsrv_cleancache() 29452196Smckusick { 29555520Smckusick register struct nfsrvcache *rp, *nextrp; 29652196Smckusick 297*67708Smckusick for (rp = nfsrvlruhead.tqh_first; rp != 0; rp = nextrp) { 298*67708Smckusick nextrp = rp->rc_lru.tqe_next; 299*67708Smckusick LIST_REMOVE(rp, rc_hash); 300*67708Smckusick TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 30155520Smckusick free(rp, M_NFSD); 30252196Smckusick } 30355520Smckusick numnfsrvcache = 0; 30452196Smckusick } 305