[libvirt] [PATCH 00/21] qemu_hotplug: refactor device detach functions to fix one serious and other minor bugs

This all started with the observation that we were sending the DEVICE_REMOVED events for unplugged devices too soon (reported in https://bugzilla.redhat.com/1658198 ). While noticing that *all* of the device types send their DEVICE_REMOVED event too early, I also noticed how eerily similar all the different detach functions are (they have converged over time, as legacy code supporting old qemus with different methods of deleting different types of devices has been removed - pkrempa and mprivozn committed patches just in the last week that contributed to this convergence and removed some duplicated code). I also saw a few other definite bugs, as well as a couple that I think are bugs, but am not enough informed about zPCI to be certain (I've Cc'ed the author and reviewers of the original patch adding that code to the patches so that they can verify, or negate, my suspicions). Many/most of these patches are either very simple / easily verifiable code movement / removal / renaming of variables. Patch 20 is a bit more involved; viewing the new versions of the functions along with the patch would probably help it go much quicker. The first two patches in the series eliminate what I'm 99% certain is erroneous calling of the qemuDomainDetachExtensionDevice() when detaching a PCI device on S390. The commit log details my reasons for believing this code should be removed. The rest of the series eliminates small differences iteratively, arriving at a set of detach functions which are completely identical at the end (and only called from one place), and then replaces all those duplicates with a single block of common code in a higher level function that calls all of the individual functions (it was their only caller anyway). This eliminated the inconsistent application of checking for multifunction PCI devices and for the existence of a device alias. It also eliminated individual calls to qemuDomainRemove*Device() calls, and replaced them with a single call to qemuDomainRemoveDevice(). Eliminating all the calls (except one) to the individual qemuDomainRemove*Device() functions in turn made it possible to move the code that builds and queues the DEVICE_REMOVED event into a single place in the one remaining higher level caller - qemuDomainRemoveDevice() - thus eliminating the "premature event" for *all* device types at once. Laine Stump (21): qemu_hotplug: remove erroneous call to qemuDomainDetachExtensionDevice() qemu_hotplug: remove another erroneous qemuDomainDetachExtensionDevice() call qemu_hotplug: remove unnecessary check for valid PCI address qemu_hotplug: rename a virDomainDeviceInfoPtr to avoid confusion qemu_hotplug: eliminate multiple identical qemuDomainDetachHost*Device() functions qemu_hotplug: eliminate unnecessary call to qemuDomainDetachNetDevice() qemu_hotplug: refactor qemuDomainDetachDiskLive and qemuDomainDetachDiskDevice qemu_hotplug: don't call DetachThisHostDevice for hostdev network devices qemu_hotplug: merge qemuDomainDetachThisHostDevice into qemuDomainDetachHostDevice qemu_hotplug: move qemuDomainChangeGraphicsPasswords() qemu_hotplug: move (almost) all qemuDomainDetach*() functions together qemu_hotplug: move (Attach|Detach)Lease functions with others of same type qemu_hotplug: move qemuDomainDetachDeviceLive() to qemu_hotplug.c qemu_hotplug: remove extra function in middle of DetachController call chain qemu_hotplug: pull qemuDomainUpdateDeviceList out of qemuDomainDetachDeviceLive test: replace calls to individual detach functions with one call to main detach qemu_hotplug: rename dev to match in qemuDomainDetachDeviceLive qemu_hotplug: standardize the names/args/calling of qemuDomainDetach*() qemu_hotplug: new function qemuDomainRemoveAuditDevice() qemu_hotplug: consolidate all common detach code in qemuDomainDetachDeviceLive qemu_hotplug: delay sending DEVICE_REMOVED event until after *all* teardown src/qemu/qemu_driver.c | 102 +-- src/qemu/qemu_hotplug.c | 1407 ++++++++++++++++++--------------------- src/qemu/qemu_hotplug.h | 56 +- tests/qemuhotplugtest.c | 8 +- 4 files changed, 655 insertions(+), 918 deletions(-) Yay! jtomko would be proud - more deletions than insertions!!! -- 2.20.1

qemuDomainDetachControllerDevice() calls qemuDomainDetachExtensionDevice() when the *controller* type is PCI. This is incorrect in multiple ways: * Any code that tears down a device should be in the qemuDomainRemove*Device() function (which is called after libvirt gets a DEVICE_DELETED event from qemu indicating that the guest is finished with the device on its end. The qemuDomainDetach*Device() functions should only contain code that ensures the requested operation is valid, and sends the command to qemu to initiate the unplug. * qemuDomainDetachExtensionDevice() is a function that applies to devices that plug into a PCI slot, *not* necessarily PCI controllers (which is what's being checked in the offending code). The proper way to check for this would be to see if the DeviceInfo for the controller device had a PCI address, not to check if the controller is a PCI controller (the code being removed was doing the latter). * According to commit 1d1e264f1 that added this code (and other support for hotplugging zPCI devices on s390), it's not necessary to explicitly detach the zPCI device when unplugging a PCI device. To quote: There's no need to implement hot unplug for zPCI as QEMU implements an unplug callback which will unplug both PCI and zPCI device in a cascaded way. and the evidence bears this out - all the other uses of qemuDomainDetachExtensionDevice() (except one, which I believe is also in error, and is being removed in a separate patch) are only to remove the zPCI extension device in cases where it was successfully added, but there was some other failure later in the hotplug process (so there was no regular PCI device to remove and trigger removal of the zPCI extension device). * PCI controllers are not hot pluggable, so this is dead code anyway. (The only controllers that can currently be hotplugged/unplugged are SCSI controllers). Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 41d60277d1..18075dc48e 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5457,7 +5457,6 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, { int idx, ret = -1; virDomainControllerDefPtr detach = NULL; - qemuDomainObjPrivatePtr priv = vm->privateData; if ((idx = virDomainControllerFind(vm->def, dev->data.controller->type, @@ -5503,17 +5502,6 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, if (!async) qemuDomainMarkDeviceForRemoval(vm, &detach->info); - if (detach->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) { - int rc; - qemuDomainObjEnterMonitor(driver, vm); - rc = qemuDomainDetachExtensionDevice(priv->mon, &detach->info); - if (qemuDomainObjExitMonitor(driver, vm) < 0) - rc = -1; - - if (rc < 0) - goto cleanup; - } - if (qemuDomainDeleteDevice(vm, detach->info.alias) < 0) goto cleanup; -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:41 -0400, Laine Stump wrote:
qemuDomainDetachControllerDevice() calls qemuDomainDetachExtensionDevice() when the *controller* type is PCI. This is incorrect in multiple ways:
* Any code that tears down a device should be in the qemuDomainRemove*Device() function (which is called after libvirt gets a DEVICE_DELETED event from qemu indicating that the guest is finished with the device on its end. The qemuDomainDetach*Device() functions should only contain code that ensures the requested operation is valid, and sends the command to qemu to initiate the unplug.
* qemuDomainDetachExtensionDevice() is a function that applies to devices that plug into a PCI slot, *not* necessarily PCI controllers (which is what's being checked in the offending code). The proper way to check for this would be to see if the DeviceInfo for the controller device had a PCI address, not to check if the controller is a PCI controller (the code being removed was doing the latter).
* According to commit 1d1e264f1 that added this code (and other support for hotplugging zPCI devices on s390), it's not necessary to explicitly detach the zPCI device when unplugging a PCI device. To quote:
There's no need to implement hot unplug for zPCI as QEMU implements an unplug callback which will unplug both PCI and zPCI device in a cascaded way.
and the evidence bears this out - all the other uses of qemuDomainDetachExtensionDevice() (except one, which I believe is also in error, and is being removed in a separate patch) are only to remove the zPCI extension device in cases where it was successfully added, but there was some other failure later in the hotplug process (so there was no regular PCI device to remove and trigger removal of the zPCI extension device).
* PCI controllers are not hot pluggable, so this is dead code anyway. (The only controllers that can currently be hotplugged/unplugged are SCSI controllers).
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 12 ------------ 1 file changed, 12 deletions(-)
The code change looks okay to me, but I know nothing of zPCI. ACKing this just because I don't care about zPCI would be wrong :)

On Thu, Mar 21, 2019 at 06:28:41PM -0400, Laine Stump wrote:
qemuDomainDetachControllerDevice() calls qemuDomainDetachExtensionDevice() when the *controller* type is PCI. This is incorrect in multiple ways:
* Any code that tears down a device should be in the qemuDomainRemove*Device() function (which is called after libvirt gets a DEVICE_DELETED event from qemu indicating that the guest is finished with the device on its end. The qemuDomainDetach*Device() functions should only contain code that ensures the requested operation is valid, and sends the command to qemu to initiate the unplug.
* qemuDomainDetachExtensionDevice() is a function that applies to devices that plug into a PCI slot, *not* necessarily PCI controllers (which is what's being checked in the offending code). The proper way to check for this would be to see if the DeviceInfo for the controller device had a PCI address, not to check if the controller is a PCI controller (the code being removed was doing the latter).
* According to commit 1d1e264f1 that added this code (and other support for hotplugging zPCI devices on s390), it's not necessary to explicitly detach the zPCI device when unplugging a PCI device. To quote:
There's no need to implement hot unplug for zPCI as QEMU implements an unplug callback which will unplug both PCI and zPCI device in a cascaded way.
and the evidence bears this out - all the other uses of qemuDomainDetachExtensionDevice() (except one, which I believe is also in error, and is being removed in a separate patch) are only to remove the zPCI extension device in cases where it was successfully added, but there was some other failure later in the hotplug process (so there was no regular PCI device to remove and trigger removal of the zPCI extension device).
* PCI controllers are not hot pluggable, so this is dead code
You would have had me convinced at 'dead code'
anyway. (The only controllers that can currently be hotplugged/unplugged are SCSI controllers).
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 12 ------------ 1 file changed, 12 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

I agree. This must have slipped in during the countless iterations. Thanks for catching it. Reviewed-by: Boris Fiuczynski <fiuczy@linux.ibm.com> On 3/21/19 11:28 PM, Laine Stump wrote:
qemuDomainDetachControllerDevice() calls qemuDomainDetachExtensionDevice() when the *controller* type is PCI. This is incorrect in multiple ways:
* Any code that tears down a device should be in the qemuDomainRemove*Device() function (which is called after libvirt gets a DEVICE_DELETED event from qemu indicating that the guest is finished with the device on its end. The qemuDomainDetach*Device() functions should only contain code that ensures the requested operation is valid, and sends the command to qemu to initiate the unplug.
* qemuDomainDetachExtensionDevice() is a function that applies to devices that plug into a PCI slot, *not* necessarily PCI controllers (which is what's being checked in the offending code). The proper way to check for this would be to see if the DeviceInfo for the controller device had a PCI address, not to check if the controller is a PCI controller (the code being removed was doing the latter).
* According to commit 1d1e264f1 that added this code (and other support for hotplugging zPCI devices on s390), it's not necessary to explicitly detach the zPCI device when unplugging a PCI device. To quote:
There's no need to implement hot unplug for zPCI as QEMU implements an unplug callback which will unplug both PCI and zPCI device in a cascaded way.
and the evidence bears this out - all the other uses of qemuDomainDetachExtensionDevice() (except one, which I believe is also in error, and is being removed in a separate patch) are only to remove the zPCI extension device in cases where it was successfully added, but there was some other failure later in the hotplug process (so there was no regular PCI device to remove and trigger removal of the zPCI extension device).
* PCI controllers are not hot pluggable, so this is dead code anyway. (The only controllers that can currently be hotplugged/unplugged are SCSI controllers).
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 12 ------------ 1 file changed, 12 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 41d60277d1..18075dc48e 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5457,7 +5457,6 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, { int idx, ret = -1; virDomainControllerDefPtr detach = NULL; - qemuDomainObjPrivatePtr priv = vm->privateData;
if ((idx = virDomainControllerFind(vm->def, dev->data.controller->type, @@ -5503,17 +5502,6 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, if (!async) qemuDomainMarkDeviceForRemoval(vm, &detach->info);
- if (detach->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) { - int rc; - qemuDomainObjEnterMonitor(driver, vm); - rc = qemuDomainDetachExtensionDevice(priv->mon, &detach->info); - if (qemuDomainObjExitMonitor(driver, vm) < 0) - rc = -1; - - if (rc < 0) - goto cleanup; - } - if (qemuDomainDeleteDevice(vm, detach->info.alias) < 0) goto cleanup;
-- Mit freundlichen Grüßen/Kind regards Boris Fiuczynski IBM Deutschland Research & Development GmbH Vorsitzender des Aufsichtsrats: Matthias Hartmann Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294

qemuDomainRemoveRNGDevice() calls qemuDomainDetachExtensionDevice(). According to commit 1d1e264f1 that added this code, it should not be necessary to explicitly remove the zPCI extension device for a PCI device during unplug, because "QEMU implements an unplug callback which will unplug both PCI and zPCI device in a cascaded way". In fact, no other devices call qemuDomainDetachExtensionDevice() during their qemuDomainRemove*Device() function, so it should be removed from qemuDomainRemoveRNGDevice as well. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 18075dc48e..65ec5187e6 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4884,9 +4884,6 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver, qemuDomainObjEnterMonitor(driver, vm); - if (qemuDomainDetachExtensionDevice(priv->mon, &rng->info) < 0) - rc = -1; - if (rc == 0 && qemuMonitorDelObject(priv->mon, objAlias) < 0) rc = -1; -- 2.20.1

On Thu, Mar 21, 2019 at 06:28:42PM -0400, Laine Stump wrote:
qemuDomainRemoveRNGDevice() calls qemuDomainDetachExtensionDevice(). According to commit 1d1e264f1 that added this code, it should not be necessary to explicitly remove the zPCI extension device for a PCI device during unplug, because "QEMU implements an unplug callback which will unplug both PCI and zPCI device in a cascaded way". In fact, no other devices call qemuDomainDetachExtensionDevice() during their qemuDomainRemove*Device() function, so it should be removed from qemuDomainRemoveRNGDevice as well.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 3 --- 1 file changed, 3 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

I agree. Reviewed-by: Boris Fiuczynski <fiuczy@linux.ibm.com> On 3/21/19 11:28 PM, Laine Stump wrote:
qemuDomainRemoveRNGDevice() calls qemuDomainDetachExtensionDevice(). According to commit 1d1e264f1 that added this code, it should not be necessary to explicitly remove the zPCI extension device for a PCI device during unplug, because "QEMU implements an unplug callback which will unplug both PCI and zPCI device in a cascaded way". In fact, no other devices call qemuDomainDetachExtensionDevice() during their qemuDomainRemove*Device() function, so it should be removed from qemuDomainRemoveRNGDevice as well.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 18075dc48e..65ec5187e6 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4884,9 +4884,6 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver,
qemuDomainObjEnterMonitor(driver, vm);
- if (qemuDomainDetachExtensionDevice(priv->mon, &rng->info) < 0) - rc = -1; - if (rc == 0 && qemuMonitorDelObject(priv->mon, objAlias) < 0) rc = -1;
-- Mit freundlichen Grüßen/Kind regards Boris Fiuczynski IBM Deutschland Research & Development GmbH Vorsitzender des Aufsichtsrats: Matthias Hartmann Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294

When support for hotplug/unplug of SCSI controllers was added way back in December 2009 (commit da9d937b), unplug was handled by calling the now-extinct function qemuMonitorRemovePCIDevice(), which required a PCI address as an argument. At the same time, the idea of every device in the config having a PCI address apparentlty was not yet fully implemented, because the author of the patch including a check for a valid PCI address in the device object. These days, all PCI devices are guaranteed to have a valid PCI address. But more important than that, we no longer detach devices by PCI address, but instead use qemuDomainDeleteDevice(), which identifies the device by its alias. So checking for a valid PCI address is just pointless extra code that obscures the high level of similarity between all the individual qemuDomainDetach*Device() functions. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 65ec5187e6..f2aa3cc048 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5467,22 +5467,6 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, detach = vm->def->controllers[idx]; - if (detach->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && - detach->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW && - detach->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("device with '%s' address cannot be detached"), - virDomainDeviceAddressTypeToString(detach->info.type)); - goto cleanup; - } - - if (!virDomainDeviceAddressIsValid(&detach->info, detach->info.type)) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("device with invalid '%s' address cannot be detached"), - virDomainDeviceAddressTypeToString(detach->info.type)); - goto cleanup; - } - if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device: %s"), -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:43 -0400, Laine Stump wrote:
When support for hotplug/unplug of SCSI controllers was added way back in December 2009 (commit da9d937b), unplug was handled by calling the now-extinct function qemuMonitorRemovePCIDevice(), which required a PCI address as an argument. At the same time, the idea of every device in the config having a PCI address apparentlty was not yet fully implemented, because the author of the patch including a check for a valid PCI address in the device object.
These days, all PCI devices are guaranteed to have a valid PCI address. But more important than that, we no longer detach devices by PCI address, but instead use qemuDomainDeleteDevice(), which identifies the device by its alias. So checking for a valid PCI address is just pointless extra code that obscures the high level of similarity between all the individual qemuDomainDetach*Device() functions.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 16 ---------------- 1 file changed, 16 deletions(-)
ACK

On Thu, Mar 21, 2019 at 06:28:43PM -0400, Laine Stump wrote:
When support for hotplug/unplug of SCSI controllers was added way back in December 2009 (commit da9d937b), unplug was handled by calling the now-extinct function qemuMonitorRemovePCIDevice(), which required a PCI address as an argument. At the same time, the idea of every device in the config having a PCI address apparentlty was not yet fully
apparently
implemented, because the author of the patch including a check for a valid PCI address in the device object.
These days, all PCI devices are guaranteed to have a valid PCI address. But more important than that, we no longer detach devices by PCI address, but instead use qemuDomainDeleteDevice(), which identifies the device by its alias. So checking for a valid PCI address is just pointless extra code that obscures the high level of similarity between all the individual qemuDomainDetach*Device() functions.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 16 ---------------- 1 file changed, 16 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Having an InfoPtr named "dev" made my brain hurt. Renaming it to "info" gives one less thing to confuse when looking at the code. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index f2aa3cc048..701458b2cd 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4380,12 +4380,12 @@ static int qemuComparePCIDevice(virDomainDefPtr def ATTRIBUTE_UNUSED, } static bool qemuIsMultiFunctionDevice(virDomainDefPtr def, - virDomainDeviceInfoPtr dev) + virDomainDeviceInfoPtr info) { - if (dev->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) + if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) return false; - if (virDomainDeviceInfoIterate(def, qemuComparePCIDevice, dev) < 0) + if (virDomainDeviceInfoIterate(def, qemuComparePCIDevice, info) < 0) return true; return false; } -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:44 -0400, Laine Stump wrote:
Having an InfoPtr named "dev" made my brain hurt. Renaming it to "info" gives one less thing to confuse when looking at the code.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
ACK

There are separate Detach functions for PCI, USB, SCSI, Vhost, and Mediated hostdevs, but the functions are all 100% the same code, except that the PCI function checks for the guest side of the device being a PCI Multifunction device, while the other 4 check that the device's alias != NULL. The check for multifunction PCI devices should be done for *all* devices that are connected to the PCI bus in the guest, not just PCI hostdevs, and qemuIsMultiFunctionDevice() conveniently returns false if the queried device doesn't connect with PCI, so it is safe to make this check for all hostdev devices. (It also needs to be done for many other device types, but that will be addressed in a future patch). Likewise, since all hostdevs are detached by calling qemuDomainDeleteDevice(), which requires the device's alias, checking for a valid alias is a reasonable thing for PCI hostdevs too (NB: you'd think that we could rely on every device having a valid alias, but unfortunately when you run virsh qemu-attach on a qemu process that was started with some "old style" device args, they don't include an "id" option on the commandline, so there is no alias in the device object that's created). Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 129 +++++++++------------------------------- 1 file changed, 28 insertions(+), 101 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 701458b2cd..389d16b090 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5499,124 +5499,42 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, return ret; } -static int -qemuDomainDetachHostPCIDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - virDomainHostdevSubsysPCIPtr pcisrc = &detach->source.subsys.u.pci; - - if (qemuIsMultiFunctionDevice(vm->def, detach->info)) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("cannot hot unplug multifunction PCI device: %.4x:%.2x:%.2x.%.1x"), - pcisrc->addr.domain, pcisrc->addr.bus, - pcisrc->addr.slot, pcisrc->addr.function); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -} static int -qemuDomainDetachHostUSBDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - if (!detach->info->alias) { - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -} - -static int -qemuDomainDetachHostSCSIDevice(virDomainObjPtr vm, +qemuDomainDetachThisHostDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, virDomainHostdevDefPtr detach, bool async) { - if (!detach->info->alias) { - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); + int ret = -1; - return qemuDomainDeleteDevice(vm, detach->info->alias); -} + if (qemuAssignDeviceHostdevAlias(vm->def, &detach->info->alias, -1) < 0) + return -1; -static int -qemuDomainDetachSCSIVHostDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - if (!detach->info->alias) { + if (qemuIsMultiFunctionDevice(vm->def, detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); + _("cannot hot unplug multifunction PCI device with guest address: " + "%.4x:%.2x:%.2x.%.1x"), + detach->info->addr.pci.domain, detach->info->addr.pci.bus, + detach->info->addr.pci.slot, detach->info->addr.pci.function); return -1; } - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -} - - -static int -qemuDomainDetachMediatedDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ if (!detach->info->alias) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("device cannot be detached without a device alias")); + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("device cannot be detached without a device alias")); return -1; } - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -} - - -static int -qemuDomainDetachThisHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - int ret = -1; - - if (qemuAssignDeviceHostdevAlias(vm->def, &detach->info->alias, -1) < 0) - return -1; - switch (detach->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: - ret = qemuDomainDetachHostPCIDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - ret = qemuDomainDetachHostUSBDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: - ret = qemuDomainDetachHostSCSIDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: - ret = qemuDomainDetachSCSIVHostDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: - ret = qemuDomainDetachMediatedDevice(vm, detach, async); - break; + /* we support detach of all these types of hostdev */ + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hot unplug is not supported for hostdev subsys type '%s'"), @@ -5624,14 +5542,23 @@ qemuDomainDetachThisHostDevice(virQEMUDriverPtr driver, return -1; } - if (ret < 0) { + if (!async) + qemuDomainMarkDeviceForRemoval(vm, detach->info); + + if (qemuDomainDeleteDevice(vm, detach->info->alias) < 0) { if (virDomainObjIsActive(vm)) virDomainAuditHostdev(vm, detach, "detach", false); - } else if (!async && - (ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) { - ret = qemuDomainRemoveHostDevice(driver, vm, detach); + goto cleanup; } + if (async) { + ret = 0; + } else { + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) + ret = qemuDomainRemoveHostDevice(driver, vm, detach); + } + + cleanup: if (!async) qemuDomainResetDeviceRemoval(vm); -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:45 -0400, Laine Stump wrote:
There are separate Detach functions for PCI, USB, SCSI, Vhost, and Mediated hostdevs, but the functions are all 100% the same code, except that the PCI function checks for the guest side of the device being a PCI Multifunction device, while the other 4 check that the device's alias != NULL.
The check for multifunction PCI devices should be done for *all* devices that are connected to the PCI bus in the guest, not just PCI hostdevs, and qemuIsMultiFunctionDevice() conveniently returns false if the queried device doesn't connect with PCI, so it is safe to make this check for all hostdev devices. (It also needs to be done for many other device types, but that will be addressed in a future patch).
Likewise, since all hostdevs are detached by calling qemuDomainDeleteDevice(), which requires the device's alias, checking for a valid alias is a reasonable thing for PCI hostdevs too (NB: you'd think that we could rely on every device having a valid alias, but unfortunately when you run virsh qemu-attach on a qemu process
We also need to do something about qemu-attach ...
that was started with some "old style" device args, they don't include an "id" option on the commandline, so there is no alias in the device object that's created).
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 129 +++++++++------------------------------- 1 file changed, 28 insertions(+), 101 deletions(-)
ACK

On 3/22/19 4:27 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:28:45 -0400, Laine Stump wrote:
There are separate Detach functions for PCI, USB, SCSI, Vhost, and Mediated hostdevs, but the functions are all 100% the same code, except that the PCI function checks for the guest side of the device being a PCI Multifunction device, while the other 4 check that the device's alias != NULL.
The check for multifunction PCI devices should be done for *all* devices that are connected to the PCI bus in the guest, not just PCI hostdevs, and qemuIsMultiFunctionDevice() conveniently returns false if the queried device doesn't connect with PCI, so it is safe to make this check for all hostdev devices. (It also needs to be done for many other device types, but that will be addressed in a future patch).
Likewise, since all hostdevs are detached by calling qemuDomainDeleteDevice(), which requires the device's alias, checking for a valid alias is a reasonable thing for PCI hostdevs too (NB: you'd think that we could rely on every device having a valid alias, but unfortunately when you run virsh qemu-attach on a qemu process We also need to do something about qemu-attach ...
In the just-now-invented-by-me "Urban Programmers Dictionary", "We also need" is defined as "I volunteer". :-)

On Thu, Mar 21, 2019 at 06:28:45PM -0400, Laine Stump wrote:
There are separate Detach functions for PCI, USB, SCSI, Vhost, and Mediated hostdevs, but the functions are all 100% the same code, except that the PCI function checks for the guest side of the device being a PCI Multifunction device, while the other 4 check that the device's alias != NULL.
The check for multifunction PCI devices should be done for *all* devices that are connected to the PCI bus in the guest, not just PCI hostdevs, and qemuIsMultiFunctionDevice() conveniently returns false if the queried device doesn't connect with PCI, so it is safe to make this check for all hostdev devices. (It also needs to be done for many other device types, but that will be addressed in a future patch).
Likewise, since all hostdevs are detached by calling qemuDomainDeleteDevice(), which requires the device's alias, checking for a valid alias is a reasonable thing for PCI hostdevs too (NB: you'd think that we could rely on every device having a valid alias, but unfortunately when you run virsh qemu-attach on a qemu process that was started with some "old style" device args, they don't include an "id" option on the commandline, so there is no alias in the device object that's created).
Wrong. As of commit e3a52afcfcc3478b553dca38140394fd93f90a2c qemu-attach: Assign device aliases that is not true. Although other than not crashing later, I'm not sure what the point of making up aliases that don't match reality is. Or what the point of qemu-attach is anymore - it does not seem to parse -device arguments, nor is it able to get us a JSON monitor, so we would not be able to detach the device anyway.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 129 +++++++++------------------------------- 1 file changed, 28 insertions(+), 101 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 701458b2cd..389d16b090 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5499,124 +5499,42 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, return ret; }
-static int -qemuDomainDetachHostPCIDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - virDomainHostdevSubsysPCIPtr pcisrc = &detach->source.subsys.u.pci; - - if (qemuIsMultiFunctionDevice(vm->def, detach->info)) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("cannot hot unplug multifunction PCI device: %.4x:%.2x:%.2x.%.1x"), - pcisrc->addr.domain, pcisrc->addr.bus, - pcisrc->addr.slot, pcisrc->addr.function); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -}
static int -qemuDomainDetachHostUSBDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - if (!detach->info->alias) { - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -} - -static int -qemuDomainDetachHostSCSIDevice(virDomainObjPtr vm, +qemuDomainDetachThisHostDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, virDomainHostdevDefPtr detach, bool async) { - if (!detach->info->alias) { - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); + int ret = -1;
- return qemuDomainDeleteDevice(vm, detach->info->alias); -} + if (qemuAssignDeviceHostdevAlias(vm->def, &detach->info->alias, -1) < 0) + return -1;
Here you leave in the bogus alias assignment introduced by 9de26f27cfa6a32ce9a23e30a58991432bdcbee5 hotplug: Check for alias in hostdev detach
-static int -qemuDomainDetachSCSIVHostDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - if (!detach->info->alias) { + if (qemuIsMultiFunctionDevice(vm->def, detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); + _("cannot hot unplug multifunction PCI device with guest address: " + "%.4x:%.2x:%.2x.%.1x"), + detach->info->addr.pci.domain, detach->info->addr.pci.bus, + detach->info->addr.pci.slot, detach->info->addr.pci.function); return -1; }
- if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -} - - -static int -qemuDomainDetachMediatedDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ if (!detach->info->alias) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("device cannot be detached without a device alias")); + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("device cannot be detached without a device alias")); return -1; }
which makes this check pointless.
- if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -} - - -static int -qemuDomainDetachThisHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - int ret = -1; - - if (qemuAssignDeviceHostdevAlias(vm->def, &detach->info->alias, -1) < 0) - return -1; - switch (detach->source.subsys.type) {
Unrelated to this patch, but it seems this could benefit from a typecast and an EnumError.
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: - ret = qemuDomainDetachHostPCIDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - ret = qemuDomainDetachHostUSBDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: - ret = qemuDomainDetachHostSCSIDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: - ret = qemuDomainDetachSCSIVHostDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: - ret = qemuDomainDetachMediatedDevice(vm, detach, async); - break; + /* we support detach of all these types of hostdev */ + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hot unplug is not supported for hostdev subsys type '%s'"),
With the qemu-attach aliaslessness allegations retracted and the double check for alias removed: Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

On 3/22/19 8:28 AM, Ján Tomko wrote:
On Thu, Mar 21, 2019 at 06:28:45PM -0400, Laine Stump wrote:
There are separate Detach functions for PCI, USB, SCSI, Vhost, and Mediated hostdevs, but the functions are all 100% the same code, except that the PCI function checks for the guest side of the device being a PCI Multifunction device, while the other 4 check that the device's alias != NULL.
The check for multifunction PCI devices should be done for *all* devices that are connected to the PCI bus in the guest, not just PCI hostdevs, and qemuIsMultiFunctionDevice() conveniently returns false if the queried device doesn't connect with PCI, so it is safe to make this check for all hostdev devices. (It also needs to be done for many other device types, but that will be addressed in a future patch).
Likewise, since all hostdevs are detached by calling qemuDomainDeleteDevice(), which requires the device's alias, checking for a valid alias is a reasonable thing for PCI hostdevs too (NB: you'd think that we could rely on every device having a valid alias, but unfortunately when you run virsh qemu-attach on a qemu process that was started with some "old style" device args, they don't include an "id" option on the commandline, so there is no alias in the device object that's created).
Wrong.
As of commit e3a52afcfcc3478b553dca38140394fd93f90a2c qemu-attach: Assign device aliases
Ah, I stand corrected. I had looked at the earlier patch in that series, but skipped this final patch because I'd already found what I wanted.
that is not true.
Although other than not crashing later, I'm not sure what the point of making up aliases that don't match reality is.
That thing you said - "not crashing later", that's about it :-P
Or what the point of qemu-attach is anymore - it does not seem to parse -device arguments, nor is it able to get us a JSON monitor, so we would not be able to detach the device anyway.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 129 +++++++++------------------------------- 1 file changed, 28 insertions(+), 101 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 701458b2cd..389d16b090 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5499,124 +5499,42 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, return ret; }
-static int -qemuDomainDetachHostPCIDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - virDomainHostdevSubsysPCIPtr pcisrc = &detach->source.subsys.u.pci; - - if (qemuIsMultiFunctionDevice(vm->def, detach->info)) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("cannot hot unplug multifunction PCI device: %.4x:%.2x:%.2x.%.1x"), - pcisrc->addr.domain, pcisrc->addr.bus, - pcisrc->addr.slot, pcisrc->addr.function); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -}
static int -qemuDomainDetachHostUSBDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - if (!detach->info->alias) { - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -} - -static int -qemuDomainDetachHostSCSIDevice(virDomainObjPtr vm, +qemuDomainDetachThisHostDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, virDomainHostdevDefPtr detach, bool async) { - if (!detach->info->alias) { - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); + int ret = -1;
- return qemuDomainDeleteDevice(vm, detach->info->alias); -} + if (qemuAssignDeviceHostdevAlias(vm->def, &detach->info->alias, -1) < 0) + return -1;
Here you leave in the bogus alias assignment introduced by 9de26f27cfa6a32ce9a23e30a58991432bdcbee5 hotplug: Check for alias in hostdev detach
Yeah, *that* is the patch I found when I was looking for the source of the lines. The reason both the "check for no alias" and "assign an alias if it isn't there" are in this patch is because I was trying to keep it to just having code movement, with no deletion, just to make review so simple it could be done by a mythical code review robot. But since both you and Peter called it out, I guess that was unnecessary, and I'll happily remove it.
-static int -qemuDomainDetachSCSIVHostDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - if (!detach->info->alias) { + if (qemuIsMultiFunctionDevice(vm->def, detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); + _("cannot hot unplug multifunction PCI device with guest address: " + "%.4x:%.2x:%.2x.%.1x"), + detach->info->addr.pci.domain, detach->info->addr.pci.bus, + detach->info->addr.pci.slot, detach->info->addr.pci.function); return -1; }
- if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -} - - -static int -qemuDomainDetachMediatedDevice(virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ if (!detach->info->alias) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("device cannot be detached without a device alias")); + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("device cannot be detached without a device alias")); return -1; }
which makes this check pointless.
Yeah, that will be removed later anyway. The only reason it showed up in the diff is because I apparently somehow reformatted it. I'll "unreformat" it so it doesn't show up in the diff.
- if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - return qemuDomainDeleteDevice(vm, detach->info->alias); -} - - -static int -qemuDomainDetachThisHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - int ret = -1; - - if (qemuAssignDeviceHostdevAlias(vm->def, &detach->info->alias, -1) < 0) - return -1; - switch (detach->source.subsys.type) {
Unrelated to this patch, but it seems this could benefit from a typecast and an EnumError.
True Dat. (Sorry for the "youngster speak", but I'm trying to compensate for the picture on my new driver's license - I just got it renewed, and comparing the picture with the one taken 7 years ago, I look like a frumpy old man (in the old license, I looked like (in the words of my my beloved daughter) "a psycho killer", but at least I looked like a *youthful* psycho killer :-/)
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: - ret = qemuDomainDetachHostPCIDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - ret = qemuDomainDetachHostUSBDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: - ret = qemuDomainDetachHostSCSIDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: - ret = qemuDomainDetachSCSIVHostDevice(vm, detach, async); - break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: - ret = qemuDomainDetachMediatedDevice(vm, detach, async); - break; + /* we support detach of all these types of hostdev */ + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hot unplug is not supported for hostdev subsys type '%s'"),
With the qemu-attach aliaslessness allegations retracted
Well, I'll reword it at least. I think there still should be a check for a null alias, just in case someone in the future adds some stupid code that doesn't set an alias, but I'll make the comment more accurate.
and the double check for alias removed: Reviewed-by: Ján Tomko <jtomko@redhat.com>
Jano

qemuDomainDetachHostDevice() has a check at the end that calls qemuDomainDetachNetDevice() in the case that the hostdev is actually a Net device of type='hostdev'. A long time ago when device removal was (supposedly but not actually) synchronous, this would cause some extra code to be run prior to removing the device (e.g. restoring the original MAC address of the device, undoing some sort of virtual port profile, etc). For quite awhile now the device removal has been asynchronous, so that "extra teardown" isn't handled by the detach function, but instead is handled by the Remove function called at a later time. The result is that when we call qemuDomainDetachNetDevice() from qemuDomainDetachHostDevice(), it ends up just calling qemuDomainDetachThisHostDevice() and returning, which is exactly what we do for all other hostdevs anyway. Based on that, remove the behavioral difference when parent.type == VIR_DOMAIN_DEVICE_NET, and just call qemuDomainDetachThisHostDevice() for all hostdevs. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 389d16b090..af99f3bf4c 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5640,13 +5640,7 @@ int qemuDomainDetachHostDevice(virQEMUDriverPtr driver, return -1; } - /* If this is a network hostdev, we need to use the higher-level detach - * function so that mac address / virtualport are reset - */ - if (detach->parent.type == VIR_DOMAIN_DEVICE_NET) - return qemuDomainDetachNetDevice(driver, vm, &detach->parent, async); - else - return qemuDomainDetachThisHostDevice(driver, vm, detach, async); + return qemuDomainDetachThisHostDevice(driver, vm, detach, async); } -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:46 -0400, Laine Stump wrote:
qemuDomainDetachHostDevice() has a check at the end that calls qemuDomainDetachNetDevice() in the case that the hostdev is actually a Net device of type='hostdev'. A long time ago when device removal was (supposedly but not actually) synchronous, this would cause some extra code to be run prior to removing the device (e.g. restoring the original MAC address of the device, undoing some sort of virtual port profile, etc).
For quite awhile now the device removal has been asynchronous, so that "extra teardown" isn't handled by the detach function, but instead is handled by the Remove function called at a later time. The result is that when we call qemuDomainDetachNetDevice() from qemuDomainDetachHostDevice(), it ends up just calling qemuDomainDetachThisHostDevice() and returning, which is exactly what we do for all other hostdevs anyway.
Based on that, remove the behavioral difference when parent.type == VIR_DOMAIN_DEVICE_NET, and just call qemuDomainDetachThisHostDevice() for all hostdevs.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
ACK

qemuDomainDetachDiskDevice() is only called from one place. Moving the contents of the function to that place makes qemuDomainDetachDiskLive() more similar to the other Detach functions called by the toplevel qemuDomainDetachDevice(). The goal is to make each of the device-type-specific functions do this: 1) find the exact device 2) do any device-specific validation 3) do general validation 4) do device-specific shutdown (only needed for net devices) 5) do the common block of code to send device_del to qemu, then optionally wait for a corresponding DEVICE_DELETED event from qemu. with the final aim being that only items 1 & 2 will remain in each device-type-specific function, while 3 & 5 (which are the same for almost every type) will be de-duplicated and moved to the toplevel function that calls all of these (qemuDomainDetachDeviceLive(), which will also contain a callout to the one instance of (4) (netdev). Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 96 ++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 53 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index af99f3bf4c..820929b109 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5273,47 +5273,6 @@ qemuDomainSignalDeviceRemoval(virDomainObjPtr vm, } -static int -qemuDomainDetachDiskDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDiskDefPtr detach, - bool async) -{ - int ret = -1; - - if (qemuDomainDiskBlockJobIsActive(detach)) - return -1; - - if (detach->bus == VIR_DOMAIN_DISK_BUS_VIRTIO && - qemuIsMultiFunctionDevice(vm->def, &detach->info)) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("cannot hot unplug multifunction PCI device: %s"), - detach->dst); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &detach->info); - - if (qemuDomainDeleteDevice(vm, detach->info.alias) < 0) { - if (virDomainObjIsActive(vm)) - virDomainAuditDisk(vm, detach->src, NULL, "detach", false); - goto cleanup; - } - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveDiskDevice(driver, vm, detach); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; -} - static int qemuFindDisk(virDomainDefPtr def, const char *dst) { @@ -5333,25 +5292,26 @@ qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, virDomainDeviceDefPtr dev, bool async) { - virDomainDiskDefPtr disk; + virDomainDiskDefPtr detach; int idx; + int ret = -1; if ((idx = qemuFindDisk(vm->def, dev->data.disk->dst)) < 0) { virReportError(VIR_ERR_OPERATION_FAILED, _("disk %s not found"), dev->data.disk->dst); return -1; } - disk = vm->def->disks[idx]; + detach = vm->def->disks[idx]; - switch ((virDomainDiskDevice) disk->device) { + switch ((virDomainDiskDevice) detach->device) { case VIR_DOMAIN_DISK_DEVICE_DISK: case VIR_DOMAIN_DISK_DEVICE_LUN: - switch ((virDomainDiskBus) disk->bus) { + switch ((virDomainDiskBus) detach->bus) { case VIR_DOMAIN_DISK_BUS_VIRTIO: case VIR_DOMAIN_DISK_BUS_USB: case VIR_DOMAIN_DISK_BUS_SCSI: - return qemuDomainDetachDiskDevice(driver, vm, disk, async); + break; case VIR_DOMAIN_DISK_BUS_IDE: case VIR_DOMAIN_DISK_BUS_FDC: @@ -5361,12 +5321,12 @@ qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, case VIR_DOMAIN_DISK_BUS_SD: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", _("This type of disk cannot be hot unplugged")); - break; + return -1; case VIR_DOMAIN_DISK_BUS_LAST: default: - virReportEnumRangeError(virDomainDiskBus, disk->bus); - break; + virReportEnumRangeError(virDomainDiskBus, detach->bus); + return -1; } break; @@ -5374,16 +5334,46 @@ qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, case VIR_DOMAIN_DISK_DEVICE_FLOPPY: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("disk device type '%s' cannot be detached"), - virDomainDiskDeviceTypeToString(disk->device)); - break; + virDomainDiskDeviceTypeToString(detach->device)); + return -1; case VIR_DOMAIN_DISK_DEVICE_LAST: default: - virReportEnumRangeError(virDomainDiskDevice, disk->device); + virReportEnumRangeError(virDomainDiskDevice, detach->device); break; } - return -1; + if (qemuDomainDiskBlockJobIsActive(detach)) + return -1; + + if (detach->bus == VIR_DOMAIN_DISK_BUS_VIRTIO && + qemuIsMultiFunctionDevice(vm->def, &detach->info)) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("cannot hot unplug multifunction PCI device: %s"), + detach->dst); + return -1; + } + + if (!async) + qemuDomainMarkDeviceForRemoval(vm, &detach->info); + + if (qemuDomainDeleteDevice(vm, detach->info.alias) < 0) { + if (virDomainObjIsActive(vm)) + virDomainAuditDisk(vm, detach->src, NULL, "detach", false); + goto cleanup; + } + + if (async) { + ret = 0; + } else { + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) + ret = qemuDomainRemoveDiskDevice(driver, vm, detach); + } + + cleanup: + if (!async) + qemuDomainResetDeviceRemoval(vm); + return ret; } -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:47 -0400, Laine Stump wrote:
qemuDomainDetachDiskDevice() is only called from one place. Moving the contents of the function to that place makes qemuDomainDetachDiskLive() more similar to the other Detach functions called by the toplevel qemuDomainDetachDevice().
The goal is to make each of the device-type-specific functions do this:
1) find the exact device 2) do any device-specific validation 3) do general validation 4) do device-specific shutdown (only needed for net devices) 5) do the common block of code to send device_del to qemu, then optionally wait for a corresponding DEVICE_DELETED event from qemu.
with the final aim being that only items 1 & 2 will remain in each device-type-specific function, while 3 & 5 (which are the same for almost every type) will be de-duplicated and moved to the toplevel function that calls all of these (qemuDomainDetachDeviceLive(), which will also contain a callout to the one instance of (4) (netdev).
I'm not sure whether this is worth setting into ston^w commit message.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 96 ++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 53 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index af99f3bf4c..820929b109 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c
[...]
@@ -5374,16 +5334,46 @@ qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, case VIR_DOMAIN_DISK_DEVICE_FLOPPY: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("disk device type '%s' cannot be detached"), - virDomainDiskDeviceTypeToString(disk->device)); - break; + virDomainDiskDeviceTypeToString(detach->device)); + return -1;
case VIR_DOMAIN_DISK_DEVICE_LAST: default: - virReportEnumRangeError(virDomainDiskDevice, disk->device); + virReportEnumRangeError(virDomainDiskDevice, detach->device); break;
Missing conversion to 'return -1;' ACK with the above fixed.

On 3/22/19 5:07 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:28:47 -0400, Laine Stump wrote:
qemuDomainDetachDiskDevice() is only called from one place. Moving the contents of the function to that place makes qemuDomainDetachDiskLive() more similar to the other Detach functions called by the toplevel qemuDomainDetachDevice().
The goal is to make each of the device-type-specific functions do this:
1) find the exact device 2) do any device-specific validation 3) do general validation 4) do device-specific shutdown (only needed for net devices) 5) do the common block of code to send device_del to qemu, then optionally wait for a corresponding DEVICE_DELETED event from qemu.
with the final aim being that only items 1 & 2 will remain in each device-type-specific function, while 3 & 5 (which are the same for almost every type) will be de-duplicated and moved to the toplevel function that calls all of these (qemuDomainDetachDeviceLive(), which will also contain a callout to the one instance of (4) (netdev). I'm not sure whether this is worth setting into ston^w commit message.
Yeah, that should go below the --, but I can never remember to do that when sending the patch, so I just include the comments in the commit message and remove it later. (Is there a way to add "meta comments"?)
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 96 ++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 53 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index af99f3bf4c..820929b109 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c [...]
@@ -5374,16 +5334,46 @@ qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, case VIR_DOMAIN_DISK_DEVICE_FLOPPY: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("disk device type '%s' cannot be detached"), - virDomainDiskDeviceTypeToString(disk->device)); - break; + virDomainDiskDeviceTypeToString(detach->device)); + return -1;
case VIR_DOMAIN_DISK_DEVICE_LAST: default: - virReportEnumRangeError(virDomainDiskDevice, disk->device); + virReportEnumRangeError(virDomainDiskDevice, detach->device); break; Missing conversion to 'return -1;'
Ooh!! Good eye!!
ACK with the above fixed.

Back in the bad old days different device types required a different qemu monitor call to detach them, and so an <interface type='hostdev'> needed to call the function for detaching hostdevs, while other <interface> types could be deleted as netdevs. Times have changed, and *all* device types are detached by calling the common function qemuDomainDeleteDevice(vm, alias), so we don't need to differentiate between hostdev interfaces and the others for that reason. There are a few other netdev-specific functions called during qemuDomainDetachNetDevice() (clearing bandwidth limits, stopping the interface), but those turn into NOPs when type=hostdev, so they're safe to call for type=hostdev. The only thing that is different + not a NOP is the call to virDomainAudit*() when qemuDomainDeleteDevice() fails, so if we add a conditional for that small bit of code, we can eliminate the callout from qemuDomainDetachNetDevice() to qemuDomainDetachThisDevice(), which makes this function fit the desired pattern for merging with the other detach functions, and paves the way to simplifying qemuDomainDetachHostDevice() too. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 820929b109..ee3042cc4c 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5801,13 +5801,6 @@ qemuDomainDetachNetDevice(virQEMUDriverPtr driver, detach = vm->def->nets[detachidx]; - if (virDomainNetGetActualType(detach) == VIR_DOMAIN_NET_TYPE_HOSTDEV) { - ret = qemuDomainDetachThisHostDevice(driver, vm, - virDomainNetGetActualHostdev(detach), - async); - goto cleanup; - } - if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device: %s"), @@ -5836,8 +5829,13 @@ qemuDomainDetachNetDevice(virQEMUDriverPtr driver, qemuDomainMarkDeviceForRemoval(vm, &detach->info); if (qemuDomainDeleteDevice(vm, detach->info.alias) < 0) { - if (virDomainObjIsActive(vm)) - virDomainAuditNet(vm, detach, NULL, "detach", false); + if (virDomainObjIsActive(vm)) { + /* the audit message has a different format for hostdev network devices */ + if (virDomainNetGetActualType(detach) == VIR_DOMAIN_NET_TYPE_HOSTDEV) + virDomainAuditHostdev(vm, virDomainNetGetActualHostdev(detach), "detach", false); + else + virDomainAuditNet(vm, detach, NULL, "detach", false); + } goto cleanup; } -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:48 -0400, Laine Stump wrote:
Back in the bad old days different device types required a different qemu monitor call to detach them, and so an <interface type='hostdev'> needed to call the function for detaching hostdevs, while other <interface> types could be deleted as netdevs.
Times have changed, and *all* device types are detached by calling the common function qemuDomainDeleteDevice(vm, alias), so we don't need to differentiate between hostdev interfaces and the others for that reason.
There are a few other netdev-specific functions called during qemuDomainDetachNetDevice() (clearing bandwidth limits, stopping the interface), but those turn into NOPs when type=hostdev, so they're safe to call for type=hostdev.
The only thing that is different + not a NOP is the call to virDomainAudit*() when qemuDomainDeleteDevice() fails, so if we add a conditional for that small bit of code, we can eliminate the callout from qemuDomainDetachNetDevice() to qemuDomainDetachThisDevice(), which makes this function fit the desired pattern for merging with the other detach functions, and paves the way to simplifying qemuDomainDetachHostDevice() too.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-)
ACK

It's now only called from one place, and combining the two functions highlights the similarity with Detach functions for other device types. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 107 +++++++++++++++------------------------- 1 file changed, 41 insertions(+), 66 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index ee3042cc4c..1f17e2a05e 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5490,71 +5490,6 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, } -static int -qemuDomainDetachThisHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainHostdevDefPtr detach, - bool async) -{ - int ret = -1; - - if (qemuAssignDeviceHostdevAlias(vm->def, &detach->info->alias, -1) < 0) - return -1; - - if (qemuIsMultiFunctionDevice(vm->def, detach->info)) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("cannot hot unplug multifunction PCI device with guest address: " - "%.4x:%.2x:%.2x.%.1x"), - detach->info->addr.pci.domain, detach->info->addr.pci.bus, - detach->info->addr.pci.slot, detach->info->addr.pci.function); - return -1; - } - - if (!detach->info->alias) { - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); - return -1; - } - - switch (detach->source.subsys.type) { - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: - /* we support detach of all these types of hostdev */ - break; - - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("hot unplug is not supported for hostdev subsys type '%s'"), - virDomainHostdevSubsysTypeToString(detach->source.subsys.type)); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); - - if (qemuDomainDeleteDevice(vm, detach->info->alias) < 0) { - if (virDomainObjIsActive(vm)) - virDomainAuditHostdev(vm, detach, "detach", false); - goto cleanup; - } - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveHostDevice(driver, vm, detach); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - - return ret; -} - /* search for a hostdev matching dev and detach it */ int qemuDomainDetachHostDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -5569,6 +5504,7 @@ int qemuDomainDetachHostDevice(virQEMUDriverPtr driver, virDomainHostdevSubsysMediatedDevPtr mdevsrc = &subsys->u.mdev; virDomainHostdevDefPtr detach = NULL; int idx; + int ret = -1; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -5630,7 +5566,46 @@ int qemuDomainDetachHostDevice(virQEMUDriverPtr driver, return -1; } - return qemuDomainDetachThisHostDevice(driver, vm, detach, async); + if (qemuAssignDeviceHostdevAlias(vm->def, &detach->info->alias, -1) < 0) + return -1; + + if (qemuIsMultiFunctionDevice(vm->def, detach->info)) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("cannot hot unplug multifunction PCI device with guest address: " + "%.4x:%.2x:%.2x.%.1x"), + detach->info->addr.pci.domain, detach->info->addr.pci.bus, + detach->info->addr.pci.slot, detach->info->addr.pci.function); + return -1; + } + + if (!detach->info->alias) { + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("device cannot be detached without a device alias")); + return -1; + } + + if (!async) + qemuDomainMarkDeviceForRemoval(vm, detach->info); + + if (qemuDomainDeleteDevice(vm, detach->info->alias) < 0) { + if (virDomainObjIsActive(vm)) + virDomainAuditHostdev(vm, detach, "detach", false); + goto cleanup; + } + + if (async) { + ret = 0; + } else { + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) + ret = qemuDomainRemoveHostDevice(driver, vm, detach); + } + + cleanup: + if (!async) + qemuDomainResetDeviceRemoval(vm); + + return ret; + } -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:49 -0400, Laine Stump wrote:
It's now only called from one place, and combining the two functions highlights the similarity with Detach functions for other device types.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 107 +++++++++++++++------------------------- 1 file changed, 41 insertions(+), 66 deletions(-)
ACK, this does what it says. There are a few things that could be done better though e.g. assigning an alias via qemuAssignDeviceHostdevAlias makes the chechk whether it's assigned dead code.

On 3/22/19 5:32 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:28:49 -0400, Laine Stump wrote:
It's now only called from one place, and combining the two functions highlights the similarity with Detach functions for other device types.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 107 +++++++++++++++------------------------- 1 file changed, 41 insertions(+), 66 deletions(-) ACK, this does what it says. There are a few things that could be done better though e.g. assigning an alias via qemuAssignDeviceHostdevAlias makes the chechk whether it's assigned dead code.
Yeah, it goes away later. I wanted to keep this patch to only code movement as much as possible.

It was sitting down in the middle of all the qemuDomainDetach*() functions. Move it up with the rest of the qemuDomain*Graphics*() functions. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 151 ++++++++++++++++++++-------------------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1f17e2a05e..ca89cf5d97 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4170,6 +4170,83 @@ qemuDomainFindGraphicsIndex(virDomainDefPtr def, return -1; } + +int +qemuDomainChangeGraphicsPasswords(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int type, + virDomainGraphicsAuthDefPtr auth, + const char *defaultPasswd, + int asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + time_t now = time(NULL); + const char *expire; + char *validTo = NULL; + const char *connected = NULL; + const char *password; + int ret = -1; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + + if (!auth->passwd && !defaultPasswd) { + ret = 0; + goto cleanup; + } + password = auth->passwd ? auth->passwd : defaultPasswd; + + if (auth->connected) + connected = virDomainGraphicsAuthConnectedTypeToString(auth->connected); + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + goto cleanup; + ret = qemuMonitorSetPassword(priv->mon, type, password, connected); + + if (ret == -2) { + if (type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Graphics password only supported for VNC")); + ret = -1; + } else { + ret = qemuMonitorSetVNCPassword(priv->mon, password); + } + } + if (ret != 0) + goto end_job; + + if (password[0] == '\0' || + (auth->expires && auth->validTo <= now)) { + expire = "now"; + } else if (auth->expires) { + if (virAsprintf(&validTo, "%lu", (unsigned long)auth->validTo) < 0) + goto end_job; + expire = validTo; + } else { + expire = "never"; + } + + ret = qemuMonitorExpirePassword(priv->mon, type, expire); + + if (ret == -2) { + /* XXX we could fake this with a timer */ + if (auth->expires) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expiry of passwords is not supported")); + ret = -1; + } else { + ret = 0; + } + } + + end_job: + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret = -1; + cleanup: + VIR_FREE(validTo); + virObjectUnref(cfg); + return ret; +} + + int qemuDomainChangeGraphics(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -5827,80 +5904,6 @@ qemuDomainDetachNetDevice(virQEMUDriverPtr driver, return ret; } -int -qemuDomainChangeGraphicsPasswords(virQEMUDriverPtr driver, - virDomainObjPtr vm, - int type, - virDomainGraphicsAuthDefPtr auth, - const char *defaultPasswd, - int asyncJob) -{ - qemuDomainObjPrivatePtr priv = vm->privateData; - time_t now = time(NULL); - const char *expire; - char *validTo = NULL; - const char *connected = NULL; - const char *password; - int ret = -1; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - - if (!auth->passwd && !defaultPasswd) { - ret = 0; - goto cleanup; - } - password = auth->passwd ? auth->passwd : defaultPasswd; - - if (auth->connected) - connected = virDomainGraphicsAuthConnectedTypeToString(auth->connected); - - if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) - goto cleanup; - ret = qemuMonitorSetPassword(priv->mon, type, password, connected); - - if (ret == -2) { - if (type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Graphics password only supported for VNC")); - ret = -1; - } else { - ret = qemuMonitorSetVNCPassword(priv->mon, password); - } - } - if (ret != 0) - goto end_job; - - if (password[0] == '\0' || - (auth->expires && auth->validTo <= now)) { - expire = "now"; - } else if (auth->expires) { - if (virAsprintf(&validTo, "%lu", (unsigned long)auth->validTo) < 0) - goto end_job; - expire = validTo; - } else { - expire = "never"; - } - - ret = qemuMonitorExpirePassword(priv->mon, type, expire); - - if (ret == -2) { - /* XXX we could fake this with a timer */ - if (auth->expires) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Expiry of passwords is not supported")); - ret = -1; - } else { - ret = 0; - } - } - - end_job: - if (qemuDomainObjExitMonitor(driver, vm) < 0) - ret = -1; - cleanup: - VIR_FREE(validTo); - virObjectUnref(cfg); - return ret; -} int qemuDomainAttachLease(virQEMUDriverPtr driver, virDomainObjPtr vm, -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:50 -0400, Laine Stump wrote:
It was sitting down in the middle of all the qemuDomainDetach*() functions. Move it up with the rest of the qemuDomain*Graphics*() functions.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 151 ++++++++++++++++++++-------------------- 1 file changed, 77 insertions(+), 74 deletions(-)
ACK

There were two outliers at the end of the file beyond the Vcpu functions. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 174 ++++++++++++++++++++-------------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index ca89cf5d97..094d135aac 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -6102,6 +6102,93 @@ qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, } +int +qemuDomainDetachInputDevice(virDomainObjPtr vm, + virDomainInputDefPtr def, + bool async) +{ + virDomainInputDefPtr input; + int ret = -1; + int idx; + + if ((idx = virDomainInputDefFind(vm->def, def)) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("matching input device not found")); + return -1; + } + input = vm->def->inputs[idx]; + + switch ((virDomainInputBus) input->bus) { + case VIR_DOMAIN_INPUT_BUS_PS2: + case VIR_DOMAIN_INPUT_BUS_XEN: + case VIR_DOMAIN_INPUT_BUS_PARALLELS: + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("input device on bus '%s' cannot be detached"), + virDomainInputBusTypeToString(input->bus)); + return -1; + + case VIR_DOMAIN_INPUT_BUS_LAST: + case VIR_DOMAIN_INPUT_BUS_USB: + case VIR_DOMAIN_INPUT_BUS_VIRTIO: + break; + } + + if (!async) + qemuDomainMarkDeviceForRemoval(vm, &input->info); + + if (qemuDomainDeleteDevice(vm, input->info.alias) < 0) + goto cleanup; + + if (async) { + ret = 0; + } else { + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) + ret = qemuDomainRemoveInputDevice(vm, input); + } + + cleanup: + if (!async) + qemuDomainResetDeviceRemoval(vm); + return ret; +} + + +int +qemuDomainDetachVsockDevice(virDomainObjPtr vm, + virDomainVsockDefPtr dev, + bool async) +{ + virDomainVsockDefPtr vsock = vm->def->vsock; + int ret = -1; + + + if (!vsock || + !virDomainVsockDefEquals(dev, vsock)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("matching vsock device not found")); + return -1; + } + + if (!async) + qemuDomainMarkDeviceForRemoval(vm, &vsock->info); + + if (qemuDomainDeleteDevice(vm, vsock->info.alias) < 0) + goto cleanup; + + if (async) { + ret = 0; + } else { + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) + ret = qemuDomainRemoveVsockDevice(vm, vsock); + } + + cleanup: + if (!async) + qemuDomainResetDeviceRemoval(vm); + return ret; +} + + static int qemuDomainRemoveVcpu(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -6755,90 +6842,3 @@ qemuDomainSetVcpuInternal(virQEMUDriverPtr driver, virObjectUnref(cfg); return ret; } - - -int -qemuDomainDetachInputDevice(virDomainObjPtr vm, - virDomainInputDefPtr def, - bool async) -{ - virDomainInputDefPtr input; - int ret = -1; - int idx; - - if ((idx = virDomainInputDefFind(vm->def, def)) < 0) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("matching input device not found")); - return -1; - } - input = vm->def->inputs[idx]; - - switch ((virDomainInputBus) input->bus) { - case VIR_DOMAIN_INPUT_BUS_PS2: - case VIR_DOMAIN_INPUT_BUS_XEN: - case VIR_DOMAIN_INPUT_BUS_PARALLELS: - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, - _("input device on bus '%s' cannot be detached"), - virDomainInputBusTypeToString(input->bus)); - return -1; - - case VIR_DOMAIN_INPUT_BUS_LAST: - case VIR_DOMAIN_INPUT_BUS_USB: - case VIR_DOMAIN_INPUT_BUS_VIRTIO: - break; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &input->info); - - if (qemuDomainDeleteDevice(vm, input->info.alias) < 0) - goto cleanup; - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveInputDevice(vm, input); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; -} - - -int -qemuDomainDetachVsockDevice(virDomainObjPtr vm, - virDomainVsockDefPtr dev, - bool async) -{ - virDomainVsockDefPtr vsock = vm->def->vsock; - int ret = -1; - - - if (!vsock || - !virDomainVsockDefEquals(dev, vsock)) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("matching vsock device not found")); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &vsock->info); - - if (qemuDomainDeleteDevice(vm, vsock->info.alias) < 0) - goto cleanup; - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveVsockDevice(vm, vsock); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; -} -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:51 -0400, Laine Stump wrote:
There were two outliers at the end of the file beyond the Vcpu functions.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 174 ++++++++++++++++++++-------------------- 1 file changed, 87 insertions(+), 87 deletions(-)
While this obscures git history I don't feel strongly against doing that. ACK

On 3/22/19 7:30 AM, Peter Krempa wrote:
There were two outliers at the end of the file beyond the Vcpu functions.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 174 ++++++++++++++++++++-------------------- 1 file changed, 87 insertions(+), 87 deletions(-) While this obscures git history I don't feel strongly against doing
On Thu, Mar 21, 2019 at 18:28:51 -0400, Laine Stump wrote: that.
That (and the trouble it causes for backports) is my main argument against "cosmetic" refactoring, and I definitely had misgivings while doing it, but cleanliness won out over accountability in the end (it would be nice if git could easily report the full history of a line, including when it was moved. You can get to the *real* origin by right clicking on deleted equivalent lines in gitk, but sometimes you have to go back half a dozen generations to find it (and then it doesn't provide a simple way to get back to the present). If only I remembered any of the teeny tiny bit of tcl/tk I learned 20 years ago, I might try to add that functionality to gitk :-P

The Attach and Detach Lease functions were together in the middle of the Detach functions. Put them at the end of their respective sections, since they behave differently from the other attach/detach functions (DetachLease doesn't use qemuDomainDeleteDevice(), and is always synchronous). Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 96 +++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 094d135aac..1f96f56942 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -3563,6 +3563,32 @@ qemuDomainAttachVsockDevice(virQEMUDriverPtr driver, } +int +qemuDomainAttachLease(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainLeaseDefPtr lease) +{ + int ret = -1; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + + if (virDomainLeaseInsertPreAlloc(vm->def) < 0) + goto cleanup; + + if (virDomainLockLeaseAttach(driver->lockManager, cfg->uri, + vm, lease) < 0) { + virDomainLeaseInsertPreAlloced(vm->def, NULL); + goto cleanup; + } + + virDomainLeaseInsertPreAlloced(vm->def, lease); + ret = 0; + + cleanup: + virObjectUnref(cfg); + return ret; +} + + static int qemuDomainChangeNetBridge(virDomainObjPtr vm, virDomainNetDefPtr olddev, @@ -5905,52 +5931,6 @@ qemuDomainDetachNetDevice(virQEMUDriverPtr driver, } -int qemuDomainAttachLease(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainLeaseDefPtr lease) -{ - int ret = -1; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - - if (virDomainLeaseInsertPreAlloc(vm->def) < 0) - goto cleanup; - - if (virDomainLockLeaseAttach(driver->lockManager, cfg->uri, - vm, lease) < 0) { - virDomainLeaseInsertPreAlloced(vm->def, NULL); - goto cleanup; - } - - virDomainLeaseInsertPreAlloced(vm->def, lease); - ret = 0; - - cleanup: - virObjectUnref(cfg); - return ret; -} - -int qemuDomainDetachLease(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainLeaseDefPtr lease) -{ - virDomainLeaseDefPtr det_lease; - int idx; - - if ((idx = virDomainLeaseIndex(vm->def, lease)) < 0) { - virReportError(VIR_ERR_INVALID_ARG, - _("Lease %s in lockspace %s does not exist"), - lease->key, NULLSTR(lease->lockspace)); - return -1; - } - - if (virDomainLockLeaseDetach(driver->lockManager, vm, lease) < 0) - return -1; - - det_lease = virDomainLeaseRemoveAt(vm->def, idx); - virDomainLeaseDefFree(det_lease); - return 0; -} - int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainChrDefPtr chr, @@ -6189,6 +6169,30 @@ qemuDomainDetachVsockDevice(virDomainObjPtr vm, } +int +qemuDomainDetachLease(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainLeaseDefPtr lease) +{ + virDomainLeaseDefPtr det_lease; + int idx; + + if ((idx = virDomainLeaseIndex(vm->def, lease)) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("Lease %s in lockspace %s does not exist"), + lease->key, NULLSTR(lease->lockspace)); + return -1; + } + + if (virDomainLockLeaseDetach(driver->lockManager, vm, lease) < 0) + return -1; + + det_lease = virDomainLeaseRemoveAt(vm->def, idx); + virDomainLeaseDefFree(det_lease); + return 0; +} + + static int qemuDomainRemoveVcpu(virQEMUDriverPtr driver, virDomainObjPtr vm, -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:52 -0400, Laine Stump wrote:
The Attach and Detach Lease functions were together in the middle of the Detach functions. Put them at the end of their respective sections, since they behave differently from the other attach/detach functions (DetachLease doesn't use qemuDomainDeleteDevice(), and is always synchronous).
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 96 +++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 46 deletions(-)
ACK

This function is going to take on some of the functionality of its subordinate functions, which all live in qemu_hotplug.c. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_driver.c | 95 ----------------------------- src/qemu/qemu_hotplug.c | 129 +++++++++++++++++++++++++++++++++++----- src/qemu/qemu_hotplug.h | 68 ++++++--------------- 3 files changed, 133 insertions(+), 159 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a16eab5467..0b80004c6e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8004,101 +8004,6 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm, return ret; } -static int -qemuDomainDetachDeviceControllerLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) -{ - virDomainControllerDefPtr cont = dev->data.controller; - int ret = -1; - - switch (cont->type) { - case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: - ret = qemuDomainDetachControllerDevice(driver, vm, dev, async); - break; - default : - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, - _("'%s' controller cannot be hot unplugged."), - virDomainControllerTypeToString(cont->type)); - } - return ret; -} - -static int -qemuDomainDetachDeviceLive(virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - virQEMUDriverPtr driver, - bool async) -{ - int ret = -1; - - switch ((virDomainDeviceType)dev->type) { - case VIR_DOMAIN_DEVICE_DISK: - ret = qemuDomainDetachDeviceDiskLive(driver, vm, dev, async); - break; - case VIR_DOMAIN_DEVICE_CONTROLLER: - ret = qemuDomainDetachDeviceControllerLive(driver, vm, dev, async); - break; - case VIR_DOMAIN_DEVICE_LEASE: - ret = qemuDomainDetachLease(driver, vm, dev->data.lease); - break; - case VIR_DOMAIN_DEVICE_NET: - ret = qemuDomainDetachNetDevice(driver, vm, dev, async); - break; - case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = qemuDomainDetachHostDevice(driver, vm, dev, async); - break; - case VIR_DOMAIN_DEVICE_CHR: - ret = qemuDomainDetachChrDevice(driver, vm, dev->data.chr, async); - break; - case VIR_DOMAIN_DEVICE_RNG: - ret = qemuDomainDetachRNGDevice(driver, vm, dev->data.rng, async); - break; - case VIR_DOMAIN_DEVICE_MEMORY: - ret = qemuDomainDetachMemoryDevice(driver, vm, dev->data.memory, async); - break; - case VIR_DOMAIN_DEVICE_SHMEM: - ret = qemuDomainDetachShmemDevice(driver, vm, dev->data.shmem, async); - break; - case VIR_DOMAIN_DEVICE_WATCHDOG: - ret = qemuDomainDetachWatchdog(driver, vm, dev->data.watchdog, async); - break; - case VIR_DOMAIN_DEVICE_INPUT: - ret = qemuDomainDetachInputDevice(vm, dev->data.input, async); - break; - case VIR_DOMAIN_DEVICE_REDIRDEV: - ret = qemuDomainDetachRedirdevDevice(driver, vm, dev->data.redirdev, async); - break; - - case VIR_DOMAIN_DEVICE_VSOCK: - ret = qemuDomainDetachVsockDevice(vm, dev->data.vsock, async); - break; - - case VIR_DOMAIN_DEVICE_FS: - case VIR_DOMAIN_DEVICE_SOUND: - case VIR_DOMAIN_DEVICE_VIDEO: - case VIR_DOMAIN_DEVICE_GRAPHICS: - case VIR_DOMAIN_DEVICE_HUB: - case VIR_DOMAIN_DEVICE_SMARTCARD: - case VIR_DOMAIN_DEVICE_MEMBALLOON: - case VIR_DOMAIN_DEVICE_NVRAM: - case VIR_DOMAIN_DEVICE_NONE: - case VIR_DOMAIN_DEVICE_TPM: - case VIR_DOMAIN_DEVICE_PANIC: - case VIR_DOMAIN_DEVICE_IOMMU: - case VIR_DOMAIN_DEVICE_LAST: - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, - _("live detach of device '%s' is not supported"), - virDomainDeviceTypeToString(dev->type)); - break; - } - - if (ret == 0) - ret = qemuDomainUpdateDeviceList(driver, vm, QEMU_ASYNC_JOB_NONE); - - return ret; -} static int qemuDomainChangeDiskLive(virDomainObjPtr vm, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1f96f56942..c7aba74c6b 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5540,10 +5540,11 @@ static bool qemuDomainControllerIsBusy(virDomainObjPtr vm, } } -int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +static int +qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async) { int idx, ret = -1; virDomainControllerDefPtr detach = NULL; @@ -5594,10 +5595,11 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, /* search for a hostdev matching dev and detach it */ -int qemuDomainDetachHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +static int +qemuDomainDetachHostDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async) { virDomainHostdevDefPtr hostdev = dev->data.hostdev; virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; @@ -5821,7 +5823,7 @@ qemuDomainDetachWatchdog(virQEMUDriverPtr driver, } -int +static int qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRedirdevDefPtr dev, @@ -5865,7 +5867,7 @@ qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, } -int +static int qemuDomainDetachNetDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev, @@ -5988,7 +5990,7 @@ int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, } -int +static int qemuDomainDetachRNGDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRNGDefPtr rng, @@ -6034,7 +6036,7 @@ qemuDomainDetachRNGDevice(virQEMUDriverPtr driver, } -int +static int qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainMemoryDefPtr memdef, @@ -6082,7 +6084,7 @@ qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, } -int +static int qemuDomainDetachInputDevice(virDomainObjPtr vm, virDomainInputDefPtr def, bool async) @@ -6133,7 +6135,7 @@ qemuDomainDetachInputDevice(virDomainObjPtr vm, } -int +static int qemuDomainDetachVsockDevice(virDomainObjPtr vm, virDomainVsockDefPtr dev, bool async) @@ -6169,7 +6171,7 @@ qemuDomainDetachVsockDevice(virDomainObjPtr vm, } -int +static int qemuDomainDetachLease(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease) @@ -6193,6 +6195,103 @@ qemuDomainDetachLease(virQEMUDriverPtr driver, } +static int +qemuDomainDetachDeviceControllerLive(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async) +{ + virDomainControllerDefPtr cont = dev->data.controller; + int ret = -1; + + switch (cont->type) { + case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: + ret = qemuDomainDetachControllerDevice(driver, vm, dev, async); + break; + default : + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("'%s' controller cannot be hot unplugged."), + virDomainControllerTypeToString(cont->type)); + } + return ret; +} + +int +qemuDomainDetachDeviceLive(virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + virQEMUDriverPtr driver, + bool async) +{ + int ret = -1; + + switch ((virDomainDeviceType)dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + ret = qemuDomainDetachDeviceDiskLive(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_CONTROLLER: + ret = qemuDomainDetachDeviceControllerLive(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_LEASE: + ret = qemuDomainDetachLease(driver, vm, dev->data.lease); + break; + case VIR_DOMAIN_DEVICE_NET: + ret = qemuDomainDetachNetDevice(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = qemuDomainDetachHostDevice(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_CHR: + ret = qemuDomainDetachChrDevice(driver, vm, dev->data.chr, async); + break; + case VIR_DOMAIN_DEVICE_RNG: + ret = qemuDomainDetachRNGDevice(driver, vm, dev->data.rng, async); + break; + case VIR_DOMAIN_DEVICE_MEMORY: + ret = qemuDomainDetachMemoryDevice(driver, vm, dev->data.memory, async); + break; + case VIR_DOMAIN_DEVICE_SHMEM: + ret = qemuDomainDetachShmemDevice(driver, vm, dev->data.shmem, async); + break; + case VIR_DOMAIN_DEVICE_WATCHDOG: + ret = qemuDomainDetachWatchdog(driver, vm, dev->data.watchdog, async); + break; + case VIR_DOMAIN_DEVICE_INPUT: + ret = qemuDomainDetachInputDevice(vm, dev->data.input, async); + break; + case VIR_DOMAIN_DEVICE_REDIRDEV: + ret = qemuDomainDetachRedirdevDevice(driver, vm, dev->data.redirdev, async); + break; + + case VIR_DOMAIN_DEVICE_VSOCK: + ret = qemuDomainDetachVsockDevice(vm, dev->data.vsock, async); + break; + + case VIR_DOMAIN_DEVICE_FS: + case VIR_DOMAIN_DEVICE_SOUND: + case VIR_DOMAIN_DEVICE_VIDEO: + case VIR_DOMAIN_DEVICE_GRAPHICS: + case VIR_DOMAIN_DEVICE_HUB: + case VIR_DOMAIN_DEVICE_SMARTCARD: + case VIR_DOMAIN_DEVICE_MEMBALLOON: + case VIR_DOMAIN_DEVICE_NVRAM: + case VIR_DOMAIN_DEVICE_NONE: + case VIR_DOMAIN_DEVICE_TPM: + case VIR_DOMAIN_DEVICE_PANIC: + case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_LAST: + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("live detach of device '%s' is not supported"), + virDomainDeviceTypeToString(dev->type)); + break; + } + + if (ret == 0) + ret = qemuDomainUpdateDeviceList(driver, vm, QEMU_ASYNC_JOB_NONE); + + return ret; +} + + static int qemuDomainRemoveVcpu(virQEMUDriverPtr driver, virDomainObjPtr vm, diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 7ac03b7810..61f732c506 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -79,10 +79,6 @@ int qemuDomainFindGraphicsIndex(virDomainDefPtr def, int qemuDomainAttachMemory(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainMemoryDefPtr mem); -int qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainMemoryDefPtr memdef, - bool async); int qemuDomainChangeGraphics(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainGraphicsDefPtr dev); @@ -99,35 +95,6 @@ int qemuDomainChangeNetLinkState(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainNetDefPtr dev, int linkstate); -int qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); -int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); -int qemuDomainDetachNetDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); -int qemuDomainDetachHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); -int qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainShmemDefPtr dev, - bool async); -int qemuDomainDetachWatchdog(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainWatchdogDefPtr watchdog, - bool async); - -int qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainRedirdevDefPtr dev, - bool async); int qemuDomainAttachInputDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -140,23 +107,33 @@ int qemuDomainAttachVsockDevice(virQEMUDriverPtr driver, int qemuDomainAttachLease(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease); -int qemuDomainDetachLease(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainLeaseDefPtr lease); int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainChrDefPtr chr); -int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainChrDefPtr chr, - bool async); int qemuDomainAttachRNGDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRNGDefPtr rng); -int qemuDomainDetachRNGDevice(virQEMUDriverPtr driver, + +int qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async); +int qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainShmemDefPtr dev, + bool async); +int qemuDomainDetachWatchdog(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainWatchdogDefPtr watchdog, + bool async); +int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, - virDomainRNGDefPtr rng, + virDomainChrDefPtr chr, bool async); +int qemuDomainDetachDeviceLive(virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + virQEMUDriverPtr driver, + bool async); void qemuDomainRemoveVcpuAlias(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -191,11 +168,4 @@ int qemuDomainSetVcpuInternal(virQEMUDriverPtr driver, virBitmapPtr vcpus, bool state); -int qemuDomainDetachInputDevice(virDomainObjPtr vm, - virDomainInputDefPtr def, - bool async); - -int qemuDomainDetachVsockDevice(virDomainObjPtr vm, - virDomainVsockDefPtr dev, - bool async); #endif /* LIBVIRT_QEMU_HOTPLUG_H */ -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:53 -0400, Laine Stump wrote:
This function is going to take on some of the functionality of its subordinate functions, which all live in qemu_hotplug.c.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_driver.c | 95 ----------------------------- src/qemu/qemu_hotplug.c | 129 +++++++++++++++++++++++++++++++++++----- src/qemu/qemu_hotplug.h | 68 ++++++--------------- 3 files changed, 133 insertions(+), 159 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a16eab5467..0b80004c6e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8004,101 +8004,6 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm, return ret; }
-static int -qemuDomainDetachDeviceControllerLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) -{ - virDomainControllerDefPtr cont = dev->data.controller; - int ret = -1; - - switch (cont->type) { - case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: - ret = qemuDomainDetachControllerDevice(driver, vm, dev, async); - break; - default : - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, - _("'%s' controller cannot be hot unplugged."), - virDomainControllerTypeToString(cont->type)); - } - return ret; -}
This function is not just moved ... [...]
static int qemuDomainChangeDiskLive(virDomainObjPtr vm, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1f96f56942..c7aba74c6b 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5540,10 +5540,11 @@ static bool qemuDomainControllerIsBusy(virDomainObjPtr vm, } }
-int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +static int +qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async)
... but replaced. This is a separate semantic change.
{ int idx, ret = -1; virDomainControllerDefPtr detach = NULL; @@ -5594,10 +5595,11 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver,
/* search for a hostdev matching dev and detach it */ -int qemuDomainDetachHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +static int +qemuDomainDetachHostDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async) { virDomainHostdevDefPtr hostdev = dev->data.hostdev; virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; @@ -5821,7 +5823,7 @@ qemuDomainDetachWatchdog(virQEMUDriverPtr driver, }
-int +static int qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRedirdevDefPtr dev, @@ -5865,7 +5867,7 @@ qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, }
-int +static int qemuDomainDetachNetDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev, @@ -5988,7 +5990,7 @@ int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, }
-int +static int qemuDomainDetachRNGDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRNGDefPtr rng, @@ -6034,7 +6036,7 @@ qemuDomainDetachRNGDevice(virQEMUDriverPtr driver, }
-int +static int qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainMemoryDefPtr memdef, @@ -6082,7 +6084,7 @@ qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, }
-int +static int qemuDomainDetachInputDevice(virDomainObjPtr vm, virDomainInputDefPtr def, bool async) @@ -6133,7 +6135,7 @@ qemuDomainDetachInputDevice(virDomainObjPtr vm, }
-int +static int qemuDomainDetachVsockDevice(virDomainObjPtr vm, virDomainVsockDefPtr dev, bool async) @@ -6169,7 +6171,7 @@ qemuDomainDetachVsockDevice(virDomainObjPtr vm, }
-int +static int qemuDomainDetachLease(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease)
I'd also prefer if these changes are made separately.
@@ -6193,6 +6195,103 @@ qemuDomainDetachLease(virQEMUDriverPtr driver, }
+static int +qemuDomainDetachDeviceControllerLive(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async) +{ + virDomainControllerDefPtr cont = dev->data.controller; + int ret = -1; + + switch (cont->type) { + case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: + ret = qemuDomainDetachControllerDevice(driver, vm, dev, async); + break; + default : + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("'%s' controller cannot be hot unplugged."), + virDomainControllerTypeToString(cont->type)); + } + return ret; +} + +int +qemuDomainDetachDeviceLive(virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + virQEMUDriverPtr driver, + bool async) +{ + int ret = -1; + + switch ((virDomainDeviceType)dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + ret = qemuDomainDetachDeviceDiskLive(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_CONTROLLER: + ret = qemuDomainDetachDeviceControllerLive(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_LEASE: + ret = qemuDomainDetachLease(driver, vm, dev->data.lease); + break; + case VIR_DOMAIN_DEVICE_NET: + ret = qemuDomainDetachNetDevice(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = qemuDomainDetachHostDevice(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_CHR: + ret = qemuDomainDetachChrDevice(driver, vm, dev->data.chr, async); + break; + case VIR_DOMAIN_DEVICE_RNG: + ret = qemuDomainDetachRNGDevice(driver, vm, dev->data.rng, async); + break; + case VIR_DOMAIN_DEVICE_MEMORY: + ret = qemuDomainDetachMemoryDevice(driver, vm, dev->data.memory, async); + break; + case VIR_DOMAIN_DEVICE_SHMEM: + ret = qemuDomainDetachShmemDevice(driver, vm, dev->data.shmem, async); + break; + case VIR_DOMAIN_DEVICE_WATCHDOG: + ret = qemuDomainDetachWatchdog(driver, vm, dev->data.watchdog, async); + break; + case VIR_DOMAIN_DEVICE_INPUT: + ret = qemuDomainDetachInputDevice(vm, dev->data.input, async); + break; + case VIR_DOMAIN_DEVICE_REDIRDEV: + ret = qemuDomainDetachRedirdevDevice(driver, vm, dev->data.redirdev, async); + break; + + case VIR_DOMAIN_DEVICE_VSOCK: + ret = qemuDomainDetachVsockDevice(vm, dev->data.vsock, async); + break; + + case VIR_DOMAIN_DEVICE_FS: + case VIR_DOMAIN_DEVICE_SOUND: + case VIR_DOMAIN_DEVICE_VIDEO: + case VIR_DOMAIN_DEVICE_GRAPHICS: + case VIR_DOMAIN_DEVICE_HUB: + case VIR_DOMAIN_DEVICE_SMARTCARD: + case VIR_DOMAIN_DEVICE_MEMBALLOON: + case VIR_DOMAIN_DEVICE_NVRAM: + case VIR_DOMAIN_DEVICE_NONE: + case VIR_DOMAIN_DEVICE_TPM: + case VIR_DOMAIN_DEVICE_PANIC: + case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_LAST: + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("live detach of device '%s' is not supported"), + virDomainDeviceTypeToString(dev->type)); + break; + } + + if (ret == 0) + ret = qemuDomainUpdateDeviceList(driver, vm, QEMU_ASYNC_JOB_NONE); + + return ret; +} + + static int qemuDomainRemoveVcpu(virQEMUDriverPtr driver, virDomainObjPtr vm, diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 7ac03b7810..61f732c506 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -79,10 +79,6 @@ int qemuDomainFindGraphicsIndex(virDomainDefPtr def, int qemuDomainAttachMemory(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainMemoryDefPtr mem); -int qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainMemoryDefPtr memdef, - bool async); int qemuDomainChangeGraphics(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainGraphicsDefPtr dev); @@ -99,35 +95,6 @@ int qemuDomainChangeNetLinkState(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainNetDefPtr dev, int linkstate); -int qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async);
[1]
-int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); -int qemuDomainDetachNetDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); -int qemuDomainDetachHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); -int qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainShmemDefPtr dev, - bool async); -int qemuDomainDetachWatchdog(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainWatchdogDefPtr watchdog, - bool async); - -int qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainRedirdevDefPtr dev, - bool async);
int qemuDomainAttachInputDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -140,23 +107,33 @@ int qemuDomainAttachVsockDevice(virQEMUDriverPtr driver, int qemuDomainAttachLease(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease); -int qemuDomainDetachLease(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainLeaseDefPtr lease); int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainChrDefPtr chr); -int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainChrDefPtr chr, - bool async); int qemuDomainAttachRNGDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRNGDefPtr rng); -int qemuDomainDetachRNGDevice(virQEMUDriverPtr driver, + +int qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async);
These header movements don't belong to this patch.
+int qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainShmemDefPtr dev, + bool async); +int qemuDomainDetachWatchdog(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainWatchdogDefPtr watchdog, + bool async); +int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, - virDomainRNGDefPtr rng, + virDomainChrDefPtr chr, bool async); +int qemuDomainDetachDeviceLive(virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + virQEMUDriverPtr driver, + bool async);
void qemuDomainRemoveVcpuAlias(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -191,11 +168,4 @@ int qemuDomainSetVcpuInternal(virQEMUDriverPtr driver, virBitmapPtr vcpus, bool state);
-int qemuDomainDetachInputDevice(virDomainObjPtr vm, - virDomainInputDefPtr def, - bool async); - -int qemuDomainDetachVsockDevice(virDomainObjPtr vm, - virDomainVsockDefPtr dev, - bool async); #endif /* LIBVIRT_QEMU_HOTPLUG_H */ -- 2.20.1
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 3/22/19 7:36 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:28:53 -0400, Laine Stump wrote:
This function is going to take on some of the functionality of its subordinate functions, which all live in qemu_hotplug.c.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_driver.c | 95 ----------------------------- src/qemu/qemu_hotplug.c | 129 +++++++++++++++++++++++++++++++++++----- src/qemu/qemu_hotplug.h | 68 ++++++--------------- 3 files changed, 133 insertions(+), 159 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a16eab5467..0b80004c6e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8004,101 +8004,6 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm, return ret; }
-static int -qemuDomainDetachDeviceControllerLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) -{ - virDomainControllerDefPtr cont = dev->data.controller; - int ret = -1; - - switch (cont->type) { - case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: - ret = qemuDomainDetachControllerDevice(driver, vm, dev, async); - break; - default : - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, - _("'%s' controller cannot be hot unplugged."), - virDomainControllerTypeToString(cont->type)); - } - return ret; -} This function is not just moved ...
[...]
static int qemuDomainChangeDiskLive(virDomainObjPtr vm, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1f96f56942..c7aba74c6b 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5540,10 +5540,11 @@ static bool qemuDomainControllerIsBusy(virDomainObjPtr vm, } }
-int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +static int +qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async) ... but replaced. This is a separate semantic change.
No, those are two different functions. In the original code, you had this call chain: qemuDomainDetachDeviceLive qemuDomainDetachDeviceControllerLive qemuDomainDetachControllerDevice and after the patch you have the same call chain. In a later patch, I merge qemuDomainDetachDeviceControllerLive and qemuDomainDeviceControllerDevice into a single function, but in this patch it's pure movement.
{ int idx, ret = -1; virDomainControllerDefPtr detach = NULL; @@ -5594,10 +5595,11 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver,
/* search for a hostdev matching dev and detach it */ -int qemuDomainDetachHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +static int +qemuDomainDetachHostDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async) { virDomainHostdevDefPtr hostdev = dev->data.hostdev; virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; @@ -5821,7 +5823,7 @@ qemuDomainDetachWatchdog(virQEMUDriverPtr driver, }
-int +static int qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRedirdevDefPtr dev, @@ -5865,7 +5867,7 @@ qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, }
-int +static int qemuDomainDetachNetDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev, @@ -5988,7 +5990,7 @@ int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, }
-int +static int qemuDomainDetachRNGDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRNGDefPtr rng, @@ -6034,7 +6036,7 @@ qemuDomainDetachRNGDevice(virQEMUDriverPtr driver, }
-int +static int qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainMemoryDefPtr memdef, @@ -6082,7 +6084,7 @@ qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, }
-int +static int qemuDomainDetachInputDevice(virDomainObjPtr vm, virDomainInputDefPtr def, bool async) @@ -6133,7 +6135,7 @@ qemuDomainDetachInputDevice(virDomainObjPtr vm, }
-int +static int qemuDomainDetachVsockDevice(virDomainObjPtr vm, virDomainVsockDefPtr dev, bool async) @@ -6169,7 +6171,7 @@ qemuDomainDetachVsockDevice(virDomainObjPtr vm, }
-int +static int qemuDomainDetachLease(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease) I'd also prefer if these changes are made separately.
You mean changing the functions from global to static. Sure, I can do that.
@@ -6193,6 +6195,103 @@ qemuDomainDetachLease(virQEMUDriverPtr driver, }
+static int +qemuDomainDetachDeviceControllerLive(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async) +{ + virDomainControllerDefPtr cont = dev->data.controller; + int ret = -1; + + switch (cont->type) { + case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: + ret = qemuDomainDetachControllerDevice(driver, vm, dev, async); + break; + default : + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("'%s' controller cannot be hot unplugged."), + virDomainControllerTypeToString(cont->type)); + } + return ret; +}
^^^^^ This is the original qemuDomainDetachDeviceControllerLive(), moved intact from one file to the other.
+ +int +qemuDomainDetachDeviceLive(virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + virQEMUDriverPtr driver, + bool async) +{ + int ret = -1; + + switch ((virDomainDeviceType)dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + ret = qemuDomainDetachDeviceDiskLive(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_CONTROLLER: + ret = qemuDomainDetachDeviceControllerLive(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_LEASE: + ret = qemuDomainDetachLease(driver, vm, dev->data.lease); + break; + case VIR_DOMAIN_DEVICE_NET: + ret = qemuDomainDetachNetDevice(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = qemuDomainDetachHostDevice(driver, vm, dev, async); + break; + case VIR_DOMAIN_DEVICE_CHR: + ret = qemuDomainDetachChrDevice(driver, vm, dev->data.chr, async); + break; + case VIR_DOMAIN_DEVICE_RNG: + ret = qemuDomainDetachRNGDevice(driver, vm, dev->data.rng, async); + break; + case VIR_DOMAIN_DEVICE_MEMORY: + ret = qemuDomainDetachMemoryDevice(driver, vm, dev->data.memory, async); + break; + case VIR_DOMAIN_DEVICE_SHMEM: + ret = qemuDomainDetachShmemDevice(driver, vm, dev->data.shmem, async); + break; + case VIR_DOMAIN_DEVICE_WATCHDOG: + ret = qemuDomainDetachWatchdog(driver, vm, dev->data.watchdog, async); + break; + case VIR_DOMAIN_DEVICE_INPUT: + ret = qemuDomainDetachInputDevice(vm, dev->data.input, async); + break; + case VIR_DOMAIN_DEVICE_REDIRDEV: + ret = qemuDomainDetachRedirdevDevice(driver, vm, dev->data.redirdev, async); + break; + + case VIR_DOMAIN_DEVICE_VSOCK: + ret = qemuDomainDetachVsockDevice(vm, dev->data.vsock, async); + break; + + case VIR_DOMAIN_DEVICE_FS: + case VIR_DOMAIN_DEVICE_SOUND: + case VIR_DOMAIN_DEVICE_VIDEO: + case VIR_DOMAIN_DEVICE_GRAPHICS: + case VIR_DOMAIN_DEVICE_HUB: + case VIR_DOMAIN_DEVICE_SMARTCARD: + case VIR_DOMAIN_DEVICE_MEMBALLOON: + case VIR_DOMAIN_DEVICE_NVRAM: + case VIR_DOMAIN_DEVICE_NONE: + case VIR_DOMAIN_DEVICE_TPM: + case VIR_DOMAIN_DEVICE_PANIC: + case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_LAST: + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("live detach of device '%s' is not supported"), + virDomainDeviceTypeToString(dev->type)); + break; + } + + if (ret == 0) + ret = qemuDomainUpdateDeviceList(driver, vm, QEMU_ASYNC_JOB_NONE); + + return ret; +} + + static int qemuDomainRemoveVcpu(virQEMUDriverPtr driver, virDomainObjPtr vm, diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 7ac03b7810..61f732c506 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -79,10 +79,6 @@ int qemuDomainFindGraphicsIndex(virDomainDefPtr def, int qemuDomainAttachMemory(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainMemoryDefPtr mem); -int qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainMemoryDefPtr memdef, - bool async); int qemuDomainChangeGraphics(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainGraphicsDefPtr dev); @@ -99,35 +95,6 @@ int qemuDomainChangeNetLinkState(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainNetDefPtr dev, int linkstate); -int qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); [1]
-int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); -int qemuDomainDetachNetDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); -int qemuDomainDetachHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); -int qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainShmemDefPtr dev, - bool async); -int qemuDomainDetachWatchdog(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainWatchdogDefPtr watchdog, - bool async); - -int qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainRedirdevDefPtr dev, - bool async);
int qemuDomainAttachInputDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -140,23 +107,33 @@ int qemuDomainAttachVsockDevice(virQEMUDriverPtr driver, int qemuDomainAttachLease(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease); -int qemuDomainDetachLease(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainLeaseDefPtr lease); int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainChrDefPtr chr); -int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainChrDefPtr chr, - bool async); int qemuDomainAttachRNGDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRNGDefPtr rng); -int qemuDomainDetachRNGDevice(virQEMUDriverPtr driver, + +int qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async); These header movements don't belong to this patch.
Yeah, I suppose. Would you accept them in the same patch where I remove the prototypes for the functions that have become static? Or must that be separate as well?
+int qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainShmemDefPtr dev, + bool async); +int qemuDomainDetachWatchdog(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainWatchdogDefPtr watchdog, + bool async); +int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, - virDomainRNGDefPtr rng, + virDomainChrDefPtr chr, bool async); +int qemuDomainDetachDeviceLive(virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + virQEMUDriverPtr driver, + bool async);
void qemuDomainRemoveVcpuAlias(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -191,11 +168,4 @@ int qemuDomainSetVcpuInternal(virQEMUDriverPtr driver, virBitmapPtr vcpus, bool state);
-int qemuDomainDetachInputDevice(virDomainObjPtr vm, - virDomainInputDefPtr def, - bool async); - -int qemuDomainDetachVsockDevice(virDomainObjPtr vm, - virDomainVsockDefPtr dev, - bool async); #endif /* LIBVIRT_QEMU_HOTPLUG_H */ -- 2.20.1
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On Fri, Mar 22, 2019 at 09:36:31 -0400, Laine Stump wrote:
On 3/22/19 7:36 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:28:53 -0400, Laine Stump wrote:
This function is going to take on some of the functionality of its subordinate functions, which all live in qemu_hotplug.c.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_driver.c | 95 ----------------------------- src/qemu/qemu_hotplug.c | 129 +++++++++++++++++++++++++++++++++++----- src/qemu/qemu_hotplug.h | 68 ++++++--------------- 3 files changed, 133 insertions(+), 159 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a16eab5467..0b80004c6e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8004,101 +8004,6 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm, return ret; } -static int -qemuDomainDetachDeviceControllerLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) -{ - virDomainControllerDefPtr cont = dev->data.controller; - int ret = -1; - - switch (cont->type) { - case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: - ret = qemuDomainDetachControllerDevice(driver, vm, dev, async); - break; - default : - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, - _("'%s' controller cannot be hot unplugged."), - virDomainControllerTypeToString(cont->type)); - } - return ret; -} This function is not just moved ...
[...]
static int qemuDomainChangeDiskLive(virDomainObjPtr vm, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1f96f56942..c7aba74c6b 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5540,10 +5540,11 @@ static bool qemuDomainControllerIsBusy(virDomainObjPtr vm, } } -int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +static int +qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async) ... but replaced. This is a separate semantic change.
No, those are two different functions. In the original code, you had this call chain:
qemuDomainDetachDeviceLive qemuDomainDetachDeviceControllerLive qemuDomainDetachControllerDevice
and after the patch you have the same call chain. In a later patch, I merge qemuDomainDetachDeviceControllerLive and qemuDomainDeviceControllerDevice into a single function, but in this patch it's pure movement.
Okay then. I'd split out the change to exported functions so that it's indeed just moving the described functions ...
[...]
{ + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + bool async); These header movements don't belong to this patch.
Yeah, I suppose. Would you accept them in the same patch where I remove the prototypes for the functions that have become static? Or must that be separate as well?
I don't think you need to move them if you then delete them later. Anyways, if you want to keep them in sync, then move the headers in the patch which is moving the code. If there's more to move than just the headers for functions which are moved it'll be better to do a separate patch to clean up the headers.

qemuDomainDetachDeviceControllerLive() just checks if the controller type is SCSI, and then either returns failure, or calls qemuDomainDetachControllerDevice(). Instead, lets just check for type != SCSI at the top of the latter function, and call it directly. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 42 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index c7aba74c6b..6b713e1c27 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5549,6 +5549,13 @@ qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, int idx, ret = -1; virDomainControllerDefPtr detach = NULL; + if (dev->data.controller->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("'%s' controller cannot be hot unplugged."), + virDomainControllerTypeToString(dev->data.controller->type)); + return -1; + } + if ((idx = virDomainControllerFind(vm->def, dev->data.controller->type, dev->data.controller->idx)) < 0) { @@ -5561,6 +5568,12 @@ qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, detach = vm->def->controllers[idx]; + if (qemuDomainControllerIsBusy(vm, detach)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("device cannot be detached: device is busy")); + goto cleanup; + } + if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device: %s"), @@ -5568,12 +5581,6 @@ qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, goto cleanup; } - if (qemuDomainControllerIsBusy(vm, detach)) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("device cannot be detached: device is busy")); - goto cleanup; - } - if (!async) qemuDomainMarkDeviceForRemoval(vm, &detach->info); @@ -6195,27 +6202,6 @@ qemuDomainDetachLease(virQEMUDriverPtr driver, } -static int -qemuDomainDetachDeviceControllerLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) -{ - virDomainControllerDefPtr cont = dev->data.controller; - int ret = -1; - - switch (cont->type) { - case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: - ret = qemuDomainDetachControllerDevice(driver, vm, dev, async); - break; - default : - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, - _("'%s' controller cannot be hot unplugged."), - virDomainControllerTypeToString(cont->type)); - } - return ret; -} - int qemuDomainDetachDeviceLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev, @@ -6229,7 +6215,7 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, ret = qemuDomainDetachDeviceDiskLive(driver, vm, dev, async); break; case VIR_DOMAIN_DEVICE_CONTROLLER: - ret = qemuDomainDetachDeviceControllerLive(driver, vm, dev, async); + ret = qemuDomainDetachControllerDevice(driver, vm, dev, async); break; case VIR_DOMAIN_DEVICE_LEASE: ret = qemuDomainDetachLease(driver, vm, dev->data.lease); -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:54 -0400, Laine Stump wrote:
qemuDomainDetachDeviceControllerLive() just checks if the controller type is SCSI, and then either returns failure, or calls qemuDomainDetachControllerDevice().
Instead, lets just check for type != SCSI at the top of the latter function, and call it directly.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 42 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 28 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index c7aba74c6b..6b713e1c27 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c
[...]
@@ -5561,6 +5568,12 @@ qemuDomainDetachControllerDevice(virQEMUDriverPtr driver,
detach = vm->def->controllers[idx];
+ if (qemuDomainControllerIsBusy(vm, detach)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("device cannot be detached: device is busy")); + goto cleanup; + } + if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device: %s"), @@ -5568,12 +5581,6 @@ qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, goto cleanup; }
- if (qemuDomainControllerIsBusy(vm, detach)) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("device cannot be detached: device is busy")); - goto cleanup; - } -
This change is not mentioned in the commit message. Also it's not really functionally important.

On 3/22/19 7:39 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:28:54 -0400, Laine Stump wrote:
qemuDomainDetachDeviceControllerLive() just checks if the controller type is SCSI, and then either returns failure, or calls qemuDomainDetachControllerDevice().
Instead, lets just check for type != SCSI at the top of the latter function, and call it directly.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 42 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 28 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index c7aba74c6b..6b713e1c27 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c [...]
@@ -5561,6 +5568,12 @@ qemuDomainDetachControllerDevice(virQEMUDriverPtr driver,
detach = vm->def->controllers[idx];
+ if (qemuDomainControllerIsBusy(vm, detach)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("device cannot be detached: device is busy")); + goto cleanup; + } + if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device: %s"), @@ -5568,12 +5581,6 @@ qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, goto cleanup; }
- if (qemuDomainControllerIsBusy(vm, detach)) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("device cannot be detached: device is busy")); - goto cleanup; - } - This change is not mentioned in the commit message. Also it's not really functionally important.
Yeah, I was just moving things around in the functions to separate "find the device", "do type-specific checking", and "do general checking". I can skip that movement though, and just go directly to the "remove general checking" in the penultimate patch.

qemuDomainDetachDeviceLive() is called from two places in qemu_driver.c. Move the call to qemuDomainUpdateDeviceList() from the end of that function, which is now in qemu_hotplug.c, to the two places that call qemuDomainDetachDeviceLive(). Removing that one call from qemuDomainDetachDeviceList() will permit us to call it from the test driver, replacing the separate calls to qemuDomainDetachDeviceDiskLive(), qemuDomainDetachChrDevice(), qemuDomainDetachShmemDevice() and qemuDomainDetachWatchdog(). We want to do this so that part of the common functionality of those three functions (and the rest of the device-specific Detach functions) can be pulled up into qemuDomainDetachDeviceLive() without breaking the test. (This is done in the next patch). Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_driver.c | 7 +++++++ src/qemu/qemu_hotplug.c | 3 --- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0b80004c6e..8e7bb913a2 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8932,6 +8932,10 @@ qemuDomainDetachDeviceLiveAndConfig(virQEMUDriverPtr driver, if (flags & VIR_DOMAIN_AFFECT_LIVE) { if (qemuDomainDetachDeviceLive(vm, dev_copy, driver, false) < 0) goto cleanup; + + if (qemuDomainUpdateDeviceList(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) + goto cleanup; + /* * update domain status forcibly because the domain status may be * changed even if we failed to attach the device. For example, @@ -9014,6 +9018,9 @@ qemuDomainDetachDeviceAliasLiveAndConfig(virQEMUDriverPtr driver, if (qemuDomainDetachDeviceLive(vm, &dev, driver, true) < 0) goto cleanup; + + if (qemuDomainUpdateDeviceList(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) + goto cleanup; } if (vmdef) { diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 6b713e1c27..ea5b6bf8ac 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -6271,9 +6271,6 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, break; } - if (ret == 0) - ret = qemuDomainUpdateDeviceList(driver, vm, QEMU_ASYNC_JOB_NONE); - return ret; } -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:55 -0400, Laine Stump wrote:
qemuDomainDetachDeviceLive() is called from two places in qemu_driver.c. Move the call to qemuDomainUpdateDeviceList() from the end of that function, which is now in qemu_hotplug.c, to the two places that call qemuDomainDetachDeviceLive().
Removing that one call from qemuDomainDetachDeviceList() will permit us to call it from the test driver, replacing the separate calls to qemuDomainDetachDeviceDiskLive(), qemuDomainDetachChrDevice(), qemuDomainDetachShmemDevice() and qemuDomainDetachWatchdog(). We want to do this so that part of the common functionality of those three functions (and the rest of the device-specific Detach functions) can be pulled up into qemuDomainDetachDeviceLive() without breaking the test. (This is done in the next patch).
I think actually that calling qemuDomainUpdateDeviceList does not make much sense in qemuDomainDetachDeviceLive. At that point the device still may not be removed. Calling it from the DEVICE_DELETED handler should be sufficient.

On 3/22/19 7:41 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:28:55 -0400, Laine Stump wrote:
qemuDomainDetachDeviceLive() is called from two places in qemu_driver.c. Move the call to qemuDomainUpdateDeviceList() from the end of that function, which is now in qemu_hotplug.c, to the two places that call qemuDomainDetachDeviceLive().
Removing that one call from qemuDomainDetachDeviceList() will permit us to call it from the test driver, replacing the separate calls to qemuDomainDetachDeviceDiskLive(), qemuDomainDetachChrDevice(), qemuDomainDetachShmemDevice() and qemuDomainDetachWatchdog(). We want to do this so that part of the common functionality of those three functions (and the rest of the device-specific Detach functions) can be pulled up into qemuDomainDetachDeviceLive() without breaking the test. (This is done in the next patch). I think actually that calling qemuDomainUpdateDeviceList does not make much sense in qemuDomainDetachDeviceLive. At that point the device still may not be removed. Calling it from the DEVICE_DELETED handler should be sufficient.
I trust your word on that, but I was trying to maintain the same functionality, and we previously were calling it. I can add a later patch to remove it completely if you like (In the meantime, I'm getting really tired of all these function names that are almost completely identical. I don't know how many times I typed qemuDomainDetachDeviceLive when I meant to type qemuDomainDetachDeviceList :-/)

The individual qemuDomainDetach*Device() functions will soon be "less functional", since some of the code that is duplicated in 10 of the 12 detach functions is going to be moved into the common qemuDomainDetachDeviceLive(), which calls them all. qemuhotplugtest.c is the only place any of these individual functions is called other than qemuDomainDetachDeviceLive() itself. Fortunately, qemuDomainDetachDeviceLive() provides exactly the functionality needed by the test driver (except that it supports detach of more device types than the test driver has tests for). This patch replaces the calls to qemuDomainDetach(Chr|Shmen|Watchdog|Disk)Device with a single call to the higher level function, allowing us to shift functionality between the lower level functions without breaking the tests. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 15 ++++++++------- src/qemu/qemu_hotplug.h | 16 ---------------- tests/qemuhotplugtest.c | 8 +------- 3 files changed, 9 insertions(+), 30 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index ea5b6bf8ac..2d13fca871 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5389,7 +5389,7 @@ qemuFindDisk(virDomainDefPtr def, const char *dst) return -1; } -int +static int qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev, @@ -5721,7 +5721,7 @@ qemuDomainDetachHostDevice(virQEMUDriverPtr driver, } -int +static int qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainShmemDefPtr dev, @@ -5775,7 +5775,7 @@ qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, } -int +static int qemuDomainDetachWatchdog(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainWatchdogDefPtr dev, @@ -5940,10 +5940,11 @@ qemuDomainDetachNetDevice(virQEMUDriverPtr driver, } -int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainChrDefPtr chr, - bool async) +static int +qemuDomainDetachChrDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainChrDefPtr chr, + bool async) { int ret = -1; qemuDomainObjPrivatePtr priv = vm->privateData; diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 61f732c506..9980c2a756 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -114,22 +114,6 @@ int qemuDomainAttachRNGDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRNGDefPtr rng); -int qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async); -int qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainShmemDefPtr dev, - bool async); -int qemuDomainDetachWatchdog(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainWatchdogDefPtr watchdog, - bool async); -int qemuDomainDetachChrDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainChrDefPtr chr, - bool async); int qemuDomainDetachDeviceLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev, virQEMUDriverPtr driver, diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index 1491c214d0..3eb97d886a 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -147,16 +147,10 @@ testQemuHotplugDetach(virDomainObjPtr vm, switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: - ret = qemuDomainDetachDeviceDiskLive(&driver, vm, dev, async); - break; case VIR_DOMAIN_DEVICE_CHR: - ret = qemuDomainDetachChrDevice(&driver, vm, dev->data.chr, async); - break; case VIR_DOMAIN_DEVICE_SHMEM: - ret = qemuDomainDetachShmemDevice(&driver, vm, dev->data.shmem, async); - break; case VIR_DOMAIN_DEVICE_WATCHDOG: - ret = qemuDomainDetachWatchdog(&driver, vm, dev->data.watchdog, async); + ret = qemuDomainDetachDeviceLive(vm, dev, &driver, async); break; default: VIR_TEST_VERBOSE("device type '%s' cannot be detached\n", -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:56 -0400, Laine Stump wrote:
The individual qemuDomainDetach*Device() functions will soon be "less functional", since some of the code that is duplicated in 10 of the 12 detach functions is going to be moved into the common qemuDomainDetachDeviceLive(), which calls them all.
qemuhotplugtest.c is the only place any of these individual functions is called other than qemuDomainDetachDeviceLive() itself. Fortunately, qemuDomainDetachDeviceLive() provides exactly the functionality needed by the test driver (except that it supports detach of more device types than the test driver has tests for).
This patch replaces the calls to qemuDomainDetach(Chr|Shmen|Watchdog|Disk)Device with a single call to the higher level function, allowing us to shift functionality between the lower level functions without breaking the tests.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 15 ++++++++------- src/qemu/qemu_hotplug.h | 16 ---------------- tests/qemuhotplugtest.c | 8 +------- 3 files changed, 9 insertions(+), 30 deletions(-)
The change makes sense, but keep the function header changes separate.

I'm about to add a second virDomainDeviceDef to this function that will point to the actual device in the domain object. while this is just a partially filled-in example of what to look for. Naming it match will make the code easier to follow. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 54 +++++++++++++++++++++++++---------------- src/qemu/qemu_hotplug.h | 2 +- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 2d13fca871..903a0c46eb 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -6205,52 +6205,64 @@ qemuDomainDetachLease(virQEMUDriverPtr driver, int qemuDomainDetachDeviceLive(virDomainObjPtr vm, - virDomainDeviceDefPtr dev, + virDomainDeviceDefPtr match, virQEMUDriverPtr driver, bool async) { int ret = -1; - switch ((virDomainDeviceType)dev->type) { + switch ((virDomainDeviceType)match->type) { + /* + * lease and chr devices don't follow the standard pattern of + * the others, so they must have their own self-contained + * Detach functions. + */ + case VIR_DOMAIN_DEVICE_LEASE: + return qemuDomainDetachLease(driver, vm, match->data.lease); + + case VIR_DOMAIN_DEVICE_CHR: + return qemuDomainDetachChrDevice(driver, vm, match->data.chr, async); + + /* + * All the other device types follow a very similar pattern - + * First we call type-specific functions to 1) locate the device + * we want to detach (based on the prototype device in match) + * and 2) do any device-type-specific validation to assure it + * is okay to detach the device. + */ case VIR_DOMAIN_DEVICE_DISK: - ret = qemuDomainDetachDeviceDiskLive(driver, vm, dev, async); + ret = qemuDomainDetachDeviceDiskLive(driver, vm, match, async); break; case VIR_DOMAIN_DEVICE_CONTROLLER: - ret = qemuDomainDetachControllerDevice(driver, vm, dev, async); - break; - case VIR_DOMAIN_DEVICE_LEASE: - ret = qemuDomainDetachLease(driver, vm, dev->data.lease); + ret = qemuDomainDetachControllerDevice(driver, vm, match, async); break; case VIR_DOMAIN_DEVICE_NET: - ret = qemuDomainDetachNetDevice(driver, vm, dev, async); + ret = qemuDomainDetachNetDevice(driver, vm, match, async); break; case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = qemuDomainDetachHostDevice(driver, vm, dev, async); - break; - case VIR_DOMAIN_DEVICE_CHR: - ret = qemuDomainDetachChrDevice(driver, vm, dev->data.chr, async); + ret = qemuDomainDetachHostDevice(driver, vm, match, async); break; case VIR_DOMAIN_DEVICE_RNG: - ret = qemuDomainDetachRNGDevice(driver, vm, dev->data.rng, async); + ret = qemuDomainDetachRNGDevice(driver, vm, match->data.rng, async); break; case VIR_DOMAIN_DEVICE_MEMORY: - ret = qemuDomainDetachMemoryDevice(driver, vm, dev->data.memory, async); + ret = qemuDomainDetachMemoryDevice(driver, vm, match->data.memory, async); break; case VIR_DOMAIN_DEVICE_SHMEM: - ret = qemuDomainDetachShmemDevice(driver, vm, dev->data.shmem, async); + ret = qemuDomainDetachShmemDevice(driver, vm, match->data.shmem, async); break; case VIR_DOMAIN_DEVICE_WATCHDOG: - ret = qemuDomainDetachWatchdog(driver, vm, dev->data.watchdog, async); + ret = qemuDomainDetachWatchdog(driver, vm, match->data.watchdog, async); break; case VIR_DOMAIN_DEVICE_INPUT: - ret = qemuDomainDetachInputDevice(vm, dev->data.input, async); + ret = qemuDomainDetachInputDevice(vm, match->data.input, async); break; case VIR_DOMAIN_DEVICE_REDIRDEV: - ret = qemuDomainDetachRedirdevDevice(driver, vm, dev->data.redirdev, async); + ret = qemuDomainDetachRedirdevDevice(driver, vm, match->data.redirdev, async); break; case VIR_DOMAIN_DEVICE_VSOCK: - ret = qemuDomainDetachVsockDevice(vm, dev->data.vsock, async); + ret = qemuDomainDetachVsockDevice(vm, match->data.vsock, async); break; case VIR_DOMAIN_DEVICE_FS: @@ -6268,8 +6280,8 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("live detach of device '%s' is not supported"), - virDomainDeviceTypeToString(dev->type)); - break; + virDomainDeviceTypeToString(match->type)); + return -1; } return ret; diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 9980c2a756..78aa4e991b 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -115,7 +115,7 @@ int qemuDomainAttachRNGDevice(virQEMUDriverPtr driver, virDomainRNGDefPtr rng); int qemuDomainDetachDeviceLive(virDomainObjPtr vm, - virDomainDeviceDefPtr dev, + virDomainDeviceDefPtr match, virQEMUDriverPtr driver, bool async); -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:57 -0400, Laine Stump wrote:
I'm about to add a second virDomainDeviceDef to this function that will point to the actual device in the domain object. while this is just a partially filled-in example of what to look for. Naming it match will make the code easier to follow.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 54 +++++++++++++++++++++++++---------------- src/qemu/qemu_hotplug.h | 2 +- 2 files changed, 34 insertions(+), 22 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 2d13fca871..903a0c46eb 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -6205,52 +6205,64 @@ qemuDomainDetachLease(virQEMUDriverPtr driver,
int qemuDomainDetachDeviceLive(virDomainObjPtr vm, - virDomainDeviceDefPtr dev, + virDomainDeviceDefPtr match, virQEMUDriverPtr driver, bool async) { int ret = -1;
- switch ((virDomainDeviceType)dev->type) { + switch ((virDomainDeviceType)match->type) { + /* + * lease and chr devices don't follow the standard pattern of + * the others, so they must have their own self-contained + * Detach functions. + */ + case VIR_DOMAIN_DEVICE_LEASE: + return qemuDomainDetachLease(driver, vm, match->data.lease); + + case VIR_DOMAIN_DEVICE_CHR: + return qemuDomainDetachChrDevice(driver, vm, match->data.chr, async); + + /* + * All the other device types follow a very similar pattern - + * First we call type-specific functions to 1) locate the device + * we want to detach (based on the prototype device in match) + * and 2) do any device-type-specific validation to assure it + * is okay to detach the device. + */
This is again doing something not mentioned in the commit message.

On 3/22/19 7:50 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:28:57 -0400, Laine Stump wrote:
I'm about to add a second virDomainDeviceDef to this function that will point to the actual device in the domain object. while this is just a partially filled-in example of what to look for. Naming it match will make the code easier to follow.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 54 +++++++++++++++++++++++++---------------- src/qemu/qemu_hotplug.h | 2 +- 2 files changed, 34 insertions(+), 22 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 2d13fca871..903a0c46eb 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -6205,52 +6205,64 @@ qemuDomainDetachLease(virQEMUDriverPtr driver,
int qemuDomainDetachDeviceLive(virDomainObjPtr vm, - virDomainDeviceDefPtr dev, + virDomainDeviceDefPtr match, virQEMUDriverPtr driver, bool async) { int ret = -1;
- switch ((virDomainDeviceType)dev->type) { + switch ((virDomainDeviceType)match->type) { + /* + * lease and chr devices don't follow the standard pattern of + * the others, so they must have their own self-contained + * Detach functions. + */ + case VIR_DOMAIN_DEVICE_LEASE: + return qemuDomainDetachLease(driver, vm, match->data.lease); + + case VIR_DOMAIN_DEVICE_CHR: + return qemuDomainDetachChrDevice(driver, vm, match->data.chr, async); + + /* + * All the other device types follow a very similar pattern - + * First we call type-specific functions to 1) locate the device + * we want to detach (based on the prototype device in match) + * and 2) do any device-type-specific validation to assure it + * is okay to detach the device. + */ This is again doing something not mentioned in the commit message.
Okay, okay. I'll do the movement of those two cases (and additional comments) in a separate patch.

Most of these functions will soon contain only some setup for detaching the device, not the detach code proper (since that code is identical for these devices). Their device specific functions are all being renamed to qemuDomainDetachPrep*(), where * is the name of that device's data member in the virDomainDeviceDef object. Since there will be other code in qemuDomainDetachDeviceLive() after the calls to qemuDomainDetachPrep*() that could still fail, we no longer directly set "ret" with the return code from qemuDomainDetachPrep*() functions, but simply return -1 on failure, and wait until the end of qemuDomainDetachDeviceLive() to set ret = 0. Two of the functions (for Lease and Chr) are atypical, and can't be easily consolidated with the others, so they are just being named qemuDomainDetachDevice*() to make it clearer that they perform the entire operation and not just some setup. Along with the rename, qemuDomainDetachPrep*() functions are also given similar arglists, including an arg called "match" that points to the proto-object of the device we want to delete, and another arg "detach" that is used to return a pointer to the actual object that will be (for now *has been*) detached. To make sure these new args aren't confused with existing local pointers that sometimes had the same name (detach), the local pointer to the device is now named after the device type ("controller", "disk", etc). These point to the same place as (*detach)->data.blah, it's just easier on the eyes to have, e.g., "disk->dst" rather than "(*detach)->data.disk-dst". Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 363 +++++++++++++++++++++++----------------- 1 file changed, 205 insertions(+), 158 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 903a0c46eb..b0e2c738b9 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5390,27 +5390,28 @@ qemuFindDisk(virDomainDefPtr def, const char *dst) } static int -qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +qemuDomainDetachPrepDisk(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr match, + virDomainDiskDefPtr *detach, + bool async) { - virDomainDiskDefPtr detach; + virDomainDiskDefPtr disk; int idx; int ret = -1; - if ((idx = qemuFindDisk(vm->def, dev->data.disk->dst)) < 0) { + if ((idx = qemuFindDisk(vm->def, match->dst)) < 0) { virReportError(VIR_ERR_OPERATION_FAILED, - _("disk %s not found"), dev->data.disk->dst); + _("disk %s not found"), match->dst); return -1; } - detach = vm->def->disks[idx]; + *detach = disk = vm->def->disks[idx]; - switch ((virDomainDiskDevice) detach->device) { + switch ((virDomainDiskDevice) disk->device) { case VIR_DOMAIN_DISK_DEVICE_DISK: case VIR_DOMAIN_DISK_DEVICE_LUN: - switch ((virDomainDiskBus) detach->bus) { + switch ((virDomainDiskBus) disk->bus) { case VIR_DOMAIN_DISK_BUS_VIRTIO: case VIR_DOMAIN_DISK_BUS_USB: case VIR_DOMAIN_DISK_BUS_SCSI: @@ -5428,7 +5429,7 @@ qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, case VIR_DOMAIN_DISK_BUS_LAST: default: - virReportEnumRangeError(virDomainDiskBus, detach->bus); + virReportEnumRangeError(virDomainDiskBus, disk->bus); return -1; } break; @@ -5437,32 +5438,32 @@ qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, case VIR_DOMAIN_DISK_DEVICE_FLOPPY: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("disk device type '%s' cannot be detached"), - virDomainDiskDeviceTypeToString(detach->device)); + virDomainDiskDeviceTypeToString(disk->device)); return -1; case VIR_DOMAIN_DISK_DEVICE_LAST: default: - virReportEnumRangeError(virDomainDiskDevice, detach->device); + virReportEnumRangeError(virDomainDiskDevice, disk->device); break; } - if (qemuDomainDiskBlockJobIsActive(detach)) + if (qemuDomainDiskBlockJobIsActive(disk)) return -1; - if (detach->bus == VIR_DOMAIN_DISK_BUS_VIRTIO && - qemuIsMultiFunctionDevice(vm->def, &detach->info)) { + if (disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO && + qemuIsMultiFunctionDevice(vm->def, &disk->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device: %s"), - detach->dst); + disk->dst); return -1; } if (!async) - qemuDomainMarkDeviceForRemoval(vm, &detach->info); + qemuDomainMarkDeviceForRemoval(vm, &disk->info); - if (qemuDomainDeleteDevice(vm, detach->info.alias) < 0) { + if (qemuDomainDeleteDevice(vm, disk->info.alias) < 0) { if (virDomainObjIsActive(vm)) - virDomainAuditDisk(vm, detach->src, NULL, "detach", false); + virDomainAuditDisk(vm, disk->src, NULL, "detach", false); goto cleanup; } @@ -5470,7 +5471,7 @@ qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, ret = 0; } else { if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveDiskDevice(driver, vm, detach); + ret = qemuDomainRemoveDiskDevice(driver, vm, disk); } cleanup: @@ -5541,57 +5542,55 @@ static bool qemuDomainControllerIsBusy(virDomainObjPtr vm, } static int -qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +qemuDomainDetachPrepController(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainControllerDefPtr match, + virDomainControllerDefPtr *detach, + bool async) { + virDomainControllerDefPtr controller; int idx, ret = -1; - virDomainControllerDefPtr detach = NULL; - if (dev->data.controller->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { + if (match->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("'%s' controller cannot be hot unplugged."), - virDomainControllerTypeToString(dev->data.controller->type)); + virDomainControllerTypeToString(match->type)); return -1; } - if ((idx = virDomainControllerFind(vm->def, - dev->data.controller->type, - dev->data.controller->idx)) < 0) { + if ((idx = virDomainControllerFind(vm->def, match->type, match->idx)) < 0) { virReportError(VIR_ERR_DEVICE_MISSING, _("controller %s:%d not found"), - virDomainControllerTypeToString(dev->data.controller->type), - dev->data.controller->idx); + virDomainControllerTypeToString(match->type), + match->idx); goto cleanup; } - detach = vm->def->controllers[idx]; + *detach = controller = vm->def->controllers[idx]; - if (qemuDomainControllerIsBusy(vm, detach)) { + if (qemuDomainControllerIsBusy(vm, controller)) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("device cannot be detached: device is busy")); goto cleanup; } - if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("cannot hot unplug multifunction PCI device: %s"), - dev->data.disk->dst); + if (qemuIsMultiFunctionDevice(vm->def, &controller->info)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("cannot hot unplug multifunction PCI device")); goto cleanup; } if (!async) - qemuDomainMarkDeviceForRemoval(vm, &detach->info); + qemuDomainMarkDeviceForRemoval(vm, &controller->info); - if (qemuDomainDeleteDevice(vm, detach->info.alias) < 0) + if (qemuDomainDeleteDevice(vm, controller->info.alias) < 0) goto cleanup; if (async) { ret = 0; } else { if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveControllerDevice(driver, vm, detach); + ret = qemuDomainRemoveControllerDevice(driver, vm, controller); } cleanup: @@ -5603,29 +5602,30 @@ qemuDomainDetachControllerDevice(virQEMUDriverPtr driver, /* search for a hostdev matching dev and detach it */ static int -qemuDomainDetachHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +qemuDomainDetachPrepHostdev(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainHostdevDefPtr match, + virDomainHostdevDefPtr *detach, + bool async) { - virDomainHostdevDefPtr hostdev = dev->data.hostdev; - virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; + virDomainHostdevSubsysPtr subsys = &match->source.subsys; virDomainHostdevSubsysUSBPtr usbsrc = &subsys->u.usb; virDomainHostdevSubsysPCIPtr pcisrc = &subsys->u.pci; virDomainHostdevSubsysSCSIPtr scsisrc = &subsys->u.scsi; virDomainHostdevSubsysMediatedDevPtr mdevsrc = &subsys->u.mdev; - virDomainHostdevDefPtr detach = NULL; + virDomainHostdevDefPtr hostdev; int idx; int ret = -1; - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + if (match->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hot unplug is not supported for hostdev mode '%s'"), - virDomainHostdevModeTypeToString(hostdev->mode)); + virDomainHostdevModeTypeToString(match->mode)); return -1; } - idx = virDomainHostdevFind(vm->def, hostdev, &detach); + idx = virDomainHostdevFind(vm->def, match, &hostdev); + *detach = hostdev; if (idx < 0) { switch (subsys->type) { @@ -5678,30 +5678,30 @@ qemuDomainDetachHostDevice(virQEMUDriverPtr driver, return -1; } - if (qemuAssignDeviceHostdevAlias(vm->def, &detach->info->alias, -1) < 0) + if (qemuAssignDeviceHostdevAlias(vm->def, &hostdev->info->alias, -1) < 0) return -1; - if (qemuIsMultiFunctionDevice(vm->def, detach->info)) { + if (qemuIsMultiFunctionDevice(vm->def, hostdev->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device with guest address: " "%.4x:%.2x:%.2x.%.1x"), - detach->info->addr.pci.domain, detach->info->addr.pci.bus, - detach->info->addr.pci.slot, detach->info->addr.pci.function); + hostdev->info->addr.pci.domain, hostdev->info->addr.pci.bus, + hostdev->info->addr.pci.slot, hostdev->info->addr.pci.function); return -1; } - if (!detach->info->alias) { + if (!hostdev->info->alias) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("device cannot be detached without a device alias")); return -1; } if (!async) - qemuDomainMarkDeviceForRemoval(vm, detach->info); + qemuDomainMarkDeviceForRemoval(vm, hostdev->info); - if (qemuDomainDeleteDevice(vm, detach->info->alias) < 0) { + if (qemuDomainDeleteDevice(vm, hostdev->info->alias) < 0) { if (virDomainObjIsActive(vm)) - virDomainAuditHostdev(vm, detach, "detach", false); + virDomainAuditHostdev(vm, hostdev, "detach", false); goto cleanup; } @@ -5709,7 +5709,7 @@ qemuDomainDetachHostDevice(virQEMUDriverPtr driver, ret = 0; } else { if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveHostDevice(driver, vm, detach); + ret = qemuDomainRemoveHostDevice(driver, vm, hostdev); } cleanup: @@ -5722,24 +5722,25 @@ qemuDomainDetachHostDevice(virQEMUDriverPtr driver, static int -qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainShmemDefPtr dev, - bool async) +qemuDomainDetachPrepShmem(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainShmemDefPtr match, + virDomainShmemDefPtr *detach, + bool async) { int ret = -1; ssize_t idx = -1; - virDomainShmemDefPtr shmem = NULL; + virDomainShmemDefPtr shmem; - if ((idx = virDomainShmemDefFind(vm->def, dev)) < 0) { + if ((idx = virDomainShmemDefFind(vm->def, match)) < 0) { virReportError(VIR_ERR_DEVICE_MISSING, _("model '%s' shmem device not present " "in domain configuration"), - virDomainShmemModelTypeToString(dev->model)); + virDomainShmemModelTypeToString(match->model)); return -1; } - shmem = vm->def->shmems[idx]; + *detach = shmem = vm->def->shmems[idx]; switch ((virDomainShmemModel)shmem->model) { case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN: @@ -5776,13 +5777,17 @@ qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, static int -qemuDomainDetachWatchdog(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainWatchdogDefPtr dev, - bool async) +qemuDomainDetachPrepWatchdog(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainWatchdogDefPtr match, + virDomainWatchdogDefPtr *detach, + bool async) { int ret = -1; - virDomainWatchdogDefPtr watchdog = vm->def->watchdog; + virDomainWatchdogDefPtr watchdog; + + + *detach = watchdog = vm->def->watchdog; if (!watchdog) { virReportError(VIR_ERR_DEVICE_MISSING, "%s", @@ -5793,9 +5798,9 @@ qemuDomainDetachWatchdog(virQEMUDriverPtr driver, /* While domains can have up to one watchdog, the one supplied by the user * doesn't necessarily match the one domain has. Refuse to detach in such * case. */ - if (!(watchdog->model == dev->model && - watchdog->action == dev->action && - virDomainDeviceInfoAddressIsEqual(&dev->info, &watchdog->info))) { + if (!(watchdog->model == match->model && + watchdog->action == match->action && + virDomainDeviceInfoAddressIsEqual(&match->info, &watchdog->info))) { virReportError(VIR_ERR_DEVICE_MISSING, _("model '%s' watchdog device not present " "in domain configuration"), @@ -5831,40 +5836,41 @@ qemuDomainDetachWatchdog(virQEMUDriverPtr driver, static int -qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainRedirdevDefPtr dev, - bool async) +qemuDomainDetachPrepRedirdev(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainRedirdevDefPtr match, + virDomainRedirdevDefPtr *detach, + bool async) { int ret = -1; - virDomainRedirdevDefPtr tmpRedirdevDef; + virDomainRedirdevDefPtr redirdev; ssize_t idx; - if ((idx = virDomainRedirdevDefFind(vm->def, dev)) < 0) { + if ((idx = virDomainRedirdevDefFind(vm->def, match)) < 0) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("no matching redirdev was not found")); return -1; } - tmpRedirdevDef = vm->def->redirdevs[idx]; + *detach = redirdev = vm->def->redirdevs[idx]; - if (!tmpRedirdevDef->info.alias) { + if (!redirdev->info.alias) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("alias not set for redirdev device")); return -1; } if (!async) - qemuDomainMarkDeviceForRemoval(vm, &tmpRedirdevDef->info); + qemuDomainMarkDeviceForRemoval(vm, &redirdev->info); - if (qemuDomainDeleteDevice(vm, tmpRedirdevDef->info.alias) < 0) + if (qemuDomainDeleteDevice(vm, redirdev->info.alias) < 0) goto cleanup; if (async) { ret = 0; } else { if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveRedirdevDevice(driver, vm, tmpRedirdevDef); + ret = qemuDomainRemoveRedirdevDevice(driver, vm, redirdev); } cleanup: @@ -5875,53 +5881,54 @@ qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, static int -qemuDomainDetachNetDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +qemuDomainDetachPrepNet(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainNetDefPtr match, + virDomainNetDefPtr *detach, + bool async) { int detachidx, ret = -1; - virDomainNetDefPtr detach = NULL; + virDomainNetDefPtr net; - if ((detachidx = virDomainNetFindIdx(vm->def, dev->data.net)) < 0) + if ((detachidx = virDomainNetFindIdx(vm->def, match)) < 0) goto cleanup; - detach = vm->def->nets[detachidx]; + *detach = net = vm->def->nets[detachidx]; - if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) { + if (qemuIsMultiFunctionDevice(vm->def, &net->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device: %s"), - detach->ifname); + net->ifname); goto cleanup; } - if (!detach->info.alias) { - if (qemuAssignDeviceNetAlias(vm->def, detach, -1) < 0) + if (!net->info.alias) { + if (qemuAssignDeviceNetAlias(vm->def, net, -1) < 0) goto cleanup; } - if (virDomainNetGetActualBandwidth(detach) && - virNetDevSupportBandwidth(virDomainNetGetActualType(detach)) && - virNetDevBandwidthClear(detach->ifname) < 0) + if (virDomainNetGetActualBandwidth(net) && + virNetDevSupportBandwidth(virDomainNetGetActualType(net)) && + virNetDevBandwidthClear(net->ifname) < 0) VIR_WARN("cannot clear bandwidth setting for device : %s", - detach->ifname); + net->ifname); /* deactivate the tap/macvtap device on the host, which could also * affect the parent device (e.g. macvtap passthrough mode sets * the parent device offline) */ - ignore_value(qemuInterfaceStopDevice(detach)); + ignore_value(qemuInterfaceStopDevice(net)); if (!async) - qemuDomainMarkDeviceForRemoval(vm, &detach->info); + qemuDomainMarkDeviceForRemoval(vm, &net->info); - if (qemuDomainDeleteDevice(vm, detach->info.alias) < 0) { + if (qemuDomainDeleteDevice(vm, net->info.alias) < 0) { if (virDomainObjIsActive(vm)) { /* the audit message has a different format for hostdev network devices */ - if (virDomainNetGetActualType(detach) == VIR_DOMAIN_NET_TYPE_HOSTDEV) - virDomainAuditHostdev(vm, virDomainNetGetActualHostdev(detach), "detach", false); + if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_HOSTDEV) + virDomainAuditHostdev(vm, virDomainNetGetActualHostdev(net), "detach", false); else - virDomainAuditNet(vm, detach, NULL, "detach", false); + virDomainAuditNet(vm, net, NULL, "detach", false); } goto cleanup; } @@ -5930,7 +5937,7 @@ qemuDomainDetachNetDevice(virQEMUDriverPtr driver, ret = 0; } else { if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveNetDevice(driver, vm, detach); + ret = qemuDomainRemoveNetDevice(driver, vm, net); } cleanup: @@ -5941,7 +5948,7 @@ qemuDomainDetachNetDevice(virQEMUDriverPtr driver, static int -qemuDomainDetachChrDevice(virQEMUDriverPtr driver, +qemuDomainDetachDeviceChr(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainChrDefPtr chr, bool async) @@ -5999,42 +6006,43 @@ qemuDomainDetachChrDevice(virQEMUDriverPtr driver, static int -qemuDomainDetachRNGDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainRNGDefPtr rng, - bool async) +qemuDomainDetachPrepRNG(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainRNGDefPtr match, + virDomainRNGDefPtr *detach, + bool async) { ssize_t idx; - virDomainRNGDefPtr tmpRNG; + virDomainRNGDefPtr rng; int ret = -1; - if ((idx = virDomainRNGFind(vm->def, rng)) < 0) { + if ((idx = virDomainRNGFind(vm->def, match)) < 0) { virReportError(VIR_ERR_DEVICE_MISSING, _("model '%s' RNG device not present " "in domain configuration"), - virDomainRNGBackendTypeToString(rng->model)); + virDomainRNGBackendTypeToString(match->model)); return -1; } - tmpRNG = vm->def->rngs[idx]; + *detach = rng = vm->def->rngs[idx]; - if (!tmpRNG->info.alias) { + if (!rng->info.alias) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("alias not set for RNG device")); return -1; } if (!async) - qemuDomainMarkDeviceForRemoval(vm, &tmpRNG->info); + qemuDomainMarkDeviceForRemoval(vm, &rng->info); - if (qemuDomainDeleteDevice(vm, tmpRNG->info.alias) < 0) + if (qemuDomainDeleteDevice(vm, rng->info.alias) < 0) goto cleanup; if (async) { ret = 0; } else { if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveRNGDevice(driver, vm, tmpRNG); + ret = qemuDomainRemoveRNGDevice(driver, vm, rng); } cleanup: @@ -6045,26 +6053,27 @@ qemuDomainDetachRNGDevice(virQEMUDriverPtr driver, static int -qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainMemoryDefPtr memdef, - bool async) +qemuDomainDetachPrepMemory(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainMemoryDefPtr match, + virDomainMemoryDefPtr *detach, + bool async) { virDomainMemoryDefPtr mem; int idx; int ret = -1; - qemuDomainMemoryDeviceAlignSize(vm->def, memdef); + qemuDomainMemoryDeviceAlignSize(vm->def, match); - if ((idx = virDomainMemoryFindByDef(vm->def, memdef)) < 0) { + if ((idx = virDomainMemoryFindByDef(vm->def, match)) < 0) { virReportError(VIR_ERR_DEVICE_MISSING, _("model '%s' memory device not present " "in the domain configuration"), - virDomainMemoryModelTypeToString(memdef->model)); + virDomainMemoryModelTypeToString(match->model)); return -1; } - mem = vm->def->mems[idx]; + *detach = mem = vm->def->mems[idx]; if (!mem->info.alias) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -6093,20 +6102,21 @@ qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, static int -qemuDomainDetachInputDevice(virDomainObjPtr vm, - virDomainInputDefPtr def, - bool async) +qemuDomainDetachPrepInput(virDomainObjPtr vm, + virDomainInputDefPtr match, + virDomainInputDefPtr *detach, + bool async) { virDomainInputDefPtr input; int ret = -1; int idx; - if ((idx = virDomainInputDefFind(vm->def, def)) < 0) { + if ((idx = virDomainInputDefFind(vm->def, match)) < 0) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("matching input device not found")); return -1; } - input = vm->def->inputs[idx]; + *detach = input = vm->def->inputs[idx]; switch ((virDomainInputBus) input->bus) { case VIR_DOMAIN_INPUT_BUS_PS2: @@ -6144,16 +6154,18 @@ qemuDomainDetachInputDevice(virDomainObjPtr vm, static int -qemuDomainDetachVsockDevice(virDomainObjPtr vm, - virDomainVsockDefPtr dev, - bool async) +qemuDomainDetachPrepVsock(virDomainObjPtr vm, + virDomainVsockDefPtr match, + virDomainVsockDefPtr *detach, + bool async) { - virDomainVsockDefPtr vsock = vm->def->vsock; + virDomainVsockDefPtr vsock; int ret = -1; + *detach = vsock = vm->def->vsock; if (!vsock || - !virDomainVsockDefEquals(dev, vsock)) { + !virDomainVsockDefEquals(match, vsock)) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("matching vsock device not found")); return -1; @@ -6180,9 +6192,9 @@ qemuDomainDetachVsockDevice(virDomainObjPtr vm, static int -qemuDomainDetachLease(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainLeaseDefPtr lease) +qemuDomainDetachDeviceLease(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainLeaseDefPtr lease) { virDomainLeaseDefPtr det_lease; int idx; @@ -6209,6 +6221,7 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, virQEMUDriverPtr driver, bool async) { + virDomainDeviceDef detach = { .type = match->type }; int ret = -1; switch ((virDomainDeviceType)match->type) { @@ -6218,10 +6231,10 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, * Detach functions. */ case VIR_DOMAIN_DEVICE_LEASE: - return qemuDomainDetachLease(driver, vm, match->data.lease); + return qemuDomainDetachDeviceLease(driver, vm, match->data.lease); case VIR_DOMAIN_DEVICE_CHR: - return qemuDomainDetachChrDevice(driver, vm, match->data.chr, async); + return qemuDomainDetachDeviceChr(driver, vm, match->data.chr, async); /* * All the other device types follow a very similar pattern - @@ -6231,38 +6244,70 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, * is okay to detach the device. */ case VIR_DOMAIN_DEVICE_DISK: - ret = qemuDomainDetachDeviceDiskLive(driver, vm, match, async); + if (qemuDomainDetachPrepDisk(driver, vm, match->data.disk, + &detach.data.disk, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_CONTROLLER: - ret = qemuDomainDetachControllerDevice(driver, vm, match, async); + if (qemuDomainDetachPrepController(driver, vm, match->data.controller, + &detach.data.controller, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_NET: - ret = qemuDomainDetachNetDevice(driver, vm, match, async); + if (qemuDomainDetachPrepNet(driver, vm, match->data.net, + &detach.data.net, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = qemuDomainDetachHostDevice(driver, vm, match, async); + if (qemuDomainDetachPrepHostdev(driver, vm, match->data.hostdev, + &detach.data.hostdev, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_RNG: - ret = qemuDomainDetachRNGDevice(driver, vm, match->data.rng, async); + if (qemuDomainDetachPrepRNG(driver, vm, match->data.rng, + &detach.data.rng, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_MEMORY: - ret = qemuDomainDetachMemoryDevice(driver, vm, match->data.memory, async); + if (qemuDomainDetachPrepMemory(driver, vm, match->data.memory, + &detach.data.memory, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_SHMEM: - ret = qemuDomainDetachShmemDevice(driver, vm, match->data.shmem, async); + if (qemuDomainDetachPrepShmem(driver, vm, match->data.shmem, + &detach.data.shmem, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_WATCHDOG: - ret = qemuDomainDetachWatchdog(driver, vm, match->data.watchdog, async); + if (qemuDomainDetachPrepWatchdog(driver, vm, match->data.watchdog, + &detach.data.watchdog, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_INPUT: - ret = qemuDomainDetachInputDevice(vm, match->data.input, async); + if (qemuDomainDetachPrepInput(vm, match->data.input, + &detach.data.input, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_REDIRDEV: - ret = qemuDomainDetachRedirdevDevice(driver, vm, match->data.redirdev, async); + if (qemuDomainDetachPrepRedirdev(driver, vm, match->data.redirdev, + &detach.data.redirdev, async) < 0) { + return -1; + } break; - case VIR_DOMAIN_DEVICE_VSOCK: - ret = qemuDomainDetachVsockDevice(vm, match->data.vsock, async); + if (qemuDomainDetachPrepVsock(vm, match->data.vsock, + &detach.data.vsock, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_FS: @@ -6284,6 +6329,8 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, return -1; } + ret = 0; + return ret; } -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:58 -0400, Laine Stump wrote:
Most of these functions will soon contain only some setup for detaching the device, not the detach code proper (since that code is identical for these devices). Their device specific functions are all being renamed to qemuDomainDetachPrep*(), where * is the name of that device's data member in the virDomainDeviceDef object.
Since there will be other code in qemuDomainDetachDeviceLive() after the calls to qemuDomainDetachPrep*() that could still fail, we no longer directly set "ret" with the return code from qemuDomainDetachPrep*() functions, but simply return -1 on failure, and wait until the end of qemuDomainDetachDeviceLive() to set ret = 0.
Two of the functions (for Lease and Chr) are atypical, and can't be easily consolidated with the others, so they are just being named qemuDomainDetachDevice*() to make it clearer that they perform the entire operation and not just some setup.
You can do this separately.
Along with the rename, qemuDomainDetachPrep*() functions are also given similar arglists, including an arg called "match" that points to the proto-object of the device we want to delete, and another arg "detach" that is used to return a pointer to the actual object that will be (for now *has been*) detached. To make sure these new args aren't confused with existing local pointers that sometimes had the same name (detach), the local pointer to the device is now named after the device type ("controller", "disk", etc). These point to the same place as (*detach)->data.blah, it's just easier on the eyes to have, e.g., "disk->dst" rather than "(*detach)->data.disk-dst".
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 363 +++++++++++++++++++++++----------------- 1 file changed, 205 insertions(+), 158 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 903a0c46eb..b0e2c738b9 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5390,27 +5390,28 @@ qemuFindDisk(virDomainDefPtr def, const char *dst) }
static int -qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +qemuDomainDetachPrepDisk(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr match, + virDomainDiskDefPtr *detach, + bool async) { - virDomainDiskDefPtr detach; + virDomainDiskDefPtr disk;
This reverts change done in previous commit of this series: qemu_hotplug: refactor qemuDomainDetachDiskLive and qemuDomainDetachDiskDevice
int idx; int ret = -1;
- if ((idx = qemuFindDisk(vm->def, dev->data.disk->dst)) < 0) { + if ((idx = qemuFindDisk(vm->def, match->dst)) < 0) {
[...]
static int -qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainShmemDefPtr dev, - bool async) +qemuDomainDetachPrepShmem(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainShmemDefPtr match, + virDomainShmemDefPtr *detach, + bool async) { int ret = -1; ssize_t idx = -1; - virDomainShmemDefPtr shmem = NULL; + virDomainShmemDefPtr shmem;
I don't see a point in removing initialization of the variable.
- if ((idx = virDomainShmemDefFind(vm->def, dev)) < 0) { + if ((idx = virDomainShmemDefFind(vm->def, match)) < 0) { virReportError(VIR_ERR_DEVICE_MISSING, _("model '%s' shmem device not present " "in domain configuration"), - virDomainShmemModelTypeToString(dev->model)); + virDomainShmemModelTypeToString(match->model)); return -1; }
- shmem = vm->def->shmems[idx]; + *detach = shmem = vm->def->shmems[idx];
switch ((virDomainShmemModel)shmem->model) { case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN:
[...]
@@ -5875,53 +5881,54 @@ qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver,
static int -qemuDomainDetachNetDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +qemuDomainDetachPrepNet(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainNetDefPtr match, + virDomainNetDefPtr *detach, + bool async) { int detachidx, ret = -1; - virDomainNetDefPtr detach = NULL; + virDomainNetDefPtr net;
Same here, I don't see the reason to stop initializing it to NULL.
- if ((detachidx = virDomainNetFindIdx(vm->def, dev->data.net)) < 0) + if ((detachidx = virDomainNetFindIdx(vm->def, match)) < 0) goto cleanup;
[...]
@@ -6180,9 +6192,9 @@ qemuDomainDetachVsockDevice(virDomainObjPtr vm,
static int -qemuDomainDetachLease(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainLeaseDefPtr lease) +qemuDomainDetachDeviceLease(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainLeaseDefPtr lease) { virDomainLeaseDefPtr det_lease; int idx; @@ -6209,6 +6221,7 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, virQEMUDriverPtr driver, bool async) { + virDomainDeviceDef detach = { .type = match->type }; int ret = -1;
switch ((virDomainDeviceType)match->type) { @@ -6218,10 +6231,10 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, * Detach functions. */ case VIR_DOMAIN_DEVICE_LEASE: - return qemuDomainDetachLease(driver, vm, match->data.lease); + return qemuDomainDetachDeviceLease(driver, vm, match->data.lease);
case VIR_DOMAIN_DEVICE_CHR: - return qemuDomainDetachChrDevice(driver, vm, match->data.chr, async); + return qemuDomainDetachDeviceChr(driver, vm, match->data.chr, async);
/* * All the other device types follow a very similar pattern - @@ -6231,38 +6244,70 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, * is okay to detach the device. */ case VIR_DOMAIN_DEVICE_DISK: - ret = qemuDomainDetachDeviceDiskLive(driver, vm, match, async); + if (qemuDomainDetachPrepDisk(driver, vm, match->data.disk, + &detach.data.disk, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_CONTROLLER: - ret = qemuDomainDetachControllerDevice(driver, vm, match, async); + if (qemuDomainDetachPrepController(driver, vm, match->data.controller, + &detach.data.controller, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_NET: - ret = qemuDomainDetachNetDevice(driver, vm, match, async); + if (qemuDomainDetachPrepNet(driver, vm, match->data.net, + &detach.data.net, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = qemuDomainDetachHostDevice(driver, vm, match, async); + if (qemuDomainDetachPrepHostdev(driver, vm, match->data.hostdev, + &detach.data.hostdev, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_RNG: - ret = qemuDomainDetachRNGDevice(driver, vm, match->data.rng, async); + if (qemuDomainDetachPrepRNG(driver, vm, match->data.rng, + &detach.data.rng, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_MEMORY: - ret = qemuDomainDetachMemoryDevice(driver, vm, match->data.memory, async); + if (qemuDomainDetachPrepMemory(driver, vm, match->data.memory, + &detach.data.memory, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_SHMEM: - ret = qemuDomainDetachShmemDevice(driver, vm, match->data.shmem, async); + if (qemuDomainDetachPrepShmem(driver, vm, match->data.shmem, + &detach.data.shmem, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_WATCHDOG: - ret = qemuDomainDetachWatchdog(driver, vm, match->data.watchdog, async); + if (qemuDomainDetachPrepWatchdog(driver, vm, match->data.watchdog, + &detach.data.watchdog, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_INPUT: - ret = qemuDomainDetachInputDevice(vm, match->data.input, async); + if (qemuDomainDetachPrepInput(vm, match->data.input, + &detach.data.input, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_REDIRDEV: - ret = qemuDomainDetachRedirdevDevice(driver, vm, match->data.redirdev, async); + if (qemuDomainDetachPrepRedirdev(driver, vm, match->data.redirdev, + &detach.data.redirdev, async) < 0) { + return -1; + } break; - case VIR_DOMAIN_DEVICE_VSOCK: - ret = qemuDomainDetachVsockDevice(vm, match->data.vsock, async); + if (qemuDomainDetachPrepVsock(vm, match->data.vsock, + &detach.data.vsock, async) < 0) { + return -1; + } break;
case VIR_DOMAIN_DEVICE_FS: @@ -6284,6 +6329,8 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, return -1; }
+ ret = 0; +
It does not seem to make sense to have 'ret' here after you removed use of 'ret'.
return ret; }
-- 2.20.1
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 3/22/19 8:10 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:28:58 -0400, Laine Stump wrote:
Most of these functions will soon contain only some setup for detaching the device, not the detach code proper (since that code is identical for these devices). Their device specific functions are all being renamed to qemuDomainDetachPrep*(), where * is the name of that device's data member in the virDomainDeviceDef object.
Since there will be other code in qemuDomainDetachDeviceLive() after the calls to qemuDomainDetachPrep*() that could still fail, we no longer directly set "ret" with the return code from qemuDomainDetachPrep*() functions, but simply return -1 on failure, and wait until the end of qemuDomainDetachDeviceLive() to set ret = 0.
Two of the functions (for Lease and Chr) are atypical, and can't be easily consolidated with the others, so they are just being named qemuDomainDetachDevice*() to make it clearer that they perform the entire operation and not just some setup. You can do this separately.
Along with the rename, qemuDomainDetachPrep*() functions are also given similar arglists, including an arg called "match" that points to the proto-object of the device we want to delete, and another arg "detach" that is used to return a pointer to the actual object that will be (for now *has been*) detached. To make sure these new args aren't confused with existing local pointers that sometimes had the same name (detach), the local pointer to the device is now named after the device type ("controller", "disk", etc). These point to the same place as (*detach)->data.blah, it's just easier on the eyes to have, e.g., "disk->dst" rather than "(*detach)->data.disk-dst".
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 363 +++++++++++++++++++++++----------------- 1 file changed, 205 insertions(+), 158 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 903a0c46eb..b0e2c738b9 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5390,27 +5390,28 @@ qemuFindDisk(virDomainDefPtr def, const char *dst) }
static int -qemuDomainDetachDeviceDiskLive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +qemuDomainDetachPrepDisk(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr match, + virDomainDiskDefPtr *detach, + bool async) { - virDomainDiskDefPtr detach; + virDomainDiskDefPtr disk; This reverts change done in previous commit of this series:
qemu_hotplug: refactor qemuDomainDetachDiskLive and qemuDomainDetachDiskDevice
int idx; int ret = -1;
- if ((idx = qemuFindDisk(vm->def, dev->data.disk->dst)) < 0) { + if ((idx = qemuFindDisk(vm->def, match->dst)) < 0) {
[...]
static int -qemuDomainDetachShmemDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainShmemDefPtr dev, - bool async) +qemuDomainDetachPrepShmem(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainShmemDefPtr match, + virDomainShmemDefPtr *detach, + bool async) { int ret = -1; ssize_t idx = -1; - virDomainShmemDefPtr shmem = NULL; + virDomainShmemDefPtr shmem;
I don't see a point in removing initialization of the variable.
Well... I don't see the point in *keeping* it. :-P It's never used without being set, so it's pointless. And not having a NULL initialization makes it more consistent with the functions for other device types.
- if ((idx = virDomainShmemDefFind(vm->def, dev)) < 0) { + if ((idx = virDomainShmemDefFind(vm->def, match)) < 0) { virReportError(VIR_ERR_DEVICE_MISSING, _("model '%s' shmem device not present " "in domain configuration"), - virDomainShmemModelTypeToString(dev->model)); + virDomainShmemModelTypeToString(match->model)); return -1; }
- shmem = vm->def->shmems[idx]; + *detach = shmem = vm->def->shmems[idx];
switch ((virDomainShmemModel)shmem->model) { case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN:
[...]
@@ -5875,53 +5881,54 @@ qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver,
static int -qemuDomainDetachNetDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - bool async) +qemuDomainDetachPrepNet(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainNetDefPtr match, + virDomainNetDefPtr *detach, + bool async) { int detachidx, ret = -1; - virDomainNetDefPtr detach = NULL; + virDomainNetDefPtr net; Same here, I don't see the reason to stop initializing it to NULL.
Same comment as above. But if you're really attached to it, then I'll continue initializing it to NULL.
- if ((detachidx = virDomainNetFindIdx(vm->def, dev->data.net)) < 0) + if ((detachidx = virDomainNetFindIdx(vm->def, match)) < 0) goto cleanup;
[...]
@@ -6180,9 +6192,9 @@ qemuDomainDetachVsockDevice(virDomainObjPtr vm,
static int -qemuDomainDetachLease(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainLeaseDefPtr lease) +qemuDomainDetachDeviceLease(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainLeaseDefPtr lease) { virDomainLeaseDefPtr det_lease; int idx; @@ -6209,6 +6221,7 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, virQEMUDriverPtr driver, bool async) { + virDomainDeviceDef detach = { .type = match->type }; int ret = -1;
switch ((virDomainDeviceType)match->type) { @@ -6218,10 +6231,10 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, * Detach functions. */ case VIR_DOMAIN_DEVICE_LEASE: - return qemuDomainDetachLease(driver, vm, match->data.lease); + return qemuDomainDetachDeviceLease(driver, vm, match->data.lease);
case VIR_DOMAIN_DEVICE_CHR: - return qemuDomainDetachChrDevice(driver, vm, match->data.chr, async); + return qemuDomainDetachDeviceChr(driver, vm, match->data.chr, async);
/* * All the other device types follow a very similar pattern - @@ -6231,38 +6244,70 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, * is okay to detach the device. */ case VIR_DOMAIN_DEVICE_DISK: - ret = qemuDomainDetachDeviceDiskLive(driver, vm, match, async); + if (qemuDomainDetachPrepDisk(driver, vm, match->data.disk, + &detach.data.disk, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_CONTROLLER: - ret = qemuDomainDetachControllerDevice(driver, vm, match, async); + if (qemuDomainDetachPrepController(driver, vm, match->data.controller, + &detach.data.controller, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_NET: - ret = qemuDomainDetachNetDevice(driver, vm, match, async); + if (qemuDomainDetachPrepNet(driver, vm, match->data.net, + &detach.data.net, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = qemuDomainDetachHostDevice(driver, vm, match, async); + if (qemuDomainDetachPrepHostdev(driver, vm, match->data.hostdev, + &detach.data.hostdev, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_RNG: - ret = qemuDomainDetachRNGDevice(driver, vm, match->data.rng, async); + if (qemuDomainDetachPrepRNG(driver, vm, match->data.rng, + &detach.data.rng, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_MEMORY: - ret = qemuDomainDetachMemoryDevice(driver, vm, match->data.memory, async); + if (qemuDomainDetachPrepMemory(driver, vm, match->data.memory, + &detach.data.memory, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_SHMEM: - ret = qemuDomainDetachShmemDevice(driver, vm, match->data.shmem, async); + if (qemuDomainDetachPrepShmem(driver, vm, match->data.shmem, + &detach.data.shmem, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_WATCHDOG: - ret = qemuDomainDetachWatchdog(driver, vm, match->data.watchdog, async); + if (qemuDomainDetachPrepWatchdog(driver, vm, match->data.watchdog, + &detach.data.watchdog, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_INPUT: - ret = qemuDomainDetachInputDevice(vm, match->data.input, async); + if (qemuDomainDetachPrepInput(vm, match->data.input, + &detach.data.input, async) < 0) { + return -1; + } break; case VIR_DOMAIN_DEVICE_REDIRDEV: - ret = qemuDomainDetachRedirdevDevice(driver, vm, match->data.redirdev, async); + if (qemuDomainDetachPrepRedirdev(driver, vm, match->data.redirdev, + &detach.data.redirdev, async) < 0) { + return -1; + } break; - case VIR_DOMAIN_DEVICE_VSOCK: - ret = qemuDomainDetachVsockDevice(vm, match->data.vsock, async); + if (qemuDomainDetachPrepVsock(vm, match->data.vsock, + &detach.data.vsock, async) < 0) { + return -1; + } break;
case VIR_DOMAIN_DEVICE_FS: @@ -6284,6 +6329,8 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, return -1; }
+ ret = 0; + It does not seem to make sense to have 'ret' here after you removed use of 'ret'.
When more code is added here in patch 20, ret will once again be used. I figured it would be better to leave it in, rather than remove it just to add it back. I can do that if you want though.
return ret; }
-- 2.20.1
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

This function can be called with a virDomainDevicePtr and whether or not the removal was successful, and it will call the appropriate virDomainAudit*() function with the appropriate args for whatever type of device it's given (or do nothing, if that's appropriate). This permits generalizing some code that currently has a separate copy for each type of device. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index b0e2c738b9..5e5ffe16d3 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5208,6 +5208,78 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, } +static inline void +qemuDomainRemoveAuditDevice(virDomainObjPtr vm, + virDomainDeviceDefPtr detach, + bool success) +{ + switch ((virDomainDeviceType)detach->type) { + case VIR_DOMAIN_DEVICE_CHR: + virDomainAuditChardev(vm, detach->data.chr, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_DISK: + virDomainAuditDisk(vm, detach->data.disk->src, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_NET: + virDomainAuditNet(vm, detach->data.net, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_HOSTDEV: + virDomainAuditHostdev(vm, detach->data.hostdev, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_RNG: + virDomainAuditRNG(vm, detach->data.rng, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_MEMORY: { + unsigned long long oldmem = virDomainDefGetMemoryTotal(vm->def); + unsigned long long newmem = oldmem - detach->data.memory->size; + + virDomainAuditMemory(vm, oldmem, newmem, "update", success); + break; + } + + case VIR_DOMAIN_DEVICE_SHMEM: + virDomainAuditShmem(vm, detach->data.shmem, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_INPUT: + virDomainAuditInput(vm, detach->data.input, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_REDIRDEV: + virDomainAuditRedirdev(vm, detach->data.redirdev, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_LEASE: + case VIR_DOMAIN_DEVICE_CONTROLLER: + case VIR_DOMAIN_DEVICE_WATCHDOG: + case VIR_DOMAIN_DEVICE_VSOCK: + /* These devices don't have associated audit logs */ + break; + + case VIR_DOMAIN_DEVICE_FS: + case VIR_DOMAIN_DEVICE_SOUND: + case VIR_DOMAIN_DEVICE_VIDEO: + case VIR_DOMAIN_DEVICE_GRAPHICS: + case VIR_DOMAIN_DEVICE_HUB: + case VIR_DOMAIN_DEVICE_SMARTCARD: + case VIR_DOMAIN_DEVICE_MEMBALLOON: + case VIR_DOMAIN_DEVICE_NVRAM: + case VIR_DOMAIN_DEVICE_NONE: + case VIR_DOMAIN_DEVICE_TPM: + case VIR_DOMAIN_DEVICE_PANIC: + case VIR_DOMAIN_DEVICE_IOMMU: + case VIR_DOMAIN_DEVICE_LAST: + /* these will never happen, these devices can't be unplugged */ + break; + } +} + + int qemuDomainRemoveDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, -- 2.20.1

On Thu, Mar 21, 2019 at 18:28:59 -0400, Laine Stump wrote:
This function can be called with a virDomainDevicePtr and whether or not the removal was successful, and it will call the appropriate virDomainAudit*() function with the appropriate args for whatever type of device it's given (or do nothing, if that's appropriate). This permits generalizing some code that currently has a separate copy for each type of device.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index b0e2c738b9..5e5ffe16d3 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5208,6 +5208,78 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, }
+static inline void
Don't use inline, it's pointless.
+qemuDomainRemoveAuditDevice(virDomainObjPtr vm, + virDomainDeviceDefPtr detach, + bool success)
@success is always false in the one call place the other patches add. Especially I doubt it will ever be different. All async device deletion callbacks do their own per-device-type audit call on success. This function is unused thus breaks build.
+{ + switch ((virDomainDeviceType)detach->type) { + case VIR_DOMAIN_DEVICE_CHR: + virDomainAuditChardev(vm, detach->data.chr, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_DISK: + virDomainAuditDisk(vm, detach->data.disk->src, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_NET: + virDomainAuditNet(vm, detach->data.net, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_HOSTDEV: + virDomainAuditHostdev(vm, detach->data.hostdev, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_RNG: + virDomainAuditRNG(vm, detach->data.rng, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_MEMORY: { + unsigned long long oldmem = virDomainDefGetMemoryTotal(vm->def); + unsigned long long newmem = oldmem - detach->data.memory->size; + + virDomainAuditMemory(vm, oldmem, newmem, "update", success);
This is currently not audited, I think it should be added separately.

On 3/22/19 8:24 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:28:59 -0400, Laine Stump wrote:
This function can be called with a virDomainDevicePtr and whether or not the removal was successful, and it will call the appropriate virDomainAudit*() function with the appropriate args for whatever type of device it's given (or do nothing, if that's appropriate). This permits generalizing some code that currently has a separate copy for each type of device.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index b0e2c738b9..5e5ffe16d3 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5208,6 +5208,78 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, }
+static inline void Don't use inline, it's pointless.
I used inline to prevent the "static function defined but not used" error from the compiler. It's removed in the next patch when I start calling it.
+qemuDomainRemoveAuditDevice(virDomainObjPtr vm, + virDomainDeviceDefPtr detach, + bool success) @success is always false in the one call place the other patches add. Especially I doubt it will ever be different.
I made (and placed) the function with the assumption that in the future the qemuDomainRemove*Device() functions might be refactored in a similar manner (patch 21 is the first step in this, actually), and we would then wnat to call this function from common code rather than each type-specific function calling their own type-specific audit. (I even considered making it *more* generic by sending attach/detach/update, but decided that was a bit too ambitious).
All async device deletion callbacks do their own per-device-type audit call on success.
in their qemuDomainRemove*Device() function, yes. But those functions could also do with some refactoring.
This function is unused thus breaks build.
Really? Adding "inline" fixed that for me (on Fedora 29 using gcc). In the past I had taken care of this with ATTRIBUTE_SOMETHING, but I couldn't remember what ATTRIBUTE it was, or the proper place in the function header to put it, so I used inline and no longer had an error. So what's the "proper" way to indicate that a static function isn't currently being called? I'd rather keep its introduction in a separate patch from the one where I start using it.
+{ + switch ((virDomainDeviceType)detach->type) { + case VIR_DOMAIN_DEVICE_CHR: + virDomainAuditChardev(vm, detach->data.chr, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_DISK: + virDomainAuditDisk(vm, detach->data.disk->src, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_NET: + virDomainAuditNet(vm, detach->data.net, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_HOSTDEV: + virDomainAuditHostdev(vm, detach->data.hostdev, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_RNG: + virDomainAuditRNG(vm, detach->data.rng, NULL, "detach", success); + break; + + case VIR_DOMAIN_DEVICE_MEMORY: { + unsigned long long oldmem = virDomainDefGetMemoryTotal(vm->def); + unsigned long long newmem = oldmem - detach->data.memory->size; + + virDomainAuditMemory(vm, oldmem, newmem, "update", success); This is currently not audited, I think it should be added separately.
Actually, I think currently only disk, net, and hostdev are audited on a *failure* to remove. According to a discussion with danpb on irc a couple days ago, it's proper to audit failure to detach for any device that would normally be audited on a successful detach, but I can remove those that are currently unaudited on failure, and add them back in a later patch.

Now that all the qemuDomainDetachPrep*() functions look nearly identical at the end, we can put one copy of that identical code in qemuDomainDetachDeviceLive() at the point after the individual prep functions have been called, and remove the duplicated code from all the prep functions. The code to locate the target "detach" device based on the "match" device remains, as do all device-type-specific validations. Unfortunately there are a few things going on at once in this patch, which makes it a bit more difficult to follow than the others; it was just impossible to do the changes in stages and still have a buildable/testable tree at each step. The other changes of note: * The individual prep functions no longer need their driver or async args, so those are removed, as are the local "ret" variables, since in all cases the functions just directly return -1 or 0. * Some of the prep functions were checking for a valid alias and/or for attempts to detach a multifunction PCI device, but not all. In fact, both checks are valid (or at least harmless) for *all* device types, so they are removed from the prep functions, and done a single time in the common function. (any attempts to *create* an alias when there isn't one has been removed, since that is doomed to failure anyway; the only way the device wouldn't have an alias is if 1) the domain was created by calling virsh qemu-attach to attach an existing qemu process to libvirt, and 2) the qemu command that started said process used "old style" arguments for creating devices that didn't have any device ids. Even if we constructed a device id for one of these devices, qemu wouldn't recognize it in the device_del command anyway, so we may as well fail earlier with "device missing alias" rather than failing later with "couldn't delete device net0".) * Only one type of device has shutdown code that must not be called until after *all* validation of the device is done (including checking for multifunction PCI and valid alias, which is done in the toplevel common code). For this reason, the Net function has been split in two, with the 2nd half (qemuDomainDetachShutdownNet()) called from the common function, right before sending the delete command to qemu. Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 497 ++++++++++++---------------------------- 1 file changed, 149 insertions(+), 348 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 5e5ffe16d3..de7a7a2c95 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5208,7 +5208,7 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, } -static inline void +static void qemuDomainRemoveAuditDevice(virDomainObjPtr vm, virDomainDeviceDefPtr detach, bool success) @@ -5462,15 +5462,12 @@ qemuFindDisk(virDomainDefPtr def, const char *dst) } static int -qemuDomainDetachPrepDisk(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainDetachPrepDisk(virDomainObjPtr vm, virDomainDiskDefPtr match, - virDomainDiskDefPtr *detach, - bool async) + virDomainDiskDefPtr *detach) { virDomainDiskDefPtr disk; int idx; - int ret = -1; if ((idx = qemuFindDisk(vm->def, match->dst)) < 0) { virReportError(VIR_ERR_OPERATION_FAILED, @@ -5522,34 +5519,7 @@ qemuDomainDetachPrepDisk(virQEMUDriverPtr driver, if (qemuDomainDiskBlockJobIsActive(disk)) return -1; - if (disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO && - qemuIsMultiFunctionDevice(vm->def, &disk->info)) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("cannot hot unplug multifunction PCI device: %s"), - disk->dst); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &disk->info); - - if (qemuDomainDeleteDevice(vm, disk->info.alias) < 0) { - if (virDomainObjIsActive(vm)) - virDomainAuditDisk(vm, disk->src, NULL, "detach", false); - goto cleanup; - } - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveDiskDevice(driver, vm, disk); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; + return 0; } @@ -5614,14 +5584,12 @@ static bool qemuDomainControllerIsBusy(virDomainObjPtr vm, } static int -qemuDomainDetachPrepController(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainDetachPrepController(virDomainObjPtr vm, virDomainControllerDefPtr match, - virDomainControllerDefPtr *detach, - bool async) + virDomainControllerDefPtr *detach) { virDomainControllerDefPtr controller; - int idx, ret = -1; + int idx; if (match->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, @@ -5635,7 +5603,7 @@ qemuDomainDetachPrepController(virQEMUDriverPtr driver, _("controller %s:%d not found"), virDomainControllerTypeToString(match->type), match->idx); - goto cleanup; + return -1; } *detach = controller = vm->def->controllers[idx]; @@ -5643,42 +5611,18 @@ qemuDomainDetachPrepController(virQEMUDriverPtr driver, if (qemuDomainControllerIsBusy(vm, controller)) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("device cannot be detached: device is busy")); - goto cleanup; - } - - if (qemuIsMultiFunctionDevice(vm->def, &controller->info)) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("cannot hot unplug multifunction PCI device")); - goto cleanup; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &controller->info); - - if (qemuDomainDeleteDevice(vm, controller->info.alias) < 0) - goto cleanup; - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveControllerDevice(driver, vm, controller); + return -1; } - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; + return 0; } /* search for a hostdev matching dev and detach it */ static int -qemuDomainDetachPrepHostdev(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainDetachPrepHostdev(virDomainObjPtr vm, virDomainHostdevDefPtr match, - virDomainHostdevDefPtr *detach, - bool async) + virDomainHostdevDefPtr *detach) { virDomainHostdevSubsysPtr subsys = &match->source.subsys; virDomainHostdevSubsysUSBPtr usbsrc = &subsys->u.usb; @@ -5687,7 +5631,6 @@ qemuDomainDetachPrepHostdev(virQEMUDriverPtr driver, virDomainHostdevSubsysMediatedDevPtr mdevsrc = &subsys->u.mdev; virDomainHostdevDefPtr hostdev; int idx; - int ret = -1; if (match->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -5750,58 +5693,16 @@ qemuDomainDetachPrepHostdev(virQEMUDriverPtr driver, return -1; } - if (qemuAssignDeviceHostdevAlias(vm->def, &hostdev->info->alias, -1) < 0) - return -1; - - if (qemuIsMultiFunctionDevice(vm->def, hostdev->info)) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("cannot hot unplug multifunction PCI device with guest address: " - "%.4x:%.2x:%.2x.%.1x"), - hostdev->info->addr.pci.domain, hostdev->info->addr.pci.bus, - hostdev->info->addr.pci.slot, hostdev->info->addr.pci.function); - return -1; - } - - if (!hostdev->info->alias) { - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, hostdev->info); - - if (qemuDomainDeleteDevice(vm, hostdev->info->alias) < 0) { - if (virDomainObjIsActive(vm)) - virDomainAuditHostdev(vm, hostdev, "detach", false); - goto cleanup; - } - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveHostDevice(driver, vm, hostdev); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - - return ret; - + return 0; } static int -qemuDomainDetachPrepShmem(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainDetachPrepShmem(virDomainObjPtr vm, virDomainShmemDefPtr match, - virDomainShmemDefPtr *detach, - bool async) + virDomainShmemDefPtr *detach) { - int ret = -1; - ssize_t idx = -1; + ssize_t idx; virDomainShmemDefPtr shmem; if ((idx = virDomainShmemDefFind(vm->def, match)) < 0) { @@ -5828,34 +5729,15 @@ qemuDomainDetachPrepShmem(virQEMUDriverPtr driver, return -1; } - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &shmem->info); - - if (qemuDomainDeleteDevice(vm, shmem->info.alias) < 0) - goto cleanup; - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveShmemDevice(driver, vm, shmem); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; + return 0; } static int -qemuDomainDetachPrepWatchdog(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainDetachPrepWatchdog(virDomainObjPtr vm, virDomainWatchdogDefPtr match, - virDomainWatchdogDefPtr *detach, - bool async) + virDomainWatchdogDefPtr *detach) { - int ret = -1; virDomainWatchdogDefPtr watchdog; @@ -5887,34 +5769,15 @@ qemuDomainDetachPrepWatchdog(virQEMUDriverPtr driver, return -1; } - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &watchdog->info); - - if (qemuDomainDeleteDevice(vm, watchdog->info.alias) < 0) - goto cleanup; - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveWatchdog(driver, vm, watchdog); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; + return 0; } static int -qemuDomainDetachPrepRedirdev(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainDetachPrepRedirdev(virDomainObjPtr vm, virDomainRedirdevDefPtr match, - virDomainRedirdevDefPtr *detach, - bool async) + virDomainRedirdevDefPtr *detach) { - int ret = -1; virDomainRedirdevDefPtr redirdev; ssize_t idx; @@ -5926,59 +5789,41 @@ qemuDomainDetachPrepRedirdev(virQEMUDriverPtr driver, *detach = redirdev = vm->def->redirdevs[idx]; - if (!redirdev->info.alias) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("alias not set for redirdev device")); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &redirdev->info); - - if (qemuDomainDeleteDevice(vm, redirdev->info.alias) < 0) - goto cleanup; - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveRedirdevDevice(driver, vm, redirdev); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; + return 0; } static int -qemuDomainDetachPrepNet(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainDetachPrepNet(virDomainObjPtr vm, virDomainNetDefPtr match, - virDomainNetDefPtr *detach, - bool async) + virDomainNetDefPtr *detach) { - int detachidx, ret = -1; + int detachidx; virDomainNetDefPtr net; if ((detachidx = virDomainNetFindIdx(vm->def, match)) < 0) - goto cleanup; + return -1; *detach = net = vm->def->nets[detachidx]; - if (qemuIsMultiFunctionDevice(vm->def, &net->info)) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("cannot hot unplug multifunction PCI device: %s"), - net->ifname); - goto cleanup; - } + return 0; +} - if (!net->info.alias) { - if (qemuAssignDeviceNetAlias(vm->def, net, -1) < 0) - goto cleanup; - } +static void +qemuDomainDetachShutdownNet(virDomainNetDefPtr net) +{ +/* + * These operations are in a separate function from + * qemuDomainDetachPrepNet() because they can't be done until after + * we've validated that this device really can be removed - in + * particular we need to check for multifunction PCI devices and + * presence of a device alias, which isn't done until *after* the + * return from qemuDomainDetachPrepNet(). Since we've already passed + * the "point of no return", we ignore any errors, and trudge ahead + * with shutting down and detaching the device even if there is an + * error in one of these functions. + */ if (virDomainNetGetActualBandwidth(net) && virNetDevSupportBandwidth(virDomainNetGetActualType(net)) && virNetDevBandwidthClear(net->ifname) < 0) @@ -5990,35 +5835,8 @@ qemuDomainDetachPrepNet(virQEMUDriverPtr driver, * the parent device offline) */ ignore_value(qemuInterfaceStopDevice(net)); - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &net->info); - - if (qemuDomainDeleteDevice(vm, net->info.alias) < 0) { - if (virDomainObjIsActive(vm)) { - /* the audit message has a different format for hostdev network devices */ - if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_HOSTDEV) - virDomainAuditHostdev(vm, virDomainNetGetActualHostdev(net), "detach", false); - else - virDomainAuditNet(vm, net, NULL, "detach", false); - } - goto cleanup; - } - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveNetDevice(driver, vm, net); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; } - static int qemuDomainDetachDeviceChr(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -6078,15 +5896,12 @@ qemuDomainDetachDeviceChr(virQEMUDriverPtr driver, static int -qemuDomainDetachPrepRNG(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainDetachPrepRNG(virDomainObjPtr vm, virDomainRNGDefPtr match, - virDomainRNGDefPtr *detach, - bool async) + virDomainRNGDefPtr *detach) { ssize_t idx; virDomainRNGDefPtr rng; - int ret = -1; if ((idx = virDomainRNGFind(vm->def, match)) < 0) { virReportError(VIR_ERR_DEVICE_MISSING, @@ -6098,42 +5913,17 @@ qemuDomainDetachPrepRNG(virQEMUDriverPtr driver, *detach = rng = vm->def->rngs[idx]; - if (!rng->info.alias) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("alias not set for RNG device")); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &rng->info); - - if (qemuDomainDeleteDevice(vm, rng->info.alias) < 0) - goto cleanup; - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveRNGDevice(driver, vm, rng); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; + return 0; } static int -qemuDomainDetachPrepMemory(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainDetachPrepMemory(virDomainObjPtr vm, virDomainMemoryDefPtr match, - virDomainMemoryDefPtr *detach, - bool async) + virDomainMemoryDefPtr *detach) { virDomainMemoryDefPtr mem; int idx; - int ret = -1; qemuDomainMemoryDeviceAlignSize(vm->def, match); @@ -6147,40 +5937,16 @@ qemuDomainDetachPrepMemory(virQEMUDriverPtr driver, *detach = mem = vm->def->mems[idx]; - if (!mem->info.alias) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("alias for the memory device was not found")); - return -1; - } - - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &mem->info); - - if (qemuDomainDeleteDevice(vm, mem->info.alias) < 0) - goto cleanup; - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveMemoryDevice(driver, vm, mem); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; + return 0; } static int qemuDomainDetachPrepInput(virDomainObjPtr vm, virDomainInputDefPtr match, - virDomainInputDefPtr *detach, - bool async) + virDomainInputDefPtr *detach) { virDomainInputDefPtr input; - int ret = -1; int idx; if ((idx = virDomainInputDefFind(vm->def, match)) < 0) { @@ -6205,35 +5971,16 @@ qemuDomainDetachPrepInput(virDomainObjPtr vm, break; } - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &input->info); - - if (qemuDomainDeleteDevice(vm, input->info.alias) < 0) - goto cleanup; - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveInputDevice(vm, input); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; + return 0; } static int qemuDomainDetachPrepVsock(virDomainObjPtr vm, virDomainVsockDefPtr match, - virDomainVsockDefPtr *detach, - bool async) + virDomainVsockDefPtr *detach) { virDomainVsockDefPtr vsock; - int ret = -1; - *detach = vsock = vm->def->vsock; if (!vsock || @@ -6243,23 +5990,7 @@ qemuDomainDetachPrepVsock(virDomainObjPtr vm, return -1; } - if (!async) - qemuDomainMarkDeviceForRemoval(vm, &vsock->info); - - if (qemuDomainDeleteDevice(vm, vsock->info.alias) < 0) - goto cleanup; - - if (async) { - ret = 0; - } else { - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) - ret = qemuDomainRemoveVsockDevice(vm, vsock); - } - - cleanup: - if (!async) - qemuDomainResetDeviceRemoval(vm); - return ret; + return 0; } @@ -6294,6 +6025,7 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, bool async) { virDomainDeviceDef detach = { .type = match->type }; + virDomainDeviceInfoPtr info = NULL; int ret = -1; switch ((virDomainDeviceType)match->type) { @@ -6309,75 +6041,76 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, return qemuDomainDetachDeviceChr(driver, vm, match->data.chr, async); /* - * All the other device types follow a very similar pattern - - * First we call type-specific functions to 1) locate the device - * we want to detach (based on the prototype device in match) - * and 2) do any device-type-specific validation to assure it - * is okay to detach the device. + * The rest of the device types (that support hot unplug) + * follow a very similar pattern - First we call type-specific + * functions to 1) locate the device we want to detach (based + * on the prototype device in match) and 2) do any + * device-type-specific validation to assure it is okay to + * detach the device. */ case VIR_DOMAIN_DEVICE_DISK: - if (qemuDomainDetachPrepDisk(driver, vm, match->data.disk, - &detach.data.disk, async) < 0) { + if (qemuDomainDetachPrepDisk(vm, match->data.disk, + &detach.data.disk) < 0) { return -1; } break; case VIR_DOMAIN_DEVICE_CONTROLLER: - if (qemuDomainDetachPrepController(driver, vm, match->data.controller, - &detach.data.controller, async) < 0) { + if (qemuDomainDetachPrepController(vm, match->data.controller, + &detach.data.controller) < 0) { return -1; } break; case VIR_DOMAIN_DEVICE_NET: - if (qemuDomainDetachPrepNet(driver, vm, match->data.net, - &detach.data.net, async) < 0) { + if (qemuDomainDetachPrepNet(vm, match->data.net, + &detach.data.net) < 0) { return -1; } break; case VIR_DOMAIN_DEVICE_HOSTDEV: - if (qemuDomainDetachPrepHostdev(driver, vm, match->data.hostdev, - &detach.data.hostdev, async) < 0) { + if (qemuDomainDetachPrepHostdev(vm, match->data.hostdev, + &detach.data.hostdev) < 0) { return -1; } break; case VIR_DOMAIN_DEVICE_RNG: - if (qemuDomainDetachPrepRNG(driver, vm, match->data.rng, - &detach.data.rng, async) < 0) { + if (qemuDomainDetachPrepRNG(vm, match->data.rng, + &detach.data.rng) < 0) { return -1; } break; case VIR_DOMAIN_DEVICE_MEMORY: - if (qemuDomainDetachPrepMemory(driver, vm, match->data.memory, - &detach.data.memory, async) < 0) { + if (qemuDomainDetachPrepMemory(vm, match->data.memory, + &detach.data.memory) < 0) { return -1; } break; case VIR_DOMAIN_DEVICE_SHMEM: - if (qemuDomainDetachPrepShmem(driver, vm, match->data.shmem, - &detach.data.shmem, async) < 0) { + if (qemuDomainDetachPrepShmem(vm, match->data.shmem, + &detach.data.shmem) < 0) { return -1; } break; case VIR_DOMAIN_DEVICE_WATCHDOG: - if (qemuDomainDetachPrepWatchdog(driver, vm, match->data.watchdog, - &detach.data.watchdog, async) < 0) { + if (qemuDomainDetachPrepWatchdog(vm, match->data.watchdog, + &detach.data.watchdog) < 0) { return -1; } break; case VIR_DOMAIN_DEVICE_INPUT: if (qemuDomainDetachPrepInput(vm, match->data.input, - &detach.data.input, async) < 0) { + &detach.data.input) < 0) { return -1; } break; case VIR_DOMAIN_DEVICE_REDIRDEV: - if (qemuDomainDetachPrepRedirdev(driver, vm, match->data.redirdev, - &detach.data.redirdev, async) < 0) { + if (qemuDomainDetachPrepRedirdev(vm, match->data.redirdev, + &detach.data.redirdev) < 0) { return -1; } break; case VIR_DOMAIN_DEVICE_VSOCK: if (qemuDomainDetachPrepVsock(vm, match->data.vsock, - &detach.data.vsock, async) < 0) { + &detach.data.vsock) < 0) { return -1; } break; @@ -6401,7 +6134,75 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, return -1; } - ret = 0; + /* "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; + } + + if (qemuIsMultiFunctionDevice(vm->def, info)) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("cannot hot unplug %s device with multifunction PCI guest address: " + "%.4x:%.2x:%.2x.%.1x"), + virDomainDeviceTypeToString(detach.type), + info->addr.pci.domain, info->addr.pci.bus, + info->addr.pci.slot, info->addr.pci.function); + return -1; + } + + + /* + * Do any device-specific shutdown that should be + * done after all validation checks, but before issuing the qemu + * command to delete the device. For now, the only type of device + * that has such shutdown needs is the net device. + */ + if (detach.type == VIR_DOMAIN_DEVICE_NET) + qemuDomainDetachShutdownNet(detach.data.net); + + + /* + * Issue the qemu monitor command to delete the device (based on + * its alias), and optionally wait a short time in case the + * DEVICE_DELETED event arrives from qemu right away. + */ + if (!async) + qemuDomainMarkDeviceForRemoval(vm, info); + + if (qemuDomainDeleteDevice(vm, info->alias) < 0) { + if (virDomainObjIsActive(vm)) + qemuDomainRemoveAuditDevice(vm, &detach, false); + goto cleanup; + } + + if (async) { + ret = 0; + } else { + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) + ret = qemuDomainRemoveDevice(driver, vm, &detach); + } + + cleanup: + if (!async) + qemuDomainResetDeviceRemoval(vm); return ret; } -- 2.20.1

On Thu, Mar 21, 2019 at 18:29:00 -0400, Laine Stump wrote:
Now that all the qemuDomainDetachPrep*() functions look nearly identical at the end, we can put one copy of that identical code in qemuDomainDetachDeviceLive() at the point after the individual prep functions have been called, and remove the duplicated code from all the prep functions. The code to locate the target "detach" device based on the "match" device remains, as do all device-type-specific validations.
Unfortunately there are a few things going on at once in this patch, which makes it a bit more difficult to follow than the others; it was just impossible to do the changes in stages and still have a buildable/testable tree at each step.
The other changes of note:
* The individual prep functions no longer need their driver or async args, so those are removed, as are the local "ret" variables, since in all cases the functions just directly return -1 or 0.
* Some of the prep functions were checking for a valid alias and/or for attempts to detach a multifunction PCI device, but not all. In fact, both checks are valid (or at least harmless) for *all* device types, so they are removed from the prep functions, and done a single time in the common function.
(any attempts to *create* an alias when there isn't one has been removed, since that is doomed to failure anyway; the only way the device wouldn't have an alias is if 1) the domain was created by calling virsh qemu-attach to attach an existing qemu process to libvirt, and 2) the qemu command that started said process used "old style" arguments for creating devices that didn't have any device ids. Even if we constructed a device id for one of these devices, qemu wouldn't recognize it in the device_del command anyway, so we may as well fail earlier with "device missing alias" rather than failing later with "couldn't delete device net0".)
* Only one type of device has shutdown code that must not be called until after *all* validation of the device is done (including
Why don't we put that to the RemoveDevice handler for network? We teardown all related stuff usually after unplug of the frontend.
checking for multifunction PCI and valid alias, which is done in the toplevel common code). For this reason, the Net function has been split in two, with the 2nd half (qemuDomainDetachShutdownNet()) called from the common function, right before sending the delete command to qemu.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 497 ++++++++++++---------------------------- 1 file changed, 149 insertions(+), 348 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 5e5ffe16d3..de7a7a2c95 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5208,7 +5208,7 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, }
-static inline void +static void
Use ATTRIBUTE_UNUSED rather than inline.
qemuDomainRemoveAuditDevice(virDomainObjPtr vm, virDomainDeviceDefPtr detach, bool success)

On 3/22/19 8:32 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:29:00 -0400, Laine Stump wrote:
Now that all the qemuDomainDetachPrep*() functions look nearly identical at the end, we can put one copy of that identical code in qemuDomainDetachDeviceLive() at the point after the individual prep functions have been called, and remove the duplicated code from all the prep functions. The code to locate the target "detach" device based on the "match" device remains, as do all device-type-specific validations.
Unfortunately there are a few things going on at once in this patch, which makes it a bit more difficult to follow than the others; it was just impossible to do the changes in stages and still have a buildable/testable tree at each step.
The other changes of note:
* The individual prep functions no longer need their driver or async args, so those are removed, as are the local "ret" variables, since in all cases the functions just directly return -1 or 0.
* Some of the prep functions were checking for a valid alias and/or for attempts to detach a multifunction PCI device, but not all. In fact, both checks are valid (or at least harmless) for *all* device types, so they are removed from the prep functions, and done a single time in the common function.
(any attempts to *create* an alias when there isn't one has been removed, since that is doomed to failure anyway; the only way the device wouldn't have an alias is if 1) the domain was created by calling virsh qemu-attach to attach an existing qemu process to libvirt, and 2) the qemu command that started said process used "old style" arguments for creating devices that didn't have any device ids. Even if we constructed a device id for one of these devices, qemu wouldn't recognize it in the device_del command anyway, so we may as well fail earlier with "device missing alias" rather than failing later with "couldn't delete device net0".)
* Only one type of device has shutdown code that must not be called until after *all* validation of the device is done (including Why don't we put that to the RemoveDevice handler for network? We teardown all related stuff usually after unplug of the frontend.
That is a good question! I'm not sure why network devices have this part of their shutdown handled early. I don't think we should make that change here though. I can insert an earlier patch to do it, hoping that it won't break anything, or I can make a later patch that moves it after this change, which seems kind of superfluous, but would make it simpler to revert in case it causes a regression. What's your opinion?
checking for multifunction PCI and valid alias, which is done in the toplevel common code). For this reason, the Net function has been split in two, with the 2nd half (qemuDomainDetachShutdownNet()) called from the common function, right before sending the delete command to qemu.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 497 ++++++++++++---------------------------- 1 file changed, 149 insertions(+), 348 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 5e5ffe16d3..de7a7a2c95 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5208,7 +5208,7 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, }
-static inline void +static void Use ATTRIBUTE_UNUSED rather than inline.
I couldn't remember where was the proper place to put ATTRIBUTE_UNUSED in the function definition, and no matter where I put it it didn't work, so I gave up and used inline. Obviously that doesn't work for all compilers though, so I'll figure out the right place.
qemuDomainRemoveAuditDevice(virDomainObjPtr vm, virDomainDeviceDefPtr detach, bool success)

On Fri, Mar 22, 2019 at 10:08:17 -0400, Laine Stump wrote:
On 3/22/19 8:32 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:29:00 -0400, Laine Stump wrote:
Now that all the qemuDomainDetachPrep*() functions look nearly identical at the end, we can put one copy of that identical code in qemuDomainDetachDeviceLive() at the point after the individual prep functions have been called, and remove the duplicated code from all the prep functions. The code to locate the target "detach" device based on the "match" device remains, as do all device-type-specific validations.
Unfortunately there are a few things going on at once in this patch, which makes it a bit more difficult to follow than the others; it was just impossible to do the changes in stages and still have a buildable/testable tree at each step.
The other changes of note:
* The individual prep functions no longer need their driver or async args, so those are removed, as are the local "ret" variables, since in all cases the functions just directly return -1 or 0.
* Some of the prep functions were checking for a valid alias and/or for attempts to detach a multifunction PCI device, but not all. In fact, both checks are valid (or at least harmless) for *all* device types, so they are removed from the prep functions, and done a single time in the common function.
(any attempts to *create* an alias when there isn't one has been removed, since that is doomed to failure anyway; the only way the device wouldn't have an alias is if 1) the domain was created by calling virsh qemu-attach to attach an existing qemu process to libvirt, and 2) the qemu command that started said process used "old style" arguments for creating devices that didn't have any device ids. Even if we constructed a device id for one of these devices, qemu wouldn't recognize it in the device_del command anyway, so we may as well fail earlier with "device missing alias" rather than failing later with "couldn't delete device net0".)
* Only one type of device has shutdown code that must not be called until after *all* validation of the device is done (including Why don't we put that to the RemoveDevice handler for network? We teardown all related stuff usually after unplug of the frontend.
That is a good question! I'm not sure why network devices have this part of their shutdown handled early. I don't think we should make that change here though. I can insert an earlier patch to do it, hoping that it won't break anything, or I can make a later patch that moves it after this change, which seems kind of superfluous, but would make it simpler to revert in case it causes a regression. What's your opinion?
I honestly have no idea what that cleanup is done for network devices, it just feels wrong to teardown stuff prior to the OS even giving up the device. Changing it prior will result in cleaner code, fixing it after will be easily revertable. I'd probably go for the latter.
checking for multifunction PCI and valid alias, which is done in the toplevel common code). For this reason, the Net function has been split in two, with the 2nd half (qemuDomainDetachShutdownNet()) called from the common function, right before sending the delete command to qemu.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 497 ++++++++++++---------------------------- 1 file changed, 149 insertions(+), 348 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 5e5ffe16d3..de7a7a2c95 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5208,7 +5208,7 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, } -static inline void +static void Use ATTRIBUTE_UNUSED rather than inline.
I couldn't remember where was the proper place to put ATTRIBUTE_UNUSED in the function definition, and no matter where I put it it didn't work, so I gave up and used inline. Obviously that doesn't work for all compilers though, so I'll figure out the right place.
static void ATTRIBUTE_UNUSED virAsdf(int qwer) { }

On 3/22/19 10:14 AM, Peter Krempa wrote:
On Fri, Mar 22, 2019 at 10:08:17 -0400, Laine Stump wrote:
On 3/22/19 8:32 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:29:00 -0400, Laine Stump wrote:
Now that all the qemuDomainDetachPrep*() functions look nearly identical at the end, we can put one copy of that identical code in qemuDomainDetachDeviceLive() at the point after the individual prep functions have been called, and remove the duplicated code from all the prep functions. The code to locate the target "detach" device based on the "match" device remains, as do all device-type-specific validations.
Unfortunately there are a few things going on at once in this patch, which makes it a bit more difficult to follow than the others; it was just impossible to do the changes in stages and still have a buildable/testable tree at each step.
The other changes of note:
* The individual prep functions no longer need their driver or async args, so those are removed, as are the local "ret" variables, since in all cases the functions just directly return -1 or 0.
* Some of the prep functions were checking for a valid alias and/or for attempts to detach a multifunction PCI device, but not all. In fact, both checks are valid (or at least harmless) for *all* device types, so they are removed from the prep functions, and done a single time in the common function.
(any attempts to *create* an alias when there isn't one has been removed, since that is doomed to failure anyway; the only way the device wouldn't have an alias is if 1) the domain was created by calling virsh qemu-attach to attach an existing qemu process to libvirt, and 2) the qemu command that started said process used "old style" arguments for creating devices that didn't have any device ids. Even if we constructed a device id for one of these devices, qemu wouldn't recognize it in the device_del command anyway, so we may as well fail earlier with "device missing alias" rather than failing later with "couldn't delete device net0".)
* Only one type of device has shutdown code that must not be called until after *all* validation of the device is done (including Why don't we put that to the RemoveDevice handler for network? We teardown all related stuff usually after unplug of the frontend.
That is a good question! I'm not sure why network devices have this part of their shutdown handled early. I don't think we should make that change here though. I can insert an earlier patch to do it, hoping that it won't break anything, or I can make a later patch that moves it after this change, which seems kind of superfluous, but would make it simpler to revert in case it causes a regression. What's your opinion? I honestly have no idea what that cleanup is done for network devices, it just feels wrong to teardown stuff prior to the OS even giving up the device.
iirc, bandwidth rules are removed, and the interface is taken offline. Most like it's just that way because the original code had those happening early by coincidence, and then later when it was split into the separate detach and remove pieces, it got left in detach.
Changing it prior will result in cleaner code, fixing it after will be easily revertable. I'd probably go for the latter.
Yeah, especially since I already made the patch that splits it out, etc. :-)
checking for multifunction PCI and valid alias, which is done in the toplevel common code). For this reason, the Net function has been split in two, with the 2nd half (qemuDomainDetachShutdownNet()) called from the common function, right before sending the delete command to qemu.
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 497 ++++++++++++---------------------------- 1 file changed, 149 insertions(+), 348 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 5e5ffe16d3..de7a7a2c95 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5208,7 +5208,7 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, } -static inline void +static void
Use ATTRIBUTE_UNUSED rather than inline.
I couldn't remember where was the proper place to put ATTRIBUTE_UNUSED in the function definition, and no matter where I put it it didn't work, so I gave up and used inline. Obviously that doesn't work for all compilers though, so I'll figure out the right place.
static void ATTRIBUTE_UNUSED virAsdf(int qwer) { }
Hmmph. I could *swear* that was the first one I tried, and I got an error. Maybe I wasn't paying close attention, and it was an error about some other detail that I coincidentally fixed at the same time I switched to using inline.

The VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED event is sent after qemu has responded to a device_del command with a DEVICE_DELETED event. Before queuing the event, *some* of the final teardown of the device's trappings in libvirt is done, but not *all* of it. As a result, an application may receive and process the DEVICE_REMOVED event before libvirt has really finished with it. Usually this doesn't cause a problem, but it can - in the case of the bug report referenced below, vdsm is assigning a PCI device to a guest with managed='no', using livirt's virNodeDeviceDetachFlags() and virNodeDeviceReAttach() APIs. Immediately after receiving a DEVICE_REMOVED event from libvirt signalling that the device had been successfully unplugged, vdsm would cal virNodeDeviceReAttach() to unbind the device from vfio-pci and rebind it to the host driverm but because the event was received before libvirt had completely finished processing the removal, that device was still on the "activeDevs" list, and so virNodeDeviceReAttach() failed. Experimentation with additional debug logs proved that libvirt would always end up dispatching the DEVICE_REMOVED event before it had removed the device from activeDevs (with a *much* greater difference with managed='yes', since in that case the re-binding of the device occurred after queuing the device). Although the case of hostdev devices is the most extreme (since there is so much involved in tearing down the device), *all* device types suffer from the same problem - the DEVICE_REMOVED event is queued very early in the qemuDomainRemove*Device() function for all of them, resulting in a possibility of any application receiving the event before libvirt has really finished with the device. The solution is to save the device's alias (which is the only piece of info from the device object that is needed for the event) at the beginning of processing the device removal, and then queue the event as a final act before returning. Since all of the qemuDomainRemove*Device() functions (except qemuDomainRemoveChrDevice()) are now called exclusively from qemuDomainRemoveDevice() (which selects which of the subordinates to call in a switch statement based on the type of device), the shortest route to a solution is to doing the saving of alias, and later queueing of the event, in the higher level qemuDomainRemoveDevice(), and just completely remove the event-related code from all the subordinate functions. The single exception to this, as mentioned before, is qemuDomainRemoveChrDevice(), which is still called from somewhere other than qemuDomainRemoveDevice() (and has a separate arg used to trigger different behavior when the chr device has targetType == GUESTFWD), so it must keep its original behavior intact, and must be treated differently by qemuDomainRemoveDevice() (similar to the way that qemuDomainDetachDeviceLive() treats chr and lease devices differently from all the others). Resolves: https://bugzilla.redhat.com/1658198 Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 154 ++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 76 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index de7a7a2c95..43cc3a314d 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4501,7 +4501,6 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, { qemuHotplugDiskSourceDataPtr diskbackend = NULL; virDomainDeviceDef dev; - virObjectEventPtr event; size_t i; qemuDomainObjPrivatePtr priv = vm->privateData; int ret = -1; @@ -4529,9 +4528,6 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, virDomainAuditDisk(vm, disk->src, NULL, "detach", true); - event = virDomainEventDeviceRemovedNewFromObj(vm, disk->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - qemuDomainReleaseDeviceAddress(vm, &disk->info, virDomainDiskGetSource(disk)); /* tear down disk security access */ @@ -4555,19 +4551,14 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, static int -qemuDomainRemoveControllerDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainRemoveControllerDevice(virDomainObjPtr vm, virDomainControllerDefPtr controller) { - virObjectEventPtr event; size_t i; VIR_DEBUG("Removing controller %s from domain %p %s", controller->info.alias, vm, vm->def->name); - event = virDomainEventDeviceRemovedNewFromObj(vm, controller->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - for (i = 0; i < vm->def->ncontrollers; i++) { if (vm->def->controllers[i] == controller) { virDomainControllerRemove(vm->def, i); @@ -4589,7 +4580,6 @@ qemuDomainRemoveMemoryDevice(virQEMUDriverPtr driver, qemuDomainObjPrivatePtr priv = vm->privateData; unsigned long long oldmem = virDomainDefGetMemoryTotal(vm->def); unsigned long long newmem = oldmem - mem->size; - virObjectEventPtr event; char *backendAlias = NULL; int rc; int idx; @@ -4611,9 +4601,6 @@ qemuDomainRemoveMemoryDevice(virQEMUDriverPtr driver, if (rc < 0) return -1; - event = virDomainEventDeviceRemovedNewFromObj(vm, mem->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainMemoryFindByDef(vm->def, mem)) >= 0) virDomainMemoryRemove(vm->def, idx); @@ -4693,7 +4680,6 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver, { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virDomainNetDefPtr net = NULL; - virObjectEventPtr event; size_t i; int ret = -1; qemuDomainObjPrivatePtr priv = vm->privateData; @@ -4737,9 +4723,6 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver, goto cleanup; } - event = virDomainEventDeviceRemovedNewFromObj(vm, hostdev->info->alias); - virObjectEventStateQueue(driver->domainEventState, event); - if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET) { net = hostdev->parent.data.net; @@ -4818,7 +4801,6 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver, { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); qemuDomainObjPrivatePtr priv = vm->privateData; - virObjectEventPtr event; char *hostnet_name = NULL; char *charDevAlias = NULL; size_t i; @@ -4863,9 +4845,6 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver, virDomainAuditNet(vm, net, NULL, "detach", true); - event = virDomainEventDeviceRemovedNewFromObj(vm, net->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - for (i = 0; i < vm->def->nnets; i++) { if (vm->def->nets[i] == net) { virDomainNetRemove(vm->def, i); @@ -4948,11 +4927,20 @@ qemuDomainRemoveChrDevice(virQEMUDriverPtr driver, if (qemuDomainNamespaceTeardownChardev(vm, chr) < 0) VIR_WARN("Unable to remove chr device from /dev"); + qemuDomainReleaseDeviceAddress(vm, &chr->info, NULL); + qemuDomainChrRemove(vm->def, chr); + + /* NB: all other qemuDomainRemove*Device() functions omit the + * sending of the DEVICE_REMOVED event, because they are *only* + * called from qemuDomainRemoveDevice(), and that function sends + * the DEVICE_REMOVED event for them, this function is different - + * it is called from other places than just + * qemuDomainRemoveDevice(), so it must send the DEVICE_REMOVED + * event itself. + */ event = virDomainEventDeviceRemovedNewFromObj(vm, chr->info.alias); virObjectEventStateQueue(driver->domainEventState, event); - qemuDomainReleaseDeviceAddress(vm, &chr->info, NULL); - qemuDomainChrRemove(vm->def, chr); virDomainChrDefFree(chr); ret = 0; @@ -4967,7 +4955,6 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRNGDefPtr rng) { - virObjectEventPtr event; char *charAlias = NULL; char *objAlias = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; @@ -5016,9 +5003,6 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver, if (qemuDomainNamespaceTeardownRNG(vm, rng) < 0) VIR_WARN("Unable to remove RNG device from /dev"); - event = virDomainEventDeviceRemovedNewFromObj(vm, rng->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainRNGFind(vm->def, rng)) >= 0) virDomainRNGRemove(vm->def, idx); qemuDomainReleaseDeviceAddress(vm, &rng->info, NULL); @@ -5043,7 +5027,6 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver, char *charAlias = NULL; char *memAlias = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; - virObjectEventPtr event = NULL; VIR_DEBUG("Removing shmem device %s from domain %p %s", shmem->info.alias, vm, vm->def->name); @@ -5071,9 +5054,6 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver, if (rc < 0) goto cleanup; - event = virDomainEventDeviceRemovedNewFromObj(vm, shmem->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainShmemDefFind(vm->def, shmem)) >= 0) virDomainShmemDefRemove(vm->def, idx); qemuDomainReleaseDeviceAddress(vm, &shmem->info, NULL); @@ -5089,17 +5069,12 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver, static int -qemuDomainRemoveWatchdog(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainRemoveWatchdog(virDomainObjPtr vm, virDomainWatchdogDefPtr watchdog) { - virObjectEventPtr event = NULL; - VIR_DEBUG("Removing watchdog %s from domain %p %s", watchdog->info.alias, vm, vm->def->name); - event = virDomainEventDeviceRemovedNewFromObj(vm, watchdog->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); qemuDomainReleaseDeviceAddress(vm, &watchdog->info, NULL); virDomainWatchdogDefFree(vm->def->watchdog); vm->def->watchdog = NULL; @@ -5111,16 +5086,11 @@ static int qemuDomainRemoveInputDevice(virDomainObjPtr vm, virDomainInputDefPtr dev) { - qemuDomainObjPrivatePtr priv = vm->privateData; - virQEMUDriverPtr driver = priv->driver; - virObjectEventPtr event = NULL; size_t i; VIR_DEBUG("Removing input device %s from domain %p %s", dev->info.alias, vm, vm->def->name); - event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); for (i = 0; i < vm->def->ninputs; i++) { if (vm->def->inputs[i] == dev) break; @@ -5145,15 +5115,9 @@ static int qemuDomainRemoveVsockDevice(virDomainObjPtr vm, virDomainVsockDefPtr dev) { - qemuDomainObjPrivatePtr priv = vm->privateData; - virQEMUDriverPtr driver = priv->driver; - virObjectEventPtr event = NULL; - VIR_DEBUG("Removing vsock device %s from domain %p %s", dev->info.alias, vm, vm->def->name); - event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); qemuDomainReleaseDeviceAddress(vm, &dev->info, NULL); virDomainVsockDefFree(vm->def->vsock); vm->def->vsock = NULL; @@ -5167,7 +5131,6 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, virDomainRedirdevDefPtr dev) { qemuDomainObjPrivatePtr priv = vm->privateData; - virObjectEventPtr event; char *charAlias = NULL; ssize_t idx; int ret = -1; @@ -5192,9 +5155,6 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, virDomainAuditRedirdev(vm, dev, "detach", true); - event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainRedirdevDefFind(vm->def, dev)) >= 0) virDomainRedirdevDefRemove(vm->def, idx); qemuDomainReleaseDeviceAddress(vm, &dev->info, NULL); @@ -5285,50 +5245,88 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { - int ret = -1; + virDomainDeviceInfoPtr info; + virObjectEventPtr event; + VIR_AUTOFREE(char *)alias = NULL; + + if (!(info = virDomainDeviceGetInfo(dev))) { + /* + * 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(dev->type)); + return -1; + } + + /* + * save the alias to use when sending a DEVICE_REMOVED event after + * all other teardown is complete + */ + if (VIR_STRDUP(alias, info->alias) < 0) + return -1; + switch ((virDomainDeviceType)dev->type) { + case VIR_DOMAIN_DEVICE_CHR: + /* unlike other qemuDomainRemove*Device() functions, this one + * can't take advantage of any common code at the end of + * qemuDomainRemoveDevice(). This is because it is called + * directly from other places (with an extra arg that would be + * clumsy to add into qemuDomainRemoveDevice()) + */ + return qemuDomainRemoveChrDevice(driver, vm, dev->data.chr, true); + + /* + * all of the following qemuDomainRemove*Device() functions + * are (and must be) only called from this function, so any + * code that is common to them all can be pulled out and put + * at the end of this function. + */ case VIR_DOMAIN_DEVICE_DISK: - ret = qemuDomainRemoveDiskDevice(driver, vm, dev->data.disk); + if (qemuDomainRemoveDiskDevice(driver, vm, dev->data.disk) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_CONTROLLER: - ret = qemuDomainRemoveControllerDevice(driver, vm, dev->data.controller); + if (qemuDomainRemoveControllerDevice(vm, dev->data.controller) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_NET: - ret = qemuDomainRemoveNetDevice(driver, vm, dev->data.net); + if (qemuDomainRemoveNetDevice(driver, vm, dev->data.net) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = qemuDomainRemoveHostDevice(driver, vm, dev->data.hostdev); - break; - - case VIR_DOMAIN_DEVICE_CHR: - ret = qemuDomainRemoveChrDevice(driver, vm, dev->data.chr, true); + if (qemuDomainRemoveHostDevice(driver, vm, dev->data.hostdev) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_RNG: - ret = qemuDomainRemoveRNGDevice(driver, vm, dev->data.rng); + if (qemuDomainRemoveRNGDevice(driver, vm, dev->data.rng) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_MEMORY: - ret = qemuDomainRemoveMemoryDevice(driver, vm, dev->data.memory); + if (qemuDomainRemoveMemoryDevice(driver, vm, dev->data.memory) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_SHMEM: - ret = qemuDomainRemoveShmemDevice(driver, vm, dev->data.shmem); + if (qemuDomainRemoveShmemDevice(driver, vm, dev->data.shmem) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_INPUT: - ret = qemuDomainRemoveInputDevice(vm, dev->data.input); + if (qemuDomainRemoveInputDevice(vm, dev->data.input) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_REDIRDEV: - ret = qemuDomainRemoveRedirdevDevice(driver, vm, dev->data.redirdev); + if (qemuDomainRemoveRedirdevDevice(driver, vm, dev->data.redirdev) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_WATCHDOG: - ret = qemuDomainRemoveWatchdog(driver, vm, dev->data.watchdog); + if (qemuDomainRemoveWatchdog(vm, dev->data.watchdog) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_VSOCK: - ret = qemuDomainRemoveVsockDevice(vm, dev->data.vsock); + if (qemuDomainRemoveVsockDevice(vm, dev->data.vsock) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_NONE: @@ -5350,7 +5348,11 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, virDomainDeviceTypeToString(dev->type)); break; } - return ret; + + event = virDomainEventDeviceRemovedNewFromObj(vm, alias); + virObjectEventStateQueue(driver->domainEventState, event); + + return 0; } -- 2.20.1

On Thu, Mar 21, 2019 at 18:29:01 -0400, Laine Stump wrote:
The VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED event is sent after qemu has responded to a device_del command with a DEVICE_DELETED event. Before queuing the event, *some* of the final teardown of the device's trappings in libvirt is done, but not *all* of it. As a result, an application may receive and process the DEVICE_REMOVED event before libvirt has really finished with it.
Usually this doesn't cause a problem, but it can - in the case of the bug report referenced below, vdsm is assigning a PCI device to a guest with managed='no', using livirt's virNodeDeviceDetachFlags() and virNodeDeviceReAttach() APIs. Immediately after receiving a DEVICE_REMOVED event from libvirt signalling that the device had been successfully unplugged, vdsm would cal virNodeDeviceReAttach() to unbind the device from vfio-pci and rebind it to the host driverm but because the event was received before libvirt had completely finished processing the removal, that device was still on the "activeDevs" list, and so virNodeDeviceReAttach() failed.
So between the lines this reads as if qemuDomainRemoveHostDevice is broken. I agree though that fixing everything systematically is better.
Experimentation with additional debug logs proved that libvirt would always end up dispatching the DEVICE_REMOVED event before it had removed the device from activeDevs (with a *much* greater difference with managed='yes', since in that case the re-binding of the device occurred after queuing the device).
Although the case of hostdev devices is the most extreme (since there is so much involved in tearing down the device), *all* device types suffer from the same problem - the DEVICE_REMOVED event is queued very early in the qemuDomainRemove*Device() function for all of them, resulting in a possibility of any application receiving the event before libvirt has really finished with the device.
The solution is to save the device's alias (which is the only piece of info from the device object that is needed for the event) at the beginning of processing the device removal, and then queue the event as a final act before returning. Since all of the qemuDomainRemove*Device() functions (except qemuDomainRemoveChrDevice()) are now called exclusively from qemuDomainRemoveDevice() (which selects which of the subordinates to call in a switch statement based on the type of device), the shortest route to a solution is to doing the saving of alias, and later queueing of the event, in the higher level qemuDomainRemoveDevice(), and just completely remove the event-related code from all the subordinate functions.
The single exception to this, as mentioned before, is qemuDomainRemoveChrDevice(), which is still called from somewhere other than qemuDomainRemoveDevice() (and has a separate arg used to trigger different behavior when the chr device has targetType == GUESTFWD), so it must keep its original behavior intact, and must be treated differently by qemuDomainRemoveDevice() (similar to the way that qemuDomainDetachDeviceLive() treats chr and lease devices differently from all the others).
Resolves: https://bugzilla.redhat.com/1658198
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 154 ++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 76 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index de7a7a2c95..43cc3a314d 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4501,7 +4501,6 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, { qemuHotplugDiskSourceDataPtr diskbackend = NULL; virDomainDeviceDef dev; - virObjectEventPtr event; size_t i; qemuDomainObjPrivatePtr priv = vm->privateData; int ret = -1; @@ -4529,9 +4528,6 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver,
virDomainAuditDisk(vm, disk->src, NULL, "detach", true);
- event = virDomainEventDeviceRemovedNewFromObj(vm, disk->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - qemuDomainReleaseDeviceAddress(vm, &disk->info, virDomainDiskGetSource(disk));
/* tear down disk security access */ @@ -4555,19 +4551,14 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver,
static int -qemuDomainRemoveControllerDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainRemoveControllerDevice(virDomainObjPtr vm, virDomainControllerDefPtr controller) { - virObjectEventPtr event; size_t i;
VIR_DEBUG("Removing controller %s from domain %p %s", controller->info.alias, vm, vm->def->name);
- event = virDomainEventDeviceRemovedNewFromObj(vm, controller->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - for (i = 0; i < vm->def->ncontrollers; i++) { if (vm->def->controllers[i] == controller) { virDomainControllerRemove(vm->def, i); @@ -4589,7 +4580,6 @@ qemuDomainRemoveMemoryDevice(virQEMUDriverPtr driver, qemuDomainObjPrivatePtr priv = vm->privateData; unsigned long long oldmem = virDomainDefGetMemoryTotal(vm->def); unsigned long long newmem = oldmem - mem->size; - virObjectEventPtr event; char *backendAlias = NULL; int rc; int idx; @@ -4611,9 +4601,6 @@ qemuDomainRemoveMemoryDevice(virQEMUDriverPtr driver, if (rc < 0) return -1;
- event = virDomainEventDeviceRemovedNewFromObj(vm, mem->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainMemoryFindByDef(vm->def, mem)) >= 0) virDomainMemoryRemove(vm->def, idx);
@@ -4693,7 +4680,6 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver, { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virDomainNetDefPtr net = NULL; - virObjectEventPtr event; size_t i; int ret = -1; qemuDomainObjPrivatePtr priv = vm->privateData; @@ -4737,9 +4723,6 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver, goto cleanup; }
- event = virDomainEventDeviceRemovedNewFromObj(vm, hostdev->info->alias); - virObjectEventStateQueue(driver->domainEventState, event); - if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET) { net = hostdev->parent.data.net;
@@ -4818,7 +4801,6 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver, { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); qemuDomainObjPrivatePtr priv = vm->privateData; - virObjectEventPtr event; char *hostnet_name = NULL; char *charDevAlias = NULL; size_t i; @@ -4863,9 +4845,6 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver,
virDomainAuditNet(vm, net, NULL, "detach", true);
- event = virDomainEventDeviceRemovedNewFromObj(vm, net->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - for (i = 0; i < vm->def->nnets; i++) { if (vm->def->nets[i] == net) { virDomainNetRemove(vm->def, i); @@ -4948,11 +4927,20 @@ qemuDomainRemoveChrDevice(virQEMUDriverPtr driver, if (qemuDomainNamespaceTeardownChardev(vm, chr) < 0) VIR_WARN("Unable to remove chr device from /dev");
+ qemuDomainReleaseDeviceAddress(vm, &chr->info, NULL); + qemuDomainChrRemove(vm->def, chr); + + /* NB: all other qemuDomainRemove*Device() functions omit the + * sending of the DEVICE_REMOVED event, because they are *only* + * called from qemuDomainRemoveDevice(), and that function sends + * the DEVICE_REMOVED event for them, this function is different - + * it is called from other places than just + * qemuDomainRemoveDevice(), so it must send the DEVICE_REMOVED + * event itself. + */ event = virDomainEventDeviceRemovedNewFromObj(vm, chr->info.alias); virObjectEventStateQueue(driver->domainEventState, event);
- qemuDomainReleaseDeviceAddress(vm, &chr->info, NULL); - qemuDomainChrRemove(vm->def, chr); virDomainChrDefFree(chr); ret = 0;
@@ -4967,7 +4955,6 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRNGDefPtr rng) { - virObjectEventPtr event; char *charAlias = NULL; char *objAlias = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; @@ -5016,9 +5003,6 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver, if (qemuDomainNamespaceTeardownRNG(vm, rng) < 0) VIR_WARN("Unable to remove RNG device from /dev");
- event = virDomainEventDeviceRemovedNewFromObj(vm, rng->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainRNGFind(vm->def, rng)) >= 0) virDomainRNGRemove(vm->def, idx); qemuDomainReleaseDeviceAddress(vm, &rng->info, NULL); @@ -5043,7 +5027,6 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver, char *charAlias = NULL; char *memAlias = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; - virObjectEventPtr event = NULL;
VIR_DEBUG("Removing shmem device %s from domain %p %s", shmem->info.alias, vm, vm->def->name); @@ -5071,9 +5054,6 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver, if (rc < 0) goto cleanup;
- event = virDomainEventDeviceRemovedNewFromObj(vm, shmem->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainShmemDefFind(vm->def, shmem)) >= 0) virDomainShmemDefRemove(vm->def, idx); qemuDomainReleaseDeviceAddress(vm, &shmem->info, NULL); @@ -5089,17 +5069,12 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver,
static int -qemuDomainRemoveWatchdog(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainRemoveWatchdog(virDomainObjPtr vm, virDomainWatchdogDefPtr watchdog) { - virObjectEventPtr event = NULL; - VIR_DEBUG("Removing watchdog %s from domain %p %s", watchdog->info.alias, vm, vm->def->name);
- event = virDomainEventDeviceRemovedNewFromObj(vm, watchdog->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); qemuDomainReleaseDeviceAddress(vm, &watchdog->info, NULL); virDomainWatchdogDefFree(vm->def->watchdog); vm->def->watchdog = NULL; @@ -5111,16 +5086,11 @@ static int qemuDomainRemoveInputDevice(virDomainObjPtr vm, virDomainInputDefPtr dev) { - qemuDomainObjPrivatePtr priv = vm->privateData; - virQEMUDriverPtr driver = priv->driver; - virObjectEventPtr event = NULL; size_t i;
VIR_DEBUG("Removing input device %s from domain %p %s", dev->info.alias, vm, vm->def->name);
- event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); for (i = 0; i < vm->def->ninputs; i++) { if (vm->def->inputs[i] == dev) break; @@ -5145,15 +5115,9 @@ static int qemuDomainRemoveVsockDevice(virDomainObjPtr vm, virDomainVsockDefPtr dev) { - qemuDomainObjPrivatePtr priv = vm->privateData; - virQEMUDriverPtr driver = priv->driver; - virObjectEventPtr event = NULL; - VIR_DEBUG("Removing vsock device %s from domain %p %s", dev->info.alias, vm, vm->def->name);
- event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); qemuDomainReleaseDeviceAddress(vm, &dev->info, NULL); virDomainVsockDefFree(vm->def->vsock); vm->def->vsock = NULL; @@ -5167,7 +5131,6 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, virDomainRedirdevDefPtr dev) { qemuDomainObjPrivatePtr priv = vm->privateData; - virObjectEventPtr event; char *charAlias = NULL; ssize_t idx; int ret = -1; @@ -5192,9 +5155,6 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver,
virDomainAuditRedirdev(vm, dev, "detach", true);
- event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainRedirdevDefFind(vm->def, dev)) >= 0) virDomainRedirdevDefRemove(vm->def, idx); qemuDomainReleaseDeviceAddress(vm, &dev->info, NULL); @@ -5285,50 +5245,88 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { - int ret = -1; + virDomainDeviceInfoPtr info; + virObjectEventPtr event; + VIR_AUTOFREE(char *)alias = NULL; + + if (!(info = virDomainDeviceGetInfo(dev))) { + /* + * 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.
Well, but it's called prior to doing a return down below. The deduction from the sentence is that those that use 'return' don't necessarily have it and thus wouldn't ever be reached.
+ */ + virReportError(VIR_ERR_INTERNAL_ERROR, + _("device of type '%s' has no device info"), + virDomainDeviceTypeToString(dev->type)); + return -1;
I think you can change this to a best-effort scenario. Copy the alias only if info is non-null and emit the event in the same case.
+ } + + /* + * save the alias to use when sending a DEVICE_REMOVED event after + * all other teardown is complete + */ + if (VIR_STRDUP(alias, info->alias) < 0) + return -1;
Also at this point 'info' should be set to NULL as any call to the Remove function frees the definition thus info would be dangling.
+ switch ((virDomainDeviceType)dev->type) { + case VIR_DOMAIN_DEVICE_CHR: + /* unlike other qemuDomainRemove*Device() functions, this one + * can't take advantage of any common code at the end of + * qemuDomainRemoveDevice(). This is because it is called + * directly from other places (with an extra arg that would be + * clumsy to add into qemuDomainRemoveDevice())
The last sentence is not necessary.
+ */ + return qemuDomainRemoveChrDevice(driver, vm, dev->data.chr, true); + + /* + * all of the following qemuDomainRemove*Device() functions + * are (and must be) only called from this function, so any + * code that is common to them all can be pulled out and put + * at the end of this function.
Or before if it's the same prologue code.
+ */ case VIR_DOMAIN_DEVICE_DISK: - ret = qemuDomainRemoveDiskDevice(driver, vm, dev->data.disk); + if (qemuDomainRemoveDiskDevice(driver, vm, dev->data.disk) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_CONTROLLER: - ret = qemuDomainRemoveControllerDevice(driver, vm, dev->data.controller); + if (qemuDomainRemoveControllerDevice(vm, dev->data.controller) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_NET: - ret = qemuDomainRemoveNetDevice(driver, vm, dev->data.net); + if (qemuDomainRemoveNetDevice(driver, vm, dev->data.net) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = qemuDomainRemoveHostDevice(driver, vm, dev->data.hostdev); - break; - - case VIR_DOMAIN_DEVICE_CHR: - ret = qemuDomainRemoveChrDevice(driver, vm, dev->data.chr, true); + if (qemuDomainRemoveHostDevice(driver, vm, dev->data.hostdev) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_RNG: - ret = qemuDomainRemoveRNGDevice(driver, vm, dev->data.rng); + if (qemuDomainRemoveRNGDevice(driver, vm, dev->data.rng) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_MEMORY: - ret = qemuDomainRemoveMemoryDevice(driver, vm, dev->data.memory); + if (qemuDomainRemoveMemoryDevice(driver, vm, dev->data.memory) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_SHMEM: - ret = qemuDomainRemoveShmemDevice(driver, vm, dev->data.shmem); + if (qemuDomainRemoveShmemDevice(driver, vm, dev->data.shmem) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_INPUT: - ret = qemuDomainRemoveInputDevice(vm, dev->data.input); + if (qemuDomainRemoveInputDevice(vm, dev->data.input) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_REDIRDEV: - ret = qemuDomainRemoveRedirdevDevice(driver, vm, dev->data.redirdev); + if (qemuDomainRemoveRedirdevDevice(driver, vm, dev->data.redirdev) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_WATCHDOG: - ret = qemuDomainRemoveWatchdog(driver, vm, dev->data.watchdog); + if (qemuDomainRemoveWatchdog(vm, dev->data.watchdog) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_VSOCK: - ret = qemuDomainRemoveVsockDevice(vm, dev->data.vsock); + if (qemuDomainRemoveVsockDevice(vm, dev->data.vsock) < 0) + return -1; break;
case VIR_DOMAIN_DEVICE_NONE: @@ -5350,7 +5348,11 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, virDomainDeviceTypeToString(dev->type)); break; } - return ret; + + event = virDomainEventDeviceRemovedNewFromObj(vm, alias); + virObjectEventStateQueue(driver->domainEventState, event);
Btw, this is probably a regression since we fixed locking of the 'driver' object. Until then the event would stay queued until the end of the API. ACK when the alias is copied best-effort rather than reporting error and info is set to NULL appropriately.

On 3/22/19 8:51 AM, Peter Krempa wrote:
On Thu, Mar 21, 2019 at 18:29:01 -0400, Laine Stump wrote:
The VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED event is sent after qemu has responded to a device_del command with a DEVICE_DELETED event. Before queuing the event, *some* of the final teardown of the device's trappings in libvirt is done, but not *all* of it. As a result, an application may receive and process the DEVICE_REMOVED event before libvirt has really finished with it.
Usually this doesn't cause a problem, but it can - in the case of the bug report referenced below, vdsm is assigning a PCI device to a guest with managed='no', using livirt's virNodeDeviceDetachFlags() and virNodeDeviceReAttach() APIs. Immediately after receiving a DEVICE_REMOVED event from libvirt signalling that the device had been successfully unplugged, vdsm would cal virNodeDeviceReAttach() to unbind the device from vfio-pci and rebind it to the host driverm but because the event was received before libvirt had completely finished processing the removal, that device was still on the "activeDevs" list, and so virNodeDeviceReAttach() failed. So between the lines this reads as if qemuDomainRemoveHostDevice is broken. I agree though that fixing everything systematically is better.
Experimentation with additional debug logs proved that libvirt would always end up dispatching the DEVICE_REMOVED event before it had removed the device from activeDevs (with a *much* greater difference with managed='yes', since in that case the re-binding of the device occurred after queuing the device).
Although the case of hostdev devices is the most extreme (since there is so much involved in tearing down the device), *all* device types suffer from the same problem - the DEVICE_REMOVED event is queued very early in the qemuDomainRemove*Device() function for all of them, resulting in a possibility of any application receiving the event before libvirt has really finished with the device.
The solution is to save the device's alias (which is the only piece of info from the device object that is needed for the event) at the beginning of processing the device removal, and then queue the event as a final act before returning. Since all of the qemuDomainRemove*Device() functions (except qemuDomainRemoveChrDevice()) are now called exclusively from qemuDomainRemoveDevice() (which selects which of the subordinates to call in a switch statement based on the type of device), the shortest route to a solution is to doing the saving of alias, and later queueing of the event, in the higher level qemuDomainRemoveDevice(), and just completely remove the event-related code from all the subordinate functions.
The single exception to this, as mentioned before, is qemuDomainRemoveChrDevice(), which is still called from somewhere other than qemuDomainRemoveDevice() (and has a separate arg used to trigger different behavior when the chr device has targetType == GUESTFWD), so it must keep its original behavior intact, and must be treated differently by qemuDomainRemoveDevice() (similar to the way that qemuDomainDetachDeviceLive() treats chr and lease devices differently from all the others).
Resolves: https://bugzilla.redhat.com/1658198
Signed-off-by: Laine Stump <laine@laine.org> --- src/qemu/qemu_hotplug.c | 154 ++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 76 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index de7a7a2c95..43cc3a314d 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4501,7 +4501,6 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, { qemuHotplugDiskSourceDataPtr diskbackend = NULL; virDomainDeviceDef dev; - virObjectEventPtr event; size_t i; qemuDomainObjPrivatePtr priv = vm->privateData; int ret = -1; @@ -4529,9 +4528,6 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver,
virDomainAuditDisk(vm, disk->src, NULL, "detach", true);
- event = virDomainEventDeviceRemovedNewFromObj(vm, disk->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - qemuDomainReleaseDeviceAddress(vm, &disk->info, virDomainDiskGetSource(disk));
/* tear down disk security access */ @@ -4555,19 +4551,14 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver,
static int -qemuDomainRemoveControllerDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainRemoveControllerDevice(virDomainObjPtr vm, virDomainControllerDefPtr controller) { - virObjectEventPtr event; size_t i;
VIR_DEBUG("Removing controller %s from domain %p %s", controller->info.alias, vm, vm->def->name);
- event = virDomainEventDeviceRemovedNewFromObj(vm, controller->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - for (i = 0; i < vm->def->ncontrollers; i++) { if (vm->def->controllers[i] == controller) { virDomainControllerRemove(vm->def, i); @@ -4589,7 +4580,6 @@ qemuDomainRemoveMemoryDevice(virQEMUDriverPtr driver, qemuDomainObjPrivatePtr priv = vm->privateData; unsigned long long oldmem = virDomainDefGetMemoryTotal(vm->def); unsigned long long newmem = oldmem - mem->size; - virObjectEventPtr event; char *backendAlias = NULL; int rc; int idx; @@ -4611,9 +4601,6 @@ qemuDomainRemoveMemoryDevice(virQEMUDriverPtr driver, if (rc < 0) return -1;
- event = virDomainEventDeviceRemovedNewFromObj(vm, mem->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainMemoryFindByDef(vm->def, mem)) >= 0) virDomainMemoryRemove(vm->def, idx);
@@ -4693,7 +4680,6 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver, { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virDomainNetDefPtr net = NULL; - virObjectEventPtr event; size_t i; int ret = -1; qemuDomainObjPrivatePtr priv = vm->privateData; @@ -4737,9 +4723,6 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver, goto cleanup; }
- event = virDomainEventDeviceRemovedNewFromObj(vm, hostdev->info->alias); - virObjectEventStateQueue(driver->domainEventState, event); - if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET) { net = hostdev->parent.data.net;
@@ -4818,7 +4801,6 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver, { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); qemuDomainObjPrivatePtr priv = vm->privateData; - virObjectEventPtr event; char *hostnet_name = NULL; char *charDevAlias = NULL; size_t i; @@ -4863,9 +4845,6 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver,
virDomainAuditNet(vm, net, NULL, "detach", true);
- event = virDomainEventDeviceRemovedNewFromObj(vm, net->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - for (i = 0; i < vm->def->nnets; i++) { if (vm->def->nets[i] == net) { virDomainNetRemove(vm->def, i); @@ -4948,11 +4927,20 @@ qemuDomainRemoveChrDevice(virQEMUDriverPtr driver, if (qemuDomainNamespaceTeardownChardev(vm, chr) < 0) VIR_WARN("Unable to remove chr device from /dev");
+ qemuDomainReleaseDeviceAddress(vm, &chr->info, NULL); + qemuDomainChrRemove(vm->def, chr); + + /* NB: all other qemuDomainRemove*Device() functions omit the + * sending of the DEVICE_REMOVED event, because they are *only* + * called from qemuDomainRemoveDevice(), and that function sends + * the DEVICE_REMOVED event for them, this function is different - + * it is called from other places than just + * qemuDomainRemoveDevice(), so it must send the DEVICE_REMOVED + * event itself. + */ event = virDomainEventDeviceRemovedNewFromObj(vm, chr->info.alias); virObjectEventStateQueue(driver->domainEventState, event);
- qemuDomainReleaseDeviceAddress(vm, &chr->info, NULL); - qemuDomainChrRemove(vm->def, chr); virDomainChrDefFree(chr); ret = 0;
@@ -4967,7 +4955,6 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRNGDefPtr rng) { - virObjectEventPtr event; char *charAlias = NULL; char *objAlias = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; @@ -5016,9 +5003,6 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver, if (qemuDomainNamespaceTeardownRNG(vm, rng) < 0) VIR_WARN("Unable to remove RNG device from /dev");
- event = virDomainEventDeviceRemovedNewFromObj(vm, rng->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainRNGFind(vm->def, rng)) >= 0) virDomainRNGRemove(vm->def, idx); qemuDomainReleaseDeviceAddress(vm, &rng->info, NULL); @@ -5043,7 +5027,6 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver, char *charAlias = NULL; char *memAlias = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; - virObjectEventPtr event = NULL;
VIR_DEBUG("Removing shmem device %s from domain %p %s", shmem->info.alias, vm, vm->def->name); @@ -5071,9 +5054,6 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver, if (rc < 0) goto cleanup;
- event = virDomainEventDeviceRemovedNewFromObj(vm, shmem->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainShmemDefFind(vm->def, shmem)) >= 0) virDomainShmemDefRemove(vm->def, idx); qemuDomainReleaseDeviceAddress(vm, &shmem->info, NULL); @@ -5089,17 +5069,12 @@ qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver,
static int -qemuDomainRemoveWatchdog(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainRemoveWatchdog(virDomainObjPtr vm, virDomainWatchdogDefPtr watchdog) { - virObjectEventPtr event = NULL; - VIR_DEBUG("Removing watchdog %s from domain %p %s", watchdog->info.alias, vm, vm->def->name);
- event = virDomainEventDeviceRemovedNewFromObj(vm, watchdog->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); qemuDomainReleaseDeviceAddress(vm, &watchdog->info, NULL); virDomainWatchdogDefFree(vm->def->watchdog); vm->def->watchdog = NULL; @@ -5111,16 +5086,11 @@ static int qemuDomainRemoveInputDevice(virDomainObjPtr vm, virDomainInputDefPtr dev) { - qemuDomainObjPrivatePtr priv = vm->privateData; - virQEMUDriverPtr driver = priv->driver; - virObjectEventPtr event = NULL; size_t i;
VIR_DEBUG("Removing input device %s from domain %p %s", dev->info.alias, vm, vm->def->name);
- event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); for (i = 0; i < vm->def->ninputs; i++) { if (vm->def->inputs[i] == dev) break; @@ -5145,15 +5115,9 @@ static int qemuDomainRemoveVsockDevice(virDomainObjPtr vm, virDomainVsockDefPtr dev) { - qemuDomainObjPrivatePtr priv = vm->privateData; - virQEMUDriverPtr driver = priv->driver; - virObjectEventPtr event = NULL; - VIR_DEBUG("Removing vsock device %s from domain %p %s", dev->info.alias, vm, vm->def->name);
- event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); qemuDomainReleaseDeviceAddress(vm, &dev->info, NULL); virDomainVsockDefFree(vm->def->vsock); vm->def->vsock = NULL; @@ -5167,7 +5131,6 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, virDomainRedirdevDefPtr dev) { qemuDomainObjPrivatePtr priv = vm->privateData; - virObjectEventPtr event; char *charAlias = NULL; ssize_t idx; int ret = -1; @@ -5192,9 +5155,6 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver,
virDomainAuditRedirdev(vm, dev, "detach", true);
- event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); - virObjectEventStateQueue(driver->domainEventState, event); - if ((idx = virDomainRedirdevDefFind(vm->def, dev)) >= 0) virDomainRedirdevDefRemove(vm->def, idx); qemuDomainReleaseDeviceAddress(vm, &dev->info, NULL); @@ -5285,50 +5245,88 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { - int ret = -1; + virDomainDeviceInfoPtr info; + virObjectEventPtr event; + VIR_AUTOFREE(char *)alias = NULL; + + if (!(info = virDomainDeviceGetInfo(dev))) { + /* + * 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. Well, but it's called prior to doing a return down below. The deduction from the sentence is that those that use 'return' don't necessarily have it and thus wouldn't ever be reached.
Yeah, I'm not sure what I was thinking when I wrote that comment :-/
+ */ + virReportError(VIR_ERR_INTERNAL_ERROR, + _("device of type '%s' has no device info"), + virDomainDeviceTypeToString(dev->type)); + return -1; I think you can change this to a best-effort scenario. Copy the alias only if info is non-null and emit the event in the same case.
Okay, that sounds reasonable.
+ } + + /* + * save the alias to use when sending a DEVICE_REMOVED event after + * all other teardown is complete + */ + if (VIR_STRDUP(alias, info->alias) < 0) + return -1; Also at this point 'info' should be set to NULL as any call to the Remove function frees the definition thus info would be dangling.
Although it's not *currently* used after this point anyway, I agree that is the prudent thing to do to prevent some future maintainer from assuming they can use it.
+ switch ((virDomainDeviceType)dev->type) { + case VIR_DOMAIN_DEVICE_CHR: + /* unlike other qemuDomainRemove*Device() functions, this one + * can't take advantage of any common code at the end of + * qemuDomainRemoveDevice(). This is because it is called + * directly from other places (with an extra arg that would be + * clumsy to add into qemuDomainRemoveDevice()) The last sentence is not necessary.
Yeah, sometimes my comments are useful when making the change, but pointless once it is made. I'll remove it.
+ */ + return qemuDomainRemoveChrDevice(driver, vm, dev->data.chr, true); + + /* + * all of the following qemuDomainRemove*Device() functions + * are (and must be) only called from this function, so any + * code that is common to them all can be pulled out and put + * at the end of this function. Or before if it's the same prologue code.
Good point. I'll change "at the end of" to "in".
+ */ case VIR_DOMAIN_DEVICE_DISK: - ret = qemuDomainRemoveDiskDevice(driver, vm, dev->data.disk); + if (qemuDomainRemoveDiskDevice(driver, vm, dev->data.disk) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_CONTROLLER: - ret = qemuDomainRemoveControllerDevice(driver, vm, dev->data.controller); + if (qemuDomainRemoveControllerDevice(vm, dev->data.controller) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_NET: - ret = qemuDomainRemoveNetDevice(driver, vm, dev->data.net); + if (qemuDomainRemoveNetDevice(driver, vm, dev->data.net) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = qemuDomainRemoveHostDevice(driver, vm, dev->data.hostdev); - break; - - case VIR_DOMAIN_DEVICE_CHR: - ret = qemuDomainRemoveChrDevice(driver, vm, dev->data.chr, true); + if (qemuDomainRemoveHostDevice(driver, vm, dev->data.hostdev) < 0) + return -1; break; case VIR_DOMAIN_DEVICE_RNG: - ret = qemuDomainRemoveRNGDevice(driver, vm, dev->data.rng); + if (qemuDomainRemoveRNGDevice(driver, vm, dev->data.rng) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_MEMORY: - ret = qemuDomainRemoveMemoryDevice(driver, vm, dev->data.memory); + if (qemuDomainRemoveMemoryDevice(driver, vm, dev->data.memory) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_SHMEM: - ret = qemuDomainRemoveShmemDevice(driver, vm, dev->data.shmem); + if (qemuDomainRemoveShmemDevice(driver, vm, dev->data.shmem) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_INPUT: - ret = qemuDomainRemoveInputDevice(vm, dev->data.input); + if (qemuDomainRemoveInputDevice(vm, dev->data.input) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_REDIRDEV: - ret = qemuDomainRemoveRedirdevDevice(driver, vm, dev->data.redirdev); + if (qemuDomainRemoveRedirdevDevice(driver, vm, dev->data.redirdev) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_WATCHDOG: - ret = qemuDomainRemoveWatchdog(driver, vm, dev->data.watchdog); + if (qemuDomainRemoveWatchdog(vm, dev->data.watchdog) < 0) + return -1; break; - case VIR_DOMAIN_DEVICE_VSOCK: - ret = qemuDomainRemoveVsockDevice(vm, dev->data.vsock); + if (qemuDomainRemoveVsockDevice(vm, dev->data.vsock) < 0) + return -1; break;
case VIR_DOMAIN_DEVICE_NONE: @@ -5350,7 +5348,11 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, virDomainDeviceTypeToString(dev->type)); break; } - return ret; + + event = virDomainEventDeviceRemovedNewFromObj(vm, alias); + virObjectEventStateQueue(driver->domainEventState, event); Btw, this is probably a regression since we fixed locking of the 'driver' object. Until then the event would stay queued until the end of the API.
It took me a minute to parse that. So what you mean is that the "event is dispatched to the application too early" bug was a regression resulting from a fix made to driver locking, right? That makes a lot of sense.
ACK when the alias is copied best-effort rather than reporting error and info is set to NULL appropriately.
participants (4)
-
Boris Fiuczynski
-
Ján Tomko
-
Laine Stump
-
Peter Krempa