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*41901Smckusick * @(#)nfs_srvcache.c 7.6 (Berkeley) 05/14/90 2139742Smckusick */ 2239742Smckusick 2339742Smckusick #include "param.h" 2439742Smckusick #include "user.h" 2539742Smckusick #include "vnode.h" 2639742Smckusick #include "mount.h" 2739742Smckusick #include "kernel.h" 2839759Smckusick #include "systm.h" 2939742Smckusick #include "mbuf.h" 3039742Smckusick #include "socket.h" 3139742Smckusick #include "socketvar.h" 3240152Smckusick #include "../netinet/in.h" 3339742Smckusick #include "nfsm_subs.h" 3439742Smckusick #include "nfsv2.h" 3539742Smckusick #include "nfsrvcache.h" 3639742Smckusick #include "nfs.h" 3739742Smckusick 3839742Smckusick #if ((NFSRCHSZ&(NFSRCHSZ-1)) == 0) 3939742Smckusick #define NFSRCHASH(xid) (((xid)+((xid)>>16))&(NFSRCHSZ-1)) 4039742Smckusick #else 4139742Smckusick #define NFSRCHASH(xid) (((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ) 4239742Smckusick #endif 4339742Smckusick 4439742Smckusick union rhead { 4539742Smckusick union rhead *rh_head[2]; 4639742Smckusick struct nfsrvcache *rh_chain[2]; 4739742Smckusick } rhead[NFSRCHSZ]; 4839742Smckusick 4939742Smckusick static struct nfsrvcache nfsrvcachehead; 5039742Smckusick static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ]; 5139742Smckusick 5239742Smckusick #define TRUE 1 5339742Smckusick #define FALSE 0 5439742Smckusick 5539742Smckusick /* 5639742Smckusick * Static array that defines which nfs rpc's are nonidempotent 5739742Smckusick */ 5840118Smckusick int nonidempotent[NFS_NPROCS] = { 5939742Smckusick FALSE, 6039742Smckusick FALSE, 6139742Smckusick TRUE, 6239742Smckusick FALSE, 6339742Smckusick FALSE, 6439742Smckusick FALSE, 6539742Smckusick FALSE, 6639742Smckusick FALSE, 6739742Smckusick TRUE, 6839742Smckusick TRUE, 6939742Smckusick TRUE, 7039742Smckusick TRUE, 7139742Smckusick TRUE, 7239742Smckusick TRUE, 7339742Smckusick TRUE, 7439742Smckusick TRUE, 7539742Smckusick FALSE, 7639742Smckusick FALSE, 7739742Smckusick }; 7839742Smckusick 7939742Smckusick /* True iff the rpc reply is an nfs status ONLY! */ 8039742Smckusick static int repliesstatus[NFS_NPROCS] = { 8139742Smckusick FALSE, 8239742Smckusick FALSE, 8339742Smckusick FALSE, 8439742Smckusick FALSE, 8539742Smckusick FALSE, 8639742Smckusick FALSE, 8739742Smckusick FALSE, 8839742Smckusick FALSE, 8939742Smckusick FALSE, 9039742Smckusick FALSE, 9139742Smckusick TRUE, 9239742Smckusick TRUE, 9339742Smckusick TRUE, 9439742Smckusick TRUE, 9539742Smckusick FALSE, 9639742Smckusick TRUE, 9739742Smckusick FALSE, 9839742Smckusick FALSE, 9939742Smckusick }; 10039742Smckusick 10139742Smckusick /* 10239742Smckusick * Initialize the server request cache list 10339742Smckusick */ 10439742Smckusick nfsrv_initcache() 10539742Smckusick { 10639742Smckusick register int i; 10739742Smckusick register struct nfsrvcache *rp = nfsrvcache; 10839742Smckusick register struct nfsrvcache *hp = &nfsrvcachehead; 10939742Smckusick register union rhead *rh = rhead; 11039742Smckusick 11139742Smckusick for (i = NFSRCHSZ; --i >= 0; rh++) { 11239742Smckusick rh->rh_head[0] = rh; 11339742Smckusick rh->rh_head[1] = rh; 11439742Smckusick } 11539742Smckusick hp->rc_next = hp->rc_prev = hp; 11639742Smckusick for (i = NFSRVCACHESIZ; i-- > 0; ) { 11739742Smckusick rp->rc_state = RC_UNUSED; 11839742Smckusick rp->rc_flag = 0; 11939742Smckusick rp->rc_forw = rp; 12039742Smckusick rp->rc_back = rp; 12139742Smckusick rp->rc_next = hp->rc_next; 12239742Smckusick hp->rc_next->rc_prev = rp; 12339742Smckusick rp->rc_prev = hp; 12439742Smckusick hp->rc_next = rp; 12539742Smckusick rp++; 12639742Smckusick } 12739742Smckusick } 12839742Smckusick 12939742Smckusick /* 13039742Smckusick * Look for the request in the cache 13139742Smckusick * If found then 13239742Smckusick * return action and optionally reply 13339742Smckusick * else 13439742Smckusick * insert it in the cache 13539742Smckusick * 13639742Smckusick * The rules are as follows: 13739742Smckusick * - if in progress, return DROP request 13839742Smckusick * - if completed within DELAY of the current time, return DROP it 13939742Smckusick * - if completed a longer time ago return REPLY if the reply was cached or 14039742Smckusick * return DOIT 14139742Smckusick * Update/add new request at end of lru list 14239742Smckusick */ 14339742Smckusick nfsrv_getcache(nam, xid, proc, repp) 14439742Smckusick struct mbuf *nam; 14539742Smckusick u_long xid; 14639742Smckusick int proc; 14739742Smckusick struct mbuf **repp; 14839742Smckusick { 14939742Smckusick register struct nfsrvcache *rp; 15039742Smckusick register union rhead *rh; 15139742Smckusick struct mbuf *mb; 15239742Smckusick caddr_t bpos; 15339742Smckusick int ret; 15439742Smckusick 15539742Smckusick rh = &rhead[NFSRCHASH(xid)]; 15639742Smckusick loop: 15739742Smckusick for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 158*41901Smckusick if (xid == rp->rc_xid && proc == rp->rc_proc && 159*41901Smckusick nfs_netaddr_match(nam, &rp->rc_nam)) { 16039742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 16139742Smckusick rp->rc_flag |= RC_WANTED; 16239742Smckusick sleep((caddr_t)rp, PZERO-1); 16339742Smckusick goto loop; 16439742Smckusick } 16539742Smckusick rp->rc_flag |= RC_LOCKED; 16639742Smckusick put_at_head(rp); 16739742Smckusick if (rp->rc_state == RC_UNUSED) 16839742Smckusick panic("nfsrv cache"); 16939742Smckusick if (rp->rc_state == RC_INPROG || 17039742Smckusick (time.tv_sec - rp->rc_timestamp) < RC_DELAY) { 17139742Smckusick nfsstats.srvcache_inproghits++; 17239742Smckusick ret = RC_DROPIT; 17339742Smckusick } else if (rp->rc_flag & RC_REPSTATUS) { 17439742Smckusick nfsstats.srvcache_idemdonehits++; 17539742Smckusick nfs_rephead(0, xid, rp->rc_status, repp, &mb, 17639742Smckusick &bpos); 17739742Smckusick rp->rc_timestamp = time.tv_sec; 17839742Smckusick ret = RC_REPLY; 17939742Smckusick } else if (rp->rc_flag & RC_REPMBUF) { 18039742Smckusick nfsstats.srvcache_idemdonehits++; 181*41901Smckusick *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 18239742Smckusick M_WAIT); 18339742Smckusick rp->rc_timestamp = time.tv_sec; 18439742Smckusick ret = RC_REPLY; 18539742Smckusick } else { 18639742Smckusick nfsstats.srvcache_nonidemdonehits++; 18739742Smckusick rp->rc_state = RC_INPROG; 18839742Smckusick ret = RC_DOIT; 18939742Smckusick } 19039742Smckusick rp->rc_flag &= ~RC_LOCKED; 19139742Smckusick if (rp->rc_flag & RC_WANTED) { 19239742Smckusick rp->rc_flag &= ~RC_WANTED; 19339742Smckusick wakeup((caddr_t)rp); 19439742Smckusick } 19539742Smckusick return (ret); 19639742Smckusick } 19739742Smckusick } 19839742Smckusick nfsstats.srvcache_misses++; 19939742Smckusick rp = nfsrvcachehead.rc_prev; 20039742Smckusick while ((rp->rc_flag & RC_LOCKED) != 0) { 20139742Smckusick rp->rc_flag |= RC_WANTED; 20239742Smckusick sleep((caddr_t)rp, PZERO-1); 20339742Smckusick } 20439742Smckusick remque(rp); 20539742Smckusick put_at_head(rp); 20639742Smckusick if (rp->rc_flag & RC_REPMBUF) 20739742Smckusick mb = rp->rc_reply; 20839742Smckusick else 20939742Smckusick mb = (struct mbuf *)0; 21039742Smckusick rp->rc_flag = 0; 21139742Smckusick rp->rc_state = RC_INPROG; 21239742Smckusick rp->rc_xid = xid; 213*41901Smckusick bcopy((caddr_t)nam, (caddr_t)&rp->rc_nam, sizeof (struct mbuf)); 21439742Smckusick rp->rc_proc = proc; 21539742Smckusick insque(rp, rh); 21639742Smckusick if (mb) 21739742Smckusick m_freem(mb); 21839742Smckusick return (RC_DOIT); 21939742Smckusick } 22039742Smckusick 22139742Smckusick /* 22239742Smckusick * Update a request cache entry after the rpc has been done 22339742Smckusick */ 22440253Smckusick nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf) 22539742Smckusick struct mbuf *nam; 22639742Smckusick u_long xid; 22739742Smckusick int proc; 22840253Smckusick int repvalid; 22939742Smckusick int repstat; 23039742Smckusick struct mbuf *repmbuf; 23139742Smckusick { 23239742Smckusick register struct nfsrvcache *rp; 23339742Smckusick register union rhead *rh; 23439742Smckusick 23539742Smckusick rh = &rhead[NFSRCHASH(xid)]; 23639742Smckusick loop: 23739742Smckusick for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 238*41901Smckusick if (xid == rp->rc_xid && proc == rp->rc_proc && 239*41901Smckusick nfs_netaddr_match(nam, &rp->rc_nam)) { 24039742Smckusick if ((rp->rc_flag & RC_LOCKED) != 0) { 24139742Smckusick rp->rc_flag |= RC_WANTED; 24239742Smckusick sleep((caddr_t)rp, PZERO-1); 24339742Smckusick goto loop; 24439742Smckusick } 24539742Smckusick rp->rc_flag |= RC_LOCKED; 24639742Smckusick rp->rc_state = RC_DONE; 24740253Smckusick /* 24840253Smckusick * If we have a valid reply update status and save 249*41901Smckusick * the reply for non-idempotent rpc's. 250*41901Smckusick * Otherwise invalidate entry by setting the timestamp 251*41901Smckusick * to nil. 25240253Smckusick */ 25340253Smckusick if (repvalid) { 25440253Smckusick rp->rc_timestamp = time.tv_sec; 25540253Smckusick if (nonidempotent[proc]) { 25640253Smckusick if (repliesstatus[proc]) { 25740253Smckusick rp->rc_status = repstat; 25840253Smckusick rp->rc_flag |= RC_REPSTATUS; 25940253Smckusick } else { 260*41901Smckusick rp->rc_reply = m_copym(repmbuf, 26140253Smckusick 0, M_COPYALL, M_WAIT); 26240253Smckusick rp->rc_flag |= RC_REPMBUF; 26340253Smckusick } 26439742Smckusick } 26540253Smckusick } else { 26640253Smckusick rp->rc_timestamp = 0; 26739742Smckusick } 26839742Smckusick rp->rc_flag &= ~RC_LOCKED; 26939742Smckusick if (rp->rc_flag & RC_WANTED) { 27039742Smckusick rp->rc_flag &= ~RC_WANTED; 27139742Smckusick wakeup((caddr_t)rp); 27239742Smckusick } 27339742Smckusick return; 27439742Smckusick } 27539742Smckusick } 27639742Smckusick } 277