Module Name:    src
Committed By:   hannken
Date:           Wed Nov 23 19:39:11 UTC 2011

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

Log Message:
Use hashinit() / hashdone() to create the union node hash list.

Cleanup the hash lookup in union_allocvp().

Needs more work as there is still a possible deadlock between
union_allocvp() and vclean().


To generate a diff of this commit:
cvs rdiff -u -r1.22 -r1.23 src/sys/fs/union/union.h
cvs rdiff -u -r1.53 -r1.54 src/sys/fs/union/union_subr.c
cvs rdiff -u -r1.65 -r1.66 src/sys/fs/union/union_vfsops.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.22 src/sys/fs/union/union.h:1.23
--- src/sys/fs/union/union.h:1.22	Mon Nov 21 18:29:22 2011
+++ src/sys/fs/union/union.h	Wed Nov 23 19:39:11 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: union.h,v 1.22 2011/11/21 18:29:22 hannken Exp $	*/
+/*	$NetBSD: union.h,v 1.23 2011/11/23 19:39:11 hannken Exp $	*/
 
 /*
  * Copyright (c) 1994 The Regents of the University of California.
@@ -175,6 +175,7 @@ int union_readdirhook(struct vnode **, s
 extern int (**union_vnodeop_p)(void *);
 
 void union_init(void);
+void union_reinit(void);
 void union_done(void);
 int union_freevp(struct vnode *);
 

Index: src/sys/fs/union/union_subr.c
diff -u src/sys/fs/union/union_subr.c:1.53 src/sys/fs/union/union_subr.c:1.54
--- src/sys/fs/union/union_subr.c:1.53	Mon Nov 21 18:29:22 2011
+++ src/sys/fs/union/union_subr.c	Wed Nov 23 19:39:11 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: union_subr.c,v 1.53 2011/11/21 18:29:22 hannken Exp $	*/
+/*	$NetBSD: union_subr.c,v 1.54 2011/11/23 19:39:11 hannken Exp $	*/
 
 /*
  * Copyright (c) 1994
@@ -72,7 +72,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.53 2011/11/21 18:29:22 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.54 2011/11/23 19:39:11 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -96,15 +96,13 @@ __KERNEL_RCSID(0, "$NetBSD: union_subr.c
 #include <miscfs/genfs/genfs.h>
 #include <miscfs/specfs/specdev.h>
 
-/* must be power of two, otherwise change UNION_HASH() */
-#define NHASH 32
-
-/* unsigned int ... */
+static LIST_HEAD(uhashhead, union_node) *uhashtbl;
+static u_long uhash_mask;		/* size of hash table - 1 */
 #define UNION_HASH(u, l) \
-	(((((unsigned long) (u)) + ((unsigned long) l)) >> 8) & (NHASH-1))
+	((((u_long) (u) + (u_long) (l)) >> 8) & uhash_mask)
+#define NOHASH	((u_long)-1)
 
-static LIST_HEAD(unhead, union_node) unhead[NHASH];
-static kmutex_t unheadlock[NHASH];
+static kmutex_t uhash_lock;
 
 void union_updatevp(struct union_node *, struct vnode *, struct vnode *);
 static int union_do_lookup(struct vnode *, struct componentname *, kauth_cred_t,    const char *, u_long);
@@ -115,12 +113,34 @@ struct vnode *union_dircache(struct vnod
 void
 union_init(void)
 {
+
+	mutex_init(&uhash_lock, MUTEX_DEFAULT, IPL_NONE);
+	uhashtbl = hashinit(desiredvnodes, HASH_LIST, true, &uhash_mask);
+}
+
+void
+union_reinit(void)
+{
+	struct union_node *un;
+	struct uhashhead *oldhash, *hash;
+	u_long oldmask, mask, val;
 	int i;
 
-	for (i = 0; i < NHASH; i++) {
-		LIST_INIT(&unhead[i]);
-		mutex_init(&unheadlock[i], MUTEX_DEFAULT, IPL_NONE);
+	hash = hashinit(desiredvnodes, HASH_LIST, true, &mask);
+	mutex_enter(&uhash_lock);
+	oldhash = uhashtbl;
+	oldmask = uhash_mask;
+	uhashtbl = hash;
+	uhash_mask = mask;
+	for (i = 0; i <= oldmask; i++) {
+		while ((un = LIST_FIRST(&oldhash[i])) != NULL) {
+			LIST_REMOVE(un, un_cache);
+			val = UNION_HASH(un->un_uppervp, un->un_lowervp);
+			LIST_INSERT_HEAD(&hash[val], un, un_cache);
+		}
 	}
+	mutex_exit(&uhash_lock);
+	hashdone(oldhash, HASH_LIST, oldmask);
 }
 
 /*
@@ -129,10 +149,9 @@ union_init(void)
 void
 union_done(void)
 {
-	int i;
 
-	for (i = 0; i < NHASH; i++)
-		mutex_destroy(&unheadlock[i]);
+	hashdone(uhashtbl, HASH_LIST, uhash_mask);
+	mutex_destroy(&uhash_lock);
 
 	/* Make sure to unset the readdir hook. */
 	vn_union_readdir_hook = NULL;
@@ -145,37 +164,19 @@ union_updatevp(struct union_node *un, st
 	int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
 	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.
-	 */
-	if (nhash < ohash) {
-		lhash = nhash;
-		uhash = ohash;
-	} else {
-		lhash = ohash;
-		uhash = nhash;
-	}
-
-	if (lhash != uhash)
-		mutex_enter(&unheadlock[lhash]);
 
-	mutex_enter(&unheadlock[uhash]);
+	mutex_enter(&uhash_lock);
 
-	if (ohash != nhash || !docache) {
+	if (!docache || ohash != nhash) {
 		if (un->un_cflags & UN_CACHED) {
 			un->un_cflags &= ~UN_CACHED;
 			LIST_REMOVE(un, un_cache);
 		}
 	}
 
-	if (ohash != nhash)
-		mutex_exit(&unheadlock[ohash]);
-
 	if (un->un_lowervp != lowervp) {
 		if (un->un_lowervp) {
 			vrele(un->un_lowervp);
@@ -222,11 +223,11 @@ union_updatevp(struct union_node *un, st
 	}
 
 	if (docache && (ohash != nhash)) {
-		LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
+		LIST_INSERT_HEAD(&uhashtbl[nhash], un, un_cache);
 		un->un_cflags |= UN_CACHED;
 	}
 
-	mutex_exit(&unheadlock[nhash]);
+	mutex_exit(&uhash_lock);
 }
 
 void
@@ -337,8 +338,8 @@ union_allocvp(
 	struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
 	voff_t uppersz, lowersz;
 	dev_t rdev;
-	int hash = 0;
-	int vflag, iflag;
+	u_long hash[3];
+	int vflag, iflag, lflag;
 	int try;
 
 	if (uppervp)
@@ -366,62 +367,53 @@ union_allocvp(
 		vflag = VV_ROOT;
 	}
 
-loop:
 	if (!docache) {
-		un = 0;
-	} else for (try = 0; try < 3; try++) {
-		switch (try) {
-		case 0:
-			if (lowervp == NULLVP)
-				continue;
-			hash = UNION_HASH(uppervp, lowervp);
-			break;
+		un = NULL;
+		goto found;
+	}
 
-		case 1:
-			if (uppervp == NULLVP)
-				continue;
-			hash = UNION_HASH(uppervp, NULLVP);
-			break;
+	/*
+	 * If both uppervp and lowervp are not NULL we have to
+	 * search union nodes with one vnode as NULL too.
+	 */
+	hash[0] = UNION_HASH(uppervp, lowervp);
+	if (uppervp == NULL || lowervp == NULL) {
+		hash[1] = hash[2] = NOHASH;
+	} else {
+		hash[1] = UNION_HASH(uppervp, NULLVP);
+		hash[2] = UNION_HASH(NULLVP, lowervp);
+	}
 
-		case 2:
-			if (lowervp == NULLVP)
-				continue;
-			hash = UNION_HASH(NULLVP, lowervp);
-			break;
-		}
+loop:
+	mutex_enter(&uhash_lock);
 
-		mutex_enter(&unheadlock[hash]);
+	for (try = 0; try < 3; try++) {
+		if (hash[try] == NOHASH)
+			continue;
+		LIST_FOREACH(un, &uhashtbl[hash[try]], un_cache) {
+			if ((un->un_lowervp && un->un_lowervp != lowervp) ||
+			    (un->un_uppervp && un->un_uppervp != uppervp) ||
+			    UNIONTOV(un)->v_mount != mp)
+				continue;
 
-		for (un = unhead[hash].lh_first; un != 0;
-					un = un->un_cache.le_next) {
-			if ((un->un_lowervp == lowervp ||
-			     un->un_lowervp == NULLVP) &&
-			    (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);
-				mutex_exit(&unheadlock[hash]);
-				if (vget(vp, lflag))
-					goto loop;
-				break;
-			}
+			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);
+			mutex_exit(&uhash_lock);
+			if (vget(vp, lflag))
+				goto loop;
+			goto found;
 		}
-
-		if (un)
-			break;
-
-		mutex_exit(&unheadlock[hash]);
 	}
 
+	mutex_exit(&uhash_lock);
+
+found:
 	if (un) {
 		KASSERT(VOP_ISLOCKED(UNIONTOV(un)) == LK_EXCLUSIVE);
 		KASSERT(uppervp == NULL ||
@@ -474,7 +466,6 @@ loop:
 		if (error == 0)
 			lowersz = va.va_size;
 	}
-	hash = UNION_HASH(uppervp, lowervp);
 
 	/*
 	 * Get a new vnode and share the lock with upper layer vnode,
@@ -497,8 +488,8 @@ loop:
 	}
 
 	if (docache) {
-		mutex_enter(&unheadlock[hash]);
-		LIST_FOREACH(un1, &unhead[hash], un_cache) {
+		mutex_enter(&uhash_lock);
+		LIST_FOREACH(un1, &uhashtbl[hash[0]], un_cache) {
 			if (un1->un_lowervp == lowervp &&
 			    un1->un_uppervp == uppervp &&
 			    UNIONTOV(un1)->v_mount == mp) {
@@ -506,7 +497,7 @@ loop:
 				 * Another thread beat us, push back freshly
 				 * allocated vnode and retry.
 				 */
-				mutex_exit(&unheadlock[hash]);
+				mutex_exit(&uhash_lock);
 				ungetnewvnode(*vpp);
 				goto loop;
 			}
@@ -571,7 +562,7 @@ loop:
 	}
 
 	if (docache) {
-		LIST_INSERT_HEAD(&unhead[hash], un, un_cache);
+		LIST_INSERT_HEAD(&uhashtbl[hash[0]], un, un_cache);
 		un->un_cflags |= UN_CACHED;
 	}
 
@@ -580,7 +571,7 @@ loop:
 
 out:
 	if (docache)
-		mutex_exit(&unheadlock[hash]);
+		mutex_exit(&uhash_lock);
 
 	return (error);
 }
@@ -593,12 +584,12 @@ union_freevp(struct vnode *vp)
 
 	hash = UNION_HASH(un->un_uppervp, un->un_lowervp);
 
-	mutex_enter(&unheadlock[hash]);
+	mutex_enter(&uhash_lock);
 	if (un->un_cflags & UN_CACHED) {
 		un->un_cflags &= ~UN_CACHED;
 		LIST_REMOVE(un, un_cache);
 	}
-	mutex_exit(&unheadlock[hash]);
+	mutex_exit(&uhash_lock);
 
 	if (un->un_pvp != NULLVP)
 		vrele(un->un_pvp);
@@ -980,12 +971,12 @@ union_removed_upper(struct union_node *u
 	hash = UNION_HASH(un->un_uppervp, un->un_lowervp);
 	VOP_UNLOCK(vp);
 
-	mutex_enter(&unheadlock[hash]);
+	mutex_enter(&uhash_lock);
 	if (un->un_cflags & UN_CACHED) {
 		un->un_cflags &= ~UN_CACHED;
 		LIST_REMOVE(un, un_cache);
 	}
-	mutex_exit(&unheadlock[hash]);
+	mutex_exit(&uhash_lock);
 }
 
 #if 0

Index: src/sys/fs/union/union_vfsops.c
diff -u src/sys/fs/union/union_vfsops.c:1.65 src/sys/fs/union/union_vfsops.c:1.66
--- src/sys/fs/union/union_vfsops.c:1.65	Mon Nov 21 18:29:22 2011
+++ src/sys/fs/union/union_vfsops.c	Wed Nov 23 19:39:11 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: union_vfsops.c,v 1.65 2011/11/21 18:29:22 hannken Exp $	*/
+/*	$NetBSD: union_vfsops.c,v 1.66 2011/11/23 19:39:11 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.65 2011/11/21 18:29:22 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: union_vfsops.c,v 1.66 2011/11/23 19:39:11 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -526,7 +526,7 @@ struct vfsops union_vfsops = {
 	(void *)eopnotsupp,		/* vfs_fhtovp */
 	(void *)eopnotsupp,		/* vfs_vptofh */
 	union_init,
-	NULL,				/* vfs_reinit */
+	union_reinit,
 	union_done,
 	NULL,				/* vfs_mountroot */
 	(int (*)(struct mount *, struct vnode *, struct timespec *)) eopnotsupp,

Reply via email to