RE: [PATCH V5] support for hotplug/hotunplug in test hypervisor

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



ping

> -----Original Message-----
> From: Thanos Makatos <thanos.makatos@xxxxxxxxxxx>
> Sent: Thursday, November 9, 2023 11:15 AM
> To: devel@xxxxxxxxxxxxxxxxx
> Cc: Thanos Makatos <thanos.makatos@xxxxxxxxxxx>
> Subject: [PATCH V5] support for hotplug/hotunplug in test hypervisor
> 
> Signed-off-by: Thanos Makatos <thanos.makatos@xxxxxxxxxxx>
> 
> ---
> 
> Changed since v4:
> * removed inadvertent calls to functions
> virNWFilterReadLockFilterUpdates/virNWFilterUnlockFilterUpdates
>   (original patch was based on v8.0.0)
> 
> ---
>  src/test/test_driver.c | 382 ++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 380 insertions(+), 2 deletions(-)
> 
> diff --git a/src/test/test_driver.c b/src/test/test_driver.c
> index e87d7cfd44..effb7f9880 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,9 +36,12 @@
>  #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 "snapshot_conf.h"
>  #include "virfdstream.h"
> @@ -50,6 +51,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 +10037,379 @@
> 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;
> +
> +    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);
> +    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 +10433,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@xxxxxxxxxxxxxxxxx
To unsubscribe send an email to devel-leave@xxxxxxxxxxxxxxxxx




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux