Commit 8cd44dd1 authored by Naohiro Aota's avatar Naohiro Aota Committed by David Sterba
Browse files

btrfs: zoned: fix zone_unusable accounting on making block group read-write again



When btrfs makes a block group read-only, it adds all free regions in the
block group to space_info->bytes_readonly. That free space excludes
reserved and pinned regions. OTOH, when btrfs makes the block group
read-write again, it moves all the unused regions into the block group's
zone_unusable. That unused region includes reserved and pinned regions.
As a result, it counts too much zone_unusable bytes.

Fortunately (or unfortunately), having erroneous zone_unusable does not
affect the calculation of space_info->bytes_readonly, because free
space (num_bytes in btrfs_dec_block_group_ro) calculation is done based on
the erroneous zone_unusable and it reduces the num_bytes just to cancel the
error.

This behavior can be easily discovered by adding a WARN_ON to check e.g,
"bg->pinned > 0" in btrfs_dec_block_group_ro(), and running fstests test
case like btrfs/282.

Fix it by properly considering pinned and reserved in
btrfs_dec_block_group_ro(). Also, add a WARN_ON and introduce
btrfs_space_info_update_bytes_zone_unusable() to catch a similar mistake.

Fixes: 169e0da9 ("btrfs: zoned: track unusable bytes for zones")
CC: stable@vger.kernel.org # 5.15+
Signed-off-by: default avatarNaohiro Aota <naohiro.aota@wdc.com>
Reviewed-by: default avatarJosef Bacik <josef@toxicpanda.com>
Reviewed-by: default avatarJohannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent d89c285d
Loading
Loading
Loading
Loading
+8 −5
Original line number Diff line number Diff line
@@ -1223,8 +1223,8 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
	block_group->space_info->total_bytes -= block_group->length;
	block_group->space_info->bytes_readonly -=
		(block_group->length - block_group->zone_unusable);
	block_group->space_info->bytes_zone_unusable -=
		block_group->zone_unusable;
	btrfs_space_info_update_bytes_zone_unusable(fs_info, block_group->space_info,
						    -block_group->zone_unusable);
	block_group->space_info->disk_total -= block_group->length * factor;

	spin_unlock(&block_group->space_info->lock);
@@ -1396,7 +1396,8 @@ static int inc_block_group_ro(struct btrfs_block_group *cache, int force)
		if (btrfs_is_zoned(cache->fs_info)) {
			/* Migrate zone_unusable bytes to readonly */
			sinfo->bytes_readonly += cache->zone_unusable;
			sinfo->bytes_zone_unusable -= cache->zone_unusable;
			btrfs_space_info_update_bytes_zone_unusable(cache->fs_info, sinfo,
								    -cache->zone_unusable);
			cache->zone_unusable = 0;
		}
		cache->ro++;
@@ -3056,9 +3057,11 @@ void btrfs_dec_block_group_ro(struct btrfs_block_group *cache)
		if (btrfs_is_zoned(cache->fs_info)) {
			/* Migrate zone_unusable bytes back */
			cache->zone_unusable =
				(cache->alloc_offset - cache->used) +
				(cache->alloc_offset - cache->used - cache->pinned -
				 cache->reserved) +
				(cache->length - cache->zone_capacity);
			sinfo->bytes_zone_unusable += cache->zone_unusable;
			btrfs_space_info_update_bytes_zone_unusable(cache->fs_info, sinfo,
								    cache->zone_unusable);
			sinfo->bytes_readonly -= cache->zone_unusable;
		}
		num_bytes = cache->length - cache->reserved -
+2 −1
Original line number Diff line number Diff line
@@ -2793,7 +2793,8 @@ static int unpin_extent_range(struct btrfs_fs_info *fs_info,
			readonly = true;
		} else if (btrfs_is_zoned(fs_info)) {
			/* Need reset before reusing in a zoned block group */
			space_info->bytes_zone_unusable += len;
			btrfs_space_info_update_bytes_zone_unusable(fs_info, space_info,
								    len);
			readonly = true;
		}
		spin_unlock(&cache->lock);
+3 −1
Original line number Diff line number Diff line
@@ -2723,8 +2723,10 @@ static int __btrfs_add_free_space_zoned(struct btrfs_block_group *block_group,
	 * If the block group is read-only, we should account freed space into
	 * bytes_readonly.
	 */
	if (!block_group->ro)
	if (!block_group->ro) {
		block_group->zone_unusable += to_unusable;
		WARN_ON(block_group->zone_unusable > block_group->length);
	}
	spin_unlock(&ctl->tree_lock);
	if (!used) {
		spin_lock(&block_group->lock);
+1 −1
Original line number Diff line number Diff line
@@ -316,7 +316,7 @@ void btrfs_add_bg_to_space_info(struct btrfs_fs_info *info,
	found->bytes_used += block_group->used;
	found->disk_used += block_group->used * factor;
	found->bytes_readonly += block_group->bytes_super;
	found->bytes_zone_unusable += block_group->zone_unusable;
	btrfs_space_info_update_bytes_zone_unusable(info, found, block_group->zone_unusable);
	if (block_group->length > 0)
		found->full = 0;
	btrfs_try_granting_tickets(info, found);
+1 −0
Original line number Diff line number Diff line
@@ -249,6 +249,7 @@ btrfs_space_info_update_##name(struct btrfs_fs_info *fs_info, \

DECLARE_SPACE_INFO_UPDATE(bytes_may_use, "space_info");
DECLARE_SPACE_INFO_UPDATE(bytes_pinned, "pinned");
DECLARE_SPACE_INFO_UPDATE(bytes_zone_unusable, "zone_unusable");

int btrfs_init_space_info(struct btrfs_fs_info *fs_info);
void btrfs_add_bg_to_space_info(struct btrfs_fs_info *info,
Loading