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