ch: start a new thread for handling ch events

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>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Purna Pavan Chandra Aekkaladevi 2025-01-17 17:11:06 +00:00 committed by Michal Privoznik
parent d7c9660b15
commit 3015c28c1e
5 changed files with 167 additions and 4 deletions

79
src/ch/ch_events.c Normal file
View File

@ -0,0 +1,79 @@
/*
* 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 "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);
}

26
src/ch/ch_events.h Normal file
View File

@ -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);

View File

@ -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'",

View File

@ -97,6 +97,10 @@ struct _virCHMonitor {
char *socketpath;
char *eventmonitorpath;
int eventmonitorfd;
virThread event_handler_thread;
int event_handler_stop;
pid_t pid;

View File

@ -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',