All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH] dcache: keep shrink_dcache_for_umount() making progress on busy roots
@ 2026-07-29  0:59 Karl Mehltretter
  2026-07-29 10:01 ` Christian Brauner
  0 siblings, 1 reply; 3+ messages in thread
From: Karl Mehltretter @ 2026-07-29  0:59 UTC (permalink / raw)
  To: Alexander Viro
  Cc: Karl Mehltretter, Christian Brauner, Jan Kara, linux-fsdevel,
	linux-kernel

Commit e9895609cb7f ("wind ->s_roots via ->d_sib instead of ->d_hash")
moved secondary roots from ->d_hash to ->d_sib.  Secondary roots are
now d_unhashed(), so __d_drop() returns without removing them from
->s_roots.  Consequently, d_drop() in do_one_tree() no longer
guarantees progress through the list.

If a secondary root has an unexpected extra reference, do_one_tree()
reports it, but its final dput() cannot evict it.  The root remains
->s_roots.first and the loop selects it forever, holding ->s_umount for
write and repeatedly reporting the same dentry.

Before e9895609cb7f, ___d_drop() special-cased IS_ROOT dentries and
removed them from ->s_roots regardless of their refcount.  Commit
9c8c10e262e0 ("more graceful recovery in umount_collect()") deliberately
made busy dentries nonfatal: report them and finish the unmount rather
than BUG() while holding ->s_umount.

Detaching busy secondary roots from ->s_roots restores the previous
behavior.  A permanently leaked reference remains leaked after unmount;
if the extra reference is only delayed, its final dput() may run after
teardown has advanced and hit poisoned or freed filesystem state.  The
current code avoids that late cleanup only by looping indefinitely under
->s_umount for a real leak.

Restore that behavior by removing each live secondary root from
->s_roots after taking the temporary reference.  In the normal case,
dentry_unlist() finds ->d_sib already unhashed when eviction occurs.

Fixes: e9895609cb7f ("wind ->s_roots via ->d_sib instead of ->d_hash")
Assisted-by: Claude:claude-fable-5
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---

Notes:
    The reproducer mounts ramfs, allocates a fresh directory inode on its
    superblock, calls d_obtain_root(), and deliberately retains the returned
    dentry reference before unmounting.  The fresh inode ensures that no
    existing alias is returned.

                            d_unhashed()   reports   umount returns
      v7.1                             0         1              yes
      7.2-rc4+ unpatched               1     13013               no
      7.2-rc4+ patched                 1         1              yes

    All 13013 reports in the unpatched run identified the same dentry,
    confirming that the loop kept selecting ->s_roots.first.  With the patch,
    diagnostics match v7.1: one busy-dentry warning, one busy-inode warning,
    and no list-debug corruption.

    This injects a pre-existing refcount bug; no in-tree filesystem was found
    to create one by itself.

    Detaching the root guarantees progress for a permanent leak.  If the extra
    reference is merely delayed, however, its eventual dput() can run after
    teardown has advanced.  Leaving the root attached avoids that late cleanup
    but retries indefinitely for a permanent leak.

 fs/dcache.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/dcache.c b/fs/dcache.c
index 3e9af9de7074..2aff15ee07fb 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1832,6 +1832,9 @@ void shrink_dcache_for_umount(struct super_block *sb)
 				wait_for_completion(&wait.completion);
 		} else {
 			dget_dlock(dentry);
+			// A busy root survives do_one_tree(); unlink it so the
+			// ->s_roots loop keeps making progress.
+			unlink_secondary_root(dentry);
 			spin_unlock(&dentry->d_lock);
 			do_one_tree(dentry);
 		}
-- 
2.53.0

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

end of thread, other threads:[~2026-07-31  0:08 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29  0:59 [RFC PATCH] dcache: keep shrink_dcache_for_umount() making progress on busy roots Karl Mehltretter
2026-07-29 10:01 ` Christian Brauner
2026-07-31  0:08   ` Karl Mehltretter

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.