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