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 * %sccs.include.redist.c% 9 * 10 * @(#)nfs_srvcache.c 7.10 (Berkeley) 03/19/91 11 */ 12 13 /* 14 * Reference: Chet Juszczak, "Improving the Performance and Correctness 15 * of an NFS Server", in Proc. Winter 1989 USENIX Conference, 16 * pages 53-63. San Diego, February 1989. 17 */ 18 19 #include "param.h" 20 #include "vnode.h" 21 #include "mount.h" 22 #include "kernel.h" 23 #include "systm.h" 24 #include "mbuf.h" 25 #include "socket.h" 26 #include "socketvar.h" 27 28 #include "../netinet/in.h" 29 30 #include "nfsm_subs.h" 31 #include "nfsv2.h" 32 #include "nfsrvcache.h" 33 #include "nfs.h" 34 35 #if ((NFSRCHSZ&(NFSRCHSZ-1)) == 0) 36 #define NFSRCHASH(xid) (((xid)+((xid)>>16))&(NFSRCHSZ-1)) 37 #else 38 #define NFSRCHASH(xid) (((unsigned)((xid)+((xid)>>16)))%NFSRCHSZ) 39 #endif 40 41 union rhead { 42 union rhead *rh_head[2]; 43 struct nfsrvcache *rh_chain[2]; 44 } rhead[NFSRCHSZ]; 45 46 static struct nfsrvcache nfsrvcachehead; 47 static struct nfsrvcache nfsrvcache[NFSRVCACHESIZ]; 48 49 #define TRUE 1 50 #define FALSE 0 51 52 /* 53 * Static array that defines which nfs rpc's are nonidempotent 54 */ 55 int nonidempotent[NFS_NPROCS] = { 56 FALSE, 57 FALSE, 58 TRUE, 59 FALSE, 60 FALSE, 61 FALSE, 62 FALSE, 63 FALSE, 64 TRUE, 65 TRUE, 66 TRUE, 67 TRUE, 68 TRUE, 69 TRUE, 70 TRUE, 71 TRUE, 72 FALSE, 73 FALSE, 74 }; 75 76 /* True iff the rpc reply is an nfs status ONLY! */ 77 static int repliesstatus[NFS_NPROCS] = { 78 FALSE, 79 FALSE, 80 FALSE, 81 FALSE, 82 FALSE, 83 FALSE, 84 FALSE, 85 FALSE, 86 FALSE, 87 FALSE, 88 TRUE, 89 TRUE, 90 TRUE, 91 TRUE, 92 FALSE, 93 TRUE, 94 FALSE, 95 FALSE, 96 }; 97 98 /* 99 * Initialize the server request cache list 100 */ 101 nfsrv_initcache() 102 { 103 register int i; 104 register struct nfsrvcache *rp = nfsrvcache; 105 register struct nfsrvcache *hp = &nfsrvcachehead; 106 register union rhead *rh = rhead; 107 108 for (i = NFSRCHSZ; --i >= 0; rh++) { 109 rh->rh_head[0] = rh; 110 rh->rh_head[1] = rh; 111 } 112 hp->rc_next = hp->rc_prev = hp; 113 for (i = NFSRVCACHESIZ; i-- > 0; ) { 114 rp->rc_state = RC_UNUSED; 115 rp->rc_flag = 0; 116 rp->rc_forw = rp; 117 rp->rc_back = rp; 118 rp->rc_next = hp->rc_next; 119 hp->rc_next->rc_prev = rp; 120 rp->rc_prev = hp; 121 hp->rc_next = rp; 122 rp++; 123 } 124 } 125 126 /* 127 * Look for the request in the cache 128 * If found then 129 * return action and optionally reply 130 * else 131 * insert it in the cache 132 * 133 * The rules are as follows: 134 * - if in progress, return DROP request 135 * - if completed within DELAY of the current time, return DROP it 136 * - if completed a longer time ago return REPLY if the reply was cached or 137 * return DOIT 138 * Update/add new request at end of lru list 139 */ 140 nfsrv_getcache(nam, xid, proc, repp) 141 struct mbuf *nam; 142 u_long xid; 143 int proc; 144 struct mbuf **repp; 145 { 146 register struct nfsrvcache *rp; 147 register union rhead *rh; 148 struct mbuf *mb; 149 caddr_t bpos; 150 int ret; 151 152 rh = &rhead[NFSRCHASH(xid)]; 153 loop: 154 for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 155 if (xid == rp->rc_xid && proc == rp->rc_proc && 156 nfs_netaddr_match(nam, &rp->rc_nam)) { 157 if ((rp->rc_flag & RC_LOCKED) != 0) { 158 rp->rc_flag |= RC_WANTED; 159 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 160 goto loop; 161 } 162 rp->rc_flag |= RC_LOCKED; 163 put_at_head(rp); 164 if (rp->rc_state == RC_UNUSED) 165 panic("nfsrv cache"); 166 if (rp->rc_state == RC_INPROG || 167 (time.tv_sec - rp->rc_timestamp) < RC_DELAY) { 168 nfsstats.srvcache_inproghits++; 169 ret = RC_DROPIT; 170 } else if (rp->rc_flag & RC_REPSTATUS) { 171 nfsstats.srvcache_idemdonehits++; 172 nfs_rephead(0, xid, rp->rc_status, repp, &mb, 173 &bpos); 174 rp->rc_timestamp = time.tv_sec; 175 ret = RC_REPLY; 176 } else if (rp->rc_flag & RC_REPMBUF) { 177 nfsstats.srvcache_idemdonehits++; 178 *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 179 M_WAIT); 180 rp->rc_timestamp = time.tv_sec; 181 ret = RC_REPLY; 182 } else { 183 nfsstats.srvcache_nonidemdonehits++; 184 rp->rc_state = RC_INPROG; 185 ret = RC_DOIT; 186 } 187 rp->rc_flag &= ~RC_LOCKED; 188 if (rp->rc_flag & RC_WANTED) { 189 rp->rc_flag &= ~RC_WANTED; 190 wakeup((caddr_t)rp); 191 } 192 return (ret); 193 } 194 } 195 nfsstats.srvcache_misses++; 196 rp = nfsrvcachehead.rc_prev; 197 while ((rp->rc_flag & RC_LOCKED) != 0) { 198 rp->rc_flag |= RC_WANTED; 199 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 200 } 201 remque(rp); 202 put_at_head(rp); 203 if (rp->rc_flag & RC_REPMBUF) 204 mb = rp->rc_reply; 205 else 206 mb = (struct mbuf *)0; 207 rp->rc_flag = 0; 208 rp->rc_state = RC_INPROG; 209 rp->rc_xid = xid; 210 bcopy((caddr_t)nam, (caddr_t)&rp->rc_nam, sizeof (struct mbuf)); 211 rp->rc_proc = proc; 212 insque(rp, rh); 213 if (mb) 214 m_freem(mb); 215 return (RC_DOIT); 216 } 217 218 /* 219 * Update a request cache entry after the rpc has been done 220 */ 221 nfsrv_updatecache(nam, xid, proc, repvalid, repstat, repmbuf) 222 struct mbuf *nam; 223 u_long xid; 224 int proc; 225 int repvalid; 226 int repstat; 227 struct mbuf *repmbuf; 228 { 229 register struct nfsrvcache *rp; 230 register union rhead *rh; 231 232 rh = &rhead[NFSRCHASH(xid)]; 233 loop: 234 for (rp = rh->rh_chain[0]; rp != (struct nfsrvcache *)rh; rp = rp->rc_forw) { 235 if (xid == rp->rc_xid && proc == rp->rc_proc && 236 nfs_netaddr_match(nam, &rp->rc_nam)) { 237 if ((rp->rc_flag & RC_LOCKED) != 0) { 238 rp->rc_flag |= RC_WANTED; 239 (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); 240 goto loop; 241 } 242 rp->rc_flag |= RC_LOCKED; 243 rp->rc_state = RC_DONE; 244 /* 245 * If we have a valid reply update status and save 246 * the reply for non-idempotent rpc's. 247 * Otherwise invalidate entry by setting the timestamp 248 * to nil. 249 */ 250 if (repvalid) { 251 rp->rc_timestamp = time.tv_sec; 252 if (nonidempotent[proc]) { 253 if (repliesstatus[proc]) { 254 rp->rc_status = repstat; 255 rp->rc_flag |= RC_REPSTATUS; 256 } else { 257 rp->rc_reply = m_copym(repmbuf, 258 0, M_COPYALL, M_WAIT); 259 rp->rc_flag |= RC_REPMBUF; 260 } 261 } 262 } else { 263 rp->rc_timestamp = 0; 264 } 265 rp->rc_flag &= ~RC_LOCKED; 266 if (rp->rc_flag & RC_WANTED) { 267 rp->rc_flag &= ~RC_WANTED; 268 wakeup((caddr_t)rp); 269 } 270 return; 271 } 272 } 273 } 274