Add support for parsing multiple IOMMU devices from
the VM definition when "smmuv3Dev" is the IOMMU model.
Signed-off-by: Nathan Chen <nathanc(a)nvidia.com>
---
src/conf/domain_conf.c | 153 ++++++++++++++++++++++++++----
src/conf/domain_conf.h | 9 +-
src/conf/domain_validate.c | 32 ++++---
src/conf/schemas/domaincommon.rng | 4 +-
src/libvirt_private.syms | 2 +
src/qemu/qemu_alias.c | 15 ++-
src/qemu/qemu_command.c | 146 ++++++++++++++--------------
src/qemu/qemu_domain_address.c | 35 +++----
src/qemu/qemu_driver.c | 8 +-
src/qemu/qemu_postparse.c | 11 ++-
src/qemu/qemu_validate.c | 2 +-
11 files changed, 284 insertions(+), 133 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index dc222887d4..5ea4d6424b 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -4132,7 +4132,8 @@ void virDomainDefFree(virDomainDef *def)
virDomainCryptoDefFree(def->cryptos[i]);
g_free(def->cryptos);
- virDomainIOMMUDefFree(def->iommu);
+ for (i = 0; i < def->niommus; i++)
+ virDomainIOMMUDefFree(def->iommu[i]);
virDomainPstoreDefFree(def->pstore);
@@ -5004,9 +5005,9 @@ virDomainDeviceInfoIterateFlags(virDomainDef *def,
}
device.type = VIR_DOMAIN_DEVICE_IOMMU;
- if (def->iommu) {
- device.data.iommu = def->iommu;
- if ((rc = cb(def, &device, &def->iommu->info, opaque)) != 0)
+ for (i = 0; i < def->niommus; i++) {
+ device.data.iommu = def->iommu[i];
+ if ((rc = cb(def, &device, &def->iommu[i]->info, opaque)) != 0)
return rc;
}
@@ -16446,6 +16447,112 @@ virDomainInputDefFind(const virDomainDef *def,
}
+bool
+virDomainIOMMUDefEquals(const virDomainIOMMUDef *a,
+ const virDomainIOMMUDef *b)
+{
+ if (a->model != b->model ||
+ a->intremap != b->intremap ||
+ a->caching_mode != b->caching_mode ||
+ a->eim != b->eim ||
+ a->iotlb != b->iotlb ||
+ a->aw_bits != b->aw_bits ||
+ a->parent_idx != b->parent_idx ||
+ a->accel != b->accel ||
+ a->dma_translation != b->dma_translation)
+ return false;
+
+ switch (a->info.type) {
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
+ if (a->info.addr.pci.domain != b->info.addr.pci.domain ||
+ a->info.addr.pci.bus != b->info.addr.pci.bus ||
+ a->info.addr.pci.slot != b->info.addr.pci.slot ||
+ a->info.addr.pci.function != b->info.addr.pci.function) {
+ return false;
+ }
+ break;
+
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE:
+ if (a->info.addr.drive.controller != b->info.addr.drive.controller ||
+ a->info.addr.drive.bus != b->info.addr.drive.bus ||
+ a->info.addr.drive.unit != b->info.addr.drive.unit) {
+ return false;
+ }
+ break;
+
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL:
+ if (a->info.addr.vioserial.controller != b->info.addr.vioserial.controller
||
+ a->info.addr.vioserial.bus != b->info.addr.vioserial.bus ||
+ a->info.addr.vioserial.port != b->info.addr.vioserial.port) {
+ return false;
+ }
+ break;
+
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID:
+ if (a->info.addr.ccid.controller != b->info.addr.ccid.controller ||
+ a->info.addr.ccid.slot != b->info.addr.ccid.slot) {
+ return false;
+ }
+ break;
+
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA:
+ if (a->info.addr.isa.iobase != b->info.addr.isa.iobase ||
+ a->info.addr.isa.irq != b->info.addr.isa.irq) {
+ return false;
+ }
+ break;
+
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM:
+ if (a->info.addr.dimm.slot != b->info.addr.dimm.slot) {
+ return false;
+ }
+
+ if (a->info.addr.dimm.base != b->info.addr.dimm.base) {
+ return false;
+ }
+ break;
+
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW:
+ if (a->info.addr.ccw.cssid != b->info.addr.ccw.cssid ||
+ a->info.addr.ccw.ssid != b->info.addr.ccw.ssid ||
+ a->info.addr.ccw.devno != b->info.addr.ccw.devno) {
+ return false;
+ }
+ break;
+
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO:
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390:
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO:
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE:
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED:
+ case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST:
+ break;
+ }
+
+ if (a->info.acpiIndex != b->info.acpiIndex) {
+ return false;
+ }
+
+ return true;
+}
+
+
+ssize_t
+virDomainIOMMUDefFind(const virDomainDef *def,
+ const virDomainIOMMUDef *iommu)
+{
+ size_t i;
+
+ for (i = 0; i < def->niommus; i++) {
+ if (virDomainIOMMUDefEquals(iommu, def->iommu[i]))
+ return i;
+ }
+
+ return -1;
+}
+
+
bool
virDomainVsockDefEquals(const virDomainVsockDef *a,
const virDomainVsockDef *b)
@@ -20098,19 +20205,28 @@ virDomainDefParseXML(xmlXPathContextPtr ctxt,
}
VIR_FREE(nodes);
+ /* analysis of iommu devices */
if ((n = virXPathNodeSet("./devices/iommu", ctxt, &nodes)) < 0)
return NULL;
- if (n > 1) {
+ if (n > 1 && !virXPathBoolean("./devices/iommu/@model =
'smmuv3Dev'", ctxt)) {
virReportError(VIR_ERR_XML_ERROR, "%s",
- _("only a single IOMMU device is supported"));
+ _("multiple IOMMU devices are only supported with model
smmuv3Dev"));
return NULL;
}
- if (n > 0) {
- if (!(def->iommu = virDomainIOMMUDefParseXML(xmlopt, nodes[0],
- ctxt, flags)))
+ if (n > 0)
+ def->iommu = g_new0(virDomainIOMMUDef *, n);
+
+ for (i = 0; i < n; i++) {
+ virDomainIOMMUDef *iommu;
+
+ iommu = virDomainIOMMUDefParseXML(xmlopt, nodes[i], ctxt, flags);
+
+ if (!iommu)
return NULL;
+
+ def->iommu[def->niommus++] = iommu;
}
VIR_FREE(nodes);
@@ -22558,15 +22674,17 @@ virDomainDefCheckABIStabilityFlags(virDomainDef *src,
goto error;
}
- if (!!src->iommu != !!dst->iommu) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("Target domain IOMMU device count does not match
source"));
+ if (src->niommus != dst->niommus) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Target domain IOMMU device count %1$zu does not match
source %2$zu"),
+ dst->niommus, src->niommus);
goto error;
}
- if (src->iommu &&
- !virDomainIOMMUDefCheckABIStability(src->iommu, dst->iommu))
- goto error;
+ for (i = 0; i < src->niommus; i++) {
+ if (!virDomainIOMMUDefCheckABIStability(src->iommu[i], dst->iommu[i]))
+ goto error;
+ }
if (!!src->vsock != !!dst->vsock) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
@@ -29402,8 +29520,9 @@ virDomainDefFormatInternalSetRootName(virDomainDef *def,
for (n = 0; n < def->ncryptos; n++) {
virDomainCryptoDefFormat(buf, def->cryptos[n], flags);
}
- if (def->iommu)
- virDomainIOMMUDefFormat(buf, def->iommu);
+
+ for (n = 0; n < def->niommus; n++)
+ virDomainIOMMUDefFormat(buf, def->iommu[n]);
if (def->vsock)
virDomainVsockDefFormat(buf, def->vsock);
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index f87c5bbe93..edb18632f3 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -3294,6 +3294,9 @@ struct _virDomainDef {
size_t nwatchdogs;
virDomainWatchdogDef **watchdogs;
+ size_t niommus;
+ virDomainIOMMUDef **iommu;
+
/* At maximum 2 TPMs on the domain if a TPM Proxy is present. */
size_t ntpms;
virDomainTPMDef **tpms;
@@ -3303,7 +3306,6 @@ struct _virDomainDef {
virDomainNVRAMDef *nvram;
virCPUDef *cpu;
virDomainRedirFilterDef *redirfilter;
- virDomainIOMMUDef *iommu;
virDomainVsockDef *vsock;
virDomainPstoreDef *pstore;
@@ -4308,6 +4310,11 @@ virDomainShmemDef *virDomainShmemDefRemove(virDomainDef *def,
size_t idx)
ssize_t virDomainInputDefFind(const virDomainDef *def,
const virDomainInputDef *input)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
+bool virDomainIOMMUDefEquals(const virDomainIOMMUDef *a,
+ const virDomainIOMMUDef *b);
+ssize_t virDomainIOMMUDefFind(const virDomainDef *def,
+ const virDomainIOMMUDef *iommu)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
bool virDomainVsockDefEquals(const virDomainVsockDef *a,
const virDomainVsockDef *b)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
index f1b1b8cc55..b2f94b921f 100644
--- a/src/conf/domain_validate.c
+++ b/src/conf/domain_validate.c
@@ -1840,21 +1840,31 @@ virDomainDefCputuneValidate(const virDomainDef *def)
static int
virDomainDefIOMMUValidate(const virDomainDef *def)
{
+ size_t i;
+
if (!def->iommu)
return 0;
- if (def->iommu->intremap == VIR_TRISTATE_SWITCH_ON &&
- def->features[VIR_DOMAIN_FEATURE_IOAPIC] != VIR_DOMAIN_IOAPIC_QEMU) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("IOMMU interrupt remapping requires split I/O APIC (ioapic
driver='qemu')"));
- return -1;
- }
+ for (i = 0; i < def->niommus; i++) {
+ virDomainIOMMUDef *iommu = def->iommu[i];
+ if (def->niommus > 1 && iommu->model !=
VIR_DOMAIN_IOMMU_MODEL_SMMUV3_DEV) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("IOMMU model smmuv3Dev must be specified for multiple
IOMMU definitions"));
+ }
- if (def->iommu->eim == VIR_TRISTATE_SWITCH_ON &&
- def->iommu->intremap != VIR_TRISTATE_SWITCH_ON) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("IOMMU eim requires interrupt remapping to be
enabled"));
- 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",
+ _("IOMMU interrupt remapping requires split I/O APIC
(ioapic driver='qemu')"));
+ return -1;
+ }
+
+ if (iommu->eim == VIR_TRISTATE_SWITCH_ON &&
+ iommu->intremap != VIR_TRISTATE_SWITCH_ON) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("IOMMU eim requires interrupt remapping to be
enabled"));
+ return -1;
+ }
}
return 0;
diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
index 0e57d2a9b9..fd19f115f7 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -6944,9 +6944,9 @@
<zeroOrMore>
<ref name="panic"/>
</zeroOrMore>
- <optional>
+ <zeroOrMore>
<ref name="iommu"/>
- </optional>
+ </zeroOrMore>
<optional>
<ref name="vsock"/>
</optional>
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index b846011f0f..924cfa1db7 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -491,6 +491,8 @@ virDomainInputSourceGrabToggleTypeToString;
virDomainInputSourceGrabTypeFromString;
virDomainInputSourceGrabTypeToString;
virDomainInputTypeToString;
+virDomainIOMMUDefEquals;
+virDomainIOMMUDefFind;
virDomainIOMMUDefFree;
virDomainIOMMUDefNew;
virDomainIOMMUModelTypeFromString;
diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c
index a27c688d79..5f2b11b9a6 100644
--- a/src/qemu/qemu_alias.c
+++ b/src/qemu/qemu_alias.c
@@ -647,10 +647,14 @@ qemuAssignDeviceVsockAlias(virDomainVsockDef *vsock)
static void
-qemuAssignDeviceIOMMUAlias(virDomainIOMMUDef *iommu)
+qemuAssignDeviceIOMMUAlias(virDomainDef *def,
+ virDomainIOMMUDef **iommu)
{
- if (!iommu->info.alias)
- iommu->info.alias = g_strdup("iommu0");
+ size_t i;
+ for (i = 0; i < def->niommus; i++) {
+ if (!iommu[i]->info.alias)
+ iommu[i]->info.alias = g_strdup_printf("iommu%zu", i);
+ }
}
@@ -766,8 +770,9 @@ qemuAssignDeviceAliases(virDomainDef *def)
if (def->vsock) {
qemuAssignDeviceVsockAlias(def->vsock);
}
- if (def->iommu)
- qemuAssignDeviceIOMMUAlias(def->iommu);
+ if (def->iommu && def->niommus > 0) {
+ qemuAssignDeviceIOMMUAlias(def, def->iommu);
+ }
for (i = 0; i < def->ncryptos; i++) {
qemuAssignDeviceCryptoAlias(def, def->cryptos[i]);
}
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 8a124a495b..cecd0661ca 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -6252,10 +6252,12 @@ qemuBuildBootCommandLine(virCommand *cmd,
static virJSONValue *
qemuBuildPCISmmuv3DevDevProps(const virDomainDef *def,
- const virDomainIOMMUDef *iommu)
+ const virDomainIOMMUDef *iommu,
+ size_t id)
{
g_autoptr(virJSONValue) props = NULL;
g_autofree char *bus = NULL;
+ g_autofree char *smmuv3_id = NULL;
size_t i;
bool contIsPHB = false;
@@ -6296,9 +6298,12 @@ qemuBuildPCISmmuv3DevDevProps(const virDomainDef *def,
bus = temp_bus;
}
+ smmuv3_id = g_strdup_printf("smmuv3.%zu", id);
+
if (virJSONValueObjectAdd(&props,
"s:driver", "arm-smmuv3",
"s:primary-bus", bus,
+ "s:id", smmuv3_id,
"b:accel", (iommu->accel ==
VIR_TRISTATE_SWITCH_ON),
NULL) < 0)
return NULL;
@@ -6312,91 +6317,92 @@ qemuBuildIOMMUCommandLine(virCommand *cmd,
const virDomainDef *def,
virQEMUCaps *qemuCaps)
{
+ size_t i;
g_autoptr(virJSONValue) props = NULL;
g_autoptr(virJSONValue) wrapperProps = NULL;
- const virDomainIOMMUDef *iommu = def->iommu;
-
- if (!iommu)
+ if (!def->iommu || def->niommus <= 0)
return 0;
- switch (iommu->model) {
- case VIR_DOMAIN_IOMMU_MODEL_INTEL:
- if (virJSONValueObjectAdd(&props,
- "s:driver", "intel-iommu",
- "s:id", iommu->info.alias,
- "S:intremap",
qemuOnOffAuto(iommu->intremap),
- "T:caching-mode", iommu->caching_mode,
- "S:eim", qemuOnOffAuto(iommu->eim),
- "T:device-iotlb", iommu->iotlb,
- "z:aw-bits", iommu->aw_bits,
- "T:dma-translation",
iommu->dma_translation,
- NULL) < 0)
- return -1;
+ for (i = 0; i < def->niommus; i++) {
+ virDomainIOMMUDef *iommu = def->iommu[i];
+ switch (iommu->model) {
+ case VIR_DOMAIN_IOMMU_MODEL_INTEL:
+ if (virJSONValueObjectAdd(&props,
+ "s:driver", "intel-iommu",
+ "s:id", iommu->info.alias,
+ "S:intremap",
qemuOnOffAuto(iommu->intremap),
+ "T:caching-mode",
iommu->caching_mode,
+ "S:eim", qemuOnOffAuto(iommu->eim),
+ "T:device-iotlb", iommu->iotlb,
+ "z:aw-bits", iommu->aw_bits,
+ "T:dma-translation",
iommu->dma_translation,
+ NULL) < 0)
+ return -1;
- if (qemuBuildDeviceCommandlineFromJSON(cmd, props, def, qemuCaps) < 0)
- return -1;
+ if (qemuBuildDeviceCommandlineFromJSON(cmd, props, def, qemuCaps) < 0)
+ return -1;
- return 0;
+ return 0;
+ case VIR_DOMAIN_IOMMU_MODEL_VIRTIO:
+ if (virJSONValueObjectAdd(&props,
+ "s:driver", "virtio-iommu",
+ "s:id", iommu->info.alias,
+ NULL) < 0) {
+ return -1;
+ }
- case VIR_DOMAIN_IOMMU_MODEL_VIRTIO:
- if (virJSONValueObjectAdd(&props,
- "s:driver", "virtio-iommu",
- "s:id", iommu->info.alias,
- NULL) < 0) {
- return -1;
- }
+ if (qemuBuildDeviceAddressProps(props, def, &iommu->info) < 0)
+ return -1;
- if (qemuBuildDeviceAddressProps(props, def, &iommu->info) < 0)
- return -1;
+ if (qemuBuildDeviceCommandlineFromJSON(cmd, props, def, qemuCaps) < 0)
+ return -1;
- if (qemuBuildDeviceCommandlineFromJSON(cmd, props, def, qemuCaps) < 0)
- return -1;
+ return 0;
+ case VIR_DOMAIN_IOMMU_MODEL_SMMUV3:
+ /* There is no -device for SMMUv3, so nothing to be done here */
+ return 0;
- return 0;
+ case VIR_DOMAIN_IOMMU_MODEL_AMD:
+ if (virJSONValueObjectAdd(&wrapperProps,
+ "s:driver", "AMDVI-PCI",
+ "s:id", iommu->info.alias,
+ NULL) < 0)
+ return -1;
- case VIR_DOMAIN_IOMMU_MODEL_SMMUV3:
- return 0;
+ if (qemuBuildDeviceAddressProps(wrapperProps, def, &iommu->info) <
0)
+ return -1;
- case VIR_DOMAIN_IOMMU_MODEL_AMD:
- if (virJSONValueObjectAdd(&wrapperProps,
- "s:driver", "AMDVI-PCI",
- "s:id", iommu->info.alias,
- NULL) < 0)
- return -1;
+ if (qemuBuildDeviceCommandlineFromJSON(cmd, wrapperProps, def, qemuCaps) <
0)
+ return -1;
- if (qemuBuildDeviceAddressProps(wrapperProps, def, &iommu->info) < 0)
- return -1;
+ if (virJSONValueObjectAdd(&props,
+ "s:driver", "amd-iommu",
+ "s:pci-id", iommu->info.alias,
+ "S:intremap",
qemuOnOffAuto(iommu->intremap),
+ "T:pt", iommu->pt,
+ "T:xtsup", iommu->xtsup,
+ "T:device-iotlb", iommu->iotlb,
+ NULL) < 0)
+ return -1;
- if (qemuBuildDeviceCommandlineFromJSON(cmd, wrapperProps, def, qemuCaps) < 0)
- return -1;
+ if (qemuBuildDeviceCommandlineFromJSON(cmd, props, def, qemuCaps) < 0)
+ return -1;
- if (virJSONValueObjectAdd(&props,
- "s:driver", "amd-iommu",
- "s:pci-id", iommu->info.alias,
- "S:intremap",
qemuOnOffAuto(iommu->intremap),
- "T:pt", iommu->pt,
- "T:xtsup", iommu->xtsup,
- "T:device-iotlb", iommu->iotlb,
- NULL) < 0)
- return -1;
+ return 0;
- if (qemuBuildDeviceCommandlineFromJSON(cmd, props, def, qemuCaps) < 0)
- return -1;
+ case VIR_DOMAIN_IOMMU_MODEL_SMMUV3_DEV:
+ if (!(props = qemuBuildPCISmmuv3DevDevProps(def, iommu, i)))
+ return -1;
+ if (qemuBuildDeviceCommandlineFromJSON(cmd, props, def, qemuCaps) < 0)
+ return -1;
+ break;
- return 0;
- case VIR_DOMAIN_IOMMU_MODEL_SMMUV3_DEV:
- if (!(props = qemuBuildPCISmmuv3DevDevProps(def, iommu)))
- return -1;
- if (qemuBuildDeviceCommandlineFromJSON(cmd, props, def, qemuCaps) < 0)
+ case VIR_DOMAIN_IOMMU_MODEL_LAST:
+ default:
+ virReportEnumRangeError(virDomainIOMMUModel, iommu->model);
return -1;
-
- return 0;
-
- case VIR_DOMAIN_IOMMU_MODEL_LAST:
- default:
- virReportEnumRangeError(virDomainIOMMUModel, iommu->model);
- return -1;
+ }
}
return 0;
@@ -7217,8 +7223,8 @@ qemuBuildMachineCommandLine(virCommand *cmd,
if (qemuAppendDomainFeaturesMachineParam(&buf, def, qemuCaps) < 0)
return -1;
- if (def->iommu) {
- switch (def->iommu->model) {
+ if (def->iommu && def->niommus == 1) {
+ switch (def->iommu[0]->model) {
case VIR_DOMAIN_IOMMU_MODEL_SMMUV3:
virBufferAddLit(&buf, ",iommu=smmuv3");
break;
@@ -7232,7 +7238,7 @@ qemuBuildMachineCommandLine(virCommand *cmd,
case VIR_DOMAIN_IOMMU_MODEL_LAST:
default:
- virReportEnumRangeError(virDomainIOMMUModel, def->iommu->model);
+ virReportEnumRangeError(virDomainIOMMUModel, def->iommu[0]->model);
return -1;
}
}
diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c
index 06bf4fab32..2ddc629304 100644
--- a/src/qemu/qemu_domain_address.c
+++ b/src/qemu/qemu_domain_address.c
@@ -2365,24 +2365,25 @@ qemuDomainAssignDevicePCISlots(virDomainDef *def,
/* Nada - none are PCI based (yet) */
}
- if (def->iommu) {
- virDomainIOMMUDef *iommu = def->iommu;
-
- switch (iommu->model) {
- case VIR_DOMAIN_IOMMU_MODEL_VIRTIO:
- case VIR_DOMAIN_IOMMU_MODEL_AMD:
- if (virDeviceInfoPCIAddressIsWanted(&iommu->info) &&
- qemuDomainPCIAddressReserveNextAddr(addrs, &iommu->info) < 0)
{
- return -1;
- }
- break;
+ if (def->iommu && def->niommus > 0) {
+ for (i = 0; i < def->niommus; i++) {
+ virDomainIOMMUDef *iommu = def->iommu[i];
+ switch (iommu->model) {
+ case VIR_DOMAIN_IOMMU_MODEL_VIRTIO:
+ case VIR_DOMAIN_IOMMU_MODEL_AMD:
+ if (virDeviceInfoPCIAddressIsWanted(&iommu->info) &&
+ qemuDomainPCIAddressReserveNextAddr(addrs, &iommu->info) <
0) {
+ return -1;
+ }
+ break;
- case VIR_DOMAIN_IOMMU_MODEL_INTEL:
- case VIR_DOMAIN_IOMMU_MODEL_SMMUV3:
- case VIR_DOMAIN_IOMMU_MODEL_SMMUV3_DEV:
- case VIR_DOMAIN_IOMMU_MODEL_LAST:
- /* These are not PCI devices */
- break;
+ case VIR_DOMAIN_IOMMU_MODEL_INTEL:
+ case VIR_DOMAIN_IOMMU_MODEL_SMMUV3:
+ case VIR_DOMAIN_IOMMU_MODEL_SMMUV3_DEV:
+ case VIR_DOMAIN_IOMMU_MODEL_LAST:
+ /* These are not PCI devices */
+ break;
+ }
}
}
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index ac72ea5cb0..3d65f78c9e 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -6894,12 +6894,12 @@ qemuDomainAttachDeviceConfig(virDomainDef *vmdef,
break;
case VIR_DOMAIN_DEVICE_IOMMU:
- if (vmdef->iommu) {
+ if (vmdef->iommu && vmdef->niommus > 0) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("domain already has an iommu device"));
return -1;
}
- vmdef->iommu = g_steal_pointer(&dev->data.iommu);
+ VIR_APPEND_ELEMENT(vmdef->iommu, vmdef->niommus, dev->data.iommu);
break;
case VIR_DOMAIN_DEVICE_VIDEO:
@@ -7113,12 +7113,12 @@ qemuDomainDetachDeviceConfig(virDomainDef *vmdef,
break;
case VIR_DOMAIN_DEVICE_IOMMU:
- if (!vmdef->iommu) {
+ if ((idx = virDomainIOMMUDefFind(vmdef, dev->data.iommu)) < 0) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("matching iommu device not found"));
return -1;
}
- g_clear_pointer(&vmdef->iommu, virDomainIOMMUDefFree);
+ VIR_DELETE_ELEMENT(vmdef->iommu, idx, vmdef->niommus);
break;
case VIR_DOMAIN_DEVICE_VIDEO:
diff --git a/src/qemu/qemu_postparse.c b/src/qemu/qemu_postparse.c
index 9c2427970d..e2744a4a61 100644
--- a/src/qemu/qemu_postparse.c
+++ b/src/qemu/qemu_postparse.c
@@ -1503,7 +1503,7 @@ qemuDomainDefAddDefaultDevices(virQEMUDriver *driver,
}
}
- if (addIOMMU && !def->iommu &&
+ if (addIOMMU && !def->iommu && def->niommus == 0 &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_INTEL_IOMMU) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) {
@@ -1515,7 +1515,8 @@ qemuDomainDefAddDefaultDevices(virQEMUDriver *driver,
iommu->intremap = VIR_TRISTATE_SWITCH_ON;
iommu->eim = VIR_TRISTATE_SWITCH_ON;
- def->iommu = g_steal_pointer(&iommu);
+ def->iommu = g_new0(virDomainIOMMUDef *, 1);
+ def->iommu[def->niommus++] = g_steal_pointer(&iommu);
}
if (qemuDomainDefAddDefaultAudioBackend(driver, def) < 0)
@@ -1591,9 +1592,9 @@ qemuDomainDefEnableDefaultFeatures(virDomainDef *def,
* domain already has IOMMU without inremap. This will be fixed in
* qemuDomainIOMMUDefPostParse() but there domain definition can't be
* modified so change it now. */
- if (def->iommu &&
- (def->iommu->intremap == VIR_TRISTATE_SWITCH_ON ||
- qemuDomainNeedsIOMMUWithEIM(def)) &&
+ if (def->iommu && def->niommus == 1 &&
+ (def->iommu[0]->intremap == VIR_TRISTATE_SWITCH_ON ||
+ qemuDomainNeedsIOMMUWithEIM(def)) &&
def->features[VIR_DOMAIN_FEATURE_IOAPIC] == VIR_DOMAIN_IOAPIC_NONE) {
def->features[VIR_DOMAIN_FEATURE_IOAPIC] = VIR_DOMAIN_IOAPIC_QEMU;
}
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c
index 163d7758b8..1b5b1deb5d 100644
--- a/src/qemu/qemu_validate.c
+++ b/src/qemu/qemu_validate.c
@@ -851,7 +851,7 @@ qemuValidateDomainVCpuTopology(const virDomainDef *def, virQEMUCaps
*qemuCaps)
QEMU_MAX_VCPUS_WITHOUT_EIM);
return -1;
}
- if (!def->iommu || def->iommu->eim != VIR_TRISTATE_SWITCH_ON) {
+ if (!def->iommu || def->iommu[0]->eim != VIR_TRISTATE_SWITCH_ON) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("more than %1$d vCPUs require extended interrupt mode
enabled on the iommu device"),
QEMU_MAX_VCPUS_WITHOUT_EIM);
--
2.43.0