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