[PATCH v5 2/5] ch: start a new thread for handling ch events

Purna Pavan Chandra Aekkaladevi posted 5 patches 3 days, 11 hours ago
[PATCH v5 2/5] ch: start a new thread for handling ch events
Posted by Purna Pavan Chandra Aekkaladevi 3 days, 11 hours ago
Use a FIFO(named pipe) for --event-monitor option in CH. Introduce a new
thread, `virCHEventHandlerLoop`, to continuously monitor and handle
events from cloud-hypervisor.

Signed-off-by: Purna Pavan Chandra Aekkaladevi <paekkaladevi@linux.microsoft.com>
Co-authored-by: Vineeth Pillai <viremana@linux.microsoft.com>
---
 src/ch/ch_events.c  | 78 +++++++++++++++++++++++++++++++++++++++++++++
 src/ch/ch_events.h  | 26 +++++++++++++++
 src/ch/ch_monitor.c | 60 +++++++++++++++++++++++++++++++---
 src/ch/ch_monitor.h |  4 +++
 src/ch/meson.build  |  2 ++
 5 files changed, 166 insertions(+), 4 deletions(-)
 create mode 100644 src/ch/ch_events.c
 create mode 100644 src/ch/ch_events.h

diff --git a/src/ch/ch_events.c b/src/ch/ch_events.c
new file mode 100644
index 0000000000..b6bbce2db0
--- /dev/null
+++ b/src/ch/ch_events.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright Microsoft Corp. 2024
+ *
+ * ch_events.c: Handle Cloud-Hypervisor events
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <fcntl.h>
+
+#include "ch_domain.h"
+#include "ch_events.h"
+#include "ch_process.h"
+#include "virfile.h"
+#include "virlog.h"
+
+VIR_LOG_INIT("ch.ch_events");
+
+
+static void virCHEventHandlerLoop(void *data)
+{
+    virCHMonitor *mon = data;
+    virDomainObj *vm = NULL;
+
+    /* Obtain a vm reference */
+    vm = virObjectRef(mon->vm);
+
+    VIR_DEBUG("%s: Event handler loop thread starting", vm->def->name);
+
+    while (g_atomic_int_get(&mon->event_handler_stop) == 0) {
+        VIR_DEBUG("%s: Reading events from event monitor file", vm->def->name);
+        /* Read and process events here */
+    }
+
+    virObjectUnref(vm);
+    VIR_DEBUG("%s: Event handler loop thread exiting", vm->def->name);
+    return;
+}
+
+int virCHStartEventHandler(virCHMonitor *mon)
+{
+    g_autofree char *name = NULL;
+    name = g_strdup_printf("ch-evt-%d", mon->pid);
+
+    virObjectRef(mon);
+    if (virThreadCreateFull(&mon->event_handler_thread,
+                            false,
+                            virCHEventHandlerLoop,
+                            name,
+                            false,
+                            mon) < 0) {
+        virObjectUnref(mon);
+        return -1;
+    }
+    virObjectUnref(mon);
+
+    g_atomic_int_set(&mon->event_handler_stop, 0);
+    return 0;
+}
+
+void virCHStopEventHandler(virCHMonitor *mon)
+{
+    g_atomic_int_set(&mon->event_handler_stop, 1);
+}
diff --git a/src/ch/ch_events.h b/src/ch/ch_events.h
new file mode 100644
index 0000000000..4c8a48231d
--- /dev/null
+++ b/src/ch/ch_events.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright Microsoft Corp. 2024
+ *
+ * ch_events.h: header file for handling Cloud-Hypervisor events
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ch_monitor.h"
+
+int virCHStartEventHandler(virCHMonitor *mon);
+void virCHStopEventHandler(virCHMonitor *mon);
diff --git a/src/ch/ch_monitor.c b/src/ch/ch_monitor.c
index a72ee40aa1..bedcde2dde 100644
--- a/src/ch/ch_monitor.c
+++ b/src/ch/ch_monitor.c
@@ -20,12 +20,14 @@
 
 #include <config.h>
 
+#include <fcntl.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <curl/curl.h>
 
 #include "datatypes.h"
 #include "ch_conf.h"
+#include "ch_events.h"
 #include "ch_interface.h"
 #include "ch_monitor.h"
 #include "domain_interface.h"
@@ -542,6 +544,7 @@ virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg, int logfile)
     g_autoptr(virCHMonitor) mon = NULL;
     g_autoptr(virCommand) cmd = NULL;
     int socket_fd = 0;
+    int event_monitor_fd;
 
     if (virCHMonitorInitialize() < 0)
         return NULL;
@@ -549,6 +552,9 @@ virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg, int logfile)
     if (!(mon = virObjectLockableNew(virCHMonitorClass)))
         return NULL;
 
+    /* avoid VIR_FORCE_CLOSE()-ing garbage fd value in virCHMonitorClose */
+    mon->eventmonitorfd = -1;
+
     if (!vm->def) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("VM is not defined"));
@@ -557,8 +563,6 @@ virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg, int logfile)
 
     /* prepare to launch Cloud-Hypervisor socket */
     mon->socketpath = g_strdup_printf("%s/%s-socket", cfg->stateDir, vm->def->name);
-    mon->eventmonitorpath = g_strdup_printf("%s/%s-event-monitor",
-                                            cfg->stateDir, vm->def->name);
     if (g_mkdir_with_parents(cfg->stateDir, 0777) < 0) {
         virReportSystemError(errno,
                              _("Cannot create socket directory '%1$s'"),
@@ -573,6 +577,27 @@ virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg, int logfile)
         return NULL;
     }
 
+    /* Event monitor file to listen for VM state changes */
+    mon->eventmonitorpath = g_strdup_printf("%s/%s-event-monitor-fifo",
+                                            cfg->stateDir, vm->def->name);
+    if (virFileExists(mon->eventmonitorpath)) {
+        VIR_WARN("Monitor file (%s) already exists, trying to delete!",
+                  mon->eventmonitorpath);
+        if (virFileRemove(mon->eventmonitorpath, -1, -1) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Failed to remove the file: %1$s"),
+                           mon->eventmonitorpath);
+            return NULL;
+        }
+    }
+
+    if (mkfifo(mon->eventmonitorpath, S_IWUSR | S_IRUSR) < 0 &&
+            errno != EEXIST) {
+        virReportSystemError(errno, "%s",
+                             _("Cannot create monitor FIFO"));
+        return NULL;
+    }
+
     cmd = virCommandNew(vm->def->emulator);
     virCommandSetOutputFD(cmd, &logfile);
     virCommandSetErrorFD(cmd, &logfile);
@@ -597,12 +622,35 @@ virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg, int logfile)
     if (virCommandRunAsync(cmd, &mon->pid) < 0)
         return NULL;
 
-    /* get a curl handle */
-    mon->handle = curl_easy_init();
+    /* open the reader end of fifo before start Event Handler */
+    while ((event_monitor_fd = open(mon->eventmonitorpath, O_RDONLY)) < 0) {
+        if (errno == EINTR) {
+            /* 100 milli seconds */
+            g_usleep(100000);
+            continue;
+        }
+        /* Any other error should be a BUG(kernel/libc/libvirtd)
+         * (ENOMEM can happen on exceeding per-user limits)
+         */
+        VIR_ERROR(_("%1$s: Failed to open the event monitor FIFO(%2$s) read end!"),
+                  vm->def->name, mon->eventmonitorpath);
+        /* CH process(Writer) is blocked at this point as EventHandler(Reader)
+         * fails to open the FIFO.
+         */
+        return NULL;
+    }
+    mon->eventmonitorfd = event_monitor_fd;
+    VIR_DEBUG("%s: Opened the event monitor FIFO(%s)", vm->def->name, mon->eventmonitorpath);
 
     /* now has its own reference */
     mon->vm = virObjectRef(vm);
 
+    if (virCHStartEventHandler(mon) < 0)
+        return NULL;
+
+    /* get a curl handle */
+    mon->handle = curl_easy_init();
+
     return g_steal_pointer(&mon);
 }
 
@@ -638,6 +686,10 @@ void virCHMonitorClose(virCHMonitor *mon)
         g_clear_pointer(&mon->socketpath, g_free);
     }
 
+    virCHStopEventHandler(mon);
+    if (mon->eventmonitorfd >= 0) {
+        VIR_FORCE_CLOSE(mon->eventmonitorfd);
+    }
     if (mon->eventmonitorpath) {
         if (virFileRemove(mon->eventmonitorpath, -1, -1) < 0) {
             VIR_WARN("Unable to remove CH event monitor file '%s'",
diff --git a/src/ch/ch_monitor.h b/src/ch/ch_monitor.h
index 67dc54f9f4..b9092f22b8 100644
--- a/src/ch/ch_monitor.h
+++ b/src/ch/ch_monitor.h
@@ -97,6 +97,10 @@ struct _virCHMonitor {
     char *socketpath;
 
     char *eventmonitorpath;
+    int eventmonitorfd;
+
+    virThread event_handler_thread;
+    int event_handler_stop;
 
     pid_t pid;
 
diff --git a/src/ch/meson.build b/src/ch/meson.build
index ca1291c158..0b4a5aeb49 100644
--- a/src/ch/meson.build
+++ b/src/ch/meson.build
@@ -7,6 +7,8 @@ ch_driver_sources = [
   'ch_domain.h',
   'ch_driver.c',
   'ch_driver.h',
+  'ch_events.c',
+  'ch_events.h',
   'ch_interface.c',
   'ch_interface.h',
   'ch_monitor.c',
-- 
2.34.1