Add support for an "iommufd" virDomainIOMMUDef member that will be
translated to a qemu "-object iommufd" command line argument. This
iommufd struct has an "id" member to specify the iommufd object id
that can be associated with a passthrough device, and an "fd"
member to specify the fd for externally opening the /dev/iommu and
VFIO cdev by a management layer.

Signed-off-by: Nathan Chen <nath...@nvidia.com>
---
 src/conf/domain_conf.c            | 108 +++++++++++++++++++++++++++++-
 src/conf/domain_conf.h            |   8 +++
 src/conf/domain_validate.c        |  44 +++++++++++-
 src/conf/schemas/domaincommon.rng |  14 ++++
 src/conf/virconftypes.h           |   2 +
 src/qemu/qemu_command.c           |  13 ++++
 6 files changed, 186 insertions(+), 3 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index c8d481eb5c..9330c47b7a 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2814,6 +2814,9 @@ virDomainIOMMUDefFree(virDomainIOMMUDef *iommu)
     if (!iommu)
         return;
 
+    if (iommu->iommufd)
+        virDomainIommufdDefFree(iommu->iommufd);
+
     virDomainDeviceInfoClear(&iommu->info);
     g_free(iommu);
 }
@@ -14292,6 +14295,54 @@ virDomainMemoryDefParseXML(virDomainXMLOption *xmlopt,
 }
 
 
+void virDomainIommufdDefFree(virDomainIommufdDef *def)
+{
+    if (!def)
+        return;
+
+    g_free(def->id);
+
+    g_free(def->fd);
+
+    g_free(def);
+}
+
+
+static virDomainIommufdDef *
+virDomainIommufdParseXML(xmlNodePtr node,
+                         xmlXPathContextPtr ctxt)
+{
+    virDomainIommufdDef *def;
+    g_autofree char *iommufdId = NULL;
+    g_autofree char *iommufdFd = NULL;
+    VIR_XPATH_NODE_AUTORESTORE(ctxt)
+
+    ctxt->node = node;
+
+    def = g_new0(virDomainIommufdDef, 1);
+
+    if (!(iommufdId = virXPathString("string(./id)", ctxt)))
+        goto error;
+
+    def->id = g_strdup(iommufdId);
+
+    iommufdFd = virXPathString("string(./fd)", ctxt);
+
+    def->fd = g_strdup(iommufdFd);
+
+    if (!def->id)
+        goto error;
+
+    if (iommufdFd && !def->fd)
+        goto error;
+
+    return def;
+ error:
+    virDomainIommufdDefFree(def);
+    return NULL;
+}
+
+
 static virDomainIOMMUDef *
 virDomainIOMMUDefParseXML(virDomainXMLOption *xmlopt,
                           xmlNodePtr node,
@@ -14299,7 +14350,7 @@ virDomainIOMMUDefParseXML(virDomainXMLOption *xmlopt,
                           unsigned int flags)
 {
     VIR_XPATH_NODE_AUTORESTORE(ctxt)
-    xmlNodePtr driver;
+    xmlNodePtr driver, iommufd;
     g_autoptr(virDomainIOMMUDef) iommu = NULL;
 
     ctxt->node = node;
@@ -14336,6 +14387,11 @@ virDomainIOMMUDefParseXML(virDomainXMLOption *xmlopt,
             return NULL;
     }
 
+    if ((iommufd = virXPathNode("./iommufd", ctxt))) {
+        if (!(iommu->iommufd = virDomainIommufdParseXML(iommufd, ctxt)))
+            return NULL;
+    }
+
     if (virDomainDeviceInfoParseXML(xmlopt, node, ctxt,
                                     &iommu->info, flags) < 0)
         return NULL;
@@ -16384,6 +16440,25 @@ virDomainIOMMUDefEquals(const virDomainIOMMUDef *a,
         a->dma_translation != b->dma_translation)
         return false;
 
+    if (a->iommufd && b->iommufd) {
+        if (a->iommufd->id && b->iommufd->id) {
+            if (STRNEQ(a->iommufd->id, b->iommufd->id))
+                return false;
+        } else if ((a->iommufd->id && !b->iommufd->id) ||
+                   (!a->iommufd->id && b->iommufd->id)) {
+            return false;
+        }
+        if (a->iommufd->fd && b->iommufd->fd) {
+            if (STRNEQ(a->iommufd->fd, b->iommufd->fd))
+                return false;
+        } else if ((a->iommufd->fd && !b->iommufd->fd) ||
+                   (!a->iommufd->fd && b->iommufd->fd)) {
+            return false;
+        }
+    } else {
+        return false;
+    }
+
     switch (a->info.type) {
     case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
         if (a->info.addr.pci.domain != b->info.addr.pci.domain ||
@@ -22078,6 +22153,27 @@ virDomainIOMMUDefCheckABIStability(virDomainIOMMUDef 
*src,
                        virTristateSwitchTypeToString(src->dma_translation));
         return false;
     }
+    if (src->iommufd && dst->iommufd) {
+        if (STRNEQ(src->iommufd->id, dst->iommufd->id)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("Target domain IOMMU device iommufd id '%1$s' 
does not match source '%2$s'"),
+                           dst->iommufd->id,
+                           src->iommufd->id);
+            return false;
+        }
+        if (STRNEQ(src->iommufd->fd, dst->iommufd->fd)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("Target domain IOMMU device iommufd fd '%1$s' 
does not match source '%2$s'"),
+                           dst->iommufd->fd,
+                           src->iommufd->fd);
+            return false;
+        }
+    } else if ((src->iommufd && !dst->iommufd) ||
+               (!src->iommufd && dst->iommufd)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Target domain IOMMU device iommufd does not match 
source"));
+        return false;
+    }
 
     return virDomainDeviceInfoCheckABIStability(&src->info, &dst->info);
 }
@@ -28310,6 +28406,16 @@ virDomainIOMMUDefFormat(virBuffer *buf,
 
     virXMLFormatElement(&childBuf, "driver", &driverAttrBuf, NULL);
 
+    if (iommu->iommufd) {
+        virBufferAddLit(&childBuf, "<iommufd>\n");
+        virBufferAdjustIndent(&childBuf, 2);
+        virBufferAsprintf(&childBuf, "<id>%s</id>\n", iommu->iommufd->id);
+        if (iommu->iommufd->fd)
+            virBufferAsprintf(&childBuf, "<fd>%s</fd>\n", iommu->iommufd->fd);
+        virBufferAdjustIndent(&childBuf, -2);
+        virBufferAddLit(&childBuf, "</iommufd>\n");
+    }
+
     virDomainDeviceInfoFormat(&childBuf, &iommu->info, 0);
 
     virBufferAsprintf(&attrBuf, " model='%s'",
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 75568ff50a..ce447e9823 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -3010,6 +3010,11 @@ typedef enum {
     VIR_DOMAIN_IOMMU_MODEL_LAST
 } virDomainIOMMUModel;
 
+struct _virDomainIommufdDef {
+    char *id;
+    char *fd;
+};
+
 struct _virDomainIOMMUDef {
     virDomainIOMMUModel model;
     virTristateSwitch intremap;
@@ -3017,6 +3022,8 @@ struct _virDomainIOMMUDef {
     virTristateSwitch eim;
     virTristateSwitch iotlb;
     unsigned int aw_bits;
+    virDomainIommufdDef *iommufd;
+
     virDomainDeviceInfo info;
     virTristateSwitch dma_translation;
 };
@@ -3747,6 +3754,7 @@ virDomainVideoDef 
*virDomainVideoDefNew(virDomainXMLOption *xmlopt);
 void virDomainVideoDefFree(virDomainVideoDef *def);
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainVideoDef, virDomainVideoDefFree);
 void virDomainVideoDefClear(virDomainVideoDef *def);
+void virDomainIommufdDefFree(virDomainIommufdDef *def);
 virDomainHostdevDef *virDomainHostdevDefNew(void);
 void virDomainHostdevDefFree(virDomainHostdevDef *def);
 void virDomainHubDefFree(virDomainHubDef *def);
diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
index 4861b1c002..88649ba0b9 100644
--- a/src/conf/domain_validate.c
+++ b/src/conf/domain_validate.c
@@ -1838,9 +1838,9 @@ virDomainDefCputuneValidate(const virDomainDef *def)
 static int
 virDomainDefIOMMUValidate(const virDomainDef *def)
 {
-    size_t i;
+    size_t i, j;
 
-    if (!def->iommu)
+    if (!def->iommu || def->niommus == 0)
          return 0;
 
     for (i = 0; i < def->niommus; i++) {
@@ -1851,6 +1851,39 @@ virDomainDefIOMMUValidate(const virDomainDef *def)
                            _("IOMMU model smmuv3Dev must be specified for 
multiple IOMMU definitions"));
         }
 
+        for (j = i + 1; j < def->niommus; j++) {
+            if (virDomainIOMMUDefEquals(iommu,
+                                    def->iommu[j])) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                               _("IOMMU already exists in the domain 
configuration"));
+                return -1;
+            }
+
+            if (iommu->iommufd && def->iommu[j]->iommufd) {
+                if (iommu->iommufd->id && def->iommu[j]->iommufd->id) {
+                    if (STRNEQ(iommu->iommufd->id, 
def->iommu[j]->iommufd->id)) {
+                        virReportError(VIR_ERR_XML_ERROR, "%s",
+                                       _("iommufd ID must be the same for 
multiple IOMMUs"));
+                        return -1;
+                    }
+                } else {
+                    virReportError(VIR_ERR_XML_ERROR, "%s",
+                                   _("iommufd ID must be specified"));
+                }
+                if (iommu->iommufd->fd && def->iommu[j]->iommufd->fd &&
+                    STRNEQ(iommu->iommufd->fd, def->iommu[j]->iommufd->fd)) {
+                    virReportError(VIR_ERR_XML_ERROR, "%s",
+                                   _("iommufd FD must be the same for multiple 
IOMMUs"));
+                    return -1;
+                }
+            } else if ((iommu->iommufd && !def->iommu[j]->iommufd) ||
+                       (!iommu->iommufd && def->iommu[j]->iommufd)) {
+                virReportError(VIR_ERR_XML_ERROR, "%s",
+                               _("The same iommufd configuration must be 
specified for multiple IOMMUs"));
+                return -1;
+            }
+        }
+
         if (iommu->intremap == VIR_TRISTATE_SWITCH_ON &&
             def->features[VIR_DOMAIN_FEATURE_IOAPIC] != 
VIR_DOMAIN_IOAPIC_QEMU) {
             virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
@@ -3115,6 +3148,13 @@ virDomainIOMMUDefValidate(const virDomainIOMMUDef *iommu)
         break;
     }
 
+    if (iommu->iommufd) {
+        if (!iommu->iommufd->id) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("iommufd must have an associated id"));
+            return -1;
+        }
+    }
     return 0;
 }
 
diff --git a/src/conf/schemas/domaincommon.rng 
b/src/conf/schemas/domaincommon.rng
index 8d1768b24f..41db338c71 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -6231,6 +6231,20 @@
         <optional>
           <ref name="acpi"/>
         </optional>
+        <optional>
+          <element name="iommufd">
+            <interleave>
+              <element name="id">
+                <text/>
+              </element>
+              <optional>
+                <element name="fd">
+                  <text/>
+                </element>
+              </optional>
+            </interleave>
+          </element>
+        </optional>
         <optional>
           <ref name="address"/>
         </optional>
diff --git a/src/conf/virconftypes.h b/src/conf/virconftypes.h
index c70437bc05..47392154a4 100644
--- a/src/conf/virconftypes.h
+++ b/src/conf/virconftypes.h
@@ -142,6 +142,8 @@ typedef struct _virDomainHubDef virDomainHubDef;
 
 typedef struct _virDomainHugePage virDomainHugePage;
 
+typedef struct _virDomainIommufdDef virDomainIommufdDef;
+
 typedef struct _virDomainIOMMUDef virDomainIOMMUDef;
 
 typedef struct _virDomainIOThreadIDDef virDomainIOThreadIDDef;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 9deafefaad..d683d0eef7 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -6166,6 +6166,19 @@ qemuBuildIOMMUCommandLine(virCommand *cmd,
     if (!def->iommu || def->niommus <= 0)
         return 0;
 
+    if (def->iommu[0]->iommufd) {
+        if (qemuMonitorCreateObjectProps(&props, "iommufd",
+                                         def->iommu[0]->iommufd->id,
+                                         "S:fd", def->iommu[0]->iommufd->fd,
+                                         NULL) < 0)
+            return -1;
+
+        if (qemuBuildObjectCommandlineFromJSON(cmd, props) < 0)
+            return -1;
+    }
+
+    props = NULL;
+
     for (i = 0; i < def->niommus; i++) {
         virDomainIOMMUDef *iommu = def->iommu[i];
         switch (iommu->model) {
-- 
2.43.0

Reply via email to