@@ -32,6 +32,10 @@ OPTIONS
enable skinny-bg-tree feature (faster mount time for large fs), enabled by mkfs
feature 'skinny-bg-tree'.
+-B::
++
+disable skinny-bg-tree feature.
+
-f::
Allow dangerous changes, e.g. clear the seeding flag or change fsid. Make sure
that you are aware of the dangers.
@@ -478,6 +478,7 @@ static void print_usage(void)
printf("\t-M UUID change fsid in metadata_uuid to UUID\n");
printf("\t-b enable skinny-bg-tree feature (mkfs: skinny-bg-tree)");
printf("\t for faster mount time\n");
+ printf("\t-B disable skinny-bg-tree feature");
printf(" general:\n");
printf("\t-f allow dangerous operations, make sure that you are aware of the dangers\n");
printf("\t--help print this help\n");
@@ -488,6 +489,7 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
struct btrfs_root *root;
unsigned ctree_flags = OPEN_CTREE_WRITES;
bool to_skinny_bg_tree = false;
+ bool to_extent_tree = false;
int success = 0;
int total = 0;
int seeding_flag = 0;
@@ -504,7 +506,7 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
{ "help", no_argument, NULL, GETOPT_VAL_HELP},
{ NULL, 0, NULL, 0 }
};
- int c = getopt_long(argc, argv, "S:rxfuU:nmM:b", long_options,
+ int c = getopt_long(argc, argv, "S:rxfuU:nmM:bB", long_options,
NULL);
if (c < 0)
@@ -546,6 +548,9 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
case 'b':
to_skinny_bg_tree = true;
break;
+ case 'B':
+ to_extent_tree = true;
+ break;
case GETOPT_VAL_HELP:
default:
print_usage();
@@ -563,11 +568,15 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
return 1;
}
if (!super_flags && !seeding_flag && !(random_fsid || new_fsid_str) &&
- !change_metadata_uuid && !to_skinny_bg_tree) {
+ !change_metadata_uuid && !to_skinny_bg_tree && !to_extent_tree) {
error("at least one option should be specified");
print_usage();
return 1;
}
+ if (to_extent_tree && to_skinny_bg_tree) {
+ error("'-b' and '-B' conflict with each other");
+ return 1;
+ }
if (new_fsid_str) {
uuid_t tmp;
@@ -617,6 +626,14 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
goto out;
}
}
+ if (to_extent_tree) {
+ ret = btrfs_convert_to_extent_tree(root->fs_info);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to disable bg-tree feature: %m");
+ goto out;
+ }
+ }
if (seeding_flag) {
if (btrfs_fs_incompat(root->fs_info, METADATA_UUID)) {
fprintf(stderr, "SEED flag cannot be changed on a metadata-uuid changed fs\n");
@@ -1206,6 +1206,7 @@ struct btrfs_fs_info {
unsigned int finalize_on_close:1;
unsigned int hide_names:1;
unsigned int convert_to_skinny_bg_tree:1;
+ unsigned int convert_to_extent_tree:1;
int transaction_aborted;
@@ -2621,6 +2622,7 @@ u64 add_new_free_space(struct btrfs_block_group *block_group,
struct btrfs_fs_info *info, u64 start, u64 end);
u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset);
int btrfs_convert_to_skinny_bg_tree(struct btrfs_fs_info *fs_info);
+int btrfs_convert_to_extent_tree(struct btrfs_fs_info *fs_info);
/* ctree.c */
int btrfs_comp_cpu_keys(const struct btrfs_key *k1, const struct btrfs_key *k2);
@@ -1597,6 +1597,14 @@ static int update_block_group_item(struct btrfs_trans_handle *trans,
btrfs_set_item_key_safe(fs_info->bg_root, path, &key);
return 0;
}
+ if (fs_info->convert_to_extent_tree) {
+ ret = insert_block_group_item(trans, cache);
+ if (ret == 0)
+ return ret;
+ if (ret < 0 && ret != -EEXIST)
+ goto fail;
+ /* -EEXIST case falls through */
+ }
key.objectid = cache->start;
key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
key.offset = cache->length;
@@ -4128,3 +4136,114 @@ error:
btrfs_abort_transaction(trans, ret);
return ret;
}
+
+static int clear_bg_tree(struct btrfs_trans_handle *trans)
+{
+ struct btrfs_fs_info *fs_info = trans->fs_info;
+ struct btrfs_root *root = fs_info->bg_root;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ int ret;
+ int nr;
+
+ btrfs_init_path(&path);
+ key.objectid = 0;
+ key.type = 0;
+ key.offset = 0;
+
+ while (1) {
+ ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
+ if (ret < 0)
+ goto out;
+ nr = btrfs_header_nritems(path.nodes[0]);
+ if (!nr)
+ break;
+ ret = btrfs_del_items(trans, root, &path, 0, nr);
+ if (ret < 0)
+ goto out;
+ btrfs_release_path(&path);
+ }
+ ret = 0;
+out:
+ btrfs_release_path(&path);
+ return ret;
+}
+
+int btrfs_convert_to_extent_tree(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_root *bg_root = fs_info->bg_root;
+ struct btrfs_block_group *bg;
+ u64 features = btrfs_super_incompat_flags(fs_info->super_copy);
+ int ret;
+
+ if (bg_root == NULL) {
+ printf("The fs is not using skinny bg tree\n");
+ return 0;
+ }
+ trans = btrfs_start_transaction(fs_info->tree_root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ errno = -ret;
+ error("failed to start transaction: %m");
+ return ret;
+ }
+
+ /*
+ * Empty bg tree, but not delete it yet, as btrfs-progs doesn't have
+ * good root deletion routine.
+ */
+ ret = clear_bg_tree(trans);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to delete bg tree: %m");
+ goto error;
+ }
+ /* Clear SKINNY_BG_FEATURE and set convert status */
+ btrfs_set_super_incompat_flags(fs_info->super_copy,
+ features & ~BTRFS_FEATURE_INCOMPAT_SKINNY_BG_TREE);
+ fs_info->convert_to_extent_tree = 1;
+
+ /* Mark all bgs dirty so convert will happen at convert time */
+ for (bg = btrfs_lookup_first_block_group(fs_info, 0); bg;
+ bg = btrfs_lookup_first_block_group(fs_info,
+ bg->start + bg->length))
+ if (list_empty(&bg->dirty_list))
+ list_add_tail(&bg->dirty_list, &trans->dirty_bgs);
+
+ ret = btrfs_commit_transaction(trans, fs_info->tree_root);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to commit transaction: %m");
+ goto error;
+ }
+ trans = btrfs_start_transaction(fs_info->tree_root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ errno = -ret;
+ error("failed to start transaction: %m");
+ return ret;
+ }
+
+ /* Now cleanup the eb used by bg tree and delete it */
+ ret = btrfs_free_tree_block(trans, bg_root, bg_root->node, 0, 0);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to free bg tree root node: %m");
+ goto error;
+ }
+ free_extent_buffer(bg_root->node);
+ ret = btrfs_del_root(trans, fs_info->tree_root, &bg_root->root_key);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to delete bg root: %m");
+ goto error;
+ }
+ free(bg_root);
+ fs_info->bg_root = NULL;
+ ret = btrfs_commit_transaction(trans, fs_info->tree_root);
+ return ret;
+error:
+ btrfs_abort_transaction(trans, ret);
+ return ret;
+}
@@ -83,7 +83,11 @@ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
ret = btrfs_search_slot(trans, root, key, path, 0, 1);
if (ret < 0)
goto out;
- BUG_ON(ret != 0);
+ /* The root has been deleted */
+ if (ret > 0) {
+ ret = 0;
+ goto out;
+ }
l = path->nodes[0];
slot = path->slots[0];
ptr = btrfs_item_ptr_offset(l, slot);
@@ -227,6 +227,7 @@ commit_tree:
fs_info->running_transaction = NULL;
fs_info->last_trans_committed = transid;
fs_info->convert_to_skinny_bg_tree = 0;
+ fs_info->convert_to_extent_tree = 0;
list_for_each_entry(sinfo, &fs_info->space_info, list) {
if (sinfo->bytes_reserved) {
warning(
Since the skinny bg tree will not be supported on older kernel, some testers may want to roll back to regular extent tree so that they can use real-world data to test this feature. So add such rollback ability to do provide a much wider test coverage while still allow testers to enjoy their old data on older kernels. Signed-off-by: Qu Wenruo <wqu@suse.com> --- Documentation/btrfstune.asciidoc | 4 ++ btrfstune.c | 21 +++++- ctree.h | 2 + extent-tree.c | 119 +++++++++++++++++++++++++++++++ root-tree.c | 6 +- transaction.c | 1 + 6 files changed, 150 insertions(+), 3 deletions(-)