| From 4865c768b563deff1b6a6384e74a62f143427b42 Mon Sep 17 00:00:00 2001 |
| From: Jan Kara <jack@suse.cz> |
| Date: Wed, 14 Jan 2026 19:28:18 +0100 |
| Subject: ext4: always allocate blocks only from groups inode can use |
| |
| From: Jan Kara <jack@suse.cz> |
| |
| commit 4865c768b563deff1b6a6384e74a62f143427b42 upstream. |
| |
| For filesystems with more than 2^32 blocks inodes using indirect block |
| based format cannot use blocks beyond the 32-bit limit. |
| ext4_mb_scan_groups_linear() takes care to not select these unsupported |
| groups for such inodes however other functions selecting groups for |
| allocation don't. So far this is harmless because the other selection |
| functions are used only with mb_optimize_scan and this is currently |
| disabled for inodes with indirect blocks however in the following patch |
| we want to enable mb_optimize_scan regardless of inode format. |
| |
| Reviewed-by: Baokun Li <libaokun1@huawei.com> |
| Reviewed-by: Zhang Yi <yi.zhang@huawei.com> |
| Signed-off-by: Jan Kara <jack@suse.cz> |
| Acked-by: Pedro Falcato <pfalcato@suse.de> |
| Cc: stable@kernel.org |
| Link: https://patch.msgid.link/20260114182836.14120-3-jack@suse.cz |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/ext4/mballoc.c | 29 ++++++++++++++++++++--------- |
| 1 file changed, 20 insertions(+), 9 deletions(-) |
| |
| --- a/fs/ext4/mballoc.c |
| +++ b/fs/ext4/mballoc.c |
| @@ -892,6 +892,21 @@ mb_update_avg_fragment_size(struct super |
| } |
| } |
| |
| +static ext4_group_t ext4_get_allocation_groups_count( |
| + struct ext4_allocation_context *ac) |
| +{ |
| + ext4_group_t ngroups = ext4_get_groups_count(ac->ac_sb); |
| + |
| + /* non-extent files are limited to low blocks/groups */ |
| + if (!(ext4_test_inode_flag(ac->ac_inode, EXT4_INODE_EXTENTS))) |
| + ngroups = EXT4_SB(ac->ac_sb)->s_blockfile_groups; |
| + |
| + /* Pairs with smp_wmb() in ext4_update_super() */ |
| + smp_rmb(); |
| + |
| + return ngroups; |
| +} |
| + |
| static int ext4_mb_scan_groups_xa_range(struct ext4_allocation_context *ac, |
| struct xarray *xa, |
| ext4_group_t start, ext4_group_t end) |
| @@ -899,7 +914,7 @@ static int ext4_mb_scan_groups_xa_range( |
| struct super_block *sb = ac->ac_sb; |
| struct ext4_sb_info *sbi = EXT4_SB(sb); |
| enum criteria cr = ac->ac_criteria; |
| - ext4_group_t ngroups = ext4_get_groups_count(sb); |
| + ext4_group_t ngroups = ext4_get_allocation_groups_count(ac); |
| unsigned long group = start; |
| struct ext4_group_info *grp; |
| |
| @@ -951,7 +966,7 @@ static int ext4_mb_scan_groups_p2_aligne |
| ext4_group_t start, end; |
| |
| start = group; |
| - end = ext4_get_groups_count(ac->ac_sb); |
| + end = ext4_get_allocation_groups_count(ac); |
| wrap_around: |
| for (i = ac->ac_2order; i < MB_NUM_ORDERS(ac->ac_sb); i++) { |
| ret = ext4_mb_scan_groups_largest_free_order_range(ac, i, |
| @@ -1001,7 +1016,7 @@ static int ext4_mb_scan_groups_goal_fast |
| ext4_group_t start, end; |
| |
| start = group; |
| - end = ext4_get_groups_count(ac->ac_sb); |
| + end = ext4_get_allocation_groups_count(ac); |
| wrap_around: |
| i = mb_avg_fragment_size_order(ac->ac_sb, ac->ac_g_ex.fe_len); |
| for (; i < MB_NUM_ORDERS(ac->ac_sb); i++) { |
| @@ -1083,7 +1098,7 @@ static int ext4_mb_scan_groups_best_avai |
| min_order = fls(ac->ac_o_ex.fe_len); |
| |
| start = group; |
| - end = ext4_get_groups_count(ac->ac_sb); |
| + end = ext4_get_allocation_groups_count(ac); |
| wrap_around: |
| for (i = order; i >= min_order; i--) { |
| int frag_order; |
| @@ -1182,11 +1197,7 @@ static int ext4_mb_scan_groups(struct ex |
| int ret = 0; |
| ext4_group_t start; |
| struct ext4_sb_info *sbi = EXT4_SB(ac->ac_sb); |
| - ext4_group_t ngroups = ext4_get_groups_count(ac->ac_sb); |
| - |
| - /* non-extent files are limited to low blocks/groups */ |
| - if (!(ext4_test_inode_flag(ac->ac_inode, EXT4_INODE_EXTENTS))) |
| - ngroups = sbi->s_blockfile_groups; |
| + ext4_group_t ngroups = ext4_get_allocation_groups_count(ac); |
| |
| /* searching for the right group start from the goal value specified */ |
| start = ac->ac_g_ex.fe_group; |