diff --git a/sys/fs/nullfs/null_vfsops.c b/sys/fs/nullfs/null_vfsops.c --- a/sys/fs/nullfs/null_vfsops.c +++ b/sys/fs/nullfs/null_vfsops.c @@ -201,6 +201,12 @@ &xmp->notify_node); } + if (lowerrootvp == mp->mnt_vnodecovered) { + vn_lock(lowerrootvp, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE); + lowerrootvp->v_vflag |= VV_CROSSLOCK; + VOP_UNLOCK(lowerrootvp); + } + MNT_ILOCK(mp); if ((xmp->nullm_flags & NULLM_CACHE) != 0) { mp->mnt_kern_flag |= lowerrootvp->v_mount->mnt_kern_flag & @@ -261,6 +267,11 @@ vfs_unregister_for_notification(mntdata->nullm_vfs, &mntdata->notify_node); } + if (mntdata->nullm_lowerrootvp == mp->mnt_vnodecovered) { + vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE); + mp->mnt_vnodecovered->v_vflag &= ~VV_CROSSLOCK; + VOP_UNLOCK(mp->mnt_vnodecovered); + } vfs_unregister_upper(mntdata->nullm_vfs, &mntdata->upper_node); vrele(mntdata->nullm_lowerrootvp); mp->mnt_data = NULL; diff --git a/sys/fs/unionfs/union_vfsops.c b/sys/fs/unionfs/union_vfsops.c --- a/sys/fs/unionfs/union_vfsops.c +++ b/sys/fs/unionfs/union_vfsops.c @@ -329,7 +329,7 @@ * order unionfs currently requires. */ if (!below) { - vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY); + vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE); mp->mnt_vnodecovered->v_vflag |= VV_CROSSLOCK; VOP_UNLOCK(mp->mnt_vnodecovered); } @@ -386,7 +386,7 @@ if (error) return (error); - vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY); + vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE); mp->mnt_vnodecovered->v_vflag &= ~VV_CROSSLOCK; VOP_UNLOCK(mp->mnt_vnodecovered); vfs_unregister_upper(ump->um_lowervp->v_mount, &ump->um_lower_link); diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -1265,14 +1265,28 @@ crosslock = (dp->v_vflag & VV_CROSSLOCK) != 0; crosslkflags = compute_cn_lkflags(mp, cnp->cn_lkflags, cnp->cn_flags); - if (__predict_false(crosslock) && - (crosslkflags & LK_EXCLUSIVE) != 0 && - VOP_ISLOCKED(dp) != LK_EXCLUSIVE) { - vn_lock(dp, LK_UPGRADE | LK_RETRY); - if (VN_IS_DOOMED(dp)) { - error = ENOENT; - goto bad2; - } + if (__predict_false(crosslock)) { + /* + * We are going to be holding the vnode lock, which + * in this case is shared by the root vnode of the + * filesystem mounted at mp, across the call to + * VFS_ROOT(). Make the situation clear to the + * filesystem by passing LK_CANRECURSE if the + * lock is held exclusive, or by clearinng + * LK_NODDLKTREAT to allow recursion on the shared + * lock in the presence of an exclusive waiter. + */ + if (VOP_ISLOCKED(dp) == LK_EXCLUSIVE) { + crosslkflags &= ~LK_SHARED; + crosslkflags |= (LK_EXCLUSIVE | LK_CANRECURSE); + } else if ((crosslkflags & LK_EXCLUSIVE) != 0) { + vn_lock(dp, LK_UPGRADE | LK_RETRY); + if (VN_IS_DOOMED(dp)) { + error = ENOENT; + goto bad2; + } + } else + crosslkflags &= ~LK_NODDLKTREAT; } if (vfs_busy(mp, 0) != 0) continue;