render / rpms / libvirt

Forked from rpms/libvirt 9 months ago
Clone
99cbc7
From d16f6dc87b5122c6d14ffb52a7276aef8f14049c Mon Sep 17 00:00:00 2001
99cbc7
Message-Id: <d16f6dc87b5122c6d14ffb52a7276aef8f14049c@dist-git>
99cbc7
From: Laine Stump <laine@laine.org>
99cbc7
Date: Thu, 11 Apr 2019 15:14:51 -0400
99cbc7
Subject: [PATCH] qemu_hotplug: consolidate all common detach code in
99cbc7
 qemuDomainDetachDeviceLive
99cbc7
99cbc7
Now that all the qemuDomainDetachPrep*() functions look nearly
99cbc7
identical at the end, we can put one copy of that identical code in
99cbc7
qemuDomainDetachDeviceLive() at the point after the individual prep
99cbc7
functions have been called, and remove the duplicated code from all
99cbc7
the prep functions. The code to locate the target "detach" device
99cbc7
based on the "match" device remains, as do all device-type-specific
99cbc7
validations.
99cbc7
99cbc7
Unfortunately there are a few things going on at once in this patch,
99cbc7
which makes it a bit more difficult to follow than the others; it was
99cbc7
just impossible to do the changes in stages and still have a
99cbc7
buildable/testable tree at each step.
99cbc7
99cbc7
The other changes of note:
99cbc7
99cbc7
* The individual prep functions no longer need their driver or async
99cbc7
  args, so those are removed, as are the local "ret" variables, since
99cbc7
  in all cases the functions just directly return -1 or 0.
99cbc7
99cbc7
* Some of the prep functions were checking for a valid alias and/or
99cbc7
  for attempts to detach a multifunction PCI device, but not all. In
99cbc7
  fact, both checks are valid (or at least harmless) for *all* device
99cbc7
  types, so they are removed from the prep functions, and done a
99cbc7
  single time in the common function.
99cbc7
99cbc7
  (any attempts to *create* an alias when there isn't one has been
99cbc7
  removed, since that is doomed to failure anyway; the only way the
99cbc7
  device wouldn't have an alias is if 1) the domain was created by
99cbc7
  calling virsh qemu-attach to attach an existing qemu process to
99cbc7
  libvirt, and 2) the qemu command that started said process used "old
99cbc7
  style" arguments for creating devices that didn't have any device
99cbc7
  ids. Even if we constructed a device id for one of these devices,
99cbc7
  qemu wouldn't recognize it in the device_del command anyway, so we
99cbc7
  may as well fail earlier with "device missing alias" rather than
99cbc7
  failing later with "couldn't delete device net0".)
99cbc7
99cbc7
* Only one type of device has shutdown code that must not be called
99cbc7
  until after *all* validation of the device is done (including
99cbc7
  checking for multifunction PCI and valid alias, which is done in the
99cbc7
  toplevel common code). For this reason, the Net function has been
99cbc7
  split in two, with the 2nd half (qemuDomainDetachShutdownNet())
99cbc7
  called from the common function, right before sending the delete
99cbc7
  command to qemu.
99cbc7
99cbc7
Signed-off-by: Laine Stump <laine@laine.org>
99cbc7
ACKed-by: Peter Krempa <pkrempa@redhat.com>
99cbc7
(cherry picked from commit dd60bd62d3ad60b564168f56b05f4c9354af4bd3)
99cbc7
99cbc7
Partially-Resolves: https://bugzilla.redhat.com/1658198
99cbc7
Signed-off-by: Laine Stump <laine@redhat.com>
99cbc7
Signed-off-by: Laine Stump <laine@laine.org>
99cbc7
Message-Id: <20190411191453.24055-40-laine@redhat.com>
99cbc7
Acked-by: Michal Privoznik <mprivozn@redhat.com>
99cbc7
---
99cbc7
 src/qemu/qemu_hotplug.c | 480 ++++++++++++----------------------------
99cbc7
 1 file changed, 142 insertions(+), 338 deletions(-)
99cbc7
99cbc7
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
99cbc7
index 390fc36cf6..27d09d173b 100644
99cbc7
--- a/src/qemu/qemu_hotplug.c
99cbc7
+++ b/src/qemu/qemu_hotplug.c
99cbc7
@@ -4650,7 +4650,7 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver,
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
-static void ATTRIBUTE_UNUSED
99cbc7
+static void
99cbc7
 qemuDomainRemoveAuditDevice(virDomainObjPtr vm,
99cbc7
                             virDomainDeviceDefPtr detach,
99cbc7
                             bool success)
99cbc7
@@ -4902,15 +4902,12 @@ qemuFindDisk(virDomainDefPtr def, const char *dst)
99cbc7
 }
99cbc7
 
99cbc7
 static int
99cbc7
-qemuDomainDetachPrepDisk(virQEMUDriverPtr driver,
99cbc7
-                         virDomainObjPtr vm,
99cbc7
+qemuDomainDetachPrepDisk(virDomainObjPtr vm,
99cbc7
                          virDomainDiskDefPtr match,
99cbc7
-                         virDomainDiskDefPtr *detach,
99cbc7
-                         bool async)
99cbc7
+                         virDomainDiskDefPtr *detach)
99cbc7
 {
99cbc7
     virDomainDiskDefPtr disk;
99cbc7
     int idx;
99cbc7
-    int ret = -1;
99cbc7
 
99cbc7
     if ((idx = qemuFindDisk(vm->def, match->dst)) < 0) {
99cbc7
         virReportError(VIR_ERR_OPERATION_FAILED,
99cbc7
@@ -4962,34 +4959,7 @@ qemuDomainDetachPrepDisk(virQEMUDriverPtr driver,
99cbc7
     if (qemuDomainDiskBlockJobIsActive(disk))
99cbc7
         return -1;
99cbc7
 
99cbc7
-    if (disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO &&
99cbc7
-        qemuIsMultiFunctionDevice(vm->def, &disk->info)) {
99cbc7
-        virReportError(VIR_ERR_OPERATION_FAILED,
99cbc7
-                       _("cannot hot unplug multifunction PCI device: %s"),
99cbc7
-                       disk->dst);
99cbc7
-        return -1;
99cbc7
-    }
99cbc7
-
99cbc7
-    if (!async)
99cbc7
-        qemuDomainMarkDeviceForRemoval(vm, &disk->info);
99cbc7
-
99cbc7
-    if (qemuDomainDeleteDevice(vm, disk->info.alias) < 0) {
99cbc7
-        if (virDomainObjIsActive(vm))
99cbc7
-            virDomainAuditDisk(vm, disk->src, NULL, "detach", false);
99cbc7
-        goto cleanup;
99cbc7
-    }
99cbc7
-
99cbc7
-    if (async) {
99cbc7
-        ret = 0;
99cbc7
-    } else {
99cbc7
-        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
-            ret = qemuDomainRemoveDiskDevice(driver, vm, disk);
99cbc7
-    }
99cbc7
-
99cbc7
- cleanup:
99cbc7
-    if (!async)
99cbc7
-        qemuDomainResetDeviceRemoval(vm);
99cbc7
-    return ret;
99cbc7
+    return 0;
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
@@ -5054,13 +5024,11 @@ static bool qemuDomainControllerIsBusy(virDomainObjPtr vm,
99cbc7
 }
99cbc7
 
99cbc7
 static int
99cbc7
-qemuDomainDetachPrepController(virQEMUDriverPtr driver,
99cbc7
-                               virDomainObjPtr vm,
99cbc7
+qemuDomainDetachPrepController(virDomainObjPtr vm,
99cbc7
                                virDomainControllerDefPtr match,
99cbc7
-                               virDomainControllerDefPtr *detach,
99cbc7
-                               bool async)
99cbc7
+                               virDomainControllerDefPtr *detach)
99cbc7
 {
99cbc7
-    int idx, ret = -1;
99cbc7
+    int idx;
99cbc7
     virDomainControllerDefPtr controller = NULL;
99cbc7
 
99cbc7
     if (match->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI) {
99cbc7
@@ -5075,50 +5043,26 @@ qemuDomainDetachPrepController(virQEMUDriverPtr driver,
99cbc7
                        _("controller %s:%d not found"),
99cbc7
                        virDomainControllerTypeToString(match->type),
99cbc7
                        match->idx);
99cbc7
-        goto cleanup;
99cbc7
+        return -1;
99cbc7
     }
99cbc7
 
99cbc7
     *detach = controller = vm->def->controllers[idx];
99cbc7
 
99cbc7
-    if (qemuIsMultiFunctionDevice(vm->def, &controller->info)) {
99cbc7
-        virReportError(VIR_ERR_OPERATION_FAILED,
99cbc7
-                       "%s", _("cannot hot unplug multifunction PCI device"));
99cbc7
-        goto cleanup;
99cbc7
-    }
99cbc7
-
99cbc7
     if (qemuDomainControllerIsBusy(vm, controller)) {
99cbc7
         virReportError(VIR_ERR_OPERATION_FAILED, "%s",
99cbc7
                        _("device cannot be detached: device is busy"));
99cbc7
-        goto cleanup;
99cbc7
+        return -1;
99cbc7
     }
99cbc7
 
99cbc7
-    if (!async)
99cbc7
-        qemuDomainMarkDeviceForRemoval(vm, &controller->info);
99cbc7
-
99cbc7
-    if (qemuDomainDeleteDevice(vm, controller->info.alias) < 0)
99cbc7
-        goto cleanup;
99cbc7
-
99cbc7
-    if (async) {
99cbc7
-        ret = 0;
99cbc7
-    } else {
99cbc7
-        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
-            ret = qemuDomainRemoveControllerDevice(driver, vm, controller);
99cbc7
-    }
99cbc7
-
99cbc7
- cleanup:
99cbc7
-    if (!async)
99cbc7
-        qemuDomainResetDeviceRemoval(vm);
99cbc7
-    return ret;
99cbc7
+    return 0;
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
 /* search for a hostdev matching dev and detach it */
99cbc7
 static int
99cbc7
-qemuDomainDetachPrepHostdev(virQEMUDriverPtr driver,
99cbc7
-                            virDomainObjPtr vm,
99cbc7
+qemuDomainDetachPrepHostdev(virDomainObjPtr vm,
99cbc7
                             virDomainHostdevDefPtr match,
99cbc7
-                            virDomainHostdevDefPtr *detach,
99cbc7
-                            bool async)
99cbc7
+                            virDomainHostdevDefPtr *detach)
99cbc7
 {
99cbc7
     virDomainHostdevSubsysPtr subsys = &match->source.subsys;
99cbc7
     virDomainHostdevSubsysUSBPtr usbsrc = &subsys->u.usb;
99cbc7
@@ -5127,7 +5071,6 @@ qemuDomainDetachPrepHostdev(virQEMUDriverPtr driver,
99cbc7
     virDomainHostdevSubsysMediatedDevPtr mdevsrc = &subsys->u.mdev;
99cbc7
     virDomainHostdevDefPtr hostdev = NULL;
99cbc7
     int idx;
99cbc7
-    int ret = -1;
99cbc7
 
99cbc7
     if (match->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
99cbc7
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
99cbc7
@@ -5190,54 +5133,15 @@ qemuDomainDetachPrepHostdev(virQEMUDriverPtr driver,
99cbc7
         return -1;
99cbc7
     }
99cbc7
 
99cbc7
-    if (qemuIsMultiFunctionDevice(vm->def, hostdev->info)) {
99cbc7
-        virReportError(VIR_ERR_OPERATION_FAILED,
99cbc7
-                       _("cannot hot unplug multifunction PCI device with guest address: "
99cbc7
-                         "%.4x:%.2x:%.2x.%.1x"),
99cbc7
-                       hostdev->info->addr.pci.domain, hostdev->info->addr.pci.bus,
99cbc7
-                       hostdev->info->addr.pci.slot, hostdev->info->addr.pci.function);
99cbc7
-        return -1;
99cbc7
-    }
99cbc7
-
99cbc7
-    if (!hostdev->info->alias) {
99cbc7
-        virReportError(VIR_ERR_OPERATION_FAILED,
99cbc7
-                       "%s", _("device cannot be detached without a device alias"));
99cbc7
-        return -1;
99cbc7
-    }
99cbc7
-
99cbc7
-    if (!async)
99cbc7
-        qemuDomainMarkDeviceForRemoval(vm, hostdev->info);
99cbc7
-
99cbc7
-    if (qemuDomainDeleteDevice(vm, hostdev->info->alias) < 0) {
99cbc7
-        if (virDomainObjIsActive(vm))
99cbc7
-            virDomainAuditHostdev(vm, hostdev, "detach", false);
99cbc7
-        goto cleanup;
99cbc7
-    }
99cbc7
-
99cbc7
-    if (async) {
99cbc7
-        ret = 0;
99cbc7
-    } else {
99cbc7
-        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
-            ret = qemuDomainRemoveHostDevice(driver, vm, hostdev);
99cbc7
-    }
99cbc7
-
99cbc7
- cleanup:
99cbc7
-    if (!async)
99cbc7
-        qemuDomainResetDeviceRemoval(vm);
99cbc7
-
99cbc7
-    return ret;
99cbc7
-
99cbc7
+    return 0;
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
 static int
99cbc7
-qemuDomainDetachPrepShmem(virQEMUDriverPtr driver,
99cbc7
-                          virDomainObjPtr vm,
99cbc7
+qemuDomainDetachPrepShmem(virDomainObjPtr vm,
99cbc7
                           virDomainShmemDefPtr match,
99cbc7
-                          virDomainShmemDefPtr *detach,
99cbc7
-                          bool async)
99cbc7
+                          virDomainShmemDefPtr *detach)
99cbc7
 {
99cbc7
-    int ret = -1;
99cbc7
     ssize_t idx = -1;
99cbc7
     virDomainShmemDefPtr shmem = NULL;
99cbc7
 
99cbc7
@@ -5265,34 +5169,15 @@ qemuDomainDetachPrepShmem(virQEMUDriverPtr driver,
99cbc7
         return -1;
99cbc7
     }
99cbc7
 
99cbc7
-    if (!async)
99cbc7
-        qemuDomainMarkDeviceForRemoval(vm, &shmem->info);
99cbc7
-
99cbc7
-    if (qemuDomainDeleteDevice(vm, shmem->info.alias) < 0)
99cbc7
-        goto cleanup;
99cbc7
-
99cbc7
-    if (async) {
99cbc7
-        ret = 0;
99cbc7
-    } else {
99cbc7
-        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
-            ret = qemuDomainRemoveShmemDevice(driver, vm, shmem);
99cbc7
-    }
99cbc7
-
99cbc7
- cleanup:
99cbc7
-    if (!async)
99cbc7
-        qemuDomainResetDeviceRemoval(vm);
99cbc7
-    return ret;
99cbc7
+    return 0;
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
 static int
99cbc7
-qemuDomainDetachPrepWatchdog(virQEMUDriverPtr driver,
99cbc7
-                             virDomainObjPtr vm,
99cbc7
+qemuDomainDetachPrepWatchdog(virDomainObjPtr vm,
99cbc7
                              virDomainWatchdogDefPtr match,
99cbc7
-                             virDomainWatchdogDefPtr *detach,
99cbc7
-                             bool async)
99cbc7
+                             virDomainWatchdogDefPtr *detach)
99cbc7
 {
99cbc7
-    int ret = -1;
99cbc7
     virDomainWatchdogDefPtr watchdog;
99cbc7
 
99cbc7
     *detach = watchdog = vm->def->watchdog;
99cbc7
@@ -5323,34 +5208,15 @@ qemuDomainDetachPrepWatchdog(virQEMUDriverPtr driver,
99cbc7
         return -1;
99cbc7
     }
99cbc7
 
99cbc7
-    if (!async)
99cbc7
-        qemuDomainMarkDeviceForRemoval(vm, &watchdog->info);
99cbc7
-
99cbc7
-    if (qemuDomainDeleteDevice(vm, watchdog->info.alias) < 0)
99cbc7
-        goto cleanup;
99cbc7
-
99cbc7
-    if (async) {
99cbc7
-        ret = 0;
99cbc7
-    } else {
99cbc7
-        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
-            ret = qemuDomainRemoveWatchdog(driver, vm, watchdog);
99cbc7
-    }
99cbc7
-
99cbc7
- cleanup:
99cbc7
-    if (!async)
99cbc7
-        qemuDomainResetDeviceRemoval(vm);
99cbc7
-    return ret;
99cbc7
+    return 0;
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
 static int
99cbc7
-qemuDomainDetachPrepRedirdev(virQEMUDriverPtr driver,
99cbc7
-                             virDomainObjPtr vm,
99cbc7
+qemuDomainDetachPrepRedirdev(virDomainObjPtr vm,
99cbc7
                              virDomainRedirdevDefPtr match,
99cbc7
-                             virDomainRedirdevDefPtr *detach,
99cbc7
-                             bool async)
99cbc7
+                             virDomainRedirdevDefPtr *detach)
99cbc7
 {
99cbc7
-    int ret = -1;
99cbc7
     virDomainRedirdevDefPtr redirdev;
99cbc7
     ssize_t idx;
99cbc7
 
99cbc7
@@ -5362,59 +5228,41 @@ qemuDomainDetachPrepRedirdev(virQEMUDriverPtr driver,
99cbc7
 
99cbc7
     *detach = redirdev = vm->def->redirdevs[idx];
99cbc7
 
99cbc7
-    if (!redirdev->info.alias) {
99cbc7
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
99cbc7
-                       _("alias not set for redirdev device"));
99cbc7
-        return -1;
99cbc7
-    }
99cbc7
-
99cbc7
-    if (!async)
99cbc7
-        qemuDomainMarkDeviceForRemoval(vm, &redirdev->info);
99cbc7
-
99cbc7
-    if (qemuDomainDeleteDevice(vm, redirdev->info.alias) < 0)
99cbc7
-        goto cleanup;
99cbc7
-
99cbc7
-    if (async) {
99cbc7
-        ret = 0;
99cbc7
-    } else {
99cbc7
-        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
-            ret = qemuDomainRemoveRedirdevDevice(driver, vm, redirdev);
99cbc7
-    }
99cbc7
-
99cbc7
- cleanup:
99cbc7
-    if (!async)
99cbc7
-        qemuDomainResetDeviceRemoval(vm);
99cbc7
-    return ret;
99cbc7
+    return 0;
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
 static int
99cbc7
-qemuDomainDetachPrepNet(virQEMUDriverPtr driver,
99cbc7
-                        virDomainObjPtr vm,
99cbc7
+qemuDomainDetachPrepNet(virDomainObjPtr vm,
99cbc7
                         virDomainNetDefPtr match,
99cbc7
-                        virDomainNetDefPtr *detach,
99cbc7
-                        bool async)
99cbc7
+                        virDomainNetDefPtr *detach)
99cbc7
 {
99cbc7
-    int detachidx, ret = -1;
99cbc7
+    int detachidx;
99cbc7
     virDomainNetDefPtr net = NULL;
99cbc7
 
99cbc7
     if ((detachidx = virDomainNetFindIdx(vm->def, match)) < 0)
99cbc7
-        goto cleanup;
99cbc7
+        return -1;
99cbc7
 
99cbc7
     *detach = net = vm->def->nets[detachidx];
99cbc7
 
99cbc7
-    if (qemuIsMultiFunctionDevice(vm->def, &net->info)) {
99cbc7
-        virReportError(VIR_ERR_OPERATION_FAILED,
99cbc7
-                       _("cannot hot unplug multifunction PCI device: %s"),
99cbc7
-                       net->ifname);
99cbc7
-        goto cleanup;
99cbc7
-    }
99cbc7
+    return 0;
99cbc7
+}
99cbc7
 
99cbc7
-    if (!net->info.alias) {
99cbc7
-        if (qemuAssignDeviceNetAlias(vm->def, net, -1) < 0)
99cbc7
-            goto cleanup;
99cbc7
-    }
99cbc7
 
99cbc7
+static void
99cbc7
+qemuDomainDetachShutdownNet(virDomainNetDefPtr net)
99cbc7
+{
99cbc7
+/*
99cbc7
+ * These operations are in a separate function from
99cbc7
+ * qemuDomainDetachPrepNet() because they can't be done until after
99cbc7
+ * we've validated that this device really can be removed - in
99cbc7
+ * particular we need to check for multifunction PCI devices and
99cbc7
+ * presence of a device alias, which isn't done until *after* the
99cbc7
+ * return from qemuDomainDetachPrepNet(). Since we've already passed
99cbc7
+ * the "point of no return", we ignore any errors, and trudge ahead
99cbc7
+ * with shutting down and detaching the device even if there is an
99cbc7
+ * error in one of these functions.
99cbc7
+ */
99cbc7
     if (virDomainNetGetActualBandwidth(net) &&
99cbc7
         virNetDevSupportBandwidth(virDomainNetGetActualType(net)) &&
99cbc7
         virNetDevBandwidthClear(net->ifname) < 0)
99cbc7
@@ -5426,32 +5274,6 @@ qemuDomainDetachPrepNet(virQEMUDriverPtr driver,
99cbc7
      * the parent device offline)
99cbc7
      */
99cbc7
     ignore_value(qemuInterfaceStopDevice(net));
99cbc7
-
99cbc7
-    if (!async)
99cbc7
-        qemuDomainMarkDeviceForRemoval(vm, &net->info);
99cbc7
-
99cbc7
-    if (qemuDomainDeleteDevice(vm, net->info.alias) < 0) {
99cbc7
-        if (virDomainObjIsActive(vm)) {
99cbc7
-            /* the audit message has a different format for hostdev network devices */
99cbc7
-            if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_HOSTDEV)
99cbc7
-                virDomainAuditHostdev(vm, virDomainNetGetActualHostdev(net), "detach", false);
99cbc7
-            else
99cbc7
-                virDomainAuditNet(vm, net, NULL, "detach", false);
99cbc7
-        }
99cbc7
-        goto cleanup;
99cbc7
-    }
99cbc7
-
99cbc7
-    if (async) {
99cbc7
-        ret = 0;
99cbc7
-    } else {
99cbc7
-        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
-            ret = qemuDomainRemoveNetDevice(driver, vm, net);
99cbc7
-    }
99cbc7
-
99cbc7
- cleanup:
99cbc7
-    if (!async)
99cbc7
-        qemuDomainResetDeviceRemoval(vm);
99cbc7
-    return ret;
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
@@ -5514,15 +5336,12 @@ qemuDomainDetachDeviceChr(virQEMUDriverPtr driver,
99cbc7
 
99cbc7
 
99cbc7
 static int
99cbc7
-qemuDomainDetachPrepRNG(virQEMUDriverPtr driver,
99cbc7
-                        virDomainObjPtr vm,
99cbc7
+qemuDomainDetachPrepRNG(virDomainObjPtr vm,
99cbc7
                         virDomainRNGDefPtr match,
99cbc7
-                        virDomainRNGDefPtr *detach,
99cbc7
-                        bool async)
99cbc7
+                        virDomainRNGDefPtr *detach)
99cbc7
 {
99cbc7
     ssize_t idx;
99cbc7
     virDomainRNGDefPtr rng;
99cbc7
-    int ret = -1;
99cbc7
 
99cbc7
     if ((idx = virDomainRNGFind(vm->def, match)) < 0) {
99cbc7
         virReportError(VIR_ERR_DEVICE_MISSING,
99cbc7
@@ -5534,42 +5353,17 @@ qemuDomainDetachPrepRNG(virQEMUDriverPtr driver,
99cbc7
 
99cbc7
     *detach = rng = vm->def->rngs[idx];
99cbc7
 
99cbc7
-    if (!rng->info.alias) {
99cbc7
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
99cbc7
-                       _("alias not set for RNG device"));
99cbc7
-        return -1;
99cbc7
-    }
99cbc7
-
99cbc7
-    if (!async)
99cbc7
-        qemuDomainMarkDeviceForRemoval(vm, &rng->info);
99cbc7
-
99cbc7
-    if (qemuDomainDeleteDevice(vm, rng->info.alias) < 0)
99cbc7
-        goto cleanup;
99cbc7
-
99cbc7
-    if (async) {
99cbc7
-        ret = 0;
99cbc7
-    } else {
99cbc7
-        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
-            ret = qemuDomainRemoveRNGDevice(driver, vm, rng);
99cbc7
-    }
99cbc7
-
99cbc7
- cleanup:
99cbc7
-    if (!async)
99cbc7
-        qemuDomainResetDeviceRemoval(vm);
99cbc7
-    return ret;
99cbc7
+    return 0;
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
 static int
99cbc7
-qemuDomainDetachPrepMemory(virQEMUDriverPtr driver,
99cbc7
-                           virDomainObjPtr vm,
99cbc7
+qemuDomainDetachPrepMemory(virDomainObjPtr vm,
99cbc7
                            virDomainMemoryDefPtr match,
99cbc7
-                           virDomainMemoryDefPtr *detach,
99cbc7
-                           bool async)
99cbc7
+                           virDomainMemoryDefPtr *detach)
99cbc7
 {
99cbc7
     virDomainMemoryDefPtr mem;
99cbc7
     int idx;
99cbc7
-    int ret = -1;
99cbc7
 
99cbc7
     qemuDomainMemoryDeviceAlignSize(vm->def, match);
99cbc7
 
99cbc7
@@ -5583,40 +5377,16 @@ qemuDomainDetachPrepMemory(virQEMUDriverPtr driver,
99cbc7
 
99cbc7
     *detach = mem = vm->def->mems[idx];
99cbc7
 
99cbc7
-    if (!mem->info.alias) {
99cbc7
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
99cbc7
-                       _("alias for the memory device was not found"));
99cbc7
-        return -1;
99cbc7
-    }
99cbc7
-
99cbc7
-    if (!async)
99cbc7
-        qemuDomainMarkDeviceForRemoval(vm, &mem->info);
99cbc7
-
99cbc7
-    if (qemuDomainDeleteDevice(vm, mem->info.alias) < 0)
99cbc7
-        goto cleanup;
99cbc7
-
99cbc7
-    if (async) {
99cbc7
-        ret = 0;
99cbc7
-    } else {
99cbc7
-        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
-            ret = qemuDomainRemoveMemoryDevice(driver, vm, mem);
99cbc7
-    }
99cbc7
-
99cbc7
- cleanup:
99cbc7
-    if (!async)
99cbc7
-        qemuDomainResetDeviceRemoval(vm);
99cbc7
-    return ret;
99cbc7
+    return 0;
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
 static int
99cbc7
 qemuDomainDetachPrepInput(virDomainObjPtr vm,
99cbc7
                           virDomainInputDefPtr match,
99cbc7
-                          virDomainInputDefPtr *detach,
99cbc7
-                          bool async)
99cbc7
+                          virDomainInputDefPtr *detach)
99cbc7
 {
99cbc7
     virDomainInputDefPtr input;
99cbc7
-    int ret = -1;
99cbc7
     int idx;
99cbc7
 
99cbc7
     if ((idx = virDomainInputDefFind(vm->def, match)) < 0) {
99cbc7
@@ -5641,35 +5411,16 @@ qemuDomainDetachPrepInput(virDomainObjPtr vm,
99cbc7
         break;
99cbc7
     }
99cbc7
 
99cbc7
-    if (!async)
99cbc7
-        qemuDomainMarkDeviceForRemoval(vm, &input->info);
99cbc7
-
99cbc7
-    if (qemuDomainDeleteDevice(vm, input->info.alias) < 0)
99cbc7
-        goto cleanup;
99cbc7
-
99cbc7
-    if (async) {
99cbc7
-        ret = 0;
99cbc7
-    } else {
99cbc7
-        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
-            ret = qemuDomainRemoveInputDevice(vm, input);
99cbc7
-    }
99cbc7
-
99cbc7
- cleanup:
99cbc7
-    if (!async)
99cbc7
-        qemuDomainResetDeviceRemoval(vm);
99cbc7
-    return ret;
99cbc7
+    return 0;
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
 static int
99cbc7
 qemuDomainDetachPrepVsock(virDomainObjPtr vm,
99cbc7
                           virDomainVsockDefPtr match,
99cbc7
-                          virDomainVsockDefPtr *detach,
99cbc7
-                          bool async)
99cbc7
+                          virDomainVsockDefPtr *detach)
99cbc7
 {
99cbc7
     virDomainVsockDefPtr vsock;
99cbc7
-    int ret = -1;
99cbc7
-
99cbc7
 
99cbc7
     *detach = vsock = vm->def->vsock;
99cbc7
     if (!vsock ||
99cbc7
@@ -5679,23 +5430,7 @@ qemuDomainDetachPrepVsock(virDomainObjPtr vm,
99cbc7
         return -1;
99cbc7
     }
99cbc7
 
99cbc7
-    if (!async)
99cbc7
-        qemuDomainMarkDeviceForRemoval(vm, &vsock->info);
99cbc7
-
99cbc7
-    if (qemuDomainDeleteDevice(vm, vsock->info.alias) < 0)
99cbc7
-        goto cleanup;
99cbc7
-
99cbc7
-    if (async) {
99cbc7
-        ret = 0;
99cbc7
-    } else {
99cbc7
-        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
-            ret = qemuDomainRemoveVsockDevice(vm, vsock);
99cbc7
-    }
99cbc7
-
99cbc7
- cleanup:
99cbc7
-    if (!async)
99cbc7
-        qemuDomainResetDeviceRemoval(vm);
99cbc7
-    return ret;
99cbc7
+    return 0;
99cbc7
 }
99cbc7
 
99cbc7
 
99cbc7
@@ -5730,6 +5465,7 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm,
99cbc7
                            bool async)
99cbc7
 {
99cbc7
     virDomainDeviceDef detach = { .type = match->type };
99cbc7
+    virDomainDeviceInfoPtr info = NULL;
99cbc7
     int ret = -1;
99cbc7
 
99cbc7
     switch ((virDomainDeviceType)match->type) {
99cbc7
@@ -5752,68 +5488,68 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm,
99cbc7
          * assure it is okay to detach the device.
99cbc7
          */
99cbc7
     case VIR_DOMAIN_DEVICE_DISK:
99cbc7
-        if (qemuDomainDetachPrepDisk(driver, vm, match->data.disk,
99cbc7
-                                     &detach.data.disk, async) < 0) {
99cbc7
+        if (qemuDomainDetachPrepDisk(vm, match->data.disk,
99cbc7
+                                     &detach.data.disk) < 0) {
99cbc7
             return -1;
99cbc7
         }
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_CONTROLLER:
99cbc7
-        if (qemuDomainDetachPrepController(driver, vm, match->data.controller,
99cbc7
-                                           &detach.data.controller, async) < 0) {
99cbc7
+        if (qemuDomainDetachPrepController(vm, match->data.controller,
99cbc7
+                                           &detach.data.controller) < 0) {
99cbc7
             return -1;
99cbc7
         }
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_NET:
99cbc7
-        if (qemuDomainDetachPrepNet(driver, vm, match->data.net,
99cbc7
-                                    &detach.data.net, async) < 0) {
99cbc7
+        if (qemuDomainDetachPrepNet(vm, match->data.net,
99cbc7
+                                    &detach.data.net) < 0) {
99cbc7
             return -1;
99cbc7
         }
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_HOSTDEV:
99cbc7
-        if (qemuDomainDetachPrepHostdev(driver, vm, match->data.hostdev,
99cbc7
-                                        &detach.data.hostdev, async) < 0) {
99cbc7
+        if (qemuDomainDetachPrepHostdev(vm, match->data.hostdev,
99cbc7
+                                        &detach.data.hostdev) < 0) {
99cbc7
             return -1;
99cbc7
         }
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_RNG:
99cbc7
-        if (qemuDomainDetachPrepRNG(driver, vm, match->data.rng,
99cbc7
-                                    &detach.data.rng, async) < 0) {
99cbc7
+        if (qemuDomainDetachPrepRNG(vm, match->data.rng,
99cbc7
+                                    &detach.data.rng) < 0) {
99cbc7
             return -1;
99cbc7
         }
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_MEMORY:
99cbc7
-        if (qemuDomainDetachPrepMemory(driver, vm, match->data.memory,
99cbc7
-                                       &detach.data.memory, async) < 0) {
99cbc7
+        if (qemuDomainDetachPrepMemory(vm, match->data.memory,
99cbc7
+                                       &detach.data.memory) < 0) {
99cbc7
             return -1;
99cbc7
         }
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_SHMEM:
99cbc7
-        if (qemuDomainDetachPrepShmem(driver, vm, match->data.shmem,
99cbc7
-                                      &detach.data.shmem, async) < 0) {
99cbc7
+        if (qemuDomainDetachPrepShmem(vm, match->data.shmem,
99cbc7
+                                      &detach.data.shmem) < 0) {
99cbc7
             return -1;
99cbc7
         }
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_WATCHDOG:
99cbc7
-        if (qemuDomainDetachPrepWatchdog(driver, vm, match->data.watchdog,
99cbc7
-                                         &detach.data.watchdog, async) < 0) {
99cbc7
+        if (qemuDomainDetachPrepWatchdog(vm, match->data.watchdog,
99cbc7
+                                         &detach.data.watchdog) < 0) {
99cbc7
             return -1;
99cbc7
         }
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_INPUT:
99cbc7
         if (qemuDomainDetachPrepInput(vm, match->data.input,
99cbc7
-                                      &detach.data.input, async) < 0) {
99cbc7
+                                      &detach.data.input) < 0) {
99cbc7
             return -1;
99cbc7
         }
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_REDIRDEV:
99cbc7
-        if (qemuDomainDetachPrepRedirdev(driver, vm, match->data.redirdev,
99cbc7
-                                         &detach.data.redirdev, async) < 0) {
99cbc7
+        if (qemuDomainDetachPrepRedirdev(vm, match->data.redirdev,
99cbc7
+                                         &detach.data.redirdev) < 0) {
99cbc7
             return -1;
99cbc7
         }
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_VSOCK:
99cbc7
         if (qemuDomainDetachPrepVsock(vm, match->data.vsock,
99cbc7
-                                      &detach.data.vsock, async) < 0) {
99cbc7
+                                      &detach.data.vsock) < 0) {
99cbc7
             return -1;
99cbc7
         }
99cbc7
         break;
99cbc7
@@ -5837,7 +5573,75 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm,
99cbc7
         return -1;
99cbc7
     }
99cbc7
 
99cbc7
-    ret = 0;
99cbc7
+    /* "detach" now points to the actual device we want to detach */
99cbc7
+
99cbc7
+    if (!(info = virDomainDeviceGetInfo(&detach))) {
99cbc7
+        /*
99cbc7
+         * This should never happen, since all of the device types in
99cbc7
+         * the switch cases that end with a "break" instead of a
99cbc7
+         * return have a virDeviceInfo in them.
99cbc7
+         */
99cbc7
+        virReportError(VIR_ERR_INTERNAL_ERROR,
99cbc7
+                       _("device of type '%s' has no device info"),
99cbc7
+                       virDomainDeviceTypeToString(detach.type));
99cbc7
+        return -1;
99cbc7
+    }
99cbc7
+
99cbc7
+
99cbc7
+    /* Make generic validation checks common to all device types */
99cbc7
+
99cbc7
+    if (!info->alias) {
99cbc7
+        virReportError(VIR_ERR_INTERNAL_ERROR,
99cbc7
+                       _("Cannot detach %s device with no alias"),
99cbc7
+                       virDomainDeviceTypeToString(detach.type));
99cbc7
+        return -1;
99cbc7
+    }
99cbc7
+
99cbc7
+    if (qemuIsMultiFunctionDevice(vm->def, info)) {
99cbc7
+        virReportError(VIR_ERR_OPERATION_FAILED,
99cbc7
+                       _("cannot hot unplug %s device with multifunction PCI guest address: "
99cbc7
+                         "%.4x:%.2x:%.2x.%.1x"),
99cbc7
+                       virDomainDeviceTypeToString(detach.type),
99cbc7
+                       info->addr.pci.domain, info->addr.pci.bus,
99cbc7
+                       info->addr.pci.slot, info->addr.pci.function);
99cbc7
+        return -1;
99cbc7
+    }
99cbc7
+
99cbc7
+
99cbc7
+    /*
99cbc7
+     * Do any device-specific shutdown that should be
99cbc7
+     * done after all validation checks, but before issuing the qemu
99cbc7
+     * command to delete the device. For now, the only type of device
99cbc7
+     * that has such shutdown needs is the net device.
99cbc7
+     */
99cbc7
+    if (detach.type == VIR_DOMAIN_DEVICE_NET)
99cbc7
+        qemuDomainDetachShutdownNet(detach.data.net);
99cbc7
+
99cbc7
+
99cbc7
+    /*
99cbc7
+     * Issue the qemu monitor command to delete the device (based on
99cbc7
+     * its alias), and optionally wait a short time in case the
99cbc7
+     * DEVICE_DELETED event arrives from qemu right away.
99cbc7
+     */
99cbc7
+    if (!async)
99cbc7
+        qemuDomainMarkDeviceForRemoval(vm, info);
99cbc7
+
99cbc7
+    if (qemuDomainDeleteDevice(vm, info->alias) < 0) {
99cbc7
+        if (virDomainObjIsActive(vm))
99cbc7
+            qemuDomainRemoveAuditDevice(vm, &detach, false);
99cbc7
+        goto cleanup;
99cbc7
+    }
99cbc7
+
99cbc7
+    if (async) {
99cbc7
+        ret = 0;
99cbc7
+    } else {
99cbc7
+        if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1)
99cbc7
+            ret = qemuDomainRemoveDevice(driver, vm, &detach);
99cbc7
+    }
99cbc7
+
99cbc7
+ cleanup:
99cbc7
+    if (!async)
99cbc7
+        qemuDomainResetDeviceRemoval(vm);
99cbc7
 
99cbc7
     return ret;
99cbc7
 }
99cbc7
-- 
99cbc7
2.21.0
99cbc7