[libvirt] [PATCH v2 19/39] conf: Format and parse NVMe type disk

Michal Privoznik mprivozn at redhat.com
Thu Sep 26 16:12:15 UTC 2019


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 at redhat.com>
---
 src/conf/domain_conf.c                 | 96 ++++++++++++++++++++++++++
 src/libvirt_private.syms               |  1 +
 src/libxl/xen_xl.c                     |  1 +
 src/qemu/qemu_block.c                  |  2 +
 src/qemu/qemu_command.c                |  1 +
 src/qemu/qemu_driver.c                 |  4 ++
 src/qemu/qemu_migration.c              |  5 ++
 src/util/virstoragefile.c              | 59 ++++++++++++++++
 src/util/virstoragefile.h              | 17 +++++
 tests/qemuxml2xmloutdata/disk-nvme.xml |  1 +
 tests/qemuxml2xmltest.c                |  1 +
 11 files changed, 188 insertions(+)
 create mode 120000 tests/qemuxml2xmloutdata/disk-nvme.xml

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index c1769f743b..fe540c195e 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -5118,6 +5118,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;
@@ -9251,6 +9256,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_ull(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,
@@ -9351,6 +9426,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,
@@ -24088,6 +24167,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='%llu'", nvme->namespace);
+    virPCIDeviceAddressFormat(childBuf, nvme->pciAddr, false);
+}
+
+
 static int
 virDomainDiskSourceFormatPrivateData(virBufferPtr buf,
                                      virStorageSourcePtr src,
@@ -24174,6 +24266,10 @@ virDomainDiskSourceFormat(virBufferPtr buf,
 
         break;
 
+    case VIR_STORAGE_TYPE_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 b3c95495c3..91b03afb17 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -3045,6 +3045,7 @@ virStorageSourceNetworkAssignDefaultPorts;
 virStorageSourceNew;
 virStorageSourceNewFromBacking;
 virStorageSourceNewFromBackingAbsolute;
+virStorageSourceNVMeDefFree;
 virStorageSourceParseRBDColonString;
 virStorageSourcePoolDefFree;
 virStorageSourcePoolModeTypeFromString;
diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c
index 3a41a4ad00..5b69812f40 100644
--- a/src/libxl/xen_xl.c
+++ b/src/libxl/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/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
index 4b5dd30e17..99b2a4efaa 100644
--- a/src/qemu/qemu_block.c
+++ b/src/qemu/qemu_block.c
@@ -1077,6 +1077,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;
@@ -2367,6 +2368,7 @@ qemuBlockStorageSourceCreateGetStorageProps(virStorageSourcePtr src,
     case VIR_STORAGE_TYPE_BLOCK:
     case VIR_STORAGE_TYPE_DIR:
     case VIR_STORAGE_TYPE_VOLUME:
+    case VIR_STORAGE_TYPE_NVME:
         return 0;
 
     case VIR_STORAGE_TYPE_NONE:
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 77470a6037..b2519465ba 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1144,6 +1144,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 0c65414a1a..ecfccae92e 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -14906,6 +14906,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,
@@ -14922,6 +14923,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,
@@ -14989,6 +14991,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,
@@ -15116,6 +15119,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 693240ed3d..4fa989ff79 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -228,6 +228,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,
@@ -1270,6 +1271,10 @@ qemuMigrationSrcIsSafe(virDomainDefPtr def,
             /* But network disks are safe again. */
             continue;
 
+        case VIR_STORAGE_TYPE_NVME:
+            unsafe = true;
+            break;
+
         case VIR_STORAGE_TYPE_NONE:
         case VIR_STORAGE_TYPE_BLOCK:
         case VIR_STORAGE_TYPE_DIR:
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
index 3201f57e62..725d68a248 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,
@@ -2116,6 +2117,50 @@ virStoragePRDefCopy(virStoragePRDefPtr src)
 }
 
 
+static virStorageSourceNVMeDefPtr
+virStorageSourceNVMeDefCopy(const virStorageSourceNVMeDef *src)
+{
+    VIR_AUTOPTR(virStorageSourceNVMeDef) ret = NULL;
+
+    if (VIR_ALLOC(ret) < 0)
+        return NULL;
+
+    ret->namespace = src->namespace;
+    ret->managed = src->managed;
+    virPCIDeviceAddressCopy(&ret->pciAddr, &src->pciAddr);
+    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)
@@ -2325,6 +2370,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;
 
@@ -2378,6 +2427,10 @@ virStorageSourceIsSameLocation(virStorageSourcePtr a,
         }
     }
 
+    if (a->type == VIR_STORAGE_TYPE_NVME &&
+        !virStorageSourceNVMeDefIsEqual(a->nvme, b->nvme))
+        return false;
+
     return true;
 }
 
@@ -2465,6 +2518,9 @@ virStorageSourceIsLocalStorage(const virStorageSource *src)
 
     case VIR_STORAGE_TYPE_NETWORK:
     case VIR_STORAGE_TYPE_VOLUME:
+        /* While NVMe disks are local, they are not accessible via src->path.
+         * Therefore, we have to return false here. */
+    case VIR_STORAGE_TYPE_NVME:
     case VIR_STORAGE_TYPE_LAST:
     case VIR_STORAGE_TYPE_NONE:
         return false;
@@ -2550,6 +2606,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);
@@ -3807,6 +3864,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:
         return -1;
@@ -4268,6 +4326,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 81b83a53ef..9e79c7c6ef 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,16 @@ struct _virStorageSourceInitiatorDef {
     char *iqn; /* Initiator IQN */
 };
 
+typedef struct _virStorageSourceNVMeDef virStorageSourceNVMeDef;
+typedef virStorageSourceNVMeDef *virStorageSourceNVMeDefPtr;
+struct _virStorageSourceNVMeDef {
+    unsigned long long namespace;
+    int managed; /* enum virTristateBool */
+    virPCIDeviceAddress pciAddr;
+
+    /* Don't forget to update virStorageSourceNVMeDefCopy */
+};
+
 typedef struct _virStorageDriverData virStorageDriverData;
 typedef virStorageDriverData *virStorageDriverDataPtr;
 
@@ -262,6 +274,8 @@ struct _virStorageSource {
     bool encryptionInherited;
     virStoragePRDefPtr pr;
 
+    virStorageSourceNVMeDefPtr nvme; /* type == VIR_STORAGE_TYPE_NVME */
+
     virStorageSourceInitiatorDef initiator;
 
     virObjectPtr privateData;
@@ -416,6 +430,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/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 d5c66d8791..6202eed439 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -348,6 +348,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, QEMU_CAPS_QCOW2_LUKS);
     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




More information about the libvir-list mailing list