Module Name:    src
Committed By:   hannken
Date:           Mon Nov 21 18:29:23 UTC 2011

Modified Files:
        src/sys/fs/union: union.h union_subr.c union_vfsops.c union_vnops.c

Log Message:
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.


To generate a diff of this commit:
cvs rdiff -u -r1.21 -r1.22 src/sys/fs/union/union.h
cvs rdiff -u -r1.52 -r1.53 src/sys/fs/union/union_subr.c
cvs rdiff -u -r1.64 -r1.65 src/sys/fs/union/union_vfsops.c
cvs rdiff -u -r1.48 -r1.49 src/sys/fs/union/union_vnops.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/fs/union/union.h
diff -u src/sys/fs/union/union.h:1.21 src/sys/fs/union/union.h:1.22
--- src/sys/fs/union/union.h:1.21	Tue Aug 23 07:39:37 2011
+++ src/sys/fs/union/union.h	Mon Nov 21 18:29:22 2011
@@ -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 @@ struct union_mount {
 #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 @@ int union_readdirhook(struct vnode **, s
 #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 *);
 

Index: src/sys/fs/union/union_subr.c
diff -u src/sys/fs/union/union_subr.c:1.52 src/sys/fs/union/union_subr.c:1.53
--- src/sys/fs/union/union_subr.c:1.52	Mon Nov 14 18:38:13 2011
+++ src/sys/fs/union/union_subr.c	Mon Nov 21 18:29:22 2011
@@ -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 @@ __KERNEL_RCSID(0, "$NetBSD: union_subr.c
 #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 @@ union_updatevp(struct union_node *un, st
 	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 @@ union_updatevp(struct union_node *un, st
 	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 @@ union_updatevp(struct union_node *un, st
 			}
 		}
 		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 @@ union_updatevp(struct union_node *un, st
 
 	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 @@ union_newupper(struct union_node *un, st
  * 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 @@ union_newsize(struct vnode *vp, off_t up
 		if (sz == VNOVAL)
 			sz = un->un_lowersz;
 	}
+	mutex_exit(&un->un_lock);
 
 	if (sz != VNOVAL) {
 #ifdef UNION_DIAGNOSTIC
@@ -319,6 +341,9 @@ union_allocvp(
 	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 @@ loop:
 			    (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 @@ loop:
 			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 @@ loop:
 		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;
 	un->un_lowervp = lowervp;
@@ -544,15 +540,23 @@ loop:
 		vref(undvp);
 	un->un_dircache = 0;
 	un->un_openl = 0;
-	un->un_flags = UN_LOCKED;
+	un->un_flags = 0;
+	un->un_cflags = 0;
 
+	if (uppervp == NULL) {
+		struct vop_lock_args ap;
+
+		ap.a_vp = UNIONTOV(un);
+		ap.a_flags = LK_EXCLUSIVE;
+		error = genfs_lock(&ap);
+		KASSERT(error == 0);
+	}
+
+	mutex_enter(&un->un_lock);
 	un->un_uppersz = VNOVAL;
 	un->un_lowersz = VNOVAL;
 	union_newsize(*vpp, uppersz, lowersz);
 
-	if (un->un_uppervp)
-		un->un_flags |= UN_ULOCK;
-	un->un_lwp = curlwp;
 	if (dvp && cnp && (lowervp != NULLVP)) {
 		un->un_hash = cnp->cn_hash;
 		un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
@@ -568,7 +572,7 @@ loop:
 
 	if (docache) {
 		LIST_INSERT_HEAD(&unhead[hash], un, un_cache);
-		un->un_flags |= UN_CACHED;
+		un->un_cflags |= UN_CACHED;
 	}
 
 	if (xlowervp)
@@ -590,8 +594,8 @@ union_freevp(struct vnode *vp)
 	hash = UNION_HASH(un->un_uppervp, un->un_lowervp);
 
 	mutex_enter(&unheadlock[hash]);
-	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);
 	}
 	mutex_exit(&unheadlock[hash]);
@@ -606,6 +610,7 @@ union_freevp(struct vnode *vp)
 		vrele(un->un_dirvp);
 	if (un->un_path)
 		free(un->un_path, M_TEMP);
+	mutex_destroy(&un->un_lock);
 
 	free(vp->v_data, M_TEMP);
 	vp->v_data = NULL;
@@ -691,9 +696,8 @@ union_copyup(struct union_node *un, int 
 	if (error)
 		return (error);
 
-	/* at this point, uppervp is locked */
+	KASSERT(VOP_ISLOCKED(uvp) == LK_EXCLUSIVE);
 	union_newupper(un, uvp);
-	un->un_flags |= UN_ULOCK;
 
 	lvp = un->un_lowervp;
 
@@ -954,9 +958,10 @@ union_vn_close(struct vnode *vp, int fmo
 void
 union_removed_upper(struct union_node *un)
 {
+	struct vnode *vp = UNIONTOV(un);
 	int hash;
 
-	KASSERT((un->un_flags & UN_ULOCK) == 0);
+	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
 #if 1
 	/*
 	 * We do not set the uppervp to NULLVP here, because lowervp
@@ -973,10 +978,11 @@ union_removed_upper(struct union_node *u
 #endif
 
 	hash = UNION_HASH(un->un_uppervp, un->un_lowervp);
+	VOP_UNLOCK(vp);
 
 	mutex_enter(&unheadlock[hash]);
-	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);
 	}
 	mutex_exit(&unheadlock[hash]);
@@ -1097,6 +1103,7 @@ union_diruncache(struct union_node *un)
 {
 	struct vnode **vpp;
 
+	KASSERT(VOP_ISLOCKED(UNIONTOV(un)) == LK_EXCLUSIVE);
 	if (un->un_dircache != 0) {
 		for (vpp = un->un_dircache; *vpp != NULLVP; vpp++)
 			vrele(*vpp);
@@ -1212,7 +1219,9 @@ union_readdirhook(struct vnode **vpp, st
 	 * If the directory is opaque,
 	 * then don't show lower entries
 	 */
+	vn_lock(vp, LK_SHARED | LK_RETRY);
 	error = VOP_GETATTR(vp, &va, fp->f_cred);
+	VOP_UNLOCK(vp);
 	if (error || (va.va_flags & OPAQUE))
 		return error;
 

Index: src/sys/fs/union/union_vfsops.c
diff -u src/sys/fs/union/union_vfsops.c:1.64 src/sys/fs/union/union_vfsops.c:1.65
--- src/sys/fs/union/union_vfsops.c:1.64	Sun Aug 28 08:27:57 2011
+++ src/sys/fs/union/union_vfsops.c	Mon Nov 21 18:29:22 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: union_vfsops.c,v 1.64 2011/08/28 08:27:57 hannken Exp $	*/
+/*	$NetBSD: union_vfsops.c,v 1.65 2011/11/21 18:29:22 hannken Exp $	*/
 
 /*
  * Copyright (c) 1994 The Regents of the University of California.
@@ -77,7 +77,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: union_vfsops.c,v 1.64 2011/08/28 08:27:57 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: union_vfsops.c,v 1.65 2011/11/21 18:29:22 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -208,6 +208,8 @@ union_mount(struct mount *mp, const char
 		goto bad;
 	}
 
+	mp->mnt_iflag |= IMNT_MPSAFE;
+
 	/*
 	 * Unless the mount is readonly, ensure that the top layer
 	 * supports whiteout operations

Index: src/sys/fs/union/union_vnops.c
diff -u src/sys/fs/union/union_vnops.c:1.48 src/sys/fs/union/union_vnops.c:1.49
--- src/sys/fs/union/union_vnops.c:1.48	Mon Nov 14 18:42:57 2011
+++ src/sys/fs/union/union_vnops.c	Mon Nov 21 18:29:22 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: union_vnops.c,v 1.48 2011/11/14 18:42:57 hannken Exp $	*/
+/*	$NetBSD: union_vnops.c,v 1.49 2011/11/21 18:29:22 hannken Exp $	*/
 
 /*
  * Copyright (c) 1992, 1993, 1994, 1995
@@ -72,7 +72,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: union_vnops.c,v 1.48 2011/11/14 18:42:57 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: union_vnops.c,v 1.49 2011/11/21 18:29:22 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -189,8 +189,6 @@ const struct vnodeopv_entry_desc union_v
 const struct vnodeopv_desc union_vnodeop_opv_desc =
 	{ &union_vnodeop_p, union_vnodeop_entries };
 
-#define FIXUP(un) \
-	KASSERT(((un)->un_flags & UN_ULOCK) == UN_ULOCK)
 #define NODE_IS_SPECIAL(vp) \
 	((vp)->v_type == VBLK || (vp)->v_type == VCHR || \
 	(vp)->v_type == VSOCK || (vp)->v_type == VFIFO)
@@ -309,41 +307,8 @@ start:
 	 * on and just return that vnode.
 	 */
 	if (upperdvp != NULLVP) {
-		FIXUP(dun);
-		/*
-		 * If we're doing `..' in the underlying filesystem,
-		 * we must drop our lock on the union node before
-		 * going up the tree in the lower file system--if we block
-		 * on the lowervp lock, and that's held by someone else
-		 * coming down the tree and who's waiting for our lock,
-		 * we would be hosed.
-		 */
-		if (cnp->cn_flags & ISDOTDOT) {
-			/* retain lock on underlying VP */
-			dun->un_flags |= UN_KLOCK;
-			VOP_UNLOCK(dvp);
-		}
 		uerror = union_lookup1(um->um_uppervp, &upperdvp,
 					&uppervp, cnp);
-
-		if (cnp->cn_flags & ISDOTDOT) {
-			if (dun->un_uppervp == upperdvp) {
-				/*
-				 * we got the underlying bugger back locked...
-				 * now take back the union node lock.  Since we
-				 *  hold the uppervp lock, we can diddle union
-				 * locking flags at will. :)
-				 */
-				dun->un_flags |= UN_ULOCK;
-			}
-			/*
-			 * if upperdvp got swapped out, it means we did
-			 * some mount point magic, and we do not have
-			 * dun->un_uppervp locked currently--so we get it
-			 * locked here (don't set the UN_ULOCK flag).
-			 */
-			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
-		}
 		if (cnp->cn_consume != 0) {
 			*ap->a_vpp = uppervp;
 			return (uerror);
@@ -480,12 +445,10 @@ start:
 			 * to get the componentname right.
 			 */
 			if (upperdvp) {
-				dun->un_flags &= ~UN_ULOCK;
 				VOP_UNLOCK(upperdvp);
 				uerror = union_mkshadow(um, upperdvp, cnp,
 				    &uppervp);
 				vn_lock(upperdvp, LK_EXCLUSIVE | LK_RETRY);
-				dun->un_flags |= UN_ULOCK;
 				if (uerror == 0 && cnp->cn_nameiop != LOOKUP) {
 					vput(uppervp);
 					if (lowervp != NULLVP)
@@ -537,8 +500,6 @@ union_create(void *v)
 		struct vnode *vp;
 		struct mount *mp;
 
-		FIXUP(un);
-
 		vref(dvp);
 		un->un_flags |= UN_KLOCK;
 		mp = ap->a_dvp->v_mount;
@@ -572,7 +533,6 @@ union_whiteout(void *v)
 	if (un->un_uppervp == NULLVP)
 		return (EOPNOTSUPP);
 
-	FIXUP(un);
 	return (VOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags));
 }
 
@@ -594,8 +554,6 @@ union_mknod(void *v)
 		struct vnode *vp;
 		struct mount *mp;
 
-		FIXUP(un);
-
 		vref(dvp);
 		un->un_flags |= UN_KLOCK;
 		mp = ap->a_dvp->v_mount;
@@ -669,8 +627,6 @@ union_open(void *v)
 	    (ap->a_vp->v_mount->mnt_flag & MNT_NODEV))
 		return ENXIO;
 
-	FIXUP(un);
-
 	error = VOP_OPEN(tvp, mode, cred);
 
 	return (error);
@@ -758,7 +714,6 @@ union_access(void *v)
 
 
 	if ((vp = un->un_uppervp) != NULLVP) {
-		FIXUP(un);
 		ap->a_vp = vp;
 		return (VCALL(vp, VOFFSET(vop_access), ap));
 	}
@@ -818,12 +773,10 @@ union_getattr(void *v)
 
 	vp = un->un_uppervp;
 	if (vp != NULLVP) {
-		if (un->un_flags & UN_LOCKED)
-			FIXUP(un);
-
 		error = VOP_GETATTR(vp, vap, ap->a_cred);
 		if (error)
 			return (error);
+		mutex_enter(&un->un_lock);
 		union_newsize(ap->a_vp, vap->va_size, VNOVAL);
 	}
 
@@ -845,6 +798,7 @@ union_getattr(void *v)
 			VOP_UNLOCK(vp);
 		if (error)
 			return (error);
+		mutex_enter(&un->un_lock);
 		union_newsize(ap->a_vp, VNOVAL, vap->va_size);
 	}
 
@@ -929,10 +883,11 @@ union_setattr(void *v)
 	 * otherwise.
 	 */
 	if (un->un_uppervp != NULLVP) {
-		FIXUP(un);
 		error = VOP_SETATTR(un->un_uppervp, vap, ap->a_cred);
-		if ((error == 0) && (vap->va_size != VNOVAL))
+		if ((error == 0) && (vap->va_size != VNOVAL)) {
+			mutex_enter(&un->un_lock);
 			union_newsize(ap->a_vp, vap->va_size, VNOVAL);
+		}
 	} else {
 		KASSERT(un->un_lowervp != NULLVP);
 		if (NODE_IS_SPECIAL(un->un_lowervp)) {
@@ -964,8 +919,6 @@ union_read(void *v)
 
 	if (dolock)
 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
-	else
-		FIXUP(VTOUNION(ap->a_vp));
 	error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
 	if (dolock)
 		VOP_UNLOCK(vp);
@@ -979,14 +932,21 @@ union_read(void *v)
 	if (error == 0) {
 		struct union_node *un = VTOUNION(ap->a_vp);
 		off_t cur = ap->a_uio->uio_offset;
+		off_t usz = VNOVAL, lsz = VNOVAL;
 
+		mutex_enter(&un->un_lock);
 		if (vp == un->un_uppervp) {
 			if (cur > un->un_uppersz)
-				union_newsize(ap->a_vp, cur, VNOVAL);
+				usz = cur;
 		} else {
 			if (cur > un->un_lowersz)
-				union_newsize(ap->a_vp, VNOVAL, cur);
+				lsz = cur;
 		}
+
+		if (usz != VNOVAL || lsz != VNOVAL)
+			union_newsize(ap->a_vp, usz, lsz);
+		else
+			mutex_exit(&un->un_lock);
 	}
 
 	return (error);
@@ -1018,7 +978,6 @@ union_write(void *v)
 		panic("union: missing upper layer in write");
 	}
 
-	FIXUP(un);
 	error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
 
 	/*
@@ -1028,8 +987,11 @@ union_write(void *v)
 	if (error == 0) {
 		off_t cur = ap->a_uio->uio_offset;
 
+		mutex_enter(&un->un_lock);
 		if (cur > un->un_uppersz)
 			union_newsize(ap->a_vp, cur, VNOVAL);
+		else
+			mutex_exit(&un->un_lock);
 	}
 
 	return (error);
@@ -1131,8 +1093,6 @@ union_fsync(void *v)
 
 		if (dolock)
 			vn_lock(targetvp, LK_EXCLUSIVE | LK_RETRY);
-		else
-			FIXUP(VTOUNION(ap->a_vp));
 		error = VOP_FSYNC(targetvp, ap->a_cred, ap->a_flags,
 			    ap->a_offlo, ap->a_offhi);
 		if (dolock)
@@ -1177,11 +1137,9 @@ union_remove(void *v)
 		struct vnode *dvp = dun->un_uppervp;
 		struct vnode *vp = un->un_uppervp;
 
-		FIXUP(dun);
 		vref(dvp);
 		dun->un_flags |= UN_KLOCK;
 		vput(ap->a_dvp);
-		FIXUP(un);
 		vref(vp);
 		un->un_flags |= UN_KLOCK;
 		vput(ap->a_vp);
@@ -1192,7 +1150,6 @@ union_remove(void *v)
 		if (!error)
 			union_removed_upper(un);
 	} else {
-		FIXUP(dun);
 		error = union_mkwhiteout(
 			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
 			dun->un_uppervp, ap->a_cnp, un);
@@ -1226,19 +1183,18 @@ union_link(void *v)
 	} else {
 		struct union_node *un = VTOUNION(ap->a_vp);
 		if (un->un_uppervp == NULLVP) {
+			const bool droplock = (dun->un_uppervp == un->un_dirvp);
+
 			/*
 			 * Needs to be copied before we can link it.
 			 */
 			vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
-			if (dun->un_uppervp == un->un_dirvp) {
-				dun->un_flags &= ~UN_ULOCK;
+			if (droplock)
 				VOP_UNLOCK(dun->un_uppervp);
-			}
 			error = union_copyup(un, 1, cnp->cn_cred, curlwp);
-			if (dun->un_uppervp == un->un_dirvp) {
+			if (droplock) {
 				vn_lock(dun->un_uppervp,
 				    LK_EXCLUSIVE | LK_RETRY);
-				dun->un_flags |= UN_ULOCK;
 				/*
 				 * During copyup, we dropped the lock on the
 				 * dir and invalidated any saved namei lookup
@@ -1283,7 +1239,6 @@ union_link(void *v)
 		return (error);
 	}
 
-	FIXUP(dun);
 	vref(dvp);
 	dun->un_flags |= UN_KLOCK;
 	vput(ap->a_dvp);
@@ -1408,7 +1363,6 @@ union_mkdir(void *v)
 		int error;
 		struct vnode *vp;
 
-		FIXUP(un);
 		vref(dvp);
 		un->un_flags |= UN_KLOCK;
 		VOP_UNLOCK(ap->a_dvp);
@@ -1457,11 +1411,9 @@ union_rmdir(void *v)
 		struct vnode *dvp = dun->un_uppervp;
 		struct vnode *vp = un->un_uppervp;
 
-		FIXUP(dun);
 		vref(dvp);
 		dun->un_flags |= UN_KLOCK;
 		vput(ap->a_dvp);
-		FIXUP(un);
 		vref(vp);
 		un->un_flags |= UN_KLOCK;
 		vput(ap->a_vp);
@@ -1472,7 +1424,6 @@ union_rmdir(void *v)
 		if (!error)
 			union_removed_upper(un);
 	} else {
-		FIXUP(dun);
 		error = union_mkwhiteout(
 			MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
 			dun->un_uppervp, ap->a_cnp, un);
@@ -1500,7 +1451,6 @@ union_symlink(void *v)
 	if (dvp != NULLVP) {
 		int error;
 
-		FIXUP(un);
 		vref(dvp);
 		un->un_flags |= UN_KLOCK;
 		vput(ap->a_dvp);
@@ -1538,7 +1488,6 @@ union_readdir(void *v)
 	if (uvp == NULLVP)
 		return (0);
 
-	FIXUP(un);
 	ap->a_vp = uvp;
 	return (VCALL(uvp, VOFFSET(vop_readdir), ap));
 }
@@ -1557,8 +1506,6 @@ union_readlink(void *v)
 
 	if (dolock)
 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
-	else
-		FIXUP(VTOUNION(ap->a_vp));
 	ap->a_vp = vp;
 	error = VCALL(vp, VOFFSET(vop_readlink), ap);
 	if (dolock)
@@ -1613,7 +1560,7 @@ union_inactive(void *v)
 		un->un_dircache = 0;
 	}
 
-	*ap->a_recycle = ((un->un_flags & UN_CACHED) == 0);
+	*ap->a_recycle = ((un->un_cflags & UN_CACHED) == 0);
 	VOP_UNLOCK(vp);
 
 	return (0);
@@ -1638,54 +1585,33 @@ union_lock(void *v)
 		struct vnode *a_vp;
 		int a_flags;
 	} */ *ap = v;
-	struct vnode *vp = ap->a_vp;
-	int flags = ap->a_flags;
+	struct vnode *vp;
 	struct union_node *un;
 	int error;
 
-	/* XXX unionfs can't handle shared locks yet */
-	if ((flags & LK_SHARED) != 0) {
-		flags = (flags & ~LK_SHARED) | LK_EXCLUSIVE;
-	}
-
-start:
-	un = VTOUNION(vp);
-
-	if (un->un_uppervp != NULLVP) {
-		if (((un->un_flags & UN_ULOCK) == 0) &&
-		    (vp->v_usecount != 0)) {
-			/*
-			 * We MUST always use the order of: take upper
-			 * vp lock, manipulate union node flags, drop
-			 * upper vp lock.  This code must not be an
-			 * exception.
-			 */
-			error = vn_lock(un->un_uppervp, flags);
-			if (error)
-				return (error);
-			un->un_flags |= UN_ULOCK;
-		}
-#ifdef DIAGNOSTIC
-		if (un->un_flags & UN_KLOCK) {
-			vprint("union: dangling klock", vp);
-			panic("union: dangling upper lock (%p)", vp);
-		}
-#endif
-	}
-
-	/* XXX ignores LK_NOWAIT */
-	if (un->un_flags & UN_LOCKED) {
-		KASSERT(curlwp == NULL || un->un_lwp == NULL ||
-		    un->un_lwp != curlwp);
-		un->un_flags |= UN_WANTED;
-		tsleep(&un->un_flags, PINOD, "unionlk2", 0);
-		goto start;
+	un = VTOUNION(ap->a_vp);
+	mutex_enter(&un->un_lock);
+	for (;;) {
+		vp = LOCKVP(ap->a_vp);
+		mutex_exit(&un->un_lock);
+		if (vp == ap->a_vp)
+			error = genfs_lock(ap);
+		else
+			error = VOP_LOCK(vp, ap->a_flags);
+		if (error != 0)
+			return error;
+		mutex_enter(&un->un_lock);
+		if (vp == LOCKVP(ap->a_vp))
+			break;
+		if (vp == ap->a_vp)
+			genfs_unlock(ap);
+		else
+			VOP_UNLOCK(vp);
 	}
+	KASSERT((un->un_flags & UN_KLOCK) == 0);
+	mutex_exit(&un->un_lock);
 
-	un->un_lwp = curlwp;
-
-	un->un_flags |= UN_LOCKED;
-	return (0);
+	return error;
 }
 
 /*
@@ -1706,27 +1632,22 @@ union_unlock(void *v)
 		struct vnode *a_vp;
 		int a_flags;
 	} */ *ap = v;
-	struct union_node *un = VTOUNION(ap->a_vp);
-
-	KASSERT((un->un_flags & UN_LOCKED) != 0);
-	KASSERT(curlwp == NULL || un->un_lwp == NULL ||
-	    un->un_lwp == curlwp);
-
-	un->un_flags &= ~UN_LOCKED;
-
-	if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
-		VOP_UNLOCK(un->un_uppervp);
-
-	un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
+	struct vnode *vp;
+	struct union_node *un;
 
-	if (un->un_flags & UN_WANTED) {
-		un->un_flags &= ~UN_WANTED;
-		wakeup( &un->un_flags);
+	un = VTOUNION(ap->a_vp);
+	vp = LOCKVP(ap->a_vp);
+	if ((un->un_flags & UN_KLOCK) == UN_KLOCK) {
+		KASSERT(vp != ap->a_vp);
+		un->un_flags &= ~UN_KLOCK;
+		return 0;
 	}
+	if (vp == ap->a_vp)
+		genfs_unlock(ap);
+	else
+		VOP_UNLOCK(vp);
 
-	un->un_lwp = NULL;
-
-	return (0);
+	return 0;
 }
 
 int
@@ -1745,8 +1666,6 @@ union_bmap(void *v)
 
 	if (dolock)
 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
-	else
-		FIXUP(VTOUNION(ap->a_vp));
 	ap->a_vp = vp;
 	error = VCALL(vp, VOFFSET(vop_bmap), ap);
 	if (dolock)
@@ -1784,8 +1703,18 @@ union_islocked(void *v)
 	struct vop_islocked_args /* {
 		struct vnode *a_vp;
 	} */ *ap = v;
+	struct vnode *vp;
+	struct union_node *un;
 
-	return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? LK_EXCLUSIVE : 0);
+	un = VTOUNION(ap->a_vp);
+	mutex_enter(&un->un_lock);
+	vp = LOCKVP(ap->a_vp);
+	mutex_exit(&un->un_lock);
+
+	if (vp == ap->a_vp)
+		return genfs_islocked(ap);
+	else
+		return VOP_ISLOCKED(vp);
 }
 
 int
@@ -1802,8 +1731,6 @@ union_pathconf(void *v)
 
 	if (dolock)
 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
-	else
-		FIXUP(VTOUNION(ap->a_vp));
 	ap->a_vp = vp;
 	error = VCALL(vp, VOFFSET(vop_pathconf), ap);
 	if (dolock)

Reply via email to