git: 42dbbaf748ea - stable/15 - dounmount(9): temporarily enable recursion for the covered vnode lock

Konstantin Belousov <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a7d1169.20645.45b31036__13245.251709462$1786581377$gmane$org@gitrepo.freebsd.org>
The branch stable/15 has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=42dbbaf748ea18195174854dae0831180af3e256

commit 42dbbaf748ea18195174854dae0831180af3e256
Author:     Konstantin Belousov <[email protected]>
AuthorDate: 2026-07-31 04:12:17 +0000
Commit:     Konstantin Belousov <[email protected]>
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);
 	}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.