]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - fs/btrfs/volumes.c
Merge tag 'for-6.9-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[thirdparty/linux.git] / fs / btrfs / volumes.c
index a2d07fa3cfdff4081f34365d121cc7acdf845750..f15591f3e54fa4cd7e92103e17b0ae74eb1a54f9 100644 (file)
@@ -692,6 +692,16 @@ static int btrfs_open_one_device(struct btrfs_fs_devices *fs_devices,
        device->bdev = file_bdev(bdev_file);
        clear_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state);
 
+       if (device->devt != device->bdev->bd_dev) {
+               btrfs_warn(NULL,
+                          "device %s maj:min changed from %d:%d to %d:%d",
+                          device->name->str, MAJOR(device->devt),
+                          MINOR(device->devt), MAJOR(device->bdev->bd_dev),
+                          MINOR(device->bdev->bd_dev));
+
+               device->devt = device->bdev->bd_dev;
+       }
+
        fs_devices->open_devices++;
        if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state) &&
            device->devid != BTRFS_DEV_REPLACE_DEVID) {
@@ -1174,23 +1184,30 @@ static int open_fs_devices(struct btrfs_fs_devices *fs_devices,
        struct btrfs_device *device;
        struct btrfs_device *latest_dev = NULL;
        struct btrfs_device *tmp_device;
+       int ret = 0;
 
        list_for_each_entry_safe(device, tmp_device, &fs_devices->devices,
                                 dev_list) {
-               int ret;
+               int ret2;
 
-               ret = btrfs_open_one_device(fs_devices, device, flags, holder);
-               if (ret == 0 &&
+               ret2 = btrfs_open_one_device(fs_devices, device, flags, holder);
+               if (ret2 == 0 &&
                    (!latest_dev || device->generation > latest_dev->generation)) {
                        latest_dev = device;
-               } else if (ret == -ENODATA) {
+               } else if (ret2 == -ENODATA) {
                        fs_devices->num_devices--;
                        list_del(&device->dev_list);
                        btrfs_free_device(device);
                }
+               if (ret == 0 && ret2 != 0)
+                       ret = ret2;
        }
-       if (fs_devices->open_devices == 0)
+
+       if (fs_devices->open_devices == 0) {
+               if (ret)
+                       return ret;
                return -EINVAL;
+       }
 
        fs_devices->opened = 1;
        fs_devices->latest_dev = latest_dev;
@@ -1303,6 +1320,47 @@ int btrfs_forget_devices(dev_t devt)
        return ret;
 }
 
+static bool btrfs_skip_registration(struct btrfs_super_block *disk_super,
+                                   const char *path, dev_t devt,
+                                   bool mount_arg_dev)
+{
+       struct btrfs_fs_devices *fs_devices;
+
+       /*
+        * Do not skip device registration for mounted devices with matching
+        * maj:min but different paths. Booting without initrd relies on
+        * /dev/root initially, later replaced with the actual root device.
+        * A successful scan ensures grub2-probe selects the correct device.
+        */
+       list_for_each_entry(fs_devices, &fs_uuids, fs_list) {
+               struct btrfs_device *device;
+
+               mutex_lock(&fs_devices->device_list_mutex);
+
+               if (!fs_devices->opened) {
+                       mutex_unlock(&fs_devices->device_list_mutex);
+                       continue;
+               }
+
+               list_for_each_entry(device, &fs_devices->devices, dev_list) {
+                       if (device->bdev && (device->bdev->bd_dev == devt) &&
+                           strcmp(device->name->str, path) != 0) {
+                               mutex_unlock(&fs_devices->device_list_mutex);
+
+                               /* Do not skip registration. */
+                               return false;
+                       }
+               }
+               mutex_unlock(&fs_devices->device_list_mutex);
+       }
+
+       if (!mount_arg_dev && btrfs_super_num_devices(disk_super) == 1 &&
+           !(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_SEEDING))
+               return true;
+
+       return false;
+}
+
 /*
  * Look for a btrfs signature on a device. This may be called out of the mount path
  * and we are not allowed to call set_blocksize during the scan. The superblock
@@ -1320,6 +1378,7 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
        struct btrfs_device *device = NULL;
        struct file *bdev_file;
        u64 bytenr, bytenr_orig;
+       dev_t devt;
        int ret;
 
        lockdep_assert_held(&uuid_mutex);
@@ -1359,19 +1418,13 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
                goto error_bdev_put;
        }
 
-       if (!mount_arg_dev && btrfs_super_num_devices(disk_super) == 1 &&
-           !(btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_SEEDING)) {
-               dev_t devt;
+       devt = file_bdev(bdev_file)->bd_dev;
+       if (btrfs_skip_registration(disk_super, path, devt, mount_arg_dev)) {
+               pr_debug("BTRFS: skip registering single non-seed device %s (%d:%d)\n",
+                         path, MAJOR(devt), MINOR(devt));
 
-               ret = lookup_bdev(path, &devt);
-               if (ret)
-                       btrfs_warn(NULL, "lookup bdev failed for path %s: %d",
-                                  path, ret);
-               else
-                       btrfs_free_stale_devices(devt, NULL);
+               btrfs_free_stale_devices(devt, NULL);
 
-       pr_debug("BTRFS: skip registering single non-seed device %s (%d:%d)\n",
-                       path, MAJOR(devt), MINOR(devt));
                device = NULL;
                goto free_disk_super;
        }