btrfs: Fix split-brain handling when changing FSID to metadata uuid
Current code doesn't correctly handle the situation which arises when a file system that has METADATA_UUID_INCOMPAT flag set and has its FSID changed to the one in metadata uuid. This causes the incompat flag to disappear. In case of a power failure we could end up in a situation where part of the disks in a multi-disk filesystem are correctly reverted to METADATA_UUID_INCOMPAT flag unset state, while others have METADATA_UUID_INCOMPAT set and CHANGING_FSID_V2_IN_PROGRESS. This patch corrects the behavior required to handle the case where a disk of the second type is scanned first, creating the necessary btrfs_fs_devices. Subsequently, when a disk which has already completed the transition is scanned it should overwrite the data in btrfs_fs_devices. Reported-by: Su Yue <Damenly_Su@gmx.com> Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Nikolay Borisov <nborisov@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
0584071014
commit
1362089d2a
@ -736,6 +736,32 @@ static struct btrfs_fs_devices *find_fsid_changed(
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct btrfs_fs_devices *find_fsid_reverted_metadata(
|
||||
struct btrfs_super_block *disk_super)
|
||||
{
|
||||
struct btrfs_fs_devices *fs_devices;
|
||||
|
||||
/*
|
||||
* Handle the case where the scanned device is part of an fs whose last
|
||||
* metadata UUID change reverted it to the original FSID. At the same
|
||||
* time * fs_devices was first created by another constitutent device
|
||||
* which didn't fully observe the operation. This results in an
|
||||
* btrfs_fs_devices created with metadata/fsid different AND
|
||||
* btrfs_fs_devices::fsid_change set AND the metadata_uuid of the
|
||||
* fs_devices equal to the FSID of the disk.
|
||||
*/
|
||||
list_for_each_entry(fs_devices, &fs_uuids, fs_list) {
|
||||
if (memcmp(fs_devices->fsid, fs_devices->metadata_uuid,
|
||||
BTRFS_FSID_SIZE) != 0 &&
|
||||
memcmp(fs_devices->metadata_uuid, disk_super->fsid,
|
||||
BTRFS_FSID_SIZE) == 0 &&
|
||||
fs_devices->fsid_change)
|
||||
return fs_devices;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Add new device to list of registered devices
|
||||
*
|
||||
@ -765,7 +791,9 @@ static noinline struct btrfs_device *device_list_add(const char *path,
|
||||
} else if (has_metadata_uuid) {
|
||||
fs_devices = find_fsid_with_metadata_uuid(disk_super);
|
||||
} else {
|
||||
fs_devices = find_fsid(disk_super->fsid, NULL);
|
||||
fs_devices = find_fsid_reverted_metadata(disk_super);
|
||||
if (!fs_devices)
|
||||
fs_devices = find_fsid(disk_super->fsid, NULL);
|
||||
}
|
||||
|
||||
|
||||
@ -795,12 +823,18 @@ static noinline struct btrfs_device *device_list_add(const char *path,
|
||||
* a device which had the CHANGING_FSID_V2 flag then replace the
|
||||
* metadata_uuid/fsid values of the fs_devices.
|
||||
*/
|
||||
if (has_metadata_uuid && fs_devices->fsid_change &&
|
||||
if (fs_devices->fsid_change &&
|
||||
found_transid > fs_devices->latest_generation) {
|
||||
memcpy(fs_devices->fsid, disk_super->fsid,
|
||||
BTRFS_FSID_SIZE);
|
||||
memcpy(fs_devices->metadata_uuid,
|
||||
disk_super->metadata_uuid, BTRFS_FSID_SIZE);
|
||||
|
||||
if (has_metadata_uuid)
|
||||
memcpy(fs_devices->metadata_uuid,
|
||||
disk_super->metadata_uuid,
|
||||
BTRFS_FSID_SIZE);
|
||||
else
|
||||
memcpy(fs_devices->metadata_uuid,
|
||||
disk_super->fsid, BTRFS_FSID_SIZE);
|
||||
|
||||
fs_devices->fsid_change = false;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user