Files
xen/tools/blktap/drivers/tapaio.c
T
kfraser@localhost.localdomain bdb957f876 blktap: Add fallback code to blktap for missing poll-on-aio support.
blktap requires a xen specific kernel AIO ABI which has been vetoed by
upstream in favour of another approach. Rather than include this ABI,
Fedora has been carrying a patch which makes tap:aio use a thread to
poll for aio events and notify the main thread via a pipe.

The upstream approach of allowing io_getevents() poll normal file
descriptors via epoll is still progressing:
 http://lkml.org/lkml/2007/1/3/16

but when that does make it upstream, blktap will require significant
re-working to use that approach.

In the meantime, here's a patch which uses the poll-in-a-thread
approach only if AIO poll support isn't available. It also hides the
details behind a simple abstraction and makes both tap:aio and
tap:qcow use it.

Signed-off-by: Mark McLoughlin <markmc@redhat.com>
2007-06-19 16:32:28 +01:00

165 lines
5.2 KiB
C

/*
* Copyright (c) 2006 Andrew Warfield and Julian Chesterfield
* Copyright (c) 2007 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation; or, when distributed
* separately from the Linux kernel or incorporated into other
* software packages, subject to the following license:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this source file (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "tapaio.h"
#include "tapdisk.h"
#include <unistd.h>
/**
* We used a kernel patch to return an fd associated with the AIO context
* so that we can concurrently poll on synchronous and async descriptors.
* This is signalled by passing 1 as the io context to io_setup.
*/
#define REQUEST_ASYNC_FD 1
/*
* If we don't have any way to do epoll on aio events in a normal kernel,
* wait for aio events in a separate thread and return completion status
* that via a pipe that can be waited on normally.
*
* To keep locking problems between the completion thread and the submit
* thread to a minimum, there's a handshake which allows only one thread
* to be doing work on the completion queue at a time:
*
* 1) main thread sends completion thread a command via the command pipe;
* 2) completion thread waits for aio events and returns the number
* received on the completion pipe
* 3) main thread processes the received ctx->aio_events events
* 4) loop back to 1) to let the completion thread refill the aio_events
* buffer.
*
* This workaround needs to disappear once the kernel provides a single
* mechanism for waiting on both aio and normal fd wakeups.
*/
static void *
tap_aio_completion_thread(void *arg)
{
tap_aio_context_t *ctx = (tap_aio_context_t *) arg;
int command;
int nr_events;
int rc;
while (1) {
rc = read(ctx->command_fd[0], &command, sizeof(command));
do {
rc = io_getevents(ctx->aio_ctx, 1,
ctx->max_aio_events, ctx->aio_events,
NULL);
if (rc) {
nr_events = rc;
rc = write(ctx->completion_fd[1], &nr_events,
sizeof(nr_events));
}
} while (!rc);
}
}
void
tap_aio_continue(tap_aio_context_t *ctx)
{
int cmd = 0;
if (!ctx->poll_in_thread)
return;
if (write(ctx->command_fd[1], &cmd, sizeof(cmd)) < 0)
DPRINTF("Cannot write to command pipe\n");
}
int
tap_aio_setup(tap_aio_context_t *ctx,
struct io_event *aio_events,
int max_aio_events)
{
int ret;
ctx->aio_events = aio_events;
ctx->max_aio_events = max_aio_events;
ctx->poll_in_thread = 0;
ctx->aio_ctx = (io_context_t) REQUEST_ASYNC_FD;
ret = io_setup(ctx->max_aio_events, &ctx->aio_ctx);
if (ret < 0 && ret != -EINVAL)
return ret;
else if (ret > 0) {
ctx->pollfd = ret;
return ctx->pollfd;
}
ctx->aio_ctx = (io_context_t) 0;
ret = io_setup(ctx->max_aio_events, &ctx->aio_ctx);
if (ret < 0)
return ret;
if ((ret = pipe(ctx->command_fd)) < 0) {
DPRINTF("Unable to create command pipe\n");
return -1;
}
if ((ret = pipe(ctx->completion_fd)) < 0) {
DPRINTF("Unable to create completion pipe\n");
return -1;
}
if ((ret = pthread_create(&ctx->aio_thread, NULL,
tap_aio_completion_thread, ctx)) != 0) {
DPRINTF("Unable to create completion thread\n");
return -1;
}
ctx->pollfd = ctx->completion_fd[0];
ctx->poll_in_thread = 1;
tap_aio_continue(ctx);
return 0;
}
int
tap_aio_get_events(tap_aio_context_t *ctx)
{
int nr_events = 0;
if (!ctx->poll_in_thread)
nr_events = io_getevents(ctx->aio_ctx, 1,
ctx->max_aio_events, ctx->aio_events, NULL);
else
read(ctx->completion_fd[0], &nr_events, sizeof(nr_events));
return nr_events;
}
int tap_aio_more_events(tap_aio_context_t *ctx)
{
return io_getevents(ctx->aio_ctx, 0,
ctx->max_aio_events, ctx->aio_events, NULL);
}