[PATCH] ext4: propagate errors from fast commit bitmap rebuild

From: lty

Date: Tue Sep 15 2026 - 06:37:46 EST


ext4_fc_set_bitmaps_and_counters() rebuilds block bitmaps for modified
inodes during fast-commit replay. When ext4_map_blocks() fails, the helper
stops scanning but previously returned success. Errors from
ext4_find_extent() were also discarded, so replay continued after an
incomplete bitmap rebuild.

Return errors from the helper and propagate them through both replay call
sites so journal recovery aborts when bitmap reconstruction fails.

Fixes: 8016e29f4362 ("ext4: fast commit recovery path")
Cc: stable@xxxxxxxxxxxxxxx

Testing: With CONFIG_EXT4_DEBUG, QEMU fast-commit replay was run using a
temporary fault injection that forced -EIO in the bitmap rebuild mapping
call. The unmodified kernel reported recovery complete and mounted the
filesystem; the patched kernel reported journal recovery failure and mount
returned EIO. Built fs/ext4/fast_commit.o and a complete x86 bzImage.

Signed-off-by: lty <781735889@xxxxxx>
---
fs/ext4/fast_commit.c | 26 +++++++++++++++-----------
1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c
index 0cac890cf370..274d4b95ed17 100644
--- a/fs/ext4/fast_commit.c
+++ b/fs/ext4/fast_commit.c
@@ -2314,13 +2314,13 @@ ext4_fc_replay_del_range(struct super_block *sb, u8 *val)
return ret;
}

-static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
+static int ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
{
struct ext4_fc_replay_state *state;
struct inode *inode;
struct ext4_ext_path *path = NULL;
struct ext4_map_blocks map;
- int i, ret, j;
+ int i, ret = 0, j;
ext4_lblk_t cur, end;

state = &EXT4_SB(sb)->s_fc_replay_state;
@@ -2348,13 +2348,14 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)

if (ret > 0) {
path = ext4_find_extent(inode, map.m_lblk, path, 0);
- if (!IS_ERR(path)) {
- for (j = 0; j < path->p_depth; j++)
- ext4_mb_mark_bb(inode->i_sb,
- path[j].p_block, 1, true);
- } else {
+ if (IS_ERR(path)) {
+ ret = PTR_ERR(path);
path = NULL;
+ break;
}
+ for (j = 0; j < path->p_depth; j++)
+ ext4_mb_mark_bb(inode->i_sb,
+ path[j].p_block, 1, true);
cur += ret;
ext4_mb_mark_bb(inode->i_sb, map.m_pblk,
map.m_len, true);
@@ -2363,9 +2364,14 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
}
}
iput(inode);
+ if (ret < 0)
+ goto out;
}

+ ret = 0;
+out:
ext4_free_ext_path(path);
+ return ret;
}

/*
@@ -2589,8 +2595,7 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,
}
if (!sbi->s_fc_replay_state.fc_replay_num_tags) {
ext4_debug("Replay stops\n");
- ext4_fc_set_bitmaps_and_counters(sb);
- return 0;
+ return ext4_fc_set_bitmaps_and_counters(sb);
}

#ifdef CONFIG_EXT4_DEBUG
@@ -2609,8 +2614,7 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh,
val = cur + EXT4_FC_TAG_BASE_LEN;

if (state->fc_replay_num_tags == 0) {
- ret = JBD2_FC_REPLAY_STOP;
- ext4_fc_set_bitmaps_and_counters(sb);
+ ret = ext4_fc_set_bitmaps_and_counters(sb);
break;
}

--
2.34.1