Annotation of XNU/bsd/vfs/vfs_cache.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
                      3:  *
                      4:  * @APPLE_LICENSE_HEADER_START@
                      5:  * 
                      6:  * The contents of this file constitute Original Code as defined in and
                      7:  * are subject to the Apple Public Source License Version 1.1 (the
                      8:  * "License").  You may not use this file except in compliance with the
                      9:  * License.  Please obtain a copy of the License at
                     10:  * http://www.apple.com/publicsource and read it before using this file.
                     11:  * 
                     12:  * This Original Code and all software distributed under the License are
                     13:  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
                     14:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
                     15:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
                     16:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
                     17:  * License for the specific language governing rights and limitations
                     18:  * under the License.
                     19:  * 
                     20:  * @APPLE_LICENSE_HEADER_END@
                     21:  */
                     22: /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
                     23: /*
                     24:  * Copyright (c) 1989, 1993, 1995
                     25:  *     The Regents of the University of California.  All rights reserved.
                     26:  *
                     27:  * This code is derived from software contributed to Berkeley by
                     28:  * Poul-Henning Kamp of the FreeBSD Project.
                     29:  *
                     30:  * Redistribution and use in source and binary forms, with or without
                     31:  * modification, are permitted provided that the following conditions
                     32:  * are met:
                     33:  * 1. Redistributions of source code must retain the above copyright
                     34:  *    notice, this list of conditions and the following disclaimer.
                     35:  * 2. Redistributions in binary form must reproduce the above copyright
                     36:  *    notice, this list of conditions and the following disclaimer in the
                     37:  *    documentation and/or other materials provided with the distribution.
                     38:  * 3. All advertising materials mentioning features or use of this software
                     39:  *    must display the following acknowledgement:
                     40:  *     This product includes software developed by the University of
                     41:  *     California, Berkeley and its contributors.
                     42:  * 4. Neither the name of the University nor the names of its contributors
                     43:  *    may be used to endorse or promote products derived from this software
                     44:  *    without specific prior written permission.
                     45:  *
                     46:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     47:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     48:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     49:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     50:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     51:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     52:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     53:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     54:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     55:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     56:  * SUCH DAMAGE.
                     57:  *
                     58:  *
                     59:  *     @(#)vfs_cache.c 8.5 (Berkeley) 3/22/95
                     60:  */
                     61: #include <mach_nbc.h>
                     62: #include <sys/param.h>
                     63: #include <sys/systm.h>
                     64: #include <sys/time.h>
                     65: #include <sys/mount.h>
                     66: #include <sys/vnode.h>
                     67: #include <sys/namei.h>
                     68: #include <sys/errno.h>
                     69: #include <sys/malloc.h>
                     70: #include <kern/mapfs.h>
                     71: /*
                     72:  * Name caching works as follows:
                     73:  *
                     74:  * Names found by directory scans are retained in a cache
                     75:  * for future reference.  It is managed LRU, so frequently
                     76:  * used names will hang around.  Cache is indexed by hash value
                     77:  * obtained from (vp, name) where vp refers to the directory
                     78:  * containing name.
                     79:  *
                     80:  * If it is a "negative" entry, (i.e. for a name that is known NOT to
                     81:  * exist) the vnode pointer will be NULL.
                     82:  *
                     83:  * For simplicity (and economy of storage), names longer than
                     84:  * a maximum length of NCHNAMLEN are not cached; they occur
                     85:  * infrequently in any case, and are almost never of interest.
                     86:  *
                     87:  * Upon reaching the last segment of a path, if the reference
                     88:  * is for DELETE, or NOCACHE is set (rewrite), and the
                     89:  * name is located in the cache, it will be dropped.
                     90:  */
                     91: 
                     92: /*
                     93:  * Structures associated with name cacheing.
                     94:  */
                     95: #define NCHHASH(dvp, cnp) \
                     96:        (&nchashtbl[((dvp)->v_id + (cnp)->cn_hash) & nchash])
                     97: LIST_HEAD(nchashhead, namecache) *nchashtbl;   /* Hash Table */
                     98: u_long nchash;                         /* size of hash table - 1 */
                     99: long   numcache;                       /* number of cache entries allocated */
                    100: TAILQ_HEAD(, namecache) nclruhead;     /* LRU chain */
                    101: struct nchstats nchstats;              /* cache effectiveness statistics */
                    102: u_long nextvnodeid = 0;
                    103: int doingcache = 1;                    /* 1 => enable the cache */
                    104: 
                    105: /*
                    106:  * Delete an entry from its hash list and move it to the front
                    107:  * of the LRU list for immediate reuse.
                    108:  */
                    109: #if DIAGNOSTIC
                    110: #define PURGE(ncp)  {                                          \
                    111:        if (ncp->nc_hash.le_prev == 0)                          \
                    112:                panic("namecache purge le_prev");               \
                    113:        if (ncp->nc_hash.le_next == ncp)                        \
                    114:                panic("namecache purge le_next");               \
                    115:        LIST_REMOVE(ncp, nc_hash);                              \
                    116:        ncp->nc_hash.le_prev = 0;                               \
                    117:        TAILQ_REMOVE(&nclruhead, ncp, nc_lru);                  \
                    118:        TAILQ_INSERT_HEAD(&nclruhead, ncp, nc_lru);             \
                    119: }
                    120: #else
                    121: #define PURGE(ncp)  {                                          \
                    122:        LIST_REMOVE(ncp, nc_hash);                              \
                    123:        ncp->nc_hash.le_prev = 0;                               \
                    124:        TAILQ_REMOVE(&nclruhead, ncp, nc_lru);                  \
                    125:        TAILQ_INSERT_HEAD(&nclruhead, ncp, nc_lru);             \
                    126: }
                    127: #endif /* DIAGNOSTIC */
                    128: 
                    129: /*
                    130:  * Move an entry that has been used to the tail of the LRU list
                    131:  * so that it will be preserved for future use.
                    132:  */
                    133: #define TOUCH(ncp)  {                                          \
                    134:        if (ncp->nc_lru.tqe_next != 0) {                        \
                    135:                TAILQ_REMOVE(&nclruhead, ncp, nc_lru);          \
                    136:                TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);     \
                    137:        }                                                       \
                    138: }
                    139: 
                    140: /*
                    141:  * Lookup an entry in the cache 
                    142:  *
                    143:  * We don't do this if the segment name is long, simply so the cache 
                    144:  * can avoid holding long names (which would either waste space, or
                    145:  * add greatly to the complexity).
                    146:  *
                    147:  * Lookup is called with dvp pointing to the directory to search,
                    148:  * cnp pointing to the name of the entry being sought. If the lookup
                    149:  * succeeds, the vnode is returned in *vpp, and a status of -1 is
                    150:  * returned. If the lookup determines that the name does not exist
                    151:  * (negative cacheing), a status of ENOENT is returned. If the lookup
                    152:  * fails, a status of zero is returned.
                    153:  */
                    154: 
                    155: int
                    156: cache_lookup(dvp, vpp, cnp)
                    157:        struct vnode *dvp;
                    158:        struct vnode **vpp;
                    159:        struct componentname *cnp;
                    160: {
                    161:        register struct namecache *ncp, *nnp;
                    162:        register struct nchashhead *ncpp;
                    163: 
                    164:        if (!doingcache) {
                    165:                cnp->cn_flags &= ~MAKEENTRY;
                    166:                return (0);
                    167:        }
                    168:        if (cnp->cn_namelen > NCHNAMLEN) {
                    169:                nchstats.ncs_long++;
                    170:                cnp->cn_flags &= ~MAKEENTRY;
                    171:                return (0);
                    172:        }
                    173: 
                    174:        ncpp = NCHHASH(dvp, cnp);
                    175:        for (ncp = ncpp->lh_first; ncp != 0; ncp = nnp) {
                    176:                nnp = ncp->nc_hash.le_next;
                    177:                /* If one of the vp's went stale, don't bother anymore. */
                    178:                if ((ncp->nc_dvpid != ncp->nc_dvp->v_id) ||
                    179:                    (ncp->nc_vp && ncp->nc_vpid != ncp->nc_vp->v_id)) {
                    180:                        nchstats.ncs_falsehits++;
                    181:                        PURGE(ncp);
                    182:                        continue;
                    183:                }
                    184:                /* Now that we know the vp's to be valid, is it ours ? */
                    185:                if (ncp->nc_dvp == dvp &&
                    186:                    ncp->nc_nlen == cnp->cn_namelen &&
                    187:                    !bcmp(ncp->nc_name, cnp->cn_nameptr, (u_int)ncp->nc_nlen))
                    188:                        break;
                    189:        }
                    190: 
                    191:        /* We failed to find an entry */
                    192:        if (ncp == 0) {
                    193:                nchstats.ncs_miss++;
                    194:                return (0);
                    195:        }
                    196: 
                    197:        /* We don't want to have an entry, so dump it */
                    198:        if ((cnp->cn_flags & MAKEENTRY) == 0) {
                    199:                nchstats.ncs_badhits++;
                    200:                PURGE(ncp);
                    201:                return (0);
                    202:        } 
                    203: 
                    204:        /* We found a "positive" match, return the vnode */
                    205:         if (ncp->nc_vp) {
                    206:                nchstats.ncs_goodhits++;
                    207:                TOUCH(ncp);
                    208:                *vpp = ncp->nc_vp;
                    209:                return (-1);
                    210:        }
                    211: 
                    212:        /* We found a negative match, and want to create it, so purge */
                    213:        if (cnp->cn_nameiop == CREATE) {
                    214:                nchstats.ncs_badhits++;
                    215:                PURGE(ncp);
                    216:                return (0);
                    217:        }
                    218: 
                    219:        /*
                    220:         * We found a "negative" match, ENOENT notifies client of this match.
                    221:         * The nc_vpid field records whether this is a whiteout.
                    222:         */
                    223:        nchstats.ncs_neghits++;
                    224:        TOUCH(ncp);
                    225:        cnp->cn_flags |= ncp->nc_vpid;
                    226:        return (ENOENT);
                    227: }
                    228: 
                    229: /*
                    230:  * Add an entry to the cache.
                    231:  */
                    232: void
                    233: cache_enter(dvp, vp, cnp)
                    234:        struct vnode *dvp;
                    235:        struct vnode *vp;
                    236:        struct componentname *cnp;
                    237: {
                    238:        register struct namecache *ncp;
                    239:        register struct nchashhead *ncpp;
                    240: 
                    241:        if (!doingcache)
                    242:                return;
                    243: 
                    244:        /*
                    245:         * If an entry that is too long, is entered, bad things happen.
                    246:         * cache_lookup acts as the sentinel to make sure longer names
                    247:         * are not stored. This here will prevent outsiders from doing
                    248:         * something that is unexpected.
                    249:         */
                    250:        if (cnp->cn_namelen > NCHNAMLEN)
                    251:                panic("cache_enter: name too long");
                    252: 
                    253:        /*
                    254:         * We allocate a new entry if we are less than the maximum
                    255:         * allowed and the one at the front of the LRU list is in use.
                    256:         * Otherwise we use the one at the front of the LRU list.
                    257:         */
                    258:        if (numcache < desiredvnodes &&
                    259:            ((ncp = nclruhead.tqh_first) == NULL ||
                    260:            ncp->nc_hash.le_prev != 0)) {
                    261:                /* Add one more entry */
                    262:                ncp = (struct namecache *)
                    263:                        _MALLOC_ZONE((u_long)sizeof *ncp, M_CACHE, M_WAITOK);
                    264:                numcache++;
                    265:        } else if (ncp = nclruhead.tqh_first) {
                    266:                /* reuse an old entry */
                    267:                TAILQ_REMOVE(&nclruhead, ncp, nc_lru);
                    268:                if (ncp->nc_hash.le_prev != 0) {
                    269: #if DIAGNOSTIC
                    270:                        if (ncp->nc_hash.le_next == ncp)
                    271:                                panic("cache_enter: le_next");
                    272: #endif
                    273:                        LIST_REMOVE(ncp, nc_hash);
                    274:                        ncp->nc_hash.le_prev = 0;
                    275:                }
                    276:        } else {
                    277:                /* give up */
                    278:                return;
                    279:        }
                    280: 
                    281:        /*
                    282:         * Fill in cache info, if vp is NULL this is a "negative" cache entry.
                    283:         * For negative entries, we have to record whether it is a whiteout.
                    284:         * the whiteout flag is stored in the nc_vpid field which is
                    285:         * otherwise unused.
                    286:         */
                    287:        ncp->nc_vp = vp;
                    288:        if (vp)
                    289:                ncp->nc_vpid = vp->v_id;
                    290:        else
                    291:                ncp->nc_vpid = cnp->cn_flags & ISWHITEOUT;
                    292:        ncp->nc_dvp = dvp;
                    293:        ncp->nc_dvpid = dvp->v_id;
                    294:        ncp->nc_nlen = cnp->cn_namelen;
                    295:        bcopy(cnp->cn_nameptr, ncp->nc_name, (unsigned)ncp->nc_nlen);
                    296:        TAILQ_INSERT_TAIL(&nclruhead, ncp, nc_lru);
                    297:        ncpp = NCHHASH(dvp, cnp);
                    298: #if DIAGNOSTIC
                    299:        {
                    300:                register struct namecache *p;
                    301: 
                    302:                for (p = ncpp->lh_first; p != 0; p = p->nc_hash.le_next)
                    303:                        if (p == ncp)
                    304:                                panic("cache_enter: duplicate");
                    305:        }
                    306: #endif
                    307:        LIST_INSERT_HEAD(ncpp, ncp, nc_hash);
                    308: }
                    309: 
                    310: /*
                    311:  * Name cache initialization, from vfs_init() when we are booting
                    312:  */
                    313: void
                    314: nchinit()
                    315: {
                    316: 
                    317:        TAILQ_INIT(&nclruhead);
                    318:        nchashtbl = hashinit(desiredvnodes, M_CACHE, &nchash);
                    319: }
                    320: 
                    321: /*
                    322:  * Invalidate a all entries to particular vnode.
                    323:  * 
                    324:  * We actually just increment the v_id, that will do it. The entries will
                    325:  * be purged by lookup as they get found. If the v_id wraps around, we
                    326:  * need to ditch the entire cache, to avoid confusion. No valid vnode will
                    327:  * ever have (v_id == 0).
                    328:  */
                    329: void
                    330: cache_purge(vp)
                    331:        struct vnode *vp;
                    332: {
                    333:        struct namecache *ncp;
                    334:        struct nchashhead *ncpp;
                    335: 
                    336:        vp->v_id = ++nextvnodeid;
                    337:        if (nextvnodeid != 0)
                    338:                return;
                    339:        for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) {
                    340:                while (ncp = ncpp->lh_first)
                    341:                        PURGE(ncp);
                    342:        }
                    343:        vp->v_id = ++nextvnodeid;
                    344: #if MACH_NBC
                    345:        /* ?? */
                    346: /*     mapfs_uncache(vp); */
                    347: #endif /* MACH_NBC */
                    348: }
                    349: 
                    350: /*
                    351:  * Flush all entries referencing a particular filesystem.
                    352:  *
                    353:  * Since we need to check it anyway, we will flush all the invalid
                    354:  * entriess at the same time.
                    355:  */
                    356: void
                    357: cache_purgevfs(mp)
                    358:        struct mount *mp;
                    359: {
                    360:        struct nchashhead *ncpp;
                    361:        struct namecache *ncp, *nnp;
                    362: 
                    363:        /* Scan hash tables for applicable entries */
                    364:        for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) {
                    365:                for (ncp = ncpp->lh_first; ncp != 0; ncp = nnp) {
                    366:                        nnp = ncp->nc_hash.le_next;
                    367:                        if (ncp->nc_dvpid != ncp->nc_dvp->v_id ||
                    368:                            (ncp->nc_vp && ncp->nc_vpid != ncp->nc_vp->v_id) ||
                    369:                            ncp->nc_dvp->v_mount == mp) {
                    370:                                PURGE(ncp);
                    371:                        }
                    372:                }
                    373:        }
                    374: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.