/*
* 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 */
/*
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 *);
vp = vp->v_mount->mnt_vnodecovered;
}
}
- name_cache_lock();
+ NAME_CACHE_LOCK_SHARED();
while (vp && vp->v_parent != vp) {
/*
vp = vp->v_mount->mnt_vnodecovered;
}
}
- name_cache_unlock();
+ NAME_CACHE_UNLOCK();
out:
/*
* slide it down to the beginning of the buffer
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
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);
}
{
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);
}
void
vnode_putname(char *name)
{
- name_cache_lock();
+ NAME_CACHE_LOCK();
remove_name_locked(name);
- name_cache_unlock();
+ NAME_CACHE_UNLOCK();
}
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) {
while ( (ncp = LIST_FIRST(&vp->v_nclinks)) )
cache_delete(ncp, 1);
}
- name_cache_unlock();
+ NAME_CACHE_UNLOCK();
if (dvp != NULLVP)
vnode_rele(dvp);
* 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
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;
vid = dp->v_id;
- name_cache_unlock();
+ NAME_CACHE_UNLOCK();
if (done == 0 && dp != start_vp) {
if (vnode_getwithvid(dp, vid) != 0) {
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;
vvid = vp->v_id;
vid = dp->v_id;
- name_cache_unlock();
+ NAME_CACHE_UNLOCK();
if ((vp != NULLVP) && (vp->v_type != VLNK) &&
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)) {
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)
}
/* 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;
/* 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);
}
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))) {
* 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
* 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.
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
*/
LIST_INSERT_HEAD(&dvp->v_ncchildren, ncp, nc_child);
- name_cache_unlock();
+ NAME_CACHE_UNLOCK();
}
/* 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);
}
return ENOMEM;
}
- name_cache_lock();
+ NAME_CACHE_LOCK();
// do the switch!
old_table = nchashtbl;
nchashtbl = new_table;
desiredNodes = dNodes;
desiredNegNodes = dNegNodes;
- name_cache_unlock();
+ NAME_CACHE_UNLOCK();
FREE(old_table, M_CACHE);
return 0;
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);
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);
while ( (ncp = LIST_FIRST(&vp->v_ncchildren)) )
cache_delete(ncp, 1);
- name_cache_unlock();
+ NAME_CACHE_UNLOCK();
}
/*
{
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();
}
/*
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:
}
}
}
- name_cache_unlock();
+ NAME_CACHE_UNLOCK();
}
{
char * ptr;
- name_cache_lock();
+ NAME_CACHE_LOCK();
ptr = add_name_locked(name, len, hashval, flags);
- name_cache_unlock();
+ NAME_CACHE_UNLOCK();
return(ptr);
}
{
int i;
- name_cache_lock();
+ NAME_CACHE_LOCK();
i = remove_name_locked(nameref);
- name_cache_unlock();
+ NAME_CACHE_UNLOCK();
return(i);
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();
}