render / rpms / libvirt

Forked from rpms/libvirt 7 months ago
Clone
99cbc7
From ac1770917c5f6020ccb5b6247f3eeb9f50a67903 Mon Sep 17 00:00:00 2001
99cbc7
Message-Id: <ac1770917c5f6020ccb5b6247f3eeb9f50a67903@dist-git>
99cbc7
From: Laine Stump <laine@laine.org>
99cbc7
Date: Thu, 11 Apr 2019 15:14:53 -0400
99cbc7
Subject: [PATCH] qemu_hotplug: delay sending DEVICE_REMOVED event until after
99cbc7
 *all* teardown
99cbc7
99cbc7
The VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED event is sent after qemu has
99cbc7
responded to a device_del command with a DEVICE_DELETED event. Before
99cbc7
queuing the event, *some* of the final teardown of the device's
99cbc7
trappings in libvirt is done, but not *all* of it. As a result, an
99cbc7
application may receive and process the DEVICE_REMOVED event before
99cbc7
libvirt has really finished with it.
99cbc7
99cbc7
Usually this doesn't cause a problem, but it can - in the case of the
99cbc7
bug report referenced below, vdsm is assigning a PCI device to a guest
99cbc7
with managed='no', using livirt's virNodeDeviceDetachFlags() and
99cbc7
virNodeDeviceReAttach() APIs. Immediately after receiving a
99cbc7
DEVICE_REMOVED event from libvirt signalling that the device had been
99cbc7
successfully unplugged, vdsm would cal virNodeDeviceReAttach() to
99cbc7
unbind the device from vfio-pci and rebind it to the host driverm but
99cbc7
because the event was received before libvirt had completely finished
99cbc7
processing the removal, that device was still on the "activeDevs"
99cbc7
list, and so virNodeDeviceReAttach() failed.
99cbc7
99cbc7
Experimentation with additional debug logs proved that libvirt would
99cbc7
always end up dispatching the DEVICE_REMOVED event before it had
99cbc7
removed the device from activeDevs (with a *much* greater difference
99cbc7
with managed='yes', since in that case the re-binding of the device
99cbc7
occurred after queuing the device).
99cbc7
99cbc7
Although the case of hostdev devices is the most extreme (since there
99cbc7
is so much involved in tearing down the device), *all* device types
99cbc7
suffer from the same problem - the DEVICE_REMOVED event is queued very
99cbc7
early in the qemuDomainRemove*Device() function for all of them,
99cbc7
resulting in a possibility of any application receiving the event
99cbc7
before libvirt has really finished with the device.
99cbc7
99cbc7
The solution is to save the device's alias (which is the only piece of
99cbc7
info from the device object that is needed for the event) at the
99cbc7
beginning of processing the device removal, and then queue the event
99cbc7
as a final act before returning. Since all of the
99cbc7
qemuDomainRemove*Device() functions (except
99cbc7
qemuDomainRemoveChrDevice()) are now called exclusively from
99cbc7
qemuDomainRemoveDevice() (which selects which of the subordinates to
99cbc7
call in a switch statement based on the type of device), the shortest
99cbc7
route to a solution is to doing the saving of alias, and later
99cbc7
queueing of the event, in the higher level qemuDomainRemoveDevice(),
99cbc7
and just completely remove the event-related code from all the
99cbc7
subordinate functions.
99cbc7
99cbc7
The single exception to this, as mentioned before, is
99cbc7
qemuDomainRemoveChrDevice(), which is still called from somewhere
99cbc7
other than qemuDomainRemoveDevice() (and has a separate arg used to
99cbc7
trigger different behavior when the chr device has targetType ==
99cbc7
GUESTFWD), so it must keep its original behavior intact, and must be
99cbc7
treated differently by qemuDomainRemoveDevice() (similar to the way
99cbc7
that qemuDomainDetachDeviceLive() treats chr and lease devices
99cbc7
differently from all the others).
99cbc7
99cbc7
Signed-off-by: Laine Stump <laine@laine.org>
99cbc7
ACKed-by: Peter Krempa <pkrempa@redhat.com>
99cbc7
(cherry picked from commit 78b03a7770f1822458be3e0769538dfc92b34803)
99cbc7
99cbc7
Resolves: https://bugzilla.redhat.com/1658198
99cbc7
Signed-off-by: Laine Stump <laine@redhat.com>
99cbc7
99cbc7
Conflicts:
99cbc7
  src/qemu/qemu_hotplug.c:
99cbc7
    * some code around a removed event queuing had been
99cbc7
      moved into a helper function upstream.
99cbc7
99cbc7
    * upstream patch used VIR_AUTOFREE, which isn't in 4.5.0
99cbc7
99cbc7
Signed-off-by: Laine Stump <laine@laine.org>
99cbc7
Message-Id: <20190411191453.24055-42-laine@redhat.com>
99cbc7
Acked-by: Michal Privoznik <mprivozn@redhat.com>
99cbc7
---
99cbc7
 src/qemu/qemu_hotplug.c | 145 +++++++++++++++++++---------------------
99cbc7
 1 file changed, 70 insertions(+), 75 deletions(-)
99cbc7
99cbc7
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
99cbc7
index ff88a827dd..103d3e59a7 100644
99cbc7
--- a/src/qemu/qemu_hotplug.c
99cbc7
+++ b/src/qemu/qemu_hotplug.c
99cbc7
@@ -3905,7 +3905,6 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver,
99cbc7
 {
99cbc7
     qemuDomainStorageSourcePrivatePtr diskPriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src);
99cbc7
     virDomainDeviceDef dev;
99cbc7
-    virObjectEventPtr event;
99cbc7
     size_t i;
99cbc7
     const char *src = virDomainDiskGetSource(disk);
99cbc7
     qemuDomainObjPrivatePtr priv = vm->privateData;
99cbc7
@@ -3972,9 +3971,6 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver,
99cbc7
 
99cbc7
     virDomainAuditDisk(vm, disk->src, NULL, "detach", true);
99cbc7
 
99cbc7
-    event = virDomainEventDeviceRemovedNewFromObj(vm, disk->info.alias);
99cbc7
-    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
-
99cbc7
     if (prManaged && !prUsed)
99cbc7
         qemuProcessKillManagedPRDaemon(vm);
99cbc7
 
99cbc7
@@ -4003,19 +3999,14 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver,
99cbc7
 
99cbc7
 
99cbc7
 static int
99cbc7
-qemuDomainRemoveControllerDevice(virQEMUDriverPtr driver,
99cbc7
-                                 virDomainObjPtr vm,
99cbc7
+qemuDomainRemoveControllerDevice(virDomainObjPtr vm,
99cbc7
                                  virDomainControllerDefPtr controller)
99cbc7
 {
99cbc7
-    virObjectEventPtr event;
99cbc7
     size_t i;
99cbc7
 
99cbc7
     VIR_DEBUG("Removing controller %s from domain %p %s",
99cbc7
               controller->info.alias, vm, vm->def->name);
99cbc7
 
99cbc7
-    event = virDomainEventDeviceRemovedNewFromObj(vm, controller->info.alias);
99cbc7
-    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
-
99cbc7
     for (i = 0; i < vm->def->ncontrollers; i++) {
99cbc7
         if (vm->def->controllers[i] == controller) {
99cbc7
             virDomainControllerRemove(vm->def, i);
99cbc7
@@ -4037,7 +4028,6 @@ qemuDomainRemoveMemoryDevice(virQEMUDriverPtr driver,
99cbc7
     qemuDomainObjPrivatePtr priv = vm->privateData;
99cbc7
     unsigned long long oldmem = virDomainDefGetMemoryTotal(vm->def);
99cbc7
     unsigned long long newmem = oldmem - mem->size;
99cbc7
-    virObjectEventPtr event;
99cbc7
     char *backendAlias = NULL;
99cbc7
     int rc;
99cbc7
     int idx;
99cbc7
@@ -4059,9 +4049,6 @@ qemuDomainRemoveMemoryDevice(virQEMUDriverPtr driver,
99cbc7
     if (rc < 0)
99cbc7
         return -1;
99cbc7
 
99cbc7
-    event = virDomainEventDeviceRemovedNewFromObj(vm, mem->info.alias);
99cbc7
-    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
-
99cbc7
     if ((idx = virDomainMemoryFindByDef(vm->def, mem)) >= 0)
99cbc7
         virDomainMemoryRemove(vm->def, idx);
99cbc7
 
99cbc7
@@ -4141,7 +4128,6 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver,
99cbc7
 {
99cbc7
     virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
99cbc7
     virDomainNetDefPtr net = NULL;
99cbc7
-    virObjectEventPtr event;
99cbc7
     size_t i;
99cbc7
     int ret = -1;
99cbc7
     qemuDomainObjPrivatePtr priv = vm->privateData;
99cbc7
@@ -4185,9 +4171,6 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver,
99cbc7
             goto cleanup;
99cbc7
     }
99cbc7
 
99cbc7
-    event = virDomainEventDeviceRemovedNewFromObj(vm, hostdev->info->alias);
99cbc7
-    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
-
99cbc7
     if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET) {
99cbc7
         net = hostdev->parent.data.net;
99cbc7
 
99cbc7
@@ -4266,7 +4249,6 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver,
99cbc7
 {
99cbc7
     virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
99cbc7
     qemuDomainObjPrivatePtr priv = vm->privateData;
99cbc7
-    virObjectEventPtr event;
99cbc7
     char *hostnet_name = NULL;
99cbc7
     char *charDevAlias = NULL;
99cbc7
     size_t i;
99cbc7
@@ -4322,9 +4304,6 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver,
99cbc7
 
99cbc7
     virDomainAuditNet(vm, net, NULL, "detach", true);
99cbc7
 
99cbc7
-    event = virDomainEventDeviceRemovedNewFromObj(vm, net->info.alias);
99cbc7
-    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
-
99cbc7
     for (i = 0; i < vm->def->nnets; i++) {
99cbc7
         if (vm->def->nets[i] == net) {
99cbc7
             virDomainNetRemove(vm->def, i);
99cbc7
@@ -4408,11 +4387,16 @@ qemuDomainRemoveChrDevice(virQEMUDriverPtr driver,
99cbc7
     if (qemuDomainNamespaceTeardownChardev(vm, chr) < 0)
99cbc7
         VIR_WARN("Unable to remove chr device from /dev");
99cbc7
 
99cbc7
+    qemuDomainReleaseDeviceAddress(vm, &chr->info, NULL);
99cbc7
+    qemuDomainChrRemove(vm->def, chr);
99cbc7
+
99cbc7
+    /* The caller does not emit the event, so we must do it here. Note
99cbc7
+     * that the event should be reported only after all backend
99cbc7
+     * teardown is completed.
99cbc7
+     */
99cbc7
     event = virDomainEventDeviceRemovedNewFromObj(vm, chr->info.alias);
99cbc7
     virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
 
99cbc7
-    qemuDomainReleaseDeviceAddress(vm, &chr->info, NULL);
99cbc7
-    qemuDomainChrRemove(vm->def, chr);
99cbc7
     virDomainChrDefFree(chr);
99cbc7
     ret = 0;
99cbc7
 
99cbc7
@@ -4427,7 +4411,6 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver,
99cbc7
                           virDomainObjPtr vm,
99cbc7
                           virDomainRNGDefPtr rng)
99cbc7
 {
99cbc7
-    virObjectEventPtr event;
99cbc7
     char *charAlias = NULL;
99cbc7
     char *objAlias = NULL;
99cbc7
     qemuDomainObjPrivatePtr priv = vm->privateData;
99cbc7
@@ -4469,9 +4452,6 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver,
99cbc7
     if (qemuDomainNamespaceTeardownRNG(vm, rng) < 0)
99cbc7
         VIR_WARN("Unable to remove RNG device from /dev");
99cbc7
 
99cbc7
-    event = virDomainEventDeviceRemovedNewFromObj(vm, rng->info.alias);
99cbc7
-    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
-
99cbc7
     if ((idx = virDomainRNGFind(vm->def, rng)) >= 0)
99cbc7
         virDomainRNGRemove(vm->def, idx);
99cbc7
     qemuDomainReleaseDeviceAddress(vm, &rng->info, NULL);
99cbc7
@@ -4496,7 +4476,6 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver,
99cbc7
     char *charAlias = NULL;
99cbc7
     char *memAlias = NULL;
99cbc7
     qemuDomainObjPrivatePtr priv = vm->privateData;
99cbc7
-    virObjectEventPtr event = NULL;
99cbc7
 
99cbc7
     VIR_DEBUG("Removing shmem device %s from domain %p %s",
99cbc7
               shmem->info.alias, vm, vm->def->name);
99cbc7
@@ -4524,9 +4503,6 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver,
99cbc7
     if (rc < 0)
99cbc7
         goto cleanup;
99cbc7
 
99cbc7
-    event = virDomainEventDeviceRemovedNewFromObj(vm, shmem->info.alias);
99cbc7
-    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
-
99cbc7
     if ((idx = virDomainShmemDefFind(vm->def, shmem)) >= 0)
99cbc7
         virDomainShmemDefRemove(vm->def, idx);
99cbc7
     qemuDomainReleaseDeviceAddress(vm, &shmem->info, NULL);
99cbc7
@@ -4542,17 +4518,12 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver,
99cbc7
 
99cbc7
 
99cbc7
 static int
99cbc7
-qemuDomainRemoveWatchdog(virQEMUDriverPtr driver,
99cbc7
-                         virDomainObjPtr vm,
99cbc7
+qemuDomainRemoveWatchdog(virDomainObjPtr vm,
99cbc7
                          virDomainWatchdogDefPtr watchdog)
99cbc7
 {
99cbc7
-    virObjectEventPtr event = NULL;
99cbc7
-
99cbc7
     VIR_DEBUG("Removing watchdog %s from domain %p %s",
99cbc7
               watchdog->info.alias, vm, vm->def->name);
99cbc7
 
99cbc7
-    event = virDomainEventDeviceRemovedNewFromObj(vm, watchdog->info.alias);
99cbc7
-    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
     qemuDomainReleaseDeviceAddress(vm, &watchdog->info, NULL);
99cbc7
     virDomainWatchdogDefFree(vm->def->watchdog);
99cbc7
     vm->def->watchdog = NULL;
99cbc7
@@ -4564,16 +4535,11 @@ static int
99cbc7
 qemuDomainRemoveInputDevice(virDomainObjPtr vm,
99cbc7
                             virDomainInputDefPtr dev)
99cbc7
 {
99cbc7
-    qemuDomainObjPrivatePtr priv = vm->privateData;
99cbc7
-    virQEMUDriverPtr driver = priv->driver;
99cbc7
-    virObjectEventPtr event = NULL;
99cbc7
     size_t i;
99cbc7
 
99cbc7
     VIR_DEBUG("Removing input device %s from domain %p %s",
99cbc7
               dev->info.alias, vm, vm->def->name);
99cbc7
 
99cbc7
-    event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias);
99cbc7
-    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
     for (i = 0; i < vm->def->ninputs; i++) {
99cbc7
         if (vm->def->inputs[i] == dev)
99cbc7
             break;
99cbc7
@@ -4598,15 +4564,9 @@ static int
99cbc7
 qemuDomainRemoveVsockDevice(virDomainObjPtr vm,
99cbc7
                             virDomainVsockDefPtr dev)
99cbc7
 {
99cbc7
-    qemuDomainObjPrivatePtr priv = vm->privateData;
99cbc7
-    virQEMUDriverPtr driver = priv->driver;
99cbc7
-    virObjectEventPtr event = NULL;
99cbc7
-
99cbc7
     VIR_DEBUG("Removing vsock device %s from domain %p %s",
99cbc7
               dev->info.alias, vm, vm->def->name);
99cbc7
 
99cbc7
-    event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias);
99cbc7
-    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
     qemuDomainReleaseDeviceAddress(vm, &dev->info, NULL);
99cbc7
     virDomainVsockDefFree(vm->def->vsock);
99cbc7
     vm->def->vsock = NULL;
99cbc7
@@ -4620,7 +4580,6 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver,
99cbc7
                                virDomainRedirdevDefPtr dev)
99cbc7
 {
99cbc7
     qemuDomainObjPrivatePtr priv = vm->privateData;
99cbc7
-    virObjectEventPtr event;
99cbc7
     char *charAlias = NULL;
99cbc7
     ssize_t idx;
99cbc7
     int ret = -1;
99cbc7
@@ -4645,9 +4604,6 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver,
99cbc7
 
99cbc7
     virDomainAuditRedirdev(vm, dev, "detach", true);
99cbc7
 
99cbc7
-    event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias);
99cbc7
-    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
-
99cbc7
     if ((idx = virDomainRedirdevDefFind(vm->def, dev)) >= 0)
99cbc7
         virDomainRedirdevDefRemove(vm->def, idx);
99cbc7
     qemuDomainReleaseDeviceAddress(vm, &dev->info, NULL);
99cbc7
@@ -4730,50 +4686,81 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver,
99cbc7
                        virDomainObjPtr vm,
99cbc7
                        virDomainDeviceDefPtr dev)
99cbc7
 {
99cbc7
+    virDomainDeviceInfoPtr info;
99cbc7
+    virObjectEventPtr event;
99cbc7
+    char *alias = NULL;
99cbc7
     int ret = -1;
99cbc7
+
99cbc7
+    /*
99cbc7
+     * save the alias to use when sending a DEVICE_REMOVED event after
99cbc7
+     * all other teardown is complete
99cbc7
+     */
99cbc7
+    if ((info = virDomainDeviceGetInfo(dev)) &&
99cbc7
+        VIR_STRDUP(alias, info->alias) < 0) {
99cbc7
+        goto cleanup;;
99cbc7
+    }
99cbc7
+    info = NULL;
99cbc7
+
99cbc7
     switch ((virDomainDeviceType)dev->type) {
99cbc7
+    case VIR_DOMAIN_DEVICE_CHR:
99cbc7
+        /* We must return directly after calling
99cbc7
+         * qemuDomainRemoveChrDevice because it is called directly
99cbc7
+         * from other places, so it must be completely self-contained
99cbc7
+         * and can't take advantage of any common code at the end of
99cbc7
+         * qemuDomainRemoveDevice().
99cbc7
+         */
99cbc7
+        ret = qemuDomainRemoveChrDevice(driver, vm, dev->data.chr, true);
99cbc7
+        goto cleanup;
99cbc7
+
99cbc7
+        /*
99cbc7
+         * all of the following qemuDomainRemove*Device() functions
99cbc7
+         * are (and must be) only called from this function, so any
99cbc7
+         * code that is common to them all can be pulled out and put
99cbc7
+         * into this function.
99cbc7
+         */
99cbc7
     case VIR_DOMAIN_DEVICE_DISK:
99cbc7
-        ret = qemuDomainRemoveDiskDevice(driver, vm, dev->data.disk);
99cbc7
+        if (qemuDomainRemoveDiskDevice(driver, vm, dev->data.disk) < 0)
99cbc7
+            goto cleanup;
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_CONTROLLER:
99cbc7
-        ret = qemuDomainRemoveControllerDevice(driver, vm, dev->data.controller);
99cbc7
+        if (qemuDomainRemoveControllerDevice(vm, dev->data.controller) < 0)
99cbc7
+            goto cleanup;
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_NET:
99cbc7
-        ret = qemuDomainRemoveNetDevice(driver, vm, dev->data.net);
99cbc7
+        if (qemuDomainRemoveNetDevice(driver, vm, dev->data.net) < 0)
99cbc7
+            goto cleanup;
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_HOSTDEV:
99cbc7
-        ret = qemuDomainRemoveHostDevice(driver, vm, dev->data.hostdev);
99cbc7
-        break;
99cbc7
-
99cbc7
-    case VIR_DOMAIN_DEVICE_CHR:
99cbc7
-        ret = qemuDomainRemoveChrDevice(driver, vm, dev->data.chr, true);
99cbc7
+        if (qemuDomainRemoveHostDevice(driver, vm, dev->data.hostdev) < 0)
99cbc7
+            goto cleanup;
99cbc7
         break;
99cbc7
     case VIR_DOMAIN_DEVICE_RNG:
99cbc7
-        ret = qemuDomainRemoveRNGDevice(driver, vm, dev->data.rng);
99cbc7
+        if (qemuDomainRemoveRNGDevice(driver, vm, dev->data.rng) < 0)
99cbc7
+            goto cleanup;
99cbc7
         break;
99cbc7
-
99cbc7
     case VIR_DOMAIN_DEVICE_MEMORY:
99cbc7
-        ret = qemuDomainRemoveMemoryDevice(driver, vm, dev->data.memory);
99cbc7
+        if (qemuDomainRemoveMemoryDevice(driver, vm, dev->data.memory) < 0)
99cbc7
+            goto cleanup;
99cbc7
         break;
99cbc7
-
99cbc7
     case VIR_DOMAIN_DEVICE_SHMEM:
99cbc7
-        ret = qemuDomainRemoveShmemDevice(driver, vm, dev->data.shmem);
99cbc7
+        if (qemuDomainRemoveShmemDevice(driver, vm, dev->data.shmem) < 0)
99cbc7
+            goto cleanup;
99cbc7
         break;
99cbc7
-
99cbc7
     case VIR_DOMAIN_DEVICE_INPUT:
99cbc7
-        ret = qemuDomainRemoveInputDevice(vm, dev->data.input);
99cbc7
+        if (qemuDomainRemoveInputDevice(vm, dev->data.input) < 0)
99cbc7
+            goto cleanup;
99cbc7
         break;
99cbc7
-
99cbc7
     case VIR_DOMAIN_DEVICE_REDIRDEV:
99cbc7
-        ret = qemuDomainRemoveRedirdevDevice(driver, vm, dev->data.redirdev);
99cbc7
+        if (qemuDomainRemoveRedirdevDevice(driver, vm, dev->data.redirdev) < 0)
99cbc7
+            goto cleanup;
99cbc7
         break;
99cbc7
-
99cbc7
     case VIR_DOMAIN_DEVICE_WATCHDOG:
99cbc7
-        ret = qemuDomainRemoveWatchdog(driver, vm, dev->data.watchdog);
99cbc7
+        if (qemuDomainRemoveWatchdog(vm, dev->data.watchdog) < 0)
99cbc7
+            goto cleanup;
99cbc7
         break;
99cbc7
-
99cbc7
     case VIR_DOMAIN_DEVICE_VSOCK:
99cbc7
-        ret = qemuDomainRemoveVsockDevice(vm, dev->data.vsock);
99cbc7
+        if (qemuDomainRemoveVsockDevice(vm, dev->data.vsock) < 0)
99cbc7
+            goto cleanup;
99cbc7
         break;
99cbc7
 
99cbc7
     case VIR_DOMAIN_DEVICE_NONE:
99cbc7
@@ -4793,8 +4780,16 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver,
99cbc7
         virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
99cbc7
                        _("don't know how to remove a %s device"),
99cbc7
                        virDomainDeviceTypeToString(dev->type));
99cbc7
-        break;
99cbc7
+        goto cleanup;
99cbc7
     }
99cbc7
+
99cbc7
+    event = virDomainEventDeviceRemovedNewFromObj(vm, alias);
99cbc7
+    virObjectEventStateQueue(driver->domainEventState, event);
99cbc7
+
99cbc7
+    ret = 0;
99cbc7
+
99cbc7
+ cleanup:
99cbc7
+    VIR_FREE(alias);
99cbc7
     return ret;
99cbc7
 }
99cbc7
 
99cbc7
-- 
99cbc7
2.21.0
99cbc7