]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
util: Add support for parsing nvmeXnY(pZ) strings
authorMartin Kletzander <mkletzan@redhat.com>
Thu, 22 May 2025 09:26:47 +0000 (11:26 +0200)
committerMartin Kletzander <mkletzan@redhat.com>
Mon, 9 Jun 2025 13:12:38 +0000 (15:12 +0200)
We do not guarantee that the disk names will be the same in guest as
they are in the XML, but that should not stop us from trying to be
consistent with the naming.  And since we use the same naming as the
linux kernel does, let's stick with it with nvme drives too.

Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
src/hyperv/hyperv_driver.c
src/libxl/libxl_driver.c
src/qemu/qemu_validate.c
src/util/virutil.c
src/util/virutil.h
src/vbox/vbox_common.c
src/vmx/vmx.c
src/vz/vz_sdk.c
tests/utiltest.c

index 0d1e388c08d72bd87801349e740b46de7bc9bdbd..25eb8872b3bf340a5ab13cc5dae1338800574bce 100644 (file)
@@ -1230,7 +1230,7 @@ hypervDomainDefAppendDisk(virDomainDef *def,
     }
 
     disk->bus = busType;
-    disk->dst = virIndexToDiskName(ctrlr_idx * diskNameOffset + addr, diskNamePrefix);
+    disk->dst = virIndexToDiskName(0, ctrlr_idx * diskNameOffset + addr, diskNamePrefix);
     if (busType == VIR_DOMAIN_DISK_BUS_IDE) {
         disk->info.addr.drive.controller = 0;
         disk->info.addr.drive.bus = ctrlr_idx;
@@ -1398,7 +1398,7 @@ hypervDomainDefParsePhysicalDisk(hypervPrivate *priv,
             }
         }
         disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
-        disk->dst = virIndexToDiskName(ctrlr_idx * 64 + addr, "sd");
+        disk->dst = virIndexToDiskName(0, ctrlr_idx * 64 + addr, "sd");
         disk->info.addr.drive.unit = addr;
         disk->info.addr.drive.controller = ctrlr_idx;
         disk->info.addr.drive.bus = 0;
@@ -1410,7 +1410,7 @@ hypervDomainDefParsePhysicalDisk(hypervPrivate *priv,
             }
         }
         disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
-        disk->dst = virIndexToDiskName(ctrlr_idx * 4 + addr, "hd");
+        disk->dst = virIndexToDiskName(0, ctrlr_idx * 4 + addr, "hd");
         disk->info.addr.drive.unit = addr;
         disk->info.addr.drive.controller = 0;
         disk->info.addr.drive.bus = ctrlr_idx;
index 0fb256e5c06df04f9c3ad102e7618756eed83c66..308c0372aaf4b96f36c4b22b9f51522ad7f8b423 100644 (file)
@@ -5440,7 +5440,7 @@ libxlDiskPathToID(const char *virtpath)
     /* Find any disk prefixes we know about */
     for (i = 0; i < G_N_ELEMENTS(drive_prefix); i++) {
         if (STRPREFIX(virtpath, drive_prefix[i]) &&
-            !virDiskNameParse(virtpath, &disk, &partition)) {
+            !virDiskNameParse(virtpath, NULL, &disk, &partition)) {
             fmt = i;
             break;
         }
index 56747a38bea06edb0fd7839f2b6b236c38bb7618..ab75a3e0469df18dffe7e591ad2a60553f966473 100644 (file)
@@ -3445,7 +3445,7 @@ qemuValidateDomainDeviceDefDisk(const virDomainDiskDef *disk,
         return -1;
     }
 
-    if (virDiskNameParse(disk->dst, &idx, &partition) < 0) {
+    if (virDiskNameParse(disk->dst, NULL, &idx, &partition) < 0) {
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                        _("invalid disk target '%1$s'"), disk->dst);
         return -1;
index 2abcb282fe529fc38fb50996fbcf6b749472f6c4..fb64237692bbc729b1f140c3f863494eccdc3739 100644 (file)
@@ -314,15 +314,82 @@ virFormatIntPretty(unsigned long long val,
 }
 
 
+static int
+virDiskNameParseNvme(const char *name, int *controller, int *disk, int *partition)
+{
+    int ctrl = 0;
+    int ns = 0;
+    int part = 0;
+    char *end_ptr = NULL;
+    const char *tmp = STRSKIP(name, "nvme");
+
+    if (!controller)
+        return -1;
+
+    if (!tmp)
+        return -1;
+
+    if (virStrToLong_i(tmp, &end_ptr, 10, &ctrl) < 0)
+        return -1;
+
+    if (*end_ptr != 'n')
+        return -1;
+
+    if (ctrl < 0)
+        return -1;
+
+    tmp = end_ptr + 1;
+
+    if (virStrToLong_i(tmp, &end_ptr, 10, &ns) < 0)
+        return -1;
+
+    if (*end_ptr != '\0' && *end_ptr != 'p')
+        return -1;
+
+    /* NSIDs start from 1, but we need to map them to [0..] for the controller
+     * address. */
+    if (ns < 1)
+        return -1;
+    ns--;
+
+    if (partition) {
+        *partition = 0;
+
+        if (*end_ptr != '\0') {
+            tmp = end_ptr + 1;
+
+            if (virStrToLong_i(tmp, NULL, 10, &part) < 0)
+                return -1;
+
+            /* Partitions on NVMe disks also from 1 */
+            if (part < 1)
+                return -1;
+
+            *partition = part - 1;
+        }
+    }
+
+    *controller = ctrl;
+    *disk = ns;
+
+    return 0;
+}
+
+
 /* Translates a device name of the form (regex) /^[fhv]d[a-z]+[0-9]*$/
- * into the corresponding index and partition number
- * (e.g. sda0 => (0,0), hdz2 => (25,2), vdaa12 => (26,12))
+ * or nvme[0-9]+n[0-9]+(p[0-9]+) for NVMe devices
+ * into the corresponding nvme controller (see below), index and partition number
+ * (e.g. sda0 => (0,0,0), hdz2 => (0,25,2), vdaa12 => (0,26,12)) as these do not
+ * support namespaces and controller is calculated from the disk index
+ * for nvme disks disks: (nvme1n2p3 => (1, 1, 2) as nvme namespaces and
+ * partitions are numbered from 1 and not 0).
  * @param name The name of the device
+ * @param nvme_ctrl The disk namespace to be returned
  * @param disk The disk index to be returned
  * @param partition The partition index to be returned
  * @return 0 on success, or -1 on failure
  */
-int virDiskNameParse(const char *name, int *disk, int *partition)
+int virDiskNameParse(const char *name, int *nvme_ctrl, int *disk, int *partition)
 {
     const char *ptr = NULL;
     char *rem;
@@ -331,6 +398,12 @@ int virDiskNameParse(const char *name, int *disk, int *partition)
     size_t i;
     size_t n_digits;
 
+    if (STRPREFIX(name, "nvme"))
+        return virDiskNameParseNvme(name, nvme_ctrl, disk, partition);
+
+    if (nvme_ctrl)
+        *nvme_ctrl = 0;
+
     for (i = 0; i < G_N_ELEMENTS(drive_prefix); i++) {
         if (STRPREFIX(name, drive_prefix[i])) {
             ptr = name + strlen(drive_prefix[i]);
@@ -381,6 +454,8 @@ int virDiskNameParse(const char *name, int *disk, int *partition)
 /* Translates a device name of the form (regex) /^[fhv]d[a-z]+[0-9]*$/
  * into the corresponding index (e.g. sda => 0, hdz => 25, vdaa => 26)
  * Note that any trailing string of digits is simply ignored.
+ * Since NVMe disks require controller devices, this function should not be used
+ * for nvme* names unless the caller does not need to know then controller index
  * @param name The name of the device
  * @return name's index, or -1 on failure
  */
@@ -388,17 +463,24 @@ int virDiskNameToIndex(const char *name)
 {
     int idx;
 
-    if (virDiskNameParse(name, &idx, NULL) < 0)
+    if (virDiskNameParse(name, NULL, &idx, NULL) < 0)
         idx = -1;
 
     return idx;
 }
 
-char *virIndexToDiskName(unsigned int idx, const char *prefix)
+char *virIndexToDiskName(unsigned int nvme_ctrl,
+                         unsigned int idx,
+                         const char *prefix)
 {
-    GString *str = g_string_new(NULL);
+    GString *str = NULL;
     long long ctr;
 
+    if (STREQ_NULLABLE(prefix, "nvme"))
+        return g_strdup_printf("nvme%dn%d", nvme_ctrl, idx + 1);
+
+    str = g_string_new(NULL);
+
     for (ctr = idx; ctr >= 0; ctr = ctr / 26 - 1)
         g_string_prepend_c(str, 'a' + (ctr % 26));
 
index 6c07aa42c8b50a723b6c47d54ac3380c70447852..ca6fd95363b5ec2a9f4f12acf685d519ceae3c6f 100644 (file)
@@ -51,9 +51,12 @@ unsigned long long
 virFormatIntPretty(unsigned long long val,
                    const char **unit);
 
-int virDiskNameParse(const char *name, int *disk, int *partition);
+int virDiskNameParse(const char *name, int *nvme_ctrl,
+                     int *disk, int *partition);
 int virDiskNameToIndex(const char* str);
-char *virIndexToDiskName(unsigned int idx, const char *prefix);
+char *virIndexToDiskName(unsigned int nvme_ctrl,
+                         unsigned int idx,
+                         const char *prefix);
 
 /* No-op workarounds for functionality missing in mingw.  */
 #ifndef WITH_GETUID
index 7e4e2dde86bfcc99cb3c783a9f886ae25a659473..735c241fb337017bd3e1d15988f9d3c83c89e017 100644 (file)
@@ -447,7 +447,7 @@ vboxGenerateMediumName(PRUint32 storageBus,
         return NULL;
     }
 
-    name = virIndexToDiskName(total, prefix);
+    name = virIndexToDiskName(0, total, prefix);
 
     return name;
 }
index 0dd03c1a88e7d076d3b12eb29fadcb5c09d56625..4a9ad11b42c4ee79023b4dcf956c9c2615f09190 100644 (file)
@@ -2257,7 +2257,7 @@ virVMXGenerateDiskTarget(virDomainDiskDef *def,
         return -1;
     }
 
-    def->dst = virIndexToDiskName(idx, prefix);
+    def->dst = virIndexToDiskName(0, idx, prefix);
     return 0;
 }
 
index c2226c409dee62352e8f28fbc1a279d1a60aa303..684b76ffa057730fa718f1567720189c5eddc65b 100644 (file)
@@ -596,16 +596,16 @@ prlsdkGetDiskId(PRL_HANDLE disk, virDomainDiskBus *bus, char **dst)
     switch (ifType) {
     case PMS_IDE_DEVICE:
         *bus = VIR_DOMAIN_DISK_BUS_IDE;
-        *dst = virIndexToDiskName(pos, "hd");
+        *dst = virIndexToDiskName(0, pos, "hd");
         break;
     case PMS_SCSI_DEVICE:
     case PMS_UNKNOWN_DEVICE:
         *bus = VIR_DOMAIN_DISK_BUS_SCSI;
-        *dst = virIndexToDiskName(pos, "sd");
+        *dst = virIndexToDiskName(0, pos, "sd");
         break;
     case PMS_SATA_DEVICE:
         *bus = VIR_DOMAIN_DISK_BUS_SATA;
-        *dst = virIndexToDiskName(pos, "sd");
+        *dst = virIndexToDiskName(0, pos, "sd");
         break;
     default:
         virReportError(VIR_ERR_INTERNAL_ERROR,
index b30bfbed70fb8c005b423de23671efc9927c030e..0bef502a84c8114b5f1e0ac9848a63966b2cc636 100644 (file)
@@ -22,19 +22,25 @@ static const char* diskNames[] = {
 struct testDiskName
 {
     const char *name;
+    int ctrl;
     int idx;
     int partition;
 };
 
 static struct testDiskName diskNamesPart[] = {
-    {"sda0",          0,           0},
-    {"sdb10",         1,          10},
-    {"sdc2147483647", 2,  2147483647},
+    {"sda",           0, 0,           0},
+    {"sda0",          0, 0,           0},
+    {"sdb10",         0, 1,          10},
+    {"sdc2147483647", 0, 2,  2147483647},
+    {"nvme0n1",       0, 0,           0},
+    {"nvme0n1p1",     0, 0,           0},
+    {"nvme1n2p3",     1, 1,           2},
 };
 
 static const char* diskNamesInvalid[] = {
     "sda00", "sda01", "sdb-1",
-    "vd2"
+    "vd2",
+    "nvme0n0", "nvme0n1p0",
 };
 
 static int
@@ -45,7 +51,7 @@ testIndexToDiskName(const void *data G_GNUC_UNUSED)
     for (i = 0; i < G_N_ELEMENTS(diskNames); ++i) {
         g_autofree char *diskName = NULL;
 
-        diskName = virIndexToDiskName(i, "sd");
+        diskName = virIndexToDiskName(0, i, "sd");
 
         if (virTestCompareToString(diskNames[i], diskName) < 0) {
             return -1;
@@ -66,7 +72,7 @@ testDiskNameToIndex(const void *data G_GNUC_UNUSED)
     for (i = 0; i < 100000; ++i) {
         g_autofree char *diskName = NULL;
 
-        diskName = virIndexToDiskName(i, "sd");
+        diskName = virIndexToDiskName(0, i, "sd");
         idx = virDiskNameToIndex(diskName);
 
         if (idx < 0 || idx != i) {
@@ -85,30 +91,38 @@ static int
 testDiskNameParse(const void *data G_GNUC_UNUSED)
 {
     size_t i;
+    int nvme_ctrl;
     int idx;
     int partition;
     struct testDiskName *disk = NULL;
 
     for (i = 0; i < G_N_ELEMENTS(diskNamesPart); ++i) {
         disk = &diskNamesPart[i];
-        if (virDiskNameParse(disk->name, &idx, &partition))
+        partition = 0;
+        if (virDiskNameParse(disk->name, &nvme_ctrl, &idx, &partition))
             return -1;
 
+        if (disk->ctrl != nvme_ctrl) {
+            VIR_TEST_DEBUG("\nExpect NVMe controller [%d]", disk->ctrl);
+            VIR_TEST_DEBUG("Actual NVMe controller [%d]", nvme_ctrl);
+            return -1;
+        }
+
         if (disk->idx != idx) {
-            VIR_TEST_DEBUG("\nExpect [%d]", disk->idx);
-            VIR_TEST_DEBUG("Actual [%d]", idx);
+            VIR_TEST_DEBUG("\nExpect index [%d]", disk->idx);
+            VIR_TEST_DEBUG("Actual index [%d]", idx);
             return -1;
         }
 
         if (disk->partition != partition) {
-            VIR_TEST_DEBUG("\nExpect [%d]", disk->partition);
-            VIR_TEST_DEBUG("Actual [%d]", partition);
+            VIR_TEST_DEBUG("\nExpect partition [%d]", disk->partition);
+            VIR_TEST_DEBUG("Actual partition [%d]", partition);
             return -1;
         }
     }
 
     for (i = 0; i < G_N_ELEMENTS(diskNamesInvalid); ++i) {
-        if (!virDiskNameParse(diskNamesInvalid[i], &idx, &partition)) {
+        if (!virDiskNameParse(diskNamesInvalid[i], &nvme_ctrl, &idx, &partition)) {
             VIR_TEST_DEBUG("Should Fail [%s]", diskNamesInvalid[i]);
             return -1;
         }