Skip to content

Commit

Permalink
btrfs: Handle final split-brain possibility during fsid change
Browse files Browse the repository at this point in the history
This patch lands the last case which needs to be handled by the fsid
change code. Namely, this is the case where a multidisk filesystem has
already undergone at least one successful fsid change i.e all disks
have the METADATA_UUID incompat bit and power failure occurs as another
fsid change is in progress. When such an event occurs, disks could be
split in 2 groups. One of the groups will have both METADATA_UUID and
CHANGING_FSID_V2 flags set coupled with old fsid/metadata_uuid pairs.
The other group of disks will have only METADATA_UUID bit set and their
fsid will be different than the one in disks in the first group. Here
we look at the following cases:

  a) A disk from the first group is scanned first, so fs_devices is
  created with stale fsid/metdata_uuid. Then when a disk from the
  second group is scanned it needs to first check whether there exists
  such an fs_devices that has fsid_change set to true (because it was
  created with a disk having the CHANGING_FSID_V2 flag), the
  metadata_uuid and fsid of the fs_devices will be different (since it was
  created by a disk which already has had at least 1 successful fsid change)
  and finally the metadata_uuid of the fs_devices will equal that of the
  currently scanned disk (because metadata_uuid never really changes).
  When the correct fs_devices is found the information from the scanned
  disk will replace the current one in fs_devices since the scanned disk
  will have higher generation number.

  b) A disk from the second group is scanned so fs_devices is created
  as usual with differing fsid/metdata_uid. Then when a disk from the
  first group is scanned the code detects that it has both
  CHANGING_FSID_V2 and METADATA_UUID flags set and will search for
  fs_devices that has differing metadata_uuid/fsid and whose
  metadata_uuid is the same as that of the scanned device.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
  • Loading branch information
Nikolay Borisov authored and David Sterba committed Dec 17, 2018
1 parent 7a62d0f commit cc5de4e
Showing 1 changed file with 53 additions and 11 deletions.
64 changes: 53 additions & 11 deletions fs/btrfs/volumes.c
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,21 @@ static noinline struct btrfs_fs_devices *find_fsid(
return fs_devices;
}
}
/*
* Handle scanned device having completed its fsid change but
* belonging to a fs_devices that was created by a device that
* has an outdated pair of fsid/metadata_uuid and
* CHANGING_FSID_V2 flag set.
*/
list_for_each_entry(fs_devices, &fs_uuids, fs_list) {
if (fs_devices->fsid_change &&
memcmp(fs_devices->metadata_uuid,
fs_devices->fsid, BTRFS_FSID_SIZE) != 0 &&
memcmp(metadata_fsid, fs_devices->metadata_uuid,
BTRFS_FSID_SIZE) == 0) {
return fs_devices;
}
}
}

/* Handle non-split brain cases */
Expand Down Expand Up @@ -814,6 +829,30 @@ static struct btrfs_fs_devices *find_fsid_inprogress(
return NULL;
}


static struct btrfs_fs_devices *find_fsid_changed(
struct btrfs_super_block *disk_super)
{
struct btrfs_fs_devices *fs_devices;

/*
* Handles the case where scanned device is part of an fs that had
* multiple successful changes of FSID but curently device didn't
* observe it. Meaning our fsid will be different than theirs.
*/
list_for_each_entry(fs_devices, &fs_uuids, fs_list) {
if (memcmp(fs_devices->metadata_uuid, fs_devices->fsid,
BTRFS_FSID_SIZE) != 0 &&
memcmp(fs_devices->metadata_uuid, disk_super->metadata_uuid,
BTRFS_FSID_SIZE) == 0 &&
memcmp(fs_devices->fsid, disk_super->fsid,
BTRFS_FSID_SIZE) != 0) {
return fs_devices;
}
}

return NULL;
}
/*
* Add new device to list of registered devices
*
Expand All @@ -835,17 +874,20 @@ static noinline struct btrfs_device *device_list_add(const char *path,
bool fsid_change_in_progress = (btrfs_super_flags(disk_super) &
BTRFS_SUPER_FLAG_CHANGING_FSID_V2);

if (fsid_change_in_progress && !has_metadata_uuid) {
/*
* When we have an image which has CHANGING_FSID_V2 set it might
* belong to either a filesystem which has disks with completed
* fsid change or it might belong to fs with no UUID changes in
* effect, handle both.
*/
fs_devices = find_fsid_inprogress(disk_super);
if (!fs_devices)
fs_devices = find_fsid(disk_super->fsid, NULL);

if (fsid_change_in_progress) {
if (!has_metadata_uuid) {
/*
* When we have an image which has CHANGING_FSID_V2 set
* it might belong to either a filesystem which has
* disks with completed fsid change or it might belong
* to fs with no UUID changes in effect, handle both.
*/
fs_devices = find_fsid_inprogress(disk_super);
if (!fs_devices)
fs_devices = find_fsid(disk_super->fsid, NULL);
} else {
fs_devices = find_fsid_changed(disk_super);
}
} else if (has_metadata_uuid) {
fs_devices = find_fsid(disk_super->fsid,
disk_super->metadata_uuid);
Expand Down

0 comments on commit cc5de4e

Please sign in to comment.