mirror of
https://github.com/kdave/btrfs-progs
synced 2025-04-08 18:21:45 +00:00
btrfs-progs: tune: add new option to convert back to extent tree
With previous btrfstune support to convert to block-group-tree, it has implemented most of the infrastructure for bi-directional conversion. This patch will implement the remaining conversion support to go back to extent tree. The modification includes: - New convert_to_extent_tree() function in btrfstune.c It's almost the same as convert_to_bg_tree(), but with small changes: * No need to set extra features like NO_HOLES/FST. * Need to delete the block group tree when everything finished. - Update btrfs_delete_and_free_root() to handle non-global roots Currently the function can only accepts global roots (extent/csum/free space trees) If we pass a non-global root into the function, we will screw up global_roots_tree and crash. Since we're going to use btrfs_delete_and_free_root() to free block group tree which is not a global tree, this is needed. - New handling for half converted fs in get_last_converted_bg() There are two cases need to be handled: * The bg tree is already empty We need to grab the first bg in extent tree. Or at conversion function we will fail at grabbing the first bg. * The bg tree is not empty Then we need to grab the last bg in extent tree. - Extra root switching in involved functions. This involves: * read_converting_block_groups() * insert_block_group_item() * update_block_group_item() We just need to update our target root according to the current compat_ro and super flags. Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
5a0398bac5
commit
68a04bc710
@ -29,6 +29,12 @@ OPTIONS
|
||||
Enable block group tree feature (greatly reduce mount time),
|
||||
enabled by mkfs feature *block-group-tree*.
|
||||
|
||||
--disable-block-group-tree
|
||||
(since kernel 6.1)
|
||||
|
||||
Disable block group tree feature and convert the filesystem to extent
|
||||
tree.
|
||||
|
||||
-f
|
||||
Allow dangerous changes, e.g. clear the seeding flag or change fsid.
|
||||
Make sure that you are aware of the dangers.
|
||||
|
@ -2282,6 +2282,14 @@ int btrfs_set_buffer_uptodate(struct extent_buffer *eb)
|
||||
return set_extent_buffer_uptodate(eb);
|
||||
}
|
||||
|
||||
static bool is_global_root(struct btrfs_root *root)
|
||||
{
|
||||
if (root->root_key.objectid == BTRFS_EXTENT_TREE_OBJECTID ||
|
||||
root->root_key.objectid == BTRFS_CSUM_TREE_OBJECTID ||
|
||||
root->root_key.objectid == BTRFS_FREE_SPACE_TREE_OBJECTID)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
int btrfs_delete_and_free_root(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root)
|
||||
{
|
||||
@ -2300,7 +2308,8 @@ int btrfs_delete_and_free_root(struct btrfs_trans_handle *trans,
|
||||
ret = btrfs_free_tree_block(trans, root, root->node, 0, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
rb_erase(&root->rb_node, &fs_info->global_roots_tree);
|
||||
if (is_global_root(root))
|
||||
rb_erase(&root->rb_node, &fs_info->global_roots_tree);
|
||||
free_extent_buffer(root->node);
|
||||
free_extent_buffer(root->commit_root);
|
||||
kfree(root);
|
||||
|
@ -1551,8 +1551,14 @@ static int update_block_group_item(struct btrfs_trans_handle *trans,
|
||||
*/
|
||||
if (btrfs_super_flags(fs_info->super_copy) &
|
||||
BTRFS_SUPER_FLAG_CHANGING_BG_TREE &&
|
||||
cache->start >= fs_info->last_converted_bg_bytenr)
|
||||
root = fs_info->block_group_root;
|
||||
cache->start >= fs_info->last_converted_bg_bytenr) {
|
||||
if (btrfs_fs_compat_ro(fs_info, BLOCK_GROUP_TREE))
|
||||
/* Converting back to extent tree. */
|
||||
root = btrfs_extent_root(fs_info, 0);
|
||||
else
|
||||
/* Convert to new bg tree.*/
|
||||
root = fs_info->block_group_root;
|
||||
}
|
||||
|
||||
key.objectid = cache->start;
|
||||
key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
|
||||
@ -2741,6 +2747,56 @@ static int get_last_converted_bg(struct btrfs_fs_info *fs_info)
|
||||
struct btrfs_key key = {0};
|
||||
int ret;
|
||||
|
||||
/* Converting back to extent tree case. */
|
||||
if (btrfs_fs_compat_ro(fs_info, BLOCK_GROUP_TREE)) {
|
||||
struct btrfs_root *extent_root = btrfs_extent_root(fs_info, 0);
|
||||
|
||||
/* Load the first bg in bg tree. */
|
||||
ret = btrfs_search_slot(NULL, bg_root, &key, &path, 0, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ASSERT(ret > 0);
|
||||
/* We should always be at the slot 0 of the first leaf. */
|
||||
ASSERT(path.slots[0] == 0);
|
||||
|
||||
key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
|
||||
|
||||
/* Empty bg tree, all converted, then grab the first bg. */
|
||||
if (btrfs_header_nritems(path.nodes[0]) == 0) {
|
||||
btrfs_release_path(&path);
|
||||
ret = find_first_block_group(extent_root, &path, &key);
|
||||
/* We should have at least one bg item in extent tree. */
|
||||
ASSERT(ret == 0);
|
||||
|
||||
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
|
||||
fs_info->last_converted_bg_bytenr = key.objectid;
|
||||
goto out;
|
||||
}
|
||||
btrfs_release_path(&path);
|
||||
|
||||
/* Grab the last bg in extent tree as the last converted one. */
|
||||
key.objectid = (u64)-1;
|
||||
key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
|
||||
key.offset = (u64)-1;
|
||||
|
||||
ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ASSERT(ret > 0);
|
||||
ret = btrfs_previous_item(extent_root, &path, 0, BTRFS_BLOCK_GROUP_ITEM_KEY);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* No converted bg item in extent tree.*/
|
||||
if (ret > 0) {
|
||||
ret = 0;
|
||||
fs_info->last_converted_bg_bytenr = (u64)-1;
|
||||
goto out;
|
||||
}
|
||||
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
|
||||
fs_info->last_converted_bg_bytenr = key.objectid;
|
||||
goto out;
|
||||
}
|
||||
/* Load the first bg in bg tree, that would be our last converted bg. */
|
||||
ret = btrfs_search_slot(NULL, bg_root, &key, &path, 0, 0);
|
||||
if (ret < 0)
|
||||
@ -2758,7 +2814,6 @@ static int get_last_converted_bg(struct btrfs_fs_info *fs_info)
|
||||
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
|
||||
ASSERT(key.type == BTRFS_BLOCK_GROUP_ITEM_KEY);
|
||||
fs_info->last_converted_bg_bytenr = key.objectid;
|
||||
|
||||
out:
|
||||
btrfs_release_path(&path);
|
||||
return ret;
|
||||
@ -2881,12 +2936,19 @@ out:
|
||||
|
||||
static int read_converting_block_groups(struct btrfs_fs_info *fs_info)
|
||||
{
|
||||
struct btrfs_root *old_root = btrfs_extent_root(fs_info, 0);
|
||||
struct btrfs_root *new_root = btrfs_block_group_root(fs_info);
|
||||
struct btrfs_root *old_root;
|
||||
struct btrfs_root *new_root;
|
||||
int ret;
|
||||
|
||||
/* Currently we only support converting to bg tree feature. */
|
||||
ASSERT(!btrfs_fs_compat_ro(fs_info, BLOCK_GROUP_TREE));
|
||||
if (btrfs_fs_compat_ro(fs_info, BLOCK_GROUP_TREE)) {
|
||||
/* Converting back to extent tree. */
|
||||
old_root = fs_info->block_group_root;
|
||||
new_root = btrfs_extent_root(fs_info, 0);
|
||||
} else {
|
||||
/* Converting to block group tree. */
|
||||
old_root = btrfs_extent_root(fs_info, 0);
|
||||
new_root = fs_info->block_group_root;
|
||||
}
|
||||
|
||||
ret = get_last_converted_bg(fs_info);
|
||||
if (ret < 0) {
|
||||
@ -2994,8 +3056,15 @@ static int insert_block_group_item(struct btrfs_trans_handle *trans,
|
||||
*/
|
||||
if (btrfs_super_flags(fs_info->super_copy) &
|
||||
BTRFS_SUPER_FLAG_CHANGING_BG_TREE &&
|
||||
block_group->start >= fs_info->last_converted_bg_bytenr)
|
||||
root = fs_info->block_group_root;
|
||||
block_group->start >= fs_info->last_converted_bg_bytenr) {
|
||||
if (btrfs_super_compat_ro_flags(fs_info->super_copy) &
|
||||
BTRFS_FEATURE_COMPAT_RO_BLOCK_GROUP_TREE)
|
||||
/* Converting to extent tree, return extent root. */
|
||||
root = btrfs_extent_root(fs_info, block_group->start);
|
||||
else
|
||||
/* Converting to bg tree, return bg root. */
|
||||
root = fs_info->block_group_root;
|
||||
}
|
||||
|
||||
return btrfs_insert_item(trans, root, &key, &bgi, sizeof(bgi));
|
||||
}
|
||||
@ -4041,11 +4110,6 @@ int btrfs_convert_one_bg(struct btrfs_trans_handle *trans, u64 bytenr)
|
||||
ASSERT(old_root);
|
||||
ASSERT(btrfs_super_flags(fs_info->super_copy) &
|
||||
BTRFS_SUPER_FLAG_CHANGING_BG_TREE);
|
||||
/*
|
||||
* Only support converting to bg tree yet, thus the feature should not
|
||||
* be set.
|
||||
*/
|
||||
ASSERT(!btrfs_fs_compat_ro(fs_info, BLOCK_GROUP_TREE));
|
||||
|
||||
bg = btrfs_lookup_block_group(fs_info, bytenr);
|
||||
if (!bg) {
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "kernel-shared/ctree.h"
|
||||
#include "kernel-shared/disk-io.h"
|
||||
#include "kernel-shared/transaction.h"
|
||||
#include "common/messages.h"
|
||||
#include "common/extent-cache.h"
|
||||
@ -147,4 +148,124 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int convert_to_extent_tree(struct btrfs_fs_info *fs_info)
|
||||
{
|
||||
struct btrfs_super_block *sb = fs_info->super_copy;
|
||||
struct btrfs_trans_handle *trans;
|
||||
struct cache_extent *ce;
|
||||
u32 converted_bgs = 0;
|
||||
int ret;
|
||||
|
||||
trans = btrfs_start_transaction(fs_info->tree_root, 2);
|
||||
if (IS_ERR(trans)) {
|
||||
ret = PTR_ERR(trans);
|
||||
error_msg(ERROR_MSG_START_TRANS, "%m");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We're resuming from previous run. */
|
||||
if (btrfs_super_flags(sb) & BTRFS_SUPER_FLAG_CHANGING_BG_TREE)
|
||||
goto iterate_bgs;
|
||||
|
||||
btrfs_set_super_flags(sb, btrfs_super_flags(sb) | BTRFS_SUPER_FLAG_CHANGING_BG_TREE);
|
||||
fs_info->last_converted_bg_bytenr = (u64)-1;
|
||||
|
||||
/* Now commit the transaction to make the above changes to reach disks. */
|
||||
ret = btrfs_commit_transaction(trans, fs_info->tree_root);
|
||||
if (ret < 0) {
|
||||
errno = -ret;
|
||||
error_msg(ERROR_MSG_COMMIT_TRANS, "new extent tree root: %m");
|
||||
goto error;
|
||||
}
|
||||
trans = btrfs_start_transaction(fs_info->tree_root, 2);
|
||||
if (IS_ERR(trans)) {
|
||||
ret = PTR_ERR(trans);
|
||||
errno = -ret;
|
||||
error_msg(ERROR_MSG_START_TRANS, "%m");
|
||||
return ret;
|
||||
}
|
||||
|
||||
iterate_bgs:
|
||||
if (fs_info->last_converted_bg_bytenr == (u64)-1) {
|
||||
ce = last_cache_extent(&fs_info->mapping_tree.cache_tree);
|
||||
} else {
|
||||
ce = search_cache_extent(&fs_info->mapping_tree.cache_tree,
|
||||
fs_info->last_converted_bg_bytenr);
|
||||
if (!ce) {
|
||||
error("failed to find block group for bytenr %llu",
|
||||
fs_info->last_converted_bg_bytenr);
|
||||
ret = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
ce = prev_cache_extent(ce);
|
||||
if (!ce) {
|
||||
error("no more block groups before bytenr %llu",
|
||||
fs_info->last_converted_bg_bytenr);
|
||||
ret = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
/* Now convert each block group. */
|
||||
while (ce) {
|
||||
struct cache_extent *prev = prev_cache_extent(ce);
|
||||
u64 bytenr = ce->start;
|
||||
|
||||
ret = btrfs_convert_one_bg(trans, bytenr);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
converted_bgs++;
|
||||
ce = prev;
|
||||
|
||||
if (converted_bgs % BLOCK_GROUP_BATCH == 0) {
|
||||
ret = btrfs_commit_transaction(trans, fs_info->tree_root);
|
||||
if (ret < 0) {
|
||||
errno = -ret;
|
||||
error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
|
||||
return ret;
|
||||
}
|
||||
trans = btrfs_start_transaction(fs_info->tree_root, 2);
|
||||
if (IS_ERR(trans)) {
|
||||
ret = PTR_ERR(trans);
|
||||
errno = -ret;
|
||||
error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Remove block group tree, at this stage, the block group tree root
|
||||
* should be empty.
|
||||
*/
|
||||
ASSERT(btrfs_header_nritems(fs_info->block_group_root->node) == 0);
|
||||
ret = btrfs_delete_and_free_root(trans, fs_info->block_group_root);
|
||||
if (ret < 0) {
|
||||
errno = -ret;
|
||||
error("failed to remove bg tree: %m");
|
||||
goto error;
|
||||
}
|
||||
fs_info->block_group_root = NULL;
|
||||
|
||||
/*
|
||||
* All bgs converted and bg tree removed, remove the CHANGING_BG_TREE
|
||||
* flag and remove the compat ro flag.
|
||||
*/
|
||||
fs_info->last_converted_bg_bytenr = 0;
|
||||
btrfs_set_super_flags(sb,
|
||||
btrfs_super_flags(sb) & ~BTRFS_SUPER_FLAG_CHANGING_BG_TREE);
|
||||
btrfs_set_super_compat_ro_flags(sb,
|
||||
btrfs_super_compat_ro_flags(sb) &
|
||||
~BTRFS_FEATURE_COMPAT_RO_BLOCK_GROUP_TREE);
|
||||
ret = btrfs_commit_transaction(trans, fs_info->tree_root);
|
||||
if (ret < 0) {
|
||||
errno = -ret;
|
||||
error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
|
||||
return ret;
|
||||
}
|
||||
pr_verbose(LOG_DEFAULT,
|
||||
"Converted filesystem with block-group-tree to extent tree feature");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
return ret;
|
||||
}
|
||||
|
38
tune/main.c
38
tune/main.c
@ -71,6 +71,7 @@ static const char * const tune_usage[] = {
|
||||
OPTLINE("-n", "enable no-holes feature (mkfs: no-holes, more efficient sparse file representation)"),
|
||||
OPTLINE("-S <0|1>", "set/unset seeding status of a device"),
|
||||
OPTLINE("--enable-block-group-tree", "enable block group tree (mkfs: block-group-tree, for less mount time)"),
|
||||
OPTLINE("--disable-block-group-tree", "disable block group tree (mkfs: ^block-group-tree)n"),
|
||||
"",
|
||||
"UUID changes:",
|
||||
OPTLINE("-u", "rewrite fsid, use a random one"),
|
||||
@ -103,6 +104,7 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
|
||||
u64 seeding_value = 0;
|
||||
int random_fsid = 0;
|
||||
int change_metadata_uuid = 0;
|
||||
bool to_extent_tree = false;
|
||||
bool to_bg_tree = false;
|
||||
int csum_type = -1;
|
||||
char *new_fsid_str = NULL;
|
||||
@ -114,11 +116,14 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
|
||||
|
||||
while(1) {
|
||||
enum { GETOPT_VAL_CSUM = GETOPT_VAL_FIRST,
|
||||
GETOPT_VAL_ENABLE_BLOCK_GROUP_TREE };
|
||||
GETOPT_VAL_ENABLE_BLOCK_GROUP_TREE,
|
||||
GETOPT_VAL_DISABLE_BLOCK_GROUP_TREE };
|
||||
static const struct option long_options[] = {
|
||||
{ "help", no_argument, NULL, GETOPT_VAL_HELP},
|
||||
{ "enable-block-group-tree", no_argument, NULL,
|
||||
GETOPT_VAL_ENABLE_BLOCK_GROUP_TREE},
|
||||
{ "disable-block-group-tree", no_argument, NULL,
|
||||
GETOPT_VAL_DISABLE_BLOCK_GROUP_TREE},
|
||||
#if EXPERIMENTAL
|
||||
{ "csum", required_argument, NULL, GETOPT_VAL_CSUM },
|
||||
#endif
|
||||
@ -165,6 +170,9 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
|
||||
case GETOPT_VAL_ENABLE_BLOCK_GROUP_TREE:
|
||||
to_bg_tree = true;
|
||||
break;
|
||||
case GETOPT_VAL_DISABLE_BLOCK_GROUP_TREE:
|
||||
to_extent_tree = true;
|
||||
break;
|
||||
#if EXPERIMENTAL
|
||||
case GETOPT_VAL_CSUM:
|
||||
btrfs_warn_experimental(
|
||||
@ -189,7 +197,8 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
if (!super_flags && !seeding_flag && !(random_fsid || new_fsid_str) &&
|
||||
!change_metadata_uuid && csum_type == -1 && !to_bg_tree) {
|
||||
!change_metadata_uuid && csum_type == -1 && !to_bg_tree &&
|
||||
!to_extent_tree) {
|
||||
error("at least one option should be specified");
|
||||
usage(&tune_cmd, 1);
|
||||
return 1;
|
||||
@ -235,7 +244,12 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (to_bg_tree) {
|
||||
if (to_bg_tree) {
|
||||
if (to_extent_tree) {
|
||||
error("option --enable-block-group-tree conflicts with --disable-block-group-tree");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
if (btrfs_fs_compat_ro(root->fs_info, BLOCK_GROUP_TREE)) {
|
||||
error("the filesystem already has block group tree feature");
|
||||
ret = 1;
|
||||
@ -253,6 +267,24 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
if (to_extent_tree) {
|
||||
if (to_bg_tree) {
|
||||
error("option --enable-block-group-tree conflicts with --disable-block-group-tree");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
if (!btrfs_fs_compat_ro(root->fs_info, BLOCK_GROUP_TREE)) {
|
||||
error("filesystem doesn't have block-group-tree feature");
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
ret = convert_to_extent_tree(root->fs_info);
|
||||
if (ret < 0) {
|
||||
error("failed to convert the filesystem from block group tree feature");
|
||||
goto out;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
if (seeding_flag) {
|
||||
if (btrfs_fs_incompat(root->fs_info, METADATA_UUID)) {
|
||||
error("SEED flag cannot be changed on a metadata-uuid changed fs");
|
||||
|
@ -28,6 +28,7 @@ int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str);
|
||||
int set_metadata_uuid(struct btrfs_root *root, const char *uuid_string);
|
||||
|
||||
int convert_to_bg_tree(struct btrfs_fs_info *fs_info);
|
||||
int convert_to_extent_tree(struct btrfs_fs_info *fs_info);
|
||||
|
||||
int rewrite_checksums(struct btrfs_fs_info *fs_info, int csum_type);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user