aio: prepare for introducing GSource-based dispatch

This adds a GPollFD to each AioHandler.  It will then be possible to
attach these GPollFDs to a GSource, and from there to the main loop.
aio_wait examines the GPollFDs and avoids calling select() if any
is set (similar to what it does if bottom halves are available).

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
master
Paolo Bonzini 2012-09-24 14:57:22 +02:00
parent 7c0628b20e
commit cd9ba1ebcf
2 changed files with 87 additions and 13 deletions

93
aio.c
View File

@ -20,7 +20,7 @@
struct AioHandler struct AioHandler
{ {
int fd; GPollFD pfd;
IOHandler *io_read; IOHandler *io_read;
IOHandler *io_write; IOHandler *io_write;
AioFlushHandler *io_flush; AioFlushHandler *io_flush;
@ -34,7 +34,7 @@ static AioHandler *find_aio_handler(AioContext *ctx, int fd)
AioHandler *node; AioHandler *node;
QLIST_FOREACH(node, &ctx->aio_handlers, node) { QLIST_FOREACH(node, &ctx->aio_handlers, node) {
if (node->fd == fd) if (node->pfd.fd == fd)
if (!node->deleted) if (!node->deleted)
return node; return node;
} }
@ -57,9 +57,10 @@ void aio_set_fd_handler(AioContext *ctx,
if (!io_read && !io_write) { if (!io_read && !io_write) {
if (node) { if (node) {
/* If the lock is held, just mark the node as deleted */ /* If the lock is held, just mark the node as deleted */
if (ctx->walking_handlers) if (ctx->walking_handlers) {
node->deleted = 1; node->deleted = 1;
else { node->pfd.revents = 0;
} else {
/* Otherwise, delete it for real. We can't just mark it as /* Otherwise, delete it for real. We can't just mark it as
* deleted because deleted nodes are only cleaned up after * deleted because deleted nodes are only cleaned up after
* releasing the walking_handlers lock. * releasing the walking_handlers lock.
@ -72,7 +73,7 @@ void aio_set_fd_handler(AioContext *ctx,
if (node == NULL) { if (node == NULL) {
/* Alloc and insert if it's not already there */ /* Alloc and insert if it's not already there */
node = g_malloc0(sizeof(AioHandler)); node = g_malloc0(sizeof(AioHandler));
node->fd = fd; node->pfd.fd = fd;
QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node); QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
} }
/* Update handler with latest information */ /* Update handler with latest information */
@ -80,6 +81,9 @@ void aio_set_fd_handler(AioContext *ctx,
node->io_write = io_write; node->io_write = io_write;
node->io_flush = io_flush; node->io_flush = io_flush;
node->opaque = opaque; node->opaque = opaque;
node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP : 0);
node->pfd.events |= (io_write ? G_IO_OUT : 0);
} }
} }
@ -93,6 +97,32 @@ void aio_set_event_notifier(AioContext *ctx,
(AioFlushHandler *)io_flush, notifier); (AioFlushHandler *)io_flush, notifier);
} }
bool aio_pending(AioContext *ctx)
{
AioHandler *node;
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
int revents;
/*
* FIXME: right now we cannot get G_IO_HUP and G_IO_ERR because
* main-loop.c is still select based (due to the slirp legacy).
* If main-loop.c ever switches to poll, G_IO_ERR should be
* tested too. Dispatching G_IO_ERR to both handlers should be
* okay, since handlers need to be ready for spurious wakeups.
*/
revents = node->pfd.revents & node->pfd.events;
if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) {
return true;
}
if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) {
return true;
}
}
return false;
}
bool aio_poll(AioContext *ctx, bool blocking) bool aio_poll(AioContext *ctx, bool blocking)
{ {
static struct timeval tv0; static struct timeval tv0;
@ -114,6 +144,43 @@ bool aio_poll(AioContext *ctx, bool blocking)
progress = true; progress = true;
} }
/*
* Then dispatch any pending callbacks from the GSource.
*
* We have to walk very carefully in case qemu_aio_set_fd_handler is
* called while we're walking.
*/
node = QLIST_FIRST(&ctx->aio_handlers);
while (node) {
AioHandler *tmp;
int revents;
ctx->walking_handlers++;
revents = node->pfd.revents & node->pfd.events;
node->pfd.revents = 0;
/* See comment in aio_pending. */
if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) {
node->io_read(node->opaque);
progress = true;
}
if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) {
node->io_write(node->opaque);
progress = true;
}
tmp = node;
node = QLIST_NEXT(node, node);
ctx->walking_handlers--;
if (!ctx->walking_handlers && tmp->deleted) {
QLIST_REMOVE(tmp, node);
g_free(tmp);
}
}
if (progress && !blocking) { if (progress && !blocking) {
return true; return true;
} }
@ -137,12 +204,12 @@ bool aio_poll(AioContext *ctx, bool blocking)
busy = true; busy = true;
} }
if (!node->deleted && node->io_read) { if (!node->deleted && node->io_read) {
FD_SET(node->fd, &rdfds); FD_SET(node->pfd.fd, &rdfds);
max_fd = MAX(max_fd, node->fd + 1); max_fd = MAX(max_fd, node->pfd.fd + 1);
} }
if (!node->deleted && node->io_write) { if (!node->deleted && node->io_write) {
FD_SET(node->fd, &wrfds); FD_SET(node->pfd.fd, &wrfds);
max_fd = MAX(max_fd, node->fd + 1); max_fd = MAX(max_fd, node->pfd.fd + 1);
} }
} }
@ -167,16 +234,16 @@ bool aio_poll(AioContext *ctx, bool blocking)
ctx->walking_handlers++; ctx->walking_handlers++;
if (!node->deleted && if (!node->deleted &&
FD_ISSET(node->fd, &rdfds) && FD_ISSET(node->pfd.fd, &rdfds) &&
node->io_read) { node->io_read) {
progress = true;
node->io_read(node->opaque); node->io_read(node->opaque);
progress = true;
} }
if (!node->deleted && if (!node->deleted &&
FD_ISSET(node->fd, &wrfds) && FD_ISSET(node->pfd.fd, &wrfds) &&
node->io_write) { node->io_write) {
progress = true;
node->io_write(node->opaque); node->io_write(node->opaque);
progress = true;
} }
tmp = node; tmp = node;

View File

@ -133,6 +133,13 @@ void qemu_bh_delete(QEMUBH *bh);
* outstanding AIO operations have been completed or cancelled. */ * outstanding AIO operations have been completed or cancelled. */
void aio_flush(AioContext *ctx); void aio_flush(AioContext *ctx);
/* Return whether there are any pending callbacks from the GSource
* attached to the AioContext.
*
* This is used internally in the implementation of the GSource.
*/
bool aio_pending(AioContext *ctx);
/* Progress in completing AIO work to occur. This can issue new pending /* Progress in completing AIO work to occur. This can issue new pending
* aio as a result of executing I/O completion or bh callbacks. * aio as a result of executing I/O completion or bh callbacks.
* *