[BACK]Return to nfs_srvcache.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / nfs

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>