btrfs: handle setting up relocation of block group with remap-tree

Handle the preliminary work for relocating a block group in a filesystem
with the remap-tree flag set.

If the block group is SYSTEM btrfs_relocate_block_group() proceeds as it
does already, as bootstrapping issues mean that these block groups have
to be processed the existing way. Similarly with METADATA_REMAP blocks, which
are dealt with in a later patch.

Otherwise we walk the free-space tree for the block group in question,
recording any holes. These get converted into identity remaps and placed
in the remap tree, and the block group's REMAPPED flag is set. From now
on no new allocations are possible within this block group, and any I/O
to it will be funnelled through btrfs_translate_remap(). We store the
number of identity remaps in `identity_remap_count`, so that we know
when we've removed the last one and the block group is fully remapped.

The change in btrfs_read_roots() is because data relocations no longer
rely on the data reloc tree as a hidden subvolume in which to do
snapshots.

(Thanks to Sun YangKai for his suggestions.)

Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Mark Harmstone <mark@harmstone.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Mark Harmstone
2026-02-03 07:54:35 +01:00
committed by David Sterba
parent 979e1dc3d6
commit b56f35560b
8 changed files with 532 additions and 93 deletions
+5 -1
View File
@@ -2396,6 +2396,7 @@ static int read_one_block_group(struct btrfs_fs_info *info,
cache->used = btrfs_stack_block_group_v2_used(bgi);
cache->last_used = cache->used;
cache->flags = btrfs_stack_block_group_v2_flags(bgi);
cache->last_flags = cache->flags;
cache->global_root_id = btrfs_stack_block_group_v2_chunk_objectid(bgi);
cache->space_info = btrfs_find_space_info(info, cache->flags);
cache->remap_bytes = btrfs_stack_block_group_v2_remap_bytes(bgi);
@@ -2700,6 +2701,7 @@ static int insert_block_group_item(struct btrfs_trans_handle *trans,
block_group->last_used = block_group->used;
block_group->last_remap_bytes = block_group->remap_bytes;
block_group->last_identity_remap_count = block_group->identity_remap_count;
block_group->last_flags = block_group->flags;
key.objectid = block_group->start;
key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
key.offset = block_group->length;
@@ -3184,13 +3186,15 @@ static int update_block_group_item(struct btrfs_trans_handle *trans,
/* No change in values, can safely skip it. */
if (cache->last_used == used &&
cache->last_remap_bytes == remap_bytes &&
cache->last_identity_remap_count == identity_remap_count) {
cache->last_identity_remap_count == identity_remap_count &&
cache->last_flags == cache->flags) {
spin_unlock(&cache->lock);
return 0;
}
cache->last_used = used;
cache->last_remap_bytes = remap_bytes;
cache->last_identity_remap_count = identity_remap_count;
cache->last_flags = cache->flags;
spin_unlock(&cache->lock);
key.objectid = cache->start;
+2
View File
@@ -143,6 +143,8 @@ struct btrfs_block_group {
u64 last_remap_bytes;
/* The last commited identity_remap_count value of this block group. */
u32 last_identity_remap_count;
/* The last committed flags value for this block group. */
u64 last_flags;
/*
* If the free space extent count exceeds this number, convert the block
+1 -3
View File
@@ -21,8 +21,7 @@ static int __add_block_group_free_space(struct btrfs_trans_handle *trans,
struct btrfs_block_group *block_group,
struct btrfs_path *path);
static struct btrfs_root *btrfs_free_space_root(
struct btrfs_block_group *block_group)
struct btrfs_root *btrfs_free_space_root(struct btrfs_block_group *block_group)
{
struct btrfs_key key = {
.objectid = BTRFS_FREE_SPACE_TREE_OBJECTID,
@@ -93,7 +92,6 @@ static int add_new_free_space_info(struct btrfs_trans_handle *trans,
return 0;
}
EXPORT_FOR_TESTS
struct btrfs_free_space_info *btrfs_search_free_space_info(
struct btrfs_trans_handle *trans,
struct btrfs_block_group *block_group,
+3 -2
View File
@@ -36,12 +36,13 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans,
int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans,
u64 start, u64 size);
int btrfs_delete_orphan_free_space_entries(struct btrfs_fs_info *fs_info);
#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
struct btrfs_free_space_info *
btrfs_search_free_space_info(struct btrfs_trans_handle *trans,
struct btrfs_block_group *block_group,
struct btrfs_path *path, int cow);
struct btrfs_root *btrfs_free_space_root(struct btrfs_block_group *block_group);
#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
int __btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans,
struct btrfs_block_group *block_group,
struct btrfs_path *path, u64 start, u64 size);
+451 -53
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -12,6 +12,17 @@ struct btrfs_trans_handle;
struct btrfs_ordered_extent;
struct btrfs_pending_snapshot;
static inline bool should_relocate_using_remap_tree(const struct btrfs_block_group *bg)
{
if (!btrfs_fs_incompat(bg->fs_info, REMAP_TREE))
return false;
if (bg->flags & (BTRFS_BLOCK_GROUP_SYSTEM | BTRFS_BLOCK_GROUP_METADATA_REMAP))
return false;
return true;
}
int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start,
bool verbose);
int btrfs_init_reloc_root(struct btrfs_trans_handle *trans, struct btrfs_root *root);
+7 -2
View File
@@ -380,8 +380,13 @@ void btrfs_add_bg_to_space_info(struct btrfs_fs_info *info,
factor = btrfs_bg_type_to_factor(block_group->flags);
spin_lock(&space_info->lock);
space_info->total_bytes += block_group->length;
space_info->disk_total += block_group->length * factor;
if (!(block_group->flags & BTRFS_BLOCK_GROUP_REMAPPED) ||
block_group->identity_remap_count != 0) {
space_info->total_bytes += block_group->length;
space_info->disk_total += block_group->length * factor;
}
space_info->bytes_used += block_group->used;
space_info->disk_used += block_group->used * factor;
space_info->bytes_readonly += block_group->bytes_super;
+52 -32
View File
@@ -3400,15 +3400,50 @@ out:
return ret;
}
int btrfs_relocate_chunk(struct btrfs_fs_info *fs_info, u64 chunk_offset,
bool verbose)
static int btrfs_relocate_chunk_finish(struct btrfs_fs_info *fs_info,
struct btrfs_block_group *bg)
{
struct btrfs_root *root = fs_info->chunk_root;
struct btrfs_trans_handle *trans;
struct btrfs_block_group *block_group;
u64 length;
int ret;
btrfs_discard_cancel_work(&fs_info->discard_ctl, bg);
length = bg->length;
btrfs_put_block_group(bg);
/*
* On a zoned file system, discard the whole block group, this will
* trigger a REQ_OP_ZONE_RESET operation on the device zone. If
* resetting the zone fails, don't treat it as a fatal problem from the
* filesystem's point of view.
*/
if (btrfs_is_zoned(fs_info)) {
ret = btrfs_discard_extent(fs_info, bg->start, length, NULL);
if (ret)
btrfs_info(fs_info, "failed to reset zone %llu after relocation",
bg->start);
}
trans = btrfs_start_trans_remove_block_group(root->fs_info, bg->start);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
btrfs_handle_fs_error(root->fs_info, ret, NULL);
return ret;
}
/* Step two, delete the device extents and the chunk tree entries. */
ret = btrfs_remove_chunk(trans, bg->start);
btrfs_end_transaction(trans);
return ret;
}
int btrfs_relocate_chunk(struct btrfs_fs_info *fs_info, u64 chunk_offset, bool verbose)
{
struct btrfs_block_group *block_group;
int ret;
if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2)) {
btrfs_err(fs_info,
"relocate: not supported on extent tree v2 yet");
@@ -3446,38 +3481,15 @@ int btrfs_relocate_chunk(struct btrfs_fs_info *fs_info, u64 chunk_offset,
block_group = btrfs_lookup_block_group(fs_info, chunk_offset);
if (!block_group)
return -ENOENT;
btrfs_discard_cancel_work(&fs_info->discard_ctl, block_group);
length = block_group->length;
btrfs_put_block_group(block_group);
/*
* On a zoned file system, discard the whole block group, this will
* trigger a REQ_OP_ZONE_RESET operation on the device zone. If
* resetting the zone fails, don't treat it as a fatal problem from the
* filesystem's point of view.
*/
if (btrfs_is_zoned(fs_info)) {
ret = btrfs_discard_extent(fs_info, chunk_offset, length, NULL);
if (ret)
btrfs_info(fs_info,
"failed to reset zone %llu after relocation",
chunk_offset);
if (should_relocate_using_remap_tree(block_group)) {
/* If we're relocating using the remap tree we're now done. */
btrfs_put_block_group(block_group);
ret = 0;
} else {
ret = btrfs_relocate_chunk_finish(fs_info, block_group);
}
trans = btrfs_start_trans_remove_block_group(root->fs_info,
chunk_offset);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
btrfs_handle_fs_error(root->fs_info, ret, NULL);
return ret;
}
/*
* step two, delete the device extents and the
* chunk tree entries
*/
ret = btrfs_remove_chunk(trans, chunk_offset);
btrfs_end_transaction(trans);
return ret;
}
@@ -4150,6 +4162,14 @@ again:
chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk);
chunk_type = btrfs_chunk_type(leaf, chunk);
/* Check if chunk has already been fully relocated. */
if (chunk_type & BTRFS_BLOCK_GROUP_REMAPPED &&
btrfs_chunk_num_stripes(leaf, chunk) == 0) {
btrfs_release_path(path);
mutex_unlock(&fs_info->reclaim_bgs_lock);
goto loop;
}
if (!counting) {
spin_lock(&fs_info->balance_lock);
bctl->stat.considered++;