diff --git a/rules.d/60-persistent-storage.rules b/rules.d/60-persistent-storage.rules index 03f0a619dc..64a2409196 100644 --- a/rules.d/60-persistent-storage.rules +++ b/rules.d/60-persistent-storage.rules @@ -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" diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c index ce7bc5caf0..7ec2e94583 100644 --- a/src/udev/udev-builtin-path_id.c +++ b/src/udev/udev-builtin-path_id.c @@ -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;