git: 42dbbaf748ea - stable/15 - dounmount(9): temporarily enable recursion for the covered vnode lock
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 13 Aug 2026 00:35:53 UTC
The branch stable/15 has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=42dbbaf748ea18195174854dae0831180af3e256
commit 42dbbaf748ea18195174854dae0831180af3e256
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-07-31 04:12:17 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-08-13 00:34:41 +0000
dounmount(9): temporarily enable recursion for the covered vnode lock
PR: 297174
(cherry picked from commit 9f5c4ef32812afb4573a278e6eafe5040f839d13)
---
sys/kern/vfs_mount.c | 29 ++++++++++++++++++++++++++---
1 file changed, 26 insertions(+), 3 deletions(-)
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index 0a3cce26ee48..82bba3f2ee61 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -1852,7 +1852,8 @@ vfs_check_usecounts(struct mount *mp)
}
static void
-dounmount_cleanup(struct mount *mp, struct vnode *coveredvp, int mntkflags)
+dounmount_cleanup(struct mount *mp, struct vnode *coveredvp, int mntkflags,
+ bool disablerec)
{
mtx_assert(MNT_MTX(mp), MA_OWNED);
@@ -1864,6 +1865,8 @@ dounmount_cleanup(struct mount *mp, struct vnode *coveredvp, int mntkflags)
vfs_op_exit_locked(mp);
MNT_IUNLOCK(mp);
if (coveredvp != NULL) {
+ if (disablerec)
+ VN_LOCK_DREC(coveredvp);
VOP_UNLOCK(coveredvp);
vdrop(coveredvp);
}
@@ -2165,6 +2168,7 @@ dounmount(struct mount *mp, uint64_t flags, struct thread *td)
uint64_t async_flag;
int mnt_gen_r;
unsigned int retries;
+ bool coveredrec;
KASSERT((flags & MNT_DEFERRED) == 0 ||
(flags & (MNT_RECURSE | MNT_FORCE)) == (MNT_RECURSE | MNT_FORCE),
@@ -2273,6 +2277,7 @@ dounmount(struct mount *mp, uint64_t flags, struct thread *td)
if ((flags & MNT_DEFERRED) != 0)
vfs_ref(mp);
+ coveredrec = false;
if ((coveredvp = mp->mnt_vnodecovered) != NULL) {
mnt_gen_r = mp->mnt_gen;
VI_LOCK(coveredvp);
@@ -2289,6 +2294,19 @@ dounmount(struct mount *mp, uint64_t flags, struct thread *td)
vfs_rel(mp);
return (EBUSY);
}
+
+ /*
+ * For some complex nullfs mount configurations, it is
+ * possible to get the covered vnode lock for the
+ * mount shared with some inside-mount vnode lock.
+ * Then at unmount time, vflush() would recurse on the
+ * covered vnode lock when reclaiming the vnode.
+ *
+ * To work around it, temprorarily allow recursion for
+ * the covered vnode lock.
+ */
+ coveredrec = VN_LOCK_CANREC(coveredvp);
+ VN_LOCK_AREC(coveredvp);
}
vfs_op_enter(mp);
@@ -2298,7 +2316,7 @@ dounmount(struct mount *mp, uint64_t flags, struct thread *td)
if ((mp->mnt_kern_flag & MNTK_UNMOUNT) != 0 ||
(mp->mnt_flag & MNT_UPDATE) != 0 ||
!TAILQ_EMPTY(&mp->mnt_uppers)) {
- dounmount_cleanup(mp, coveredvp, 0);
+ dounmount_cleanup(mp, coveredvp, 0, !coveredrec);
return (EBUSY);
}
mp->mnt_kern_flag |= MNTK_UNMOUNT;
@@ -2311,7 +2329,8 @@ dounmount(struct mount *mp, uint64_t flags, struct thread *td)
MNT_ILOCK(mp);
if (error != 0) {
vn_seqc_write_end(coveredvp);
- dounmount_cleanup(mp, coveredvp, MNTK_UNMOUNT);
+ dounmount_cleanup(mp, coveredvp, MNTK_UNMOUNT,
+ !coveredrec);
if (rootvp != NULL) {
vn_seqc_write_end(rootvp);
vrele(rootvp);
@@ -2393,6 +2412,8 @@ dounmount(struct mount *mp, uint64_t flags, struct thread *td)
MNT_IUNLOCK(mp);
if (coveredvp) {
vn_seqc_write_end(coveredvp);
+ if (!coveredrec)
+ VN_LOCK_DREC(coveredvp);
VOP_UNLOCK(coveredvp);
vdrop(coveredvp);
}
@@ -2413,6 +2434,8 @@ dounmount(struct mount *mp, uint64_t flags, struct thread *td)
coveredvp->v_mountedhere = NULL;
vn_seqc_write_end_locked(coveredvp);
VI_UNLOCK(coveredvp);
+ if (!coveredrec)
+ VN_LOCK_DREC(coveredvp);
VOP_UNLOCK(coveredvp);
vdrop(coveredvp);
}