git: bae3d28fb6a7 - stable/15 - vn_lock_pair(): handle the case of vp1->v_vnlock == vp2->v_vnlock
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 28 May 2026 07:57:36 UTC
The branch stable/15 has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=bae3d28fb6a72a5be337bf92c2294bbfdccbb13c
commit bae3d28fb6a72a5be337bf92c2294bbfdccbb13c
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-05-16 23:19:50 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-05-28 07:57:14 +0000
vn_lock_pair(): handle the case of vp1->v_vnlock == vp2->v_vnlock
(cherry picked from commit ace4a3e177c0da5efd9ceee0d9f46068562e3f5e)
---
sys/kern/vfs_vnops.c | 32 +++++++++++++++++++++++++-------
sys/sys/vnode.h | 2 +-
2 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
index 393353d8c038..1746d344aa64 100644
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -4358,9 +4358,16 @@ vn_lock_pair_pause(const char *wmesg)
* Only one of LK_SHARED and LK_EXCLUSIVE must be specified.
* LK_NODDLKTREAT can be optionally passed.
*
- * If vp1 == vp2, only one, most exclusive, lock is obtained on it.
+ * If vp1->v_vnlock == vp2->v_vnlock, only one, most exclusive, lock
+ * is obtained on the vnode(s). The function accounts for the
+ * possibility of vp1 or vp2' v_vnlock changing while the
+ * corresponding vnode is unlocked.
+ *
+ * Return values:
+ * 0 - locked, two unlocks are required
+ * EDEADLK - locked, vnodes share the same lock, only one unlock is due.
*/
-void
+int
vn_lock_pair(struct vnode *vp1, bool vp1_locked, int lkflags1,
struct vnode *vp2, bool vp2_locked, int lkflags2)
{
@@ -4374,9 +4381,10 @@ vn_lock_pair(struct vnode *vp1, bool vp1_locked, int lkflags1,
MPASS((lkflags2 & ~(LK_SHARED | LK_EXCLUSIVE | LK_NODDLKTREAT)) == 0);
if (vp1 == NULL && vp2 == NULL)
- return;
+ return (0);
- if (vp1 == vp2) {
+recheck_same:
+ if (vp1 != NULL && vp2 != NULL && vp1->v_vnlock == vp2->v_vnlock) {
MPASS(vp1_locked == vp2_locked);
/* Select the most exclusive mode for lock. */
@@ -4389,20 +4397,26 @@ vn_lock_pair(struct vnode *vp1, bool vp1_locked, int lkflags1,
/* No need to relock if any lock is exclusive. */
if ((vp1->v_vnlock->lock_object.lo_flags &
LK_NOSHARE) != 0)
- return;
+ return (EDEADLK);
locked1 = VOP_ISLOCKED(vp1);
if (((lkflags1 & LK_SHARED) != 0 &&
locked1 != LK_EXCLUSIVE) ||
((lkflags1 & LK_EXCLUSIVE) != 0 &&
locked1 == LK_EXCLUSIVE))
- return;
+ return (EDEADLK);
VOP_UNLOCK(vp1);
}
ASSERT_VOP_UNLOCKED(vp1, "vp1");
vn_lock(vp1, lkflags1 | LK_RETRY);
- return;
+ if (vp1->v_vnlock == vp2->v_vnlock)
+ return (EDEADLK);
+ VOP_UNLOCK(vp1);
+ if (vp2_locked) {
+ VOP_UNLOCK(vp2);
+ vp2_locked = false;
+ }
}
if (vp1 != NULL) {
@@ -4473,6 +4487,9 @@ vn_lock_pair(struct vnode *vp1, bool vp1_locked, int lkflags1,
vn_lock(vp1, lkflags1 | LK_RETRY);
vp1_locked = true;
}
+ if (vp1 != NULL && vp2 != NULL &&
+ vp1->v_vnlock == vp2->v_vnlock)
+ goto recheck_same;
}
if (vp1 != NULL) {
if (lkflags1 == LK_EXCLUSIVE)
@@ -4486,6 +4503,7 @@ vn_lock_pair(struct vnode *vp1, bool vp1_locked, int lkflags1,
else
ASSERT_VOP_LOCKED(vp2, "vp2 ret");
}
+ return (0);
}
int
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
index e48c4db95341..bb1d3faf567e 100644
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -794,7 +794,7 @@ bool vn_isdisk_error(struct vnode *vp, int *errp);
bool vn_isdisk(struct vnode *vp);
int _vn_lock(struct vnode *vp, int flags, const char *file, int line);
#define vn_lock(vp, flags) _vn_lock(vp, flags, __FILE__, __LINE__)
-void vn_lock_pair(struct vnode *vp1, bool vp1_locked, int lkflags1,
+int vn_lock_pair(struct vnode *vp1, bool vp1_locked, int lkflags1,
struct vnode *vp2, bool vp2_locked, int lkflags2);
int vn_open(struct nameidata *ndp, int *flagp, int cmode, struct file *fp);
int vn_open_cred(struct nameidata *ndp, int *flagp, int cmode,