[BACK]Return to vfs_cache.c CVS log [TXT][DIR] Up to [local] / sys / kern

Annotation of sys/kern/vfs_cache.c, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: vfs_cache.c,v 1.25 2007/06/21 12:05:14 pedro Exp $    */
        !             2: /*     $NetBSD: vfs_cache.c,v 1.13 1996/02/04 02:18:09 christos Exp $  */
        !             3:
        !             4: /*
        !             5:  * Copyright (c) 1989, 1993
        !             6:  *     The Regents of the University of California.  All rights reserved.
        !             7:  *
        !             8:  * Redistribution and use in source and binary forms, with or without
        !             9:  * modification, are permitted provided that the following conditions
        !            10:  * are met:
        !            11:  * 1. Redistributions of source code must retain the above copyright
        !            12:  *    notice, this list of conditions and the following disclaimer.
        !            13:  * 2. Redistributions in binary form must reproduce the above copyright
        !            14:  *    notice, this list of conditions and the following disclaimer in the
        !            15:  *    documentation and/or other materials provided with the distribution.
        !            16:  * 3. Neither the name of the University nor the names of its contributors
        !            17:  *    may be used to endorse or promote products derived from this software
        !            18:  *    without specific prior written permission.
        !            19:  *
        !            20:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
        !            21:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            22:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            23:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            24:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            25:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            26:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            27:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            28:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            29:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            30:  * SUCH DAMAGE.
        !            31:  *
        !            32:  *     @(#)vfs_cache.c 8.3 (Berkeley) 8/22/94
        !            33:  */
        !            34:
        !            35: #include <sys/param.h>
        !            36: #include <sys/systm.h>
        !            37: #include <sys/time.h>
        !            38: #include <sys/mount.h>
        !            39: #include <sys/vnode.h>
        !            40: #include <sys/namei.h>
        !            41: #include <sys/errno.h>
        !            42: #include <sys/malloc.h>
        !            43: #include <sys/pool.h>
        !            44: #include <sys/hash.h>
        !            45:
        !            46: /*
        !            47:  * TODO: namecache access should really be locked.
        !            48:  */
        !            49:
        !            50: /*
        !            51:  * Name caching works as follows:
        !            52:  *
        !            53:  * Names found by directory scans are retained in a cache
        !            54:  * for future reference.  It is managed LRU, so frequently
        !            55:  * used names will hang around.  Cache is indexed by hash value
        !            56:  * obtained from (vp, name) where vp refers to the directory
        !            57:  * containing name.
        !            58:  *
        !            59:  * For simplicity (and economy of storage), names longer than
        !            60:  * a maximum length of NCHNAMLEN are not cached; they occur
        !            61:  * infrequently in any case, and are almost never of interest.
        !            62:  *
        !            63:  * Upon reaching the last segment of a path, if the reference
        !            64:  * is for DELETE, or NOCACHE is set (rewrite), and the
        !            65:  * name is located in the cache, it will be dropped.
        !            66:  */
        !            67:
        !            68: /*
        !            69:  * Structures associated with name caching.
        !            70:  */
        !            71: LIST_HEAD(nchashhead, namecache) *nchashtbl;
        !            72: u_long nchash;                         /* size of hash table - 1 */
        !            73: long   numcache;                       /* number of cache entries allocated */
        !            74: TAILQ_HEAD(, namecache) nclruhead;             /* LRU chain */
        !            75: struct nchstats nchstats;              /* cache effectiveness statistics */
        !            76:
        !            77: LIST_HEAD(ncvhashhead, namecache) *ncvhashtbl;
        !            78: u_long  ncvhash;                        /* size of hash table - 1 */
        !            79:
        !            80: #define NCHASH(dvp, cnp) \
        !            81:        hash32_buf(&(dvp)->v_id, sizeof((dvp)->v_id), (cnp)->cn_hash) & nchash
        !            82:
        !            83: #define NCVHASH(vp) (vp)->v_id & ncvhash
        !            84:
        !            85: int doingcache = 1;                    /* 1 => enable the cache */
        !            86:
        !            87: struct pool nch_pool;
        !            88:
        !            89: u_long nextvnodeid;
        !            90:
        !            91: /*
        !            92:  * Look for a name in the cache. We don't do this if the segment name is
        !            93:  * long, simply so the cache can avoid holding long names (which would
        !            94:  * either waste space, or add greatly to the complexity).
        !            95:  *
        !            96:  * Lookup is called with ni_dvp pointing to the directory to search,
        !            97:  * ni_ptr pointing to the name of the entry being sought, ni_namelen
        !            98:  * tells the length of the name, and ni_hash contains a hash of
        !            99:  * the name. If the lookup succeeds, the vnode is returned in ni_vp
        !           100:  * and a status of 0 is returned. If the locking fails for whatever
        !           101:  * reason, the vnode is unlocked and the error is returned to caller.
        !           102:  * If the lookup determines that the name does not exist (negative caching),
        !           103:  * a status of ENOENT is returned. If the lookup fails, a status of -1
        !           104:  * is returned.
        !           105:  */
        !           106: int
        !           107: cache_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp)
        !           108: {
        !           109:        struct namecache *ncp;
        !           110:        struct nchashhead *ncpp;
        !           111:        struct vnode *vp;
        !           112:        struct proc *p = curproc;
        !           113:        u_long vpid;
        !           114:        int error;
        !           115:
        !           116:        *vpp = NULL;
        !           117:
        !           118:        if (!doingcache) {
        !           119:                cnp->cn_flags &= ~MAKEENTRY;
        !           120:                return (-1);
        !           121:        }
        !           122:        if (cnp->cn_namelen > NCHNAMLEN) {
        !           123:                nchstats.ncs_long++;
        !           124:                cnp->cn_flags &= ~MAKEENTRY;
        !           125:                return (-1);
        !           126:        }
        !           127:
        !           128:        ncpp = &nchashtbl[NCHASH(dvp, cnp)];
        !           129:        LIST_FOREACH(ncp, ncpp, nc_hash) {
        !           130:                if (ncp->nc_dvp == dvp &&
        !           131:                    ncp->nc_dvpid == dvp->v_id &&
        !           132:                    ncp->nc_nlen == cnp->cn_namelen &&
        !           133:                    !memcmp(ncp->nc_name, cnp->cn_nameptr, (u_int)ncp->nc_nlen))
        !           134:                        break;
        !           135:        }
        !           136:        if (ncp == NULL) {
        !           137:                nchstats.ncs_miss++;
        !           138:                return (-1);
        !           139:        }
        !           140:        if ((cnp->cn_flags & MAKEENTRY) == 0) {
        !           141:                nchstats.ncs_badhits++;
        !           142:                goto remove;
        !           143:        } else if (ncp->nc_vp == NULL) {
        !           144:                if (cnp->cn_nameiop != CREATE ||
        !           145:                    (cnp->cn_flags & ISLASTCN) == 0) {
        !           146:                        nchstats.ncs_neghits++;
        !           147:                        /*
        !           148:                         * Move this slot to end of LRU chain,
        !           149:                         * if not already there.
        !           150:                         */
        !           151:                        if (TAILQ_NEXT(ncp, nc_lru) != NULL) {
        !           152:                                TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
        !           153:                                TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
        !           154:                        }
        !           155:                        return (ENOENT);
        !           156:                } else {
        !           157:                        nchstats.ncs_badhits++;
        !           158:                        goto remove;
        !           159:                }
        !           160:        } else if (ncp->nc_vpid != ncp->nc_vp->v_id) {
        !           161:                nchstats.ncs_falsehits++;
        !           162:                goto remove;
        !           163:        }
        !           164:
        !           165:        vp = ncp->nc_vp;
        !           166:        vpid = vp->v_id;
        !           167:        if (vp == dvp) {        /* lookup on "." */
        !           168:                VREF(dvp);
        !           169:                error = 0;
        !           170:        } else if (cnp->cn_flags & ISDOTDOT) {
        !           171:                VOP_UNLOCK(dvp, 0, p);
        !           172:                cnp->cn_flags |= PDIRUNLOCK;
        !           173:                error = vget(vp, LK_EXCLUSIVE, p);
        !           174:                /*
        !           175:                 * If the above vget() succeeded and both LOCKPARENT and
        !           176:                 * ISLASTCN is set, lock the directory vnode as well.
        !           177:                 */
        !           178:                if (!error && (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) == 0) {
        !           179:                        if ((error = vn_lock(dvp, LK_EXCLUSIVE, p)) != 0) {
        !           180:                                vput(vp);
        !           181:                                return (error);
        !           182:                        }
        !           183:                        cnp->cn_flags &= ~PDIRUNLOCK;
        !           184:                }
        !           185:        } else {
        !           186:                error = vget(vp, LK_EXCLUSIVE, p);
        !           187:                /*
        !           188:                 * If the above vget() failed or either of LOCKPARENT or
        !           189:                 * ISLASTCN is set, unlock the directory vnode.
        !           190:                 */
        !           191:                if (error || (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
        !           192:                        VOP_UNLOCK(dvp, 0, p);
        !           193:                        cnp->cn_flags |= PDIRUNLOCK;
        !           194:                }
        !           195:        }
        !           196:
        !           197:        /*
        !           198:         * Check that the lock succeeded, and that the capability number did
        !           199:         * not change while we were waiting for the lock.
        !           200:         */
        !           201:        if (error || vpid != vp->v_id) {
        !           202:                if (!error) {
        !           203:                        vput(vp);
        !           204:                        nchstats.ncs_falsehits++;
        !           205:                } else
        !           206:                        nchstats.ncs_badhits++;
        !           207:                /*
        !           208:                 * The parent needs to be locked when we return to VOP_LOOKUP().
        !           209:                 * The `.' case here should be extremely rare (if it can happen
        !           210:                 * at all), so we don't bother optimizing out the unlock/relock.
        !           211:                 */
        !           212:                if (vp == dvp || error ||
        !           213:                    (~cnp->cn_flags & (LOCKPARENT|ISLASTCN)) != 0) {
        !           214:                        if ((error = vn_lock(dvp, LK_EXCLUSIVE, p)) != 0)
        !           215:                                return (error);
        !           216:                        cnp->cn_flags &= ~PDIRUNLOCK;
        !           217:                }
        !           218:                return (-1);
        !           219:        }
        !           220:
        !           221:        nchstats.ncs_goodhits++;
        !           222:        /*
        !           223:         * Move this slot to end of LRU chain, if not already there.
        !           224:         */
        !           225:        if (TAILQ_NEXT(ncp, nc_lru) != NULL) {
        !           226:                TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
        !           227:                TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
        !           228:        }
        !           229:        *vpp = vp;
        !           230:        return (0);
        !           231:
        !           232: remove:
        !           233:        /*
        !           234:         * Last component and we are renaming or deleting,
        !           235:         * the cache entry is invalid, or otherwise don't
        !           236:         * want cache entry to exist.
        !           237:         */
        !           238:        TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
        !           239:        LIST_REMOVE(ncp, nc_hash);
        !           240:        ncp->nc_hash.le_prev = NULL;
        !           241:
        !           242:        if (ncp->nc_vhash.le_prev != NULL) {
        !           243:                LIST_REMOVE(ncp, nc_vhash);
        !           244:                ncp->nc_vhash.le_prev = NULL;
        !           245:        }
        !           246:
        !           247:        pool_put(&nch_pool, ncp);
        !           248:        numcache--;
        !           249:        return (-1);
        !           250: }
        !           251:
        !           252: /*
        !           253:  * Scan cache looking for name of directory entry pointing at vp.
        !           254:  *
        !           255:  * Fill in dvpp.
        !           256:  *
        !           257:  * If bufp is non-NULL, also place the name in the buffer which starts
        !           258:  * at bufp, immediately before *bpp, and move bpp backwards to point
        !           259:  * at the start of it.  (Yes, this is a little baroque, but it's done
        !           260:  * this way to cater to the whims of getcwd).
        !           261:  *
        !           262:  * Returns 0 on success, -1 on cache miss, positive errno on failure.
        !           263:  *
        !           264:  * TODO: should we return *dvpp locked?
        !           265:  */
        !           266:
        !           267: int
        !           268: cache_revlookup(struct vnode *vp, struct vnode **dvpp, char **bpp, char *bufp)
        !           269: {
        !           270:        struct namecache *ncp;
        !           271:        struct vnode *dvp;
        !           272:        struct ncvhashhead *nvcpp;
        !           273:        char *bp;
        !           274:
        !           275:        if (!doingcache)
        !           276:                goto out;
        !           277:
        !           278:        nvcpp = &ncvhashtbl[NCVHASH(vp)];
        !           279:
        !           280:        LIST_FOREACH(ncp, nvcpp, nc_vhash) {
        !           281:                if (ncp->nc_vp == vp &&
        !           282:                    ncp->nc_vpid == vp->v_id &&
        !           283:                    (dvp = ncp->nc_dvp) != NULL &&
        !           284:                    /* avoid pesky '.' entries.. */
        !           285:                    dvp != vp && ncp->nc_dvpid == dvp->v_id) {
        !           286:
        !           287: #ifdef DIAGNOSTIC
        !           288:                        if (ncp->nc_nlen == 1 &&
        !           289:                            ncp->nc_name[0] == '.')
        !           290:                                panic("cache_revlookup: found entry for .");
        !           291:
        !           292:                        if (ncp->nc_nlen == 2 &&
        !           293:                            ncp->nc_name[0] == '.' &&
        !           294:                            ncp->nc_name[1] == '.')
        !           295:                                panic("cache_revlookup: found entry for ..");
        !           296: #endif
        !           297:                        nchstats.ncs_revhits++;
        !           298:
        !           299:                        if (bufp != NULL) {
        !           300:                                bp = *bpp;
        !           301:                                bp -= ncp->nc_nlen;
        !           302:                                if (bp <= bufp) {
        !           303:                                        *dvpp = NULL;
        !           304:                                        return (ERANGE);
        !           305:                                }
        !           306:                                memcpy(bp, ncp->nc_name, ncp->nc_nlen);
        !           307:                                *bpp = bp;
        !           308:                        }
        !           309:
        !           310:                        *dvpp = dvp;
        !           311:
        !           312:                        /*
        !           313:                         * XXX: Should we vget() here to have more
        !           314:                         * consistent semantics with cache_lookup()?
        !           315:                         *
        !           316:                         * For MP safety it might be necessary to do
        !           317:                         * this here, while also protecting hash
        !           318:                         * tables themselves to provide some sort of
        !           319:                         * sane inter locking.
        !           320:                         */
        !           321:                        return (0);
        !           322:                }
        !           323:        }
        !           324:        nchstats.ncs_revmiss++;
        !           325:
        !           326:  out:
        !           327:        *dvpp = NULL;
        !           328:        return (-1);
        !           329: }
        !           330:
        !           331: /*
        !           332:  * Add an entry to the cache
        !           333:  */
        !           334: void
        !           335: cache_enter(struct vnode *dvp, struct vnode *vp, struct componentname *cnp)
        !           336: {
        !           337:        struct namecache *ncp;
        !           338:        struct nchashhead *ncpp;
        !           339:        struct ncvhashhead *nvcpp;
        !           340:
        !           341:        if (!doingcache || cnp->cn_namelen > NCHNAMLEN)
        !           342:                return;
        !           343:
        !           344:        /*
        !           345:         * Free the cache slot at head of lru chain.
        !           346:         */
        !           347:        if (numcache < desiredvnodes) {
        !           348:                ncp = pool_get(&nch_pool, PR_WAITOK);
        !           349:                bzero((char *)ncp, sizeof *ncp);
        !           350:                numcache++;
        !           351:        } else if ((ncp = TAILQ_FIRST(&nclruhead)) != NULL) {
        !           352:                TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
        !           353:                if (ncp->nc_hash.le_prev != NULL) {
        !           354:                        LIST_REMOVE(ncp, nc_hash);
        !           355:                        ncp->nc_hash.le_prev = NULL;
        !           356:                }
        !           357:                if (ncp->nc_vhash.le_prev != NULL) {
        !           358:                        LIST_REMOVE(ncp, nc_vhash);
        !           359:                        ncp->nc_vhash.le_prev = NULL;
        !           360:                }
        !           361:        } else
        !           362:                return;
        !           363:        /* grab the vnode we just found */
        !           364:        ncp->nc_vp = vp;
        !           365:        if (vp)
        !           366:                ncp->nc_vpid = vp->v_id;
        !           367:        /* fill in cache info */
        !           368:        ncp->nc_dvp = dvp;
        !           369:        ncp->nc_dvpid = dvp->v_id;
        !           370:        ncp->nc_nlen = cnp->cn_namelen;
        !           371:        bcopy(cnp->cn_nameptr, ncp->nc_name, (unsigned)ncp->nc_nlen);
        !           372:        TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
        !           373:        ncpp = &nchashtbl[NCHASH(dvp, cnp)];
        !           374:        LIST_INSERT_HEAD(ncpp, ncp, nc_hash);
        !           375:
        !           376:        /*
        !           377:         * Create reverse-cache entries (used in getcwd) for
        !           378:         * directories.
        !           379:         */
        !           380:
        !           381:        ncp->nc_vhash.le_prev = NULL;
        !           382:        ncp->nc_vhash.le_next = NULL;
        !           383:
        !           384:        if (vp && vp != dvp && vp->v_type == VDIR &&
        !           385:            (ncp->nc_nlen > 2 ||
        !           386:                (ncp->nc_nlen > 1 && ncp->nc_name[1] != '.') ||
        !           387:                (ncp->nc_nlen > 0 && ncp->nc_name[0] != '.'))) {
        !           388:                nvcpp = &ncvhashtbl[NCVHASH(vp)];
        !           389:                LIST_INSERT_HEAD(nvcpp, ncp, nc_vhash);
        !           390:        }
        !           391: }
        !           392:
        !           393: /*
        !           394:  * Name cache initialization, from vfs_init() when we are booting
        !           395:  */
        !           396: void
        !           397: nchinit(void)
        !           398: {
        !           399:
        !           400:        TAILQ_INIT(&nclruhead);
        !           401:        nchashtbl = hashinit(desiredvnodes, M_CACHE, M_WAITOK, &nchash);
        !           402:        ncvhashtbl = hashinit(desiredvnodes/8, M_CACHE, M_WAITOK, &ncvhash);
        !           403:        pool_init(&nch_pool, sizeof(struct namecache), 0, 0, 0, "nchpl",
        !           404:            &pool_allocator_nointr);
        !           405: }
        !           406:
        !           407: /*
        !           408:  * Cache flush, a particular vnode; called when a vnode is renamed to
        !           409:  * hide entries that would now be invalid
        !           410:  */
        !           411: void
        !           412: cache_purge(struct vnode *vp)
        !           413: {
        !           414:        struct namecache *ncp;
        !           415:        struct nchashhead *ncpp;
        !           416:
        !           417:        vp->v_id = ++nextvnodeid;
        !           418:        if (nextvnodeid != 0)
        !           419:                return;
        !           420:        for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) {
        !           421:                LIST_FOREACH(ncp, ncpp, nc_hash) {
        !           422:                        ncp->nc_vpid = 0;
        !           423:                        ncp->nc_dvpid = 0;
        !           424:                }
        !           425:        }
        !           426:        vp->v_id = ++nextvnodeid;
        !           427: }
        !           428:
        !           429: /*
        !           430:  * Cache flush, a whole filesystem; called when filesys is umounted to
        !           431:  * remove entries that would now be invalid
        !           432:  */
        !           433: void
        !           434: cache_purgevfs(struct mount *mp)
        !           435: {
        !           436:        struct namecache *ncp, *nxtcp;
        !           437:
        !           438:        for (ncp = TAILQ_FIRST(&nclruhead); ncp != TAILQ_END(&nclruhead);
        !           439:            ncp = nxtcp) {
        !           440:                nxtcp = TAILQ_NEXT(ncp, nc_lru);
        !           441:                if (ncp->nc_dvp == NULL || ncp->nc_dvp->v_mount != mp)
        !           442:                        continue;
        !           443:                /* free the resources we had */
        !           444:                ncp->nc_vp = NULL;
        !           445:                ncp->nc_dvp = NULL;
        !           446:                TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
        !           447:                if (ncp->nc_hash.le_prev != NULL) {
        !           448:                        LIST_REMOVE(ncp, nc_hash);
        !           449:                        ncp->nc_hash.le_prev = NULL;
        !           450:                }
        !           451:                if (ncp->nc_vhash.le_prev != NULL) {
        !           452:                        LIST_REMOVE(ncp, nc_vhash);
        !           453:                        ncp->nc_vhash.le_prev = NULL;
        !           454:                }
        !           455:                pool_put(&nch_pool, ncp);
        !           456:                numcache--;
        !           457:        }
        !           458: }

CVSweb