From: Hsiu-Hsien Lee <swinds24@gmail.com>
To: tytso@mit.edu
Cc: adilger.kernel@dilger.ca, jack@suse.cz,
libaokun@linux.alibaba.com, ojaswin@linux.ibm.com,
ritesh.list@gmail.com, yi.zhang@huawei.com,
harshadshirwadkar@gmail.com, linux-ext4@vger.kernel.org,
linux-kernel@vger.kernel.org, Hsiu-Hsien Lee <swinds24@gmail.com>,
stable@vger.kernel.org
Subject: [PATCH] ext4: fix fast commit replay failing on a read-only mount
Date: Wed, 12 Aug 2026 12:43:53 +0800 [thread overview]
Message-ID: <20260812044353.1018268-1-swinds24@gmail.com> (raw)
A filesystem with fast_commit that needs recovery cannot be mounted
read-only:
EXT4-fs (dm-0): INFO: recovery required on readonly filesystem
EXT4-fs (dm-0): write access will be enabled during recovery
WARNING: CPU: 22 PID: 5544 at fs/ext4/ext4_jbd2.c:73
ext4_journal_check_start
__ext4_journal_start_sb
__ext4_unlink
ext4_fc_replay
do_one_pass
jbd2_journal_recover
jbd2_journal_load
__ext4_fill_super
JBD2: journal recovery failed
EXT4-fs (dm-0): error loading journal
Fast commit replay runs ext4 metadata operations instead of writing
blocks through the buffer cache: ext4_fc_replay_{unlink,link,create}()
reach __ext4_unlink() and __ext4_link(), which start a handle.
ext4_journal_check_start() returns -EROFS on a read-only sb, and as
that is not -ENOENT it propagates out of jbd2_journal_recover() and
kills the whole recovery. The EXT4_FC_REPLAY check that would hand
out a no-journal handle sits after the sb_rdonly() test, so replay can
never complete read-only.
ext4_load_journal() has already promised that write access will be
enabled during recovery, so make that true for the superblock as well:
clear SB_RDONLY across jbd2_journal_load() when recovery is needed on a
read-only mount and the devices are writable, as ext4_orphan_cleanup()
does. Unlike ext4_handle_error(), which avoids SB_RDONLY because it
would need s_umount, the sb here is still inside ext4_fill_super() and
not published, so nothing can observe it.
The failure is not clean either: the replay handlers passing a NULL
handle (ext4_fc_replay_inode(), _add_range(), _del_range()) never hit
ext4_journal_check_start() and do write, leaving a partially applied
fast commit behind.
Reproducer, where the unlink only ever reaches the fast commit area:
mke2fs -q -F -t ext4 -O fast_commit -b 4096 /dev/sdb3 262144
mount /dev/sdb3 /mnt
dd if=/dev/zero of=/mnt/victim bs=4k count=1 conv=fsync
sync # victim now in a full commit
rm /mnt/victim
dd if=/dev/zero of=/mnt/trigger bs=4k count=1 conv=fsync
<crash, or snapshot the device while mounted and write it back>
mount -o ro /dev/sdb3 /mnt
Without this patch that mount fails; with it recovery completes and
victim is gone, i.e. the UNLINK record was really replayed.
Fixes: 8016e29f4362 ("ext4: fast commit recovery path")
Cc: stable@vger.kernel.org
Signed-off-by: Hsiu-Hsien Lee <swinds24@gmail.com>
---
fs/ext4/super.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 245f67d10ded..6c2b275a9cf3 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -6096,6 +6096,7 @@ static int ext4_load_journal(struct super_block *sb,
int err = 0;
int really_read_only;
int journal_dev_ro;
+ bool enable_write = false;
if (WARN_ON_ONCE(!ext4_has_feature_journal(sb)))
return -EFSCORRUPTED;
@@ -6152,6 +6153,7 @@ static int ext4_load_journal(struct super_block *sb,
}
ext4_msg(sb, KERN_INFO, "write access will "
"be enabled during recovery");
+ enable_write = true;
}
}
@@ -6168,7 +6170,19 @@ static int ext4_load_journal(struct super_block *sb,
if (save)
memcpy(save, ((char *) es) +
EXT4_S_ERR_START, EXT4_S_ERR_LEN);
+ /*
+ * Fast commit replay performs regular ext4 metadata updates
+ * (see ext4_fc_replay()) which refuse to run on a read-only
+ * superblock. We promised write access above, so make that
+ * true for the duration of the recovery, the same way
+ * ext4_orphan_cleanup() does. The superblock is not published
+ * yet, so nothing can observe the transient state.
+ */
+ if (enable_write)
+ sb->s_flags &= ~SB_RDONLY;
err = jbd2_journal_load(journal);
+ if (enable_write)
+ sb->s_flags |= SB_RDONLY;
if (save && memcmp(((char *) es) + EXT4_S_ERR_START,
save, EXT4_S_ERR_LEN)) {
memcpy(((char *) es) + EXT4_S_ERR_START,
--
2.43.0
next reply other threads:[~2026-08-12 4:44 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-12 4:43 Hsiu-Hsien Lee [this message]
2026-08-12 4:51 ` [PATCH] ext4: fix fast commit replay failing on a read-only mount sashiko-bot
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=20260812044353.1018268-1-swinds24@gmail.com \
--to=swinds24@gmail.com \
--cc=adilger.kernel@dilger.ca \
--cc=harshadshirwadkar@gmail.com \
--cc=jack@suse.cz \
--cc=libaokun@linux.alibaba.com \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ojaswin@linux.ibm.com \
--cc=ritesh.list@gmail.com \
--cc=stable@vger.kernel.org \
--cc=tytso@mit.edu \
--cc=yi.zhang@huawei.com \
/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 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.