From: Martin Kletzander Date: Thu, 22 May 2025 09:26:47 +0000 (+0200) Subject: util: Add support for parsing nvmeXnY(pZ) strings X-Git-Tag: v11.5.0-rc1~66 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ebf6347879443065acdef0ddbc45095d8e18cdc1;p=thirdparty%2Flibvirt.git util: Add support for parsing nvmeXnY(pZ) strings 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 Reviewed-by: Ján Tomko --- diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index 0d1e388c08..25eb8872b3 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -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; diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 0fb256e5c0..308c0372aa 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -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; } diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 56747a38be..ab75a3e046 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -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; diff --git a/src/util/virutil.c b/src/util/virutil.c index 2abcb282fe..fb64237692 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -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)); diff --git a/src/util/virutil.h b/src/util/virutil.h index 6c07aa42c8..ca6fd95363 100644 --- a/src/util/virutil.h +++ b/src/util/virutil.h @@ -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 diff --git a/src/vbox/vbox_common.c b/src/vbox/vbox_common.c index 7e4e2dde86..735c241fb3 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -447,7 +447,7 @@ vboxGenerateMediumName(PRUint32 storageBus, return NULL; } - name = virIndexToDiskName(total, prefix); + name = virIndexToDiskName(0, total, prefix); return name; } diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 0dd03c1a88..4a9ad11b42 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -2257,7 +2257,7 @@ virVMXGenerateDiskTarget(virDomainDiskDef *def, return -1; } - def->dst = virIndexToDiskName(idx, prefix); + def->dst = virIndexToDiskName(0, idx, prefix); return 0; } diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c index c2226c409d..684b76ffa0 100644 --- a/src/vz/vz_sdk.c +++ b/src/vz/vz_sdk.c @@ -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, diff --git a/tests/utiltest.c b/tests/utiltest.c index b30bfbed70..0bef502a84 100644 --- a/tests/utiltest.c +++ b/tests/utiltest.c @@ -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; }