]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
btrfs: Fix split-brain handling when changing FSID to metadata uuid
authorNikolay Borisov <nborisov@suse.com>
Fri, 10 Jan 2020 12:11:35 +0000 (14:11 +0200)
committerPaolo Pisati <paolo.pisati@canonical.com>
Mon, 24 Feb 2020 15:20:05 +0000 (16:20 +0100)
BugLink: https://bugs.launchpad.net/bugs/1864488
[ Upstream commit 1362089d2ad7e20d16371b39d3c11990d4ec23e4 ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
fs/btrfs/volumes.c

index 9ab3ae5df300532c518d76ad94057f04533a0a3f..3e64f49c394b8bef98109afb5f15da358c7f0552 100644 (file)
@@ -907,6 +907,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
  *
@@ -946,7 +972,9 @@ static noinline struct btrfs_device *device_list_add(const char *path,
                fs_devices = find_fsid(disk_super->fsid,
                                       disk_super->metadata_uuid);
        } 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);
        }
 
 
@@ -976,12 +1004,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;
                }