diff -urBN /usr/src.orig/sys/fs/unionfs/union_vfsops.c /usr/src/sys/fs/unionfs/union_vfsops.c --- /usr/src.orig/sys/fs/unionfs/union_vfsops.c 2011-04-18 15:06:07.000000000 +0900 +++ /usr/src/sys/fs/unionfs/union_vfsops.c 2011-05-09 18:41:08.000000000 +0900 @@ -326,7 +326,10 @@ static int unionfs_unmount(struct mount *mp, int mntflags) { + struct mount *pmp; + struct vnode *pvp; struct unionfs_mount *ump; + struct unionfs_mount *pump; int error; int num; int freeing; @@ -335,11 +338,32 @@ UNIONFSDEBUG("unionfs_unmount: mp = %p\n", (void *)mp); ump = MOUNTTOUNIONFSMOUNT(mp); + pvp = NULLVP; flags = 0; + error = 0; + num = 0; if (mntflags & MNT_FORCE) flags |= FORCECLOSE; + /* get parent vnode */ + pmp = ump->um_rootvp->v_mountedhere; + if (pmp != NULL) { + if ((error = VFS_ROOT(pmp, LK_EXCLUSIVE, &pvp)) != 0) + return (error); + if (pvp->v_op != &unionfs_vnodeops || + pmp->mnt_vnodecovered != ump->um_rootvp || + (UNIONFSVPTOLOWERVP(pvp) != ump->um_rootvp && + UNIONFSVPTOUPPERVP(pvp) != ump->um_rootvp)) { + vput(pvp); + pvp = NULLVP; + pmp = NULL; + } + } + + if (pvp != NULLVP) + VOP_UNLOCK(pvp, 0); + /* vflush (no need to call vrele) */ for (freeing = 0; (error = vflush(mp, 1, flags, curthread)) != 0;) { num = mp->mnt_nvnodelistsize; @@ -348,6 +372,46 @@ freeing = num; } + UNIONFSDEBUG("unionfs_unmount: vnodesize=%d, parent=%p\n", num, pvp); + + /* check */ + if (pvp != NULLVP) { + vn_lock(pvp, LK_EXCLUSIVE | LK_RETRY); + if (error == (EBUSY) && num == 1) { + /* reconnect unionfs tree */ + pump = MOUNTTOUNIONFSMOUNT(pmp); + if (pump->um_lowervp == ump->um_rootvp) { + vput(pump->um_lowervp); + vref(ump->um_lowervp); + vn_lock(ump->um_lowervp, LK_EXCLUSIVE|LK_RETRY); + pump->um_lowervp = ump->um_lowervp; + UNIONFSVPTOLOWERVP(pvp) = ump->um_lowervp; + } + else { + vput(pump->um_uppervp); + vref(ump->um_uppervp); + vn_lock(ump->um_uppervp, LK_EXCLUSIVE|LK_RETRY); + pump->um_uppervp = ump->um_uppervp; + UNIONFSVPTOUPPERVP(pvp) = ump->um_uppervp; + } + + /* fix mount tree */ + MNT_ILOCK(pmp); + vrele(pmp->mnt_vnodecovered); + vref(mp->mnt_vnodecovered); + pmp->mnt_vnodecovered = mp->mnt_vnodecovered; + pmp->mnt_vnodecovered->v_mountedhere = pmp; + mp->mnt_vnodecovered = NULLVP; + MNT_IUNLOCK(pmp); + + /* retry vflush */ + error = vflush(mp, 1, flags, curthread); + if (error) + panic("unionfs_unmount: invalid tree."); + } + vput(pvp); + } + if (error) return (error); diff -urBN /usr/src.orig/sys/kern/vfs_mount.c /usr/src/sys/kern/vfs_mount.c --- /usr/src.orig/sys/kern/vfs_mount.c 2011-04-18 15:05:56.000000000 +0900 +++ /usr/src/sys/kern/vfs_mount.c 2011-05-09 17:50:16.000000000 +0900 @@ -1365,7 +1365,8 @@ TAILQ_REMOVE(&mountlist, mp, mnt_list); mtx_unlock(&mountlist_mtx); if (coveredvp != NULL) { - coveredvp->v_mountedhere = NULL; + if (coveredvp->v_mountedhere == mp) + coveredvp->v_mountedhere = NULL; vput(coveredvp); } vfs_event_signal(NULL, VQ_UNMOUNT, 0);