[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

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