Skip to content

Commit 1362089

Browse files
lorddoskiaskdave
authored andcommitted
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 <[email protected]> Reviewed-by: Josef Bacik <[email protected]> Signed-off-by: Nikolay Borisov <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 0584071 commit 1362089

File tree

1 file changed

+38
-4
lines changed

1 file changed

+38
-4
lines changed

fs/btrfs/volumes.c

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,32 @@ static struct btrfs_fs_devices *find_fsid_changed(
736736

737737
return NULL;
738738
}
739+
740+
static struct btrfs_fs_devices *find_fsid_reverted_metadata(
741+
struct btrfs_super_block *disk_super)
742+
{
743+
struct btrfs_fs_devices *fs_devices;
744+
745+
/*
746+
* Handle the case where the scanned device is part of an fs whose last
747+
* metadata UUID change reverted it to the original FSID. At the same
748+
* time * fs_devices was first created by another constitutent device
749+
* which didn't fully observe the operation. This results in an
750+
* btrfs_fs_devices created with metadata/fsid different AND
751+
* btrfs_fs_devices::fsid_change set AND the metadata_uuid of the
752+
* fs_devices equal to the FSID of the disk.
753+
*/
754+
list_for_each_entry(fs_devices, &fs_uuids, fs_list) {
755+
if (memcmp(fs_devices->fsid, fs_devices->metadata_uuid,
756+
BTRFS_FSID_SIZE) != 0 &&
757+
memcmp(fs_devices->metadata_uuid, disk_super->fsid,
758+
BTRFS_FSID_SIZE) == 0 &&
759+
fs_devices->fsid_change)
760+
return fs_devices;
761+
}
762+
763+
return NULL;
764+
}
739765
/*
740766
* Add new device to list of registered devices
741767
*
@@ -765,7 +791,9 @@ static noinline struct btrfs_device *device_list_add(const char *path,
765791
} else if (has_metadata_uuid) {
766792
fs_devices = find_fsid_with_metadata_uuid(disk_super);
767793
} else {
768-
fs_devices = find_fsid(disk_super->fsid, NULL);
794+
fs_devices = find_fsid_reverted_metadata(disk_super);
795+
if (!fs_devices)
796+
fs_devices = find_fsid(disk_super->fsid, NULL);
769797
}
770798

771799

@@ -795,12 +823,18 @@ static noinline struct btrfs_device *device_list_add(const char *path,
795823
* a device which had the CHANGING_FSID_V2 flag then replace the
796824
* metadata_uuid/fsid values of the fs_devices.
797825
*/
798-
if (has_metadata_uuid && fs_devices->fsid_change &&
826+
if (fs_devices->fsid_change &&
799827
found_transid > fs_devices->latest_generation) {
800828
memcpy(fs_devices->fsid, disk_super->fsid,
801829
BTRFS_FSID_SIZE);
802-
memcpy(fs_devices->metadata_uuid,
803-
disk_super->metadata_uuid, BTRFS_FSID_SIZE);
830+
831+
if (has_metadata_uuid)
832+
memcpy(fs_devices->metadata_uuid,
833+
disk_super->metadata_uuid,
834+
BTRFS_FSID_SIZE);
835+
else
836+
memcpy(fs_devices->metadata_uuid,
837+
disk_super->fsid, BTRFS_FSID_SIZE);
804838

805839
fs_devices->fsid_change = false;
806840
}

0 commit comments

Comments
 (0)