]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
qemu: start/stop an event loop thread for domains
authorDaniel P. Berrangé <berrange@redhat.com>
Wed, 12 Feb 2020 12:26:11 +0000 (12:26 +0000)
committerDaniel P. Berrangé <berrange@redhat.com>
Wed, 11 Mar 2020 14:44:44 +0000 (14:44 +0000)
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.

Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
src/qemu/qemu_domain.c
src/qemu/qemu_domain.h
src/qemu/qemu_process.c

index 3d3f796d8544775a946aaf59ec54ac218916da4a..bcdd518be684cacb9a0371cefddb560b3e2ddab8 100644 (file)
@@ -2150,6 +2150,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)
 {
@@ -2289,6 +2316,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);
 }
 
index 3929ee9ca1f475b0a1e48c3d104a6af49e32cfa2..099ee597728300b7a82adf5d9452a584bf0a07ba 100644 (file)
@@ -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);
index 499d39a9202ccac95c56fc315d2eb1bd845f4457..82d2dde15bd65d8eb0ea449b8452202b46b113b2 100644 (file)
@@ -320,6 +320,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);
 }
 
@@ -6908,6 +6911,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;
@@ -7390,6 +7396,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);
 
@@ -7981,6 +7999,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);