]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: support by-path devlink for multipath nvme block devices
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 20 Sep 2022 17:26:42 +0000 (02:26 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 23 Sep 2022 01:21:46 +0000 (10:21 +0900)
If multipath feature is enabled, nvme block devices may belong to the
"nvme-subsystem" subsystem, instead of "nvme" subsystem.
(What a confusing name...)

Then, the syspath is something like the following,
    /sys/devices/virtual/nvme-subsystem/nvme-subsys0/nvme0n1
Hence, we need to find the 'real parent' device, such as
    /sys/devices/pci0000:00/0000:00:1c.4/0000:3c:00.0/nvme/nvme0

Fixes https://bugzilla.redhat.com/show_bug.cgi?id=2031810.
Fixes https://bugzilla.redhat.com/show_bug.cgi?id=2124964.
Replaces #24748.

rules.d/60-persistent-storage.rules
src/udev/udev-builtin-path_id.c

index 03f0a619dc4568e6bc234fb1048bf1cffbda7da9..64a24091964bf5c7c9e14480e4e64a675a5235e5 100644 (file)
@@ -88,6 +88,7 @@ KERNEL=="msblk[0-9]p[0-9]|mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}
 
 # by-path
 ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id"
+ENV{DEVTYPE}=="disk", SUBSYSTEMS=="nvme-subsystem", IMPORT{builtin}="path_id"
 KERNEL=="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-boot%n"
 KERNEL!="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}"
 ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n"
index ce7bc5caf047c93edef3b23766143b7d8e9fce5c..7ec2e94583e18b18c4635a2b67c69a64afc4a792 100644 (file)
@@ -543,19 +543,55 @@ static sd_device *handle_ap(sd_device *parent, char **path) {
         return skip_subsystem(parent, "ap");
 }
 
+static int find_real_nvme_parent(sd_device *dev, sd_device **ret) {
+        _cleanup_(sd_device_unrefp) sd_device *nvme = NULL;
+        const char *sysname, *end;
+        int r;
+
+        /* If the device belongs to "nvme-subsystem" (not to be confused with "nvme"), which happens when
+         * NVMe multipathing is enabled in the kernel (/sys/module/nvme_core/parameters/multipath is Y),
+         * then the syspath is something like the following:
+         *   /sys/devices/virtual/nvme-subsystem/nvme-subsys0/nvme0n1
+         * Hence, we need to find the 'real parent' in "nvme" subsystem, e.g,
+         *   /sys/devices/pci0000:00/0000:00:1c.4/0000:3c:00.0/nvme/nvme0 */
+
+        assert(dev);
+        assert(nvme);
+
+        r = sd_device_get_sysname(dev, &sysname);
+        if (r < 0)
+                return r;
+
+        /* The sysname format of nvme block device is nvme%d[c%d]n%d[p%d], e.g. nvme0n1p2 or nvme0c1n2.
+         * (Note, nvme device with 'c' can be ignored, as they are hidden. )
+         * The sysname format of nvme subsystem device is nvme%d.
+         * See nvme_alloc_ns() and nvme_init_ctrl() in drivers/nvme/host/core.c for more details. */
+        end = startswith(sysname, "nvme");
+        if (!end)
+                return -ENXIO;
+
+        end += strspn(end, DIGITS);
+        sysname = strndupa(sysname, end - sysname);
+
+        r = sd_device_new_from_subsystem_sysname(&nvme, "nvme", sysname);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(nvme);
+        return 0;
+}
+
 static int builtin_path_id(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) {
-        sd_device *parent;
-        _cleanup_free_ char *path = NULL;
-        _cleanup_free_ char *compat_path = NULL;
-        bool supported_transport = false;
-        bool supported_parent = false;
+        _cleanup_(sd_device_unrefp) sd_device *dev_other_branch = NULL;
+        _cleanup_free_ char *path = NULL, *compat_path = NULL;
+        bool supported_transport = false, supported_parent = false;
         const char *subsystem;
+        int r;
 
         assert(dev);
 
         /* walk up the chain of devices and compose path */
-        parent = dev;
-        while (parent) {
+        for (sd_device *parent = dev; parent; ) {
                 const char *subsys, *sysname;
 
                 if (sd_device_get_subsystem(parent, &subsys) < 0 ||
@@ -642,13 +678,22 @@ static int builtin_path_id(sd_device *dev, sd_netlink **rtnl, int argc, char *ar
                         parent = skip_subsystem(parent, "iucv");
                         supported_transport = true;
                         supported_parent = true;
-                } else if (streq(subsys, "nvme")) {
+                } else if (STR_IN_SET(subsys, "nvme", "nvme-subsystem")) {
                         const char *nsid;
 
                         if (sd_device_get_sysattr_value(dev, "nsid", &nsid) >= 0) {
                                 path_prepend(&path, "nvme-%s", nsid);
                                 if (compat_path)
                                         path_prepend(&compat_path, "nvme-%s", nsid);
+
+                                if (streq(subsys, "nvme-subsystem")) {
+                                        r = find_real_nvme_parent(dev, &dev_other_branch);
+                                        if (r < 0)
+                                                return r;
+
+                                        parent = dev_other_branch;
+                                }
+
                                 parent = skip_subsystem(parent, "nvme");
                                 supported_parent = true;
                                 supported_transport = true;