Skip to content

Commit 7d345aa

Browse files
LiBaokun96tytso
authored andcommitted
ext4: fix largest free orders lists corruption on mb_optimize_scan switch
The grp->bb_largest_free_order is updated regardless of whether mb_optimize_scan is enabled. This can lead to inconsistencies between grp->bb_largest_free_order and the actual s_mb_largest_free_orders list index when mb_optimize_scan is repeatedly enabled and disabled via remount. For example, if mb_optimize_scan is initially enabled, largest free order is 3, and the group is in s_mb_largest_free_orders[3]. Then, mb_optimize_scan is disabled via remount, block allocations occur, updating largest free order to 2. Finally, mb_optimize_scan is re-enabled via remount, more block allocations update largest free order to 1. At this point, the group would be removed from s_mb_largest_free_orders[3] under the protection of s_mb_largest_free_orders_locks[2]. This lock mismatch can lead to list corruption. To fix this, whenever grp->bb_largest_free_order changes, we now always attempt to remove the group from its old order list. However, we only insert the group into the new order list if `mb_optimize_scan` is enabled. This approach helps prevent lock inconsistencies and ensures the data in the order lists remains reliable. Fixes: 196e402 ("ext4: improve cr 0 / cr 1 group scanning") CC: [email protected] Suggested-by: Jan Kara <[email protected]> Signed-off-by: Baokun Li <[email protected]> Reviewed-by: Zhang Yi <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent 1c320d8 commit 7d345aa

File tree

1 file changed

+14
-19
lines changed

1 file changed

+14
-19
lines changed

fs/ext4/mballoc.c

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,33 +1152,28 @@ static void
11521152
mb_set_largest_free_order(struct super_block *sb, struct ext4_group_info *grp)
11531153
{
11541154
struct ext4_sb_info *sbi = EXT4_SB(sb);
1155-
int i;
1155+
int new, old = grp->bb_largest_free_order;
11561156

1157-
for (i = MB_NUM_ORDERS(sb) - 1; i >= 0; i--)
1158-
if (grp->bb_counters[i] > 0)
1157+
for (new = MB_NUM_ORDERS(sb) - 1; new >= 0; new--)
1158+
if (grp->bb_counters[new] > 0)
11591159
break;
1160+
11601161
/* No need to move between order lists? */
1161-
if (!test_opt2(sb, MB_OPTIMIZE_SCAN) ||
1162-
i == grp->bb_largest_free_order) {
1163-
grp->bb_largest_free_order = i;
1162+
if (new == old)
11641163
return;
1165-
}
11661164

1167-
if (grp->bb_largest_free_order >= 0) {
1168-
write_lock(&sbi->s_mb_largest_free_orders_locks[
1169-
grp->bb_largest_free_order]);
1165+
if (old >= 0 && !list_empty(&grp->bb_largest_free_order_node)) {
1166+
write_lock(&sbi->s_mb_largest_free_orders_locks[old]);
11701167
list_del_init(&grp->bb_largest_free_order_node);
1171-
write_unlock(&sbi->s_mb_largest_free_orders_locks[
1172-
grp->bb_largest_free_order]);
1168+
write_unlock(&sbi->s_mb_largest_free_orders_locks[old]);
11731169
}
1174-
grp->bb_largest_free_order = i;
1175-
if (grp->bb_largest_free_order >= 0 && grp->bb_free) {
1176-
write_lock(&sbi->s_mb_largest_free_orders_locks[
1177-
grp->bb_largest_free_order]);
1170+
1171+
grp->bb_largest_free_order = new;
1172+
if (test_opt2(sb, MB_OPTIMIZE_SCAN) && new >= 0 && grp->bb_free) {
1173+
write_lock(&sbi->s_mb_largest_free_orders_locks[new]);
11781174
list_add_tail(&grp->bb_largest_free_order_node,
1179-
&sbi->s_mb_largest_free_orders[grp->bb_largest_free_order]);
1180-
write_unlock(&sbi->s_mb_largest_free_orders_locks[
1181-
grp->bb_largest_free_order]);
1175+
&sbi->s_mb_largest_free_orders[new]);
1176+
write_unlock(&sbi->s_mb_largest_free_orders_locks[new]);
11821177
}
11831178
}
11841179

0 commit comments

Comments
 (0)