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); }