1 /* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Rick Macklem at The University of Guelph. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)nfs_srvcache.c 7.11 (Berkeley) 4/16/91 37 */ 38 39 /* 40 * Reference: Chet Juszczak, "Improving the Performance and Correctness 41 * of an NFS Server", in Proc. Winter 1989 USENIX Conference, 42 * pages 53-63. San Diego, February 1989. 43 */ 44 45 #include "param.h" 46 #include "namei.h" 47 #include "vnode.h" 48 #include "mount.h" 49 #include "kernel.h" 50 #include "systm.h" 51 #include "mbuf.h" 52 #include "socket.h" 53 #include "socketvar.h" 54 55 #include "../netinet/in.h" 56 57 #include "nfsm_subs.h" 58 #include "nfsv2.h" 59 #include "nfsrvcache.h" 60 #include "nfs.h" 61 62 #if ((NFSRCHSZ&(NFSRCHSZ-1)) == 0) 63 #define NFSRCHASH(xid) (((xid)+((xid)>>16))&(NFSRCHSZ-1)) 64 #else 65 #define NFSRCHASH(xid) (((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ) 66 #endif 67 68 extern int nonidempotent[NFS_NPROCS]; 69 70 union rhead { 71 union rhead *rh_head[2]; 72 struct nfsrvcache *rh_chain[2]; 73 } rhead[NFSRCHSZ]; 74 75 static struct nfsrvcache nfsrvcachehead; 76 static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ]; 77 78 #define TRUE 1 79 #define FALSE 0 80 81 82 /* True iff the rpc reply is an nfs status ONLY! */ 83 static int repliesstatus[NFS_NPROCS] = { 84 FALSE, 85 FALSE, 86 FALSE, 87 FALSE, 88 FALSE, 89 FALSE, 90 FALSE, 91 FALSE, 92 FALSE, 93 FALSE, 94 TRUE, 95 TRUE, 96 TRUE, 97 TRUE, 98 FALSE, 99 TRUE, 100 FALSE, 101 FALSE, 102 }; 103 104 /* 105 * Initialize the server request cache list 106 */ 107 nfsrv_initcache() 108 { 109 register int i; 110 register struct nfsrvcache *rp = nfsrvcache; 111 register struct nfsrvcache *hp = &nfsrvcachehead; 112 register union rhead *rh = rhead; 113 114 for (i = NFSRCHSZ; --i >= 0; rh++) { 115 rh->rh_head[0] = rh; 116 rh->rh_head[1] = rh; 117 } 118 hp->rc_next = hp->rc_prev = hp; 119 for (i = NFSRVCACHESIZ; i-- > 0; ) { 120 rp->rc_state = RC_UNUSED; 121 rp->rc_flag = 0; 122 rp->rc_forw = rp; 123 rp->rc_back = rp; 124 rp->rc_next = hp->rc_next; 125 hp->rc_next->rc_prev = rp; 126 rp->rc_prev = hp; 127 hp->rc_next = rp; 128 rp++; 129 } 130 } 131 132 /* 133 * Look for the request in the cache 134 * If found then 135 * return action and optionally reply 136 * else 137 * insert it in the cache 138 * 139 * The rules are as follows: 140 * - if in progress, return DROP request 141 * - if completed within DELAY of the current time, return DROP it 142 * - if completed a longer time ago return REPLY if the reply was cached or 143 * return DOIT 144 * Update/add new request at end of lru list 145 */ 146 nfsrv_getcache(nam, xid, proc, repp) 147 struct mbuf *nam; 148 u_long xid; 149 int proc; 150 struct mbuf **repp; 151 { 152 register struct nfsrvcache *rp; 153 register union rhead *rh; 154 struct mbuf *mb; 155 caddr_t bpos; 156 int ret; 157 158 rh = &rhead[NFSRCHASH(xid)]; 159 loop: 160 for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 161 if (xid == rp->rc_xid && proc == rp->rc_proc && 162 nfs_netaddr_match(nam, &rp->rc_nam)) { 163 if ((rp->rc_flag & RC_LOCKED) != 0) { 164 rp->rc_flag |= RC_WANTED; 165 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 166 goto loop; 167 } 168 rp->rc_flag |= RC_LOCKED; 169 put_at_head(rp); 170 if (rp->rc_state == RC_UNUSED) 171 panic("nfsrv cache"); 172 if (rp->rc_state == RC_INPROG || 173 (time.tv_sec - rp->rc_timestamp) < RC_DELAY) { 174 nfsstats.srvcache_inproghits++; 175 ret = RC_DROPIT; 176 } else if (rp->rc_flag & RC_REPSTATUS) { 177 nfsstats.srvcache_idemdonehits++; 178 nfs_rephead(0, xid, rp->rc_status, repp, &mb, 179 &bpos); 180 rp->rc_timestamp = time.tv_sec; 181 ret = RC_REPLY; 182 } else if (rp->rc_flag & RC_REPMBUF) { 183 nfsstats.srvcache_idemdonehits++; 184 *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 185 M_WAIT); 186 rp->rc_timestamp = time.tv_sec; 187 ret = RC_REPLY; 188 } else { 189 nfsstats.srvcache_nonidemdonehits++; 190 rp->rc_state = RC_INPROG; 191 ret = RC_DOIT; 192 } 193 rp->rc_flag &= ~RC_LOCKED; 194 if (rp->rc_flag & RC_WANTED) { 195 rp->rc_flag &= ~RC_WANTED; 196 wakeup((caddr_t)rp); 197 } 198 return (ret); 199 } 200 } 201 nfsstats.srvcache_misses++; 202 rp = nfsrvcachehead.rc_prev; 203 while ((rp->rc_flag & RC_LOCKED) != 0) { 204 rp->rc_flag |= RC_WANTED; 205 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 206 } 207 remque(rp); 208 put_at_head(rp); 209 if (rp->rc_flag & RC_REPMBUF) 210 mb = rp->rc_reply; 211 else 212 mb = (struct mbuf *)0; 213 rp->rc_flag = 0; 214 rp->rc_state = RC_INPROG; 215 rp->rc_xid = xid; 216 bcopy((caddr_t)nam, (caddr_t)&rp->rc_nam, sizeof (struct mbuf)); 217 rp->rc_proc = proc; 218 insque(rp, rh); 219 if (mb) 220 m_freem(mb); 221 return (RC_DOIT); 222 } 223 224 /* 225 * Update a request cache entry after the rpc has been done 226 */ 227 nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf) 228 struct mbuf *nam; 229 u_long xid; 230 int proc; 231 int repvalid; 232 int repstat; 233 struct mbuf *repmbuf; 234 { 235 register struct nfsrvcache *rp; 236 register union rhead *rh; 237 238 rh = &rhead[NFSRCHASH(xid)]; 239 loop: 240 for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 241 if (xid == rp->rc_xid && proc == rp->rc_proc && 242 nfs_netaddr_match(nam, &rp->rc_nam)) { 243 if ((rp->rc_flag & RC_LOCKED) != 0) { 244 rp->rc_flag |= RC_WANTED; 245 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 246 goto loop; 247 } 248 rp->rc_flag |= RC_LOCKED; 249 rp->rc_state = RC_DONE; 250 /* 251 * If we have a valid reply update status and save 252 * the reply for non-idempotent rpc's. 253 * Otherwise invalidate entry by setting the timestamp 254 * to nil. 255 */ 256 if (repvalid) { 257 rp->rc_timestamp = time.tv_sec; 258 if (nonidempotent[proc]) { 259 if (repliesstatus[proc]) { 260 rp->rc_status = repstat; 261 rp->rc_flag |= RC_REPSTATUS; 262 } else { 263 rp->rc_reply = m_copym(repmbuf, 264 0, M_COPYALL, M_WAIT); 265 rp->rc_flag |= RC_REPMBUF; 266 } 267 } 268 } else { 269 rp->rc_timestamp = 0; 270 } 271 rp->rc_flag &= ~RC_LOCKED; 272 if (rp->rc_flag & RC_WANTED) { 273 rp->rc_flag &= ~RC_WANTED; 274 wakeup((caddr_t)rp); 275 } 276 return; 277 } 278 } 279 } 280