btrfs: make clear_cache mount option to rebuild FST without disabling it
commit 1d6a4fc85717677e00fefffd847a50fc5928ce69 upstream. Previously clear_cache mount option would simply disable free-space-tree feature temporarily then re-enable it to rebuild the whole free space tree. But this is problematic for block-group-tree feature, as we have an artificial dependency on free-space-tree feature. If we go the existing method, after clearing the free-space-tree feature, we would flip the filesystem to read-only mode, as we detect a super block write with block-group-tree but no free-space-tree feature. This patch would change the behavior by properly rebuilding the free space tree without disabling this feature, thus allowing clear_cache mount option to work with block group tree. Now we can mount a filesystem with block-group-tree feature and clear_mount option: $ mkfs.btrfs -O block-group-tree /dev/test/scratch1 -f $ sudo mount /dev/test/scratch1 /mnt/btrfs -o clear_cache $ sudo dmesg -t | head -n 5 BTRFS info (device dm-1): force clearing of disk cache BTRFS info (device dm-1): using free space tree BTRFS info (device dm-1): auto enabling async discard BTRFS info (device dm-1): rebuilding free space tree BTRFS info (device dm-1): checking UUID tree CC: stable@vger.kernel.org # 6.1+ Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
dd5a21941f
commit
83ae0282f1
@ -3215,23 +3215,34 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
const bool cache_opt = btrfs_test_opt(fs_info, SPACE_CACHE);
|
const bool cache_opt = btrfs_test_opt(fs_info, SPACE_CACHE);
|
||||||
bool clear_free_space_tree = false;
|
bool rebuild_free_space_tree = false;
|
||||||
|
|
||||||
if (btrfs_test_opt(fs_info, CLEAR_CACHE) &&
|
if (btrfs_test_opt(fs_info, CLEAR_CACHE) &&
|
||||||
btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
|
btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
|
||||||
clear_free_space_tree = true;
|
rebuild_free_space_tree = true;
|
||||||
} else if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) &&
|
} else if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) &&
|
||||||
!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID)) {
|
!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID)) {
|
||||||
btrfs_warn(fs_info, "free space tree is invalid");
|
btrfs_warn(fs_info, "free space tree is invalid");
|
||||||
clear_free_space_tree = true;
|
rebuild_free_space_tree = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clear_free_space_tree) {
|
if (rebuild_free_space_tree) {
|
||||||
btrfs_info(fs_info, "clearing free space tree");
|
btrfs_info(fs_info, "rebuilding free space tree");
|
||||||
ret = btrfs_clear_free_space_tree(fs_info);
|
ret = btrfs_rebuild_free_space_tree(fs_info);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
btrfs_warn(fs_info,
|
btrfs_warn(fs_info,
|
||||||
"failed to clear free space tree: %d", ret);
|
"failed to rebuild free space tree: %d", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) &&
|
||||||
|
!btrfs_test_opt(fs_info, FREE_SPACE_TREE)) {
|
||||||
|
btrfs_info(fs_info, "disabling free space tree");
|
||||||
|
ret = btrfs_delete_free_space_tree(fs_info);
|
||||||
|
if (ret) {
|
||||||
|
btrfs_warn(fs_info,
|
||||||
|
"failed to disable free space tree: %d", ret);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1247,7 +1247,7 @@ static int clear_free_space_tree(struct btrfs_trans_handle *trans,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
|
int btrfs_delete_free_space_tree(struct btrfs_fs_info *fs_info)
|
||||||
{
|
{
|
||||||
struct btrfs_trans_handle *trans;
|
struct btrfs_trans_handle *trans;
|
||||||
struct btrfs_root *tree_root = fs_info->tree_root;
|
struct btrfs_root *tree_root = fs_info->tree_root;
|
||||||
@ -1293,6 +1293,54 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int btrfs_rebuild_free_space_tree(struct btrfs_fs_info *fs_info)
|
||||||
|
{
|
||||||
|
struct btrfs_trans_handle *trans;
|
||||||
|
struct btrfs_key key = {
|
||||||
|
.objectid = BTRFS_FREE_SPACE_TREE_OBJECTID,
|
||||||
|
.type = BTRFS_ROOT_ITEM_KEY,
|
||||||
|
.offset = 0,
|
||||||
|
};
|
||||||
|
struct btrfs_root *free_space_root = btrfs_global_root(fs_info, &key);
|
||||||
|
struct rb_node *node;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
trans = btrfs_start_transaction(free_space_root, 1);
|
||||||
|
if (IS_ERR(trans))
|
||||||
|
return PTR_ERR(trans);
|
||||||
|
|
||||||
|
set_bit(BTRFS_FS_CREATING_FREE_SPACE_TREE, &fs_info->flags);
|
||||||
|
set_bit(BTRFS_FS_FREE_SPACE_TREE_UNTRUSTED, &fs_info->flags);
|
||||||
|
|
||||||
|
ret = clear_free_space_tree(trans, free_space_root);
|
||||||
|
if (ret)
|
||||||
|
goto abort;
|
||||||
|
|
||||||
|
node = rb_first_cached(&fs_info->block_group_cache_tree);
|
||||||
|
while (node) {
|
||||||
|
struct btrfs_block_group *block_group;
|
||||||
|
|
||||||
|
block_group = rb_entry(node, struct btrfs_block_group,
|
||||||
|
cache_node);
|
||||||
|
ret = populate_free_space_tree(trans, block_group);
|
||||||
|
if (ret)
|
||||||
|
goto abort;
|
||||||
|
node = rb_next(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE);
|
||||||
|
btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID);
|
||||||
|
clear_bit(BTRFS_FS_CREATING_FREE_SPACE_TREE, &fs_info->flags);
|
||||||
|
|
||||||
|
ret = btrfs_commit_transaction(trans);
|
||||||
|
clear_bit(BTRFS_FS_FREE_SPACE_TREE_UNTRUSTED, &fs_info->flags);
|
||||||
|
return ret;
|
||||||
|
abort:
|
||||||
|
btrfs_abort_transaction(trans, ret);
|
||||||
|
btrfs_end_transaction(trans);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int __add_block_group_free_space(struct btrfs_trans_handle *trans,
|
static int __add_block_group_free_space(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_block_group *block_group,
|
struct btrfs_block_group *block_group,
|
||||||
struct btrfs_path *path)
|
struct btrfs_path *path)
|
||||||
|
@ -18,7 +18,8 @@ struct btrfs_caching_control;
|
|||||||
|
|
||||||
void set_free_space_tree_thresholds(struct btrfs_block_group *block_group);
|
void set_free_space_tree_thresholds(struct btrfs_block_group *block_group);
|
||||||
int btrfs_create_free_space_tree(struct btrfs_fs_info *fs_info);
|
int btrfs_create_free_space_tree(struct btrfs_fs_info *fs_info);
|
||||||
int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info);
|
int btrfs_delete_free_space_tree(struct btrfs_fs_info *fs_info);
|
||||||
|
int btrfs_rebuild_free_space_tree(struct btrfs_fs_info *fs_info);
|
||||||
int load_free_space_tree(struct btrfs_caching_control *caching_ctl);
|
int load_free_space_tree(struct btrfs_caching_control *caching_ctl);
|
||||||
int add_block_group_free_space(struct btrfs_trans_handle *trans,
|
int add_block_group_free_space(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_block_group *block_group);
|
struct btrfs_block_group *block_group);
|
||||||
|
@ -1138,8 +1138,7 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options,
|
|||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
if (btrfs_fs_compat_ro(info, BLOCK_GROUP_TREE) &&
|
if (btrfs_fs_compat_ro(info, BLOCK_GROUP_TREE) &&
|
||||||
(btrfs_test_opt(info, CLEAR_CACHE) ||
|
!btrfs_test_opt(info, FREE_SPACE_TREE)) {
|
||||||
!btrfs_test_opt(info, FREE_SPACE_TREE))) {
|
|
||||||
btrfs_err(info, "cannot disable free space tree with block-group-tree feature");
|
btrfs_err(info, "cannot disable free space tree with block-group-tree feature");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user