Add speed/width target attributes for the generic PCIe root and switch
ports to set the link speed/width capability.

This is useful when used with passthrough devices to make the in-guest
apparent bandwidth match the hardware bandwidth. This allows in-guest
software (such as NCCL) to make better decisions about whether to use
P2P transfers etc.

Signed-off-by: David Vrabel <[email protected]>
---
 docs/formatdomain.rst                         | 25 ++++++-
 src/conf/domain_conf.c                        | 40 +++++++++++
 src/conf/domain_conf.h                        | 30 ++++++++
 src/conf/schemas/domaincommon.rng             | 24 +++++++
 src/libvirt_private.syms                      |  4 ++
 src/qemu/qemu_command.c                       | 32 +++++++++
 src/qemu/qemu_validate.c                      | 69 +++++++++++++++++++
 .../pci-bridge-speed.x86_64-latest.err        |  1 +
 tests/qemuxmlconfdata/pci-bridge-speed.xml    | 18 +++++
 ...-root-port-speed-ioh3420.x86_64-latest.err |  1 +
 .../pcie-root-port-speed-ioh3420.xml          | 19 +++++
 ...ie-root-port-speed-width.x86_64-11.0.0.err |  1 +
 ...e-root-port-speed-width.x86_64-latest.args | 37 ++++++++++
 ...ie-root-port-speed-width.x86_64-latest.xml | 48 +++++++++++++
 .../pcie-root-port-speed-width.xml            | 23 +++++++
 ...ream-port-model-generic.x86_64-latest.args |  2 +-
 ...tream-port-model-generic.x86_64-latest.xml |  2 +-
 ...e-switch-downstream-port-model-generic.xml |  2 +-
 ...tream-port-width-xio3130.x86_64-latest.err |  1 +
 ...e-switch-downstream-port-width-xio3130.xml | 23 +++++++
 ...ream-port-model-generic.x86_64-latest.args |  2 +-
 ...tream-port-model-generic.x86_64-latest.xml |  1 +
 ...cie-switch-upstream-port-model-generic.xml |  1 +
 tests/qemuxmlconftest.c                       |  5 ++
 24 files changed, 405 insertions(+), 6 deletions(-)
 create mode 100644 tests/qemuxmlconfdata/pci-bridge-speed.x86_64-latest.err
 create mode 100644 tests/qemuxmlconfdata/pci-bridge-speed.xml
 create mode 100644 
tests/qemuxmlconfdata/pcie-root-port-speed-ioh3420.x86_64-latest.err
 create mode 100644 tests/qemuxmlconfdata/pcie-root-port-speed-ioh3420.xml
 create mode 100644 
tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-11.0.0.err
 create mode 100644 
tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-latest.args
 create mode 100644 
tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-latest.xml
 create mode 100644 tests/qemuxmlconfdata/pcie-root-port-speed-width.xml
 create mode 100644 
tests/qemuxmlconfdata/pcie-switch-downstream-port-width-xio3130.x86_64-latest.err
 create mode 100644 
tests/qemuxmlconfdata/pcie-switch-downstream-port-width-xio3130.xml

diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index 2160cb4802..77cd6b504b 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -4633,6 +4633,26 @@ generated by libvirt. :since:`Since 1.2.19 (QEMU only)`.
    reserve more memory (in KiB) so that such PCI device can be hot plugged.
    For cold plugged PCI devices, the firmware will automatically reserve the
    correct amount of memory. :since:`Since 10.3.0`
+``speed``
+   pcie-root-port, pcie-switch-upstream-port, and
+   pcie-switch-downstream-port controllers can also have a ``speed`` attribute
+   in the ``<target>`` subelement, which controls the PCIe link speed 
capability
+   visible to the guest (in GT/s). Supported values are 2.5, 5, 8, 16, 32,
+   and 64. This is only supported by the ``pcie-root-port``,
+   ``pcie-upstream-port``, and ``pcie-downstream-port`` models (see
+   ``<model>`` above). If omitted, the hypervisor default is used, which for
+   QEMU depends on its version and the machine type.
+   :since:`Since 12.10.0 (QEMU only)`
+``width``
+   pcie-root-port, pcie-switch-upstream-port, and
+   pcie-switch-downstream-port controllers can also have a ``width`` attribute
+   in the ``<target>`` subelement, which controls the PCIe link width (number 
of
+   lanes) capability visible to the guest. Supported values are 1, 2, 4, 8, 16,
+   and 32. This is only supported by the ``pcie-root-port``,
+   ``pcie-upstream-port``, and ``pcie-downstream-port`` models (see
+   ``<model>`` above). If omitted, the hypervisor default is used, which for
+   QEMU depends on its version and the machine type.
+   :since:`Since 12.10.0 (QEMU only)`
 
 For machine types which provide an implicit PCI bus, the pci-root controller
 with index=0 is auto-added and required to use PCI devices. pci-root has no
@@ -4699,8 +4719,9 @@ By default, QEMU's generic ``pcie-upstream-port`` and 
``pcie-downstream-port``
 devices are used for pcie-switch-upstream-port and pcie-switch-downstream-port
 controllers if the QEMU binary supports them; otherwise the ``x3130-upstream``
 and ``xio3130-downstream`` devices (which emulate a TI XIO3130 switch limited
-to a 2.5 GT/s x1 link) are used. Existing domains keep the model recorded in
-their configuration. :since:`Since 12.10.0`
+to a 2.5 GT/s x1 link) are used. The generic devices also support the
+``speed`` and ``width`` target attributes. Existing domains keep the model
+recorded in their configuration. :since:`Since 12.10.0`
 
 ::
 
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 14cdc43b37..f3179926c9 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -458,6 +458,28 @@ VIR_ENUM_IMPL(virDomainControllerPCIModelName,
               "pcie-pci-bridge",
 );
 
+VIR_ENUM_IMPL(virDomainControllerPCILinkSpeed,
+              VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_LAST,
+              "default",
+              "2.5",
+              "5",
+              "8",
+              "16",
+              "32",
+              "64",
+);
+
+VIR_ENUM_IMPL(virDomainControllerPCILinkWidth,
+              VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_LAST,
+              "default",
+              "1",
+              "2",
+              "4",
+              "8",
+              "16",
+              "32",
+);
+
 VIR_ENUM_IMPL(virDomainControllerModelSCSI,
               VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LAST,
               "auto",
@@ -9233,6 +9255,18 @@ virDomainControllerDefParseXML(virDomainXMLOption 
*xmlopt,
                               def->opts.pciopts.port) < 0)
                 return NULL;
 
+            if (virXMLPropEnum(targetNodes[0], "speed",
+                               virDomainControllerPCILinkSpeedTypeFromString,
+                               VIR_XML_PROP_NONZERO,
+                               &def->opts.pciopts.speed) < 0)
+                return NULL;
+
+            if (virXMLPropEnum(targetNodes[0], "width",
+                               virDomainControllerPCILinkWidthTypeFromString,
+                               VIR_XML_PROP_NONZERO,
+                               &def->opts.pciopts.width) < 0)
+                return NULL;
+
             if (virXMLPropInt(targetNodes[0], "busNr", 0, 
VIR_XML_PROP_NONNEGATIVE,
                               &def->opts.pciopts.busNr,
                               def->opts.pciopts.busNr) < 0)
@@ -25019,6 +25053,12 @@ virDomainControllerDefFormatPCI(virBuffer *buf,
         virBufferAsprintf(&targetAttrBuf, " chassis='%d'", 
def->opts.pciopts.chassis);
     if (def->opts.pciopts.port != -1)
         virBufferAsprintf(&targetAttrBuf, " port='0x%x'", 
def->opts.pciopts.port);
+    if (def->opts.pciopts.speed != 
VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_DEFAULT)
+        virBufferAsprintf(&targetAttrBuf, " speed='%s'",
+                          
virDomainControllerPCILinkSpeedTypeToString(def->opts.pciopts.speed));
+    if (def->opts.pciopts.width != 
VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_DEFAULT)
+        virBufferAsprintf(&targetAttrBuf, " width='%s'",
+                          
virDomainControllerPCILinkWidthTypeToString(def->opts.pciopts.width));
     if (def->opts.pciopts.busNr != -1)
         virBufferAsprintf(&targetAttrBuf, " busNr='%d'", 
def->opts.pciopts.busNr);
     if (def->opts.pciopts.targetIndex != -1)
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index c3aa558923..fe56a36155 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -670,6 +670,30 @@ typedef enum {
     VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_LAST
 } virDomainControllerPCIModelName;
 
+typedef enum {
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_DEFAULT = 0,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_2_5,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_5,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_8,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_16,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_32,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_64,
+
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_LAST
+} virDomainControllerPCILinkSpeed;
+
+typedef enum {
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_DEFAULT = 0,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_1,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_2,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_4,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_8,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_16,
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_32,
+
+    VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_LAST
+} virDomainControllerPCILinkWidth;
+
 typedef enum {
     VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DEFAULT = -1,
     VIR_DOMAIN_CONTROLLER_MODEL_SCSI_AUTO,
@@ -767,6 +791,10 @@ struct _virDomainPCIControllerOpts {
      * pcie-root-port/pcie-switch-downstream-port, -1 = unspecified */
     int chassis;
     int port;
+    /* speed & width used by pcie-root-port/pcie-switch-upstream-port/
+     * pcie-switch-downstream-port */
+    virDomainControllerPCILinkSpeed speed;
+    virDomainControllerPCILinkWidth width;
     int busNr; /* used by pci-expander-bus, -1 == unspecified */
     int targetIndex; /* used by spapr-pci-host-bridge, -1 == unspecified */
     /* numaNode is a *subelement* of target (to match existing
@@ -4434,6 +4462,8 @@ VIR_ENUM_DECL(virDomainDiskMirrorState);
 VIR_ENUM_DECL(virDomainController);
 VIR_ENUM_DECL(virDomainControllerModelPCI);
 VIR_ENUM_DECL(virDomainControllerPCIModelName);
+VIR_ENUM_DECL(virDomainControllerPCILinkSpeed);
+VIR_ENUM_DECL(virDomainControllerPCILinkWidth);
 VIR_ENUM_DECL(virDomainControllerModelSCSI);
 VIR_ENUM_DECL(virDomainControllerModelUSB);
 VIR_ENUM_DECL(virDomainControllerModelIDE);
diff --git a/src/conf/schemas/domaincommon.rng 
b/src/conf/schemas/domaincommon.rng
index c1dee5470e..4f02b9fabb 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -3113,6 +3113,30 @@
                       <ref name="uint8"/>
                     </attribute>
                   </optional>
+                  <optional>
+                    <attribute name="speed">
+                      <choice>
+                        <value>2.5</value>
+                        <value>5</value>
+                        <value>8</value>
+                        <value>16</value>
+                        <value>32</value>
+                        <value>64</value>
+                      </choice>
+                    </attribute>
+                  </optional>
+                  <optional>
+                    <attribute name="width">
+                      <choice>
+                        <value>1</value>
+                        <value>2</value>
+                        <value>4</value>
+                        <value>8</value>
+                        <value>16</value>
+                        <value>32</value>
+                      </choice>
+                    </attribute>
+                  </optional>
                   <optional>
                     <attribute name="busNr">
                       <ref name="uint8"/>
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 798d2d28c8..47ba4acf95 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -302,6 +302,10 @@ virDomainControllerModelSCSITypeFromString;
 virDomainControllerModelSCSITypeToString;
 virDomainControllerModelUSBTypeFromString;
 virDomainControllerModelUSBTypeToString;
+virDomainControllerPCILinkSpeedTypeFromString;
+virDomainControllerPCILinkSpeedTypeToString;
+virDomainControllerPCILinkWidthTypeFromString;
+virDomainControllerPCILinkWidthTypeToString;
 virDomainControllerPCIModelNameTypeFromString;
 virDomainControllerPCIModelNameTypeToString;
 virDomainControllerRemove;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 9adb18be4f..a27425d8da 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -182,6 +182,19 @@ qemuOnOffAuto(virTristateSwitch s)
 }
 
 
+VIR_ENUM_DECL(qemuControllerPCILinkSpeed);
+VIR_ENUM_IMPL(qemuControllerPCILinkSpeed,
+              VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_LAST,
+              "", /* default value, we shouldn't see this */
+              "2_5",
+              "5",
+              "8",
+              "16",
+              "32",
+              "64",
+);
+
+
 static int
 qemuBuildObjectCommandlineFromJSON(virCommand *cmd,
                                    virJSONValue *props)
@@ -2790,6 +2803,25 @@ qemuBuildControllerPCIDevProps(virDomainControllerDef 
*def,
         return -1;
     }
 
+    /* Options common to all PCIe ports. */
+    if (def->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT ||
+        def->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT ||
+        def->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT) 
{
+        if (pciopts->speed != VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_DEFAULT) {
+            const char *speedstr = 
qemuControllerPCILinkSpeedTypeToString(pciopts->speed);
+
+            if (virJSONValueObjectAdd(&props, "s:speed", speedstr, NULL) < 0)
+                return -1;
+        }
+
+        if (pciopts->width != VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_DEFAULT) {
+            const char *widthstr = 
virDomainControllerPCILinkWidthTypeToString(pciopts->width);
+
+            if (virJSONValueObjectAdd(&props, "s:width", widthstr, NULL) < 0)
+                return -1;
+        }
+    }
+
     *devprops = g_steal_pointer(&props);
     return 0;
 }
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c
index ffd05fe765..04f33df7eb 100644
--- a/src/qemu/qemu_validate.c
+++ b/src/qemu/qemu_validate.c
@@ -4275,6 +4275,15 @@ virValidateControllerPCIModelNameToQEMUCaps(int 
modelName)
 }
 
 
+static bool
+qemuValidateControllerPCIModelNameHasLinkProps(virDomainControllerPCIModelName 
modelName)
+{
+    return modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCIE_ROOT_PORT ||
+           modelName == 
VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCIE_UPSTREAM_PORT ||
+           modelName == 
VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCIE_DOWNSTREAM_PORT;
+}
+
+
 static int
 qemuValidateDomainDeviceDefControllerAttributes(const virDomainControllerDef 
*controller)
 {
@@ -4765,6 +4774,66 @@ qemuValidateDomainDeviceDefControllerPCI(const 
virDomainControllerDef *cont,
         return -1;
     }
 
+    /* speed and width */
+    switch ((virDomainControllerModelPCI) cont->model) {
+    case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
+    case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
+    case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
+        if (pciopts->speed != VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_DEFAULT) {
+            if 
(!qemuValidateControllerPCIModelNameHasLinkProps(pciopts->modelName)) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("setting the 'speed' property on a '%1$s' 
device is not supported"),
+                               modelName);
+                return -1;
+            }
+            if (pciopts->modelName == 
VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCIE_ROOT_PORT &&
+                !virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIE_ROOT_PORT_SPEED)) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("setting the 'speed' property on a '%1$s' 
device is not supported by this QEMU binary"),
+                               modelName);
+                return -1;
+            }
+        }
+
+        if (pciopts->width != VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_DEFAULT) {
+            if 
(!qemuValidateControllerPCIModelNameHasLinkProps(pciopts->modelName)) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("setting the 'width' property on a '%1$s' 
device is not supported"),
+                               modelName);
+                return -1;
+            }
+            if (pciopts->modelName == 
VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCIE_ROOT_PORT &&
+                !virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIE_ROOT_PORT_WIDTH)) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("setting the 'width' property on a '%1$s' 
device is not supported by this QEMU binary"),
+                               modelName);
+                return -1;
+            }
+        }
+        break;
+    case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
+    case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
+    case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
+    case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
+    case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
+    case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
+    case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE:
+        if (pciopts->speed != VIR_DOMAIN_CONTROLLER_PCI_LINK_SPEED_DEFAULT) {
+            virReportControllerInvalidOption(cont, model, modelName, "speed");
+            return -1;
+        }
+        if (pciopts->width != VIR_DOMAIN_CONTROLLER_PCI_LINK_WIDTH_DEFAULT) {
+            virReportControllerInvalidOption(cont, model, modelName, "width");
+            return -1;
+        }
+        break;
+    case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT:
+    case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
+    default:
+        virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
+        return -1;
+    }
+
     /* hotplug */
     if (pciopts->hotplug != VIR_TRISTATE_SWITCH_ABSENT) {
         switch ((virDomainControllerModelPCI) cont->model) {
diff --git a/tests/qemuxmlconfdata/pci-bridge-speed.x86_64-latest.err 
b/tests/qemuxmlconfdata/pci-bridge-speed.x86_64-latest.err
new file mode 100644
index 0000000000..01742d8110
--- /dev/null
+++ b/tests/qemuxmlconfdata/pci-bridge-speed.x86_64-latest.err
@@ -0,0 +1 @@
+unsupported configuration: Option 'speed' is not valid for PCI controller with 
index '1', model 'pci-bridge' and modelName 'pci-bridge'
diff --git a/tests/qemuxmlconfdata/pci-bridge-speed.xml 
b/tests/qemuxmlconfdata/pci-bridge-speed.xml
new file mode 100644
index 0000000000..336c630f58
--- /dev/null
+++ b/tests/qemuxmlconfdata/pci-bridge-speed.xml
@@ -0,0 +1,18 @@
+<domain type='qemu'>
+  <name>guest</name>
+  <uuid>11dbdcdd-4c3b-482b-8903-9bdb8c0a2774</uuid>
+  <memory unit='KiB'>2097152</memory>
+  <currentMemory unit='KiB'>2097152</currentMemory>
+  <vcpu placement='static'>2</vcpu>
+  <os>
+    <type arch='x86_64' machine='pc'>hvm</type>
+  </os>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <controller type='pci' index='0' model='pci-root'/>
+    <controller type='pci' index='1' model='pci-bridge'>
+      <target speed='16'/>
+    </controller>
+    <memballoon model='none'/>
+  </devices>
+</domain>
diff --git 
a/tests/qemuxmlconfdata/pcie-root-port-speed-ioh3420.x86_64-latest.err 
b/tests/qemuxmlconfdata/pcie-root-port-speed-ioh3420.x86_64-latest.err
new file mode 100644
index 0000000000..55ae4423fa
--- /dev/null
+++ b/tests/qemuxmlconfdata/pcie-root-port-speed-ioh3420.x86_64-latest.err
@@ -0,0 +1 @@
+unsupported configuration: setting the 'speed' property on a 'ioh3420' device 
is not supported
diff --git a/tests/qemuxmlconfdata/pcie-root-port-speed-ioh3420.xml 
b/tests/qemuxmlconfdata/pcie-root-port-speed-ioh3420.xml
new file mode 100644
index 0000000000..d06faee66d
--- /dev/null
+++ b/tests/qemuxmlconfdata/pcie-root-port-speed-ioh3420.xml
@@ -0,0 +1,19 @@
+<domain type='qemu'>
+  <name>guest</name>
+  <uuid>11dbdcdd-4c3b-482b-8903-9bdb8c0a2774</uuid>
+  <memory unit='KiB'>2097152</memory>
+  <currentMemory unit='KiB'>2097152</currentMemory>
+  <vcpu placement='static'>2</vcpu>
+  <os>
+    <type arch='x86_64' machine='q35'>hvm</type>
+  </os>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <controller type='pci' index='0' model='pcie-root'/>
+    <controller type='pci' index='1' model='pcie-root-port'>
+      <model name='ioh3420'/>
+      <target speed='16'/>
+    </controller>
+    <memballoon model='none'/>
+  </devices>
+</domain>
diff --git a/tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-11.0.0.err 
b/tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-11.0.0.err
new file mode 100644
index 0000000000..05b799a9e9
--- /dev/null
+++ b/tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-11.0.0.err
@@ -0,0 +1 @@
+unsupported configuration: setting the 'speed' property on a 'pcie-root-port' 
device is not supported by this QEMU binary
diff --git 
a/tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-latest.args 
b/tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-latest.args
new file mode 100644
index 0000000000..a31e0903bc
--- /dev/null
+++ b/tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-latest.args
@@ -0,0 +1,37 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/var/lib/libvirt/qemu/domain--1-guest \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-guest/.local/share \
+XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-guest/.cache \
+XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-guest/.config \
+/usr/bin/qemu-system-x86_64 \
+-name guest=guest,debug-threads=on \
+-S \
+-object 
'{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-guest/master-key.aes"}'
 \
+-machine q35,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \
+-accel tcg \
+-cpu qemu64 \
+-m size=2097152k \
+-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":2147483648}' \
+-overcommit mem-lock=off \
+-smp 2,sockets=2,cores=1,threads=1 \
+-uuid 11dbdcdd-4c3b-482b-8903-9bdb8c0a2774 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=@mon-fd@,server=on,wait=off \
+-object '{"qom-type":"monitor-qmp","id":"monitor","chardev":"charmonitor"}' \
+-rtc base=utc \
+-no-shutdown \
+-boot strict=on \
+-device 
'{"driver":"pcie-root-port","port":8,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x1"}'
 \
+-device 
'{"driver":"pcie-root-port","port":9,"chassis":2,"id":"pci.2","speed":"16","width":"8","bus":"pcie.0","addr":"0x1.0x1"}'
 \
+-device 
'{"driver":"ioh3420","port":10,"chassis":3,"id":"pci.3","bus":"pcie.0","addr":"0x1.0x2"}'
 \
+-device '{"driver":"qemu-xhci","id":"usb","bus":"pci.1","addr":"0x0"}' \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-global ICH9-LPC.noreboot=off \
+-watchdog-action reset \
+-sandbox 
on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
+-msg timestamp=on
diff --git a/tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-latest.xml 
b/tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-latest.xml
new file mode 100644
index 0000000000..cf6e0f936f
--- /dev/null
+++ b/tests/qemuxmlconfdata/pcie-root-port-speed-width.x86_64-latest.xml
@@ -0,0 +1,48 @@
+<domain type='qemu'>
+  <name>guest</name>
+  <uuid>11dbdcdd-4c3b-482b-8903-9bdb8c0a2774</uuid>
+  <memory unit='KiB'>2097152</memory>
+  <currentMemory unit='KiB'>2097152</currentMemory>
+  <vcpu placement='static'>2</vcpu>
+  <os>
+    <type arch='x86_64' machine='q35'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <cpu mode='custom' match='exact' check='none'>
+    <model fallback='forbid'>qemu64</model>
+  </cpu>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <controller type='pci' index='0' model='pcie-root'/>
+    <controller type='pci' index='1' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='1' port='0x8'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' 
function='0x0' multifunction='on'/>
+    </controller>
+    <controller type='pci' index='2' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='2' port='0x9' speed='16' width='8'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' 
function='0x1'/>
+    </controller>
+    <controller type='pci' index='3' model='pcie-root-port'>
+      <model name='ioh3420'/>
+      <target chassis='3' port='0xa'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' 
function='0x2'/>
+    </controller>
+    <controller type='usb' index='0' model='qemu-xhci'>
+      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' 
function='0x0'/>
+    </controller>
+    <controller type='sata' index='0'>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' 
function='0x2'/>
+    </controller>
+    <input type='mouse' bus='ps2'/>
+    <input type='keyboard' bus='ps2'/>
+    <audio id='1' type='none'/>
+    <watchdog model='itco' action='reset'/>
+    <memballoon model='none'/>
+  </devices>
+</domain>
diff --git a/tests/qemuxmlconfdata/pcie-root-port-speed-width.xml 
b/tests/qemuxmlconfdata/pcie-root-port-speed-width.xml
new file mode 100644
index 0000000000..5581730709
--- /dev/null
+++ b/tests/qemuxmlconfdata/pcie-root-port-speed-width.xml
@@ -0,0 +1,23 @@
+<domain type='qemu'>
+  <name>guest</name>
+  <uuid>11dbdcdd-4c3b-482b-8903-9bdb8c0a2774</uuid>
+  <memory unit='KiB'>2097152</memory>
+  <currentMemory unit='KiB'>2097152</currentMemory>
+  <vcpu placement='static'>2</vcpu>
+  <os>
+    <type arch='x86_64' machine='q35'>hvm</type>
+  </os>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <controller type='pci' index='0' model='pcie-root'/>
+    <controller type='pci' index='1' model='pcie-root-port'/>
+    <controller type='pci' index='2' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target speed='16' width='8'/>
+    </controller>
+    <controller type='pci' index='3' model='pcie-root-port'>
+      <model name='ioh3420'/>
+    </controller>
+    <memballoon model='none'/>
+  </devices>
+</domain>
diff --git 
a/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.x86_64-latest.args
 
b/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.x86_64-latest.args
index 024895a9f2..9892050405 100644
--- 
a/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.x86_64-latest.args
+++ 
b/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.x86_64-latest.args
@@ -31,7 +31,7 @@ 
XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-q35-test/.config \
 -device 
'{"driver":"pcie-downstream-port","port":0,"chassis":3,"id":"pci.3","bus":"pci.2","addr":"0x0"}'
 \
 -device 
'{"driver":"pcie-downstream-port","port":1,"chassis":4,"id":"pci.4","bus":"pci.2","addr":"0x1"}'
 \
 -device 
'{"driver":"pcie-downstream-port","port":2,"chassis":5,"id":"pci.5","bus":"pci.2","addr":"0x2"}'
 \
--device 
'{"driver":"pcie-downstream-port","port":39,"chassis":30,"id":"pci.6","bus":"pci.2","addr":"0x3"}'
 \
+-device 
'{"driver":"pcie-downstream-port","port":39,"chassis":30,"id":"pci.6","speed":"16","width":"8","bus":"pci.2","addr":"0x3"}'
 \
 -device 
'{"driver":"pcie-upstream-port","id":"pci.7","bus":"pci.3","addr":"0x0"}' \
 -device 
'{"driver":"pcie-downstream-port","port":4,"chassis":8,"id":"pci.8","bus":"pci.2","addr":"0x4"}'
 \
 -device 
'{"driver":"pcie-downstream-port","port":5,"chassis":9,"id":"pci.9","bus":"pci.2","addr":"0x5"}'
 \
diff --git 
a/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.x86_64-latest.xml
 
b/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.x86_64-latest.xml
index d4ca578b42..e810c82e65 100644
--- 
a/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.x86_64-latest.xml
+++ 
b/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.x86_64-latest.xml
@@ -50,7 +50,7 @@
     </controller>
     <controller type='pci' index='6' model='pcie-switch-downstream-port'>
       <model name='pcie-downstream-port'/>
-      <target chassis='30' port='0x27'/>
+      <target chassis='30' port='0x27' speed='16' width='8'/>
       <address type='pci' domain='0x0000' bus='0x02' slot='0x03' 
function='0x0'/>
     </controller>
     <controller type='pci' index='7' model='pcie-switch-upstream-port'>
diff --git 
a/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.xml 
b/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.xml
index 19d5e96233..d0cb8e9939 100644
--- a/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.xml
+++ b/tests/qemuxmlconfdata/pcie-switch-downstream-port-model-generic.xml
@@ -27,7 +27,7 @@
     <controller type='pci' index='5' model='pcie-switch-downstream-port'/>
     <controller type='pci' index='6' model='pcie-switch-downstream-port'>
       <model name='pcie-downstream-port'/>
-      <target chassis='30' port='0x27'/>
+      <target chassis='30' port='0x27' speed='16' width='8'/>
     </controller>
     <controller type='pci' index='7' model='pcie-switch-upstream-port'/>
     <controller type='pci' index='8' model='pcie-switch-downstream-port'/>
diff --git 
a/tests/qemuxmlconfdata/pcie-switch-downstream-port-width-xio3130.x86_64-latest.err
 
b/tests/qemuxmlconfdata/pcie-switch-downstream-port-width-xio3130.x86_64-latest.err
new file mode 100644
index 0000000000..38c97b77b6
--- /dev/null
+++ 
b/tests/qemuxmlconfdata/pcie-switch-downstream-port-width-xio3130.x86_64-latest.err
@@ -0,0 +1 @@
+unsupported configuration: setting the 'width' property on a 
'xio3130-downstream' device is not supported
diff --git 
a/tests/qemuxmlconfdata/pcie-switch-downstream-port-width-xio3130.xml 
b/tests/qemuxmlconfdata/pcie-switch-downstream-port-width-xio3130.xml
new file mode 100644
index 0000000000..d83bea51dc
--- /dev/null
+++ b/tests/qemuxmlconfdata/pcie-switch-downstream-port-width-xio3130.xml
@@ -0,0 +1,23 @@
+<domain type='qemu'>
+  <name>guest</name>
+  <uuid>11dbdcdd-4c3b-482b-8903-9bdb8c0a2774</uuid>
+  <memory unit='KiB'>2097152</memory>
+  <currentMemory unit='KiB'>2097152</currentMemory>
+  <vcpu placement='static'>2</vcpu>
+  <os>
+    <type arch='x86_64' machine='q35'>hvm</type>
+  </os>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <controller type='pci' index='0' model='pcie-root'/>
+    <controller type='pci' index='1' model='pcie-root-port'/>
+    <controller type='pci' index='2' model='pcie-switch-upstream-port'>
+      <model name='x3130-upstream'/>
+    </controller>
+    <controller type='pci' index='3' model='pcie-switch-downstream-port'>
+      <model name='xio3130-downstream'/>
+      <target width='8'/>
+    </controller>
+    <memballoon model='none'/>
+  </devices>
+</domain>
diff --git 
a/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.x86_64-latest.args
 
b/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.x86_64-latest.args
index 5ed1f048fe..d551ca070f 100644
--- 
a/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.x86_64-latest.args
+++ 
b/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.x86_64-latest.args
@@ -29,7 +29,7 @@ 
XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-q35-test/.config \
 -device 
'{"driver":"pcie-root-port","port":16,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x2"}'
 \
 -device 
'{"driver":"pcie-root-port","port":17,"chassis":2,"id":"pci.2","bus":"pcie.0","addr":"0x2.0x1"}'
 \
 -device 
'{"driver":"pcie-upstream-port","id":"pci.3","bus":"pci.1","addr":"0x0"}' \
--device 
'{"driver":"pcie-upstream-port","id":"pci.4","bus":"pci.2","addr":"0x0"}' \
+-device 
'{"driver":"pcie-upstream-port","id":"pci.4","speed":"16","width":"8","bus":"pci.2","addr":"0x0"}'
 \
 -device 
'{"driver":"pcie-root-port","port":18,"chassis":5,"id":"pci.5","bus":"pcie.0","addr":"0x2.0x2"}'
 \
 -device '{"driver":"qemu-xhci","id":"usb","bus":"pci.5","addr":"0x0"}' \
 -blockdev 
'{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest1","node-name":"libvirt-1-storage","read-only":false}'
 \
diff --git 
a/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.x86_64-latest.xml
 
b/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.x86_64-latest.xml
index bd276eccb3..07027c2f6d 100644
--- 
a/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.x86_64-latest.xml
+++ 
b/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.x86_64-latest.xml
@@ -40,6 +40,7 @@
     </controller>
     <controller type='pci' index='4' model='pcie-switch-upstream-port'>
       <model name='pcie-upstream-port'/>
+      <target speed='16' width='8'/>
       <address type='pci' domain='0x0000' bus='0x02' slot='0x00' 
function='0x0'/>
     </controller>
     <controller type='pci' index='5' model='pcie-root-port'>
diff --git a/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.xml 
b/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.xml
index d015b2631e..b46e895d41 100644
--- a/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.xml
+++ b/tests/qemuxmlconfdata/pcie-switch-upstream-port-model-generic.xml
@@ -25,6 +25,7 @@
     <controller type='pci' index='3' model='pcie-switch-upstream-port'/>
     <controller type='pci' index='4' model='pcie-switch-upstream-port'>
       <model name='pcie-upstream-port'/>
+      <target speed='16' width='8'/>
     </controller>
     <controller type='sata' index='0'/>
     <input type='mouse' bus='ps2'/>
diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c
index 51d6b94fe6..ca64f89d4c 100644
--- a/tests/qemuxmlconftest.c
+++ b/tests/qemuxmlconftest.c
@@ -2770,6 +2770,11 @@ mymain(void)
      * based on QEMU binary capabilities. We use x86/q35 for the test, but
      * any PCIe machine type (such as aarch64/virt) will behave the same */
     DO_TEST_CAPS_LATEST("pcie-root-port-model-generic");
+    DO_TEST_CAPS_LATEST("pcie-root-port-speed-width");
+    DO_TEST_CAPS_VER_PARSE_ERROR("pcie-root-port-speed-width", "11.0.0");
+    DO_TEST_CAPS_LATEST_PARSE_ERROR("pcie-root-port-speed-ioh3420");
+    
DO_TEST_CAPS_LATEST_PARSE_ERROR("pcie-switch-downstream-port-width-xio3130");
+    DO_TEST_CAPS_LATEST_PARSE_ERROR("pci-bridge-speed");
     DO_TEST_CAPS_LATEST("pcie-root-port-model-ioh3420");
     DO_TEST_CAPS_LATEST("pcie-root-port-nohotplug");
 
-- 
2.43.0

Reply via email to