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 * Look for the request in the cache 150 * If found then 151 * return action and optionally reply 152 * else 153 * insert it in the cache 154 * 155 * The rules are as follows: 156 * - if in progress, return DROP request 157 * - if completed within DELAY of the current time, return DROP it 158 * - if completed a longer time ago return REPLY if the reply was cached or 159 * return DOIT 160 * Update/add new request at end of lru list 161 */ 162 int 163 nfsrv_getcache(struct nfsrv_descript *nd, struct nfssvc_sock *slp, 164 struct mbuf **repp) 165 { 166 struct nfsrvcache *rp; 167 struct mbuf *mb; 168 struct sockaddr_in *saddr; 169 caddr_t bpos; 170 int ret; 171 172 /* 173 * Don't cache recent requests for reliable transport protocols. 174 * (Maybe we should for the case of a reconnect, but..) 175 */ 176 if (!nd->nd_nam2) 177 return (RC_DOIT); 178 179 lwkt_gettoken(&srvcache_token); 180 loop: 181 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL; 182 rp = rp->rc_hash.le_next) { 183 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 184 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) { 185 NFS_DPF(RC, ("H%03x", rp->rc_xid & 0xfff)); 186 if ((rp->rc_flag & RC_LOCKED) != 0) { 187 rp->rc_flag |= RC_WANTED; 188 tsleep((caddr_t)rp, 0, "nfsrc", 0); 189 goto loop; 190 } 191 rp->rc_flag |= RC_LOCKED; 192 /* If not at end of LRU chain, move it there */ 193 if (TAILQ_NEXT(rp, rc_lru) != NULL) { 194 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 195 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 196 } 197 if (rp->rc_state == RC_UNUSED) 198 panic("nfsrv cache"); 199 if (rp->rc_state == RC_INPROG) { 200 nfsstats.srvcache_inproghits++; 201 ret = RC_DROPIT; 202 } else if (rp->rc_flag & RC_REPSTATUS) { 203 nfsstats.srvcache_nonidemdonehits++; 204 nfs_rephead(0, nd, slp, rp->rc_status, 205 repp, &mb, &bpos); 206 ret = RC_REPLY; 207 } else if (rp->rc_flag & RC_REPMBUF) { 208 nfsstats.srvcache_nonidemdonehits++; 209 *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 210 M_WAITOK); 211 ret = RC_REPLY; 212 } else { 213 nfsstats.srvcache_idemdonehits++; 214 rp->rc_state = RC_INPROG; 215 ret = RC_DOIT; 216 } 217 rp->rc_flag &= ~RC_LOCKED; 218 if (rp->rc_flag & RC_WANTED) { 219 rp->rc_flag &= ~RC_WANTED; 220 wakeup((caddr_t)rp); 221 } 222 lwkt_reltoken(&srvcache_token); 223 return (ret); 224 } 225 } 226 227 nfsstats.srvcache_misses++; 228 NFS_DPF(RC, ("M%03x", nd->nd_retxid & 0xfff)); 229 if (numnfsrvcache < desirednfsrvcache) { 230 rp = kmalloc((u_long)sizeof *rp, M_NFSD, M_WAITOK | M_ZERO); 231 numnfsrvcache++; 232 rp->rc_flag = RC_LOCKED; 233 } else { 234 rp = TAILQ_FIRST(&nfsrvlruhead); 235 while ((rp->rc_flag & RC_LOCKED) != 0) { 236 rp->rc_flag |= RC_WANTED; 237 tsleep((caddr_t)rp, 0, "nfsrc", 0); 238 rp = TAILQ_FIRST(&nfsrvlruhead); 239 } 240 rp->rc_flag |= RC_LOCKED; 241 LIST_REMOVE(rp, rc_hash); 242 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 243 if (rp->rc_flag & RC_REPMBUF) { 244 m_freem(rp->rc_reply); 245 rp->rc_reply = NULL; 246 rp->rc_flag &= ~RC_REPMBUF; 247 } 248 if (rp->rc_flag & RC_NAM) { 249 kfree(rp->rc_nam, M_SONAME); 250 rp->rc_nam = NULL; 251 rp->rc_flag &= ~RC_NAM; 252 } 253 } 254 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 255 256 rp->rc_state = RC_INPROG; 257 rp->rc_xid = nd->nd_retxid; 258 saddr = (struct sockaddr_in *)nd->nd_nam; 259 switch (saddr->sin_family) { 260 case AF_INET: 261 rp->rc_flag |= RC_INETADDR; 262 rp->rc_inetaddr = saddr->sin_addr.s_addr; 263 break; 264 default: 265 rp->rc_flag |= RC_NAM; 266 rp->rc_nam = dup_sockaddr(nd->nd_nam); 267 break; 268 } 269 rp->rc_proc = nd->nd_procnum; 270 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash); 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 lwkt_reltoken(&srvcache_token); 277 278 return (RC_DOIT); 279 } 280 281 /* 282 * Update a request cache entry after the rpc has been done 283 */ 284 void 285 nfsrv_updatecache(struct nfsrv_descript *nd, int repvalid, struct mbuf *repmbuf) 286 { 287 struct nfsrvcache *rp; 288 289 if (!nd->nd_nam2) 290 return; 291 292 lwkt_gettoken(&srvcache_token); 293 loop: 294 for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != NULL; 295 rp = rp->rc_hash.le_next) { 296 if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && 297 netaddr_match(AF_INET, &rp->rc_haddr, nd->nd_nam)) { 298 NFS_DPF(RC, ("U%03x", rp->rc_xid & 0xfff)); 299 if ((rp->rc_flag & RC_LOCKED) != 0) { 300 rp->rc_flag |= RC_WANTED; 301 tsleep((caddr_t)rp, 0, "nfsrc", 0); 302 goto loop; 303 } 304 rp->rc_flag |= RC_LOCKED; 305 if (rp->rc_state == RC_DONE) { 306 /* 307 * This can occur if the cache is too small. 308 * Retransmits of the same request aren't 309 * dropped so we may see the operation 310 * complete more then once. 311 */ 312 if (rp->rc_flag & RC_REPMBUF) { 313 m_freem(rp->rc_reply); 314 rp->rc_reply = NULL; 315 rp->rc_flag &= ~RC_REPMBUF; 316 } 317 } 318 rp->rc_state = RC_DONE; 319 320 /* 321 * If we have a valid reply update status and save 322 * the reply for non-idempotent rpc's. 323 */ 324 if (repvalid && nonidempotent[nd->nd_procnum]) { 325 if ((nd->nd_flag & ND_NFSV3) == 0 && 326 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) { 327 rp->rc_status = nd->nd_repstat; 328 rp->rc_flag |= RC_REPSTATUS; 329 } else { 330 if (rp->rc_flag & RC_REPMBUF) { 331 m_freem(rp->rc_reply); 332 rp->rc_reply = NULL; 333 rp->rc_flag &= ~RC_REPMBUF; 334 } 335 rp->rc_reply = m_copym(repmbuf, 0, 336 M_COPYALL, M_WAITOK); 337 rp->rc_flag |= RC_REPMBUF; 338 } 339 } 340 rp->rc_flag &= ~RC_LOCKED; 341 if (rp->rc_flag & RC_WANTED) { 342 rp->rc_flag &= ~RC_WANTED; 343 wakeup((caddr_t)rp); 344 } 345 break; 346 } 347 } 348 lwkt_reltoken(&srvcache_token); 349 NFS_DPF(RC, ("L%03x", nd->nd_retxid & 0xfff)); 350 } 351 352 /* 353 * Clean out the cache. Called when the last nfsd terminates. 354 */ 355 void 356 nfsrv_cleancache(void) 357 { 358 struct nfsrvcache *rp; 359 360 lwkt_gettoken(&srvcache_token); 361 while ((rp = TAILQ_FIRST(&nfsrvlruhead)) != NULL) { 362 if (rp->rc_flag & RC_LOCKED) { 363 rp->rc_flag |= RC_WANTED; 364 tsleep((caddr_t)rp, 0, "nfsrc", 0); 365 continue; 366 } 367 LIST_REMOVE(rp, rc_hash); 368 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 369 if (rp->rc_flag & RC_REPMBUF) { 370 m_freem(rp->rc_reply); 371 rp->rc_reply = NULL; 372 rp->rc_flag &= ~RC_REPMBUF; 373 } 374 if (rp->rc_flag & RC_NAM) { 375 kfree(rp->rc_nam, M_SONAME); 376 rp->rc_nam = NULL; 377 rp->rc_flag &= ~RC_NAM; 378 } 379 kfree(rp, M_NFSD); 380 } 381 numnfsrvcache = 0; 382 lwkt_reltoken(&srvcache_token); 383 } 384 385 #endif /* NFS_NOSERVER */ 386