Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/fs/union Replace flag based union node locking with gene...



details:   https://anonhg.NetBSD.org/src/rev/0aaa69551718
branches:  trunk
changeset: 771464:0aaa69551718
user:      hannken <hannken%NetBSD.org@localhost>
date:      Mon Nov 21 18:29:22 2011 +0000

description:
Replace flag based union node locking with generic vnode lock, support
shared and nowait locks and protect un_uppervp and un_*sz with mutex.

Mark file system MPSAFE.

diffstat:

 sys/fs/union/union.h        |   47 +++++---
 sys/fs/union/union_subr.c   |  141 +++++++++++++++-------------
 sys/fs/union/union_vfsops.c |    6 +-
 sys/fs/union/union_vnops.c  |  215 ++++++++++++++-----------------------------
 4 files changed, 178 insertions(+), 231 deletions(-)

diffs (truncated from 933 to 300 lines):

diff -r ddaeb5166de7 -r 0aaa69551718 sys/fs/union/union.h
--- a/sys/fs/union/union.h      Mon Nov 21 17:51:03 2011 +0000
+++ b/sys/fs/union/union.h      Mon Nov 21 18:29:22 2011 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: union.h,v 1.21 2011/08/23 07:39:37 hannken Exp $       */
+/*     $NetBSD: union.h,v 1.22 2011/11/21 18:29:22 hannken Exp $       */
 
 /*
  * Copyright (c) 1994 The Regents of the University of California.
@@ -105,28 +105,36 @@
 #define        UN_FILEMODE     (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
 
 /*
- * A cache of vnode references
+ * A cache of vnode references.
+ * Lock requirements are:
+ *
+ *     :       stable
+ *     c       unheadlock[hash]
+ *     l       un_lock
+ *     m       un_lock or vnode lock to read, un_lock and
+ *                     exclusive vnode lock to write
+ *     v       vnode lock to read, exclusive vnode lock to write
+ *
+ * Lock order is vnode then un_lock.
  */
 struct union_node {
-       LIST_ENTRY(union_node)  un_cache;       /* Hash chain */
-       struct vnode            *un_vnode;      /* Back pointer */
-       struct vnode            *un_uppervp;    /* overlaying object */
-       struct vnode            *un_lowervp;    /* underlying object */
-       struct vnode            *un_dirvp;      /* Parent dir of uppervp */
-       struct vnode            *un_pvp;        /* Parent vnode */
-       char                    *un_path;       /* saved component name */
-       int                     un_hash;        /* saved un_path hash value */
-       int                     un_openl;       /* # of opens on lowervp */
-       unsigned int            un_flags;
-       struct vnode            **un_dircache;  /* cached union stack */
-       off_t                   un_uppersz;     /* size of upper object */
-       off_t                   un_lowersz;     /* size of lower object */
-       lwp_t                   *un_lwp;                /* DIAGNOSTIC only */
+       kmutex_t                un_lock;
+       LIST_ENTRY(union_node)  un_cache;       /* c: Hash chain */
+       struct vnode            *un_vnode;      /* :: Back pointer */
+       struct vnode            *un_uppervp;    /* m: overlaying object */
+       struct vnode            *un_lowervp;    /* v: underlying object */
+       struct vnode            *un_dirvp;      /* v: Parent dir of uppervp */
+       struct vnode            *un_pvp;        /* v: Parent vnode */
+       char                    *un_path;       /* v: saved component name */
+       int                     un_hash;        /* v: saved un_path hash */
+       int                     un_openl;       /* v: # of opens on lowervp */
+       unsigned int            un_flags;       /* v: node flags */
+       unsigned int            un_cflags;      /* c: cache flags */
+       struct vnode            **un_dircache;  /* v: cached union stack */
+       off_t                   un_uppersz;     /* l: size of upper object */
+       off_t                   un_lowersz;     /* l: size of lower object */
 };
 
-#define UN_WANTED      0x01
-#define UN_LOCKED      0x02
-#define UN_ULOCK       0x04            /* Upper node is locked */
 #define UN_KLOCK       0x08            /* Keep upper node locked on vput */
 #define UN_CACHED      0x10            /* In union cache */
 
@@ -162,6 +170,7 @@
 #define        LOWERVP(vp) (VTOUNION(vp)->un_lowervp)
 #define        UPPERVP(vp) (VTOUNION(vp)->un_uppervp)
 #define OTHERVP(vp) (UPPERVP(vp) ? UPPERVP(vp) : LOWERVP(vp))
+#define LOCKVP(vp) (UPPERVP(vp) ? UPPERVP(vp) : (vp))
 
 extern int (**union_vnodeop_p)(void *);
 
diff -r ddaeb5166de7 -r 0aaa69551718 sys/fs/union/union_subr.c
--- a/sys/fs/union/union_subr.c Mon Nov 21 17:51:03 2011 +0000
+++ b/sys/fs/union/union_subr.c Mon Nov 21 18:29:22 2011 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: union_subr.c,v 1.52 2011/11/14 18:38:13 hannken Exp $  */
+/*     $NetBSD: union_subr.c,v 1.53 2011/11/21 18:29:22 hannken Exp $  */
 
 /*
  * Copyright (c) 1994
@@ -72,7 +72,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.52 2011/11/14 18:38:13 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.53 2011/11/21 18:29:22 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -93,6 +93,7 @@
 #include <uvm/uvm_extern.h>
 
 #include <fs/union/union.h>
+#include <miscfs/genfs/genfs.h>
 #include <miscfs/specfs/specdev.h>
 
 /* must be power of two, otherwise change UNION_HASH() */
@@ -145,7 +146,9 @@
        int nhash = UNION_HASH(uppervp, lowervp);
        int docache = (lowervp != NULLVP || uppervp != NULLVP);
        int lhash, uhash;
+       bool un_unlock;
 
+       KASSERT(VOP_ISLOCKED(UNIONTOV(un)) == LK_EXCLUSIVE);
        /*
         * Ensure locking is ordered from lower to higher
         * to avoid deadlocks.
@@ -164,8 +167,8 @@
        mutex_enter(&unheadlock[uhash]);
 
        if (ohash != nhash || !docache) {
-               if (un->un_flags & UN_CACHED) {
-                       un->un_flags &= ~UN_CACHED;
+               if (un->un_cflags & UN_CACHED) {
+                       un->un_cflags &= ~UN_CACHED;
                        LIST_REMOVE(un, un_cache);
                }
        }
@@ -186,15 +189,30 @@
                        }
                }
                un->un_lowervp = lowervp;
+               mutex_enter(&un->un_lock);
                un->un_lowersz = VNOVAL;
+               mutex_exit(&un->un_lock);
        }
 
        if (un->un_uppervp != uppervp) {
-               if (un->un_uppervp)
+               if (un->un_uppervp) {
+                       un_unlock = false;
                        vrele(un->un_uppervp);
+               } else
+                       un_unlock = true;
 
+               mutex_enter(&un->un_lock);
                un->un_uppervp = uppervp;
+               mutex_exit(&un->un_lock);
+               if (un_unlock) {
+                       struct vop_unlock_args ap;
+
+                       ap.a_vp = UNIONTOV(un);
+                       genfs_unlock(&ap);
+               }
+               mutex_enter(&un->un_lock);
                un->un_uppersz = VNOVAL;
+               mutex_exit(&un->un_lock);
                /* Update union vnode interlock. */
                if (uppervp != NULL) {
                        mutex_obj_hold(uppervp->v_interlock);
@@ -205,7 +223,7 @@
 
        if (docache && (ohash != nhash)) {
                LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
-               un->un_flags |= UN_CACHED;
+               un->un_cflags |= UN_CACHED;
        }
 
        mutex_exit(&unheadlock[nhash]);
@@ -229,20 +247,23 @@
  * Keep track of size changes in the underlying vnodes.
  * If the size changes, then callback to the vm layer
  * giving priority to the upper layer size.
+ *
+ * Mutex un_lock hold on entry and released on return.
  */
 void
 union_newsize(struct vnode *vp, off_t uppersz, off_t lowersz)
 {
-       struct union_node *un;
+       struct union_node *un = VTOUNION(vp);
        off_t sz;
 
+       KASSERT(mutex_owned(&un->un_lock));
        /* only interested in regular files */
        if (vp->v_type != VREG) {
+               mutex_exit(&un->un_lock);
                uvm_vnp_setsize(vp, 0);
                return;
        }
 
-       un = VTOUNION(vp);
        sz = VNOVAL;
 
        if ((uppersz != VNOVAL) && (un->un_uppersz != uppersz)) {
@@ -256,6 +277,7 @@
                if (sz == VNOVAL)
                        sz = un->un_lowersz;
        }
+       mutex_exit(&un->un_lock);
 
        if (sz != VNOVAL) {
 #ifdef UNION_DIAGNOSTIC
@@ -319,6 +341,9 @@
        int vflag, iflag;
        int try;
 
+       if (uppervp)
+               KASSERT(VOP_ISLOCKED(uppervp) == LK_EXCLUSIVE);
+
        if (uppervp == NULLVP && lowervp == NULLVP)
                panic("union: unidentifiable allocation");
 
@@ -374,61 +399,33 @@
                            (un->un_uppervp == uppervp ||
                             un->un_uppervp == NULLVP) &&
                            (UNIONTOV(un)->v_mount == mp)) {
+                               int lflag;
+
+                               if (uppervp != NULL && (uppervp == dvp ||
+                                   uppervp == un->un_uppervp))
+                                       /* "." or already locked. */
+                                       lflag = 0;
+                               else
+                                       lflag = LK_EXCLUSIVE;
                                vp = UNIONTOV(un);
                                mutex_enter(vp->v_interlock);
-                               if (vget(vp, 0)) {
-                                       mutex_exit(&unheadlock[hash]);
+                               mutex_exit(&unheadlock[hash]);
+                               if (vget(vp, lflag))
                                        goto loop;
-                               }
                                break;
                        }
                }
 
-               mutex_exit(&unheadlock[hash]);
-
                if (un)
                        break;
+
+               mutex_exit(&unheadlock[hash]);
        }
 
        if (un) {
-               /*
-                * Obtain a lock on the union_node.
-                * uppervp is locked, though un->un_uppervp
-                * may not be.  this doesn't break the locking
-                * hierarchy since in the case that un->un_uppervp
-                * is not yet locked it will be vrele'd and replaced
-                * with uppervp.
-                */
-
-               if ((dvp != NULLVP) && (uppervp == dvp)) {
-                       /*
-                        * Access ``.'', so (un) will already
-                        * be locked.  Since this process has
-                        * the lock on (uppervp) no other
-                        * process can hold the lock on (un).
-                        */
-                       KASSERT((un->un_flags & UN_LOCKED) != 0);
-                       KASSERT(curlwp == NULL || un->un_lwp == NULL ||
-                           un->un_lwp == curlwp);
-               } else {
-                       if (un->un_flags & UN_LOCKED) {
-                               vrele(UNIONTOV(un));
-                               un->un_flags |= UN_WANTED;
-                               (void) tsleep(&un->un_flags, PINOD,
-                                   "unionalloc", 0);
-                               goto loop;
-                       }
-                       un->un_flags |= UN_LOCKED;
-
-                       un->un_lwp = curlwp;
-               }
-
-               /*
-                * At this point, the union_node is locked,
-                * un->un_uppervp may not be locked, and uppervp
-                * is locked or nil.
-                */
-
+               KASSERT(VOP_ISLOCKED(UNIONTOV(un)) == LK_EXCLUSIVE);
+               KASSERT(uppervp == NULL ||
+                   VOP_ISLOCKED(uppervp) == LK_EXCLUSIVE);
                /*
                 * Save information about the upper layer.
                 */
@@ -438,10 +435,8 @@
                        vrele(uppervp);
                }
 
-               if (un->un_uppervp) {
-                       un->un_flags |= UN_ULOCK;
+               if (un->un_uppervp)
                        un->un_flags &= ~UN_KLOCK;
-               }
 
                /*
                 * Save information about the lower layer.
@@ -536,6 +531,7 @@
                spec_node_init(*vpp, rdev);
 
        un = VTOUNION(*vpp);
+       mutex_init(&un->un_lock, MUTEX_DEFAULT, IPL_NONE);
        un->un_vnode = *vpp;
        un->un_uppervp = uppervp;



Home | Main Index | Thread Index | Old Index