btrfs: zoned: activate metadata block group on flush_space

For metadata space on zoned filesystem, reaching ALLOC_CHUNK{,_FORCE}
means we don't have enough space left in the active_total_bytes. Before
allocating a new chunk, we can try to activate an existing block group
in this case.

Also, allocating a chunk is not enough to grant a ticket for metadata
space on zoned filesystem we need to activate the block group to
increase the active_total_bytes.

btrfs_zoned_activate_one_bg() implements the activation feature. It will
activate a block group by (maybe) finishing a block group. It will give up
activating a block group if it cannot finish any block group.

CC: stable@vger.kernel.org # 5.16+
Fixes: afba2bc036 ("btrfs: zoned: implement active zone tracking")
Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Naohiro Aota 2022-07-09 08:18:47 +09:00 committed by David Sterba
parent 79417d040f
commit b093151391
3 changed files with 93 additions and 0 deletions

View File

@ -9,6 +9,7 @@
#include "ordered-data.h" #include "ordered-data.h"
#include "transaction.h" #include "transaction.h"
#include "block-group.h" #include "block-group.h"
#include "zoned.h"
/* /*
* HOW DOES SPACE RESERVATION WORK * HOW DOES SPACE RESERVATION WORK
@ -724,6 +725,18 @@ static void flush_space(struct btrfs_fs_info *fs_info,
break; break;
case ALLOC_CHUNK: case ALLOC_CHUNK:
case ALLOC_CHUNK_FORCE: case ALLOC_CHUNK_FORCE:
/*
* For metadata space on zoned filesystem, reaching here means we
* don't have enough space left in active_total_bytes. Try to
* activate a block group first, because we may have inactive
* block group already allocated.
*/
ret = btrfs_zoned_activate_one_bg(fs_info, space_info, false);
if (ret < 0)
break;
else if (ret == 1)
break;
trans = btrfs_join_transaction(root); trans = btrfs_join_transaction(root);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
ret = PTR_ERR(trans); ret = PTR_ERR(trans);
@ -734,6 +747,23 @@ static void flush_space(struct btrfs_fs_info *fs_info,
(state == ALLOC_CHUNK) ? CHUNK_ALLOC_NO_FORCE : (state == ALLOC_CHUNK) ? CHUNK_ALLOC_NO_FORCE :
CHUNK_ALLOC_FORCE); CHUNK_ALLOC_FORCE);
btrfs_end_transaction(trans); btrfs_end_transaction(trans);
/*
* For metadata space on zoned filesystem, allocating a new chunk
* is not enough. We still need to activate the block * group.
* Active the newly allocated block group by (maybe) finishing
* a block group.
*/
if (ret == 1) {
ret = btrfs_zoned_activate_one_bg(fs_info, space_info, true);
/*
* Revert to the original ret regardless we could finish
* one block group or not.
*/
if (ret >= 0)
ret = 1;
}
if (ret > 0 || ret == -ENOSPC) if (ret > 0 || ret == -ENOSPC)
ret = 0; ret = 0;
break; break;

View File

@ -2226,3 +2226,56 @@ int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info)
return ret < 0 ? ret : 1; return ret < 0 ? ret : 1;
} }
int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info,
struct btrfs_space_info *space_info,
bool do_finish)
{
struct btrfs_block_group *bg;
int index;
if (!btrfs_is_zoned(fs_info) || (space_info->flags & BTRFS_BLOCK_GROUP_DATA))
return 0;
/* No more block groups to activate */
if (space_info->active_total_bytes == space_info->total_bytes)
return 0;
for (;;) {
int ret;
bool need_finish = false;
down_read(&space_info->groups_sem);
for (index = 0; index < BTRFS_NR_RAID_TYPES; index++) {
list_for_each_entry(bg, &space_info->block_groups[index],
list) {
if (!spin_trylock(&bg->lock))
continue;
if (btrfs_zoned_bg_is_full(bg) || bg->zone_is_active) {
spin_unlock(&bg->lock);
continue;
}
spin_unlock(&bg->lock);
if (btrfs_zone_activate(bg)) {
up_read(&space_info->groups_sem);
return 1;
}
need_finish = true;
}
}
up_read(&space_info->groups_sem);
if (!do_finish || !need_finish)
break;
ret = btrfs_zone_finish_one_bg(fs_info);
if (ret == 0)
break;
if (ret < 0)
return ret;
}
return 0;
}

View File

@ -81,6 +81,8 @@ bool btrfs_zoned_should_reclaim(struct btrfs_fs_info *fs_info);
void btrfs_zoned_release_data_reloc_bg(struct btrfs_fs_info *fs_info, u64 logical, void btrfs_zoned_release_data_reloc_bg(struct btrfs_fs_info *fs_info, u64 logical,
u64 length); u64 length);
int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info); int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info);
int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info,
struct btrfs_space_info *space_info, bool do_finish);
#else /* CONFIG_BLK_DEV_ZONED */ #else /* CONFIG_BLK_DEV_ZONED */
static inline int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos, static inline int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,
struct blk_zone *zone) struct blk_zone *zone)
@ -256,6 +258,14 @@ static inline int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info)
return 1; return 1;
} }
static inline int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info,
struct btrfs_space_info *space_info,
bool do_finish)
{
/* Consider all the block groups are active */
return 0;
}
#endif #endif
static inline bool btrfs_dev_is_sequential(struct btrfs_device *device, u64 pos) static inline bool btrfs_dev_is_sequential(struct btrfs_device *device, u64 pos)