From: Martin Kletzander Date: Thu, 22 May 2025 13:47:27 +0000 (+0200) Subject: docs, conf, schemas: Add support for NVMe disks X-Git-Tag: v11.5.0-rc1~64 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d80386745af74ac629a675d8123adfffd93fc9c8;p=thirdparty%2Flibvirt.git docs, conf, schemas: Add support for NVMe disks NVMe disks are essentially a namespace of an NVMe controller, but to make it easier for the users to just add a disk, the necessary details like adding the proper controller, setting the serial number for the controller based on the disk, are done automatically. Signed-off-by: Martin Kletzander Signed-off-by: Honglei Wang Reviewed-by: Ján Tomko --- diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4c70ff004c..ae054a52b3 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -2824,7 +2824,7 @@ paravirtualized driver is specified via the ``disk`` element. - + @@ -3313,7 +3313,8 @@ paravirtualized driver is specified via the ``disk`` element. name in the guest OS. Treat it as a device ordering hint. The optional ``bus`` attribute specifies the type of disk device to emulate; possible values are driver specific, with typical values being "ide", "scsi", - "virtio", "xen", "usb", "sata", or "sd" :since:`"sd" since 1.1.2`. If + "virtio", "xen", "usb", "sata", "sd", or "nvme" + :since:`"sd" since 1.1.2, "nvme" since 11.5.0`. If omitted, the bus type is inferred from the style of the device name (e.g. a device named 'sda' will typically be exported using a SCSI bus). The optional attribute ``tray`` indicates the tray status of the removable disks (i.e. @@ -3651,7 +3652,9 @@ paravirtualized driver is specified via the ``disk`` element. If present, this specify serial number of virtual hard drive. For example, it may look like ``WD-WMAP9A966149``. Not supported for scsi-block devices, that is those using disk ``type`` 'block' using - ``device`` 'lun' on ``bus`` 'scsi'. :since:`Since 0.7.1` + ``device`` 'lun' on ``bus`` 'scsi'. Also not supported for multiple NVMe + devices on the same controller since those have serial number per controller + and not per disk. :since:`Since 0.7.1` Note that depending on hypervisor and device type the serial number may be truncated silently. IDE/SATA devices are commonly limited to 20 characters. diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 4371b25e3b..1a6c8afb1d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -372,6 +372,7 @@ VIR_ENUM_IMPL(virDomainDiskBus, "uml", "sata", "sd", + "nvme", ); VIR_ENUM_IMPL(virDomainDiskCache, @@ -6813,8 +6814,10 @@ virDomainDiskDefAssignAddress(virDomainXMLOption *xmlopt G_GNUC_UNUSED, virDomainDiskDef *def, const virDomainDef *vmdef) { - int idx = virDiskNameToIndex(def->dst); - if (idx < 0) { + int idx = 0; + int nvme_ctrl = 0; + + if (virDiskNameParse(def->dst, &nvme_ctrl, &idx, NULL) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Unknown disk name '%1$s' and no address specified"), def->dst); @@ -6891,6 +6894,13 @@ virDomainDiskDefAssignAddress(virDomainXMLOption *xmlopt G_GNUC_UNUSED, def->info.addr.drive.unit = idx % 2; break; + case VIR_DOMAIN_DISK_BUS_NVME: + def->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; + def->info.addr.drive.controller = nvme_ctrl; + def->info.addr.drive.bus = 0; + def->info.addr.drive.unit = idx; + break; + case VIR_DOMAIN_DISK_BUS_NONE: case VIR_DOMAIN_DISK_BUS_VIRTIO: case VIR_DOMAIN_DISK_BUS_XEN: @@ -15015,6 +15025,10 @@ virDomainDiskControllerMatch(int controller_type, int disk_bus) disk_bus == VIR_DOMAIN_DISK_BUS_SATA) return true; + if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_NVME && + disk_bus == VIR_DOMAIN_DISK_BUS_NVME) + return true; + return false; } @@ -22664,6 +22678,36 @@ virDomainDefMaybeAddSmartcardController(virDomainDef *def) } } +static int +virDomainDefMaybeAssignNvmeControllerSerials(virDomainDef *def) +{ + size_t i = 0; + + for (i = 0; i < def->ndisks; i++) { + virDomainDiskDef *disk = def->disks[i]; + virDomainControllerDef *ctrl = NULL; + + if (!disk->serial || + disk->bus != VIR_DOMAIN_DISK_BUS_NVME || + def->disks[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) + continue; + + ctrl = virDomainDeviceFindNvmeController(def, &disk->info.addr.drive); + if (ctrl) { + if (!ctrl->opts.nvmeopts.serial) { + ctrl->opts.nvmeopts.serial = g_strdup(disk->serial); + } else if (STRNEQ_NULLABLE(disk->serial, ctrl->opts.nvmeopts.serial)) { + virReportError(VIR_ERR_XML_DETAIL, "%s", + _("Conflicting NVME disk serial number, all disks on a controller must have the same serial number as the controller itself")); + return -1; + } + } + } + + return 0; +} + + /* * Based on the declared
info for any devices, * add necessary drive controllers which are not already present @@ -22681,6 +22725,8 @@ virDomainDefAddImplicitControllers(virDomainDef *def) VIR_DOMAIN_DISK_BUS_IDE); virDomainDefAddDiskControllersForType(def, VIR_DOMAIN_CONTROLLER_TYPE_SATA, VIR_DOMAIN_DISK_BUS_SATA); + virDomainDefAddDiskControllersForType(def, VIR_DOMAIN_CONTROLLER_TYPE_NVME, + VIR_DOMAIN_DISK_BUS_NVME); virDomainDefMaybeAddVirtioSerialController(def); virDomainDefMaybeAddSmartcardController(def); @@ -22713,6 +22759,9 @@ virDomainDefAddImplicitDevices(virDomainDef *def, virDomainXMLOption *xmlopt) } virDomainDefAddImplicitControllers(def); + if (virDomainDefMaybeAssignNvmeControllerSerials(def) < 0) + return -1; + if (virDomainDefAddImplicitVideo(def, xmlopt) < 0) return -1; @@ -29674,8 +29723,10 @@ virDiskNameToBusDeviceIndex(virDomainDiskDef *disk, int *busIdx, int *devIdx) { - int idx = virDiskNameToIndex(disk->dst); - if (idx < 0) + int idx = -1; + int nvme_ctrl = 0; + + if (virDiskNameParse(disk->dst, &nvme_ctrl, &idx, NULL) < 0 || idx < 0) return -1; switch (disk->bus) { @@ -29687,6 +29738,10 @@ virDiskNameToBusDeviceIndex(virDomainDiskDef *disk, *busIdx = idx / 7; *devIdx = idx % 7; break; + case VIR_DOMAIN_DISK_BUS_NVME: + *busIdx = nvme_ctrl; + *devIdx = idx; + break; case VIR_DOMAIN_DISK_BUS_FDC: case VIR_DOMAIN_DISK_BUS_USB: case VIR_DOMAIN_DISK_BUS_VIRTIO: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 4adc952b6b..3d380073cf 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -401,6 +401,7 @@ typedef enum { VIR_DOMAIN_DISK_BUS_UML, VIR_DOMAIN_DISK_BUS_SATA, VIR_DOMAIN_DISK_BUS_SD, + VIR_DOMAIN_DISK_BUS_NVME, VIR_DOMAIN_DISK_BUS_LAST } virDomainDiskBus; diff --git a/src/conf/domain_postparse.c b/src/conf/domain_postparse.c index bf33f29638..a07ec8d94e 100644 --- a/src/conf/domain_postparse.c +++ b/src/conf/domain_postparse.c @@ -523,6 +523,8 @@ virDomainDiskDefPostParse(virDomainDiskDef *disk, disk->bus = VIR_DOMAIN_DISK_BUS_XEN; else if (STRPREFIX(disk->dst, "ubd")) disk->bus = VIR_DOMAIN_DISK_BUS_UML; + else if (STRPREFIX(disk->dst, "nvme")) + disk->bus = VIR_DOMAIN_DISK_BUS_NVME; } } diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index d0d4bc0bf4..96f76f2f7b 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -260,6 +260,7 @@ virDomainDiskAddressDiskBusCompatibility(virDomainDiskBus bus, case VIR_DOMAIN_DISK_BUS_FDC: case VIR_DOMAIN_DISK_BUS_SCSI: case VIR_DOMAIN_DISK_BUS_SATA: + case VIR_DOMAIN_DISK_BUS_NVME: return addressType == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; case VIR_DOMAIN_DISK_BUS_VIRTIO: case VIR_DOMAIN_DISK_BUS_XEN: @@ -948,7 +949,8 @@ virDomainDiskDefValidate(const virDomainDef *def, !STRPREFIX(disk->dst, "sd") && !STRPREFIX(disk->dst, "vd") && !STRPREFIX(disk->dst, "xvd") && - !STRPREFIX(disk->dst, "ubd")) { + !STRPREFIX(disk->dst, "ubd") && + !STRPREFIX(disk->dst, "nvme")) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid harddisk device name: %1$s"), disk->dst); return -1; diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 3317cf1665..b1fe51f519 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -2517,9 +2517,14 @@ - - (ioemu:)?(fd|hd|sd|vd|xvd|ubd)[a-zA-Z0-9_]+ - + + + (ioemu:)?(fd|hd|sd|vd|xvd|ubd)[a-zA-Z0-9_]+ + + + nvme[0-9]+n[0-9]+(p[0-9]+)? + + @@ -2539,6 +2544,7 @@ uml sata sd + nvme diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c index 25eb8872b3..0c17ef16ec 100644 --- a/src/hyperv/hyperv_driver.c +++ b/src/hyperv/hyperv_driver.c @@ -948,6 +948,7 @@ hypervDomainAttachStorage(virDomainPtr domain, virDomainDef *def, const char *ho case VIR_DOMAIN_DISK_BUS_UML: case VIR_DOMAIN_DISK_BUS_SATA: case VIR_DOMAIN_DISK_BUS_SD: + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_LAST: default: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unsupported controller type")); @@ -3078,6 +3079,7 @@ hypervDomainAttachDeviceFlags(virDomainPtr domain, const char *xml, unsigned int case VIR_DOMAIN_DISK_BUS_UML: case VIR_DOMAIN_DISK_BUS_SATA: case VIR_DOMAIN_DISK_BUS_SD: + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_LAST: default: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid disk bus in definition")); diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index 3e6bced4a8..9d39ebd63d 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -258,6 +258,7 @@ qemuAssignDeviceDiskAlias(virDomainDef *def, case VIR_DOMAIN_DISK_BUS_IDE: case VIR_DOMAIN_DISK_BUS_SATA: case VIR_DOMAIN_DISK_BUS_SCSI: + case VIR_DOMAIN_DISK_BUS_NVME: diskPriv->qomName = g_strdup(disk->info.alias); break; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d7fb88cbc1..f193ba8cca 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -532,6 +532,7 @@ qemuBuildDeviceAddresDriveProps(virJSONValue *props, break; + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_VIRTIO: case VIR_DOMAIN_DISK_BUS_USB: case VIR_DOMAIN_DISK_BUS_XEN: @@ -1722,6 +1723,7 @@ qemuBuildDiskDeviceProps(const virDomainDef *def, driver = "floppy"; break; + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_XEN: case VIR_DOMAIN_DISK_BUS_UML: case VIR_DOMAIN_DISK_BUS_SD: diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index 7bc769fc19..9b2faf1e8e 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -739,6 +739,7 @@ qemuDomainDeviceCalculatePCIConnectFlags(virDomainDeviceDef *dev, case VIR_DOMAIN_DISK_BUS_UML: case VIR_DOMAIN_DISK_BUS_SATA: case VIR_DOMAIN_DISK_BUS_SD: + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_NONE: case VIR_DOMAIN_DISK_BUS_LAST: return 0; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 7a1170b2dd..9427eec643 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1058,6 +1058,7 @@ qemuDomainAttachDeviceDiskLiveInternal(virQEMUDriver *driver, /* Note that SD card hotplug support should be added only once * they support '-device' (don't require -drive only). * See also: qemuDiskBusIsSD */ + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_NONE: case VIR_DOMAIN_DISK_BUS_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, @@ -5776,6 +5777,7 @@ qemuDomainDetachPrepDisk(virDomainObj *vm, case VIR_DOMAIN_DISK_BUS_SCSI: break; + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_IDE: case VIR_DOMAIN_DISK_BUS_FDC: case VIR_DOMAIN_DISK_BUS_XEN: diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index ab75a3e046..fee5e1ed87 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -2945,6 +2945,7 @@ qemuValidateDomainDeviceDefDiskIOThreads(const virDomainDef *def, case VIR_DOMAIN_DISK_BUS_SATA: case VIR_DOMAIN_DISK_BUS_SD: case VIR_DOMAIN_DISK_BUS_NONE: + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_LAST: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("IOThreads not available for bus %1$s target %2$s"), @@ -3086,6 +3087,7 @@ qemuValidateDomainDeviceDefDiskFrontend(const virDomainDiskDef *disk, case VIR_DOMAIN_DISK_BUS_UML: case VIR_DOMAIN_DISK_BUS_SATA: case VIR_DOMAIN_DISK_BUS_SD: + case VIR_DOMAIN_DISK_BUS_NVME: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("disk device='lun' is not supported for bus='%1$s'"), virDomainDiskBusTypeToString(disk->bus)); @@ -3201,6 +3203,7 @@ qemuValidateDomainDeviceDefDiskFrontend(const virDomainDiskDef *disk, break; + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_XEN: case VIR_DOMAIN_DISK_BUS_SD: case VIR_DOMAIN_DISK_BUS_NONE: @@ -3397,6 +3400,7 @@ qemuValidateDomainDeviceDefDiskTransient(const virDomainDiskDef *disk, case VIR_DOMAIN_DISK_BUS_UML: case VIR_DOMAIN_DISK_BUS_SATA: case VIR_DOMAIN_DISK_BUS_SD: + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_NONE: case VIR_DOMAIN_DISK_BUS_LAST: default: @@ -3418,6 +3422,7 @@ qemuValidateDomainDeviceDefDisk(const virDomainDiskDef *disk, { const char *driverName = virDomainDiskGetDriver(disk); virStorageSource *n; + int nvme_ctrl; int idx; int partition; @@ -3445,7 +3450,7 @@ qemuValidateDomainDeviceDefDisk(const virDomainDiskDef *disk, return -1; } - if (virDiskNameParse(disk->dst, NULL, &idx, &partition) < 0) { + if (virDiskNameParse(disk->dst, &nvme_ctrl, &idx, &partition) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid disk target '%1$s'"), disk->dst); return -1; diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 6f18b2b2c8..25335d9002 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -10344,6 +10344,7 @@ testDomainAttachDeviceDiskLiveInternal(testDriver *driver G_GNUC_UNUSED, case VIR_DOMAIN_DISK_BUS_UML: case VIR_DOMAIN_DISK_BUS_SATA: case VIR_DOMAIN_DISK_BUS_SD: + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_NONE: case VIR_DOMAIN_DISK_BUS_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, @@ -10792,6 +10793,7 @@ testDomainDetachPrepDisk(virDomainObj *vm, case VIR_DOMAIN_DISK_BUS_UML: case VIR_DOMAIN_DISK_BUS_SATA: case VIR_DOMAIN_DISK_BUS_SD: + case VIR_DOMAIN_DISK_BUS_NVME: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", _("This type of disk cannot be hot unplugged")); return -1; diff --git a/src/vbox/vbox_common.c b/src/vbox/vbox_common.c index 735c241fb3..26c5fdfef6 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -1239,6 +1239,7 @@ vboxAttachDrives(virDomainDef *def, struct _vboxDriver *data, IMachine *machine) case VIR_DOMAIN_DISK_BUS_USB: case VIR_DOMAIN_DISK_BUS_UML: case VIR_DOMAIN_DISK_BUS_SD: + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_NONE: case VIR_DOMAIN_DISK_BUS_LAST: vboxReportError(VIR_ERR_CONFIG_UNSUPPORTED, diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 4a9ad11b42..7fa00669a7 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -2240,6 +2240,7 @@ virVMXGenerateDiskTarget(virDomainDiskDef *def, prefix = "fd"; break; + case VIR_DOMAIN_DISK_BUS_NVME: case VIR_DOMAIN_DISK_BUS_VIRTIO: case VIR_DOMAIN_DISK_BUS_XEN: case VIR_DOMAIN_DISK_BUS_USB: diff --git a/tests/genericxml2xmlindata/disk-nvme-invalid-serials.xml b/tests/genericxml2xmlindata/disk-nvme-invalid-serials.xml new file mode 100644 index 0000000000..dcfe6eaacf --- /dev/null +++ b/tests/genericxml2xmlindata/disk-nvme-invalid-serials.xml @@ -0,0 +1,29 @@ + + bar + 00010203-0405-4607-8809-0a0b0c0d0e0f + 219136 + 219136 + 1 + + hvm + + + + destroy + restart + destroy + + + + + + abcdefgh + + + + + + IJKLMNOP + + + diff --git a/tests/genericxml2xmlindata/disk-nvme.xml b/tests/genericxml2xmlindata/disk-nvme.xml new file mode 100644 index 0000000000..bdfec4ef7b --- /dev/null +++ b/tests/genericxml2xmlindata/disk-nvme.xml @@ -0,0 +1,32 @@ + + bar + 00010203-0405-4607-8809-0a0b0c0d0e0f + 219136 + 219136 + 1 + + hvm + + + + destroy + restart + destroy + + + + + + + + + + + abcdefgh + + + + CDEFGAHC + + + diff --git a/tests/genericxml2xmloutdata/disk-nvme.xml b/tests/genericxml2xmloutdata/disk-nvme.xml new file mode 100644 index 0000000000..4251e1288e --- /dev/null +++ b/tests/genericxml2xmloutdata/disk-nvme.xml @@ -0,0 +1,38 @@ + + bar + 00010203-0405-4607-8809-0a0b0c0d0e0f + 219136 + 219136 + 1 + + hvm + + + + destroy + restart + destroy + + + + + +
+ + + + + + abcdefgh +
+ + + + CDEFGAHC + + + + abcdefgh + + + diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c index f4e04d84f8..6757fc44de 100644 --- a/tests/genericxml2xmltest.c +++ b/tests/genericxml2xmltest.c @@ -232,6 +232,8 @@ mymain(void) DO_TEST("fibrechannel-appid"); DO_TEST("controller-nvme"); + DO_TEST_DIFFERENT("disk-nvme"); + DO_TEST_FAIL_INACTIVE("disk-nvme-invalid-serials"); #define DO_TEST_BACKUP_FULL(name, intrnl) \ do { \