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*52196Smckusick * @(#)nfs_srvcache.c 7.12 (Berkeley) 01/14/92 1139742Smckusick */ 1239742Smckusick 1343431Smckusick /* 1443431Smckusick * Reference: Chet Juszczak, "Improving the Performance and Correctness 15*52196Smckusick * of an NFS Server", in Proc. Winter 1989 USENIX Conference, 16*52196Smckusick * pages 53-63. San Diego, February 1989. 1743431Smckusick */ 1839742Smckusick #include "param.h" 1939742Smckusick #include "vnode.h" 2039742Smckusick #include "mount.h" 2139742Smckusick #include "kernel.h" 2239759Smckusick #include "systm.h" 2339742Smckusick #include "mbuf.h" 2439742Smckusick #include "socket.h" 2539742Smckusick #include "socketvar.h" 26*52196Smckusick #include "netinet/in.h" 27*52196Smckusick #ifdef ISO 28*52196Smckusick #include "netiso/iso.h" 29*52196Smckusick #endif 3039742Smckusick #include "nfsm_subs.h" 31*52196Smckusick #include "rpcv2.h" 3239742Smckusick #include "nfsv2.h" 3339742Smckusick #include "nfsrvcache.h" 3439742Smckusick #include "nfs.h" 35*52196Smckusick #include "nqnfs.h" 3639742Smckusick 3739742Smckusick #if ((NFSRCHSZ&(NFSRCHSZ-1)) == 0) 3839742Smckusick #define NFSRCHASH(xid) (((xid)+((xid)>>16))&(NFSRCHSZ-1)) 3939742Smckusick #else 4039742Smckusick #define NFSRCHASH(xid) (((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ) 4139742Smckusick #endif 4239742Smckusick 4339742Smckusick union rhead { 4439742Smckusick union rhead *rh_head[2]; 4539742Smckusick struct nfsrvcache *rh_chain[2]; 4639742Smckusick } rhead[NFSRCHSZ]; 4739742Smckusick 4839742Smckusick static struct nfsrvcache nfsrvcachehead; 4939742Smckusick static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ]; 5039742Smckusick 5139742Smckusick #define TRUE 1 5239742Smckusick #define FALSE 0 5339742Smckusick 54*52196Smckusick #define NETFAMILY(rp) \ 55*52196Smckusick (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO) 56*52196Smckusick 5739742Smckusick /* 5839742Smckusick * Static array that defines which nfs rpc's are nonidempotent 5939742Smckusick */ 6040118Smckusick int nonidempotent[NFS_NPROCS] = { 6139742Smckusick FALSE, 6239742Smckusick FALSE, 6339742Smckusick TRUE, 6439742Smckusick FALSE, 6539742Smckusick FALSE, 6639742Smckusick FALSE, 6739742Smckusick FALSE, 6839742Smckusick FALSE, 6939742Smckusick TRUE, 7039742Smckusick TRUE, 7139742Smckusick TRUE, 7239742Smckusick TRUE, 7339742Smckusick TRUE, 7439742Smckusick TRUE, 7539742Smckusick TRUE, 7639742Smckusick TRUE, 7739742Smckusick FALSE, 7839742Smckusick FALSE, 79*52196Smckusick FALSE, 80*52196Smckusick FALSE, 81*52196Smckusick FALSE, 82*52196Smckusick FALSE, 8339742Smckusick }; 8439742Smckusick 8539742Smckusick /* True iff the rpc reply is an nfs status ONLY! */ 8639742Smckusick static int repliesstatus[NFS_NPROCS] = { 8739742Smckusick FALSE, 8839742Smckusick FALSE, 8939742Smckusick FALSE, 9039742Smckusick FALSE, 9139742Smckusick FALSE, 9239742Smckusick FALSE, 9339742Smckusick FALSE, 9439742Smckusick FALSE, 9539742Smckusick FALSE, 9639742Smckusick FALSE, 9739742Smckusick TRUE, 9839742Smckusick TRUE, 9939742Smckusick TRUE, 10039742Smckusick TRUE, 10139742Smckusick FALSE, 10239742Smckusick TRUE, 10339742Smckusick FALSE, 10439742Smckusick FALSE, 105*52196Smckusick FALSE, 106*52196Smckusick FALSE, 107*52196Smckusick FALSE, 108*52196Smckusick FALSE, 10939742Smckusick }; 11039742Smckusick 11139742Smckusick /* 11239742Smckusick * Initialize the server request cache list 11339742Smckusick */ 11439742Smckusick nfsrv_initcache() 11539742Smckusick { 11639742Smckusick register int i; 11739742Smckusick register struct nfsrvcache *rp = nfsrvcache; 11839742Smckusick register struct nfsrvcache *hp = &nfsrvcachehead; 11939742Smckusick register union rhead *rh = rhead; 12039742Smckusick 12139742Smckusick for (i = NFSRCHSZ; --i >= 0; rh++) { 12239742Smckusick rh->rh_head[0] = rh; 12339742Smckusick rh->rh_head[1] = rh; 12439742Smckusick } 12539742Smckusick hp->rc_next = hp->rc_prev = hp; 12639742Smckusick for (i = NFSRVCACHESIZ; i-- > 0; ) { 12739742Smckusick rp->rc_state = RC_UNUSED; 12839742Smckusick rp->rc_flag = 0; 12939742Smckusick rp->rc_forw = rp; 13039742Smckusick rp->rc_back = rp; 13139742Smckusick rp->rc_next = hp->rc_next; 13239742Smckusick hp->rc_next->rc_prev = rp; 13339742Smckusick rp->rc_prev = hp; 13439742Smckusick hp->rc_next = rp; 13539742Smckusick rp++; 13639742Smckusick } 13739742Smckusick } 13839742Smckusick 13939742Smckusick /* 14039742Smckusick * Look for the request in the cache 14139742Smckusick * If found then 14239742Smckusick * return action and optionally reply 14339742Smckusick * else 14439742Smckusick * insert it in the cache 14539742Smckusick * 14639742Smckusick * The rules are as follows: 14739742Smckusick * - if in progress, return DROP request 14839742Smckusick * - if completed within DELAY of the current time, return DROP it 14939742Smckusick * - if completed a longer time ago return REPLY if the reply was cached or 15039742Smckusick * return DOIT 15139742Smckusick * Update/add new request at end of lru list 15239742Smckusick */ 153*52196Smckusick nfsrv_getcache(nam, nd, repp) 15439742Smckusick struct mbuf *nam; 155*52196Smckusick register struct nfsd *nd; 15639742Smckusick struct mbuf **repp; 15739742Smckusick { 15839742Smckusick register struct nfsrvcache *rp; 15939742Smckusick register union rhead *rh; 16039742Smckusick struct mbuf *mb; 161*52196Smckusick struct sockaddr_in *saddr; 16239742Smckusick caddr_t bpos; 16339742Smckusick int ret; 16439742Smckusick 165*52196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 166*52196Smckusick return (RC_DOIT); 167*52196Smckusick rh = &rhead[NFSRCHASH(nd->nd_retxid)]; 16839742Smckusick loop: 16939742Smckusick for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 170*52196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 171*52196Smckusick nfs_netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) { 17239742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 17339742Smckusick rp->rc_flag |= RC_WANTED; 17443352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 17539742Smckusick goto loop; 17639742Smckusick } 17739742Smckusick rp->rc_flag |= RC_LOCKED; 178*52196Smckusick if (rp->rc_prev != &nfsrvcachehead) { 179*52196Smckusick rp->rc_next->rc_prev = rp->rc_prev; 180*52196Smckusick rp->rc_prev->rc_next = rp->rc_next; 181*52196Smckusick rp->rc_next = nfsrvcachehead.rc_next; 182*52196Smckusick nfsrvcachehead.rc_next = rp; 183*52196Smckusick rp->rc_prev = &nfsrvcachehead; 184*52196Smckusick rp->rc_next->rc_prev = rp; 185*52196Smckusick } 18639742Smckusick if (rp->rc_state == RC_UNUSED) 18739742Smckusick panic("nfsrv cache"); 18839742Smckusick if (rp->rc_state == RC_INPROG || 18939742Smckusick (time.tv_sec - rp->rc_timestamp) < RC_DELAY) { 19039742Smckusick nfsstats.srvcache_inproghits++; 19139742Smckusick ret = RC_DROPIT; 19239742Smckusick } else if (rp->rc_flag & RC_REPSTATUS) { 19339742Smckusick nfsstats.srvcache_idemdonehits++; 194*52196Smckusick nfs_rephead(0, nd, rp->rc_status, 195*52196Smckusick 0, (u_quad_t *)0, repp, &mb, &bpos); 19639742Smckusick rp->rc_timestamp = time.tv_sec; 19739742Smckusick ret = RC_REPLY; 19839742Smckusick } else if (rp->rc_flag & RC_REPMBUF) { 19939742Smckusick nfsstats.srvcache_idemdonehits++; 20041901Smckusick *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 20139742Smckusick M_WAIT); 20239742Smckusick rp->rc_timestamp = time.tv_sec; 20339742Smckusick ret = RC_REPLY; 20439742Smckusick } else { 20539742Smckusick nfsstats.srvcache_nonidemdonehits++; 20639742Smckusick rp->rc_state = RC_INPROG; 20739742Smckusick ret = RC_DOIT; 20839742Smckusick } 20939742Smckusick rp->rc_flag &= ~RC_LOCKED; 21039742Smckusick if (rp->rc_flag & RC_WANTED) { 21139742Smckusick rp->rc_flag &= ~RC_WANTED; 21239742Smckusick wakeup((caddr_t)rp); 21339742Smckusick } 21439742Smckusick return (ret); 21539742Smckusick } 21639742Smckusick } 21739742Smckusick nfsstats.srvcache_misses++; 21839742Smckusick rp = nfsrvcachehead.rc_prev; 21939742Smckusick while ((rp->rc_flag & RC_LOCKED) != 0) { 22039742Smckusick rp->rc_flag |= RC_WANTED; 22143352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 222*52196Smckusick rp = nfsrvcachehead.rc_prev; 22339742Smckusick } 224*52196Smckusick rp->rc_flag |= RC_LOCKED; 22539742Smckusick remque(rp); 226*52196Smckusick if (rp->rc_prev != &nfsrvcachehead) { 227*52196Smckusick rp->rc_next->rc_prev = rp->rc_prev; 228*52196Smckusick rp->rc_prev->rc_next = rp->rc_next; 229*52196Smckusick rp->rc_next = nfsrvcachehead.rc_next; 230*52196Smckusick nfsrvcachehead.rc_next = rp; 231*52196Smckusick rp->rc_prev = &nfsrvcachehead; 232*52196Smckusick rp->rc_next->rc_prev = rp; 233*52196Smckusick } 23439742Smckusick if (rp->rc_flag & RC_REPMBUF) 235*52196Smckusick m_freem(rp->rc_reply); 236*52196Smckusick if (rp->rc_flag & RC_NAM) 237*52196Smckusick MFREE(rp->rc_nam, mb); 238*52196Smckusick rp->rc_flag &= (RC_LOCKED | RC_WANTED); 23939742Smckusick rp->rc_state = RC_INPROG; 240*52196Smckusick rp->rc_xid = nd->nd_retxid; 241*52196Smckusick saddr = mtod(nam, struct sockaddr_in *); 242*52196Smckusick switch (saddr->sin_family) { 243*52196Smckusick case AF_INET: 244*52196Smckusick rp->rc_flag |= RC_INETADDR; 245*52196Smckusick rp->rc_inetaddr = saddr->sin_addr.s_addr; 246*52196Smckusick break; 247*52196Smckusick case AF_ISO: 248*52196Smckusick default: 249*52196Smckusick rp->rc_flag |= RC_NAM; 250*52196Smckusick rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT); 251*52196Smckusick break; 252*52196Smckusick }; 253*52196Smckusick rp->rc_proc = nd->nd_procnum; 25439742Smckusick insque(rp, rh); 255*52196Smckusick rp->rc_flag &= ~RC_LOCKED; 256*52196Smckusick if (rp->rc_flag & RC_WANTED) { 257*52196Smckusick rp->rc_flag &= ~RC_WANTED; 258*52196Smckusick wakeup((caddr_t)rp); 259*52196Smckusick } 26039742Smckusick return (RC_DOIT); 26139742Smckusick } 26239742Smckusick 26339742Smckusick /* 26439742Smckusick * Update a request cache entry after the rpc has been done 26539742Smckusick */ 266*52196Smckusick void 267*52196Smckusick nfsrv_updatecache(nam, nd, repvalid, repmbuf) 26839742Smckusick struct mbuf *nam; 269*52196Smckusick register struct nfsd *nd; 27040253Smckusick int repvalid; 27139742Smckusick struct mbuf *repmbuf; 27239742Smckusick { 27339742Smckusick register struct nfsrvcache *rp; 27439742Smckusick register union rhead *rh; 27539742Smckusick 276*52196Smckusick if (nd->nd_nqlflag != NQL_NOVAL) 277*52196Smckusick return; 278*52196Smckusick rh = &rhead[NFSRCHASH(nd->nd_retxid)]; 27939742Smckusick loop: 28039742Smckusick for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 281*52196Smckusick if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 282*52196Smckusick nfs_netaddr_match(NETFAMILY(rp), &rp->rc_haddr, (union nethostaddr *)0, nam)) { 28339742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 28439742Smckusick rp->rc_flag |= RC_WANTED; 28543352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 28639742Smckusick goto loop; 28739742Smckusick } 28839742Smckusick rp->rc_flag |= RC_LOCKED; 28939742Smckusick rp->rc_state = RC_DONE; 29040253Smckusick /* 29140253Smckusick * If we have a valid reply update status and save 29241901Smckusick * the reply for non-idempotent rpc's. 29341901Smckusick * Otherwise invalidate entry by setting the timestamp 29441901Smckusick * to nil. 29540253Smckusick */ 29640253Smckusick if (repvalid) { 29740253Smckusick rp->rc_timestamp = time.tv_sec; 298*52196Smckusick if (nonidempotent[nd->nd_procnum]) { 299*52196Smckusick if (repliesstatus[nd->nd_procnum]) { 300*52196Smckusick rp->rc_status = nd->nd_repstat; 30140253Smckusick rp->rc_flag |= RC_REPSTATUS; 30240253Smckusick } else { 30341901Smckusick rp->rc_reply = m_copym(repmbuf, 30440253Smckusick 0, M_COPYALL, M_WAIT); 30540253Smckusick rp->rc_flag |= RC_REPMBUF; 30640253Smckusick } 30739742Smckusick } 30840253Smckusick } else { 30940253Smckusick rp->rc_timestamp = 0; 31039742Smckusick } 31139742Smckusick rp->rc_flag &= ~RC_LOCKED; 31239742Smckusick if (rp->rc_flag & RC_WANTED) { 31339742Smckusick rp->rc_flag &= ~RC_WANTED; 31439742Smckusick wakeup((caddr_t)rp); 31539742Smckusick } 31639742Smckusick return; 31739742Smckusick } 31839742Smckusick } 31939742Smckusick } 320*52196Smckusick 321*52196Smckusick /* 322*52196Smckusick * Clean out the cache. Called when the last nfsd terminates. 323*52196Smckusick */ 324*52196Smckusick void 325*52196Smckusick nfsrv_cleancache() 326*52196Smckusick { 327*52196Smckusick register int i; 328*52196Smckusick register struct nfsrvcache *rp = nfsrvcache; 329*52196Smckusick register struct nfsrvcache *hp = &nfsrvcachehead; 330*52196Smckusick register union rhead *rh = rhead; 331*52196Smckusick 332*52196Smckusick for (i = NFSRCHSZ; --i >= 0; rh++) { 333*52196Smckusick rh->rh_head[0] = rh; 334*52196Smckusick rh->rh_head[1] = rh; 335*52196Smckusick } 336*52196Smckusick hp->rc_next = hp->rc_prev = hp; 337*52196Smckusick for (i = NFSRVCACHESIZ; i-- > 0; ) { 338*52196Smckusick if (rp->rc_flag & RC_REPMBUF) 339*52196Smckusick m_freem(rp->rc_reply); 340*52196Smckusick if (rp->rc_flag & RC_NAM) 341*52196Smckusick m_freem(rp->rc_nam); 342*52196Smckusick rp->rc_state = RC_UNUSED; 343*52196Smckusick rp->rc_flag = 0; 344*52196Smckusick rp->rc_forw = rp; 345*52196Smckusick rp->rc_back = rp; 346*52196Smckusick rp->rc_next = hp->rc_next; 347*52196Smckusick hp->rc_next->rc_prev = rp; 348*52196Smckusick rp->rc_prev = hp; 349*52196Smckusick hp->rc_next = rp; 350*52196Smckusick rp++; 351*52196Smckusick } 352*52196Smckusick } 353