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,