diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -4061,7 +4061,7 @@ vn_lock_pair(struct vnode *vp1, bool vp1_locked, int lkflags1, struct vnode *vp2, bool vp2_locked, int lkflags2) { - int error; + int error, locked1; MPASS(((lkflags1 & LK_SHARED) != 0) ^ ((lkflags1 & LK_EXCLUSIVE) != 0)); MPASS((lkflags1 & ~(LK_SHARED | LK_EXCLUSIVE | LK_NODDLKTREAT)) == 0); @@ -4071,6 +4071,29 @@ if (vp1 == NULL && vp2 == NULL) return; + if (vp1 == vp2) { + MPASS(vp1_locked == vp2_locked); + + /* Select the most exclusive mode for lock. */ + if ((lkflags1 & LK_TYPE_MASK) != (lkflags2 & LK_TYPE_MASK)) + lkflags1 = (lkflags1 & ~LK_SHARED) | LK_EXCLUSIVE; + + if (vp1_locked) { + ASSERT_VOP_LOCKED(vp1, "vp1"); + locked1 = VOP_ISLOCKED(vp1); + if (((lkflags1 & LK_SHARED) != 0 && + locked1 != LK_EXCLUSIVE) || + ((lkflags1 & LK_EXCLUSIVE) != 0 && + locked1 == LK_EXCLUSIVE)) + return; + VOP_UNLOCK(vp1); + } + + ASSERT_VOP_UNLOCKED(vp1, "vp1"); + vn_lock(vp1, lkflags1 | LK_RETRY); + return; + } + if (vp1 != NULL) { if ((lkflags1 & LK_SHARED) != 0 && (vp1->v_vnlock->lock_object.lo_flags & LK_NOSHARE) != 0)