Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 07 Aug 2026 09:10:06 +0000
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 9f5c4ef32812 - main - dounmount(9): temporarily enable recursion for the covered vnode lock
Message-ID:  <6a75a0ee.1c782.5e203c3e@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=9f5c4ef32812afb4573a278e6eafe5040f839d13

commit 9f5c4ef32812afb4573a278e6eafe5040f839d13
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-07-31 04:12:17 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-08-07 09:09:50 +0000

    dounmount(9): temporarily enable recursion for the covered vnode lock
    
    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.
    
    Work around it, by temprorarily allowing recursion on the covered vnode
    lock.  Disable recursion after the unmount if it was not enabled before.
    
    PR:     297174
    Reviewed by:    jah
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D58567
---
 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 f123c59981c4..90215a64b08b 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -1880,7 +1880,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);
@@ -1892,6 +1893,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);
 	}
@@ -2193,6 +2196,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),
@@ -2301,6 +2305,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);
@@ -2317,6 +2322,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);
@@ -2326,7 +2344,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;
@@ -2339,7 +2357,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);
@@ -2425,6 +2444,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);
 		}
@@ -2445,6 +2466,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);
 	}


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a75a0ee.1c782.5e203c3e>