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:
Stefan Hajnoczi 2012-11-14 15:23:00 +01:00
parent 88807f89d9
commit 71973b0461
3 changed files with 141 additions and 1 deletions

View file

@ -1,3 +1,3 @@
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

100
hw/dataplane/event-poll.c Normal file
View 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
View 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 */