X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/378393581903b274cb7a4d18e0d978071a6b592d..5d5c5d0d5b79ade9a973d55186ffda2638ba2b6e:/bsd/vfs/vfs_cache.c diff --git a/bsd/vfs/vfs_cache.c b/bsd/vfs/vfs_cache.c index 8cb282de6..e568900a5 100644 --- a/bsd/vfs/vfs_cache.c +++ b/bsd/vfs/vfs_cache.c @@ -1,23 +1,31 @@ /* * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_LICENSE_OSREFERENCE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the + * License may not be used to create, or enable the creation or + * redistribution of, unlawful or unlicensed copies of an Apple operating + * system, or to circumvent, violate, or enable the circumvention or + * violation of, any terms of an Apple operating system software license + * agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_OSREFERENCE_HEADER_END@ */ /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ /* @@ -96,15 +104,37 @@ u_long nchash; /* size of hash table - 1 */ long numcache; /* number of cache entries allocated */ int desiredNodes; int desiredNegNodes; +int ncs_negtotal; TAILQ_HEAD(, namecache) nchead; /* chain of all name cache entries */ TAILQ_HEAD(, namecache) neghead; /* chain of only negative cache entries */ + + +#if COLLECT_STATS + struct nchstats nchstats; /* cache effectiveness statistics */ +#define NCHSTAT(v) { \ + nchstats.v++; \ +} +#define NAME_CACHE_LOCK() name_cache_lock() +#define NAME_CACHE_UNLOCK() name_cache_unlock() +#define NAME_CACHE_LOCK_SHARED() name_cache_lock() + +#else + +#define NCHSTAT(v) +#define NAME_CACHE_LOCK() name_cache_lock() +#define NAME_CACHE_UNLOCK() name_cache_unlock() +#define NAME_CACHE_LOCK_SHARED() name_cache_lock_shared() + +#endif + + /* vars for name cache list lock */ lck_grp_t * namecache_lck_grp; lck_grp_attr_t * namecache_lck_grp_attr; lck_attr_t * namecache_lck_attr; -lck_mtx_t * namecache_mtx_lock; +lck_rw_t * namecache_rw_lock; static vnode_t cache_lookup_locked(vnode_t dvp, struct componentname *cnp); static int remove_name_locked(const char *); @@ -157,7 +187,7 @@ build_path(vnode_t first_vp, char *buff, int buflen, int *outlen) vp = vp->v_mount->mnt_vnodecovered; } } - name_cache_lock(); + NAME_CACHE_LOCK_SHARED(); while (vp && vp->v_parent != vp) { /* @@ -218,7 +248,7 @@ build_path(vnode_t first_vp, char *buff, int buflen, int *outlen) vp = vp->v_mount->mnt_vnodecovered; } } - name_cache_unlock(); + NAME_CACHE_UNLOCK(); out: /* * slide it down to the beginning of the buffer @@ -242,7 +272,7 @@ vnode_getparent(vnode_t vp) vnode_t pvp = NULLVP; int pvid; - name_cache_lock(); + NAME_CACHE_LOCK_SHARED(); /* * v_parent is stable behind the name_cache lock * however, the only thing we can really guarantee @@ -253,13 +283,12 @@ vnode_getparent(vnode_t vp) if ( (pvp = vp->v_parent) != NULLVP ) { pvid = pvp->v_id; - name_cache_unlock(); + NAME_CACHE_UNLOCK(); if (vnode_getwithvid(pvp, pvid) != 0) pvp = NULL; } else - name_cache_unlock(); - + NAME_CACHE_UNLOCK(); return (pvp); } @@ -268,11 +297,11 @@ vnode_getname(vnode_t vp) { char *name = NULL; - name_cache_lock(); + NAME_CACHE_LOCK(); if (vp->v_name) name = add_name_locked(vp->v_name, strlen(vp->v_name), 0, 0); - name_cache_unlock(); + NAME_CACHE_UNLOCK(); return (name); } @@ -280,11 +309,11 @@ vnode_getname(vnode_t vp) void vnode_putname(char *name) { - name_cache_lock(); + NAME_CACHE_LOCK(); remove_name_locked(name); - name_cache_unlock(); + NAME_CACHE_UNLOCK(); } @@ -313,7 +342,7 @@ vnode_update_identity(vnode_t vp, vnode_t dvp, char *name, int name_len, int nam dvp = NULLVP; } else dvp = NULLVP; - name_cache_lock(); + NAME_CACHE_LOCK(); if ( (flags & VNODE_UPDATE_NAME) && (name != vp->v_name) ) { if (vp->v_name != NULL) { @@ -340,7 +369,7 @@ vnode_update_identity(vnode_t vp, vnode_t dvp, char *name, int name_len, int nam while ( (ncp = LIST_FIRST(&vp->v_nclinks)) ) cache_delete(ncp, 1); } - name_cache_unlock(); + NAME_CACHE_UNLOCK(); if (dvp != NULLVP) vnode_rele(dvp); @@ -393,10 +422,10 @@ vnode_update_identity(vnode_t vp, vnode_t dvp, char *name, int name_len, int nam * vnode_reclaim for each of the vnodes in the uu_vreclaims * list, we won't recurse back through here */ - name_cache_lock(); + NAME_CACHE_LOCK(); old_parentvp = vp->v_parent; vp->v_parent = NULLVP; - name_cache_unlock(); + NAME_CACHE_UNLOCK(); } else { /* * we're done... we ran into a vnode that isn't @@ -515,7 +544,7 @@ reverse_lookup(vnode_t start_vp, vnode_t *lookup_vpp, struct filedesc *fdp, vfs_ ucred = vfs_context_ucred(context); *lookup_vpp = start_vp; - name_cache_lock(); + NAME_CACHE_LOCK_SHARED(); if ( dp->v_mount && (dp->v_mount->mnt_kern_flag & MNTK_AUTH_OPAQUE) ) { auth_opaque = 1; @@ -551,7 +580,7 @@ reverse_lookup(vnode_t start_vp, vnode_t *lookup_vpp, struct filedesc *fdp, vfs_ vid = dp->v_id; - name_cache_unlock(); + NAME_CACHE_UNLOCK(); if (done == 0 && dp != start_vp) { if (vnode_getwithvid(dp, vid) != 0) { @@ -577,8 +606,7 @@ cache_lookup_path(struct nameidata *ndp, struct componentname *cnp, vnode_t dp, ucred = vfs_context_ucred(context); *trailing_slash = 0; - name_cache_lock(); - + NAME_CACHE_LOCK_SHARED(); if ( dp->v_mount && (dp->v_mount->mnt_kern_flag & MNTK_AUTH_OPAQUE) ) { auth_opaque = 1; @@ -655,11 +683,33 @@ cache_lookup_path(struct nameidata *ndp, struct componentname *cnp, vnode_t dp, if ( (cnp->cn_flags & (ISLASTCN | ISDOTDOT)) ) { if (cnp->cn_nameiop != LOOKUP) break; - if (cnp->cn_flags & (LOCKPARENT | NOCACHE | ISDOTDOT)) + if (cnp->cn_flags & (LOCKPARENT | NOCACHE)) break; + if (cnp->cn_flags & ISDOTDOT) { + /* + * Quit here only if we can't use + * the parent directory pointer or + * don't have one. Otherwise, we'll + * use it below. + */ + if ((dp->v_flag & VROOT) || + dp->v_parent == NULLVP) + break; + } + } + + /* + * "." and ".." aren't supposed to be cached, so check + * for them before checking the cache. + */ + if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') + vp = dp; + else if (cnp->cn_flags & ISDOTDOT) + vp = dp->v_parent; + else { + if ( (vp = cache_lookup_locked(dp, cnp)) == NULLVP) + break; } - if ( (vp = cache_lookup_locked(dp, cnp)) == NULLVP) - break; if ( (cnp->cn_flags & ISLASTCN) ) break; @@ -686,7 +736,7 @@ cache_lookup_path(struct nameidata *ndp, struct componentname *cnp, vnode_t dp, vvid = vp->v_id; vid = dp->v_id; - name_cache_unlock(); + NAME_CACHE_UNLOCK(); if ((vp != NULLVP) && (vp->v_type != VLNK) && @@ -772,11 +822,14 @@ cache_lookup_locked(vnode_t dvp, struct componentname *cnp) break; } } - if (ncp == 0) + if (ncp == 0) { /* * We failed to find an entry */ + NCHSTAT(ncs_miss); return (NULL); + } + NCHSTAT(ncs_goodhits); vp = ncp->nc_vp; if (vp && (vp->v_flag & VISHARDLINK)) { @@ -845,12 +898,14 @@ cache_lookup(dvp, vpp, cnp) register long namelen = cnp->cn_namelen; char *nameptr = cnp->cn_nameptr; unsigned int hashval = (cnp->cn_hash & NCHASHMASK); + boolean_t have_exclusive = FALSE; uint32_t vid; vnode_t vp; - name_cache_lock(); + NAME_CACHE_LOCK_SHARED(); ncpp = NCHHASH(dvp, cnp->cn_hash); +relook: LIST_FOREACH(ncp, ncpp, nc_hash) { if ((ncp->nc_dvp == dvp) && (ncp->nc_hashval == hashval)) { if (memcmp(ncp->nc_name, nameptr, namelen) == 0 && ncp->nc_name[namelen] == 0) @@ -859,31 +914,39 @@ cache_lookup(dvp, vpp, cnp) } /* We failed to find an entry */ if (ncp == 0) { - nchstats.ncs_miss++; - name_cache_unlock(); + NCHSTAT(ncs_miss); + NAME_CACHE_UNLOCK(); return (0); } /* We don't want to have an entry, so dump it */ if ((cnp->cn_flags & MAKEENTRY) == 0) { - nchstats.ncs_badhits++; - cache_delete(ncp, 1); - name_cache_unlock(); - return (0); + if (have_exclusive == TRUE) { + NCHSTAT(ncs_badhits); + cache_delete(ncp, 1); + NAME_CACHE_UNLOCK(); + return (0); + } + NAME_CACHE_UNLOCK(); + NAME_CACHE_LOCK(); + have_exclusive = TRUE; + goto relook; } vp = ncp->nc_vp; /* We found a "positive" match, return the vnode */ if (vp) { - nchstats.ncs_goodhits++; + NCHSTAT(ncs_goodhits); vid = vp->v_id; - name_cache_unlock(); + NAME_CACHE_UNLOCK(); if (vnode_getwithvid(vp, vid)) { - name_cache_lock(); - nchstats.ncs_badvid++; - name_cache_unlock(); +#if COLLECT_STATS + NAME_CACHE_LOCK(); + NCHSTAT(ncs_badvid); + NAME_CACHE_UNLOCK(); +#endif return (0); } *vpp = vp; @@ -892,21 +955,27 @@ cache_lookup(dvp, vpp, cnp) /* We found a negative match, and want to create it, so purge */ if (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) { - nchstats.ncs_badhits++; - cache_delete(ncp, 1); - name_cache_unlock(); - return (0); + if (have_exclusive == TRUE) { + NCHSTAT(ncs_badhits); + cache_delete(ncp, 1); + NAME_CACHE_UNLOCK(); + return (0); + } + NAME_CACHE_UNLOCK(); + NAME_CACHE_LOCK(); + have_exclusive = TRUE; + goto relook; } /* * We found a "negative" match, ENOENT notifies client of this match. * The nc_whiteout field records whether this is a whiteout. */ - nchstats.ncs_neghits++; + NCHSTAT(ncs_neghits); if (ncp->nc_whiteout) cnp->cn_flags |= ISWHITEOUT; - name_cache_unlock(); + NAME_CACHE_UNLOCK(); return (ENOENT); } @@ -925,7 +994,7 @@ cache_enter(dvp, vp, cnp) if (cnp->cn_hash == 0) cnp->cn_hash = hash_string(cnp->cn_nameptr, cnp->cn_namelen); - name_cache_lock(); + NAME_CACHE_LOCK(); /* if the entry is for -ve caching vp is null */ if ((vp != NULLVP) && (LIST_FIRST(&vp->v_nclinks))) { @@ -933,8 +1002,8 @@ cache_enter(dvp, vp, cnp) * someone beat us to the punch.. * this vnode is already in the cache */ - name_cache_unlock(); - return; + NAME_CACHE_UNLOCK(); + return; } /* * We allocate a new entry if we are less than the maximum @@ -961,11 +1030,11 @@ cache_enter(dvp, vp, cnp) * still in use... we need to * delete it before re-using it */ - nchstats.ncs_stolen++; + NCHSTAT(ncs_stolen); cache_delete(ncp, 0); } } - nchstats.ncs_enters++; + NCHSTAT(ncs_enters); /* * Fill in cache info, if vp is NULL this is a "negative" cache entry. @@ -1013,9 +1082,9 @@ cache_enter(dvp, vp, cnp) if (cnp->cn_flags & ISWHITEOUT) ncp->nc_whiteout = TRUE; - nchstats.ncs_negtotal++; + ncs_negtotal++; - if (nchstats.ncs_negtotal > desiredNegNodes) { + if (ncs_negtotal > desiredNegNodes) { /* * if we've reached our desired limit * of negative cache entries, delete @@ -1033,7 +1102,7 @@ cache_enter(dvp, vp, cnp) */ LIST_INSERT_HEAD(&dvp->v_ncchildren, ncp, nc_child); - name_cache_unlock(); + NAME_CACHE_UNLOCK(); } @@ -1089,31 +1158,34 @@ nchinit(void) /* Allocate mount list lock group attribute and group */ namecache_lck_grp_attr= lck_grp_attr_alloc_init(); - lck_grp_attr_setstat(namecache_lck_grp_attr); namecache_lck_grp = lck_grp_alloc_init("Name Cache", namecache_lck_grp_attr); /* Allocate mount list lock attribute */ namecache_lck_attr = lck_attr_alloc_init(); - //lck_attr_setdebug(namecache_lck_attr); /* Allocate mount list lock */ - namecache_mtx_lock = lck_mtx_alloc_init(namecache_lck_grp, namecache_lck_attr); + namecache_rw_lock = lck_rw_alloc_init(namecache_lck_grp, namecache_lck_attr); } +void +name_cache_lock_shared(void) +{ + lck_rw_lock_shared(namecache_rw_lock); +} + void name_cache_lock(void) { - lck_mtx_lock(namecache_mtx_lock); + lck_rw_lock_exclusive(namecache_rw_lock); } void name_cache_unlock(void) { - lck_mtx_unlock(namecache_mtx_lock); - + lck_rw_done(namecache_rw_lock); } @@ -1142,7 +1214,7 @@ resize_namecache(u_int newsize) return ENOMEM; } - name_cache_lock(); + NAME_CACHE_LOCK(); // do the switch! old_table = nchashtbl; nchashtbl = new_table; @@ -1170,7 +1242,7 @@ resize_namecache(u_int newsize) desiredNodes = dNodes; desiredNegNodes = dNegNodes; - name_cache_unlock(); + NAME_CACHE_UNLOCK(); FREE(old_table, M_CACHE); return 0; @@ -1179,13 +1251,13 @@ resize_namecache(u_int newsize) static void cache_delete(struct namecache *ncp, int age_entry) { - nchstats.ncs_deletes++; + NCHSTAT(ncs_deletes); if (ncp->nc_vp) { LIST_REMOVE(ncp, nc_un.nc_link); } else { TAILQ_REMOVE(&neghead, ncp, nc_un.nc_negentry); - nchstats.ncs_negtotal--; + ncs_negtotal--; } LIST_REMOVE(ncp, nc_child); @@ -1223,7 +1295,7 @@ cache_purge(vnode_t vp) if ((LIST_FIRST(&vp->v_nclinks) == NULL) && (LIST_FIRST(&vp->v_ncchildren) == NULL)) return; - name_cache_lock(); + NAME_CACHE_LOCK(); while ( (ncp = LIST_FIRST(&vp->v_nclinks)) ) cache_delete(ncp, 1); @@ -1231,7 +1303,7 @@ cache_purge(vnode_t vp) while ( (ncp = LIST_FIRST(&vp->v_ncchildren)) ) cache_delete(ncp, 1); - name_cache_unlock(); + NAME_CACHE_UNLOCK(); } /* @@ -1246,13 +1318,13 @@ cache_purge_negatives(vnode_t vp) { struct namecache *ncp; - name_cache_lock(); + NAME_CACHE_LOCK(); LIST_FOREACH(ncp, &vp->v_ncchildren, nc_child) if (ncp->nc_vp == NULL) cache_delete(ncp , 1); - name_cache_unlock(); + NAME_CACHE_UNLOCK(); } /* @@ -1268,7 +1340,7 @@ cache_purgevfs(mp) struct nchashhead *ncpp; struct namecache *ncp; - name_cache_lock(); + NAME_CACHE_LOCK(); /* Scan hash tables for applicable entries */ for (ncpp = &nchashtbl[nchash - 1]; ncpp >= nchashtbl; ncpp--) { restart: @@ -1279,7 +1351,7 @@ restart: } } } - name_cache_unlock(); + NAME_CACHE_UNLOCK(); } @@ -1365,9 +1437,9 @@ vfs_addname(const char *name, size_t len, u_int hashval, u_int flags) { char * ptr; - name_cache_lock(); + NAME_CACHE_LOCK(); ptr = add_name_locked(name, len, hashval, flags); - name_cache_unlock(); + NAME_CACHE_UNLOCK(); return(ptr); } @@ -1434,9 +1506,9 @@ vfs_removename(const char *nameref) { int i; - name_cache_lock(); + NAME_CACHE_LOCK(); i = remove_name_locked(nameref); - name_cache_unlock(); + NAME_CACHE_UNLOCK(); return(i); @@ -1485,12 +1557,13 @@ dump_string_table(void) string_t *entry; u_long i; - name_cache_lock(); + NAME_CACHE_LOCK_SHARED(); + for (i = 0; i <= string_table_mask; i++) { head = &string_ref_table[i]; for (entry=head->lh_first; entry != NULL; entry=entry->hash_chain.le_next) { printf("%6d - %s\n", entry->refcount, entry->str); } } - name_cache_unlock(); + NAME_CACHE_UNLOCK(); }