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 <anand.jain@oracle.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Anand Jain 2023-09-15 12:08:58 +08:00 committed by David Sterba
parent e75658fe57
commit cabf70c7b8
3 changed files with 41 additions and 12 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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);