]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_cache.c
xnu-792.13.8.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_cache.c
index fe7fe5269231c825bbbedaf17879afe0b7c68385..e568900a50dc947aae29ee11e962f95f7a9ba62a 100644 (file)
@@ -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;
@@ -708,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) &&
@@ -794,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)) {
@@ -867,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)
@@ -881,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;
@@ -914,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);
 }
 
@@ -947,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))) {
@@ -955,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
@@ -983,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.
@@ -1035,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
@@ -1055,7 +1102,7 @@ cache_enter(dvp, vp, cnp)
         */
        LIST_INSERT_HEAD(&dvp->v_ncchildren, ncp, nc_child);
 
-       name_cache_unlock();
+       NAME_CACHE_UNLOCK();
 }
 
 
@@ -1111,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);
 }
 
 
@@ -1164,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;
@@ -1192,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;
@@ -1201,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);
 
@@ -1245,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);
@@ -1253,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();
 }
 
 /*
@@ -1268,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();
 }
 
 /*
@@ -1290,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:         
@@ -1301,7 +1351,7 @@ restart:
                        }
                }
        }
-       name_cache_unlock();
+       NAME_CACHE_UNLOCK();
 }
 
 
@@ -1387,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);
 }
@@ -1456,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);
        
@@ -1507,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();
 }