ia64/xen-unstable

changeset 15390:eeeb77195ac2

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>
author kfraser@localhost.localdomain
date Tue Jun 19 16:32:28 2007 +0100 (2007-06-19)
parents 865c4ae59be3
children fb5077ecf9a4
files tools/blktap/drivers/Makefile tools/blktap/drivers/block-aio.c tools/blktap/drivers/block-qcow.c tools/blktap/drivers/tapaio.c tools/blktap/drivers/tapaio.h
line diff
     1.1 --- a/tools/blktap/drivers/Makefile	Tue Jun 19 16:29:22 2007 +0100
     1.2 +++ b/tools/blktap/drivers/Makefile	Tue Jun 19 16:32:28 2007 +0100
     1.3 @@ -35,6 +35,7 @@ BLK-OBJS  += block-vmdk.o
     1.4  BLK-OBJS  += block-ram.o
     1.5  BLK-OBJS  += block-qcow.o
     1.6  BLK-OBJS  += aes.o
     1.7 +BLK-OBJS  += tapaio.o
     1.8  
     1.9  all: $(IBIN) qcow-util
    1.10  
     2.1 --- a/tools/blktap/drivers/block-aio.c	Tue Jun 19 16:29:22 2007 +0100
     2.2 +++ b/tools/blktap/drivers/block-aio.c	Tue Jun 19 16:32:28 2007 +0100
     2.3 @@ -43,14 +43,7 @@
     2.4  #include <sys/ioctl.h>
     2.5  #include <linux/fs.h>
     2.6  #include "tapdisk.h"
     2.7 -
     2.8 -
     2.9 -/**
    2.10 - * We used a kernel patch to return an fd associated with the AIO context
    2.11 - * so that we can concurrently poll on synchronous and async descriptors.
    2.12 - * This is signalled by passing 1 as the io context to io_setup.
    2.13 - */
    2.14 -#define REQUEST_ASYNC_FD 1
    2.15 +#include "tapaio.h"
    2.16  
    2.17  #define MAX_AIO_REQS (MAX_REQUESTS * MAX_SEGMENTS_PER_REQ)
    2.18  
    2.19 @@ -65,14 +58,13 @@ struct tdaio_state {
    2.20  	int fd;
    2.21  	
    2.22  	/* libaio state */
    2.23 -	io_context_t       aio_ctx;
    2.24 +	tap_aio_context_t  aio_ctx;
    2.25  	struct iocb        iocb_list  [MAX_AIO_REQS];
    2.26  	struct iocb       *iocb_free  [MAX_AIO_REQS];
    2.27  	struct pending_aio pending_aio[MAX_AIO_REQS];
    2.28  	int                iocb_free_count;
    2.29  	struct iocb       *iocb_queue[MAX_AIO_REQS];
    2.30  	int                iocb_queued;
    2.31 -	int                poll_fd; /* NB: we require aio_poll support */
    2.32  	struct io_event    aio_events[MAX_AIO_REQS];
    2.33  };
    2.34  
    2.35 @@ -148,7 +140,7 @@ static inline void init_fds(struct disk_
    2.36  	for(i = 0; i < MAX_IOFD; i++) 
    2.37  		dd->io_fd[i] = 0;
    2.38  
    2.39 -	dd->io_fd[0] = prv->poll_fd;
    2.40 +	dd->io_fd[0] = prv->aio_ctx.pollfd;
    2.41  }
    2.42  
    2.43  /* Open the disk file and initialize aio state. */
    2.44 @@ -162,12 +154,9 @@ int tdaio_open (struct disk_driver *dd, 
    2.45  	/* Initialize AIO */
    2.46  	prv->iocb_free_count = MAX_AIO_REQS;
    2.47  	prv->iocb_queued     = 0;
    2.48 -	
    2.49 -	prv->aio_ctx = (io_context_t) REQUEST_ASYNC_FD;
    2.50 -	prv->poll_fd = io_setup(MAX_AIO_REQS, &prv->aio_ctx);
    2.51  
    2.52 -	if (prv->poll_fd < 0) {
    2.53 -		ret = prv->poll_fd;
    2.54 +	ret = tap_aio_setup(&prv->aio_ctx, prv->aio_events, MAX_AIO_REQS);
    2.55 +	if (ret < 0) {
    2.56                  if (ret == -EAGAIN) {
    2.57                          DPRINTF("Couldn't setup AIO context.  If you are "
    2.58                                  "trying to concurrently use a large number "
    2.59 @@ -176,9 +165,7 @@ int tdaio_open (struct disk_driver *dd, 
    2.60                                  "(e.g. 'echo echo 1048576 > /proc/sys/fs/"
    2.61                                  "aio-max-nr')\n");
    2.62                  } else {
    2.63 -                        DPRINTF("Couldn't get fd for AIO poll support.  This "
    2.64 -                                "is probably because your kernel does not "
    2.65 -                                "have the aio-poll patch applied.\n");
    2.66 +                        DPRINTF("Couldn't setup AIO context.\n");
    2.67                  }
    2.68  		goto done;
    2.69  	}
    2.70 @@ -286,7 +273,7 @@ int tdaio_submit(struct disk_driver *dd)
    2.71  	if (!prv->iocb_queued)
    2.72  		return 0;
    2.73  
    2.74 -	ret = io_submit(prv->aio_ctx, prv->iocb_queued, prv->iocb_queue);
    2.75 +	ret = io_submit(prv->aio_ctx.aio_ctx, prv->iocb_queued, prv->iocb_queue);
    2.76  	
    2.77  	/* XXX: TODO: Handle error conditions here. */
    2.78  	
    2.79 @@ -300,7 +287,7 @@ int tdaio_close(struct disk_driver *dd)
    2.80  {
    2.81  	struct tdaio_state *prv = (struct tdaio_state *)dd->private;
    2.82  	
    2.83 -	io_destroy(prv->aio_ctx);
    2.84 +	io_destroy(prv->aio_ctx.aio_ctx);
    2.85  	close(prv->fd);
    2.86  
    2.87  	return 0;
    2.88 @@ -308,15 +295,13 @@ int tdaio_close(struct disk_driver *dd)
    2.89  
    2.90  int tdaio_do_callbacks(struct disk_driver *dd, int sid)
    2.91  {
    2.92 -	int ret, i, rsp = 0;
    2.93 +	int i, nr_events, rsp = 0;
    2.94  	struct io_event *ep;
    2.95  	struct tdaio_state *prv = (struct tdaio_state *)dd->private;
    2.96  
    2.97 -	/* Non-blocking test for completed io. */
    2.98 -	ret = io_getevents(prv->aio_ctx, 0, MAX_AIO_REQS, prv->aio_events,
    2.99 -			   NULL);
   2.100 -			
   2.101 -	for (ep=prv->aio_events,i=ret; i-->0; ep++) {
   2.102 +	nr_events = tap_aio_get_events(&prv->aio_ctx);
   2.103 +repeat:
   2.104 +	for (ep = prv->aio_events, i = nr_events; i-- > 0; ep++) {
   2.105  		struct iocb        *io  = ep->obj;
   2.106  		struct pending_aio *pio;
   2.107  		
   2.108 @@ -327,6 +312,14 @@ int tdaio_do_callbacks(struct disk_drive
   2.109  
   2.110  		prv->iocb_free[prv->iocb_free_count++] = io;
   2.111  	}
   2.112 +
   2.113 +	if (nr_events) {
   2.114 +		nr_events = tap_aio_more_events(&prv->aio_ctx);
   2.115 +		goto repeat;
   2.116 +	}
   2.117 +
   2.118 +	tap_aio_continue(&prv->aio_ctx);
   2.119 +
   2.120  	return rsp;
   2.121  }
   2.122  
     3.1 --- a/tools/blktap/drivers/block-qcow.c	Tue Jun 19 16:29:22 2007 +0100
     3.2 +++ b/tools/blktap/drivers/block-qcow.c	Tue Jun 19 16:32:28 2007 +0100
     3.3 @@ -38,6 +38,7 @@
     3.4  #include "bswap.h"
     3.5  #include "aes.h"
     3.6  #include "tapdisk.h"
     3.7 +#include "tapaio.h"
     3.8  
     3.9  #if 1
    3.10  #define ASSERT(_p) \
    3.11 @@ -53,9 +54,6 @@
    3.12          (l + (s - 1)) - ((l + (s - 1)) % s)); \
    3.13  })
    3.14  
    3.15 -/******AIO DEFINES******/
    3.16 -#define REQUEST_ASYNC_FD 1
    3.17 -
    3.18  struct pending_aio {
    3.19          td_callback_t cb;
    3.20          int id;
    3.21 @@ -145,7 +143,7 @@ struct tdqcow_state {
    3.22  	AES_KEY aes_encrypt_key;       /*AES key*/
    3.23  	AES_KEY aes_decrypt_key;       /*AES key*/
    3.24          /* libaio state */
    3.25 -        io_context_t        aio_ctx;
    3.26 +        tap_aio_context_t   aio_ctx;
    3.27          int                 max_aio_reqs;
    3.28          struct iocb        *iocb_list;
    3.29          struct iocb       **iocb_free;
    3.30 @@ -153,7 +151,6 @@ struct tdqcow_state {
    3.31          int                 iocb_free_count;
    3.32          struct iocb       **iocb_queue;
    3.33          int                 iocb_queued;
    3.34 -        int                 poll_fd;      /* NB: we require aio_poll support */
    3.35          struct io_event    *aio_events;
    3.36  };
    3.37  
    3.38 @@ -179,7 +176,7 @@ static void free_aio_state(struct disk_d
    3.39  
    3.40  static int init_aio_state(struct disk_driver *dd)
    3.41  {
    3.42 -        int i;
    3.43 +	int i, ret;
    3.44  	struct td_state     *bs = dd->td_state;
    3.45  	struct tdqcow_state  *s = (struct tdqcow_state *)dd->private;
    3.46          long     ioidx;
    3.47 @@ -216,12 +213,9 @@ static int init_aio_state(struct disk_dr
    3.48                  goto fail;
    3.49          }
    3.50  
    3.51 -        /*Signal kernel to create Poll FD for Asyc completion events*/
    3.52 -        s->aio_ctx = (io_context_t) REQUEST_ASYNC_FD;   
    3.53 -        s->poll_fd = io_setup(s->max_aio_reqs, &s->aio_ctx);
    3.54 -
    3.55 -	if (s->poll_fd < 0) {
    3.56 -                if (s->poll_fd == -EAGAIN) {
    3.57 +	ret = tap_aio_setup(&s->aio_ctx, s->aio_events, s->max_aio_reqs);
    3.58 +	if (ret < 0) {
    3.59 +                if (ret == -EAGAIN) {
    3.60                          DPRINTF("Couldn't setup AIO context.  If you are "
    3.61                                  "trying to concurrently use a large number "
    3.62                                  "of blktap-based disks, you may need to "
    3.63 @@ -229,9 +223,7 @@ static int init_aio_state(struct disk_dr
    3.64                                  "(e.g. 'echo echo 1048576 > /proc/sys/fs/"
    3.65                                  "aio-max-nr')\n");
    3.66                  } else {
    3.67 -                        DPRINTF("Couldn't get fd for AIO poll support.  This "
    3.68 -                                "is probably because your kernel does not "
    3.69 -                                "have the aio-poll patch applied.\n");
    3.70 +                        DPRINTF("Couldn't setup AIO context.\n");
    3.71                  }
    3.72  		goto fail;
    3.73  	}
    3.74 @@ -845,7 +837,7 @@ static inline void init_fds(struct disk_
    3.75  	for(i = 0; i < MAX_IOFD; i++) 
    3.76  		dd->io_fd[i] = 0;
    3.77  
    3.78 -	dd->io_fd[0] = s->poll_fd;
    3.79 +	dd->io_fd[0] = s->aio_ctx.pollfd;
    3.80  }
    3.81  
    3.82  /* Open the disk file and initialize qcow state. */
    3.83 @@ -1144,7 +1136,7 @@ int tdqcow_submit(struct disk_driver *dd
    3.84  	if (!prv->iocb_queued)
    3.85  		return 0;
    3.86  
    3.87 -	ret = io_submit(prv->aio_ctx, prv->iocb_queued, prv->iocb_queue);
    3.88 +	ret = io_submit(prv->aio_ctx.aio_ctx, prv->iocb_queued, prv->iocb_queue);
    3.89  
    3.90          /* XXX: TODO: Handle error conditions here. */
    3.91  
    3.92 @@ -1172,7 +1164,7 @@ int tdqcow_close(struct disk_driver *dd)
    3.93  		close(fd);
    3.94  	}
    3.95  
    3.96 -	io_destroy(s->aio_ctx);
    3.97 +	io_destroy(s->aio_ctx.aio_ctx);
    3.98  	free(s->name);
    3.99  	free(s->l1_table);
   3.100  	free(s->l2_cache);
   3.101 @@ -1184,17 +1176,15 @@ int tdqcow_close(struct disk_driver *dd)
   3.102  
   3.103  int tdqcow_do_callbacks(struct disk_driver *dd, int sid)
   3.104  {
   3.105 -        int ret, i, rsp = 0,*ptr;
   3.106 +        int ret, i, nr_events, rsp = 0,*ptr;
   3.107          struct io_event *ep;
   3.108          struct tdqcow_state *prv = (struct tdqcow_state *)dd->private;
   3.109  
   3.110          if (sid > MAX_IOFD) return 1;
   3.111 -	
   3.112 -	/* Non-blocking test for completed io. */
   3.113 -        ret = io_getevents(prv->aio_ctx, 0, prv->max_aio_reqs, prv->aio_events,
   3.114 -                           NULL);
   3.115  
   3.116 -        for (ep = prv->aio_events, i = ret; i-- > 0; ep++) {
   3.117 +        nr_events = tap_aio_get_events(&prv->aio_ctx);
   3.118 +repeat:
   3.119 +        for (ep = prv->aio_events, i = nr_events; i-- > 0; ep++) {
   3.120                  struct iocb        *io  = ep->obj;
   3.121                  struct pending_aio *pio;
   3.122  
   3.123 @@ -1215,6 +1205,14 @@ int tdqcow_do_callbacks(struct disk_driv
   3.124  
   3.125                  prv->iocb_free[prv->iocb_free_count++] = io;
   3.126          }
   3.127 +
   3.128 +        if (nr_events) {
   3.129 +                nr_events = tap_aio_more_events(&prv->aio_ctx);
   3.130 +                goto repeat;
   3.131 +        }
   3.132 +
   3.133 +        tap_aio_continue(&prv->aio_ctx);
   3.134 +
   3.135          return rsp;
   3.136  }
   3.137  
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/tools/blktap/drivers/tapaio.c	Tue Jun 19 16:32:28 2007 +0100
     4.3 @@ -0,0 +1,164 @@
     4.4 +/*
     4.5 + * Copyright (c) 2006 Andrew Warfield and Julian Chesterfield
     4.6 + * Copyright (c) 2007 Red Hat, Inc.
     4.7 + *
     4.8 + * This program is free software; you can redistribute it and/or
     4.9 + * modify it under the terms of the GNU General Public License version 2
    4.10 + * as published by the Free Software Foundation; or, when distributed
    4.11 + * separately from the Linux kernel or incorporated into other
    4.12 + * software packages, subject to the following license:
    4.13 + *
    4.14 + * Permission is hereby granted, free of charge, to any person obtaining a copy
    4.15 + * of this source file (the "Software"), to deal in the Software without
    4.16 + * restriction, including without limitation the rights to use, copy, modify,
    4.17 + * merge, publish, distribute, sublicense, and/or sell copies of the Software,
    4.18 + * and to permit persons to whom the Software is furnished to do so, subject to
    4.19 + * the following conditions:
    4.20 + *
    4.21 + * The above copyright notice and this permission notice shall be included in
    4.22 + * all copies or substantial portions of the Software.
    4.23 + *
    4.24 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    4.25 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    4.26 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    4.27 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    4.28 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    4.29 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    4.30 + * IN THE SOFTWARE.
    4.31 + */
    4.32 +
    4.33 +#include "tapaio.h"
    4.34 +#include "tapdisk.h"
    4.35 +#include <unistd.h>
    4.36 +
    4.37 +/**
    4.38 + * We used a kernel patch to return an fd associated with the AIO context
    4.39 + * so that we can concurrently poll on synchronous and async descriptors.
    4.40 + * This is signalled by passing 1 as the io context to io_setup.
    4.41 + */
    4.42 +#define REQUEST_ASYNC_FD 1
    4.43 +
    4.44 +/*
    4.45 + * If we don't have any way to do epoll on aio events in a normal kernel,
    4.46 + * wait for aio events in a separate thread and return completion status
    4.47 + * that via a pipe that can be waited on normally.
    4.48 + *
    4.49 + * To keep locking problems between the completion thread and the submit
    4.50 + * thread to a minimum, there's a handshake which allows only one thread
    4.51 + * to be doing work on the completion queue at a time:
    4.52 + *
    4.53 + * 1) main thread sends completion thread a command via the command pipe;
    4.54 + * 2) completion thread waits for aio events and returns the number
    4.55 + *    received on the completion pipe
    4.56 + * 3) main thread processes the received ctx->aio_events events
    4.57 + * 4) loop back to 1) to let the completion thread refill the aio_events
    4.58 + *    buffer.
    4.59 + *
    4.60 + * This workaround needs to disappear once the kernel provides a single
    4.61 + * mechanism for waiting on both aio and normal fd wakeups.
    4.62 + */
    4.63 +static void *
    4.64 +tap_aio_completion_thread(void *arg)
    4.65 +{
    4.66 +	tap_aio_context_t *ctx = (tap_aio_context_t *) arg;
    4.67 +	int command;
    4.68 +	int nr_events;
    4.69 +	int rc;
    4.70 +
    4.71 +	while (1) {
    4.72 +		rc = read(ctx->command_fd[0], &command, sizeof(command));
    4.73 +
    4.74 +		do {
    4.75 +			rc = io_getevents(ctx->aio_ctx, 1,
    4.76 +					  ctx->max_aio_events, ctx->aio_events,
    4.77 +					  NULL);
    4.78 +			if (rc) {
    4.79 +				nr_events = rc;
    4.80 +				rc = write(ctx->completion_fd[1], &nr_events,
    4.81 +					   sizeof(nr_events));
    4.82 +			}
    4.83 +		} while (!rc);
    4.84 +	}
    4.85 +}
    4.86 +
    4.87 +void
    4.88 +tap_aio_continue(tap_aio_context_t *ctx)
    4.89 +{
    4.90 +        int cmd = 0;
    4.91 +
    4.92 +        if (!ctx->poll_in_thread)
    4.93 +                return;
    4.94 +
    4.95 +        if (write(ctx->command_fd[1], &cmd, sizeof(cmd)) < 0)
    4.96 +                DPRINTF("Cannot write to command pipe\n");
    4.97 +}
    4.98 +
    4.99 +int
   4.100 +tap_aio_setup(tap_aio_context_t *ctx,
   4.101 +              struct io_event *aio_events,
   4.102 +              int max_aio_events)
   4.103 +{
   4.104 +        int ret;
   4.105 +
   4.106 +        ctx->aio_events = aio_events;
   4.107 +        ctx->max_aio_events = max_aio_events;
   4.108 +        ctx->poll_in_thread = 0;
   4.109 +
   4.110 +        ctx->aio_ctx = (io_context_t) REQUEST_ASYNC_FD;
   4.111 +        ret = io_setup(ctx->max_aio_events, &ctx->aio_ctx);
   4.112 +        if (ret < 0 && ret != -EINVAL)
   4.113 +                return ret;
   4.114 +        else if (ret > 0) {
   4.115 +                ctx->pollfd = ret;
   4.116 +                return ctx->pollfd;
   4.117 +        }
   4.118 +
   4.119 +        ctx->aio_ctx = (io_context_t) 0;
   4.120 +        ret = io_setup(ctx->max_aio_events, &ctx->aio_ctx);
   4.121 +        if (ret < 0)
   4.122 +                return ret;
   4.123 +
   4.124 +        if ((ret = pipe(ctx->command_fd)) < 0) {
   4.125 +                DPRINTF("Unable to create command pipe\n");
   4.126 +                return -1;
   4.127 +        }
   4.128 +        if ((ret = pipe(ctx->completion_fd)) < 0) {
   4.129 +                DPRINTF("Unable to create completion pipe\n");
   4.130 +                return -1;
   4.131 +        }
   4.132 +
   4.133 +        if ((ret = pthread_create(&ctx->aio_thread, NULL,
   4.134 +                                  tap_aio_completion_thread, ctx)) != 0) {
   4.135 +                DPRINTF("Unable to create completion thread\n");
   4.136 +                return -1;
   4.137 +        }
   4.138 +
   4.139 +        ctx->pollfd = ctx->completion_fd[0];
   4.140 +        ctx->poll_in_thread = 1;
   4.141 +
   4.142 +        tap_aio_continue(ctx);
   4.143 +
   4.144 +        return 0;
   4.145 +}
   4.146 +
   4.147 +int
   4.148 +tap_aio_get_events(tap_aio_context_t *ctx)
   4.149 +{
   4.150 +        int nr_events = 0;
   4.151 +
   4.152 +        if (!ctx->poll_in_thread)
   4.153 +                nr_events = io_getevents(ctx->aio_ctx, 1,
   4.154 +                                         ctx->max_aio_events, ctx->aio_events, NULL);
   4.155 +        else
   4.156 +                read(ctx->completion_fd[0], &nr_events, sizeof(nr_events));
   4.157 +
   4.158 +        return nr_events;
   4.159 +}
   4.160 +
   4.161 +int tap_aio_more_events(tap_aio_context_t *ctx)
   4.162 +{
   4.163 +        return io_getevents(ctx->aio_ctx, 0,
   4.164 +                            ctx->max_aio_events, ctx->aio_events, NULL);
   4.165 +}
   4.166 +
   4.167 +
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/tools/blktap/drivers/tapaio.h	Tue Jun 19 16:32:28 2007 +0100
     5.3 @@ -0,0 +1,58 @@
     5.4 +/*
     5.5 + * Copyright (c) 2006 Andrew Warfield and Julian Chesterfield
     5.6 + * Copyright (c) 2007 Red Hat, Inc.
     5.7 + *
     5.8 + * This program is free software; you can redistribute it and/or
     5.9 + * modify it under the terms of the GNU General Public License version 2
    5.10 + * as published by the Free Software Foundation; or, when distributed
    5.11 + * separately from the Linux kernel or incorporated into other
    5.12 + * software packages, subject to the following license:
    5.13 + *
    5.14 + * Permission is hereby granted, free of charge, to any person obtaining a copy
    5.15 + * of this source file (the "Software"), to deal in the Software without
    5.16 + * restriction, including without limitation the rights to use, copy, modify,
    5.17 + * merge, publish, distribute, sublicense, and/or sell copies of the Software,
    5.18 + * and to permit persons to whom the Software is furnished to do so, subject to
    5.19 + * the following conditions:
    5.20 + *
    5.21 + * The above copyright notice and this permission notice shall be included in
    5.22 + * all copies or substantial portions of the Software.
    5.23 + *
    5.24 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    5.25 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    5.26 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    5.27 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    5.28 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    5.29 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    5.30 + * IN THE SOFTWARE.
    5.31 + */
    5.32 +
    5.33 +#ifndef __TAPAIO_H__
    5.34 +#define __TAPAIO_H__
    5.35 +
    5.36 +#include <pthread.h>
    5.37 +#include <libaio.h>
    5.38 +
    5.39 +struct tap_aio_context {
    5.40 +        io_context_t     aio_ctx;
    5.41 +
    5.42 +        struct io_event *aio_events;
    5.43 +        int              max_aio_events;
    5.44 +
    5.45 +        pthread_t        aio_thread;
    5.46 +        int              command_fd[2];
    5.47 +        int              completion_fd[2];
    5.48 +        int              pollfd;
    5.49 +        unsigned int     poll_in_thread : 1;
    5.50 +};
    5.51 +
    5.52 +typedef struct tap_aio_context tap_aio_context_t;
    5.53 +
    5.54 +int  tap_aio_setup      (tap_aio_context_t *ctx,
    5.55 +                         struct io_event *aio_events,
    5.56 +                         int max_aio_events);
    5.57 +void tap_aio_continue   (tap_aio_context_t *ctx);
    5.58 +int  tap_aio_get_events (tap_aio_context_t *ctx);
    5.59 +int  tap_aio_more_events(tap_aio_context_t *ctx);
    5.60 +
    5.61 +#endif /* __TAPAIO_H__ */