summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQu Wenruo <wqu@suse.com>2026-09-12 18:12:06 +0930
committerDavid Sterba <dsterba@suse.com>2026-09-17 17:05:50 +0200
commit5321707948bfcf3a9d13073b310ae3095a04e523 (patch)
tree8e5f0a3e0731eaefda8b612a73580e519f00f10b
parentb351dbcd5a1c7b53552c51c7175d9b1c112cd7db (diff)
downloadlinux-next-5321707948bfcf3a9d13073b310ae3095a04e523.tar.gz
linux-next-5321707948bfcf3a9d13073b310ae3095a04e523.zip
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 <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/volumes.c33
1 files changed, 32 insertions, 1 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 4ddabadc9188..7c040f22dbc3 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;
/*