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