1 /* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)nfs_srvcache.c 8.3 (Berkeley) 3/30/95 33 * $FreeBSD: src/sys/nfs/nfs_srvcache.c,v 1.21 2000/02/13 03:32:06 peter Exp $ 34 */ 35 36 /* 37 * Reference: Chet Juszczak, "Improving the Performance and Correctness 38 * of an NFS Server", in Proc. Winter 1989 USENIX Conference, 39 * pages 53-63. San Diego, February 1989. 40 */ 41 #include <sys/param.h> 42 #include <sys/malloc.h> 43 #include <sys/mount.h> 44 #include <sys/systm.h> 45 #include <sys/mbuf.h> 46 #include <sys/socket.h> 47 #include <sys/socketvar.h> /* for dup_sockaddr */ 48 49 #include <netinet/in.h> 50 #include "rpcv2.h" 51 #include "nfsproto.h" 52 #include "nfs.h" 53 #include "nfsrvcache.h" 54 55 #ifndef NFS_NOSERVER 56 static long numnfsrvcache; 57 static long desirednfsrvcache; 58 59 #define NFSRCHASH(xid) \ 60 (&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash]) 61 static LIST_HEAD(nfsrvhash, nfsrvcache) *nfsrvhashtbl; 62 static TAILQ_HEAD(nfsrvlru, nfsrvcache) nfsrvlruhead; 63 static u_long nfsrvhash; 64 65 #define TRUE 1 66 #define FALSE 0 67 68 struct lwkt_token srvcache_token = LWKT_TOKEN_INITIALIZER(srvcache_token); 69 70 /* 71 * Static array that defines which nfs rpc's are nonidempotent 72 */ 73 static int nonidempotent[NFS_NPROCS] = { 74 FALSE, 75 FALSE, 76 TRUE, 77 FALSE, 78 FALSE, 79 FALSE, 80 FALSE, 81 TRUE, 82 TRUE, 83 TRUE, 84 TRUE, 85 TRUE, 86 TRUE, 87 TRUE, 88 TRUE, 89 TRUE, 90 FALSE, 91 FALSE, 92 FALSE, 93 FALSE, 94 FALSE, 95 FALSE, 96 FALSE, 97 FALSE, 98 FALSE, 99 FALSE, 100 }; 101 102 /* True iff the rpc reply is an nfs status ONLY! */ 103 static int nfsv2_repstat[NFS_NPROCS] = { 104 FALSE, 105 FALSE, 106 FALSE, 107 FALSE, 108 FALSE, 109 FALSE, 110 FALSE, 111 FALSE, 112 FALSE, 113 FALSE, 114 TRUE, 115 TRUE, 116 TRUE, 117 TRUE, 118 FALSE, 119 TRUE, 120 FALSE, 121 FALSE, 122 }; 123 124 /* 125 * Size the NFS server's duplicate request cache at 1/2 the nmbclusters, 126 * within a (64, 2048) range. This is to prevent all mbuf clusters being 127 * tied up in the NFS dupreq cache for small values of nmbclusters. 128 */ 129 static void 130 nfsrvcache_size_change(void) 131 { 132 desirednfsrvcache = nmbclusters / 2; 133 desirednfsrvcache = MIN(desirednfsrvcache, NFSRVCACHE_MAX_SIZE); 134 desirednfsrvcache = MAX(desirednfsrvcache, NFSRVCACHE_MIN_SIZE); 135 } 136 137 /* 138 * Initialize the server request cache list 139 */ 140 void 141 nfsrv_initcache(void) 142 { 143 nfsrvcache_size_change(); 144 nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash); 145 TAILQ_INIT(&nfsrvlruhead); 146 } 147 148 /* 149 * Destroy the server request cache list 150 */ 151 void 152 nfsrv_destroycache(void) 153 { 154 KKASSERT(TAILQ_EMPTY(&nfsrvlruhead)); 155 hashdestroy(nfsrvhashtbl, M_NFSD, nfsrvhash); 156 } 157 158 /* 159 * Look for the request in the cache 160 * If found then 161 * return action and optionally reply 162 * else 163 * insert it in the cache 164 * 165 * The rules are as follows: 166 * - if in progress, return DROP request 167 * - if completed within DELAY of the current time, return DROP it 168 * - if completed a longer time ago return REPLY if the reply was cached or 169 * return DOIT 170 * Update/add new request at end of lru list 171 */ 172 int 173 nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp, 174 struct mbuf **repp) 175 { 176 struct nfsrvcache *rp; 177 struct mbuf *mb; 178 struct sockaddr_in *saddr; 179 caddr_t bpos; 180 int ret; 181 182 /* 183 * Don't cache recent requests for reliable transport protocols. 184 * (Maybe we should for the case of a reconnect, but..) 185 */ 186 if (!nd->nd_nam2) 187 return (RC_DOIT); 188 189 lwkt_gettoken(&srvcache_token); 190 loop: 191 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL; 192 rp = rp->rc_hash.le_next) { 193 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 194 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) { 195 NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff)); 196 if ((rp->rc_flag & RC_LOCKED) != 0) { 197 rp->rc_flag |= RC_WANTED; 198 tsleep((caddr_t)rp, 0, "nfsrc", 0); 199 goto loop; 200 } 201 rp->rc_flag |= RC_LOCKED; 202 /* If not at end of LRU chain, move it there */ 203 if (TAILQ_NEXT(rp, rc_lru) != NULL) { 204 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 205 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 206 } 207 if (rp->rc_state == RC_UNUSED) 208 panic("nfsrv cache"); 209 if (rp->rc_state == RC_INPROG) { 210 nfsstats.srvcache_inproghits++; 211 ret = RC_DROPIT; 212 } else if (rp->rc_flag & RC_REPSTATUS) { 213 nfsstats.srvcache_nonidemdonehits++; 214 nfs_rephead(0, nd, slp, rp->rc_status, 215 repp, &mb, &bpos); 216 ret = RC_REPLY; 217 } else if (rp->rc_flag & RC_REPMBUF) { 218 nfsstats.srvcache_nonidemdonehits++; 219 *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 220 M_WAITOK); 221 ret = RC_REPLY; 222 } else { 223 nfsstats.srvcache_idemdonehits++; 224 rp->rc_state = RC_INPROG; 225 ret = RC_DOIT; 226 } 227 rp->rc_flag &= ~RC_LOCKED; 228 if (rp->rc_flag & RC_WANTED) { 229 rp->rc_flag &= ~RC_WANTED; 230 wakeup((caddr_t)rp); 231 } 232 lwkt_reltoken(&srvcache_token); 233 return (ret); 234 } 235 } 236 237 nfsstats.srvcache_misses++; 238 NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff)); 239 if (numnfsrvcache < desirednfsrvcache) { 240 rp = kmalloc((u_long)sizeof *rp, M_NFSD, M_WAITOK | M_ZERO); 241 numnfsrvcache++; 242 rp->rc_flag = RC_LOCKED; 243 } else { 244 rp = TAILQ_FIRST(&nfsrvlruhead); 245 while ((rp->rc_flag & RC_LOCKED) != 0) { 246 rp->rc_flag |= RC_WANTED; 247 tsleep((caddr_t)rp, 0, "nfsrc", 0); 248 rp = TAILQ_FIRST(&nfsrvlruhead); 249 } 250 rp->rc_flag |= RC_LOCKED; 251 LIST_REMOVE(rp, rc_hash); 252 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 253 if (rp->rc_flag & RC_REPMBUF) { 254 m_freem(rp->rc_reply); 255 rp->rc_reply = NULL; 256 rp->rc_flag &= ~RC_REPMBUF; 257 } 258 if (rp->rc_flag & RC_NAM) { 259 kfree(rp->rc_nam, M_SONAME); 260 rp->rc_nam = NULL; 261 rp->rc_flag &= ~RC_NAM; 262 } 263 } 264 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 265 266 rp->rc_state = RC_INPROG; 267 rp->rc_xid = nd->nd_retxid; 268 saddr = (struct sockaddr_in *)nd->nd_nam; 269 switch (saddr->sin_family) { 270 case AF_INET: 271 rp->rc_flag |= RC_INETADDR; 272 rp->rc_inetaddr = saddr->sin_addr.s_addr; 273 break; 274 default: 275 rp->rc_flag |= RC_NAM; 276 rp->rc_nam = dup_sockaddr(nd->nd_nam); 277 break; 278 } 279 rp->rc_proc = nd->nd_procnum; 280 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash); 281 rp->rc_flag &= ~RC_LOCKED; 282 if (rp->rc_flag & RC_WANTED) { 283 rp->rc_flag &= ~RC_WANTED; 284 wakeup((caddr_t)rp); 285 } 286 lwkt_reltoken(&srvcache_token); 287 288 return (RC_DOIT); 289 } 290 291 /* 292 * Update a request cache entry after the rpc has been done 293 */ 294 void 295 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf) 296 { 297 struct nfsrvcache *rp; 298 299 if (!nd->nd_nam2) 300 return; 301 302 lwkt_gettoken(&srvcache_token); 303 loop: 304 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL; 305 rp = rp->rc_hash.le_next) { 306 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 307 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) { 308 NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff)); 309 if ((rp->rc_flag & RC_LOCKED) != 0) { 310 rp->rc_flag |= RC_WANTED; 311 tsleep((caddr_t)rp, 0, "nfsrc", 0); 312 goto loop; 313 } 314 rp->rc_flag |= RC_LOCKED; 315 if (rp->rc_state == RC_DONE) { 316 /* 317 * This can occur if the cache is too small. 318 * Retransmits of the same request aren't 319 * dropped so we may see the operation 320 * complete more then once. 321 */ 322 if (rp->rc_flag & RC_REPMBUF) { 323 m_freem(rp->rc_reply); 324 rp->rc_reply = NULL; 325 rp->rc_flag &= ~RC_REPMBUF; 326 } 327 } 328 rp->rc_state = RC_DONE; 329 330 /* 331 * If we have a valid reply update status and save 332 * the reply for non-idempotent rpc's. 333 */ 334 if (repvalid && nonidempotent[nd->nd_procnum]) { 335 if ((nd->nd_flag & ND_NFSV3) == 0 && 336 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) { 337 rp->rc_status = nd->nd_repstat; 338 rp->rc_flag |= RC_REPSTATUS; 339 } else { 340 if (rp->rc_flag & RC_REPMBUF) { 341 m_freem(rp->rc_reply); 342 rp->rc_reply = NULL; 343 rp->rc_flag &= ~RC_REPMBUF; 344 } 345 rp->rc_reply = m_copym(repmbuf, 0, 346 M_COPYALL, M_WAITOK); 347 rp->rc_flag |= RC_REPMBUF; 348 } 349 } 350 rp->rc_flag &= ~RC_LOCKED; 351 if (rp->rc_flag & RC_WANTED) { 352 rp->rc_flag &= ~RC_WANTED; 353 wakeup((caddr_t)rp); 354 } 355 break; 356 } 357 } 358 lwkt_reltoken(&srvcache_token); 359 NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff)); 360 } 361 362 /* 363 * Clean out the cache. Called when the last nfsd terminates. 364 */ 365 void 366 nfsrv_cleancache(void) 367 { 368 struct nfsrvcache *rp; 369 370 lwkt_gettoken(&srvcache_token); 371 while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) { 372 if (rp->rc_flag & RC_LOCKED) { 373 rp->rc_flag |= RC_WANTED; 374 tsleep((caddr_t)rp, 0, "nfsrc", 0); 375 continue; 376 } 377 LIST_REMOVE(rp, rc_hash); 378 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 379 if (rp->rc_flag & RC_REPMBUF) { 380 m_freem(rp->rc_reply); 381 rp->rc_reply = NULL; 382 rp->rc_flag &= ~RC_REPMBUF; 383 } 384 if (rp->rc_flag & RC_NAM) { 385 kfree(rp->rc_nam, M_SONAME); 386 rp->rc_nam = NULL; 387 rp->rc_flag &= ~RC_NAM; 388 } 389 kfree(rp, M_NFSD); 390 } 391 numnfsrvcache = 0; 392 lwkt_reltoken(&srvcache_token); 393 } 394 395 #endif /* NFS_NOSERVER */ 396