btrfs: zoned: do not remove unwritten non-data block group

commit 3061801420 upstream.

There are some reports of "unable to find chunk map for logical 2147483648
length 16384" error message appears in dmesg. This means some IOs are
occurring after a block group is removed.

When a metadata tree node is cleaned on a zoned setup, we keep that node
still dirty and write it out not to create a write hole. However, this can
make a block group's used bytes == 0 while there is a dirty region left.

Such an unused block group is moved into the unused_bg list and processed
for removal. When the removal succeeds, the block group is removed from the
transaction->dirty_bgs list, so the unused dirty nodes in the block group
are not sent at the transaction commit time. It will be written at some
later time e.g, sync or umount, and causes "unable to find chunk map"
errors.

This can happen relatively easy on SMR whose zone size is 256MB. However,
calling do_zone_finish() on such block group returns -EAGAIN and keep that
block group intact, which is why the issue is hidden until now.

Fixes: afba2bc036 ("btrfs: zoned: implement active zone tracking")
CC: stable@vger.kernel.org # 6.1+
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Naohiro Aota 2025-06-29 23:07:42 +09:00 committed by Greg Kroah-Hartman
parent 940baded5e
commit 184fd94b13

View File

@ -46,6 +46,19 @@ static u64 get_restripe_target(struct btrfs_fs_info *fs_info, u64 flags)
return target;
}
static inline bool has_unwritten_metadata(struct btrfs_block_group *block_group)
{
/* The meta_write_pointer is available only on the zoned setup. */
if (!btrfs_is_zoned(block_group->fs_info))
return false;
if (block_group->flags & BTRFS_BLOCK_GROUP_DATA)
return false;
return block_group->start + block_group->alloc_offset >
block_group->meta_write_pointer;
}
/*
* @flags: available profiles in extended format (see ctree.h)
*
@ -1091,6 +1104,15 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
goto out;
spin_lock(&block_group->lock);
/*
* Hitting this WARN means we removed a block group with an unwritten
* region. It will cause "unable to find chunk map for logical" errors.
*/
if (WARN_ON(has_unwritten_metadata(block_group)))
btrfs_warn(fs_info,
"block group %llu is removed before metadata write out",
block_group->start);
set_bit(BLOCK_GROUP_FLAG_REMOVED, &block_group->runtime_flags);
/*
@ -1414,8 +1436,9 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
* needing to allocate extents from the block group.
*/
used = btrfs_space_info_used(space_info, true);
if (space_info->total_bytes - block_group->length < used &&
block_group->zone_unusable < block_group->length) {
if ((space_info->total_bytes - block_group->length < used &&
block_group->zone_unusable < block_group->length) ||
has_unwritten_metadata(block_group)) {
/*
* Add a reference for the list, compensate for the ref
* drop under the "next" label for the