Author: mjg
Date: Sun Sep  4 16:52:14 2016
New Revision: 305386
URL: https://svnweb.freebsd.org/changeset/base/305386

Log:
  cache: defer freeing entries until after the global lock is dropped
  
  This also defers vdrop for held vnodes.
  
  Glanced at by:        kib

Modified:
  head/sys/kern/vfs_cache.c

Modified: head/sys/kern/vfs_cache.c
==============================================================================
--- head/sys/kern/vfs_cache.c   Sun Sep  4 15:08:14 2016        (r305385)
+++ head/sys/kern/vfs_cache.c   Sun Sep  4 16:52:14 2016        (r305386)
@@ -131,6 +131,7 @@ struct      namecache_ts {
 #define NCF_ISDOTDOT   0x02
 #define        NCF_TS          0x04
 #define        NCF_DTS         0x08
+#define        NCF_DVDROP      0x10
 
 /*
  * Name caching works as follows:
@@ -227,6 +228,8 @@ cache_free(struct namecache *ncp)
        if (ncp == NULL)
                return;
        ts = ncp->nc_flag & NCF_TS;
+       if ((ncp->nc_flag & NCF_DVDROP) != 0)
+               vdrop(ncp->nc_dvp);
        if (ncp->nc_nlen <= CACHE_PATH_CUTOFF) {
                if (ts)
                        uma_zfree(cache_zone_small_ts, ncp);
@@ -476,7 +479,7 @@ cache_negative_remove(struct namecache *
        numneg--;
 }
 
-static void
+static struct namecache *
 cache_negative_zap_one(void)
 {
        struct namecache *ncp;
@@ -486,6 +489,7 @@ cache_negative_zap_one(void)
        KASSERT(ncp->nc_vp == NULL, ("ncp %p vp %p on ncneg",
            ncp, ncp->nc_vp));
        cache_zap(ncp);
+       return (ncp);
 }
 
 /*
@@ -497,7 +501,6 @@ cache_negative_zap_one(void)
 static void
 cache_zap(struct namecache *ncp)
 {
-       struct vnode *vp;
 
        rw_assert(&cache_lock, RA_WLOCKED);
        CTR2(KTR_VFS, "cache_zap(%p) vp %p", ncp, ncp->nc_vp);
@@ -508,7 +511,6 @@ cache_zap(struct namecache *ncp)
                SDT_PROBE2(vfs, namecache, zap_negative, done, ncp->nc_dvp,
                    nc_get_name(ncp));
        }
-       vp = NULL;
        LIST_REMOVE(ncp, nc_hash);
        if (ncp->nc_flag & NCF_ISDOTDOT) {
                if (ncp == ncp->nc_dvp->v_cache_dd)
@@ -516,7 +518,7 @@ cache_zap(struct namecache *ncp)
        } else {
                LIST_REMOVE(ncp, nc_src);
                if (LIST_EMPTY(&ncp->nc_dvp->v_cache_src)) {
-                       vp = ncp->nc_dvp;
+                       ncp->nc_flag |= NCF_DVDROP;
                        numcachehv--;
                }
        }
@@ -528,9 +530,6 @@ cache_zap(struct namecache *ncp)
                cache_negative_remove(ncp);
        }
        numcache--;
-       cache_free(ncp);
-       if (vp != NULL)
-               vdrop(vp);
 }
 
 /*
@@ -611,10 +610,14 @@ retry_wlocked:
                        if ((cnp->cn_flags & MAKEENTRY) == 0) {
                                if (!wlocked && !CACHE_UPGRADE_LOCK())
                                        goto wlock;
-                               if (dvp->v_cache_dd->nc_flag & NCF_ISDOTDOT)
-                                       cache_zap(dvp->v_cache_dd);
+                               ncp = NULL;
+                               if (dvp->v_cache_dd->nc_flag & NCF_ISDOTDOT) {
+                                       ncp = dvp->v_cache_dd;
+                                       cache_zap(ncp);
+                               }
                                dvp->v_cache_dd = NULL;
                                CACHE_WUNLOCK();
+                               cache_free(ncp);
                                return (0);
                        }
                        ncp = dvp->v_cache_dd;
@@ -666,6 +669,7 @@ retry_wlocked:
                        goto wlock;
                cache_zap(ncp);
                CACHE_WUNLOCK();
+               cache_free(ncp);
                return (0);
        }
 
@@ -689,6 +693,7 @@ negative_success:
                        goto wlock;
                cache_zap(ncp);
                CACHE_WUNLOCK();
+               cache_free(ncp);
                return (0);
        }
 
@@ -767,7 +772,7 @@ void
 cache_enter_time(struct vnode *dvp, struct vnode *vp, struct componentname 
*cnp,
     struct timespec *tsp, struct timespec *dtsp)
 {
-       struct namecache *ncp, *n2;
+       struct namecache *ncp, *n2, *ndd, *nneg;
        struct namecache_ts *n3;
        struct nchashhead *ncpp;
        uint32_t hash;
@@ -789,6 +794,7 @@ cache_enter_time(struct vnode *dvp, stru
        if (numcache >= desiredvnodes * ncsizefactor)
                return;
 
+       ndd = nneg = NULL;
        flag = 0;
        if (cnp->cn_nameptr[0] == '.') {
                if (cnp->cn_namelen == 1)
@@ -905,9 +911,12 @@ cache_enter_time(struct vnode *dvp, stru
                                 * directory name in it and the name ".." for 
the
                                 * directory's parent.
                                 */
-                               if ((n2 = vp->v_cache_dd) != NULL &&
-                                   (n2->nc_flag & NCF_ISDOTDOT) != 0)
-                                       cache_zap(n2);
+                               if ((ndd = vp->v_cache_dd) != NULL) {
+                                       if ((ndd->nc_flag & NCF_ISDOTDOT) != 0)
+                                               cache_zap(ndd);
+                                       else
+                                               ndd = NULL;
+                               }
                                vp->v_cache_dd = ncp;
                        }
                } else {
@@ -945,8 +954,10 @@ cache_enter_time(struct vnode *dvp, stru
                    nc_get_name(ncp));
        }
        if (numneg * ncnegfactor > numcache)
-               cache_negative_zap_one();
+               nneg = cache_negative_zap_one();
        CACHE_WUNLOCK();
+       cache_free(ndd);
+       cache_free(nneg);
 }
 
 /*
@@ -1034,21 +1045,35 @@ cache_changesize(int newmaxvnodes)
 void
 cache_purge(struct vnode *vp)
 {
+       TAILQ_HEAD(, namecache) ncps;
+       struct namecache *ncp, *nnp;
 
        CTR1(KTR_VFS, "cache_purge(%p)", vp);
        SDT_PROBE1(vfs, namecache, purge, done, vp);
+       TAILQ_INIT(&ncps);
        CACHE_WLOCK();
-       while (!LIST_EMPTY(&vp->v_cache_src))
-               cache_zap(LIST_FIRST(&vp->v_cache_src));
-       while (!TAILQ_EMPTY(&vp->v_cache_dst))
-               cache_zap(TAILQ_FIRST(&vp->v_cache_dst));
+       while (!LIST_EMPTY(&vp->v_cache_src)) {
+               ncp = LIST_FIRST(&vp->v_cache_src);
+               cache_zap(ncp);
+               TAILQ_INSERT_TAIL(&ncps, ncp, nc_dst);
+       }
+       while (!TAILQ_EMPTY(&vp->v_cache_dst)) {
+               ncp = TAILQ_FIRST(&vp->v_cache_dst);
+               cache_zap(ncp);
+               TAILQ_INSERT_TAIL(&ncps, ncp, nc_dst);
+       }
        if (vp->v_cache_dd != NULL) {
-               KASSERT(vp->v_cache_dd->nc_flag & NCF_ISDOTDOT,
+               ncp = vp->v_cache_dd;
+               KASSERT(ncp->nc_flag & NCF_ISDOTDOT,
                   ("lost dotdot link"));
-               cache_zap(vp->v_cache_dd);
+               cache_zap(ncp);
+               TAILQ_INSERT_TAIL(&ncps, ncp, nc_dst);
        }
        KASSERT(vp->v_cache_dd == NULL, ("incomplete purge"));
        CACHE_WUNLOCK();
+       TAILQ_FOREACH_SAFE(ncp, &ncps, nc_dst, nnp) {
+               cache_free(ncp);
+       }
 }
 
 /*
@@ -1057,16 +1082,23 @@ cache_purge(struct vnode *vp)
 void
 cache_purge_negative(struct vnode *vp)
 {
-       struct namecache *cp, *ncp;
+       TAILQ_HEAD(, namecache) ncps;
+       struct namecache *ncp, *nnp;
 
        CTR1(KTR_VFS, "cache_purge_negative(%p)", vp);
        SDT_PROBE1(vfs, namecache, purge_negative, done, vp);
+       TAILQ_INIT(&ncps);
        CACHE_WLOCK();
-       LIST_FOREACH_SAFE(cp, &vp->v_cache_src, nc_src, ncp) {
-               if (cp->nc_vp == NULL)
-                       cache_zap(cp);
+       LIST_FOREACH_SAFE(ncp, &vp->v_cache_src, nc_src, nnp) {
+               if (ncp->nc_vp != NULL)
+                       continue;
+               cache_zap(ncp);
+               TAILQ_INSERT_TAIL(&ncps, ncp, nc_dst);
        }
        CACHE_WUNLOCK();
+       TAILQ_FOREACH_SAFE(ncp, &ncps, nc_dst, nnp) {
+               cache_free(ncp);
+       }
 }
 
 /*
@@ -1075,19 +1107,26 @@ cache_purge_negative(struct vnode *vp)
 void
 cache_purgevfs(struct mount *mp)
 {
+       TAILQ_HEAD(, namecache) ncps;
        struct nchashhead *ncpp;
        struct namecache *ncp, *nnp;
 
        /* Scan hash tables for applicable entries */
        SDT_PROBE1(vfs, namecache, purgevfs, done, mp);
+       TAILQ_INIT(&ncps);
        CACHE_WLOCK();
        for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) {
                LIST_FOREACH_SAFE(ncp, ncpp, nc_hash, nnp) {
-                       if (ncp->nc_dvp->v_mount == mp)
-                               cache_zap(ncp);
+                       if (ncp->nc_dvp->v_mount != mp)
+                               continue;
+                       cache_zap(ncp);
+                       TAILQ_INSERT_TAIL(&ncps, ncp, nc_dst);
                }
        }
        CACHE_WUNLOCK();
+       TAILQ_FOREACH_SAFE(ncp, &ncps, nc_dst, nnp) {
+               cache_free(ncp);
+       }
 }
 
 /*
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to