dataplane: add event loop
Outside the safety of the global mutex we need to poll on file descriptors. I found epoll(2) is a convenient way to do that, although other options could replace this module in the future (such as an AioContext-based loop or glib's GMainLoop). One important feature of this small event loop implementation is that the loop can be terminated in a thread-safe way. This allows QEMU to stop the data plane thread cleanly. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
parent
88807f89d9
commit
71973b0461
|
@ -1,3 +1,3 @@
|
||||||
ifeq ($(CONFIG_VIRTIO), y)
|
ifeq ($(CONFIG_VIRTIO), y)
|
||||||
common-obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += hostmem.o vring.o
|
common-obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += hostmem.o vring.o event-poll.o
|
||||||
endif
|
endif
|
||||||
|
|
100
hw/dataplane/event-poll.c
Normal file
100
hw/dataplane/event-poll.c
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Event loop with file descriptor polling
|
||||||
|
*
|
||||||
|
* Copyright 2012 IBM, Corp.
|
||||||
|
* Copyright 2012 Red Hat, Inc. and/or its affiliates
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include "hw/dataplane/event-poll.h"
|
||||||
|
|
||||||
|
/* Add an event notifier and its callback for polling */
|
||||||
|
void event_poll_add(EventPoll *poll, EventHandler *handler,
|
||||||
|
EventNotifier *notifier, EventCallback *callback)
|
||||||
|
{
|
||||||
|
struct epoll_event event = {
|
||||||
|
.events = EPOLLIN,
|
||||||
|
.data.ptr = handler,
|
||||||
|
};
|
||||||
|
handler->notifier = notifier;
|
||||||
|
handler->callback = callback;
|
||||||
|
if (epoll_ctl(poll->epoll_fd, EPOLL_CTL_ADD,
|
||||||
|
event_notifier_get_fd(notifier), &event) != 0) {
|
||||||
|
fprintf(stderr, "failed to add event handler to epoll: %m\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Event callback for stopping event_poll() */
|
||||||
|
static void handle_stop(EventHandler *handler)
|
||||||
|
{
|
||||||
|
/* Do nothing */
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_poll_init(EventPoll *poll)
|
||||||
|
{
|
||||||
|
/* Create epoll file descriptor */
|
||||||
|
poll->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
||||||
|
if (poll->epoll_fd < 0) {
|
||||||
|
fprintf(stderr, "epoll_create1 failed: %m\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up stop notifier */
|
||||||
|
if (event_notifier_init(&poll->stop_notifier, 0) < 0) {
|
||||||
|
fprintf(stderr, "failed to init stop notifier\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
event_poll_add(poll, &poll->stop_handler,
|
||||||
|
&poll->stop_notifier, handle_stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_poll_cleanup(EventPoll *poll)
|
||||||
|
{
|
||||||
|
event_notifier_cleanup(&poll->stop_notifier);
|
||||||
|
close(poll->epoll_fd);
|
||||||
|
poll->epoll_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block until the next event and invoke its callback */
|
||||||
|
void event_poll(EventPoll *poll)
|
||||||
|
{
|
||||||
|
EventHandler *handler;
|
||||||
|
struct epoll_event event;
|
||||||
|
int nevents;
|
||||||
|
|
||||||
|
/* Wait for the next event. Only do one event per call to keep the
|
||||||
|
* function simple, this could be changed later. */
|
||||||
|
do {
|
||||||
|
nevents = epoll_wait(poll->epoll_fd, &event, 1, -1);
|
||||||
|
} while (nevents < 0 && errno == EINTR);
|
||||||
|
if (unlikely(nevents != 1)) {
|
||||||
|
fprintf(stderr, "epoll_wait failed: %m\n");
|
||||||
|
exit(1); /* should never happen */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find out which event handler has become active */
|
||||||
|
handler = event.data.ptr;
|
||||||
|
|
||||||
|
/* Clear the eventfd */
|
||||||
|
event_notifier_test_and_clear(handler->notifier);
|
||||||
|
|
||||||
|
/* Handle the event */
|
||||||
|
handler->callback(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop event_poll()
|
||||||
|
*
|
||||||
|
* This function can be used from another thread.
|
||||||
|
*/
|
||||||
|
void event_poll_notify(EventPoll *poll)
|
||||||
|
{
|
||||||
|
event_notifier_set(&poll->stop_notifier);
|
||||||
|
}
|
40
hw/dataplane/event-poll.h
Normal file
40
hw/dataplane/event-poll.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Event loop with file descriptor polling
|
||||||
|
*
|
||||||
|
* Copyright 2012 IBM, Corp.
|
||||||
|
* Copyright 2012 Red Hat, Inc. and/or its affiliates
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EVENT_POLL_H
|
||||||
|
#define EVENT_POLL_H
|
||||||
|
|
||||||
|
#include "qemu/event_notifier.h"
|
||||||
|
|
||||||
|
typedef struct EventHandler EventHandler;
|
||||||
|
typedef void EventCallback(EventHandler *handler);
|
||||||
|
struct EventHandler {
|
||||||
|
EventNotifier *notifier; /* eventfd */
|
||||||
|
EventCallback *callback; /* callback function */
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int epoll_fd; /* epoll(2) file descriptor */
|
||||||
|
EventNotifier stop_notifier; /* stop poll notifier */
|
||||||
|
EventHandler stop_handler; /* stop poll handler */
|
||||||
|
} EventPoll;
|
||||||
|
|
||||||
|
void event_poll_add(EventPoll *poll, EventHandler *handler,
|
||||||
|
EventNotifier *notifier, EventCallback *callback);
|
||||||
|
void event_poll_init(EventPoll *poll);
|
||||||
|
void event_poll_cleanup(EventPoll *poll);
|
||||||
|
void event_poll(EventPoll *poll);
|
||||||
|
void event_poll_notify(EventPoll *poll);
|
||||||
|
|
||||||
|
#endif /* EVENT_POLL_H */
|
Loading…
Reference in a new issue