kernel - Improve namecache generation handling
authorMatthew Dillon <dillon@apollo.backplane.com>
Tue, 1 Sep 2015 03:28:50 +0000 (20:28 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Tue, 1 Sep 2015 03:31:50 +0000 (20:31 -0700)
* Reduce mount->mnt_namecache_gen from 64 to 32 bits and add a dummy
  field so the structure size does not change.

* Reduce namecache->nc_namecache_gen from 64 to 32 bits and add a
  second generation number to detect cache_unlink() and cache_rename()
  calls.  Bump the counter in cache_rename() and _cache_unlink().
  Structure size did not change.

* Refactor kern_rename() to use namecache->nc_generation to detect
  a larger subset of changes to the namecache entries which can leak
  in due to the temporary unlock of fromnd->nl_nch.ncp.

sys/kern/vfs_cache.c
sys/kern/vfs_syscalls.c
sys/sys/mount.h
sys/sys/namecache.h

index e899e2a..a5cdffa 100644 (file)
@@ -1542,8 +1542,10 @@ _cache_inval_internal(struct namecache *ncp, int flags, struct cinvtrack *track)
        KKASSERT(_cache_lockstatus(ncp) == LK_EXCLUSIVE);
 
        _cache_setunresolved(ncp);
-       if (flags & CINV_DESTROY)
+       if (flags & CINV_DESTROY) {
                ncp->nc_flag |= NCF_DESTROYED;
+               ++ncp->nc_generation;
+       }
        if ((flags & CINV_CHILDREN) && 
            (kid = TAILQ_FIRST(&ncp->nc_list)) != NULL
        ) {
@@ -1714,6 +1716,8 @@ cache_rename(struct nchandle *fnch, struct nchandle *tnch)
        char *oname;
        char *nname;
 
+       ++fncp->nc_generation;
+       ++tncp->nc_generation;
        if (tncp->nc_nlen) {
                nname = kmalloc(tncp->nc_nlen + 1, M_VFSCACHE, M_WAITOK);
                bcopy(tncp->nc_name, nname, tncp->nc_nlen);
@@ -1786,6 +1790,7 @@ _cache_unlink(struct namecache *ncp)
         * name to be created under ncp->nc_parent.
         */
        ncp->nc_flag |= NCF_DESTROYED;
+       ++ncp->nc_generation;
 
        /*
         * Attempt to trigger a deactivation.  Set VREF_FINALIZE to
index b73fee6..a15f0d0 100644 (file)
@@ -3859,6 +3859,8 @@ kern_rename(struct nlookupdata *fromnd, struct nlookupdata *tond)
        struct vnode *tdvp;
        struct mount *mp;
        int error;
+       u_int fncp_gen;
+       u_int tncp_gen;
 
        bwillinode(1);
        fromnd->nl_flags |= NLC_REFDVP | NLC_RENAME_SRC;
@@ -3880,6 +3882,9 @@ kern_rename(struct nlookupdata *fromnd, struct nlookupdata *tond)
         */
        KKASSERT(fromnd->nl_flags & NLC_NCPISLOCKED);
        fromnd->nl_flags &= ~NLC_NCPISLOCKED;
+
+       fncp_gen = fromnd->nl_nch.ncp->nc_generation;
+
        cache_unlock(&fromnd->nl_nch);
 
        tond->nl_flags |= NLC_RENAME_DST | NLC_REFDVP;
@@ -3887,6 +3892,8 @@ kern_rename(struct nlookupdata *fromnd, struct nlookupdata *tond)
                cache_drop(&fnchd);
                return (error);
        }
+       tncp_gen = tond->nl_nch.ncp->nc_generation;
+
        if ((tnchd.ncp = tond->nl_nch.ncp->nc_parent) == NULL) {
                cache_drop(&fnchd);
                return (ENOENT);
@@ -3927,6 +3934,21 @@ kern_rename(struct nlookupdata *fromnd, struct nlookupdata *tond)
                     &tond->nl_nch, tond->nl_cred);
        fromnd->nl_flags |= NLC_NCPISLOCKED;
 
+       /*
+        * If the namecache generation changed for either fromnd or tond,
+        * we must retry.
+        */
+       if (fromnd->nl_nch.ncp->nc_generation != fncp_gen ||
+           tond->nl_nch.ncp->nc_generation != tncp_gen) {
+               kprintf("kern_rename: retry due to gen on: "
+                       "\"%s\" -> \"%s\"\n",
+                       fromnd->nl_nch.ncp->nc_name,
+                       tond->nl_nch.ncp->nc_name);
+               cache_drop(&fnchd);
+               cache_drop(&tnchd);
+               return (EAGAIN);
+       }
+
        /*
         * If either fromnd or tond are marked destroyed a ripout occured
         * out from under us and we must retry.
@@ -3944,7 +3966,8 @@ kern_rename(struct nlookupdata *fromnd, struct nlookupdata *tond)
        }
 
        /*
-        * make sure the parent directories linkages are the same
+        * Make sure the parent directories linkages are the same.
+        * XXX shouldn't be needed any more w/ generation check above.
         */
        if (fnchd.ncp != fromnd->nl_nch.ncp->nc_parent ||
            tnchd.ncp != tond->nl_nch.ncp->nc_parent) {
index 5a4f02b..bccccfa 100644 (file)
@@ -213,7 +213,8 @@ struct mount {
        TAILQ_ENTRY(mount) mnt_list;            /* mount list */
        struct vfsops   *mnt_op;                /* operations on fs */
        struct vfsconf  *mnt_vfc;               /* configuration info */
-       long            mnt_namecache_gen;      /* ++ to clear negative hits */
+       u_int           mnt_namecache_gen;      /* ++ to clear negative hits */
+       u_int           mnt_unused01;
        struct vnode    *mnt_syncer;            /* syncer vnode */
        struct syncer_ctx *mnt_syncer_ctx;      /* syncer process context */
        struct vnodelst mnt_nvnodelist;         /* list of vnodes this mount */
index 0777f6e..ba42c47 100644 (file)
@@ -133,7 +133,8 @@ struct namecache {
     int                        nc_timeout;     /* compared against ticks, or 0 */
     u_int              nc_lockstatus;  /* namespace locking */
     struct thread      *nc_locktd;     /* namespace locking */
-    long               nc_namecache_gen; /* cmp against mnt_namecache_gen */
+    u_int              nc_namecache_gen; /* mount generation (autoclear) */
+    u_int              nc_generation;  /* rename/unlink generation */
 };
 
 /*