From: Michal Privoznik Date: Wed, 21 Jan 2026 11:51:20 +0000 (+0100) Subject: conf: Introduce granule attribute for virtio-iommu X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=43892d991505ed992b245485c5ae9defb670d0c9;p=thirdparty%2Flibvirt.git conf: Introduce granule attribute for virtio-iommu 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 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 Reviewed-by: Peter Krempa --- diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 7dd99e040f..87644ad42a 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -9204,7 +9204,7 @@ IOMMU devices The ``iommu`` element can be used to add an IOMMU device. :since:`Since 2.1.0` -Example: +Examples: :: @@ -9216,6 +9216,17 @@ Example: ... + + ... + + + + + + + + ... + ``model`` Supported values are ``intel`` (for Q35 guests) ``smmuv3`` (:since:`since 5.5.0`, for ARM virt guests), ``virtio`` @@ -9274,6 +9285,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: + +:: + + + + + + + + + + + + + +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 7e4de6d1f9..e88dc62520 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1593,6 +1593,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); @@ -14497,6 +14512,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; @@ -14532,6 +14549,39 @@ 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 (VIR_ASSIGN_IS_OVERFLOW(iommu->granule, size)) { + virReportError(VIR_ERR_OVERFLOW, "%s", _("size value too large")); + return NULL; + } + } 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, @@ -16595,7 +16645,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 && @@ -22352,6 +22403,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); } @@ -28640,6 +28701,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'", @@ -28677,8 +28739,17 @@ virDomainIOMMUDefFormat(virBuffer *buf, virBufferAsprintf(&driverAttrBuf, " pciBus='%d'", iommu->pci_bus); } + if (iommu->granule != 0) { + if (iommu->granule == -1) { + virBufferAddLit(&driverChildBuf, "\n"); + } else { + virBufferAsprintf(&driverChildBuf, + "\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 66dc4e3417..1e16310ef9 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3065,6 +3065,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 8669d8f791..e659900687 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -6329,6 +6329,28 @@ + + + + + + host + + + + + + + + + + + + + + + + diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index d0572bcbb2..94a34b6efa 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -5686,6 +5686,18 @@ qemuValidateDomainDeviceDefIOMMU(const virDomainIOMMUDef *iommu, return -1; } + if (iommu->granule > 0) { + /* QEMU supports only 4KiB, 8KiB, 16KiB and 64KiB granule size */ + if (!(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 @@