From: Qu Wenruo Date: Sat, 12 Sep 2026 08:42:06 +0000 (+0930) Subject: btrfs: add "/dev/root" exception for device path update X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5321707948bfcf3a9d13073b310ae3095a04e523;p=linux btrfs: add "/dev/root" exception for device path update [BEHAVIOR CHANGE] Since commit 108cc8733989 ("btrfs: fix a lockdep caused by path resolution during device scan"), users with btrfs rootfs but without an initramfs are complaining that grub2 can no longer detect the rootfs device: /usr/sbin/grub-probe: error: cannot find a device for / (is /dev mounted?). [CAUSE] Although using btrfs without an initramfs is not recommended (if a new device is added to the rootfs, the system can no longer boot, as there is no way to register all devices), there is still a minority of users doing this. If there is no initramfs but the rootfs is on a block-device-based filesystem, the kernel boot sequence initializes a minimal ramfs/tmpfs, creates "/dev/root" with the proper device number for the rootfs, and then invokes mount using "/dev/root". That's why the end user will get the mount output: /dev/root on / rw To be honest, this is a user space problem: no one should trust the device path shown in mount, only the device number. E.g. one can even use "/proc/self/fd/*" to mount an fs, and that proc path will be registered, and no one else can mount that fs using that path. Before commit 108cc8733989 ("btrfs: fix a lockdep caused by path resolution during device scan"), btrfs had an internal path lookup workaround to address such weird paths, it works by checking if the existing device path can still resolve to the device number. But that path resolution is deadlock prone, thus it's replaced by a simple devt check. This works fine in most cases, as a btrfs device is registered by udev at boot time, thus all paths are sane. However this will not work for systems without an initramfs, causing the unreachable "/dev/root" path to exist forever without a way to rename it. [WORKAROUND] Despite updating the docs to discourage root btrfs without an initramfs, add an exception to the device path rename requirement. If the device has the name "/dev/root", we know it's booted without an initramfs, and only for that case we allow device path update. And if someone intentionally created "/dev/root" after boot, the existing devt checks will reject that weird name as usual. This should satisfy the minority of users, and still keep most of the existing guards preventing unexpected/unnecessary device path updates. But still, I prefer grub2 to implement a more robust device number based probing, and no one should use btrfs as rootfs without an initramfs. Fixes: 108cc8733989 ("btrfs: fix a lockdep caused by path resolution during device scan") Link: https://lore.kernel.org/linux-btrfs/CAKLYgeL7nrA4nXcewdv9Fqg_s=3GS=vmoypnEiZBKQ7rySZFuQ@mail.gmail.com/ Link: https://lore.kernel.org/linux-btrfs/dfbe1e27-dab8-4d55-8cf3-0b28eeac5df4@gmail.com/ Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 4ddabadc91885..7c040f22dbc3e 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -749,6 +749,36 @@ const u8 *btrfs_sb_fsid_ptr(const struct btrfs_super_block *sb) return has_metadata_uuid ? sb->metadata_uuid : sb->fsid; } +static bool should_rename_device(const struct btrfs_device *dev) +{ + bool ret; + const char *old_name; + + rcu_read_lock(); + old_name = rcu_dereference(dev->name); + /* + * For systems booted without an initramfs, the rootfs has the device + * name "/dev/root". + * + * Although using btrfs without an initramfs is not recommended (if a + * new device is added to the rootfs, the system can no longer boot, as + * there is no way to register all devices), there is still a minority + * of users doing this. + * + * And after the system is up, a later device scan on the real block + * device file will never get this device's name updated, as the + * device->devt is still the same. + * + * Here we add one and only one exception for "/dev/root", to allow the + * device name to be updated even if the new path points to the same + * block device. + */ + ret = (strcmp(old_name, "/dev/root") == 0); + rcu_read_unlock(); + + return ret; +} + /* * Add new device to list of registered devices * @@ -869,7 +899,8 @@ static noinline struct btrfs_device *device_list_add(const char *path, MAJOR(path_devt), MINOR(path_devt), current->comm, task_pid_nr(current)); - } else if (!device->name || device->devt != path_devt) { + } else if (!device->name || device->devt != path_devt || + should_rename_device(device)) { const char *old_name; /*