From: Michal Privoznik <[email protected]> In PCI assignment scenario the virtio-iommu needs to know the guest page size also known as granule. Expose it as an attribute to the <driver/> element of a virtio-iommu.
This is possibly interesting only for aarch64 since it supports virtio-iommu and also supports running guests with different page size than the host. Signed-off-by: Michal Privoznik <[email protected]> --- docs/formatdomain.rst | 38 ++++++++- src/conf/domain_conf.c | 77 ++++++++++++++++++- src/conf/domain_conf.h | 1 + src/conf/domain_validate.c | 9 ++- src/conf/schemas/domaincommon.rng | 22 ++++++ src/qemu/qemu_validate.c | 11 +++ .../virtio-iommu-aarch64.aarch64-latest.xml | 4 +- .../qemuxmlconfdata/virtio-iommu-aarch64.xml | 4 +- .../virtio-iommu-x86_64.x86_64-latest.xml | 3 + tests/qemuxmlconfdata/virtio-iommu-x86_64.xml | 6 +- 10 files changed, 166 insertions(+), 9 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 4b34a8a963..50563741a0 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -9194,7 +9194,7 @@ IOMMU devices The ``iommu`` element can be used to add an IOMMU device. :since:`Since 2.1.0` -Example: +Examples: :: @@ -9206,6 +9206,17 @@ Example: </devices> ... + + ... + <devices> + <iommu model='virtio'> + <driver aw_bits='48'> + <granule size='64' unit='KiB'/> + </driver> + </iommu> + </devices> + ... + ``model`` Supported values are ``intel`` (for Q35 guests) ``smmuv3`` (:since:`since 5.5.0`, for ARM virt guests), ``virtio`` @@ -9264,6 +9275,31 @@ Example: The ``pciBus`` attribute notes the index of the controller that an IOMMU device is attached to. (QEMU/KVM and ``smmuv3`` model only) +In case of ``virtio`` IOMMU device, the ``driver`` element can optionally +contain ``granule`` subelement that allows to choose which granule will be +used by default. It is useful when running guests with different page size +than the host. :since:`Since 12.1.0` (QEMU/KVM and ``virtio`` model only). +There are two possible options: + +:: + + <iommu model='virtio'> + <driver> + <granule mode='host'/> + </driver> + </iommu> + + <iommu model='virtio'> + <driver> + <granule size='64' unit='KiB'/> + </driver> + </iommu> + +The ``mode='host'`` case matches the host page size, the other sets desired +granule size. Please note that hypervisor might support only some selected +values. For instance, QEMU supports only 4KiB, 8KiB, 16KiB and 64KiB large +granules. + The ``virtio`` IOMMU devices can further have ``address`` element as described in `Device addresses`_ (address has to by type of ``pci``). diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 1b2a439ca4..4503fc0095 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1592,6 +1592,21 @@ VIR_ENUM_IMPL(virDomainChrSourceMode, ); +/*virDomainIOMMUGranuleModeTypeToString: + * @val: value to format + * + * Reuturns: an allocated string. Caller must free it. + */ +static char * +virDomainIOMMUGranuleModeTypeToString(int val) +{ + if (val == -1) + return g_strdup("host"); + + return g_strdup_printf("%dKiB", val); +} + + static virClass *virDomainObjClass; static virClass *virDomainXMLOptionClass; static void virDomainObjDispose(void *obj); @@ -14487,6 +14502,8 @@ virDomainIOMMUDefParseXML(virDomainXMLOption *xmlopt, return NULL; if ((driver = virXPathNode("./driver", ctxt))) { + xmlNodePtr granule; + if (virXMLPropTristateSwitch(driver, "intremap", VIR_XML_PROP_NONE, &iommu->intremap) < 0) return NULL; @@ -14522,6 +14539,41 @@ virDomainIOMMUDefParseXML(virDomainXMLOption *xmlopt, if (virXMLPropInt(driver, "pciBus", 10, VIR_XML_PROP_NONE, &iommu->pci_bus, -1) < 0) return NULL; + + if ((granule = virXPathNode("./driver/granule", ctxt))) { + g_autofree char *mode = virXMLPropString(granule, "mode"); + unsigned long long size; + int rc; + + rc = virDomainParseMemory("./driver/granule/@size", + "./driver/granule/@unit", + ctxt, &size, false, false); + if (rc < 0) { + return NULL; + } else if (rc > 0) { + if (mode) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'mode' and 'size' can't be specified at the same time for 'granule'")); + return NULL; + } + + if (size != (int) size) { + virReportError(VIR_ERR_OVERFLOW, "%s", _("size value too large")); + return NULL; + } + + iommu->granule = (int)size; + } else { + if (STREQ_NULLABLE(mode, "host")) { + iommu->granule = -1; + } else if (mode) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid value for attribute '%1$s' in element '%2$s': '%3$s'."), + "mode", "granule", mode); + return NULL; + } + } + } } if (virDomainDeviceInfoParseXML(xmlopt, node, ctxt, @@ -16585,7 +16637,8 @@ virDomainIOMMUDefEquals(const virDomainIOMMUDef *a, a->eim != b->eim || a->iotlb != b->iotlb || a->aw_bits != b->aw_bits || - a->dma_translation != b->dma_translation) + a->dma_translation != b->dma_translation || + a->granule != b->granule) return false; if (a->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && @@ -22340,6 +22393,16 @@ virDomainIOMMUDefCheckABIStability(virDomainIOMMUDef *src, virTristateSwitchTypeToString(src->xtsup)); return false; } + if (src->granule != dst->granule) { + g_autofree char *src_granule = virDomainIOMMUGranuleModeTypeToString(src->granule); + g_autofree char *dst_granule = virDomainIOMMUGranuleModeTypeToString(dst->granule); + + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target domain IOMMU device granule '%1$s' does not match source '%2$s'"), + dst_granule, + src_granule); + return false; + } return virDomainDeviceInfoCheckABIStability(&src->info, &dst->info); } @@ -28628,6 +28691,7 @@ virDomainIOMMUDefFormat(virBuffer *buf, g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf); g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) driverAttrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) driverChildBuf = VIR_BUFFER_INIT_CHILD(&childBuf); if (iommu->intremap != VIR_TRISTATE_SWITCH_ABSENT) { virBufferAsprintf(&driverAttrBuf, " intremap='%s'", @@ -28665,8 +28729,17 @@ virDomainIOMMUDefFormat(virBuffer *buf, virBufferAsprintf(&driverAttrBuf, " pciBus='%d'", iommu->pci_bus); } + if (iommu->granule != 0) { + if (iommu->granule == -1) { + virBufferAddLit(&driverChildBuf, "<granule mode='host'/>\n"); + } else { + virBufferAsprintf(&driverChildBuf, + "<granule size='%d' unit='KiB'/>\n", + iommu->granule); + } + } - virXMLFormatElement(&childBuf, "driver", &driverAttrBuf, NULL); + virXMLFormatElement(&childBuf, "driver", &driverAttrBuf, &driverChildBuf); virDomainDeviceInfoFormat(&childBuf, &iommu->info, 0); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 83d49969d3..72badb32a6 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3062,6 +3062,7 @@ struct _virDomainIOMMUDef { virTristateSwitch dma_translation; virTristateSwitch xtsup; virTristateSwitch pt; + int granule; /* -1 means 'host', 0 unset, page size in KiB otherwise */ }; typedef enum { diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index c83fff132b..1ad614935f 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -3194,7 +3194,8 @@ virDomainIOMMUDefValidate(const virDomainIOMMUDef *iommu) iommu->aw_bits != 0 || iommu->dma_translation != VIR_TRISTATE_SWITCH_ABSENT || iommu->xtsup != VIR_TRISTATE_SWITCH_ABSENT || - iommu->pt != VIR_TRISTATE_SWITCH_ABSENT) { + iommu->pt != VIR_TRISTATE_SWITCH_ABSENT || + iommu->granule != 0) { virReportError(VIR_ERR_XML_ERROR, _("iommu model '%1$s' doesn't support some additional attributes"), virDomainIOMMUModelTypeToString(iommu->model)); @@ -3229,7 +3230,8 @@ virDomainIOMMUDefValidate(const virDomainIOMMUDef *iommu) iommu->eim != VIR_TRISTATE_SWITCH_ABSENT || iommu->aw_bits != 0 || iommu->dma_translation != VIR_TRISTATE_SWITCH_ABSENT || - iommu->pci_bus >= 0) { + iommu->pci_bus >= 0 || + iommu->granule != 0) { virReportError(VIR_ERR_XML_ERROR, _("iommu model '%1$s' doesn't support some additional attributes"), virDomainIOMMUModelTypeToString(iommu->model)); @@ -3240,7 +3242,8 @@ virDomainIOMMUDefValidate(const virDomainIOMMUDef *iommu) case VIR_DOMAIN_IOMMU_MODEL_INTEL: if (iommu->pt != VIR_TRISTATE_SWITCH_ABSENT || iommu->xtsup != VIR_TRISTATE_SWITCH_ABSENT || - iommu->pci_bus >= 0) { + iommu->pci_bus >= 0 || + iommu->granule != 0) { virReportError(VIR_ERR_XML_ERROR, _("iommu model '%1$s' doesn't support some additional attributes"), virDomainIOMMUModelTypeToString(iommu->model)); diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 114dd3f96f..3d5eb7d4f3 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -6329,6 +6329,28 @@ <data type="unsignedInt"/> </attribute> </optional> + <optional> + <element name="granule"> + <choice> + <group> + <attribute name="mode"> + <value>host</value> + </attribute> + </group> + <group> + <attribute name="size"> + <ref name="unsignedInt"/> + </attribute> + <optional> + <attribute name="unit"> + <ref name="unit"/> + </attribute> + </optional> + </group> + </choice> + <empty/> + </element> + </optional> </element> </optional> <optional> diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index ab8a1938c1..8f85334cf9 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -5685,6 +5685,17 @@ qemuValidateDomainDeviceDefIOMMU(const virDomainIOMMUDef *iommu, return -1; } + /* QEMU supports only 4KiB, 8KiB, 16KiB and 64KiB granule size */ + if (iommu->granule > 0 && + !(iommu->granule == 4 || + iommu->granule == 8 || + iommu->granule == 16 || + iommu->granule == 64)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("iommu: unsupported granule size. Supported values are 4, 8, 16 and 64 KiB")); + return -1; + } + return 0; } diff --git a/tests/qemuxmlconfdata/virtio-iommu-aarch64.aarch64-latest.xml b/tests/qemuxmlconfdata/virtio-iommu-aarch64.aarch64-latest.xml index 4ae628ab5a..ad2fedaab7 100644 --- a/tests/qemuxmlconfdata/virtio-iommu-aarch64.aarch64-latest.xml +++ b/tests/qemuxmlconfdata/virtio-iommu-aarch64.aarch64-latest.xml @@ -29,7 +29,9 @@ <audio id='1' type='none'/> <memballoon model='none'/> <iommu model='virtio'> - <driver aw_bits='48'/> + <driver aw_bits='48'> + <granule size='64' unit='KiB'/> + </driver> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/> </iommu> </devices> diff --git a/tests/qemuxmlconfdata/virtio-iommu-aarch64.xml b/tests/qemuxmlconfdata/virtio-iommu-aarch64.xml index 96e5ea05ae..8d5f081b92 100644 --- a/tests/qemuxmlconfdata/virtio-iommu-aarch64.xml +++ b/tests/qemuxmlconfdata/virtio-iommu-aarch64.xml @@ -14,7 +14,9 @@ <controller type='usb' model='none'/> <memballoon model='none'/> <iommu model='virtio'> - <driver aw_bits='48'/> + <driver aw_bits='48'> + <granule size='64' unit='KiB'/> + </driver> </iommu> </devices> </domain> diff --git a/tests/qemuxmlconfdata/virtio-iommu-x86_64.x86_64-latest.xml b/tests/qemuxmlconfdata/virtio-iommu-x86_64.x86_64-latest.xml index f458f9a706..56cee393d3 100644 --- a/tests/qemuxmlconfdata/virtio-iommu-x86_64.x86_64-latest.xml +++ b/tests/qemuxmlconfdata/virtio-iommu-x86_64.x86_64-latest.xml @@ -31,6 +31,9 @@ <watchdog model='itco' action='reset'/> <memballoon model='none'/> <iommu model='virtio'> + <driver> + <granule mode='host'/> + </driver> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/> </iommu> </devices> diff --git a/tests/qemuxmlconfdata/virtio-iommu-x86_64.xml b/tests/qemuxmlconfdata/virtio-iommu-x86_64.xml index 51c13d2ef6..db0af57f69 100644 --- a/tests/qemuxmlconfdata/virtio-iommu-x86_64.xml +++ b/tests/qemuxmlconfdata/virtio-iommu-x86_64.xml @@ -13,6 +13,10 @@ <emulator>/usr/bin/qemu-system-x86_64</emulator> <controller type='usb' model='none'/> <memballoon model='none'/> - <iommu model='virtio'/> + <iommu model='virtio'> + <driver> + <granule mode='host'/> + </driver> + </iommu> </devices> </domain> -- 2.52.0
