[PATCH V4] support for hotplug/hotunplug in test hypervisor

Thanos Makatos posted 1 patch 5 months, 3 weeks ago
Failed in applying to current master (apply log)
There is a newer version of this series
src/test/test_driver.c | 387 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 385 insertions(+), 2 deletions(-)
[PATCH V4] support for hotplug/hotunplug in test hypervisor
Posted by Thanos Makatos 5 months, 3 weeks ago
Signed-off-by: Thanos Makatos <thanos.makatos@nutanix.com>
---
 src/test/test_driver.c | 387 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 385 insertions(+), 2 deletions(-)

diff --git a/src/test/test_driver.c b/src/test/test_driver.c
index e87d7cfd44..a3b4799a0f 100644
--- a/src/test/test_driver.c
+++ b/src/test/test_driver.c
@@ -26,8 +26,6 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #include <libxml/xmlsave.h>
-
-
 #include "virerror.h"
 #include "datatypes.h"
 #include "test_driver.h"
@@ -38,10 +36,14 @@
 #include "virnetworkobj.h"
 #include "interface_conf.h"
 #include "checkpoint_conf.h"
+#include "domain_addr.h"
+#include "domain_audit.h"
 #include "domain_conf.h"
 #include "domain_driver.h"
 #include "domain_event.h"
+#include "domain_validate.h"
 #include "network_event.h"
+#include "nwfilter_conf.h"
 #include "snapshot_conf.h"
 #include "virfdstream.h"
 #include "storage_conf.h"
@@ -50,6 +52,7 @@
 #include "node_device_conf.h"
 #include "virnodedeviceobj.h"
 #include "node_device_event.h"
+#include "vircgroup.h"
 #include "virxml.h"
 #include "virthread.h"
 #include "virlog.h"
@@ -10035,6 +10038,383 @@ testConnectGetDomainCapabilities(virConnectPtr conn G_GNUC_UNUSED,
     return virDomainCapsFormat(domCaps);
 }
 
+static int
+testDomainAttachHostPCIDevice(testDriver *driver G_GNUC_UNUSED,
+                              virDomainObj *vm,
+                              virDomainHostdevDef *hostdev)
+{
+    int backend = hostdev->source.subsys.u.pci.backend;
+
+    switch ((virDomainHostdevSubsysPCIBackendType)backend) {
+    case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO:
+    case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
+    case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM:
+        break;
+
+    case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN:
+    case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST:
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("test hypervisor does not support device assignment mode '%s'"),
+                       virDomainHostdevSubsysPCIBackendTypeToString(backend));
+        return -1;
+    }
+
+    if (!virDomainObjIsActive(vm)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("guest unexpectedly quit during hotplug"));
+        return -1;
+    }
+
+    VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1);
+
+    virDomainAuditHostdev(vm, hostdev, "attach", true);
+
+    vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
+
+    return 0;
+}
+
+static int
+testDomainAttachHostDevice(testDriver *driver,
+                           virDomainObj *vm,
+                           virDomainHostdevDef *hostdev)
+{
+    if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("hotplug is not supported for hostdev mode '%s'"),
+                       virDomainHostdevModeTypeToString(hostdev->mode));
+        return -1;
+    }
+
+    if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("hotplug is not supported for hostdev subsys type '%s'"),
+                       virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type));
+        return -1;
+    }
+
+    return testDomainAttachHostPCIDevice(driver, vm, hostdev);
+}
+
+static int
+testDomainAttachDeviceLive(virDomainObj *vm,
+                           virDomainDeviceDef *dev,
+                           testDriver *driver)
+{
+    int ret = -1;
+    const char *alias = NULL;
+
+    if ((virDomainDeviceType)dev->type != VIR_DOMAIN_DEVICE_HOSTDEV) {
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+                       _("live attach of device '%s' is not supported"),
+                       virDomainDeviceTypeToString(dev->type));
+        return -1;
+    }
+
+    testDomainObjCheckHostdevTaint(vm, dev->data.hostdev);
+    if ((ret = testDomainAttachHostDevice(driver, vm, dev->data.hostdev)) != 0)
+        return ret;
+
+    alias = dev->data.hostdev->info->alias;
+    dev->data.hostdev = NULL;
+
+    if (alias) {
+        virObjectEvent *event;
+        event = virDomainEventDeviceAddedNewFromObj(vm, alias);
+        virObjectEventStateQueue(driver->eventState, event);
+    }
+
+    return 0;
+}
+
+static int
+testDomainAttachDeviceLiveAndConfig(virDomainObj *vm,
+                                    testDriver *driver,
+                                    const char *xml,
+                                    unsigned int flags)
+{
+    g_autoptr(virDomainDeviceDef) devConf = NULL;
+    g_autoptr(virDomainDeviceDef) devLive = NULL;
+    unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE |
+                               VIR_DOMAIN_DEF_PARSE_ABI_UPDATE;
+
+    virCheckFlags(VIR_DOMAIN_AFFECT_LIVE, -1);
+
+    if (flags & VIR_DOMAIN_AFFECT_LIVE) {
+        if (!(devLive = virDomainDeviceDefParse(xml, vm->def,
+                                                driver->xmlopt, NULL,
+                                                parse_flags)))
+            return -1;
+
+        if (virDomainDeviceValidateAliasForHotplug(vm, devLive,
+                                                   VIR_DOMAIN_AFFECT_LIVE) < 0)
+            return -1;
+
+        if (virDomainDefCompatibleDevice(vm->def, devLive, NULL,
+                                         VIR_DOMAIN_DEVICE_ACTION_ATTACH,
+                                         true) < 0)
+            return -1;
+
+        if (testDomainAttachDeviceLive(vm, devLive, driver) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+static int
+testDomainAttachDeviceFlags(virDomainPtr domain,
+                               const char *xml,
+                               unsigned int flags) {
+
+    testDriver *driver = domain->conn->privateData;
+    virDomainObj *vm = NULL;
+    int ret = -1;
+
+    virNWFilterReadLockFilterUpdates();
+
+    if (!(vm = testDomObjFromDomain(domain)))
+        return -1;
+
+    if (virDomainObjUpdateModificationImpact(vm, &flags) < 0)
+        goto cleanup;
+
+    if (testDomainAttachDeviceLiveAndConfig(vm, driver, xml, flags) < 0)
+        goto cleanup;
+
+    ret = 0;
+
+ cleanup:
+    virDomainObjEndAPI(&vm);
+    virNWFilterUnlockFilterUpdates();
+    return ret;
+
+}
+
+static int
+testDomainAttachDevice(virDomainPtr domain, const char *xml)
+{
+    return testDomainAttachDeviceFlags(domain, xml, 0);
+}
+
+/* search for a hostdev matching dev and detach it */
+static int
+testDomainDetachPrepHostdev(virDomainObj *vm,
+                            virDomainHostdevDef *match,
+                            virDomainHostdevDef **detach)
+{
+    virDomainHostdevSubsys *subsys = &match->source.subsys;
+    virDomainHostdevSubsysPCI *pcisrc = &subsys->u.pci;
+    virDomainHostdevDef *hostdev = NULL;
+
+    if (match->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("hot unplug is not supported for hostdev mode '%s'"),
+                       virDomainHostdevModeTypeToString(match->mode));
+        return -1;
+    }
+
+    if (virDomainHostdevFind(vm->def, match, &hostdev) < 0) {
+        if (subsys->type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
+            virReportError(VIR_ERR_DEVICE_MISSING,
+                           _("host pci device " VIR_PCI_DEVICE_ADDRESS_FMT
+                             " not found"),
+                           pcisrc->addr.domain, pcisrc->addr.bus,
+                           pcisrc->addr.slot, pcisrc->addr.function);
+        } else {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("unexpected hostdev type %d"), subsys->type);
+        }
+        return -1;
+    }
+
+    *detach = hostdev;
+
+    return 0;
+}
+
+static int
+testDomainRemoveHostDevice(testDriver *driver G_GNUC_UNUSED,
+                           virDomainObj *vm,
+                           virDomainHostdevDef *hostdev)
+{
+    virDomainNetDef *net = NULL;
+    size_t i;
+
+    VIR_DEBUG("Removing host device %s from domain %p %s",
+              hostdev->info->alias, vm, vm->def->name);
+
+    if (hostdev->parentnet) {
+        net = hostdev->parentnet;
+        for (i = 0; i < vm->def->nnets; i++) {
+            if (vm->def->nets[i] == hostdev->parentnet) {
+                virDomainNetRemove(vm->def, i);
+                break;
+            }
+        }
+    }
+
+    for (i = 0; i < vm->def->nhostdevs; i++) {
+        if (vm->def->hostdevs[i] == hostdev) {
+            virDomainHostdevRemove(vm->def, i);
+            break;
+        }
+    }
+
+    virDomainAuditHostdev(vm, hostdev, "detach", true);
+
+    virDomainHostdevDefFree(hostdev);
+
+    if (net) {
+        if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
+            g_autoptr(virConnect) conn = virGetConnectNetwork();
+            if (conn)
+                virDomainNetReleaseActualDevice(conn, vm->def, net);
+            else
+                VIR_WARN("Unable to release network device '%s'", NULLSTR(net->ifname));
+        }
+        virDomainNetDefFree(net);
+    }
+
+    return 0;
+}
+
+static int
+testDomainRemoveDevice(testDriver *driver,
+                       virDomainObj *vm,
+                       virDomainDeviceDef *dev)
+{
+    virDomainDeviceInfo *info;
+    virObjectEvent *event;
+    g_autofree char *alias = NULL;
+
+    /*
+     * save the alias to use when sending a DEVICE_REMOVED event after
+     * all other teardown is complete
+     */
+    if ((info = virDomainDeviceGetInfo(dev)))
+        alias = g_strdup(info->alias);
+    info = NULL;
+
+    if ((virDomainDeviceType)dev->type != VIR_DOMAIN_DEVICE_HOSTDEV) {
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+                       _("don't know how to remove a %s device"),
+                       virDomainDeviceTypeToString(dev->type));
+        goto out;
+    }
+
+    if (testDomainRemoveHostDevice(driver, vm, dev->data.hostdev) < 0)
+        return -1;
+
+out:
+    event = virDomainEventDeviceRemovedNewFromObj(vm, alias);
+    virObjectEventStateQueue(driver->eventState, event);
+
+    return 0;
+}
+
+static int
+testDomainDetachDeviceLive(virDomainObj *vm,
+                           virDomainDeviceDef *match,
+                           testDriver *driver)
+{
+    virDomainDeviceDef detach = { .type = match->type };
+    virDomainDeviceInfo *info = NULL;
+
+    if ((virDomainDeviceType)match->type != VIR_DOMAIN_DEVICE_HOSTDEV) {
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+                       _("live detach of device '%s' is not supported"),
+                       virDomainDeviceTypeToString(match->type));
+        return -1;
+    }
+
+    if (testDomainDetachPrepHostdev(vm, match->data.hostdev,
+                                    &detach.data.hostdev) < 0)
+        return -1;
+
+    /* "detach" now points to the actual device we want to detach */
+
+    if (!(info = virDomainDeviceGetInfo(&detach))) {
+        /*
+         * This should never happen, since all of the device types in
+         * the switch cases that end with a "break" instead of a
+         * return have a virDeviceInfo in them.
+         */
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("device of type '%s' has no device info"),
+                       virDomainDeviceTypeToString(detach.type));
+        return -1;
+    }
+
+    /* Make generic validation checks common to all device types */
+
+    if (!info->alias) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Cannot detach %s device with no alias"),
+                       virDomainDeviceTypeToString(detach.type));
+        return -1;
+    }
+
+    return testDomainRemoveDevice(driver, vm, &detach);
+}
+
+static int
+testDomainDetachDeviceAliasLiveAndConfig(testDriver *driver,
+                                         virDomainObj *vm,
+                                         const char *alias,
+                                         unsigned int flags)
+{
+    virDomainDef *def = NULL;
+    g_autoptr(virDomainDef) vmdef = NULL;
+
+    virCheckFlags(VIR_DOMAIN_AFFECT_LIVE, -1);
+
+    if (virDomainObjGetDefs(vm, flags, &def, NULL) < 0)
+        return -1;
+
+    if (def) {
+        virDomainDeviceDef dev;
+
+        if (virDomainDefFindDevice(def, alias, &dev, true) < 0)
+            return -1;
+
+        if (testDomainDetachDeviceLive(vm, &dev, driver) < 0)
+            return -1;
+    }
+
+    if (vmdef) {
+        if (virDomainDefSave(vmdef, driver->xmlopt, NULL) < 0)
+            return -1;
+        virDomainObjAssignDef(vm, &vmdef, false, NULL);
+    }
+
+    return 0;
+}
+
+static int
+testDomainDetachDeviceAlias(virDomainPtr dom,
+                            const char *alias,
+                            unsigned int flags)
+{
+    testDriver *driver = dom->conn->privateData;
+    virDomainObj *vm = NULL;
+    int ret = -1;
+
+    if (!(vm = testDomObjFromDomain(dom)))
+        return -1;
+
+    if (virDomainObjUpdateModificationImpact(vm, &flags) < 0)
+        goto cleanup;
+
+    if (testDomainDetachDeviceAliasLiveAndConfig(driver, vm, alias, flags) < 0)
+        goto cleanup;
+
+    ret = 0;
+
+ cleanup:
+    virDomainObjEndAPI(&vm);
+    return ret;
+}
 
 /*
  * Test driver
@@ -10058,6 +10438,9 @@ static virHypervisorDriver testHypervisorDriver = {
     .connectListDomains = testConnectListDomains, /* 0.1.1 */
     .connectNumOfDomains = testConnectNumOfDomains, /* 0.1.1 */
     .connectListAllDomains = testConnectListAllDomains, /* 0.9.13 */
+    .domainAttachDevice = testDomainAttachDevice, /* 9.10.0 */
+    .domainAttachDeviceFlags = testDomainAttachDeviceFlags, /* 9.10.0 */
+    .domainDetachDeviceAlias = testDomainDetachDeviceAlias, /* 9.10.0 */
     .domainCreateXML = testDomainCreateXML, /* 0.1.4 */
     .domainCreateXMLWithFiles = testDomainCreateXMLWithFiles, /* 5.7.0 */
     .domainLookupByID = testDomainLookupByID, /* 0.1.1 */
-- 
2.27.0
_______________________________________________
Devel mailing list -- devel@lists.libvirt.org
To unsubscribe send an email to devel-leave@lists.libvirt.org