All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9] fs: unmount without a grace period when nothing else holds the mounts
@ 2026-10-12 15:39 Christian Brauner
  2026-10-12 15:39 ` [PATCH 1/9] ovl: free the ovl_fs after a grace period Christian Brauner
                   ` (8 more replies)
  0 siblings, 9 replies; 12+ messages in thread
From: Christian Brauner @ 2026-10-12 15:39 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Alexander Viro, Paul E. McKenney, NeilBrown, Alexander Larsson,
	Jan Kara, Jeff Layton, lkmm, Christian Brauner (Amutable)

namespace_unlock() waits for an expedited grace period every time it
puts a batch of unmounted mounts. The RCU delay was added for lockless
path walk in 2013. The fast path of mntput_no_expire() started relying
on it in 2018 and Neil made it expedited the same year because 5000
umounts took 100 seconds otherwise.

An expedited grace period IPIs every CPU. On a 64 vcpu VM that's 20 to
30 microseconds per namespace_unlock(), two thirds of tearing down a
single detached mount. On PREEMPT_RT or with rcupdate.rcu_normal=1 it's
a normal grace period of 6 milliseconds and podman run --rm pays 14 of
them per container.

People have tried to get rid of it before. There was a series to hand
lazy umounts to a workqueue. It was dropped from 6.16 again after LTP
found loop devices busy right after umount -l and mkfs corrupted an ext4
image that a kworker was still tearing down. Returning from umount(2)
before the filesystem is gone is a uapi change, lazy or not. I also
tried sampling the grace period state in umount_tree() and polling it in
namespace_unlock() but the two are only microseconds apart. No gain.

So let's elide the grace period when it isn't needed. Since commit
7eb84d54fac5 ("fs: don't let a migrating task hide its reference from
do_umount()") the per-cpu count is split into gets and puts and the
puts are summed first. A count of exactly one under mount_lock after
the mount has left its namespace can only be the mount's own reference.

A holder's get is counted whenever its put is. A walker's increment is
either counted or the walker sees the seqcount change and backs out
under mount_lock where it finds MNT_DOOMED. And nobody can find the
mount anymore. So we drop the reference and finish the mount off right
there. Everything else waits for the grace period as before. The fast
paths of mntput_no_expire() and __legitimize_mnt() don't change and
nothing changes for userspace.

For that the caller's umount(2) reference has to be dropped under the
mount_lock hold that unmounted the mount so the peek finds the mount's
own reference alone. The peek also has to be the last put.

Taking the grace period away exposes filesystems that unknowingly relied
on it for whatever they touch in rcu path walk (but rest assured that
the bug is reproducible outside of umount(2)). The first patch fixes
overlayfs. ext4, ntfs3 and the unicode map of casefolded filesystems are
broken without this series already because a chdir() out of a lazily
unmounted directory drops the last reference without a grace period too.

The ipc namespace is now freed with kfree_rcu() because free_ipc()'s
grace period also covered the lockless lookups of the namespace tree.

Dropping the explicit smp_mb()s that mnt_get_count() made redundant is
already in vfs.fixes (cf. [2]). This series doesn't depend on it.

A reproducer can't really exercise this so the formal side is heavier
than usual:

- a TLA+ model of a two-mount batch in 62 configurations where every
  mutation found its bug
- 20 LKMM litmus tests, 18 of them added under
  Documentation/litmus-tests/fs/. Every shape the series relies on is
  Never and every barrier mutation is Sometimes.
- the same tests under the AArch64 and POWER models and klitmus7 on
  x86, except the three late walker tests
- a Dartagnan harness of the C that agrees with herd7
- TLA+ trace validation of ftrace traces from the selftest and stress
  runs

All of it is in:

    https://github.com/brauner/linux-formal
    kernel/mount/gp-on-demand-proposal/

umount-bench in kops/s, median of 15 runs on a 64 vcpu VM, base against
the series, with 1 and 8 workers:

    mode               base            series            delta
    mountumount        26.1 /  75.4     57.1 / 125.5    +118% / +66%
    dissolveone        38.2 / 108.5    170.6 / 173.4    +347% / +60%
    dissolveflat       15.5             22.4             +45%
    dissolvechain      15.7             22.4             +43%
    nsdeath             6.15             7.10            +15%
    detachlocked        2.72             3.22            +18%
    lazytree            2.93             3.11             +6%
    held modes         level, +1 to +4%

And scaling with the vcpu count, single worker, median of 9:

    vcpus            1     8    16    32    64   128   256   512
    mountumount    +30%  +44%  +63%  +92%  +83%  +93%  +71%  +75%
    dissolveone    +86% +130% +183% +293% +224% +242% +155% +149%

Above 64 vcpus the per-cpu sums start to dominate a namespace death so
the multi-mount modes only gain 10 to 27% there.

With rcupdate.rcu_normal=1, the RT case, podman run --rm in an 8 vcpu
VM goes from 124 to 93 milliseconds and the 14 grace periods per
container become 9. With expedited grace periods it doesn't move.

[1]: https://lore.kernel.org/r/20261009-work-fixes-rcu-v1-0-7446d2d335e6@kernel.org
[2]: https://lore.kernel.org/r/20261011-work-mount-fixes-5-v1-0-a9b9a8ecf565@kernel.org

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Christian Brauner (9):
      ovl: free the ovl_fs after a grace period
      fs: split the final put out of mntput_no_expire_slowpath()
      fs: drop the caller's umount(2) reference under mount_lock
      fs: unmount without a grace period when nothing else holds the mounts
      fs: port kern_unmount{_array}() to mntput_unmounted()
      selftests/filesystems: check that unheld mounts are gone after umount
      Documentation/litmus-tests: add the mount reference count tests
      ipc: free the namespace after a grace period
      ipc: unmount the mqueue mount with kern_unmount()

 Documentation/litmus-tests/README                  |  95 ++++++++
 .../fs/mnt-gp-sees-fastpath-put-nogp.litmus        |  75 ++++++
 .../fs/mnt-gp-sees-fastpath-put-sync-rcu.litmus    |  73 ++++++
 .../fs/mnt-gp-sees-fastpath-put.litmus             |  79 +++++++
 .../fs/mnt-holder-get-before-put-getsfirst.litmus  |  63 +++++
 .../fs/mnt-holder-get-before-put-nomb.litmus       |  62 +++++
 .../fs/mnt-holder-get-before-put-nowmb.litmus      |  62 +++++
 .../fs/mnt-holder-get-before-put.litmus            |  73 ++++++
 .../mnt-kern-unmount-gp-sees-fastpath-put.litmus   |  70 ++++++
 .../mnt-kern-unmount-holder-get-before-put.litmus  |  60 +++++
 .../fs/mnt-sum-misses-fastpath-put.litmus          |  69 ++++++
 .../fs/mnt-walker-bail-sees-doomed-nolock.litmus   |  89 +++++++
 .../fs/mnt-walker-bail-sees-doomed.litmus          |  95 ++++++++
 .../mnt-walker-get-vs-seqcount-no-outer-mb.litmus  |  92 ++++++++
 .../fs/mnt-walker-get-vs-seqcount-nomb.litmus      |  92 ++++++++
 .../fs/mnt-walker-get-vs-seqcount.litmus           | 102 ++++++++
 .../fs/mnt-walker-late-sees-new-pwd-nomb.litmus    |  89 +++++++
 .../fs/mnt-walker-late-sees-new-pwd-nowmb.litmus   |  88 +++++++
 .../fs/mnt-walker-late-sees-new-pwd.litmus         | 103 ++++++++
 fs/namespace.c                                     | 219 +++++++++++------
 fs/overlayfs/ovl_entry.h                           |   2 +
 fs/overlayfs/params.c                              |  29 ++-
 include/linux/mount.h                              |   1 -
 ipc/namespace.c                                    |  17 +-
 .../selftests/filesystems/mount_cycle/.gitignore   |   1 +
 .../selftests/filesystems/mount_cycle/Makefile     |   2 +-
 .../filesystems/mount_cycle/unheld_umount_test.c   | 262 +++++++++++++++++++++
 27 files changed, 1974 insertions(+), 90 deletions(-)
---
base-commit: 24a19b953e2a3d0ea5a8c4951a0ab8f32720c907
change-id: 20261009-work-mount-gp_on_demand-proposal-77800244d92a


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-10-18  4:16 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-12 15:39 [PATCH 0/9] fs: unmount without a grace period when nothing else holds the mounts Christian Brauner
2026-10-12 15:39 ` [PATCH 1/9] ovl: free the ovl_fs after a grace period Christian Brauner
2026-10-12 15:39 ` [PATCH 2/9] fs: split the final put out of mntput_no_expire_slowpath() Christian Brauner
2026-10-12 15:39 ` [PATCH 3/9] fs: drop the caller's umount(2) reference under mount_lock Christian Brauner
2026-10-18  2:20   ` Al Viro
2026-10-12 15:39 ` [PATCH 4/9] fs: unmount without a grace period when nothing else holds the mounts Christian Brauner
2026-10-18  4:16   ` Al Viro
2026-10-12 15:39 ` [PATCH 5/9] fs: port kern_unmount{_array}() to mntput_unmounted() Christian Brauner
2026-10-12 15:39 ` [PATCH 6/9] selftests/filesystems: check that unheld mounts are gone after umount Christian Brauner
2026-10-12 15:39 ` [PATCH 7/9] Documentation/litmus-tests: add the mount reference count tests Christian Brauner
2026-10-12 15:39 ` [PATCH 8/9] ipc: free the namespace after a grace period Christian Brauner
2026-10-12 15:39 ` [PATCH 9/9] ipc: unmount the mqueue mount with kern_unmount() Christian Brauner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.