1 /* $NetBSD: nfs_srvcache.c,v 1.31 2004/05/21 13:53:40 yamt 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.31 2004/05/21 13:53:40 yamt 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, /* NULL */ 96 FALSE, /* GETATTR */ 97 TRUE, /* SETATTR */ 98 FALSE, /* LOOKUP */ 99 FALSE, /* ACCESS */ 100 FALSE, /* READLINK */ 101 FALSE, /* READ */ 102 TRUE, /* WRITE */ 103 TRUE, /* CREATE */ 104 TRUE, /* MKDIR */ 105 TRUE, /* SYMLINK */ 106 TRUE, /* MKNOD */ 107 TRUE, /* REMOVE */ 108 TRUE, /* RMDIR */ 109 TRUE, /* RENAME */ 110 TRUE, /* LINK */ 111 FALSE, /* READDIR */ 112 FALSE, /* READDIRPLUS */ 113 FALSE, /* FSSTAT */ 114 FALSE, /* FSINFO */ 115 FALSE, /* PATHCONF */ 116 FALSE, /* COMMIT */ 117 FALSE, /* GETLEASE */ 118 FALSE, /* VACATED */ 119 FALSE, /* EVICTED */ 120 FALSE, /* NOOP */ 121 }; 122 123 /* True iff the rpc reply is an nfs status ONLY! */ 124 static const int nfsv2_repstat[NFS_NPROCS] = { 125 FALSE, /* NULL */ 126 FALSE, /* GETATTR */ 127 FALSE, /* SETATTR */ 128 FALSE, /* NOOP */ 129 FALSE, /* LOOKUP */ 130 FALSE, /* READLINK */ 131 FALSE, /* READ */ 132 FALSE, /* Obsolete WRITECACHE */ 133 FALSE, /* WRITE */ 134 FALSE, /* CREATE */ 135 TRUE, /* REMOVE */ 136 TRUE, /* RENAME */ 137 TRUE, /* LINK */ 138 TRUE, /* SYMLINK */ 139 FALSE, /* MKDIR */ 140 TRUE, /* RMDIR */ 141 FALSE, /* READDIR */ 142 FALSE, /* STATFS */ 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, *rpdup; 227 struct mbuf *mb; 228 struct sockaddr_in *saddr; 229 caddr_t bpos; 230 int ret; 231 232 simple_lock(&nfsrv_reqcache_lock); 233 rp = nfsrv_lookupcache(nd); 234 if (rp) { 235 simple_unlock(&nfsrv_reqcache_lock); 236 found: 237 /* If not at end of LRU chain, move it there */ 238 if (TAILQ_NEXT(rp, rc_lru)) { /* racy but ok */ 239 simple_lock(&nfsrv_reqcache_lock); 240 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 241 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 242 simple_unlock(&nfsrv_reqcache_lock); 243 } 244 if (rp->rc_state == RC_UNUSED) 245 panic("nfsrv cache"); 246 if (rp->rc_state == RC_INPROG) { 247 nfsstats.srvcache_inproghits++; 248 ret = RC_DROPIT; 249 } else if (rp->rc_flag & RC_REPSTATUS) { 250 nfsstats.srvcache_nonidemdonehits++; 251 nfs_rephead(0, nd, slp, rp->rc_status, 252 0, (u_quad_t *)0, repp, &mb, &bpos); 253 ret = RC_REPLY; 254 } else if (rp->rc_flag & RC_REPMBUF) { 255 nfsstats.srvcache_nonidemdonehits++; 256 *repp = m_copym(rp->rc_reply, 0, M_COPYALL, 257 M_WAIT); 258 ret = RC_REPLY; 259 } else { 260 nfsstats.srvcache_idemdonehits++; 261 rp->rc_state = RC_INPROG; 262 ret = RC_DOIT; 263 } 264 simple_lock(&nfsrv_reqcache_lock); 265 nfsrv_unlockcache(rp); 266 simple_unlock(&nfsrv_reqcache_lock); 267 return ret; 268 } 269 nfsstats.srvcache_misses++; 270 if (numnfsrvcache < desirednfsrvcache) { 271 numnfsrvcache++; 272 simple_unlock(&nfsrv_reqcache_lock); 273 rp = pool_get(&nfs_reqcache_pool, PR_WAITOK); 274 memset(rp, 0, sizeof *rp); 275 rp->rc_flag = RC_LOCKED; 276 } else { 277 rp = TAILQ_FIRST(&nfsrvlruhead); 278 while ((rp->rc_flag & RC_LOCKED) != 0) { 279 rp->rc_flag |= RC_WANTED; 280 (void) ltsleep(rp, PZERO-1, "nfsrc", 0, 281 &nfsrv_reqcache_lock); 282 rp = TAILQ_FIRST(&nfsrvlruhead); 283 } 284 rp->rc_flag |= RC_LOCKED; 285 LIST_REMOVE(rp, rc_hash); 286 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 287 simple_unlock(&nfsrv_reqcache_lock); 288 if (rp->rc_flag & RC_REPMBUF) 289 m_freem(rp->rc_reply); 290 if (rp->rc_flag & RC_NAM) 291 (void) m_free(rp->rc_nam); 292 rp->rc_flag &= (RC_LOCKED | RC_WANTED); 293 } 294 rp->rc_state = RC_INPROG; 295 rp->rc_xid = nd->nd_retxid; 296 saddr = mtod(nd->nd_nam, struct sockaddr_in *); 297 switch (saddr->sin_family) { 298 case AF_INET: 299 rp->rc_flag |= RC_INETADDR; 300 rp->rc_inetaddr = saddr->sin_addr.s_addr; 301 break; 302 case AF_ISO: 303 default: 304 rp->rc_flag |= RC_NAM; 305 rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT); 306 break; 307 }; 308 rp->rc_proc = nd->nd_procnum; 309 simple_lock(&nfsrv_reqcache_lock); 310 rpdup = nfsrv_lookupcache(nd); 311 if (rpdup != NULL) { 312 /* 313 * other thread made duplicate cache entry. 314 */ 315 simple_unlock(&nfsrv_reqcache_lock); 316 pool_put(&nfs_reqcache_pool, rp); 317 rp = rpdup; 318 goto found; 319 } 320 TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); 321 LIST_INSERT_HEAD(NFSRCHASH(nd->nd_retxid), rp, rc_hash); 322 nfsrv_unlockcache(rp); 323 simple_unlock(&nfsrv_reqcache_lock); 324 return RC_DOIT; 325 } 326 327 /* 328 * Update a request cache entry after the rpc has been done 329 */ 330 void 331 nfsrv_updatecache(nd, repvalid, repmbuf) 332 struct nfsrv_descript *nd; 333 int repvalid; 334 struct mbuf *repmbuf; 335 { 336 struct nfsrvcache *rp; 337 338 if (!nd->nd_nam2) 339 return; 340 simple_lock(&nfsrv_reqcache_lock); 341 rp = nfsrv_lookupcache(nd); 342 simple_unlock(&nfsrv_reqcache_lock); 343 if (rp) { 344 rp->rc_state = RC_DONE; 345 /* 346 * If we have a valid reply update status and save 347 * the reply for non-idempotent rpc's. 348 */ 349 if (repvalid && nonidempotent[nd->nd_procnum]) { 350 if ((nd->nd_flag & ND_NFSV3) == 0 && 351 nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) { 352 rp->rc_status = nd->nd_repstat; 353 rp->rc_flag |= RC_REPSTATUS; 354 } else { 355 rp->rc_reply = m_copym(repmbuf, 356 0, M_COPYALL, M_WAIT); 357 rp->rc_flag |= RC_REPMBUF; 358 } 359 } 360 simple_lock(&nfsrv_reqcache_lock); 361 nfsrv_unlockcache(rp); 362 simple_unlock(&nfsrv_reqcache_lock); 363 } 364 } 365 366 /* 367 * Clean out the cache. Called when the last nfsd terminates. 368 */ 369 void 370 nfsrv_cleancache() 371 { 372 struct nfsrvcache *rp, *nextrp; 373 374 simple_lock(&nfsrv_reqcache_lock); 375 for (rp = TAILQ_FIRST(&nfsrvlruhead); rp != 0; rp = nextrp) { 376 nextrp = TAILQ_NEXT(rp, rc_lru); 377 LIST_REMOVE(rp, rc_hash); 378 TAILQ_REMOVE(&nfsrvlruhead, rp, rc_lru); 379 pool_put(&nfs_reqcache_pool, rp); 380 } 381 numnfsrvcache = 0; 382 simple_unlock(&nfsrv_reqcache_lock); 383 } 384