The event loop thread will be responsible for handling
any per-domain I/O operations, most notably the QEMU
monitor and agent sockets.
We start this event loop when launching QEMU, but stopping
the event loop is a little more complicated. The obvious
idea is to stop it in qemuProcessStop(), but if we do that
we risk loosing the final events from the QEMU monitor, as
they might not have been read by the event thread at the
time we tell the thread to stop.
The solution is to delay shutdown of the event thread until
we have seen EOF from the QEMU monitor, and thus we know
there are no further events to process.
Note that this assumes that we don't have events to process
from the QEMU agent.
Signed-off-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
src/qemu/qemu_domain.c | 33 +++++++++++++++++++++++++++++++++
src/qemu/qemu_domain.h | 6 ++++++
src/qemu/qemu_process.c | 21 +++++++++++++++++++++
3 files changed, 60 insertions(+)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 6fc0bd4e68..916a21b5f2 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -2146,6 +2146,33 @@ dbusVMStateHashFree(void *opaque)
}
+int
+qemuDomainObjStartWorker(virDomainObjPtr dom)
+{
+ qemuDomainObjPrivatePtr priv = dom->privateData;
+
+ if (!priv->eventThread) {
+ g_autofree char *threadName = g_strdup_printf("vm-%s",
dom->def->name);
+ if (!(priv->eventThread = virEventThreadNew(threadName)))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void
+qemuDomainObjStopWorker(virDomainObjPtr dom)
+{
+ qemuDomainObjPrivatePtr priv = dom->privateData;
+
+ if (priv->eventThread) {
+ g_object_unref(priv->eventThread);
+ priv->eventThread = NULL;
+ }
+}
+
+
static void *
qemuDomainObjPrivateAlloc(void *opaque)
{
@@ -2285,6 +2312,12 @@ qemuDomainObjPrivateFree(void *data)
virHashFree(priv->blockjobs);
virHashFree(priv->dbusVMStates);
+ /* This should never be non-NULL if we get here, but just in case... */
+ if (priv->eventThread) {
+ VIR_ERROR(_("Unexpected event thread still active during domain
deletion"));
+ g_object_unref(priv->eventThread);
+ }
+
VIR_FREE(priv);
}
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index f8fb48f2ff..9ce27fbdda 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -40,6 +40,7 @@
#include "logging/log_manager.h"
#include "virdomainmomentobjlist.h"
#include "virenum.h"
+#include "vireventthread.h"
#define QEMU_DOMAIN_FORMAT_LIVE_FLAGS \
(VIR_DOMAIN_XML_SECURE)
@@ -300,6 +301,8 @@ struct _qemuDomainObjPrivate {
virBitmapPtr namespaces;
+ virEventThread *eventThread;
+
qemuMonitorPtr mon;
virDomainChrSourceDefPtr monConfig;
bool monError;
@@ -630,6 +633,9 @@ struct _qemuDomainXmlNsDef {
char **capsdel;
};
+int qemuDomainObjStartWorker(virDomainObjPtr dom);
+void qemuDomainObjStopWorker(virDomainObjPtr dom);
+
virDomainObjPtr qemuDomainObjFromDomain(virDomainPtr domain);
qemuDomainSaveCookiePtr qemuDomainSaveCookieNew(virDomainObjPtr vm);
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index e36d1dd7c7..73158e29e6 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -319,6 +319,9 @@ qemuProcessHandleMonitorEOF(qemuMonitorPtr mon,
qemuDomainDestroyNamespace(driver, vm);
cleanup:
+ /* Now we got EOF we're not expecting more I/O, so we
+ * can finally kill the event thread */
+ qemuDomainObjStopWorker(vm);
virObjectUnlock(vm);
}
@@ -6912,6 +6915,9 @@ qemuProcessLaunch(virConnectPtr conn,
if (rv == -1) /* The VM failed to start */
goto cleanup;
+ if (qemuDomainObjStartWorker(vm) < 0)
+ goto cleanup;
+
VIR_DEBUG("Waiting for monitor to show up");
if (qemuProcessWaitForMonitor(driver, vm, asyncJob, logCtxt) < 0)
goto cleanup;
@@ -7394,6 +7400,18 @@ void qemuProcessStop(virQEMUDriverPtr driver,
priv->monConfig = NULL;
}
+ /*
+ * We cannot stop the event thread at this time. When
+ * we are in this code, we may not yet have processed the
+ * STOP event or EOF from the monitor. So the event loop
+ * may have pending input that we need to process still.
+ * The qemuProcessHandleMonitorEOF method will kill
+ * the event thread because at that point we don't
+ * expect any more I/O from the QEMU monitor. We are
+ * assuming we don't need to get any more events from the
+ * QEMU agent at that time.
+ */
+
/* Remove the master key */
qemuDomainMasterKeyRemove(priv);
@@ -7985,6 +8003,9 @@ qemuProcessReconnect(void *opaque)
virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_CHARDEV_FD_PASS))
retry = false;
+ if (qemuDomainObjStartWorker(obj) < 0)
+ goto error;
+
VIR_DEBUG("Reconnect monitor to def=%p name='%s' retry=%d",
obj, obj->def->name, retry);
--
2.24.1