Hi,
I would like to start a discussion about how vmi_events_listen(vmi, 0)
should be implementing with the APIs available in KVMi.
As we know, the API definition is the following:
https://github.com/libvmi/libvmi/blob/master/libvmi/events.h#L760
- Listen for events until one occurs or a timeout.
- If the timeout is given as 0, it will process leftover events
- in the ring-buffer (if there are any).
The current implementation I gave in my first PR on v5 events is not handling the case where timeout=0
, to process leftover events.
Simple implementation
We could consider the following implementation, with a do ... while()
loop
status_t
kvm_events_listen(
vmi_instance_t vmi,
uint32_t timeout)
{
#ifdef ENABLE_SAFETY_CHECKS
if (!vmi)
return VMI_FAILURE;
#endif
struct kvmi_dom_event *event = NULL;
unsigned int ev_reason = 0;
// if timeout is 0, we have to process all leftover events on the ring
bool process_all_events = (timeout == 0) ? true : false;
kvm_instance_t *kvm = kvm_get_instance(vmi);
#ifdef ENABLE_SAFETY_CHECKS
if (!kvm || !kvm->kvmi_dom)
return VMI_FAILURE;
#endif
do {
// wait next event
if (kvmi_wait_event(kvm->kvmi_dom, (kvmi_timeout_t)timeout)) {
if (errno == ETIMEDOUT) {
// no events !
break;
}
errprint("%s: kvmi_wait_event failed: %s\n", __func__, strerror(errno));
return VMI_FAILURE;
}
// pop event from queue
if (kvmi_pop_event(kvm->kvmi_dom, &event)) {
errprint("%s: kvmi_pop_event failed: %s\n", __func__, strerror(errno));
return VMI_FAILURE;
}
// handle event
ev_reason = event->event.common.event;
#ifdef ENABLE_SAFETY_CHECKS
if ( ev_reason >= KVMI_NUM_EVENTS || !kvm->process_event[ev_reason] ) {
errprint("Undefined handler for %u event reason\n", ev_reason);
goto error_exit;
}
#endif
// call handler
if (VMI_FAILURE == kvm->process_event[ev_reason](vmi, event))
goto error_exit;
if (event) {
free(event);
event = NULL;
}
} while (process_all_events);
return VMI_SUCCESS;
error_exit:
if (event)
free(event);
return VMI_FAILURE;
}
Problematic use case
Considering this simple use-case:
vmi_pause_vm(vmi);
...
vmi_events_listen(vmi, 0); <--- This call should clean the ring and process leftover events, while keeping the VM paused.
Pause events ?
With the API available today in KVMi, it is quite challenging to honor the behavior expected by the user.
The main problem lies in the fact that pausing the VM in KVMi means that a KVMI_EVENT_PAUSE_VCPU
event will be queued in the event ring.
Therefore our implementation will remove these events from the ring.
From the moment where we remove them, we have to give a reply to the VCPU with the event sequence number.
I'm puzzled by how we can handle this implementation correctly and I would like your advices.
ideas:
- pop the pause events anyway, reply continue and call
vmi_pause_vm
to reque the pause events at the end (if we encountered pause events) ? not doable, the vcpu will be running anyway since we sent a reply.
- pop the next event, if it's a pause events, store it for the next
vmi_resume_vm
call ? 🤔
cc @mdontu, @adlazar, @tklengyel, @smaresca
Thanks.