]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
conf: Introduce granule attribute for virtio-iommu
authorMichal Privoznik <mprivozn@redhat.com>
Wed, 21 Jan 2026 11:51:20 +0000 (12:51 +0100)
committerMichal Privoznik <mprivozn@redhat.com>
Fri, 6 Feb 2026 15:13:37 +0000 (16:13 +0100)
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 <mprivozn@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
docs/formatdomain.rst
src/conf/domain_conf.c
src/conf/domain_conf.h
src/conf/domain_validate.c
src/conf/schemas/domaincommon.rng
src/qemu/qemu_validate.c
tests/qemuxmlconfdata/virtio-iommu-aarch64.aarch64-latest.xml
tests/qemuxmlconfdata/virtio-iommu-aarch64.xml
tests/qemuxmlconfdata/virtio-iommu-x86_64.x86_64-latest.xml
tests/qemuxmlconfdata/virtio-iommu-x86_64.xml

index 7dd99e040f9d35f4316bd38f7831c7985831a66f..87644ad42ae39bdfc731c19e6dcc64454d6a6169 100644 (file)
@@ -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:
    </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``
@@ -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:
+
+::
+
+  <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``).
 
index 7e4de6d1f9b9f666a18cf0bd80d66552cdb94a94..e88dc62520e56535a636d0e0dca2eb064ca6e898 100644 (file)
@@ -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, "<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);
 
index 66dc4e3417b8cb5bce60217a4e529add61149962..1e16310ef9ed3b8c768e709bdb7f264dc2cec534 100644 (file)
@@ -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 {
index c83fff132b81f46da55f1c4989f58b627811d3de..1ad614935fb2c589ad128720070475b55f7b89f5 100644 (file)
@@ -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));
index 8669d8f791bd5d8e75f1e493d7a1409764b6b16a..e6599006878d8f4aae9ce89ae24d2cecec43fa95 100644 (file)
                 <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>
index d0572bcbb2cca63f0c51247bb255eda8418a3a67..94a34b6efabb7658885f821e92e4f49b97f1e043 100644 (file)
@@ -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;
 }
 
index 4ae628ab5a0a7cd6a7369f1963fef53c6e007fd6..ad2fedaab71dacaa67e84fcec64ae898bed361eb 100644 (file)
@@ -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>
index 96e5ea05ae4a988c3d950cab5bdbd8355880e6da..8d5f081b922e22d17633219dff6582300e7ace9b 100644 (file)
@@ -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>
index f458f9a706d41f23a8dd3560de8c9b09bd68ee53..56cee393d3d41c907b9d706f7095edf09fa52e0c 100644 (file)
@@ -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>
index 51c13d2ef6c6dafba7bfae28111e951a14b2afba..db0af57f69744f54c643865e101fd213c97e0c50 100644 (file)
     <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>