[PATCH v2 16/21] qemu: hotplug: Implement multifunction device hotplug

Daniel Henrique Barboza danielhb413 at gmail.com
Thu Jan 30 16:44:28 UTC 2020


From: Shivaprasad G Bhat <sbhat at linux.vnet.ibm.com>

The support to allow multifunction device hotplug consists
of using virDomainDeviceDefListPtr lists instead of single
virDomainDeviceDefPtr device definitions in both
qemuDomainAttachDeviceConfig() and qemuDomainAttachDeviceLive().
In AttachDeviceConfig() the same existing single device code
is ran for all devs in the list.

AttachDeviceLive() will verify if the list has size = 1 (meaning it
is a single device attach) and redirect the code to the regular single
dev attach procedure. If list size > 1, we'll execute a new specialized
function called qemuDomainAttachMultifunctionDevice(), which is
prepared to handle the nuances of this type of hotplug.

The changes in both AttachDeviceLive() and AttachDeviceConfig()
implied in changes to be made in qemuDomainAttachDeviceLiveAndConfig(),
which is now handling a device list instead of a single device.

All other added functions, most notably
qemuDomainPCIMultifunctionHostdevEnsurePCIAddresses(), are used
inside qemuDomainAttachMultifunctionDevice().

No changes in regular device hotplug mechanics were made.

Signed-off-by: Shivaprasad G Bhat <sbhat at linux.vnet.ibm.com>
Signed-off-by: Daniel Henrique Barboza <danielhb413 at gmail.com>
---
 src/qemu/qemu_domain_address.c                |  76 +++++++++++
 src/qemu/qemu_domain_address.h                |   5 +
 src/qemu/qemu_driver.c                        | 119 +++++++++++-------
 src/qemu/qemu_hotplug.c                       |  86 +++++++++++++
 src/qemu/qemu_hotplug.h                       |   4 +
 tests/qemuhotplugtest.c                       |  40 ++++--
 ...emuhotplug-multifunction-hostdev-pci-2.xml |  14 +++
 .../qemuhotplug-multifunction-hostdev-pci.xml |  26 ++++
 ...ug-base-live+multifunction-hostdev-pci.xml |  82 ++++++++++++
 ...-base-live+multifunction-hostdev-pci-2.xml |  59 +++++++++
 ...es-base-live+multifunction-hostdev-pci.xml |  69 ++++++++++
 11 files changed, 528 insertions(+), 52 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_address.c b/src/qemu/qemu_domain_address.c
index d9e36867bd..395f054b8d 100644
--- a/src/qemu/qemu_domain_address.c
+++ b/src/qemu/qemu_domain_address.c
@@ -23,6 +23,7 @@
 
 #include "qemu_domain_address.h"
 #include "qemu_domain.h"
+#include "domain_conf.h"
 #include "viralloc.h"
 #include "virhostdev.h"
 #include "virerror.h"
@@ -3477,6 +3478,81 @@ 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++) {
+        virDomainHostdevDefPtr hostdev = devlist->devs[i]->data.hostdev;
+
+        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 : hostdev->info->aggregateSlotIdx;
+
+        if (aggrslotidx != 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++) {
+        virDomainHostdevDefPtr hostdev = devlist->devs[i]->data.hostdev;
+        virPCIDeviceAddress addr = hostdev->source.subsys.u.pci.addr;
+
+        devinfos->infos[addr.function] = 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 a17245dbde..035b40a5a7 100644
--- a/src/qemu/qemu_domain_address.h
+++ b/src/qemu/qemu_domain_address.h
@@ -49,6 +49,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 fb53dc79df..95ad4c5e69 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7969,17 +7969,28 @@ 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,
-                                     VIR_DOMAIN_DEVICE_ACTION_ATTACH,
-                                     false) < 0)
-        return -1;
+    for (i = 0; i < devlist->count; i++)
+        if (virDomainDefCompatibleDevice(vm->def, devlist->devs[i], NULL,
+                                         VIR_DOMAIN_DEVICE_ACTION_ATTACH,
+                                         false) < 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);
 
@@ -8328,18 +8339,24 @@ qemuDomainAttachDeviceConfigInternal(virDomainDefPtr vmdef,
 
 static int
 qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef,
-                             virDomainDeviceDefPtr dev,
+                             virDomainDeviceDefListPtr devlist,
                              virQEMUCapsPtr qemuCaps,
                              unsigned int parse_flags,
                              virDomainXMLOptionPtr xmlopt)
 {
-    if (virDomainDefCompatibleDevice(vmdef, dev, NULL,
-                                     VIR_DOMAIN_DEVICE_ACTION_ATTACH,
-                                     false) < 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,
+                                         VIR_DOMAIN_DEVICE_ACTION_ATTACH,
+                                         false) < 0)
+            return -1;
+    }
+
+    for (i = 0; i < devlist->count; i++) {
+        if (qemuDomainAttachDeviceConfigInternal(vmdef, devlist->devs[i]))
+            return -1;
+    }
 
     if (virDomainDefPostParse(vmdef, parse_flags, xmlopt, qemuCaps) < 0)
         return -1;
@@ -8705,9 +8722,14 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm,
     qemuDomainObjPrivatePtr priv = vm->privateData;
     virDomainDefPtr vmdef = NULL;
     g_autoptr(virQEMUDriverConfig) cfg = NULL;
-    virDomainDeviceDefPtr devConf = NULL;
-    virDomainDeviceDef devConfSave = { 0 };
+    virDomainDeviceDefPtr devConfSave = NULL;
     virDomainDeviceDefPtr devLive = NULL;
+    virDomainDeviceDefListPtr devListConf = NULL;
+    virDomainDeviceDefListPtr devListConfSave = NULL;
+    virDomainDeviceDefListPtr devListLive = NULL;
+    virDomainDeviceDefListData data = {.def = vm->def,
+                                       .xmlopt = driver->xmlopt};
+    size_t i;
     int ret = -1;
     unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE |
                                VIR_DOMAIN_DEF_PARSE_ABI_UPDATE;
@@ -8726,52 +8748,58 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm,
         if (!vmdef)
             goto cleanup;
 
-        if (!(devConf = virDomainDeviceDefParse(xml, vmdef,
-                                                driver->xmlopt, priv->qemuCaps,
-                                                parse_flags)))
+        devListConf = qemuDomainDeviceParseXMLMany(xml, &data, priv->qemuCaps,
+                                                   parse_flags);
+        if (!devListConf)
             goto cleanup;
 
         /*
-         * devConf will be NULLed out by
+         * devListConf will be NULLed out by
          * qemuDomainAttachDeviceConfig(), so save it for later use by
          * qemuDomainAttachDeviceLiveAndConfigHomogenize()
          */
-        devConfSave = *devConf;
-
-        if (virDomainDeviceValidateAliasForHotplug(vm, devConf,
-                                                   VIR_DOMAIN_AFFECT_CONFIG) < 0)
-            goto cleanup;
+        if (flags & VIR_DOMAIN_AFFECT_LIVE) {
+            devListConfSave = virDomainDeviceDefListCopy(devListConf,
+                                                         &data,
+                                                         priv->qemuCaps);
+            if (!devListConfSave)
+                goto cleanup;
+        }
 
-        if (virDomainDefCompatibleDevice(vmdef, devConf, NULL,
-                                         VIR_DOMAIN_DEVICE_ACTION_ATTACH,
-                                         false) < 0)
-            goto cleanup;
+        for (i = 0; i < devListConf->count; i++) {
+            if (virDomainDeviceValidateAliasForHotplug(vm,
+                                                       devListConf->devs[i],
+                                                       flags) < 0)
+                goto cleanup;
+        }
 
-        if (qemuDomainAttachDeviceConfig(vmdef, devConf, priv->qemuCaps,
+        if (qemuDomainAttachDeviceConfig(vmdef, devListConf, priv->qemuCaps,
                                          parse_flags,
                                          driver->xmlopt) < 0)
             goto cleanup;
     }
 
     if (flags & VIR_DOMAIN_AFFECT_LIVE) {
-        if (!(devLive = virDomainDeviceDefParse(xml, vm->def,
-                                                driver->xmlopt, priv->qemuCaps,
-                                                parse_flags)))
+        devListLive = qemuDomainDeviceParseXMLMany(xml, &data, priv->qemuCaps,
+                                                   parse_flags);
+        if (!devListLive)
             goto cleanup;
 
-        if (flags & VIR_DOMAIN_AFFECT_CONFIG)
-            qemuDomainAttachDeviceLiveAndConfigHomogenize(&devConfSave, devLive);
+        for (i = 0; i < devListLive->count; i++) {
+            devLive = devListLive->devs[i];
 
-        if (virDomainDeviceValidateAliasForHotplug(vm, devLive,
-                                                   VIR_DOMAIN_AFFECT_LIVE) < 0)
-            goto cleanup;
+            if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
+                devConfSave = devListConfSave->devs[i];
+                qemuDomainAttachDeviceLiveAndConfigHomogenize(devConfSave,
+                                                              devLive);
+            }
 
-        if (virDomainDefCompatibleDevice(vm->def, devLive, NULL,
-                                         VIR_DOMAIN_DEVICE_ACTION_ATTACH,
-                                         true) < 0)
-            goto cleanup;
+            if (virDomainDeviceValidateAliasForHotplug(vm, devLive,
+                                                       VIR_DOMAIN_AFFECT_LIVE) < 0)
+                goto cleanup;
+        }
 
-        if (qemuDomainAttachDeviceLive(vm, devLive, driver) < 0)
+        if (qemuDomainAttachDeviceLive(vm, devListLive, driver) < 0)
             goto cleanup;
         /*
          * update domain status forcibly because the domain status may be
@@ -8794,8 +8822,11 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm,
     ret = 0;
  cleanup:
     virDomainDefFree(vmdef);
-    virDomainDeviceDefFree(devConf);
-    virDomainDeviceDefFree(devLive);
+    devConfSave = NULL;
+    devLive = NULL;
+    virDomainDeviceDefListFree(devListConf);
+    virDomainDeviceDefListFree(devListConfSave);
+    virDomainDeviceDefListFree(devListLive);
 
     return ret;
 }
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 2269fd5c10..495a5c9bdd 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -1668,6 +1668,92 @@ 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)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    size_t i, d, h = devlist->count;
+    int ret = -1;
+    char *alias;
+    virObjectEventPtr event;
+    virDomainHostdevDefPtr hostdev;
+
+    qsort(devlist->devs, devlist->count, sizeof(*devlist->devs),
+          qemuiHostdevPCIMultifunctionDevicesListSort);
+
+    if (qemuDomainPCIMultifunctionHostdevEnsurePCIAddresses(vm, devlist,
+                                                            driver) < 0)
+        return -1;
+
+    for (d = 0; d < devlist->count; d++) {
+        hostdev = devlist->devs[d]->data.hostdev;
+
+        if (qemuDomainAttachPCIHostDevicePrepare(driver, vm->def, hostdev,
+                                                 priv->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);
+        virObjectEventStateQueue(driver->domainEventState, 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);
+
+ 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 2874db43ee..9fd262a81b 100644
--- a/src/qemu/qemu_hotplug.h
+++ b/src/qemu/qemu_hotplug.h
@@ -126,6 +126,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 48284fdb3e..9c84b639a6 100644
--- a/tests/qemuhotplugtest.c
+++ b/tests/qemuhotplugtest.c
@@ -88,6 +88,7 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt,
     virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SPICE);
     virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SPICE_FILE_XFER_DISABLE);
     virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_VIRTIO_PCI_DISABLE_LEGACY);
+    virQEMUCapsSet(priv->qemuCaps, X_QEMU_CAPS_PCI_MULTIFUNCTION);
 
     if (qemuTestCapsCacheInsert(driver.qemuCapsCache, priv->qemuCaps) < 0)
         return -1;
@@ -116,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:
@@ -243,7 +249,9 @@ testQemuHotplug(const void *data)
     bool keep = test->keep;
     unsigned int device_parse_flags = 0;
     virDomainObjPtr vm = NULL;
-    virDomainDeviceDefPtr dev = NULL;
+    virDomainDeviceDefPtr dev = NULL; /* temporary */
+    virDomainDeviceDefListPtr devlist = NULL;
+    virDomainDeviceDefListData listdata;
     virCapsPtr caps = NULL;
     qemuMonitorTestPtr test_mon = NULL;
     qemuDomainObjPrivatePtr priv = NULL;
@@ -281,11 +289,15 @@ testQemuHotplug(const void *data)
     if (test->action == ATTACH)
         device_parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
 
-    if (!(dev = virDomainDeviceDefParse(device_xml, vm->def,
-                                        driver.xmlopt, NULL,
-                                        device_parse_flags)))
+    listdata.def = vm->def;
+    listdata.xmlopt = driver.xmlopt;
+    devlist = qemuDomainDeviceParseXMLMany(device_xml, &listdata, NULL,
+                                           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. */
     if (!(test_mon = qemuMonitorTestNew(driver.xmlopt, vm, &driver,
@@ -314,11 +326,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,
@@ -352,7 +364,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;
@@ -817,6 +829,18 @@ 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,
+                   "device_add", QMP_OK);
+
+    qemuTestSetHostArch(&driver, 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, 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..bef4be219f
--- /dev/null
+++ b/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci.xml
@@ -0,0 +1,26 @@
+<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='0x3'/>
+      </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..9dd04d3350
--- /dev/null
+++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci.xml
@@ -0,0 +1,82 @@
+<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='0x3'/>
+      </source>
+      <alias name='hostdev0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x3'/>
+    </hostdev>
+    <hostdev mode='subsystem' type='pci' managed='yes'>
+      <driver name='vfio'/>
+      <source>
+        <address domain='0x0005' bus='0x90' slot='0x01' function='0x2'/>
+      </source>
+      <alias name='hostdev1'/>
+      <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='hostdev2'/>
+      <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='hostdev3'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0' multifunction='on'/>
+    </hostdev>
+    <memballoon model='none'/>
+  </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..f99c1b3741
--- /dev/null
+++ b/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci-2.xml
@@ -0,0 +1,59 @@
+<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'/>
+    <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>
-- 
2.24.1





More information about the libvir-list mailing list