]> git.hungrycats.org Git - linux/commit
btrfs: keep unused block groups queued when a pass fails
authorBoris Burkov <boris@bur.io>
Wed, 16 Sep 2026 22:17:24 +0000 (15:17 -0700)
committerDavid Sterba <dsterba@suse.com>
Thu, 17 Sep 2026 17:46:46 +0000 (19:46 +0200)
commite8d38dd63712671a2d1da52988d6a5e6ef541d84
treeaccaea9f239138bef532337c85cf5214afa77e45
parent4a8aae3d14fc6c8022d0196853be3e46c4037d39
btrfs: keep unused block groups queued when a pass fails

Once any block_group sets ret!=0 in the main loop of
btrfs_delete_unused_bgs(), the check
  if (ret || btrfs_mixed_space_info(space_info)) {
          btrfs_put_block_group(block_group);
          continue;
  }
skips the rest of the unused bgs while unlinking them from
fs_info->unused_bgs. There is no "level triggered" re-queueing of empty
block groups onto fs_info->unused_bgs so it is possible to leak quite a
bit of space this way and unless we happen to get a balance or
re-use/re-empty one of these bgs, they are leaked for good, which can
lead to a spurious enospc later.

While I have observed such leaked blocked groups that are empty but not
on the unused_bgs list on production systems, I have not observed that
it is definitely due to this issue. I also reproduced this behavior by
injecting an ENOSPC error from btrfs_start_trans_remove_block_group
which can also fail with ENOMEM, so this feels like a legitimate
injection point.

To fix it, instead of checking ret in the loop, just break out of the
loop when ret != 0. Also, link the bg to the retry list at the
individual failure sites so that the failing bg is not leaked.

Assisted-by: LLM (reproducer/error injection)
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Boris Burkov <boris@bur.io>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/block-group.c