From 3865410e7f67ca4ec66e9a905e75f452762a97f0 Mon Sep 17 00:00:00 2001 From: Peter Krempa Date: Wed, 12 Jun 2024 15:54:24 +0200 Subject: [PATCH] qemuProcessStop: Move code not depending on 'vm->def->id' after reset of the ID There are few function calls done while cleaning up a stopped VM which do require the old VM id, to e.g. clean up paths containing the 'short' domain name in the path. Anything else, which doesn't strictly require it can be moved after clearing the 'id' in order to decrease likelyhood of potential bugs. This patch moves all the code which does not require the 'id' (except for the log entry and closing the monitor socket) after the statement clearing the id and adds a comment explaining that anything in the section must not unlock the VM object. Signed-off-by: Peter Krempa Reviewed-by: Michal Privoznik --- src/qemu/qemu_process.c | 85 ++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index d3163a8605..4a7cbda16e 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -8488,10 +8488,11 @@ void qemuProcessStop(virQEMUDriver *driver, goto endjob; } - qemuProcessBuildDestroyMemoryPaths(driver, vm, NULL, false); - - if (!!g_atomic_int_dec_and_test(&driver->nactive) && driver->inhibitCallback) - driver->inhibitCallback(false, driver->inhibitOpaque); + /* BEWARE: At this point 'vm->def->id' is not cleared yet. Any code that + * requires the id (e.g. to call virDomainDefGetShortName()) must be placed + * between here (after the VM is killed) and the statement clearing the id. + * The code *MUST NOT* unlock vm, otherwise other code might be confused + * about the state of the VM. */ if ((timestamp = virTimeStringNow()) != NULL) { qemuDomainLogAppendMessage(driver, vm, "%s: shutting down, reason=%s\n", @@ -8499,6 +8500,47 @@ void qemuProcessStop(virQEMUDriver *driver, virDomainShutoffReasonTypeToString(reason)); } + /* shut it off for sure */ + ignore_value(qemuProcessKill(vm, + VIR_QEMU_PROCESS_KILL_FORCE| + VIR_QEMU_PROCESS_KILL_NOCHECK)); + + if (priv->agent) { + g_clear_pointer(&priv->agent, qemuAgentClose); + } + priv->agentError = false; + + if (priv->mon) { + g_clear_pointer(&priv->mon, qemuMonitorClose); + } + + qemuProcessBuildDestroyMemoryPaths(driver, vm, NULL, false); + + /* Do this before we delete the tree and remove pidfile. */ + qemuProcessKillManagedPRDaemon(vm); + + qemuDomainCleanupRun(driver, vm); + + outgoingMigration = (flags & VIR_QEMU_PROCESS_STOP_MIGRATED) && + (asyncJob == VIR_ASYNC_JOB_MIGRATION_OUT); + + qemuExtDevicesStop(driver, vm, outgoingMigration); + + qemuDBusStop(driver, vm); + + vm->def->id = -1; + + /* Wake up anything waiting on domain condition */ + virDomainObjBroadcast(vm); + + /* IMPORTANT: qemuDomainObjStopWorker() unlocks @vm in order to prevent + * deadlocks with the per-VM event loop thread. This MUST be done after + * marking the VM as dead */ + qemuDomainObjStopWorker(vm); + + if (!!g_atomic_int_dec_and_test(&driver->nactive) && driver->inhibitCallback) + driver->inhibitCallback(false, driver->inhibitOpaque); + /* Clear network bandwidth */ virDomainClearNetBandwidth(vm->def); @@ -8518,15 +8560,6 @@ void qemuProcessStop(virQEMUDriver *driver, virPortAllocatorRelease(priv->nbdPort); priv->nbdPort = 0; - if (priv->agent) { - g_clear_pointer(&priv->agent, qemuAgentClose); - } - priv->agentError = false; - - if (priv->mon) { - g_clear_pointer(&priv->mon, qemuMonitorClose); - } - if (priv->monConfig) { if (priv->monConfig->type == VIR_DOMAIN_CHR_TYPE_UNIX) unlink(priv->monConfig->data.nix.path); @@ -8536,41 +8569,15 @@ void qemuProcessStop(virQEMUDriver *driver, /* Remove the master key */ qemuDomainMasterKeyRemove(priv); - /* Do this before we delete the tree and remove pidfile. */ - qemuProcessKillManagedPRDaemon(vm); - ignore_value(virDomainChrDefForeach(vm->def, false, qemuProcessCleanupChardevDevice, NULL)); - /* shut it off for sure */ - ignore_value(qemuProcessKill(vm, - VIR_QEMU_PROCESS_KILL_FORCE| - VIR_QEMU_PROCESS_KILL_NOCHECK)); - /* Its namespace is also gone then. */ qemuDomainDestroyNamespace(driver, vm); - qemuDomainCleanupRun(driver, vm); - - outgoingMigration = (flags & VIR_QEMU_PROCESS_STOP_MIGRATED) && - (asyncJob == VIR_ASYNC_JOB_MIGRATION_OUT); - qemuExtDevicesStop(driver, vm, outgoingMigration); - - qemuDBusStop(driver, vm); - - vm->def->id = -1; - - /* Wake up anything waiting on domain condition */ - virDomainObjBroadcast(vm); - - /* IMPORTANT: qemuDomainObjStopWorker() unlocks @vm in order to prevent - * deadlocks with the per-VM event loop thread. This MUST be done after - * marking the VM as dead */ - qemuDomainObjStopWorker(vm); - virFileDeleteTree(priv->libDir); virFileDeleteTree(priv->channelTargetDir); -- 2.39.5