From cabf70c7b8f68e2bdb30ca4ba76f8b2c52c42c01 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 15 Sep 2023 12:08:58 +0800 Subject: [PATCH] btrfs-progs: tune: recover from failed btrfstune -m|M Currently, to fix device following the write failure of one or more devices during btrfstune -m|M, we rely on the kernel's ability to reassemble devices, even when they possess distinct fsids. Kernel hinges combinations of metadata_uuid and generation number, with additional cues taken from the fsid and the BTRFS_SUPER_FLAG_CHANGING_FSID_V2 flag. This patch adds this logic to btrfs-progs. In complex scenarios (such as multiple fsids with the same metadata_uuid and matching generation), user intervention becomes necessary to resolve the situations which btrfs-progs can do better. Signed-off-by: Anand Jain Signed-off-by: David Sterba --- tune/change-metadata-uuid.c | 47 ++++++++++++++++++++++++++++++------- tune/change-uuid.c | 4 ++-- tune/tune.h | 2 -- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/tune/change-metadata-uuid.c b/tune/change-metadata-uuid.c index ada3149a..798acb6a 100644 --- a/tune/change-metadata-uuid.c +++ b/tune/change-metadata-uuid.c @@ -21,9 +21,31 @@ #include "kernel-shared/uapi/btrfs.h" #include "kernel-shared/ctree.h" #include "kernel-shared/transaction.h" +#include "kernel-shared/volumes.h" #include "common/messages.h" #include "tune/tune.h" +/* + * Return 0 for no unfinished metadata_uuid change. + * Return >0 for unfinished metadata_uuid change, and restore unfinished + * fsid/metadata_uuid into fsid_ret/metadata_uuid_ret. + */ +static int check_unfinished_metadata_uuid(struct btrfs_fs_info *fs_info, + uuid_t fsid_ret, + uuid_t metadata_uuid_ret) +{ + struct btrfs_root *tree_root = fs_info->tree_root; + + if (fs_info->fs_devices->inconsistent_super) { + memcpy(fsid_ret, fs_info->super_copy->fsid, BTRFS_FSID_SIZE); + read_extent_buffer(tree_root->node, metadata_uuid_ret, + btrfs_header_chunk_tree_uuid(tree_root->node), + BTRFS_UUID_SIZE); + return 1; + } + return 0; +} + int set_metadata_uuid(struct btrfs_root *root, const char *new_fsid_string) { struct btrfs_super_block *disk_super; @@ -45,15 +67,24 @@ int set_metadata_uuid(struct btrfs_root *root, const char *new_fsid_string) return 1; } - if (check_unfinished_fsid_change(root->fs_info, fsid, metadata_uuid)) { - error("UUID rewrite in progress, cannot change metadata_uuid"); - return 1; - } + if (check_unfinished_metadata_uuid(root->fs_info, fsid, metadata_uuid)) { + if (new_fsid_string) { + uuid_t tmp; - if (new_fsid_string) - uuid_parse(new_fsid_string, fsid); - else - uuid_generate(fsid); + uuid_parse(new_fsid_string, tmp); + if (memcmp(tmp, fsid, BTRFS_FSID_SIZE) != 0) { + error( + "new fsid %s is not the same with unfinished fsid change", + new_fsid_string); + return -EINVAL; + } + } + } else { + if (new_fsid_string) + uuid_parse(new_fsid_string, fsid); + else + uuid_generate(fsid); + } new_fsid = (memcmp(fsid, disk_super->fsid, BTRFS_FSID_SIZE) != 0); diff --git a/tune/change-uuid.c b/tune/change-uuid.c index 30cfb145..e81b7980 100644 --- a/tune/change-uuid.c +++ b/tune/change-uuid.c @@ -210,8 +210,8 @@ static int change_fsid_done(struct btrfs_fs_info *fs_info) * Return >0 for unfinished fsid change, and restore unfinished fsid/ * chunk_tree_id into fsid_ret/chunk_id_ret. */ -int check_unfinished_fsid_change(struct btrfs_fs_info *fs_info, - uuid_t fsid_ret, uuid_t chunk_id_ret) +static int check_unfinished_fsid_change(struct btrfs_fs_info *fs_info, + uuid_t fsid_ret, uuid_t chunk_id_ret) { struct btrfs_root *tree_root = fs_info->tree_root; diff --git a/tune/tune.h b/tune/tune.h index cbf33b2e..397cfe4f 100644 --- a/tune/tune.h +++ b/tune/tune.h @@ -24,8 +24,6 @@ struct btrfs_fs_info; int update_seeding_flag(struct btrfs_root *root, const char *device, int set_flag, int force); -int check_unfinished_fsid_change(struct btrfs_fs_info *fs_info, - uuid_t fsid_ret, uuid_t chunk_id_ret); 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);