To simplify implementation, some restrictions are added. For
instance, an NVMe disk can't go to any bus but virtio and has to
be type of 'disk' and can't have startupPolicy set.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/conf/domain_conf.c | 129 +++++++++++++++++++++++++
src/libvirt_private.syms | 1 +
src/qemu/qemu_block.c | 1 +
src/qemu/qemu_command.c | 1 +
src/qemu/qemu_driver.c | 4 +
src/qemu/qemu_migration.c | 1 +
src/util/virstoragefile.c | 59 +++++++++++
src/util/virstoragefile.h | 15 +++
src/xenconfig/xen_xl.c | 1 +
tests/qemuxml2argvdata/disk-nvme.xml | 12 ++-
tests/qemuxml2xmloutdata/disk-nvme.xml | 1 +
tests/qemuxml2xmltest.c | 1 +
12 files changed, 224 insertions(+), 2 deletions(-)
create mode 120000 tests/qemuxml2xmloutdata/disk-nvme.xml
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 3323c9a5b1..73f5e1fa0f 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -5088,6 +5088,11 @@ virDomainDiskDefPostParse(virDomainDiskDefPtr disk,
return -1;
}
+ if (disk->src->type == VIR_STORAGE_TYPE_NVME) {
+ if (disk->src->nvme->managed == VIR_TRISTATE_BOOL_ABSENT)
+ disk->src->nvme->managed = VIR_TRISTATE_BOOL_YES;
+ }
+
if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
virDomainDiskDefAssignAddress(xmlopt, disk, def) < 0) {
return -1;
@@ -5938,6 +5943,38 @@ virDomainDiskDefValidate(const virDomainDiskDef *disk)
return -1;
}
+ if (disk->src->type == VIR_STORAGE_TYPE_NVME) {
+ /* These might not be valid for all hypervisors, but be
+ * strict now and possibly refine in the future. */
+ if (disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Unsupported disk type '%s' for NVMe
disk"),
+ virDomainDiskDeviceTypeToString(disk->device));
+ return -1;
+ }
+
+ if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Unsupported bus '%s' for NVMe disk"),
+ virDomainDiskBusTypeToString(disk->bus));
+ return -1;
+ }
+
+ if (disk->startupPolicy != VIR_DOMAIN_STARTUP_POLICY_DEFAULT &&
+ disk->startupPolicy != VIR_DOMAIN_STARTUP_POLICY_MANDATORY) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Unsupported startup policy '%s' for NVMe
disk"),
+ virDomainStartupPolicyTypeToString(disk->startupPolicy));
+ return -1;
+ }
+
+ if (disk->src->shared) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Unsupported <shareable/> for NVMe disk"));
+ return -1;
+ }
+ }
+
return 0;
}
@@ -9184,6 +9221,76 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node,
}
+static int
+virDomainDiskSourceNVMeParse(xmlNodePtr node,
+ xmlXPathContextPtr ctxt,
+ virStorageSourcePtr src)
+{
+ VIR_AUTOPTR(virStorageSourceNVMeDef) nvme = NULL;
+ VIR_AUTOFREE(char *) type = NULL;
+ VIR_AUTOFREE(char *) namespace = NULL;
+ VIR_AUTOFREE(char *) managed = NULL;
+ xmlNodePtr address;
+
+ if (VIR_ALLOC(nvme) < 0)
+ return -1;
+
+ if (!(type = virXMLPropString(node, "type"))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing 'type' attribute to disk source"));
+ return -1;
+ }
+
+ if (STRNEQ(type, "pci")) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unsupported source type '%s'"),
+ type);
+ return -1;
+ }
+
+ if (!(namespace = virXMLPropString(node, "namespace"))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing 'namespace' attribute to disk
source"));
+ return -1;
+ }
+
+ if (virStrToLong_ul(namespace, NULL, 10, &nvme->namespace) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("malformed namespace '%s'"),
+ namespace);
+ return -1;
+ }
+
+ /* NVMe namespaces start from 1 */
+ if (nvme->namespace == 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("NVMe namespace can't be zero"));
+ return -1;
+ }
+
+ if ((managed = virXMLPropString(node, "managed"))) {
+ if ((nvme->managed = virTristateBoolTypeFromString(managed)) <= 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("malformed managed value '%s'"),
+ managed);
+ return -1;
+ }
+ }
+
+ if (!(address = virXPathNode("./address", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("NVMe disk source is missing address"));
+ return -1;
+ }
+
+ if (virPCIDeviceAddressParseXML(address, &nvme->pciAddr) < 0)
+ return -1;
+
+ VIR_STEAL_PTR(src->nvme, nvme);
+ return 0;
+}
+
+
static int
virDomainDiskSourcePRParse(xmlNodePtr node,
xmlXPathContextPtr ctxt,
@@ -9284,6 +9391,10 @@ virDomainStorageSourceParse(xmlNodePtr node,
if (virDomainDiskSourcePoolDefParse(node, &src->srcpool) < 0)
return -1;
break;
+ case VIR_STORAGE_TYPE_NVME:
+ if (virDomainDiskSourceNVMeParse(node, ctxt, src) < 0)
+ return -1;
+ break;
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -23922,6 +24033,19 @@ virDomainDiskSourceFormatNetwork(virBufferPtr attrBuf,
}
+static void
+virDomainDiskSourceNVMeFormat(virBufferPtr attrBuf,
+ virBufferPtr childBuf,
+ const virStorageSourceNVMeDef *nvme)
+{
+ virBufferAddLit(attrBuf, " type='pci'");
+ virBufferAsprintf(attrBuf, " managed='%s'",
+ virTristateBoolTypeToString(nvme->managed));
+ virBufferAsprintf(attrBuf, " namespace='%ld'",
nvme->namespace);
+ virPCIDeviceAddressFormat(childBuf, nvme->pciAddr, false);
+}
+
+
static int
virDomainDiskSourceFormatPrivateData(virBufferPtr buf,
virStorageSourcePtr src,
@@ -24008,6 +24132,11 @@ virDomainDiskSourceFormat(virBufferPtr buf,
break;
+ case VIR_STORAGE_TYPE_NVME:
+ if (src->nvme)
+ virDomainDiskSourceNVMeFormat(&attrBuf, &childBuf, src->nvme);
+ break;
+
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 6cef8d20fe..350b638193 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2994,6 +2994,7 @@ virStorageSourceNetworkAssignDefaultPorts;
virStorageSourceNew;
virStorageSourceNewFromBacking;
virStorageSourceNewFromBackingAbsolute;
+virStorageSourceNVMeDefFree;
virStorageSourceParseRBDColonString;
virStorageSourcePoolDefFree;
virStorageSourcePoolModeTypeFromString;
diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
index 0a6522577d..5eeb3757f1 100644
--- a/src/qemu/qemu_block.c
+++ b/src/qemu/qemu_block.c
@@ -1050,6 +1050,7 @@ qemuBlockStorageSourceGetBackendProps(virStorageSourcePtr src,
break;
case VIR_STORAGE_TYPE_VOLUME:
+ case VIR_STORAGE_TYPE_NVME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
return NULL;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 688dc324c6..927641cf46 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1184,6 +1184,7 @@ qemuGetDriveSourceString(virStorageSourcePtr src,
break;
case VIR_STORAGE_TYPE_VOLUME:
+ case VIR_STORAGE_TYPE_NVME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
break;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index a52b54b9d8..9bf12ea20d 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -14637,6 +14637,7 @@
qemuDomainSnapshotPrepareDiskExternalInactive(virDomainSnapshotDiskDefPtr snapdi
case VIR_STORAGE_TYPE_DIR:
case VIR_STORAGE_TYPE_VOLUME:
+ case VIR_STORAGE_TYPE_NVME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -14653,6 +14654,7 @@
qemuDomainSnapshotPrepareDiskExternalInactive(virDomainSnapshotDiskDefPtr snapdi
case VIR_STORAGE_TYPE_NETWORK:
case VIR_STORAGE_TYPE_DIR:
case VIR_STORAGE_TYPE_VOLUME:
+ case VIR_STORAGE_TYPE_NVME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -14715,6 +14717,7 @@
qemuDomainSnapshotPrepareDiskExternalActive(virDomainSnapshotDiskDefPtr snapdisk
case VIR_STORAGE_TYPE_DIR:
case VIR_STORAGE_TYPE_VOLUME:
+ case VIR_STORAGE_TYPE_NVME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -14837,6 +14840,7 @@ qemuDomainSnapshotPrepareDiskInternal(virDomainDiskDefPtr disk,
case VIR_STORAGE_TYPE_DIR:
case VIR_STORAGE_TYPE_VOLUME:
+ case VIR_STORAGE_TYPE_NVME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 2436f5051b..87adccab3d 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -227,6 +227,7 @@ qemuMigrationDstPrecreateDisk(virConnectPtr conn,
case VIR_STORAGE_TYPE_BLOCK:
case VIR_STORAGE_TYPE_DIR:
+ case VIR_STORAGE_TYPE_NVME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
index 269d0050fd..18aa33fe05 100644
--- a/src/util/virstoragefile.c
+++ b/src/util/virstoragefile.c
@@ -57,6 +57,7 @@ VIR_ENUM_IMPL(virStorage,
"dir",
"network",
"volume",
+ "nvme",
);
VIR_ENUM_IMPL(virStorageFileFormat,
@@ -2114,6 +2115,48 @@ virStoragePRDefCopy(virStoragePRDefPtr src)
}
+static virStorageSourceNVMeDefPtr
+virStorageSourceNVMeDefCopy(const virStorageSourceNVMeDef *src)
+{
+ VIR_AUTOPTR(virStorageSourceNVMeDef) ret = NULL;
+
+ if (VIR_ALLOC(ret) < 0)
+ return NULL;
+
+ *ret = *src;
+ VIR_RETURN_PTR(ret);
+}
+
+
+static bool
+virStorageSourceNVMeDefIsEqual(const virStorageSourceNVMeDef *a,
+ const virStorageSourceNVMeDef *b)
+{
+ if (!a && !b)
+ return true;
+
+ if (!a || !b)
+ return false;
+
+ if (a->namespace != b->namespace ||
+ a->managed != b->managed ||
+ !virPCIDeviceAddressEqual(&a->pciAddr, &b->pciAddr))
+ return false;
+
+ return true;
+}
+
+
+void
+virStorageSourceNVMeDefFree(virStorageSourceNVMeDefPtr def)
+{
+ if (!def)
+ return;
+
+ VIR_FREE(def);
+}
+
+
virSecurityDeviceLabelDefPtr
virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src,
const char *model)
@@ -2323,6 +2366,10 @@ virStorageSourceCopy(const virStorageSource *src,
!(def->pr = virStoragePRDefCopy(src->pr)))
return NULL;
+ if (src->nvme &&
+ !(def->nvme = virStorageSourceNVMeDefCopy(src->nvme)))
+ return NULL;
+
if (virStorageSourceInitiatorCopy(&def->initiator, &src->initiator))
return NULL;
@@ -2376,6 +2423,10 @@ virStorageSourceIsSameLocation(virStorageSourcePtr a,
}
}
+ if (a->type == VIR_STORAGE_TYPE_NVME &&
+ !virStorageSourceNVMeDefIsEqual(a->nvme, b->nvme))
+ return false;
+
return true;
}
@@ -2463,6 +2514,7 @@ virStorageSourceIsLocalStorage(const virStorageSource *src)
case VIR_STORAGE_TYPE_NETWORK:
case VIR_STORAGE_TYPE_VOLUME:
+ case VIR_STORAGE_TYPE_NVME:
case VIR_STORAGE_TYPE_LAST:
case VIR_STORAGE_TYPE_NONE:
return false;
@@ -2493,6 +2545,10 @@ virStorageSourceIsEmpty(virStorageSourcePtr src)
src->protocol == VIR_STORAGE_NET_PROTOCOL_NONE)
return true;
+ if (src->type == VIR_STORAGE_TYPE_NVME &&
+ !src->nvme)
+ return true;
+
return false;
}
@@ -2548,6 +2604,7 @@ virStorageSourceClear(virStorageSourcePtr def)
VIR_FREE(def->compat);
virStorageEncryptionFree(def->encryption);
virStoragePRDefFree(def->pr);
+ virStorageSourceNVMeDefFree(def->nvme);
virStorageSourceSeclabelsClear(def);
virStoragePermsFree(def->perms);
VIR_FREE(def->timestamps);
@@ -3776,6 +3833,7 @@ virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src,
/* We shouldn't get VOLUME, but the switch requires all cases */
case VIR_STORAGE_TYPE_VOLUME:
+ case VIR_STORAGE_TYPE_NVME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -4242,6 +4300,7 @@ virStorageSourceIsRelative(virStorageSourcePtr src)
case VIR_STORAGE_TYPE_NETWORK:
case VIR_STORAGE_TYPE_VOLUME:
+ case VIR_STORAGE_TYPE_NVME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
return false;
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
index 38ba901858..a1294ea608 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -31,6 +31,7 @@
#include "virsecret.h"
#include "virautoclean.h"
#include "virenum.h"
+#include "virpci.h"
/* Minimum header size required to probe all known formats with
* virStorageFileProbeFormat, or obtain metadata from a known format.
@@ -52,6 +53,7 @@ typedef enum {
VIR_STORAGE_TYPE_DIR,
VIR_STORAGE_TYPE_NETWORK,
VIR_STORAGE_TYPE_VOLUME,
+ VIR_STORAGE_TYPE_NVME,
VIR_STORAGE_TYPE_LAST
} virStorageType;
@@ -231,6 +233,14 @@ struct _virStorageSourceInitiatorDef {
char *iqn; /* Initiator IQN */
};
+typedef struct _virStorageSourceNVMeDef virStorageSourceNVMeDef;
+typedef virStorageSourceNVMeDef *virStorageSourceNVMeDefPtr;
+struct _virStorageSourceNVMeDef {
+ unsigned long namespace;
+ int managed; /* enum virTristateBool */
+ virPCIDeviceAddress pciAddr;
+};
+
typedef struct _virStorageDriverData virStorageDriverData;
typedef virStorageDriverData *virStorageDriverDataPtr;
@@ -262,6 +272,8 @@ struct _virStorageSource {
bool encryptionInherited;
virStoragePRDefPtr pr;
+ virStorageSourceNVMeDefPtr nvme; /* type == VIR_STORAGE_TYPE_NVME */
+
virStorageSourceInitiatorDef initiator;
virObjectPtr privateData;
@@ -416,6 +428,9 @@ bool virStoragePRDefIsManaged(virStoragePRDefPtr prd);
bool
virStorageSourceChainHasManagedPR(virStorageSourcePtr src);
+void virStorageSourceNVMeDefFree(virStorageSourceNVMeDefPtr def);
+VIR_DEFINE_AUTOPTR_FUNC(virStorageSourceNVMeDef, virStorageSourceNVMeDefFree);
+
virSecurityDeviceLabelDefPtr
virStorageSourceGetSecurityLabelDef(virStorageSourcePtr src,
const char *model);
diff --git a/src/xenconfig/xen_xl.c b/src/xenconfig/xen_xl.c
index ca094d30c2..fa15e4e2a5 100644
--- a/src/xenconfig/xen_xl.c
+++ b/src/xenconfig/xen_xl.c
@@ -1662,6 +1662,7 @@ xenFormatXLDiskSrc(virStorageSourcePtr src, char **srcstr)
break;
case VIR_STORAGE_TYPE_VOLUME:
+ case VIR_STORAGE_TYPE_NVME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
break;
diff --git a/tests/qemuxml2argvdata/disk-nvme.xml b/tests/qemuxml2argvdata/disk-nvme.xml
index 0b3dbad4eb..fe956d5ab6 100644
--- a/tests/qemuxml2argvdata/disk-nvme.xml
+++ b/tests/qemuxml2argvdata/disk-nvme.xml
@@ -20,6 +20,7 @@
<address domain='0x0000' bus='0x01' slot='0x00'
function='0x0'/>
</source>
<target dev='vda' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x04' function='0x0'/>
</disk>
<disk type='nvme' device='disk'>
<driver name='qemu' type='raw'/>
@@ -27,6 +28,7 @@
<address domain='0x0000' bus='0x01' slot='0x00'
function='0x0'/>
</source>
<target dev='vdb' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x05' function='0x0'/>
</disk>
<disk type='nvme' device='disk'>
<driver name='qemu' type='raw'/>
@@ -34,6 +36,7 @@
<address domain='0x0000' bus='0x02' slot='0x00'
function='0x0'/>
</source>
<target dev='vdc' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x06' function='0x0'/>
</disk>
<disk type='nvme' device='disk'>
<driver name='qemu' type='raw'/>
@@ -44,10 +47,15 @@
</encryption>
</source>
<target dev='vdd' bus='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x07' function='0x0'/>
</disk>
- <controller type='usb' index='0'/>
+ <controller type='usb' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
<controller type='pci' index='0' model='pci-root'/>
- <controller type='scsi' index='0'
model='virtio-scsi'/>
+ <controller type='scsi' index='0' model='virtio-scsi'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </controller>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<memballoon model='none'/>
diff --git a/tests/qemuxml2xmloutdata/disk-nvme.xml
b/tests/qemuxml2xmloutdata/disk-nvme.xml
new file mode 120000
index 0000000000..ea9eb267ac
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/disk-nvme.xml
@@ -0,0 +1 @@
+../qemuxml2argvdata/disk-nvme.xml
\ No newline at end of file
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 6d808e172f..c9f3a8dbfa 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -336,6 +336,7 @@ mymain(void)
DO_TEST("disk-network-sheepdog", NONE);
DO_TEST("disk-network-vxhs", NONE);
DO_TEST("disk-network-tlsx509", NONE);
+ DO_TEST("disk-nvme", QEMU_CAPS_VIRTIO_SCSI);
DO_TEST("disk-scsi", QEMU_CAPS_SCSI_LSI, QEMU_CAPS_SCSI_MEGASAS,
QEMU_CAPS_SCSI_MPTSAS1068, QEMU_CAPS_SCSI_DISK_WWN);
DO_TEST("disk-virtio-scsi-reservations",
--
2.21.0