diff mbox series

[v3,RFC] btrfs: do not skip re-registration for the mounted device

Message ID e2add8d54fbbd813305ba014c11d21d297ad87d0.1709782041.git.anand.jain@oracle.com (mailing list archive)
State New, archived
Headers show
Series [v3,RFC] btrfs: do not skip re-registration for the mounted device | expand

Commit Message

Anand Jain March 7, 2024, 4:13 a.m. UTC
There are reports that since version 6.7 update-grub fails to find the
device of the root on systems without initrd and on a single device.

This looks like the device name changed in the output of
/proc/self/mountinfo:

6.5-rc5 working

  18 1 0:16 / / rw,noatime - btrfs /dev/sda8 ...

6.7 not working:

  17 1 0:15 / / rw,noatime - btrfs /dev/root ...

and "update-grub" shows this error:

  /usr/sbin/grub-probe: error: cannot find a device for / (is /dev mounted?)

This looks like it's related to the device name, but grub-probe
recognizes the "/dev/root" path and tries to find the underlying device.
However there's a special case for some filesystems, for btrfs in
particular.

The generic root device detection heuristic is not done and it all
relies on reading the device infos by a btrfs specific ioctl. This ioctl
returns the device name as it was saved at the time of device scan (in
this case it's /dev/root).

The change in 6.7 for temp_fsid to allow several single device
filesystem to exist with the same fsid (and transparently generate a new
UUID at mount time) was to skip caching/registering such devices.

This also skipped mounted device. One step of scanning is to check if
the device name hasn't changed, and if yes then update the cached value.

This broke the grub-probe as it always read the device /dev/root and
couldn't find it in the system. A temporary workaround is to create a
symlink but this does not survive reboot.

The right fix is to allow updating the device path of a mounted
filesystem even if this is a single device one.

In the fix, check if the device's major:minor number matches with the
cached device. If they do, then we can allow the scan to happen so that
device_list_add() can take care of updating the device path. The file
descriptor remains unchanged.

This does not affect the temp_fsid feature, the UUID of the mounted
filesystem remains the same and the matching is based on device major:minor
which is unique per mounted filesystem.

This covers the path when the device (that exists for all mounted
devices) name changes, updating /dev/root to /dev/sdx. Any other single
device with filesystem and is not mounted is still skipped.

Note that if a system is booted and initial mount is done on the
/dev/root device, this will be the cached name of the device. Only after
the command "btrfs device scan" it will change as it triggers the
rename.

The fix was verified by users whose systems were affected.

Fixes: bc27d6f0aa0e ("btrfs: scan but don't register device on single device filesystem")
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=218353
Link: https://lore.kernel.org/lkml/CAKLYgeJ1tUuqLcsquwuFqjDXPSJpEiokrWK2gisPKDZLs8Y2TQ@mail.gmail.com/
Signed-off-by: Anand Jain <anand.jain@oracle.com>
Tested-by: Alex Romosan <aromosan@gmail.com>
Tested-by: CHECK_1234543212345@protonmail.com
Reviewed-by: David Sterba <dsterba@suse.com>
---
v3:
I removed CC: stable@vger.kernel.org # 6.7+ as this is still in the RFC stage.
I need this patch verified by the bug filer.

Changes in v3:
Verify if the device is opened/mounted to prevent skipping registration,
fixing the following fstests failures.
   ./check btrfs/14[6-9] btrfs/15[8-9]
Update comments.
Only reregister when devt matches but the path differs.

v2:
https://lore.kernel.org/linux-btrfs/88673c60b1d866c289ef019945647adfc8ab51d0.1707781507.git.anand.jain@oracle.com/

 fs/btrfs/volumes.c | 61 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 50 insertions(+), 11 deletions(-)

Comments

Alex Romosan March 7, 2024, 8:36 a.m. UTC | #1
I tried to apply the patch against the latest linux git HEAD but it
failed. Care to send an updated version? If not, I'll try to fix it
myself. Thanks.

On Thu, Mar 7, 2024 at 5:14 AM Anand Jain <anand.jain@oracle.com> wrote:
>
> There are reports that since version 6.7 update-grub fails to find the
> device of the root on systems without initrd and on a single device.
>
> This looks like the device name changed in the output of
> /proc/self/mountinfo:
>
> 6.5-rc5 working
>
>   18 1 0:16 / / rw,noatime - btrfs /dev/sda8 ...
>
> 6.7 not working:
>
>   17 1 0:15 / / rw,noatime - btrfs /dev/root ...
>
> and "update-grub" shows this error:
>
>   /usr/sbin/grub-probe: error: cannot find a device for / (is /dev mounted?)
>
> This looks like it's related to the device name, but grub-probe
> recognizes the "/dev/root" path and tries to find the underlying device.
> However there's a special case for some filesystems, for btrfs in
> particular.
>
> The generic root device detection heuristic is not done and it all
> relies on reading the device infos by a btrfs specific ioctl. This ioctl
> returns the device name as it was saved at the time of device scan (in
> this case it's /dev/root).
>
> The change in 6.7 for temp_fsid to allow several single device
> filesystem to exist with the same fsid (and transparently generate a new
> UUID at mount time) was to skip caching/registering such devices.
>
> This also skipped mounted device. One step of scanning is to check if
> the device name hasn't changed, and if yes then update the cached value.
>
> This broke the grub-probe as it always read the device /dev/root and
> couldn't find it in the system. A temporary workaround is to create a
> symlink but this does not survive reboot.
>
> The right fix is to allow updating the device path of a mounted
> filesystem even if this is a single device one.
>
> In the fix, check if the device's major:minor number matches with the
> cached device. If they do, then we can allow the scan to happen so that
> device_list_add() can take care of updating the device path. The file
> descriptor remains unchanged.
>
> This does not affect the temp_fsid feature, the UUID of the mounted
> filesystem remains the same and the matching is based on device major:minor
> which is unique per mounted filesystem.
>
> This covers the path when the device (that exists for all mounted
> devices) name changes, updating /dev/root to /dev/sdx. Any other single
> device with filesystem and is not mounted is still skipped.
>
> Note that if a system is booted and initial mount is done on the
> /dev/root device, this will be the cached name of the device. Only after
> the command "btrfs device scan" it will change as it triggers the
> rename.
>
> The fix was verified by users whose systems were affected.
>
> Fixes: bc27d6f0aa0e ("btrfs: scan but don't register device on single device filesystem")
> Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=218353
> Link: https://lore.kernel.org/lkml/CAKLYgeJ1tUuqLcsquwuFqjDXPSJpEiokrWK2gisPKDZLs8Y2TQ@mail.gmail.com/
> Signed-off-by: Anand Jain <anand.jain@oracle.com>
> Tested-by: Alex Romosan <aromosan@gmail.com>
> Tested-by: CHECK_1234543212345@protonmail.com
> Reviewed-by: David Sterba <dsterba@suse.com>
> ---
> v3:
> I removed CC: stable@vger.kernel.org # 6.7+ as this is still in the RFC stage.
> I need this patch verified by the bug filer.
>
> Changes in v3:
> Verify if the device is opened/mounted to prevent skipping registration,
> fixing the following fstests failures.
>    ./check btrfs/14[6-9] btrfs/15[8-9]
> Update comments.
> Only reregister when devt matches but the path differs.
>
> v2:
> https://lore.kernel.org/linux-btrfs/88673c60b1d866c289ef019945647adfc8ab51d0.1707781507.git.anand.jain@oracle.com/
>
>  fs/btrfs/volumes.c | 61 +++++++++++++++++++++++++++++++++++++---------
>  1 file changed, 50 insertions(+), 11 deletions(-)
>
> diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
> index e49935a54da0..ea71a2c14927 100644
> --- a/fs/btrfs/volumes.c
> +++ b/fs/btrfs/volumes.c
> @@ -1303,6 +1303,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 update-grub 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->devt == devt) &&
> +                           strcmp(device->name->str, path)) {
> +                               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 +1361,7 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
>         struct btrfs_device *device = NULL;
>         struct bdev_handle *bdev_handle;
>         u64 bytenr, bytenr_orig;
> +       dev_t devt = 0;
>         int ret;
>
>         lockdep_assert_held(&uuid_mutex);
> @@ -1359,19 +1401,16 @@ 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;
> +       ret = lookup_bdev(path, &devt);
> +       if (ret)
> +               btrfs_warn(NULL, "lookup bdev failed for path %s: %d",
> +                          path, ret);
>
> -               ret = lookup_bdev(path, &devt);
> -               if (ret)
> -                       btrfs_warn(NULL, "lookup bdev failed for path %s: %d",
> -                                  path, ret);
> -               else
> +       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));
> +               if (devt)
>                         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;
>         }
> --
> 2.38.1
>
Anand Jain March 7, 2024, 8:47 a.m. UTC | #2
Ah, it is based on the https://github.com/btrfs/linux.git for-next 
branch. I tried applying it again, and it works well.


On 3/7/24 14:06, Alex Romosan wrote:
> I tried to apply the patch against the latest linux git HEAD but it
> failed. Care to send an updated version? If not, I'll try to fix it
> myself. Thanks.
> 
> On Thu, Mar 7, 2024 at 5:14 AM Anand Jain <anand.jain@oracle.com> wrote:
>>
>> There are reports that since version 6.7 update-grub fails to find the
>> device of the root on systems without initrd and on a single device.
>>
>> This looks like the device name changed in the output of
>> /proc/self/mountinfo:
>>
>> 6.5-rc5 working
>>
>>    18 1 0:16 / / rw,noatime - btrfs /dev/sda8 ...
>>
>> 6.7 not working:
>>
>>    17 1 0:15 / / rw,noatime - btrfs /dev/root ...
>>
>> and "update-grub" shows this error:
>>
>>    /usr/sbin/grub-probe: error: cannot find a device for / (is /dev mounted?)
>>
>> This looks like it's related to the device name, but grub-probe
>> recognizes the "/dev/root" path and tries to find the underlying device.
>> However there's a special case for some filesystems, for btrfs in
>> particular.
>>
>> The generic root device detection heuristic is not done and it all
>> relies on reading the device infos by a btrfs specific ioctl. This ioctl
>> returns the device name as it was saved at the time of device scan (in
>> this case it's /dev/root).
>>
>> The change in 6.7 for temp_fsid to allow several single device
>> filesystem to exist with the same fsid (and transparently generate a new
>> UUID at mount time) was to skip caching/registering such devices.
>>
>> This also skipped mounted device. One step of scanning is to check if
>> the device name hasn't changed, and if yes then update the cached value.
>>
>> This broke the grub-probe as it always read the device /dev/root and
>> couldn't find it in the system. A temporary workaround is to create a
>> symlink but this does not survive reboot.
>>
>> The right fix is to allow updating the device path of a mounted
>> filesystem even if this is a single device one.
>>
>> In the fix, check if the device's major:minor number matches with the
>> cached device. If they do, then we can allow the scan to happen so that
>> device_list_add() can take care of updating the device path. The file
>> descriptor remains unchanged.
>>
>> This does not affect the temp_fsid feature, the UUID of the mounted
>> filesystem remains the same and the matching is based on device major:minor
>> which is unique per mounted filesystem.
>>
>> This covers the path when the device (that exists for all mounted
>> devices) name changes, updating /dev/root to /dev/sdx. Any other single
>> device with filesystem and is not mounted is still skipped.
>>
>> Note that if a system is booted and initial mount is done on the
>> /dev/root device, this will be the cached name of the device. Only after
>> the command "btrfs device scan" it will change as it triggers the
>> rename.
>>
>> The fix was verified by users whose systems were affected.
>>
>> Fixes: bc27d6f0aa0e ("btrfs: scan but don't register device on single device filesystem")
>> Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=218353
>> Link: https://lore.kernel.org/lkml/CAKLYgeJ1tUuqLcsquwuFqjDXPSJpEiokrWK2gisPKDZLs8Y2TQ@mail.gmail.com/
>> Signed-off-by: Anand Jain <anand.jain@oracle.com>
>> Tested-by: Alex Romosan <aromosan@gmail.com>
>> Tested-by: CHECK_1234543212345@protonmail.com
>> Reviewed-by: David Sterba <dsterba@suse.com>
>> ---
>> v3:
>> I removed CC: stable@vger.kernel.org # 6.7+ as this is still in the RFC stage.
>> I need this patch verified by the bug filer.
>>
>> Changes in v3:
>> Verify if the device is opened/mounted to prevent skipping registration,
>> fixing the following fstests failures.
>>     ./check btrfs/14[6-9] btrfs/15[8-9]
>> Update comments.
>> Only reregister when devt matches but the path differs.
>>
>> v2:
>> https://lore.kernel.org/linux-btrfs/88673c60b1d866c289ef019945647adfc8ab51d0.1707781507.git.anand.jain@oracle.com/
>>
>>   fs/btrfs/volumes.c | 61 +++++++++++++++++++++++++++++++++++++---------
>>   1 file changed, 50 insertions(+), 11 deletions(-)
>>
>> diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
>> index e49935a54da0..ea71a2c14927 100644
>> --- a/fs/btrfs/volumes.c
>> +++ b/fs/btrfs/volumes.c
>> @@ -1303,6 +1303,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 update-grub 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->devt == devt) &&
>> +                           strcmp(device->name->str, path)) {
>> +                               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 +1361,7 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
>>          struct btrfs_device *device = NULL;
>>          struct bdev_handle *bdev_handle;
>>          u64 bytenr, bytenr_orig;
>> +       dev_t devt = 0;
>>          int ret;
>>
>>          lockdep_assert_held(&uuid_mutex);
>> @@ -1359,19 +1401,16 @@ 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;
>> +       ret = lookup_bdev(path, &devt);
>> +       if (ret)
>> +               btrfs_warn(NULL, "lookup bdev failed for path %s: %d",
>> +                          path, ret);
>>
>> -               ret = lookup_bdev(path, &devt);
>> -               if (ret)
>> -                       btrfs_warn(NULL, "lookup bdev failed for path %s: %d",
>> -                                  path, ret);
>> -               else
>> +       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));
>> +               if (devt)
>>                          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;
>>          }
>> --
>> 2.38.1
>>
Anand Jain March 7, 2024, 9 a.m. UTC | #3
Sending one for the mainline version. Thx.


On 3/7/24 14:17, Anand Jain wrote:
> 
> Ah, it is based on the https://github.com/btrfs/linux.git for-next 
> branch. I tried applying it again, and it works well.
> 
> 
> On 3/7/24 14:06, Alex Romosan wrote:
>> I tried to apply the patch against the latest linux git HEAD but it
>> failed. Care to send an updated version? If not, I'll try to fix it
>> myself. Thanks.
>>
>> On Thu, Mar 7, 2024 at 5:14 AM Anand Jain <anand.jain@oracle.com> wrote:
>>>
>>> There are reports that since version 6.7 update-grub fails to find the
>>> device of the root on systems without initrd and on a single device.
>>>
>>> This looks like the device name changed in the output of
>>> /proc/self/mountinfo:
>>>
>>> 6.5-rc5 working
>>>
>>>    18 1 0:16 / / rw,noatime - btrfs /dev/sda8 ...
>>>
>>> 6.7 not working:
>>>
>>>    17 1 0:15 / / rw,noatime - btrfs /dev/root ...
>>>
>>> and "update-grub" shows this error:
>>>
>>>    /usr/sbin/grub-probe: error: cannot find a device for / (is /dev 
>>> mounted?)
>>>
>>> This looks like it's related to the device name, but grub-probe
>>> recognizes the "/dev/root" path and tries to find the underlying device.
>>> However there's a special case for some filesystems, for btrfs in
>>> particular.
>>>
>>> The generic root device detection heuristic is not done and it all
>>> relies on reading the device infos by a btrfs specific ioctl. This ioctl
>>> returns the device name as it was saved at the time of device scan (in
>>> this case it's /dev/root).
>>>
>>> The change in 6.7 for temp_fsid to allow several single device
>>> filesystem to exist with the same fsid (and transparently generate a new
>>> UUID at mount time) was to skip caching/registering such devices.
>>>
>>> This also skipped mounted device. One step of scanning is to check if
>>> the device name hasn't changed, and if yes then update the cached value.
>>>
>>> This broke the grub-probe as it always read the device /dev/root and
>>> couldn't find it in the system. A temporary workaround is to create a
>>> symlink but this does not survive reboot.
>>>
>>> The right fix is to allow updating the device path of a mounted
>>> filesystem even if this is a single device one.
>>>
>>> In the fix, check if the device's major:minor number matches with the
>>> cached device. If they do, then we can allow the scan to happen so that
>>> device_list_add() can take care of updating the device path. The file
>>> descriptor remains unchanged.
>>>
>>> This does not affect the temp_fsid feature, the UUID of the mounted
>>> filesystem remains the same and the matching is based on device 
>>> major:minor
>>> which is unique per mounted filesystem.
>>>
>>> This covers the path when the device (that exists for all mounted
>>> devices) name changes, updating /dev/root to /dev/sdx. Any other single
>>> device with filesystem and is not mounted is still skipped.
>>>
>>> Note that if a system is booted and initial mount is done on the
>>> /dev/root device, this will be the cached name of the device. Only after
>>> the command "btrfs device scan" it will change as it triggers the
>>> rename.
>>>
>>> The fix was verified by users whose systems were affected.
>>>
>>> Fixes: bc27d6f0aa0e ("btrfs: scan but don't register device on single 
>>> device filesystem")
>>> Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=218353
>>> Link: 
>>> https://lore.kernel.org/lkml/CAKLYgeJ1tUuqLcsquwuFqjDXPSJpEiokrWK2gisPKDZLs8Y2TQ@mail.gmail.com/
>>> Signed-off-by: Anand Jain <anand.jain@oracle.com>
>>> Tested-by: Alex Romosan <aromosan@gmail.com>
>>> Tested-by: CHECK_1234543212345@protonmail.com
>>> Reviewed-by: David Sterba <dsterba@suse.com>
>>> ---
>>> v3:
>>> I removed CC: stable@vger.kernel.org # 6.7+ as this is still in the 
>>> RFC stage.
>>> I need this patch verified by the bug filer.
>>>
>>> Changes in v3:
>>> Verify if the device is opened/mounted to prevent skipping registration,
>>> fixing the following fstests failures.
>>>     ./check btrfs/14[6-9] btrfs/15[8-9]
>>> Update comments.
>>> Only reregister when devt matches but the path differs.
>>>
>>> v2:
>>> https://lore.kernel.org/linux-btrfs/88673c60b1d866c289ef019945647adfc8ab51d0.1707781507.git.anand.jain@oracle.com/
>>>
>>>   fs/btrfs/volumes.c | 61 +++++++++++++++++++++++++++++++++++++---------
>>>   1 file changed, 50 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
>>> index e49935a54da0..ea71a2c14927 100644
>>> --- a/fs/btrfs/volumes.c
>>> +++ b/fs/btrfs/volumes.c
>>> @@ -1303,6 +1303,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 update-grub 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->devt == devt) &&
>>> +                           strcmp(device->name->str, path)) {
>>> +                               
>>> 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 +1361,7 @@ struct btrfs_device 
>>> *btrfs_scan_one_device(const char *path, blk_mode_t flags,
>>>          struct btrfs_device *device = NULL;
>>>          struct bdev_handle *bdev_handle;
>>>          u64 bytenr, bytenr_orig;
>>> +       dev_t devt = 0;
>>>          int ret;
>>>
>>>          lockdep_assert_held(&uuid_mutex);
>>> @@ -1359,19 +1401,16 @@ 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;
>>> +       ret = lookup_bdev(path, &devt);
>>> +       if (ret)
>>> +               btrfs_warn(NULL, "lookup bdev failed for path %s: %d",
>>> +                          path, ret);
>>>
>>> -               ret = lookup_bdev(path, &devt);
>>> -               if (ret)
>>> -                       btrfs_warn(NULL, "lookup bdev failed for path 
>>> %s: %d",
>>> -                                  path, ret);
>>> -               else
>>> +       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));
>>> +               if (devt)
>>>                          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;
>>>          }
>>> -- 
>>> 2.38.1
>>>
Alex Romosan March 7, 2024, 9:21 a.m. UTC | #4
can you send it as an attachment (or at least a link to the actual
patch)? i think part of the problem with the patch not applying was
tabs vs spaces. thanks

On Thu, Mar 7, 2024 at 10:00 AM Anand Jain <anand.jain@oracle.com> wrote:
>
>
> Sending one for the mainline version. Thx.
>
>
> On 3/7/24 14:17, Anand Jain wrote:
> >
> > Ah, it is based on the https://github.com/btrfs/linux.git for-next
> > branch. I tried applying it again, and it works well.
> >
> >
> > On 3/7/24 14:06, Alex Romosan wrote:
> >> I tried to apply the patch against the latest linux git HEAD but it
> >> failed. Care to send an updated version? If not, I'll try to fix it
> >> myself. Thanks.
> >>
> >> On Thu, Mar 7, 2024 at 5:14 AM Anand Jain <anand.jain@oracle.com> wrote:
> >>>
> >>> There are reports that since version 6.7 update-grub fails to find the
> >>> device of the root on systems without initrd and on a single device.
> >>>
> >>> This looks like the device name changed in the output of
> >>> /proc/self/mountinfo:
> >>>
> >>> 6.5-rc5 working
> >>>
> >>>    18 1 0:16 / / rw,noatime - btrfs /dev/sda8 ...
> >>>
> >>> 6.7 not working:
> >>>
> >>>    17 1 0:15 / / rw,noatime - btrfs /dev/root ...
> >>>
> >>> and "update-grub" shows this error:
> >>>
> >>>    /usr/sbin/grub-probe: error: cannot find a device for / (is /dev
> >>> mounted?)
> >>>
> >>> This looks like it's related to the device name, but grub-probe
> >>> recognizes the "/dev/root" path and tries to find the underlying device.
> >>> However there's a special case for some filesystems, for btrfs in
> >>> particular.
> >>>
> >>> The generic root device detection heuristic is not done and it all
> >>> relies on reading the device infos by a btrfs specific ioctl. This ioctl
> >>> returns the device name as it was saved at the time of device scan (in
> >>> this case it's /dev/root).
> >>>
> >>> The change in 6.7 for temp_fsid to allow several single device
> >>> filesystem to exist with the same fsid (and transparently generate a new
> >>> UUID at mount time) was to skip caching/registering such devices.
> >>>
> >>> This also skipped mounted device. One step of scanning is to check if
> >>> the device name hasn't changed, and if yes then update the cached value.
> >>>
> >>> This broke the grub-probe as it always read the device /dev/root and
> >>> couldn't find it in the system. A temporary workaround is to create a
> >>> symlink but this does not survive reboot.
> >>>
> >>> The right fix is to allow updating the device path of a mounted
> >>> filesystem even if this is a single device one.
> >>>
> >>> In the fix, check if the device's major:minor number matches with the
> >>> cached device. If they do, then we can allow the scan to happen so that
> >>> device_list_add() can take care of updating the device path. The file
> >>> descriptor remains unchanged.
> >>>
> >>> This does not affect the temp_fsid feature, the UUID of the mounted
> >>> filesystem remains the same and the matching is based on device
> >>> major:minor
> >>> which is unique per mounted filesystem.
> >>>
> >>> This covers the path when the device (that exists for all mounted
> >>> devices) name changes, updating /dev/root to /dev/sdx. Any other single
> >>> device with filesystem and is not mounted is still skipped.
> >>>
> >>> Note that if a system is booted and initial mount is done on the
> >>> /dev/root device, this will be the cached name of the device. Only after
> >>> the command "btrfs device scan" it will change as it triggers the
> >>> rename.
> >>>
> >>> The fix was verified by users whose systems were affected.
> >>>
> >>> Fixes: bc27d6f0aa0e ("btrfs: scan but don't register device on single
> >>> device filesystem")
> >>> Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=218353
> >>> Link:
> >>> https://lore.kernel.org/lkml/CAKLYgeJ1tUuqLcsquwuFqjDXPSJpEiokrWK2gisPKDZLs8Y2TQ@mail.gmail.com/
> >>> Signed-off-by: Anand Jain <anand.jain@oracle.com>
> >>> Tested-by: Alex Romosan <aromosan@gmail.com>
> >>> Tested-by: CHECK_1234543212345@protonmail.com
> >>> Reviewed-by: David Sterba <dsterba@suse.com>
> >>> ---
> >>> v3:
> >>> I removed CC: stable@vger.kernel.org # 6.7+ as this is still in the
> >>> RFC stage.
> >>> I need this patch verified by the bug filer.
> >>>
> >>> Changes in v3:
> >>> Verify if the device is opened/mounted to prevent skipping registration,
> >>> fixing the following fstests failures.
> >>>     ./check btrfs/14[6-9] btrfs/15[8-9]
> >>> Update comments.
> >>> Only reregister when devt matches but the path differs.
> >>>
> >>> v2:
> >>> https://lore.kernel.org/linux-btrfs/88673c60b1d866c289ef019945647adfc8ab51d0.1707781507.git.anand.jain@oracle.com/
> >>>
> >>>   fs/btrfs/volumes.c | 61 +++++++++++++++++++++++++++++++++++++---------
> >>>   1 file changed, 50 insertions(+), 11 deletions(-)
> >>>
> >>> diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
> >>> index e49935a54da0..ea71a2c14927 100644
> >>> --- a/fs/btrfs/volumes.c
> >>> +++ b/fs/btrfs/volumes.c
> >>> @@ -1303,6 +1303,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 update-grub 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->devt == devt) &&
> >>> +                           strcmp(device->name->str, path)) {
> >>> +
> >>> 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 +1361,7 @@ struct btrfs_device
> >>> *btrfs_scan_one_device(const char *path, blk_mode_t flags,
> >>>          struct btrfs_device *device = NULL;
> >>>          struct bdev_handle *bdev_handle;
> >>>          u64 bytenr, bytenr_orig;
> >>> +       dev_t devt = 0;
> >>>          int ret;
> >>>
> >>>          lockdep_assert_held(&uuid_mutex);
> >>> @@ -1359,19 +1401,16 @@ 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;
> >>> +       ret = lookup_bdev(path, &devt);
> >>> +       if (ret)
> >>> +               btrfs_warn(NULL, "lookup bdev failed for path %s: %d",
> >>> +                          path, ret);
> >>>
> >>> -               ret = lookup_bdev(path, &devt);
> >>> -               if (ret)
> >>> -                       btrfs_warn(NULL, "lookup bdev failed for path
> >>> %s: %d",
> >>> -                                  path, ret);
> >>> -               else
> >>> +       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));
> >>> +               if (devt)
> >>>                          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;
> >>>          }
> >>> --
> >>> 2.38.1
> >>>
diff mbox series

Patch

diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index e49935a54da0..ea71a2c14927 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -1303,6 +1303,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 update-grub 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->devt == devt) &&
+			    strcmp(device->name->str, path)) {
+				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 +1361,7 @@  struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
 	struct btrfs_device *device = NULL;
 	struct bdev_handle *bdev_handle;
 	u64 bytenr, bytenr_orig;
+	dev_t devt = 0;
 	int ret;
 
 	lockdep_assert_held(&uuid_mutex);
@@ -1359,19 +1401,16 @@  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;
+	ret = lookup_bdev(path, &devt);
+	if (ret)
+		btrfs_warn(NULL, "lookup bdev failed for path %s: %d",
+			   path, ret);
 
-		ret = lookup_bdev(path, &devt);
-		if (ret)
-			btrfs_warn(NULL, "lookup bdev failed for path %s: %d",
-				   path, ret);
-		else
+	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));
+		if (devt)
 			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;
 	}