From: Christian Brauner <brauner@kernel.org>
To: Jan Kara <jack@suse.cz>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>,
Josef Bacik <josef@toxicpanda.com>,
linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
"Christian Brauner (Amutable)" <brauner@kernel.org>
Subject: [PATCH 4/5] super: make sync(2)'s wait for s_umount killable
Date: Fri, 23 Oct 2026 11:41:04 +0200 [thread overview]
Message-ID: <20261023-work-super-lock_helpers-v1-4-d0fcb3f2f275@kernel.org> (raw)
In-Reply-To: <20261023-work-super-lock_helpers-v1-0-d0fcb3f2f275@kernel.org>
iterate_supers_sync() waits killably for a superblock that's being shut
down, but it takes s_umount of a live superblock uninterruptibly. So a
freeze_super() or remount that's stuck under s_umount, in
sync_filesystem() or ->freeze_fs() say, still hangs every sync(2) on
the machine without anyone being able to kill it.
That's inconsistent with fs_bdev_sync() which already uses a killable
super_lock() for both waits. And the reasoning is the same: if the task
is killed the walk stops and ksys_sync() returns, there's nobody left to
make a promise to. The emergency sync and the sync before suspend and
hibernation run from a workqueue where that can't happen. The PROM sync
on sparc32 runs in whatever task it interrupted but that one could
already be cut short while it waited for a superblock being shut down.
Let __iterate_supers() pass SUPER_LOCK_KILLABLE together with
SUPER_LOCK_WAIT_SHUTDOWN for SUPER_ITER_WAIT_SHUTDOWN. Only
iterate_supers_sync() passes that flag. The wait for SB_BORN doesn't
matter for sync(2) as iterate_supers_sync() skips unborn superblocks
anyway.
This can be reproduced by freezing a filesystem on a suspended dm-linear
device and calling sync(2) behind the freeze:
truncate -s 512M img
DEV=$(losetup -f --show img)
mke2fs -q $DEV
dmsetup create t --table "0 $(blockdev --getsz $DEV) linear $DEV 0"
mount -t ext2 /dev/mapper/t /mnt
dd if=/dev/urandom of=/mnt/f bs=1M count=64
dmsetup suspend --nolockfs t
fsfreeze --freeze /mnt &
sleep 3
sync &
sleep 3
kill -9 $!
fsfreeze sits in sync_filesystem() with s_umount held for writing.
Without this patch the sync stays in down_read() after the SIGKILL. With
it the sync goes away.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/super.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/fs/super.c b/fs/super.c
index 0f3308fb99aa..a0f9eadf0da7 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1010,9 +1010,13 @@ static inline struct super_block *next_super(struct super_block *sb,
static int __iterate_supers(void (*f)(struct super_block *, void *), void *arg,
enum super_iter_flags_t flags)
{
+ enum super_lock_flags_t lock_flags = 0;
struct super_block *sb, *p = NULL;
int ret = 0;
+ if (flags & SUPER_ITER_WAIT_SHUTDOWN)
+ lock_flags |= SUPER_LOCK_KILLABLE | SUPER_LOCK_WAIT_SHUTDOWN;
+
spin_lock(&sb_lock);
for (sb = first_super(flags);
@@ -1032,12 +1036,14 @@ static int __iterate_supers(void (*f)(struct super_block *, void *), void *arg,
if (flags & SUPER_ITER_UNLOCKED) {
f(sb, arg);
- } else if (!super_lock(sb, 0)) {
- f(sb, arg);
- super_unlock_shared(sb);
- } else if (flags & SUPER_ITER_WAIT_SHUTDOWN) {
- /* whoever shuts it down writes it out, wait for that */
- ret = super_wait(sb, SB_DYING, TASK_KILLABLE);
+ } else {
+ ret = super_lock(sb, lock_flags);
+ if (!ret) {
+ f(sb, arg);
+ super_unlock_shared(sb);
+ } else if (ret == -ENOENT) {
+ ret = 0;
+ }
}
if (p)
@@ -1085,8 +1091,8 @@ void iterate_supers_born(void (*f)(struct super_block *, void *), void *arg)
* A superblock which is still being set up isn't passed to @f either, and
* isn't waited for. Nobody can have written to it yet.
*
- * Return: 0 if all the superblocks were seen, -ERESTARTSYS if the task was
- * killed while it waited for one.
+ * Return: 0 if all the superblocks were seen, -EINTR or -ERESTARTSYS if the
+ * task was killed while it waited for one.
*/
int iterate_supers_sync(void (*f)(struct super_block *, void *), void *arg)
{
--
2.53.0
next prev parent reply other threads:[~2026-10-23 9:42 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-23 9:41 [PATCH 0/5] super: rework super_lock() Christian Brauner
2026-10-23 9:41 ` [PATCH 1/5] super: add super_wait() Christian Brauner
2026-10-23 9:41 ` [PATCH 2/5] super: let super_lock() take flags Christian Brauner
2026-10-23 9:41 ` [PATCH 3/5] super: let super_lock() wait for a superblock being shut down Christian Brauner
2026-10-23 9:41 ` Christian Brauner [this message]
2026-10-23 9:41 ` [PATCH 5/5] super: add a guard for super_lock() Christian Brauner
2026-10-23 13:54 ` [PATCH 0/5] super: rework super_lock() Josef Bacik
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261023-work-super-lock_helpers-v1-4-d0fcb3f2f275@kernel.org \
--to=brauner@kernel.org \
--cc=jack@suse.cz \
--cc=josef@toxicpanda.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®