Module Name: src Committed By: hannken Date: Fri Jul 16 08:23:28 UTC 2010
Modified Files: src/sys/fs/union: union_subr.c Log Message: Always take the hash list lock before removing a node from the hash chain. Release the hash list lock before calling getnewvnode() and check the hash list again like other file systems do. Take v_interlock before calling vget(). To generate a diff of this commit: cvs rdiff -u -r1.37 -r1.38 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_subr.c diff -u src/sys/fs/union/union_subr.c:1.37 src/sys/fs/union/union_subr.c:1.38 --- src/sys/fs/union/union_subr.c:1.37 Thu Jun 24 13:03:11 2010 +++ src/sys/fs/union/union_subr.c Fri Jul 16 08:23:28 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: union_subr.c,v 1.37 2010/06/24 13:03:11 hannken Exp $ */ +/* $NetBSD: union_subr.c,v 1.38 2010/07/16 08:23:28 hannken Exp $ */ /* * Copyright (c) 1994 @@ -72,7 +72,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.37 2010/06/24 13:03:11 hannken Exp $"); +__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.38 2010/07/16 08:23:28 hannken Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -330,8 +330,8 @@ { int error; struct vattr va; - struct union_node *un = NULL; - struct vnode *xlowervp = NULLVP; + struct union_node *un = NULL, *un1; + struct vnode *vp, *xlowervp = NULLVP; struct union_mount *um = MOUNTTOUNIONMOUNT(mp); voff_t uppersz, lowersz; int hash = 0; @@ -394,7 +394,9 @@ (un->un_uppervp == uppervp || un->un_uppervp == NULLVP) && (UNIONTOV(un)->v_mount == mp)) { - if (vget(UNIONTOV(un), 0)) { + vp = UNIONTOV(un); + mutex_enter(&vp->v_interlock); + if (vget(vp, LK_INTERLOCK)) { union_list_unlock(hash); goto loop; } @@ -502,17 +504,7 @@ if (lowervp != NULLVP) if (VOP_GETATTR(lowervp, &va, FSCRED) == 0) lowersz = va.va_size; - - if (docache) { - /* - * otherwise lock the vp list while we call getnewvnode - * since that can block. - */ - hash = UNION_HASH(uppervp, lowervp); - - if (union_list_lock(hash)) - goto loop; - } + hash = UNION_HASH(uppervp, lowervp); error = getnewvnode(VT_UNION, mp, union_vnodeop_p, vpp); if (error) { @@ -528,6 +520,24 @@ goto out; } + if (docache) { + while (union_list_lock(hash)) + continue; + LIST_FOREACH(un1, &unhead[hash], un_cache) { + if (un1->un_lowervp == lowervp && + un1->un_uppervp == uppervp && + UNIONTOV(un1)->v_mount == mp) { + /* + * Another thread beat us, push back freshly + * allocated vnode and retry. + */ + union_list_unlock(hash); + ungetnewvnode(*vpp); + goto loop; + } + } + } + (*vpp)->v_data = malloc(sizeof(struct union_node), M_TEMP, M_WAITOK); (*vpp)->v_vflag |= vflag; @@ -590,12 +600,18 @@ int union_freevp(struct vnode *vp) { + int hash; struct union_node *un = VTOUNION(vp); + hash = UNION_HASH(un->un_uppervp, un->un_lowervp); + + while (union_list_lock(hash)) + continue; if (un->un_flags & UN_CACHED) { un->un_flags &= ~UN_CACHED; LIST_REMOVE(un, un_cache); } + union_list_unlock(hash); if (un->un_pvp != NULLVP) vrele(un->un_pvp); @@ -998,6 +1014,8 @@ void union_removed_upper(struct union_node *un) { + int hash; + #if 1 /* * We do not set the uppervp to NULLVP here, because lowervp @@ -1013,10 +1031,15 @@ union_newupper(un, NULLVP); #endif + hash = UNION_HASH(un->un_uppervp, un->un_lowervp); + + while (union_list_lock(hash)) + continue; if (un->un_flags & UN_CACHED) { un->un_flags &= ~UN_CACHED; LIST_REMOVE(un, un_cache); } + union_list_unlock(hash); if (un->un_flags & UN_ULOCK) { un->un_flags &= ~UN_ULOCK;