Signed-off-by: Shivaprasad G Bhat <sbhat(a)linux.vnet.ibm.com>
---
src/qemu/qemu_domain.c | 2
src/qemu/qemu_domain_address.c | 68 +++++++++++++++++
src/qemu/qemu_domain_address.h | 5 +
src/qemu/qemu_driver.c | 66 ++++++++++------
src/qemu/qemu_hotplug.c | 82 ++++++++++++++++++++
src/qemu/qemu_hotplug.h | 4 +
tests/qemuhotplugtest.c | 39 ++++++++--
.../qemuhotplug-multifunction-hostdev-pci-2.xml | 14 +++
.../qemuhotplug-multifunction-hostdev-pci.xml | 20 +++++
...hotplug-base-live+multifunction-hostdev-pci.xml | 76 +++++++++++++++++++
...eries-base-live+multifunction-hostdev-pci-2.xml | 61 +++++++++++++++
...pseries-base-live+multifunction-hostdev-pci.xml | 69 +++++++++++++++++
12 files changed, 475 insertions(+), 31 deletions(-)
create mode 100644
tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-2.xml
create mode 100644
tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci.xml
create mode 100644
tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci.xml
create mode 100644
tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci-2.xml
create mode 100644
tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci.xml
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index c0a0af525f..1cfaf01540 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -33,6 +33,8 @@
#include "qemu_capabilities.h"
#include "qemu_migration.h"
#include "qemu_security.h"
+#include "qemu_hotplug.h"
+#include "qemu_hostdev.h"
#include "viralloc.h"
#include "virlog.h"
#include "virerror.h"
diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c
index 7bee4fb937..ee743321dd 100644
--- a/src/qemu/qemu_domain_address.c
+++ b/src/qemu/qemu_domain_address.c
@@ -25,6 +25,7 @@
#include "qemu_domain_address.h"
#include "qemu_domain.h"
+#include "domain_conf.h"
#include "viralloc.h"
#include "virhostdev.h"
#include "virerror.h"
@@ -3206,6 +3207,73 @@ qemuDomainEnsurePCIAddress(virDomainObjPtr obj,
info->pciConnectFlags);
}
+
+int
+qemuDomainPCIMultifunctionHostdevEnsurePCIAddresses(virDomainObjPtr vm,
+ virDomainDeviceDefListPtr devlist,
+ virQEMUDriverPtr driver)
+{
+ int ret = -1, aggrslotidx = 0;
+ virBitmapPtr slotmap = NULL;
+ size_t i;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virDomainPCIMultifunctionAddressInfoPtr devinfos = NULL;
+
+ if (devlist->count > VIR_PCI_MAX_FUNCTIONS) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("More devices per slot found"));
+ return -1;
+ }
+
+ for (i = 0; i < devlist->count; i++)
+ qemuDomainFillDevicePCIConnectFlags(vm->def, devlist->devs[i],
priv->qemuCaps, driver);
+
+ if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs +
devlist->count) < 0)
+ return -1;
+
+ /* Temporarily add the devices to the domain def to get the
+ * next aggregateIdx */
+ for (i = 0; i < devlist->count; i++)
+ vm->def->hostdevs[vm->def->nhostdevs++] =
devlist->devs[i]->data.hostdev;
+
+ for (i = 0; i < devlist->count; i++) {
+ if (qemuDomainIsPSeries(vm->def)) {
+ /* Isolation groups are only relevant for pSeries guests */
+ if (qemuDomainFillDeviceIsolationGroup(vm->def, devlist->devs[i]) <
0)
+ return -1;
+ }
+ qemuDomainSetDeviceSlotAggregateIdx(vm->def, devlist->devs[i]);
+ aggrslotidx = aggrslotidx ? aggrslotidx :
devlist->devs[i]->data.hostdev->info->aggregateSlotIdx;
+ if (aggrslotidx !=
devlist->devs[i]->data.hostdev->info->aggregateSlotIdx) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Devices belong to different PCI slots"));
+ return -1;
+ }
+ }
+
+ for (i = 0; i < devlist->count; i++)
+ vm->def->hostdevs[--(vm->def->nhostdevs)] = NULL;
+
+ slotmap = virDomainDefHostdevGetPCIOnlineFunctionMap(vm->def, aggrslotidx);
+ if (!virBitmapIsAllClear(slotmap)) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("Device already assigned to guest"));
+ return -1;
+ }
+
+ if (VIR_ALLOC(devinfos) < 0)
+ return -1;
+
+ for (i = 0; i < devlist->count; i++) {
+ virPCIDeviceAddress addr =
devlist->devs[i]->data.hostdev->source.subsys.u.pci.addr;
+ devinfos->infos[addr.function] =
devlist->devs[i]->data.hostdev->info;
+ }
+
+ ret = virDomainPCIAddressEnsureMultifunctionAddress(priv->pciaddrs, devinfos);
+ VIR_FREE(devinfos);
+ return ret;
+}
+
void
qemuDomainReleaseDeviceAddress(virDomainObjPtr vm,
virDomainDeviceInfoPtr info,
diff --git a/src/qemu/qemu_domain_address.h b/src/qemu/qemu_domain_address.h
index e1cc467714..43d1de889e 100644
--- a/src/qemu/qemu_domain_address.h
+++ b/src/qemu/qemu_domain_address.h
@@ -51,6 +51,11 @@ int qemuDomainEnsurePCIAddress(virDomainObjPtr obj,
virQEMUDriverPtr driver)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+int
+qemuDomainPCIMultifunctionHostdevEnsurePCIAddresses(virDomainObjPtr obj,
+ virDomainDeviceDefListPtr devlist,
+ virQEMUDriverPtr driver);
+
int qemuDomainFillDeviceIsolationGroup(virDomainDefPtr def,
virDomainDeviceDefPtr dev)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index d2e10082ea..fb14475d8c 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7744,15 +7744,26 @@ qemuDomainAttachDeviceLiveInternal(virDomainObjPtr vm,
static int
qemuDomainAttachDeviceLive(virDomainObjPtr vm,
- virDomainDeviceDefPtr dev,
+ virDomainDeviceDefListPtr devlist,
virQEMUDriverPtr driver)
{
int ret = -1;
+ size_t i;
- if (virDomainDefCompatibleDevice(vm->def, dev, NULL) < 0)
- return -1;
+ for (i = 0; i < devlist->count; i++)
+ if (virDomainDefCompatibleDevice(vm->def, devlist->devs[i], NULL) < 0)
+ return ret;
+
+ if (devlist->count > 1) {
+ ret = qemuDomainAttachMultifunctionDevice(vm, devlist, driver);
+ if (ret == 0) {
+ for (i = 0; i < devlist->count; i++)
+ devlist->devs[i]->data.hostdev = NULL;
+ }
+ } else if (devlist->count == 1) {
+ ret = qemuDomainAttachDeviceLiveInternal(vm, devlist->devs[0], driver);
+ }
- ret = qemuDomainAttachDeviceLiveInternal(vm, dev, driver);
if (ret == 0)
ret = qemuDomainUpdateDeviceList(driver, vm, QEMU_ASYNC_JOB_NONE);
@@ -8178,16 +8189,22 @@ qemuDomainAttachDeviceConfigInternal(virDomainDefPtr vmdef,
static int
qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef,
- virDomainDeviceDefPtr dev,
+ virDomainDeviceDefListPtr devlist,
virCapsPtr caps,
unsigned int parse_flags,
virDomainXMLOptionPtr xmlopt)
{
- if (virDomainDefCompatibleDevice(vmdef, dev, NULL) < 0)
- return -1;
+ size_t i;
- if (qemuDomainAttachDeviceConfigInternal(vmdef, dev))
- return -1;
+ for (i = 0; i < devlist->count; i++) {
+ if (virDomainDefCompatibleDevice(vmdef, devlist->devs[i], NULL) < 0)
+ return -1;
+ }
+
+ for (i = 0; i < devlist->count; i++) {
+ if (qemuDomainAttachDeviceConfigInternal(vmdef, devlist->devs[i]))
+ return -1;
+ }
if (virDomainDefPostParse(vmdef, caps, parse_flags, xmlopt, NULL) < 0)
return -1;
@@ -8493,9 +8510,11 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm,
const char *xml,
unsigned int flags)
{
+ size_t i;
virDomainDefPtr vmdef = NULL;
virQEMUDriverConfigPtr cfg = NULL;
- virDomainDeviceDefPtr dev = NULL, dev_copy = NULL;
+ virDomainDeviceDefListPtr devlist = NULL, devcopylist = NULL;
+ virDomainDeviceDefListData data = {.def = vm->def, .xmlopt = driver->xmlopt,
.caps = NULL};
int ret = -1;
virCapsPtr caps = NULL;
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE |
@@ -8508,15 +8527,16 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm,
if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
goto cleanup;
+ data.caps = caps;
- dev = dev_copy = virDomainDeviceDefParse(xml, vm->def,
- caps, driver->xmlopt,
- parse_flags);
- if (dev == NULL)
+ devlist = qemuDomainDeviceParseXMLMany(xml, &data, parse_flags);
+ if (!devlist)
goto cleanup;
+ devcopylist = devlist;
- if (virDomainDeviceValidateAliasForHotplug(vm, dev, flags) < 0)
- goto cleanup;
+ for (i = 0; i < devlist->count; i++)
+ if (virDomainDeviceValidateAliasForHotplug(vm, devlist->devs[i], flags) <
0)
+ goto cleanup;
if (flags & VIR_DOMAIN_AFFECT_CONFIG &&
flags & VIR_DOMAIN_AFFECT_LIVE) {
@@ -8524,8 +8544,8 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm,
* create a deep copy of device as adding
* to CONFIG takes one instance.
*/
- dev_copy = virDomainDeviceDefCopy(dev, vm->def, caps, driver->xmlopt);
- if (!dev_copy)
+ devcopylist = virDomainDeviceDefListCopy(devlist, &data);
+ if (!devcopylist)
goto cleanup;
}
@@ -8535,14 +8555,14 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm,
if (!vmdef)
goto cleanup;
- if ((ret = qemuDomainAttachDeviceConfig(vmdef, dev, caps,
+ if ((ret = qemuDomainAttachDeviceConfig(vmdef, devlist, caps,
parse_flags,
driver->xmlopt)) < 0)
goto cleanup;
}
if (flags & VIR_DOMAIN_AFFECT_LIVE) {
- if ((ret = qemuDomainAttachDeviceLive(vm, dev_copy, driver)) < 0)
+ if ((ret = qemuDomainAttachDeviceLive(vm, devcopylist, driver)) < 0)
goto cleanup;
/*
* update domain status forcibly because the domain status may be
@@ -8566,9 +8586,9 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm,
cleanup:
virDomainDefFree(vmdef);
- if (dev != dev_copy)
- virDomainDeviceDefFree(dev_copy);
- virDomainDeviceDefFree(dev);
+ if (devlist != devcopylist)
+ virDomainDeviceDefListFree(devcopylist);
+ virDomainDeviceDefListFree(devlist);
virObjectUnref(cfg);
virObjectUnref(caps);
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 7dffaf9502..a339e92bfa 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -1402,6 +1402,88 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver,
}
+static int
+qemuiHostdevPCIMultifunctionDevicesListSort(const void *p1,
+ const void *p2)
+{
+ virDomainDeviceDefPtr a = *(virDomainDeviceDefPtr *) p1;
+ virDomainDeviceDefPtr b = *(virDomainDeviceDefPtr *) p2;
+ virPCIDeviceAddressPtr addr1 = &a->data.hostdev->source.subsys.u.pci.addr;
+ virPCIDeviceAddressPtr addr2 = &b->data.hostdev->source.subsys.u.pci.addr;
+
+ return addr1->function - addr2->function;
+}
+
+
+int
+qemuDomainAttachMultifunctionDevice(virDomainObjPtr vm,
+ virDomainDeviceDefListPtr devlist,
+ virQEMUDriverPtr driver)
+{
+ size_t i, d, h = devlist->count;
+ int ret = -1;
+ char *alias;
+ virObjectEventPtr event;
+ virQEMUCapsPtr qemuCaps = NULL;
+ virDomainHostdevDefPtr hostdev = NULL;
+
+ if (!(qemuCaps = virQEMUCapsCacheLookup(driver->qemuCapsCache,
+ vm->def->emulator)))
+ return -1;
+
+ qsort(devlist->devs, devlist->count, sizeof(*devlist->devs),
qemuiHostdevPCIMultifunctionDevicesListSort);
+
+ if (qemuDomainPCIMultifunctionHostdevEnsurePCIAddresses(vm, devlist, driver) < 0)
+ return -1;
+
+ for (d = 0; d < devlist->count; d++)
+ if (qemuDomainAttachPCIHostDevicePrepare(driver, vm->def,
devlist->devs[d]->data.hostdev, qemuCaps) < 0)
+ goto cleanup;
+
+ /* Hotplug all functions, and Primary at last */
+ for (h = devlist->count; h > 0; h--) {
+ /* The functions need not be contiguous, as a card may be sold with
+ * minimal functionality and then install the additional functions on
+ * purchase into any of the daughter-card connectors.
+ */
+ hostdev = devlist->devs[h-1]->data.hostdev;
+ ret = qemuDomainAttachHostPCIDevice(driver, vm, hostdev);
+ if (ret)
+ goto release;
+
+ alias = hostdev->info->alias;
+ hostdev = NULL;
+
+ event = virDomainEventDeviceAddedNewFromObj(vm, alias);
+ qemuDomainEventQueue(driver, event);
+ }
+
+ release:
+ /* Release addresses for the device which are not hotplugged.
+ */
+ for (i = 0; i < h; i++)
+ qemuDomainReleaseDeviceAddress(vm, devlist->devs[i]->data.hostdev->info,
NULL);
+
+ cleanup:
+ /* If none are actually hotplugged and just detached from the
+ * host driver reattach the devices to host driver.
+ *
+ * If one of the hotplug failed, those which are already hotplugged cannot
+ * be unplugged as they are released by qemu only on guest reboot even
+ * if we issue device_del on them.
+ * So, dont attempt to reattach any of them.
+ * NB: Let them be in the guest as they are not used anyway without
+ * function-zero?
+ */
+ if (d > 0 && h == devlist->count) {
+ for (i = 0; i < d; i++)
+ qemuHostdevReAttachPCIDevices(driver, vm->def->name,
&devlist->devs[i]->data.hostdev, 1);
+ }
+
+ return ret;
+}
+
+
void
qemuDomainDelTLSObjects(virQEMUDriverPtr driver,
virDomainObjPtr vm,
diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h
index db0e1df79a..7a6e2dfb60 100644
--- a/src/qemu/qemu_hotplug.h
+++ b/src/qemu/qemu_hotplug.h
@@ -158,6 +158,10 @@ int qemuDomainAttachPCIHostDevicePrepare(virQEMUDriverPtr driver,
virDomainDefPtr def,
virDomainHostdevDefPtr dev,
virQEMUCapsPtr qemuCaps);
+int
+qemuDomainAttachMultifunctionDevice(virDomainObjPtr vm,
+ virDomainDeviceDefListPtr devlist,
+ virQEMUDriverPtr driver);
int
qemuDomainChrInsert(virDomainDefPtr vmdef,
diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c
index b5dca5e5c9..39f122e083 100644
--- a/tests/qemuhotplugtest.c
+++ b/tests/qemuhotplugtest.c
@@ -82,6 +82,8 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt,
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI);
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_DEVICE_SPAPR_PCI_HOST_BRIDGE);
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_VIRTIO_PCI_DISABLE_LEGACY);
+ virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_HOST_PCI_MULTIDOMAIN);
+ virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION);
if (event)
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_DEVICE_DEL_EVENT);
@@ -115,9 +117,14 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt,
static int
testQemuHotplugAttach(virDomainObjPtr vm,
- virDomainDeviceDefPtr dev)
+ virDomainDeviceDefListPtr devlist)
{
int ret = -1;
+ virDomainDeviceDefPtr dev;
+
+ if (devlist->count > 1)
+ return qemuDomainAttachMultifunctionDevice(vm, devlist, &driver);
+ dev = devlist->devs[0];
switch (dev->type) {
case VIR_DOMAIN_DEVICE_DISK:
@@ -249,7 +256,9 @@ testQemuHotplug(const void *data)
bool keep = test->keep;
unsigned int device_parse_flags = 0;
virDomainObjPtr vm = NULL;
- virDomainDeviceDefPtr dev = NULL;
+ virDomainDeviceDefPtr dev = NULL; /*temperory */
+ virDomainDeviceDefListPtr devlist = NULL;
+ virDomainDeviceDefListData listdata;
virCapsPtr caps = NULL;
qemuMonitorTestPtr test_mon = NULL;
qemuDomainObjPrivatePtr priv = NULL;
@@ -286,10 +295,13 @@ testQemuHotplug(const void *data)
if (test->action == ATTACH)
device_parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
- if (!(dev = virDomainDeviceDefParse(device_xml, vm->def,
- caps, driver.xmlopt,
- device_parse_flags)))
+ listdata.def = vm->def;
+ listdata.xmlopt = driver.xmlopt;
+ listdata.caps = caps;
+ devlist = qemuDomainDeviceParseXMLMany(device_xml, &listdata,
device_parse_flags);
+ if (!devlist)
goto cleanup;
+ dev = devlist->devs[0]; /* temporary */
/* Now is the best time to feed the spoofed monitor with predefined
* replies. */
@@ -319,11 +331,11 @@ testQemuHotplug(const void *data)
switch (test->action) {
case ATTACH:
- ret = testQemuHotplugAttach(vm, dev);
+ ret = testQemuHotplugAttach(vm, devlist);
if (ret == 0) {
/* vm->def stolen dev->data.* so we just need to free the dev
* envelope */
- VIR_FREE(dev);
+ virDomainDeviceDefListFreeShallow(devlist);
}
if (ret == 0 || fail)
ret = testQemuHotplugCheckResult(vm, result_xml,
@@ -357,7 +369,7 @@ testQemuHotplug(const void *data)
virObjectUnref(vm);
test->vm = NULL;
}
- virDomainDeviceDefFree(dev);
+ virDomainDeviceDefListFree(devlist);
virObjectUnref(caps);
qemuMonitorTestFree(test_mon);
return ((ret < 0 && fail) || (!ret && !fail)) ? 0 : -1;
@@ -856,6 +868,17 @@ mymain(void)
"device_add", QMP_OK);
DO_TEST_DETACH("pseries-base-live", "hostdev-pci", false, false,
"device_del", QMP_DEVICE_DELETED("hostdev0")
QMP_OK);
+ DO_TEST_ATTACH("base-live", "multifunction-hostdev-pci", false,
false,
+ "device_add", QMP_OK,
+ "device_add", QMP_OK,
+ "device_add", QMP_OK);
+
+ qemuTestSetHostArch(driver.caps, VIR_ARCH_PPC64);
+ DO_TEST_ATTACH("pseries-base-live",
"multifunction-hostdev-pci-2", false, false,
+ "device_add", QMP_OK,
+ "device_add", QMP_OK,
+ "device_add", QMP_OK);
+ qemuTestSetHostArch(driver.caps, VIR_ARCH_X86_64);
DO_TEST_ATTACH("base-live", "watchdog", false, true,
"watchdog-set-action", QMP_OK,
diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-2.xml
b/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-2.xml
new file mode 100644
index 0000000000..02e2236e5d
--- /dev/null
+++ b/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-2.xml
@@ -0,0 +1,14 @@
+<devices>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0001' bus='0x01' slot='0x00'
function='0x1'/>
+ </source>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0001' bus='0x01' slot='0x00'
function='0x0'/>
+ </source>
+ </hostdev>
+</devices>
diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci.xml
b/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci.xml
new file mode 100644
index 0000000000..54bb627e30
--- /dev/null
+++ b/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci.xml
@@ -0,0 +1,20 @@
+<devices>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0005' bus='0x90' slot='0x01'
function='0x1'/>
+ </source>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0005' bus='0x90' slot='0x01'
function='0x2'/>
+ </source>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0005' bus='0x90' slot='0x01'
function='0x0'/>
+ </source>
+ </hostdev>
+</devices>
diff --git
a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci.xml
b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci.xml
new file mode 100644
index 0000000000..f53fe7601b
--- /dev/null
+++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci.xml
@@ -0,0 +1,76 @@
+<domain type='kvm' id='7'>
+ <name>hotplug</name>
+ <uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>4</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <features>
+ <acpi/>
+ <apic/>
+ <pae/>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <controller type='usb' index='0'>
+ <alias name='usb'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='ide' index='0'>
+ <alias name='ide'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='scsi' index='0' model='virtio-scsi'>
+ <alias name='scsi0'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </controller>
+ <controller type='pci' index='0' model='pci-root'>
+ <alias name='pci'/>
+ </controller>
+ <controller type='virtio-serial' index='0'>
+ <alias name='virtio-serial0'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x04' function='0x0'/>
+ </controller>
+ <input type='mouse' bus='ps2'>
+ <alias name='input0'/>
+ </input>
+ <input type='keyboard' bus='ps2'>
+ <alias name='input1'/>
+ </input>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0005' bus='0x90' slot='0x01'
function='0x2'/>
+ </source>
+ <alias name='hostdev0'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x05' function='0x2'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0005' bus='0x90' slot='0x01'
function='0x1'/>
+ </source>
+ <alias name='hostdev1'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x05' function='0x1'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0005' bus='0x90' slot='0x01'
function='0x0'/>
+ </source>
+ <alias name='hostdev2'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x05' function='0x0' multifunction='on'/>
+ </hostdev>
+ <memballoon model='none'>
+ <alias name='balloon0'/>
+ </memballoon>
+ </devices>
+ <seclabel type='none' model='none'/>
+</domain>
diff --git
a/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci-2.xml
b/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci-2.xml
new file mode 100644
index 0000000000..85dc48fdb8
--- /dev/null
+++
b/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci-2.xml
@@ -0,0 +1,61 @@
+<domain type='kvm' id='7'>
+ <name>hotplug</name>
+ <uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>4</vcpu>
+ <os>
+ <type arch='ppc64' machine='pseries'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-ppc64</emulator>
+ <controller type='pci' index='0' model='pci-root'>
+ <model name='spapr-pci-host-bridge'/>
+ <target index='0'/>
+ <alias name='pci.0'/>
+ </controller>
+ <controller type='pci' index='1' model='pci-root'>
+ <model name='spapr-pci-host-bridge'/>
+ <target index='1'/>
+ <alias name='pci.1'/>
+ </controller>
+ <controller type='usb' index='0'>
+ <alias name='usb'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x0'/>
+ </controller>
+ <input type='keyboard' bus='usb'>
+ <alias name='input0'/>
+ <address type='usb' bus='0' port='1'/>
+ </input>
+ <input type='mouse' bus='usb'>
+ <alias name='input1'/>
+ <address type='usb' bus='0' port='2'/>
+ </input>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0001' bus='0x01' slot='0x00'
function='0x1'/>
+ </source>
+ <alias name='hostdev0'/>
+ <address type='pci' domain='0x0000' bus='0x01'
slot='0x01' function='0x1'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0001' bus='0x01' slot='0x00'
function='0x0'/>
+ </source>
+ <alias name='hostdev1'/>
+ <address type='pci' domain='0x0000' bus='0x01'
slot='0x01' function='0x0' multifunction='on'/>
+ </hostdev>
+ <memballoon model='none'>
+ <alias name='balloon0'/>
+ </memballoon>
+ <panic model='pseries'/>
+ </devices>
+ <seclabel type='none' model='none'/>
+</domain>
diff --git
a/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci.xml
b/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci.xml
new file mode 100644
index 0000000000..9338d42e2e
--- /dev/null
+++
b/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci.xml
@@ -0,0 +1,69 @@
+<domain type='kvm' id='7'>
+ <name>hotplug</name>
+ <uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid>
+ <memory unit='KiB'>4194304</memory>
+ <currentMemory unit='KiB'>4194304</currentMemory>
+ <vcpu placement='static'>4</vcpu>
+ <os>
+ <type arch='ppc64' machine='pseries'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-ppc64</emulator>
+ <controller type='pci' index='0' model='pci-root'>
+ <model name='spapr-pci-host-bridge'/>
+ <target index='0'/>
+ <alias name='pci.0'/>
+ </controller>
+ <controller type='pci' index='1' model='pci-root'>
+ <model name='spapr-pci-host-bridge'/>
+ <target index='1'/>
+ <alias name='pci.1'/>
+ </controller>
+ <controller type='usb' index='0'>
+ <alias name='usb'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x0'/>
+ </controller>
+ <input type='keyboard' bus='usb'>
+ <alias name='input0'/>
+ <address type='usb' bus='0' port='1'/>
+ </input>
+ <input type='mouse' bus='usb'>
+ <alias name='input1'/>
+ <address type='usb' bus='0' port='2'/>
+ </input>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0005' bus='0x90' slot='0x01'
function='0x2'/>
+ </source>
+ <alias name='hostdev0'/>
+ <address type='pci' domain='0x0000' bus='0x01'
slot='0x01' function='0x2'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0005' bus='0x90' slot='0x01'
function='0x1'/>
+ </source>
+ <alias name='hostdev1'/>
+ <address type='pci' domain='0x0000' bus='0x01'
slot='0x01' function='0x1'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0005' bus='0x90' slot='0x01'
function='0x0'/>
+ </source>
+ <alias name='hostdev2'/>
+ <address type='pci' domain='0x0000' bus='0x01'
slot='0x01' function='0x0' multifunction='on'/>
+ </hostdev>
+ <memballoon model='none'>
+ <alias name='balloon0'/>
+ </memballoon>
+ <panic model='pseries'/>
+ </devices>
+ <seclabel type='none' model='none'/>
+</domain>