6d3351
From a5f055c8eb5c761473088866022167d4a9442673 Mon Sep 17 00:00:00 2001
6d3351
Message-Id: <a5f055c8eb5c761473088866022167d4a9442673@dist-git>
6d3351
From: Andrea Bolognani <abologna@redhat.com>
6d3351
Date: Tue, 18 Jul 2017 12:10:07 +0200
6d3351
Subject: [PATCH] qemu: Isolate hostdevs on pSeries guests
6d3351
6d3351
All the pieces are now in place, so we can finally start
6d3351
using isolation groups to achieve our initial goal, which is
6d3351
separating hostdevs from emulated PCI devices while keeping
6d3351
hostdevs that belong to the same host IOMMU group together.
6d3351
6d3351
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1280542
6d3351
6d3351
Signed-off-by: Andrea Bolognani <abologna@redhat.com>
6d3351
Reviewed-by: Laine Stump <laine@laine.org>
6d3351
(cherry picked from commit b84b6ab5027c386d19299771387b4c4cf5e844cd)
6d3351
6d3351
Bug: https://bugzilla.redhat.com/show_bug.cgi?id=1280542
6d3351
6d3351
Signed-off-by: Andrea Bolognani <abologna@redhat.com>
6d3351
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
6d3351
---
6d3351
 src/qemu/qemu_domain_address.c                     | 241 +++++++++++++++++++++
6d3351
 src/qemu/qemu_domain_address.h                     |   4 +
6d3351
 src/qemu/qemu_hotplug.c                            |   7 +
6d3351
 tests/qemumemlocktest.c                            |   2 +-
6d3351
 .../qemuxml2argv-pseries-hostdevs-1.args           |   8 +-
6d3351
 .../qemuxml2argv-pseries-hostdevs-2.args           |   3 +-
6d3351
 .../qemuxml2argv-pseries-hostdevs-3.args           |   2 +-
6d3351
 .../qemuxml2xmlout-pseries-hostdevs-1.xml          |  14 +-
6d3351
 .../qemuxml2xmlout-pseries-hostdevs-2.xml          |   6 +-
6d3351
 .../qemuxml2xmlout-pseries-hostdevs-3.xml          |   2 +-
6d3351
 10 files changed, 278 insertions(+), 11 deletions(-)
6d3351
6d3351
diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c
6d3351
index 02e214b8dd..756cd97970 100644
6d3351
--- a/src/qemu/qemu_domain_address.c
6d3351
+++ b/src/qemu/qemu_domain_address.c
6d3351
@@ -25,6 +25,7 @@
6d3351
 
6d3351
 #include "qemu_domain_address.h"
6d3351
 #include "qemu_domain.h"
6d3351
+#include "network/bridge_driver.h"
6d3351
 #include "viralloc.h"
6d3351
 #include "virerror.h"
6d3351
 #include "virlog.h"
6d3351
@@ -901,6 +902,243 @@ qemuDomainFillAllPCIConnectFlags(virDomainDefPtr def,
6d3351
 
6d3351
 
6d3351
 /**
6d3351
+ * qemuDomainFindUnusedIsolationGroupIter:
6d3351
+ * @def: domain definition
6d3351
+ * @dev: device definition
6d3351
+ * @info: device information
6d3351
+ * @opaque: user data
6d3351
+ *
6d3351
+ * Used to implement qemuDomainFindUnusedIsolationGroup(). You probably
6d3351
+ * don't want to call this directly.
6d3351
+ *
6d3351
+ * Return: 0 if the isolation group is not used by the device, <1 otherwise.
6d3351
+ */
6d3351
+static int
6d3351
+qemuDomainFindUnusedIsolationGroupIter(virDomainDefPtr def ATTRIBUTE_UNUSED,
6d3351
+                                       virDomainDeviceDefPtr dev ATTRIBUTE_UNUSED,
6d3351
+                                       virDomainDeviceInfoPtr info,
6d3351
+                                       void *opaque)
6d3351
+{
6d3351
+    unsigned int *isolationGroup = opaque;
6d3351
+
6d3351
+    if (info->isolationGroup == *isolationGroup)
6d3351
+        return -1;
6d3351
+
6d3351
+    return 0;
6d3351
+}
6d3351
+
6d3351
+
6d3351
+/**
6d3351
+ * qemuDomainFindUnusedIsolationGroup:
6d3351
+ * @def: domain definition
6d3351
+ *
6d3351
+ * Find an isolation group that is not used by any device in @def yet.
6d3351
+ *
6d3351
+ * Normally, we'd look up the device's IOMMU group and base its isolation
6d3351
+ * group on that; however, when a network interface uses a network backed
6d3351
+ * by SR-IOV Virtual Functions, we can't know at PCI address assignment
6d3351
+ * time which host device will be used so we can't look up its IOMMU group.
6d3351
+ *
6d3351
+ * We still want such a device to be isolated: this function can be used
6d3351
+ * to obtain a synthetic isolation group usable for the purpose.
6d3351
+ *
6d3351
+ * Return: unused isolation group
6d3351
+ */
6d3351
+static unsigned int
6d3351
+qemuDomainFindUnusedIsolationGroup(virDomainDefPtr def)
6d3351
+{
6d3351
+    unsigned int isolationGroup = UINT_MAX;
6d3351
+
6d3351
+    /* We start from the highest possible isolation group and work our
6d3351
+     * way backwards so that we're working in a completely different range
6d3351
+     * from IOMMU groups, thus avoiding clashes. We're realistically going
6d3351
+     * to call this function just a few times per guest anyway */
6d3351
+    while (isolationGroup > 0 &&
6d3351
+           virDomainDeviceInfoIterate(def,
6d3351
+                                      qemuDomainFindUnusedIsolationGroupIter,
6d3351
+                                      &isolationGroup) < 0) {
6d3351
+        isolationGroup--;
6d3351
+    }
6d3351
+
6d3351
+    return isolationGroup;
6d3351
+}
6d3351
+
6d3351
+
6d3351
+/**
6d3351
+ * qemuDomainFillDeviceIsolationGroup:
6d3351
+ * @def: domain definition
6d3351
+ * @dev: device definition
6d3351
+ *
6d3351
+ * Fill isolation group information for a single device.
6d3351
+ *
6d3351
+ * Return: 0 on success, <0 on failure
6d3351
+ * */
6d3351
+int
6d3351
+qemuDomainFillDeviceIsolationGroup(virDomainDefPtr def,
6d3351
+                                   virDomainDeviceDefPtr dev)
6d3351
+{
6d3351
+    int ret = -1;
6d3351
+
6d3351
+    /* Only host devices need their isolation group to be different from
6d3351
+     * the default. Interfaces of type hostdev are just host devices in
6d3351
+     * disguise, but we don't need to handle them separately because for
6d3351
+     * each such interface a corresponding hostdev is also added to the
6d3351
+     * guest configuration */
6d3351
+    if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
6d3351
+        virDomainHostdevDefPtr hostdev = dev->data.hostdev;
6d3351
+        virDomainDeviceInfoPtr info = hostdev->info;
6d3351
+        virPCIDeviceAddressPtr hostAddr;
6d3351
+        int tmp;
6d3351
+
6d3351
+        /* Only PCI host devices are subject to isolation */
6d3351
+        if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
6d3351
+            hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
6d3351
+            goto skip;
6d3351
+        }
6d3351
+
6d3351
+        hostAddr = &hostdev->source.subsys.u.pci.addr;
6d3351
+
6d3351
+        /* If a non-default isolation has already been assigned to the
6d3351
+         * device, we can avoid looking up the information again */
6d3351
+        if (info->isolationGroup > 0)
6d3351
+            goto skip;
6d3351
+
6d3351
+        /* The isolation group depends on the IOMMU group assigned by the host */
6d3351
+        tmp = virPCIDeviceAddressGetIOMMUGroupNum(hostAddr);
6d3351
+
6d3351
+        if (tmp < 0) {
6d3351
+            VIR_WARN("Can't look up isolation group for host device "
6d3351
+                     "%04x:%02x:%02x.%x",
6d3351
+                     hostAddr->domain, hostAddr->bus,
6d3351
+                     hostAddr->slot, hostAddr->function);
6d3351
+            goto cleanup;
6d3351
+        }
6d3351
+
6d3351
+        /* The isolation group for a host device is its IOMMU group,
6d3351
+         * increased by one: this is because zero is a valid IOMMU group but
6d3351
+         * that's also the default isolation group, which we want to save
6d3351
+         * for emulated devices. Shifting isolation groups for host devices
6d3351
+         * by one ensures there is no overlap */
6d3351
+        info->isolationGroup = tmp + 1;
6d3351
+
6d3351
+        VIR_DEBUG("Isolation group for host device %04x:%02x:%02x.%x is %u",
6d3351
+                  hostAddr->domain, hostAddr->bus,
6d3351
+                  hostAddr->slot, hostAddr->function,
6d3351
+                  info->isolationGroup);
6d3351
+
6d3351
+    } else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
6d3351
+        virDomainNetDefPtr iface = dev->data.net;
6d3351
+        virDomainDeviceInfoPtr info = &iface->info;
6d3351
+        unsigned int tmp;
6d3351
+
6d3351
+        /* Network interfaces can ultimately result in the guest being
6d3351
+         * assigned a host device if the libvirt network they're connected
6d3351
+         * to is of type hostdev. All other kinds of network interfaces don't
6d3351
+         * require us to isolate the guest device, so we can skip them */
6d3351
+        if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK ||
6d3351
+            networkGetActualType(iface) != VIR_DOMAIN_NET_TYPE_HOSTDEV) {
6d3351
+            goto skip;
6d3351
+        }
6d3351
+
6d3351
+        /* If a non-default isolation has already been assigned to the
6d3351
+         * device, we can avoid looking up the information again */
6d3351
+        if (info->isolationGroup > 0)
6d3351
+            goto skip;
6d3351
+
6d3351
+        /* Obtain a synthetic isolation group for the device, since at this
6d3351
+         * point in time we don't have access to the IOMMU group of the host
6d3351
+         * device that will eventually be used by the guest */
6d3351
+        tmp = qemuDomainFindUnusedIsolationGroup(def);
6d3351
+
6d3351
+        if (tmp == 0) {
6d3351
+            VIR_WARN("Can't obtain usable isolation group for interface "
6d3351
+                     "configured to use hostdev-backed network '%s'",
6d3351
+                     iface->data.network.name);
6d3351
+            goto cleanup;
6d3351
+        }
6d3351
+
6d3351
+        info->isolationGroup = tmp;
6d3351
+
6d3351
+        VIR_DEBUG("Isolation group for interface configured to use "
6d3351
+                  "hostdev-backed network '%s' is %u",
6d3351
+                  iface->data.network.name, info->isolationGroup);
6d3351
+    }
6d3351
+
6d3351
+ skip:
6d3351
+    ret = 0;
6d3351
+
6d3351
+ cleanup:
6d3351
+    return ret;
6d3351
+}
6d3351
+
6d3351
+
6d3351
+/**
6d3351
+ * qemuDomainFillDeviceIsolationGroupIter:
6d3351
+ * @def: domain definition
6d3351
+ * @dev: device definition
6d3351
+ * @info: device information
6d3351
+ * @opaque: user data
6d3351
+ *
6d3351
+ * A version of qemuDomainFillDeviceIsolationGroup() to be used
6d3351
+ * with virDomainDeviceInfoIterate()
6d3351
+ *
6d3351
+ * Return: 0 on success, <0 on failure
6d3351
+ */
6d3351
+static int
6d3351
+qemuDomainFillDeviceIsolationGroupIter(virDomainDefPtr def,
6d3351
+                                       virDomainDeviceDefPtr dev,
6d3351
+                                       virDomainDeviceInfoPtr info ATTRIBUTE_UNUSED,
6d3351
+                                       void *opaque ATTRIBUTE_UNUSED)
6d3351
+{
6d3351
+    return qemuDomainFillDeviceIsolationGroup(def, dev);
6d3351
+}
6d3351
+
6d3351
+
6d3351
+/**
6d3351
+ * qemuDomainSetupIsolationGroups:
6d3351
+ * @def: domain definition
6d3351
+ *
6d3351
+ * High-level function to set up isolation groups for all devices
6d3351
+ * and controllers in @def. Isolation groups will only be set up if
6d3351
+ * the guest architecture and machine type require it, so this
6d3351
+ * function can and should be called unconditionally before attempting
6d3351
+ * to assign any PCI address.
6d3351
+ *
6d3351
+ * Return: 0 on success, <0 on failure
6d3351
+ */
6d3351
+static int
6d3351
+qemuDomainSetupIsolationGroups(virDomainDefPtr def)
6d3351
+{
6d3351
+    int idx;
6d3351
+    int ret = -1;
6d3351
+
6d3351
+    /* Only pSeries guests care about isolation groups at the moment */
6d3351
+    if (!qemuDomainIsPSeries(def))
6d3351
+        return 0;
6d3351
+
6d3351
+    idx = virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0);
6d3351
+    if (idx < 0)
6d3351
+        goto cleanup;
6d3351
+
6d3351
+    /* We want to prevent hostdevs from being plugged into the default PHB:
6d3351
+     * we can make sure that doesn't happen by locking its isolation group */
6d3351
+    def->controllers[idx]->info.isolationGroupLocked = true;
6d3351
+
6d3351
+    /* Fill in isolation groups for all other devices */
6d3351
+    if (virDomainDeviceInfoIterate(def,
6d3351
+                                   qemuDomainFillDeviceIsolationGroupIter,
6d3351
+                                   NULL) < 0) {
6d3351
+        goto cleanup;
6d3351
+    }
6d3351
+
6d3351
+    ret = 0;
6d3351
+
6d3351
+ cleanup:
6d3351
+    return ret;
6d3351
+}
6d3351
+
6d3351
+
6d3351
+/**
6d3351
  * qemuDomainFillDevicePCIConnectFlags:
6d3351
  *
6d3351
  * @def: the entire DomainDef
6d3351
@@ -2049,6 +2287,9 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def,
6d3351
     if (qemuDomainFillAllPCIConnectFlags(def, qemuCaps, driver) < 0)
6d3351
         goto cleanup;
6d3351
 
6d3351
+    if (qemuDomainSetupIsolationGroups(def) < 0)
6d3351
+        goto cleanup;
6d3351
+
6d3351
     if (nbuses > 0) {
6d3351
         /* 1st pass to figure out how many PCI bridges we need */
6d3351
         if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, true)))
6d3351
diff --git a/src/qemu/qemu_domain_address.h b/src/qemu/qemu_domain_address.h
6d3351
index 067f4e7997..b5644fa9c2 100644
6d3351
--- a/src/qemu/qemu_domain_address.h
6d3351
+++ b/src/qemu/qemu_domain_address.h
6d3351
@@ -44,6 +44,10 @@ int qemuDomainEnsurePCIAddress(virDomainObjPtr obj,
6d3351
                                virQEMUDriverPtr driver)
6d3351
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
6d3351
 
6d3351
+int qemuDomainFillDeviceIsolationGroup(virDomainDefPtr def,
6d3351
+                                       virDomainDeviceDefPtr dev)
6d3351
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
6d3351
+
6d3351
 void qemuDomainReleaseDeviceAddress(virDomainObjPtr vm,
6d3351
                                     virDomainDeviceInfoPtr info,
6d3351
                                     const char *devstr);
6d3351
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
6d3351
index 476e2b81a3..34f1a646e9 100644
6d3351
--- a/src/qemu/qemu_hotplug.c
6d3351
+++ b/src/qemu/qemu_hotplug.c
6d3351
@@ -1468,6 +1468,13 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver,
6d3351
 
6d3351
     if (qemuAssignDeviceHostdevAlias(vm->def, &info->alias, -1) < 0)
6d3351
         goto error;
6d3351
+
6d3351
+    if (qemuDomainIsPSeries(vm->def)) {
6d3351
+        /* Isolation groups are only relevant for pSeries guests */
6d3351
+        if (qemuDomainFillDeviceIsolationGroup(vm->def, &dev) < 0)
6d3351
+            goto error;
6d3351
+    }
6d3351
+
6d3351
     if (qemuDomainEnsurePCIAddress(vm, &dev, driver) < 0)
6d3351
         goto error;
6d3351
     releaseaddr = true;
6d3351
diff --git a/tests/qemumemlocktest.c b/tests/qemumemlocktest.c
6d3351
index ea25cd9a66..42561ac19e 100644
6d3351
--- a/tests/qemumemlocktest.c
6d3351
+++ b/tests/qemumemlocktest.c
6d3351
@@ -131,7 +131,7 @@ mymain(void)
6d3351
 
6d3351
     DO_TEST("pseries-hardlimit", 2147483648);
6d3351
     DO_TEST("pseries-locked", VIR_DOMAIN_MEMORY_PARAM_UNLIMITED);
6d3351
-    DO_TEST("pseries-hostdev", 2168455168);
6d3351
+    DO_TEST("pseries-hostdev", 4320133120);
6d3351
 
6d3351
     DO_TEST("pseries-hardlimit+locked", 2147483648);
6d3351
     DO_TEST("pseries-hardlimit+hostdev", 2147483648);
6d3351
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-1.args b/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-1.args
6d3351
index 051ffdeb3e..8a4a4c5a63 100644
6d3351
--- a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-1.args
6d3351
+++ b/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-1.args
6d3351
@@ -18,6 +18,8 @@ QEMU_AUDIO_DRV=none \
6d3351
 server,nowait \
6d3351
 -mon chardev=charmonitor,id=monitor,mode=readline \
6d3351
 -boot c \
6d3351
--device vfio-pci,host=0005:90:01.0,id=hostdev0,bus=pci.0,addr=0x1 \
6d3351
--device vfio-pci,host=0001:01:00.0,id=hostdev1,bus=pci.0,addr=0x2 \
6d3351
--device vfio-pci,host=0001:01:00.1,id=hostdev2,bus=pci.0,addr=0x3
6d3351
+-device spapr-pci-host-bridge,index=1,id=pci.1 \
6d3351
+-device spapr-pci-host-bridge,index=2,id=pci.2 \
6d3351
+-device vfio-pci,host=0005:90:01.0,id=hostdev0,bus=pci.1.0,addr=0x1 \
6d3351
+-device vfio-pci,host=0001:01:00.0,id=hostdev1,bus=pci.2.0,addr=0x1 \
6d3351
+-device vfio-pci,host=0001:01:00.1,id=hostdev2,bus=pci.2.0,addr=0x2
6d3351
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-2.args b/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-2.args
6d3351
index 83d4306036..cd5b66404e 100644
6d3351
--- a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-2.args
6d3351
+++ b/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-2.args
6d3351
@@ -19,6 +19,7 @@ server,nowait \
6d3351
 -mon chardev=charmonitor,id=monitor,mode=readline \
6d3351
 -boot c \
6d3351
 -device spapr-pci-host-bridge,index=1,id=pci.1 \
6d3351
+-device spapr-pci-host-bridge,index=2,id=pci.2 \
6d3351
 -device virtio-scsi-pci,id=scsi0,bus=pci.1.0,addr=0x1 \
6d3351
 -device vfio-pci,host=0001:01:00.0,id=hostdev0,bus=pci.1.0,addr=0x2 \
6d3351
--device vfio-pci,host=0005:90:01.0,id=hostdev1,bus=pci.0,addr=0x1
6d3351
+-device vfio-pci,host=0005:90:01.0,id=hostdev1,bus=pci.2.0,addr=0x1
6d3351
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-3.args b/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-3.args
6d3351
index eda6cc73ac..66a31ba1a8 100644
6d3351
--- a/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-3.args
6d3351
+++ b/tests/qemuxml2argvdata/qemuxml2argv-pseries-hostdevs-3.args
6d3351
@@ -21,4 +21,4 @@ server,nowait \
6d3351
 -device spapr-pci-host-bridge,index=1,id=pci.1 \
6d3351
 -device spapr-pci-host-bridge,index=2,id=pci.2 \
6d3351
 -device vfio-pci,host=0001:01:00.0,id=hostdev0,bus=pci.2.0,addr=0x1 \
6d3351
--device vfio-pci,host=0001:01:00.1,id=hostdev1,bus=pci.0,addr=0x1
6d3351
+-device vfio-pci,host=0001:01:00.1,id=hostdev1,bus=pci.2.0,addr=0x2
6d3351
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-1.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-1.xml
6d3351
index fa9e4daca5..e77a060a38 100644
6d3351
--- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-1.xml
6d3351
+++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-1.xml
6d3351
@@ -19,27 +19,35 @@
6d3351
       <model name='spapr-pci-host-bridge'/>
6d3351
       <target index='0'/>
6d3351
     </controller>
6d3351
+    <controller type='pci' index='1' model='pci-root'>
6d3351
+      <model name='spapr-pci-host-bridge'/>
6d3351
+      <target index='1'/>
6d3351
+    </controller>
6d3351
+    <controller type='pci' index='2' model='pci-root'>
6d3351
+      <model name='spapr-pci-host-bridge'/>
6d3351
+      <target index='2'/>
6d3351
+    </controller>
6d3351
     <interface type='hostdev' managed='yes'>
6d3351
       <mac address='52:54:00:6d:90:02'/>
6d3351
       <driver name='vfio'/>
6d3351
       <source>
6d3351
         <address type='pci' domain='0x0005' bus='0x90' slot='0x01' function='0x0'/>
6d3351
       </source>
6d3351
-      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
6d3351
+      <address type='pci' domain='0x0000' bus='0x01' slot='0x01' function='0x0'/>
6d3351
     </interface>
6d3351
     <hostdev mode='subsystem' type='pci' managed='yes'>
6d3351
       <driver name='vfio'/>
6d3351
       <source>
6d3351
         <address domain='0x0001' bus='0x01' slot='0x00' function='0x0'/>
6d3351
       </source>
6d3351
-      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
6d3351
+      <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/>
6d3351
     </hostdev>
6d3351
     <hostdev mode='subsystem' type='pci' managed='yes'>
6d3351
       <driver name='vfio'/>
6d3351
       <source>
6d3351
         <address domain='0x0001' bus='0x01' slot='0x00' function='0x1'/>
6d3351
       </source>
6d3351
-      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
6d3351
+      <address type='pci' domain='0x0000' bus='0x02' slot='0x02' function='0x0'/>
6d3351
     </hostdev>
6d3351
     <memballoon model='none'/>
6d3351
     <panic model='pseries'/>
6d3351
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-2.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-2.xml
6d3351
index 17ff4c8537..cfa395b001 100644
6d3351
--- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-2.xml
6d3351
+++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-2.xml
6d3351
@@ -26,6 +26,10 @@
6d3351
       <model name='spapr-pci-host-bridge'/>
6d3351
       <target index='1'/>
6d3351
     </controller>
6d3351
+    <controller type='pci' index='2' model='pci-root'>
6d3351
+      <model name='spapr-pci-host-bridge'/>
6d3351
+      <target index='2'/>
6d3351
+    </controller>
6d3351
     <hostdev mode='subsystem' type='pci' managed='yes'>
6d3351
       <driver name='vfio'/>
6d3351
       <source>
6d3351
@@ -38,7 +42,7 @@
6d3351
       <source>
6d3351
         <address domain='0x0005' bus='0x90' slot='0x01' function='0x0'/>
6d3351
       </source>
6d3351
-      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
6d3351
+      <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/>
6d3351
     </hostdev>
6d3351
     <memballoon model='none'/>
6d3351
     <panic model='pseries'/>
6d3351
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-3.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-3.xml
6d3351
index 58023ecd72..f91959b805 100644
6d3351
--- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-3.xml
6d3351
+++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pseries-hostdevs-3.xml
6d3351
@@ -39,7 +39,7 @@
6d3351
       <source>
6d3351
         <address domain='0x0001' bus='0x01' slot='0x00' function='0x1'/>
6d3351
       </source>
6d3351
-      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
6d3351
+      <address type='pci' domain='0x0000' bus='0x02' slot='0x02' function='0x0'/>
6d3351
     </hostdev>
6d3351
     <memballoon model='none'/>
6d3351
     <panic model='pseries'/>
6d3351
-- 
6d3351
2.13.3
6d3351