FAILED: patch "[PATCH] super: make iterate_supers_type() deletion-safe" failed to apply to 7.2-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 7.2-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-7.2.y
git checkout FETCH_HEAD
git cherry-pick -x 2d2a2d7aa98741b58f54cacc99b52024e4d865f9
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026092911-clunky-same-c5c5@gregkh' --subject-prefix 'PATCH 7.2.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 2d2a2d7aa98741b58f54cacc99b52024e4d865f9 Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Wed, 9 Sep 2026 21:30:34 +0200
Subject: [PATCH] super: make iterate_supers_type() deletion-safe
iterate_supers_type() drops sb_lock while invoking the callback and keeps only a passive reference to the current superblock. That reference keeps the object allocated, but does not keep its s_instances node linked.
After the iterator releases s_umount, final teardown can unlink the current s_instances node. The iterator then advances through a reinitialized node. With the current hlist it stops without visiting the remaining superblocks. The unlink moved from generic_shutdown_super() to kill_super_notify(), but the cursor lifetime has been unsafe since the helper was introduced.
The CIFS DFS lookup can consequently miss a matching superblock and return -EINVAL.
Move removal from fs_supers to put_super(), alongside removal from super_blocks, so a passive reference keeps both list nodes linked. Keep the filesystem module reference until then, since unlinking s_instances may touch type->fs_supers.
Make sget_fc() skip SB_DEAD superblocks before invoking test(), and set SB_DEAD under sb_lock to serialize with those callbacks. This allows kernfs to free its private information after kill_anon_super() returns. Keep matching SB_DYING superblocks until SB_DEAD is set so concurrent mounts still wait for teardown before retrying.
fs/kernfs/mount.c +2 -2
@@ -434,8 +434,8 @@ void kernfs_kill_sb(struct super_block *sb)up_write (&root->kernfs_supers_rwsem);/* -* Remove the superblock from fs_supers/s_instances -* so we can't find it, before freeing kernfs_super_info. +* Mark the superblock dead so sget_fc() can't find it, +* before freeing kernfs_super_info. */ kill_anon_super (sb);kfree (info);
fs/super.c +19 -20
@@ -433,15 +433,19 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,void put_super (struct super_block *s) {if (refcount_dec_and_test (&s->s_passive)) { +struct file_system_type *type = s->s_type;spin_lock (&sb_lock);list_del_init (&s->s_list); +hlist_del_init (&s->s_instances);spin_unlock (&sb_lock);WARN_ON (s->s_dentry_lru.node);WARN_ON (s->s_inode_lru.node);WARN_ON (s->s_mounts);call_rcu (&s->rcu, destroy_super_rcu); +/* The unlink above may touch type->fs_supers, so drop it last. */ +put_filesystem (type); } }
@@ -558,17 +562,6 @@ static void kill_super_notify(struct super_block *sb)if (sb->s_flags &SB_DEAD )return ; -/* -* Remove it from @fs_supers so it isn't found by new -* sget_fc() walkers anymore. Any concurrent mounter still -* managing to grab a temporary reference is guaranteed to -* already see SB_DYING and will wait until we notify them about -* SB_DEAD. -*/ -spin_lock (&sb_lock); -hlist_del_init (&sb->s_instances); -spin_unlock (&sb_lock); -/* Drop sget_fc()'s claim; a never-registered entry stays with the sb. */ if (sb->s_super_dev->sd_dev) {super_dev_put (sb->s_super_dev);
@@ -577,11 +570,15 @@ static void kill_super_notify(struct super_block *sb)/* * Let concurrent mounts know that this thing is really dead. -* We don't need @sb->s_umount here as every concurrent caller -* will see SB_DYING and either discard the superblock or wait -* for SB_DEAD. +* sget_fc() skips SB_DEAD superblocks and calls test() under +* sb_lock, so set it under sb_lock: once we return no test() +* runs on this superblock anymore and none will start. Everyone +* else already saw SB_DYING and either discarded the superblock +* or waits for SB_DEAD. */ +spin_lock (&sb_lock);super_wake (sb,SB_DEAD ); +spin_unlock (&sb_lock); }/**
@@ -608,7 +605,6 @@ void deactivate_locked_super(struct super_block *s)list_lru_destroy (&s->s_dentry_lru);list_lru_destroy (&s->s_inode_lru); -put_filesystem (fs);put_super (s); }else {super_unlock_excl (s);
@@ -795,12 +791,12 @@ void generic_shutdown_super(struct super_block *sb) }/* * Broadcast to everyone that grabbed a temporary reference to this -* superblock before we removed it from @fs_supers that the superblock -* is dying. Every walker of @fs_supers outside of sget_fc() will now -* discard this superblock and treat it as dead. +* superblock that it is dying. Every walker of @fs_supers outside +* of sget_fc() will now discard this superblock and treat it as +* dead. * -* We leave the superblock on @fs_supers so it can be found by -* sget_fc() until we passed sb->kill_sb(). +* sget_fc() keeps finding the superblock until SB_DEAD is set, so +* a concurrent mounter waits until we passed sb->kill_sb(). */ super_wake (sb,SB_DYING );super_unlock_excl (sb);
@@ -879,6 +875,9 @@ struct super_block *sget_fc(struct fs_context *fc,spin_lock (&sb_lock);if (test) {hlist_for_each_entry (old, &fc->fs_type->fs_supers, s_instances) { +/* Only unlinked at the last passive reference. */ +if (super_flags (old,SB_DEAD )) +continue ;if (test (old, fc)) goto share_extant_sb; }
Sasha Levin
maintainerPATCH 1/3 super: convert s_count to refcount_t s_passive
From: Christian Brauner <[email protected]>
[ Upstream commit 3ec9800c2d33c783dd3b27d4cc3bb22b9385f828 ]
The superblock carries two counters: s_active, the active reference count that keeps the filesystem usable, and s_count, the passive reference count that merely keeps the structure itself alive. Turn the passive count into a refcount_t and rename it to s_passive to make the pairing with s_active obvious.
Everything is still serialized by sb_lock, so there is no functional change; the conversion buys the usual refcount_t saturation and underflow checking. The following patches start dropping passive references without holding sb_lock and make the device-to-superblock table hold one passive reference per registered entry, which a plain integer cannot support.
Stable-dep-of: 2d2a2d7aa987 ("super: make iterate_supers_type() deletion-safe")
--- fs/super.c | 18 +++++++++--------- include/linux/fs/super_types.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-)
fs/super.c +9 -9
@@ -103,7 +103,7 @@ static bool super_flags(const struct super_block *sb, unsigned int flags)* creation will succeed and SB_BORN is set by vfs_get_tree() or we're * woken and we'll see SB_DYING. * -* The caller must have acquired a temporary reference on @sb->s_count. +* The caller must have acquired a temporary reference on @sb->s_passive. * * Return: The function returns true if SB_BORN was set and with * s_umount held. The function returns false if SB_DYING was
@@ -368,7 +368,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,spin_lock_init (&s->s_inode_wblist_lock);fserror_mount (s); - s->s_count =1 ; +refcount_set (&s->s_passive,1 );atomic_set (&s->s_active,1 );mutex_init (&s->s_vfs_rename_mutex);lockdep_set_class (&s->s_vfs_rename_mutex, &type->s_vfs_rename_key);
@@ -408,7 +408,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,*/ static void __put_super (struct super_block *s) { -if (!--s->s_count) { +if (refcount_dec_and_test (&s->s_passive)) {list_del_init (&s->s_list);WARN_ON (s->s_dentry_lru.node);WARN_ON (s->s_inode_lru.node);
@@ -530,7 +530,7 @@ static bool grab_super(struct super_block *sb) {bool locked; - sb->s_count++; +refcount_inc (&sb->s_passive);spin_unlock (&sb_lock); locked =super_lock_excl (sb);if (locked) {
@@ -557,7 +557,7 @@ static bool grab_super(struct super_block *sb)* lock held in read mode in case of success. On successful return, * the caller must drop the s_umount lock when done. * -* Note that unlike get_super() et.al. this one does *not* bump ->s_count. +* Note that unlike get_super() et.al. this one does *not* bump ->s_passive. * The reason why it's safe is that we are OK with doing trylock instead * of down_read(). There's a couple of places that are OK with that, but * it's very much not a general-purpose interface.
@@ -859,7 +859,7 @@ static void __iterate_supers(void (*f)(struct super_block *, void *), void *arg, sb = next_super(sb, flags)) {if (super_flags (sb,SB_DYING ))continue ; - sb->s_count++; +refcount_inc (&sb->s_passive);spin_unlock (&sb_lock);if (flags &SUPER_ITER_UNLOCKED ) {
@@ -904,7 +904,7 @@ void iterate_supers_type(struct file_system_type *type,if (super_flags (sb,SB_DYING ))continue ; - sb->s_count++; +refcount_inc (&sb->s_passive);spin_unlock (&sb_lock); locked =super_lock_shared (sb);
@@ -936,7 +936,7 @@ struct super_block *user_get_super(dev_t dev, bool excl)if (sb->s_dev != dev)continue ; - sb->s_count++; +refcount_inc (&sb->s_passive);spin_unlock (&sb_lock); locked =super_lock (sb, excl);
@@ -1376,7 +1376,7 @@ static struct super_block *bdev_super_lock(struct block_device *bdev, bool excl)/* Make sure sb doesn't go away from under us */ spin_lock (&sb_lock); - sb->s_count++; +refcount_inc (&sb->s_passive);spin_unlock (&sb_lock);mutex_unlock (&bdev->bd_holder_lock);
include/linux/fs/super_types.h +1 -1
@@ -145,7 +145,7 @@ struct super_block {unsigned long s_magic;struct dentry *s_root;struct rw_semaphore s_umount; -int s_count; +refcount_t s_passive;atomic_t s_active;#ifdef CONFIG_SECURITY void *s_security;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 super: take lock after last reference count
From: Christian Brauner <[email protected]>
[ Upstream commit 9c486f28994fdfc1a83f5f60129402cf4379957b ]
__put_super() required the caller to hold sb_lock, so put_super() wrapped it. The per-device superblock table introduced later drops its passive references from contexts that do not hold sb_lock, so make put_super() self-locking: drop the count first and take sb_lock only for the final list_del.
With the count now dropped outside sb_lock a superblock can briefly sit on @super_blocks with s_passive == 0 before it is unlinked, so the list walkers (__iterate_supers(), iterate_supers_type(), user_get_super()) switch to refcount_inc_not_zero() and skip it.
Stable-dep-of: 2d2a2d7aa987 ("super: make iterate_supers_type() deletion-safe")
--- fs/super.c | 63 ++++++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 35 deletions(-)
fs/super.c +28 -35
@@ -404,12 +404,17 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,/* Superblock refcounting */ /* -* Drop a superblock's refcount. The caller must hold sb_lock. +* Drop a superblock's passive reference. Must be called WITHOUT sb_lock held; +* put_super() acquires sb_lock itself when the final reference is dropped. */ -static void __put_super (struct super_block *s) +void put_super (struct super_block *s) {if (refcount_dec_and_test (&s->s_passive)) { + +spin_lock (&sb_lock);list_del_init (&s->s_list); +spin_unlock (&sb_lock); +WARN_ON (s->s_dentry_lru.node);WARN_ON (s->s_inode_lru.node);WARN_ON (s->s_mounts);
@@ -417,20 +422,6 @@ static void __put_super(struct super_block *s) } } -/** -* put_super - drop a temporary reference to superblock -* @sb: superblock in question -* -* Drops a temporary reference, frees superblock if there's no -* references left. -*/ -void put_super (struct super_block *sb) -{ -spin_lock (&sb_lock); -__put_super (sb); -spin_unlock (&sb_lock); -} -static void kill_super_notify (struct super_block *sb) {lockdep_assert_not_held (&sb->s_umount);
@@ -479,11 +470,7 @@ void deactivate_locked_super(struct super_block *s)kill_super_notify (s); -/* -* Since list_lru_destroy() may sleep, we cannot call it from -* put_super(), where we hold the sb_lock. Therefore we destroy -* the lru lists right now. -*/ +/* list_lru_destroy() may sleep; put_super() callers may not. */ list_lru_destroy (&s->s_dentry_lru);list_lru_destroy (&s->s_inode_lru);
@@ -852,14 +839,17 @@ static void __iterate_supers(void (*f)(struct super_block *, void *), void *arg,struct super_block *sb, *p =NULL ;bool excl = flags &SUPER_ITER_EXCL ; -guard (spinlock)(&sb_lock); +spin_lock (&sb_lock);for (sb =first_super (flags); !list_entry_is_head (sb, &super_blocks, s_list); sb =next_super (sb, flags)) {if (super_flags (sb,SB_DYING ))continue ; -refcount_inc (&sb->s_passive); + +if (!refcount_inc_not_zero (&sb->s_passive)) +continue ; +spin_unlock (&sb_lock);if (flags &SUPER_ITER_UNLOCKED ) {
@@ -869,13 +859,14 @@ static void __iterate_supers(void (*f)(struct super_block *, void *), void *arg,super_unlock (sb, excl); } -spin_lock (&sb_lock);if (p) -__put_super (p); +put_super (p); p = sb; +spin_lock (&sb_lock); } +spin_unlock (&sb_lock);if (p) -__put_super (p); +put_super (p); }void iterate_supers (void (*f)(struct super_block *,void *),void *arg)
@@ -904,7 +895,9 @@ void iterate_supers_type(struct file_system_type *type,if (super_flags (sb,SB_DYING ))continue ; -refcount_inc (&sb->s_passive); +if (!refcount_inc_not_zero (&sb->s_passive)) +continue ; +spin_unlock (&sb_lock); locked =super_lock_shared (sb);
@@ -913,14 +906,14 @@ void iterate_supers_type(struct file_system_type *type,super_unlock_shared (sb); } -spin_lock (&sb_lock);if (p) -__put_super (p); +put_super (p); p = sb; +spin_lock (&sb_lock); } -if (p) -__put_super (p);spin_unlock (&sb_lock); +if (p) +put_super (p); }EXPORT_SYMBOL (iterate_supers_type);
@@ -936,15 +929,17 @@ struct super_block *user_get_super(dev_t dev, bool excl)if (sb->s_dev != dev)continue ; -refcount_inc (&sb->s_passive); +if (!refcount_inc_not_zero (&sb->s_passive)) +continue ; +spin_unlock (&sb_lock); locked =super_lock (sb, excl);if (locked)return sb; +put_super (sb);spin_lock (&sb_lock); -__put_super (sb);break ; }spin_unlock (&sb_lock);
@@ -1375,9 +1370,7 @@ static struct super_block *bdev_super_lock(struct block_device *bdev, bool excl)lockdep_assert_not_held (&bdev->bd_disk->open_mutex);/* Make sure sb doesn't go away from under us */ -spin_lock (&sb_lock);refcount_inc (&sb->s_passive); -spin_unlock (&sb_lock);mutex_unlock (&bdev->bd_holder_lock);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 super: make iterate_supers_type() deletion-safe
In reply to Sasha LevinFrom: Christian Brauner <[email protected]>
[ Upstream commit 2d2a2d7aa98741b58f54cacc99b52024e4d865f9 ]
iterate_supers_type() drops sb_lock while invoking the callback and keeps only a passive reference to the current superblock. That reference keeps the object allocated, but does not keep its s_instances node linked.
After the iterator releases s_umount, final teardown can unlink the current s_instances node. The iterator then advances through a reinitialized node. With the current hlist it stops without visiting the remaining superblocks. The unlink moved from generic_shutdown_super() to kill_super_notify(), but the cursor lifetime has been unsafe since the helper was introduced.
The CIFS DFS lookup can consequently miss a matching superblock and return -EINVAL.
Move removal from fs_supers to put_super(), alongside removal from super_blocks, so a passive reference keeps both list nodes linked. Keep the filesystem module reference until then, since unlinking s_instances may touch type->fs_supers.
Make sget_fc() skip SB_DEAD superblocks before invoking test(), and set SB_DEAD under sb_lock to serialize with those callbacks. This allows kernfs to free its private information after kill_anon_super() returns. Keep matching SB_DYING superblocks until SB_DEAD is set so concurrent mounts still wait for teardown before retrying.
--- fs/kernfs/mount.c | 4 ++-- fs/super.c | 39 +++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 22 deletions(-)
fs/kernfs/mount.c +2 -2
@@ -434,8 +434,8 @@ void kernfs_kill_sb(struct super_block *sb)up_write (&root->kernfs_supers_rwsem);/* -* Remove the superblock from fs_supers/s_instances -* so we can't find it, before freeing kernfs_super_info. +* Mark the superblock dead so sget_fc() can't find it, +* before freeing kernfs_super_info. */ kill_anon_super (sb);kfree (info);
fs/super.c +19 -20
@@ -410,15 +410,19 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,void put_super (struct super_block *s) {if (refcount_dec_and_test (&s->s_passive)) { +struct file_system_type *type = s->s_type;spin_lock (&sb_lock);list_del_init (&s->s_list); +hlist_del_init (&s->s_instances);spin_unlock (&sb_lock);WARN_ON (s->s_dentry_lru.node);WARN_ON (s->s_inode_lru.node);WARN_ON (s->s_mounts);call_rcu (&s->rcu, destroy_super_rcu); +/* The unlink above may touch type->fs_supers, so drop it last. */ +put_filesystem (type); } }
@@ -430,24 +434,17 @@ static void kill_super_notify(struct super_block *sb)if (sb->s_flags &SB_DEAD )return ; -/* -* Remove it from @fs_supers so it isn't found by new -* sget_fc() walkers anymore. Any concurrent mounter still -* managing to grab a temporary reference is guaranteed to -* already see SB_DYING and will wait until we notify them about -* SB_DEAD. -*/ -spin_lock (&sb_lock); -hlist_del_init (&sb->s_instances); -spin_unlock (&sb_lock); -/* * Let concurrent mounts know that this thing is really dead. -* We don't need @sb->s_umount here as every concurrent caller -* will see SB_DYING and either discard the superblock or wait -* for SB_DEAD. +* sget_fc() skips SB_DEAD superblocks and calls test() under +* sb_lock, so set it under sb_lock: once we return no test() +* runs on this superblock anymore and none will start. Everyone +* else already saw SB_DYING and either discarded the superblock +* or waits for SB_DEAD. */ +spin_lock (&sb_lock);super_wake (sb,SB_DEAD ); +spin_unlock (&sb_lock); }/**
@@ -474,7 +471,6 @@ void deactivate_locked_super(struct super_block *s)list_lru_destroy (&s->s_dentry_lru);list_lru_destroy (&s->s_inode_lru); -put_filesystem (fs);put_super (s); }else {super_unlock_excl (s);
@@ -661,12 +657,12 @@ void generic_shutdown_super(struct super_block *sb) }/* * Broadcast to everyone that grabbed a temporary reference to this -* superblock before we removed it from @fs_supers that the superblock -* is dying. Every walker of @fs_supers outside of sget_fc() will now -* discard this superblock and treat it as dead. +* superblock that it is dying. Every walker of @fs_supers outside +* of sget_fc() will now discard this superblock and treat it as +* dead. * -* We leave the superblock on @fs_supers so it can be found by -* sget_fc() until we passed sb->kill_sb(). +* sget_fc() keeps finding the superblock until SB_DEAD is set, so +* a concurrent mounter waits until we passed sb->kill_sb(). */ super_wake (sb,SB_DYING );super_unlock_excl (sb);
@@ -745,6 +741,9 @@ struct super_block *sget_fc(struct fs_context *fc,spin_lock (&sb_lock);if (test) {hlist_for_each_entry (old, &fc->fs_type->fs_supers, s_instances) { +/* Only unlinked at the last passive reference. */ +if (super_flags (old,SB_DEAD )) +continue ;if (test (old, fc)) goto share_extant_sb; }
Signature · 1 line
2.53.0