Skip to content

Commit 0cb9f6a

Browse files
Vineeth Pillaibryteise
authored andcommitted
ch_monitor: Use the monitor framework from cloud-hypervisor
Use the events from cloud-hypervisor monitor framework to handle VM lifecycle events and state changes Signed-off-by: Vineeth Pillai <[email protected]>
1 parent 9ed9ab8 commit 0cb9f6a

File tree

3 files changed

+374
-14
lines changed

3 files changed

+374
-14
lines changed

src/ch/ch_monitor.c

Lines changed: 328 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <stdio.h>
2424
#include <unistd.h>
25+
#include <assert.h>
2526

2627
#include <curl/curl.h>
2728

@@ -563,25 +564,338 @@ chMonitorBuildSocketCmd(virDomainObjPtr vm, const char *socket_path)
563564
return cmd;
564565
}
565566

567+
static const char *virCHMonitorEventStrings[] = {
568+
"vmm:starting",
569+
"vmm:shutdown",
570+
"vm:booting", "vm:booted",
571+
"vm:pausing", "vm:paused",
572+
"vm:resuming", "vm:resumed",
573+
"vm:snapshotting", "vm:snapshotted",
574+
"vm:restoring", "vm:restored",
575+
"vm:resizing", "vm:resized",
576+
"vm:shutdown", "vm:deleted",
577+
"cpu_manager:create_vcpu",
578+
"virtio-device:activated", "virtio-device:reset"
579+
};
580+
581+
static virCHMonitorEvent virCHMonitorEventFromString(const char *sourceStr,
582+
const char *eventStr)
583+
{
584+
int i;
585+
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
586+
const char *event;
587+
588+
virBufferAsprintf(&buf, "%s:%s", sourceStr, eventStr);
589+
event = virBufferCurrentContent(&buf);
590+
591+
for (i = 0; i < virCHMonitorEventMax; i++) {
592+
if STREQ(event, virCHMonitorEventStrings[i])
593+
break;
594+
}
595+
596+
return i;
597+
}
598+
599+
static int virCHMonitorValidateEventsJSON(char *buffer, size_t sz,
600+
bool *incomplete)
601+
{
602+
/*
603+
* Marks the start of a JSON doc(starting with '{')
604+
*/
605+
char *json_start = buffer;
606+
/*
607+
* Marks the start of the buffer where the scan starts.
608+
* It could be either:
609+
* - Start of the buffer. Or
610+
* - Next location after a valid JSON doc.
611+
*/
612+
char *scan_start = buffer;
613+
int blocks = 0;
614+
int events = 0;
615+
int i = 0;
616+
617+
if (sz == 0)
618+
return 0;
619+
620+
/*
621+
* Check if the message is a wellformed JSON. Try to find all
622+
* wellformed JSON doc and adjust the buffer accordingly by
623+
* removing invalid snippets in the buffer.
624+
*/
625+
do {
626+
if (buffer[i] == '{') {
627+
blocks++;
628+
629+
if (blocks != 1)
630+
continue;
631+
632+
/*
633+
* Possible start of a valid JSON doc. Check if
634+
* there were any white characters or garbage
635+
* before the JSON doc at this location.
636+
*/
637+
json_start = buffer + i;
638+
if (scan_start != json_start) {
639+
int invalid_chars = json_start - scan_start;
640+
VIR_WARN("invalid json or white chars in buffer: %.*s",
641+
invalid_chars, scan_start);
642+
memmove(scan_start, json_start, invalid_chars);
643+
}
644+
} else if (buffer[i] == '}' && blocks != 0) {
645+
blocks--;
646+
if (blocks == 0) {
647+
events++;
648+
/*
649+
* This location marks the end of a valid JSON doc.
650+
* Reset the scan_start to next location.
651+
*/
652+
scan_start = buffer + i + 1;
653+
}
654+
}
655+
} while (++i < sz);
656+
657+
*incomplete = blocks != 0 ? true : false;
658+
VIR_DEBUG("Total events received: %d, incomplete: %d",
659+
events, *incomplete);
660+
661+
return events;
662+
}
663+
664+
/*
665+
* Caller should have reference on Monitor and Domain
666+
*/
667+
static int virCHMonitorProcessEvent(virCHMonitorPtr mon,
668+
virJSONValuePtr eventJSON)
669+
{
670+
const char *event;
671+
const char *source;
672+
virDomainObjPtr vm = mon->vm;
673+
virCHMonitorEvent ev;
674+
675+
if (virJSONValueObjectHasKey(eventJSON, "source") == 0) {
676+
VIR_WARN("Invalid JSON from monitor, no source key");
677+
return -1;
678+
}
679+
if (virJSONValueObjectHasKey(eventJSON, "event") == 0) {
680+
VIR_WARN("Invalid JSON from monitor, no event key");
681+
return -1;
682+
}
683+
source = virJSONValueObjectGetString(eventJSON, "source");
684+
event = virJSONValueObjectGetString(eventJSON, "event");
685+
686+
ev = virCHMonitorEventFromString(source, event);
687+
VIR_DEBUG("Source: %s Event: %s, ev: %d", source, event, ev);
688+
switch (ev) {
689+
case virCHMonitorVmEventBooted:
690+
case virCHMonitorVmEventResumed:
691+
case virCHMonitorVmEventRestored:
692+
case virCHMonitorVirtioDeviceEventActivated:
693+
case virCHMonitorVirtioDeviceEventReset:
694+
virObjectLock(vm);
695+
if (virDomainObjIsActive(vm) &&
696+
virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) == 0) {
697+
virCHProcessSetupThreads(vm);
698+
virCHDomainObjEndJob(vm);
699+
}
700+
virObjectUnlock(vm);
701+
break;
702+
case virCHMonitorVmmEventShutdown: // shutdown inside vmm
703+
case virCHMonitorVmEventShutdown:
704+
{
705+
virCHDriverPtr driver = CH_DOMAIN_PRIVATE(vm)->driver;
706+
virDomainState state;
707+
708+
virObjectLock(vm);
709+
state = virDomainObjGetState(vm, NULL);
710+
if ((ev == virCHMonitorVmmEventShutdown ||
711+
state == VIR_DOMAIN_SHUTDOWN) &&
712+
(virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) == 0)) {
713+
714+
virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
715+
716+
virCHDomainObjEndJob(vm);
717+
}
718+
virObjectUnlock(vm);
719+
break;
720+
}
721+
case virCHMonitorVmEventBooting:
722+
case virCHMonitorVmEventPausing:
723+
case virCHMonitorVmEventPaused:
724+
case virCHMonitorVmEventResuming:
725+
case virCHMonitorVmEventSnapshotting:
726+
case virCHMonitorVmEventSnapshotted:
727+
case virCHMonitorVmEventRestoring:
728+
case virCHMonitorVmEventResizing:
729+
case virCHMonitorVmEventResized:
730+
case virCHMonitorVmEventDeleted:
731+
case virCHMonitorVmmEventStarting:
732+
case virCHMonitorCpuCreateVcpu:
733+
break;
734+
case virCHMonitorEventMax:
735+
default:
736+
VIR_WARN("unkown event from monitor!");
737+
break;
738+
}
739+
740+
return 0;
741+
}
742+
743+
/*
744+
* Helper function to find a block of valid JSON
745+
* from a stream of multiple JSON blocks.
746+
*/
747+
static inline char *end_of_json(char *str, size_t len)
748+
{
749+
bool started = false;
750+
int blocks = 0;
751+
int i = 0;
752+
753+
while ((i < len) && (!started || blocks > 0)) {
754+
if (str[i] == '{') {
755+
if (!started)
756+
started = true;
757+
blocks++;
758+
} else if (str[i] == '}') {
759+
blocks--;
760+
}
761+
i++;
762+
}
763+
764+
return (i == len && blocks) ? NULL : str + i;
765+
}
766+
767+
/*
768+
* Caller should have reference on Monitor and Domain
769+
*/
770+
static int virCHMonitorProcessEvents(virCHMonitorPtr mon, int events)
771+
{
772+
virJSONValuePtr obj = NULL;
773+
char *buf = mon->buffer;
774+
int ret = 0;
775+
int i = 0;
776+
size_t sz;
777+
778+
for (i = 0; i < events; i++) {
779+
char tmp;
780+
char *end = end_of_json(buf, mon->buf_fill_sz);
781+
782+
/*
783+
* end should never be NULL! We validated that there
784+
* is a valid JSON document before calling end_of_json,
785+
* and end_of_json returns NULL only if it cannot find
786+
* a valid JSON document.
787+
*/
788+
assert(end);
789+
790+
#pragma GCC diagnostic push
791+
#pragma GCC diagnostic ignored "-Wnull-dereference"
792+
tmp = *end, *end = 0;
793+
#pragma GCC diagnostic pop
794+
795+
if ((obj = virJSONValueFromString(buf))) {
796+
if (virCHMonitorProcessEvent(mon, obj) < 0) {
797+
VIR_WARN("Failed to process the event!");
798+
ret = -1;
799+
}
800+
virJSONValueFree(obj);
801+
} else {
802+
VIR_WARN("Invalid JSON from monitor");
803+
ret = -1;
804+
}
805+
806+
*end = tmp;
807+
buf = end;
808+
}
809+
810+
/*
811+
* If the buffer still has incomplete data, lets
812+
* push it to the beginning.
813+
*/
814+
sz = buf - mon->buffer;
815+
if (sz < mon->buf_fill_sz) {
816+
mon->buf_offset = mon->buf_fill_sz - sz;
817+
memmove(mon->buffer, buf, sz);
818+
} else {
819+
mon->buf_offset = 0;
820+
}
821+
822+
return ret;
823+
}
824+
825+
static int virCHMonitorReadProcessEvents(virCHMonitorPtr mon)
826+
{
827+
size_t max_sz = CH_MONITOR_BUFFER_SZ - mon->buf_offset;
828+
char *buf = mon->buffer + mon->buf_offset;
829+
virDomainObjPtr vm = mon->vm;
830+
bool incomplete = false;
831+
int events = 0;
832+
size_t sz = 0;
833+
834+
memset(buf, 0, max_sz);
835+
do {
836+
ssize_t ret;
837+
838+
ret = read(mon->monitor_fd, buf + sz, max_sz - sz);
839+
if (ret == 0 || (ret < 0 && errno == EINTR)) {
840+
g_usleep(G_USEC_PER_SEC);
841+
continue;
842+
} else if (ret < 0) {
843+
/*
844+
* We should never reach here. read(2) says possible errors
845+
* are EINTR, EAGAIN, EBADF, EFAULT, EINVAL, EIO, EISDIR
846+
* We handle EINTR gracefully. There is some serious issue
847+
* if we encounter any of the other errors(either in our code
848+
* or in the system). Better to bail out.
849+
*/
850+
VIR_ERROR("Failed to read monitor events!: %s", strerror(errno));
851+
abort();
852+
}
853+
854+
sz += ret;
855+
events = virCHMonitorValidateEventsJSON(mon->buffer,
856+
mon->buf_offset + sz, &incomplete);
857+
VIR_DEBUG("Monitor event(%ld):\n%s", sz, mon->buffer);
858+
859+
} while (virDomainObjIsActive(vm) && (sz < max_sz) &&
860+
(events == 0 || incomplete));
861+
862+
/*
863+
* We process the events from the read buffer if
864+
* - There is atleast one event in the buffer
865+
* - No incomplete events in the buffer or
866+
* - The buffer is full and may have incomplete entries.
867+
*
868+
* If the buffer is full, virCHMonitorProcessEvents processes
869+
* the completed events in the buffer and moves incomplete
870+
* entries to the start of the buffer and next read from the pipe
871+
* starts from the offset.
872+
*/
873+
mon->buf_fill_sz = sz + mon->buf_offset;
874+
return (events > 0) ? virCHMonitorProcessEvents(mon, events) : events;
875+
}
876+
566877
static void virCHMonitorEventLoop(void *data)
567878
{
568879
virCHMonitorPtr mon = (virCHMonitorPtr)data;
569880
virDomainObjPtr vm = mon->vm;
570881

571882
VIR_DEBUG("Monitor event loop thread starting");
572883

884+
mon->buffer = g_malloc_n(sizeof(char), CH_MONITOR_BUFFER_SZ);
885+
mon->buf_offset = 0;
886+
mon->buf_fill_sz = 0;
887+
573888
virObjectRef(vm);
574889
while (g_atomic_int_get(&mon->event_loop_stop) == 0) {
575-
576-
g_usleep(G_USEC_PER_SEC);
577-
578-
virObjectLock(vm);
579-
if (virDomainObjIsActive(vm) &&
580-
virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) == 0) {
581-
virCHProcessSetupThreads(vm);
582-
virCHDomainObjEndJob(vm);
583-
}
584-
virObjectUnlock(vm);
890+
VIR_DEBUG("Reading events from monitor..");
891+
/*
892+
* virCHMonitorReadProcessEvents errors out only if
893+
* virjson detects an invalid JSON doc and the buffer
894+
* in that case is automatically taken care of. We can
895+
* safely continue.
896+
*/
897+
if (virCHMonitorReadProcessEvents(mon) < 0)
898+
VIR_WARN("Failed to process events from monitor!");
585899
}
586900
virObjectUnref(vm);
587901

@@ -793,7 +1107,8 @@ void virCHMonitorClose(virCHMonitorPtr mon)
7931107
curl_easy_cleanup(mon->handle);
7941108

7951109
if (mon->socketpath) {
796-
if (virFileRemove(mon->socketpath, -1, -1) < 0) {
1110+
if (virFileExists(mon->socketpath) &&
1111+
virFileRemove(mon->socketpath, -1, -1) < 0) {
7971112
VIR_WARN("Unable to remove CH socket file '%s'",
7981113
mon->socketpath);
7991114
}
@@ -1209,7 +1524,7 @@ virCHMonitorRefreshThreadInfo(virCHMonitorPtr mon)
12091524
continue;
12101525
}
12111526

1212-
VIR_INFO("VM PID: %d, TID %d, COMM: %s",
1527+
VIR_DEBUG("VM PID: %d, TID %d, COMM: %s",
12131528
(int)vm->pid, (int)tids[i], data);
12141529
if (STRPREFIX(data, "vcpu")) {
12151530
int index;
@@ -1220,7 +1535,7 @@ virCHMonitorRefreshThreadInfo(virCHMonitorPtr mon)
12201535
info[i].type = virCHThreadTypeVcpu;
12211536
info[i].vcpuInfo.online = true;
12221537
info[i].vcpuInfo.cpuid = index;
1223-
VIR_INFO("vcpu%d -> tid: %d", index, tids[i]);
1538+
VIR_DEBUG("vcpu%d -> tid: %d", index, tids[i]);
12241539
} else if (STRPREFIX(data, "virtio")) {
12251540
info[i].type = virCHThreadTypeIO;
12261541
strncpy(info[i].ioInfo.thrName, data, VIRCH_THREAD_NAME_LEN - 1);

0 commit comments

Comments
 (0)