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 * 839742Smckusick * Redistribution and use in source and binary forms are permitted 939742Smckusick * provided that the above copyright notice and this paragraph are 1039742Smckusick * duplicated in all such forms and that any documentation, 1139742Smckusick * advertising materials, and other materials related to such 1239742Smckusick * distribution and use acknowledge that the software was developed 1339742Smckusick * by the University of California, Berkeley. The name of the 1439742Smckusick * University may not be used to endorse or promote products derived 1539742Smckusick * from this software without specific prior written permission. 1639742Smckusick * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1739742Smckusick * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1839742Smckusick * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1939742Smckusick * 20*43431Smckusick * @(#)nfs_srvcache.c 7.8 (Berkeley) 06/22/90 2139742Smckusick */ 2239742Smckusick 23*43431Smckusick /* 24*43431Smckusick * Reference: Chet Juszczak, "Improving the Performance and Correctness 25*43431Smckusick * of an NFS Server", in Proc. Winter 1989 USENIX Conference, 26*43431Smckusick * pages 53-63. San Diego, February 1989. 27*43431Smckusick */ 28*43431Smckusick 2939742Smckusick #include "param.h" 3039742Smckusick #include "user.h" 3139742Smckusick #include "vnode.h" 3239742Smckusick #include "mount.h" 3339742Smckusick #include "kernel.h" 3439759Smckusick #include "systm.h" 3539742Smckusick #include "mbuf.h" 3639742Smckusick #include "socket.h" 3739742Smckusick #include "socketvar.h" 3840152Smckusick #include "../netinet/in.h" 3939742Smckusick #include "nfsm_subs.h" 4039742Smckusick #include "nfsv2.h" 4139742Smckusick #include "nfsrvcache.h" 4239742Smckusick #include "nfs.h" 4339742Smckusick 4439742Smckusick #if ((NFSRCHSZ&(NFSRCHSZ-1)) == 0) 4539742Smckusick #define NFSRCHASH(xid) (((xid)+((xid)>>16))&(NFSRCHSZ-1)) 4639742Smckusick #else 4739742Smckusick #define NFSRCHASH(xid) (((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ) 4839742Smckusick #endif 4939742Smckusick 5039742Smckusick union rhead { 5139742Smckusick union rhead *rh_head[2]; 5239742Smckusick struct nfsrvcache *rh_chain[2]; 5339742Smckusick } rhead[NFSRCHSZ]; 5439742Smckusick 5539742Smckusick static struct nfsrvcache nfsrvcachehead; 5639742Smckusick static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ]; 5739742Smckusick 5839742Smckusick #define TRUE 1 5939742Smckusick #define FALSE 0 6039742Smckusick 6139742Smckusick /* 6239742Smckusick * Static array that defines which nfs rpc's are nonidempotent 6339742Smckusick */ 6440118Smckusick int nonidempotent[NFS_NPROCS] = { 6539742Smckusick FALSE, 6639742Smckusick FALSE, 6739742Smckusick TRUE, 6839742Smckusick FALSE, 6939742Smckusick FALSE, 7039742Smckusick FALSE, 7139742Smckusick FALSE, 7239742Smckusick FALSE, 7339742Smckusick TRUE, 7439742Smckusick TRUE, 7539742Smckusick TRUE, 7639742Smckusick TRUE, 7739742Smckusick TRUE, 7839742Smckusick TRUE, 7939742Smckusick TRUE, 8039742Smckusick TRUE, 8139742Smckusick FALSE, 8239742Smckusick 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, 10539742Smckusick }; 10639742Smckusick 10739742Smckusick /* 10839742Smckusick * Initialize the server request cache list 10939742Smckusick */ 11039742Smckusick nfsrv_initcache() 11139742Smckusick { 11239742Smckusick register int i; 11339742Smckusick register struct nfsrvcache *rp = nfsrvcache; 11439742Smckusick register struct nfsrvcache *hp = &nfsrvcachehead; 11539742Smckusick register union rhead *rh = rhead; 11639742Smckusick 11739742Smckusick for (i = NFSRCHSZ; --i >= 0; rh++) { 11839742Smckusick rh->rh_head[0] = rh; 11939742Smckusick rh->rh_head[1] = rh; 12039742Smckusick } 12139742Smckusick hp->rc_next = hp->rc_prev = hp; 12239742Smckusick for (i = NFSRVCACHESIZ; i-- > 0; ) { 12339742Smckusick rp->rc_state = RC_UNUSED; 12439742Smckusick rp->rc_flag = 0; 12539742Smckusick rp->rc_forw = rp; 12639742Smckusick rp->rc_back = rp; 12739742Smckusick rp->rc_next = hp->rc_next; 12839742Smckusick hp->rc_next->rc_prev = rp; 12939742Smckusick rp->rc_prev = hp; 13039742Smckusick hp->rc_next = rp; 13139742Smckusick rp++; 13239742Smckusick } 13339742Smckusick } 13439742Smckusick 13539742Smckusick /* 13639742Smckusick * Look for the request in the cache 13739742Smckusick * If found then 13839742Smckusick * return action and optionally reply 13939742Smckusick * else 14039742Smckusick * insert it in the cache 14139742Smckusick * 14239742Smckusick * The rules are as follows: 14339742Smckusick * - if in progress, return DROP request 14439742Smckusick * - if completed within DELAY of the current time, return DROP it 14539742Smckusick * - if completed a longer time ago return REPLY if the reply was cached or 14639742Smckusick * return DOIT 14739742Smckusick * Update/add new request at end of lru list 14839742Smckusick */ 14939742Smckusick nfsrv_getcache(nam, xid, proc, repp) 15039742Smckusick struct mbuf *nam; 15139742Smckusick u_long xid; 15239742Smckusick int proc; 15339742Smckusick struct mbuf **repp; 15439742Smckusick { 15539742Smckusick register struct nfsrvcache *rp; 15639742Smckusick register union rhead *rh; 15739742Smckusick struct mbuf *mb; 15839742Smckusick caddr_t bpos; 15939742Smckusick int ret; 16039742Smckusick 16139742Smckusick rh = &rhead[NFSRCHASH(xid)]; 16239742Smckusick loop: 16339742Smckusick for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 16441901Smckusick if (xid == rp->rc_xid && proc == rp->rc_proc && 16541901Smckusick nfs_netaddr_match(nam, &rp->rc_nam)) { 16639742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 16739742Smckusick rp->rc_flag |= RC_WANTED; 16843352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 16939742Smckusick goto loop; 17039742Smckusick } 17139742Smckusick rp->rc_flag |= RC_LOCKED; 17239742Smckusick put_at_head(rp); 17339742Smckusick if (rp->rc_state == RC_UNUSED) 17439742Smckusick panic("nfsrv cache"); 17539742Smckusick if (rp->rc_state == RC_INPROG || 17639742Smckusick (time.tv_sec - rp->rc_timestamp) < RC_DELAY) { 17739742Smckusick nfsstats.srvcache_inproghits++; 17839742Smckusick ret = RC_DROPIT; 17939742Smckusick } else if (rp->rc_flag & RC_REPSTATUS) { 18039742Smckusick nfsstats.srvcache_idemdonehits++; 18139742Smckusick nfs_rephead(0, xid, rp->rc_status, repp, &mb, 18239742Smckusick &bpos); 18339742Smckusick rp->rc_timestamp = time.tv_sec; 18439742Smckusick ret = RC_REPLY; 18539742Smckusick } else if (rp->rc_flag & RC_REPMBUF) { 18639742Smckusick nfsstats.srvcache_idemdonehits++; 18741901Smckusick *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 18839742Smckusick M_WAIT); 18939742Smckusick rp->rc_timestamp = time.tv_sec; 19039742Smckusick ret = RC_REPLY; 19139742Smckusick } else { 19239742Smckusick nfsstats.srvcache_nonidemdonehits++; 19339742Smckusick rp->rc_state = RC_INPROG; 19439742Smckusick ret = RC_DOIT; 19539742Smckusick } 19639742Smckusick rp->rc_flag &= ~RC_LOCKED; 19739742Smckusick if (rp->rc_flag & RC_WANTED) { 19839742Smckusick rp->rc_flag &= ~RC_WANTED; 19939742Smckusick wakeup((caddr_t)rp); 20039742Smckusick } 20139742Smckusick return (ret); 20239742Smckusick } 20339742Smckusick } 20439742Smckusick nfsstats.srvcache_misses++; 20539742Smckusick rp = nfsrvcachehead.rc_prev; 20639742Smckusick while ((rp->rc_flag & RC_LOCKED) != 0) { 20739742Smckusick rp->rc_flag |= RC_WANTED; 20843352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 20939742Smckusick } 21039742Smckusick remque(rp); 21139742Smckusick put_at_head(rp); 21239742Smckusick if (rp->rc_flag & RC_REPMBUF) 21339742Smckusick mb = rp->rc_reply; 21439742Smckusick else 21539742Smckusick mb = (struct mbuf *)0; 21639742Smckusick rp->rc_flag = 0; 21739742Smckusick rp->rc_state = RC_INPROG; 21839742Smckusick rp->rc_xid = xid; 21941901Smckusick bcopy((caddr_t)nam, (caddr_t)&rp->rc_nam, sizeof (struct mbuf)); 22039742Smckusick rp->rc_proc = proc; 22139742Smckusick insque(rp, rh); 22239742Smckusick if (mb) 22339742Smckusick m_freem(mb); 22439742Smckusick return (RC_DOIT); 22539742Smckusick } 22639742Smckusick 22739742Smckusick /* 22839742Smckusick * Update a request cache entry after the rpc has been done 22939742Smckusick */ 23040253Smckusick nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf) 23139742Smckusick struct mbuf *nam; 23239742Smckusick u_long xid; 23339742Smckusick int proc; 23440253Smckusick int repvalid; 23539742Smckusick int repstat; 23639742Smckusick struct mbuf *repmbuf; 23739742Smckusick { 23839742Smckusick register struct nfsrvcache *rp; 23939742Smckusick register union rhead *rh; 24039742Smckusick 24139742Smckusick rh = &rhead[NFSRCHASH(xid)]; 24239742Smckusick loop: 24339742Smckusick for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 24441901Smckusick if (xid == rp->rc_xid && proc == rp->rc_proc && 24541901Smckusick nfs_netaddr_match(nam, &rp->rc_nam)) { 24639742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 24739742Smckusick rp->rc_flag |= RC_WANTED; 24843352Smckusick (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 24939742Smckusick goto loop; 25039742Smckusick } 25139742Smckusick rp->rc_flag |= RC_LOCKED; 25239742Smckusick rp->rc_state = RC_DONE; 25340253Smckusick /* 25440253Smckusick * If we have a valid reply update status and save 25541901Smckusick * the reply for non-idempotent rpc's. 25641901Smckusick * Otherwise invalidate entry by setting the timestamp 25741901Smckusick * to nil. 25840253Smckusick */ 25940253Smckusick if (repvalid) { 26040253Smckusick rp->rc_timestamp = time.tv_sec; 26140253Smckusick if (nonidempotent[proc]) { 26240253Smckusick if (repliesstatus[proc]) { 26340253Smckusick rp->rc_status = repstat; 26440253Smckusick rp->rc_flag |= RC_REPSTATUS; 26540253Smckusick } else { 26641901Smckusick rp->rc_reply = m_copym(repmbuf, 26740253Smckusick 0, M_COPYALL, M_WAIT); 26840253Smckusick rp->rc_flag |= RC_REPMBUF; 26940253Smckusick } 27039742Smckusick } 27140253Smckusick } else { 27240253Smckusick rp->rc_timestamp = 0; 27339742Smckusick } 27439742Smckusick rp->rc_flag &= ~RC_LOCKED; 27539742Smckusick if (rp->rc_flag & RC_WANTED) { 27639742Smckusick rp->rc_flag &= ~RC_WANTED; 27739742Smckusick wakeup((caddr_t)rp); 27839742Smckusick } 27939742Smckusick return; 28039742Smckusick } 28139742Smckusick } 28239742Smckusick } 283