Module Name:    src
Committed By:   hannken
Date:           Mon Jul 17 09:22:36 UTC 2017

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

Log Message:
Make union_newlower() ans union_newupper() local to union_subr.c,
expand and remove union_updatevp() and take care to transfer the
vnode lock from the union vnode to its new upper vnode without
breaking the fstrans state.

Add assertions that un_lowervp and un_uppervp never change from
non-NULL to non-NULL.


To generate a diff of this commit:
cvs rdiff -u -r1.28 -r1.29 src/sys/fs/union/union.h
cvs rdiff -u -r1.75 -r1.76 src/sys/fs/union/union_subr.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.28 src/sys/fs/union/union.h:1.29
--- src/sys/fs/union/union.h:1.28	Mon Feb 16 10:22:00 2015
+++ src/sys/fs/union/union.h	Mon Jul 17 09:22:36 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: union.h,v 1.28 2015/02/16 10:22:00 hannken Exp $	*/
+/*	$NetBSD: union.h,v 1.29 2017/07/17 09:22:36 hannken Exp $	*/
 
 /*
  * Copyright (c) 1994 The Regents of the University of California.
@@ -158,8 +158,6 @@ extern int union_cn_close(struct vnode *
     struct lwp *);
 extern void union_removed_upper(struct union_node *un);
 extern struct vnode *union_lowervp(struct vnode *);
-extern void union_newlower(struct union_node *, struct vnode *);
-extern void union_newupper(struct union_node *, struct vnode *);
 extern void union_newsize(struct vnode *, off_t, off_t);
 int union_readdirhook(struct vnode **, struct file *, struct lwp *);
 

Index: src/sys/fs/union/union_subr.c
diff -u src/sys/fs/union/union_subr.c:1.75 src/sys/fs/union/union_subr.c:1.76
--- src/sys/fs/union/union_subr.c:1.75	Thu Jun  1 02:45:13 2017
+++ src/sys/fs/union/union_subr.c	Mon Jul 17 09:22:36 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: union_subr.c,v 1.75 2017/06/01 02:45:13 chs Exp $	*/
+/*	$NetBSD: union_subr.c,v 1.76 2017/07/17 09:22:36 hannken Exp $	*/
 
 /*
  * Copyright (c) 1994
@@ -72,7 +72,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.75 2017/06/01 02:45:13 chs Exp $");
+__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.76 2017/07/17 09:22:36 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -104,7 +104,8 @@ static u_long uhash_mask;		/* size of ha
 
 static kmutex_t uhash_lock;
 
-void union_updatevp(struct union_node *, struct vnode *, struct vnode *);
+static void union_newupper(struct union_node *, struct vnode *);
+static void union_newlower(struct union_node *, struct vnode *);
 static void union_ref(struct union_node *);
 static void union_rele(struct union_node *);
 static int union_do_lookup(struct vnode *, struct componentname *, kauth_cred_t,    const char *);
@@ -160,71 +161,28 @@ union_done(void)
 }
 
 void
-union_updatevp(struct union_node *un, struct vnode *uppervp,
-	struct vnode *lowervp)
+union_newlower(struct union_node *un, struct vnode *lowervp)
 {
 	int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
-	int nhash = UNION_HASH(uppervp, lowervp);
-	int docache = (lowervp != NULLVP || uppervp != NULLVP);
-	bool un_unlock;
+	int nhash = UNION_HASH(un->un_uppervp, lowervp);
+
+	if (un->un_lowervp == lowervp)
+		return;
 
 	KASSERT(VOP_ISLOCKED(UNIONTOV(un)) == LK_EXCLUSIVE);
+	KASSERT(un->un_lowervp == NULL);
 
 	mutex_enter(&uhash_lock);
 
-	if (!docache || ohash != nhash) {
-		if (un->un_cflags & UN_CACHED) {
-			un->un_cflags &= ~UN_CACHED;
-			LIST_REMOVE(un, un_cache);
-		}
-	}
-
-	if (un->un_lowervp != lowervp) {
-		if (un->un_lowervp) {
-			vrele(un->un_lowervp);
-			if (un->un_path) {
-				free(un->un_path, M_TEMP);
-				un->un_path = 0;
-			}
-			if (un->un_dirvp) {
-				vrele(un->un_dirvp);
-				un->un_dirvp = NULLVP;
-			}
-		}
-		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) {
-			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);
-			uvm_obj_setlock(&UNIONTOV(un)->v_uobj,
-			    uppervp->v_interlock);
-		}
+	if (ohash != nhash && (un->un_cflags & UN_CACHED)) {
+		un->un_cflags &= ~UN_CACHED;
+		LIST_REMOVE(un, un_cache);
 	}
-
-	if (docache && (ohash != nhash)) {
+	mutex_enter(&un->un_lock);
+	un->un_lowervp = lowervp;
+	un->un_lowersz = VNOVAL;
+	mutex_exit(&un->un_lock);
+	if (ohash != nhash) {
 		LIST_INSERT_HEAD(&uhashtbl[nhash], un, un_cache);
 		un->un_cflags |= UN_CACHED;
 	}
@@ -233,17 +191,57 @@ union_updatevp(struct union_node *un, st
 }
 
 void
-union_newlower(struct union_node *un, struct vnode *lowervp)
+union_newupper(struct union_node *un, struct vnode *uppervp)
 {
+	int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
+	int nhash = UNION_HASH(uppervp, un->un_lowervp);
+	struct vop_lock_args lock_ap;
+	struct vop_unlock_args unlock_ap;
+	int error __diagused;
 
-	union_updatevp(un, un->un_uppervp, lowervp);
-}
+	if (un->un_uppervp == uppervp)
+		return;
 
-void
-union_newupper(struct union_node *un, struct vnode *uppervp)
-{
+	KASSERT(VOP_ISLOCKED(UNIONTOV(un)) == LK_EXCLUSIVE);
+	KASSERT(un->un_uppervp == NULL);
 
-	union_updatevp(un, uppervp, un->un_lowervp);
+	/*
+	 * We have to transfer the vnode lock from the union vnode to
+	 * the upper vnode.  Lock the upper vnode first.  We cannot use
+	 * VOP_LOCK() here as it would break the fstrans state.
+	 */
+	lock_ap.a_desc = VDESC(vop_lock);
+	lock_ap.a_vp = uppervp;
+	lock_ap.a_flags = LK_EXCLUSIVE;
+	error = VCALL(lock_ap.a_vp,  VOFFSET(vop_lock), &lock_ap);
+	KASSERT(error == 0);
+
+	mutex_enter(&uhash_lock);
+
+	if (ohash != nhash && (un->un_cflags & UN_CACHED)) {
+		un->un_cflags &= ~UN_CACHED;
+		LIST_REMOVE(un, un_cache);
+	}
+	mutex_enter(&un->un_lock);
+	un->un_uppervp = uppervp;
+	un->un_uppersz = VNOVAL;
+	/*
+	 * With the upper vnode in place unlock the union vnode to
+	 * finalize the lock transfer.
+	 */
+	unlock_ap.a_desc = VDESC(vop_unlock);
+	unlock_ap.a_vp = UNIONTOV(un);
+	genfs_unlock(&unlock_ap);
+	/* Update union vnode interlock. */
+	mutex_obj_hold(uppervp->v_interlock);
+	uvm_obj_setlock(&UNIONTOV(un)->v_uobj, uppervp->v_interlock);
+	mutex_exit(&un->un_lock);
+	if (ohash != nhash) {
+		LIST_INSERT_HEAD(&uhashtbl[nhash], un, un_cache);
+		un->un_cflags |= UN_CACHED;
+	}
+
+	mutex_exit(&uhash_lock);
 }
 
 /*
@@ -694,7 +692,6 @@ union_copyup(struct union_node *un, int 
 	if (error)
 		return (error);
 
-	KASSERT(VOP_ISLOCKED(uvp) == LK_EXCLUSIVE);
 	union_newupper(un, uvp);
 
 	lvp = un->un_lowervp;
@@ -934,6 +931,7 @@ union_vn_create(struct vnode **vpp, stru
 	}
 
 	vp->v_writecount++;
+	VOP_UNLOCK(vp);
 	*vpp = vp;
 	return 0;
 }

Reply via email to