Skip to content

Commit 2331fd4

Browse files
LiBaokun96tytso
authored andcommitted
ext4: avoid bb_free and bb_fragments inconsistency in mb_free_blocks()
After updating bb_free in mb_free_blocks, it is possible to return without updating bb_fragments because the block being freed is found to have already been freed, which leads to inconsistency between bb_free and bb_fragments. Since the group may be unlocked in ext4_grp_locked_error(), this can lead to problems such as dividing by zero when calculating the average fragment length. Hence move the update of bb_free to after the block double-free check guarantees that the corresponding statistics are updated only after the core block bitmap is modified. Fixes: eabe044 ("ext4: speed-up releasing blocks on commit") CC: <stable@vger.kernel.org> # 3.10 Suggested-by: Jan Kara <jack@suse.cz> Signed-off-by: Baokun Li <libaokun1@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/20240104142040.2835097-5-libaokun1@huawei.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
1 parent c9b528c commit 2331fd4

File tree

1 file changed

+21
-18
lines changed

1 file changed

+21
-18
lines changed

fs/ext4/mballoc.c

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,11 +1909,6 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
19091909
mb_check_buddy(e4b);
19101910
mb_free_blocks_double(inode, e4b, first, count);
19111911

1912-
this_cpu_inc(discard_pa_seq);
1913-
e4b->bd_info->bb_free += count;
1914-
if (first < e4b->bd_info->bb_first_free)
1915-
e4b->bd_info->bb_first_free = first;
1916-
19171912
/* access memory sequentially: check left neighbour,
19181913
* clear range and then check right neighbour
19191914
*/
@@ -1927,23 +1922,31 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
19271922
struct ext4_sb_info *sbi = EXT4_SB(sb);
19281923
ext4_fsblk_t blocknr;
19291924

1925+
/*
1926+
* Fastcommit replay can free already freed blocks which
1927+
* corrupts allocation info. Regenerate it.
1928+
*/
1929+
if (sbi->s_mount_state & EXT4_FC_REPLAY) {
1930+
mb_regenerate_buddy(e4b);
1931+
goto check;
1932+
}
1933+
19301934
blocknr = ext4_group_first_block_no(sb, e4b->bd_group);
19311935
blocknr += EXT4_C2B(sbi, block);
1932-
if (!(sbi->s_mount_state & EXT4_FC_REPLAY)) {
1933-
ext4_grp_locked_error(sb, e4b->bd_group,
1934-
inode ? inode->i_ino : 0,
1935-
blocknr,
1936-
"freeing already freed block (bit %u); block bitmap corrupt.",
1937-
block);
1938-
ext4_mark_group_bitmap_corrupted(
1939-
sb, e4b->bd_group,
1936+
ext4_grp_locked_error(sb, e4b->bd_group,
1937+
inode ? inode->i_ino : 0, blocknr,
1938+
"freeing already freed block (bit %u); block bitmap corrupt.",
1939+
block);
1940+
ext4_mark_group_bitmap_corrupted(sb, e4b->bd_group,
19401941
EXT4_GROUP_INFO_BBITMAP_CORRUPT);
1941-
} else {
1942-
mb_regenerate_buddy(e4b);
1943-
}
1944-
goto done;
1942+
return;
19451943
}
19461944

1945+
this_cpu_inc(discard_pa_seq);
1946+
e4b->bd_info->bb_free += count;
1947+
if (first < e4b->bd_info->bb_first_free)
1948+
e4b->bd_info->bb_first_free = first;
1949+
19471950
/* let's maintain fragments counter */
19481951
if (left_is_free && right_is_free)
19491952
e4b->bd_info->bb_fragments--;
@@ -1968,9 +1971,9 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
19681971
if (first <= last)
19691972
mb_buddy_mark_free(e4b, first >> 1, last >> 1);
19701973

1971-
done:
19721974
mb_set_largest_free_order(sb, e4b->bd_info);
19731975
mb_update_avg_fragment_size(sb, e4b->bd_info);
1976+
check:
19741977
mb_check_buddy(e4b);
19751978
}
19761979

0 commit comments

Comments
 (0)