diff options
-rw-r--r-- | block.c | 3 | ||||
-rwxr-xr-x | configure | 17 | ||||
-rw-r--r-- | posix-aio-compat.c | 85 |
3 files changed, 101 insertions, 4 deletions
@@ -1354,10 +1354,9 @@ static void bdrv_aio_bh_cb(void *opaque) { BlockDriverAIOCBSync *acb = opaque; - qemu_vfree(acb->bounce); - if (!acb->is_write) qemu_iovec_from_buffer(acb->qiov, acb->bounce, acb->qiov->size); + qemu_vfree(acb->bounce); acb->common.cb(acb->common.opaque, acb->ret); qemu_aio_release(acb); @@ -1108,6 +1108,19 @@ if $cc $ARCH_CFLAGS -o $TMPE $TMPC > /dev/null 2> /dev/null ; then fi ########################################## +# preadv probe +cat > $TMPC <<EOF +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> +int main(void) { preadv; } +EOF +preadv=no +if $cc $ARCH_CFLAGS -o $TMPE $TMPC > /dev/null 2> /dev/null ; then + preadv=yes +fi + +########################################## # fdt probe if test "$fdt" = "yes" ; then fdt=no @@ -1221,6 +1234,7 @@ echo "AIO support $aio" echo "Install blobs $blobs" echo "KVM support $kvm" echo "fdt support $fdt" +echo "preadv support $preadv" if test $sdl_too_old = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -1522,6 +1536,9 @@ fi if test "$iovec" = "yes" ; then echo "#define HAVE_IOVEC 1" >> $config_h fi +if test "$preadv" = "yes" ; then + echo "#define HAVE_PREADV 1" >> $config_h +fi if test "$fdt" = "yes" ; then echo "#define HAVE_FDT 1" >> $config_h echo "FDT_LIBS=-lfdt" >> $config_mak diff --git a/posix-aio-compat.c b/posix-aio-compat.c index 0eb77a50d3..c7fdac70b4 100644 --- a/posix-aio-compat.c +++ b/posix-aio-compat.c @@ -33,6 +33,12 @@ static int cur_threads = 0; static int idle_threads = 0; static TAILQ_HEAD(, qemu_paiocb) request_list; +#ifdef HAVE_PREADV +static int preadv_present = 1; +#else +static int preadv_present = 0; +#endif + static void die2(int err, const char *what) { fprintf(stderr, "%s failed: %s\n", what, strerror(err)); @@ -87,6 +93,36 @@ static size_t handle_aiocb_ioctl(struct qemu_paiocb *aiocb) return ret; } +#ifdef HAVE_PREADV + +static ssize_t +qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset) +{ + return preadv(fd, iov, nr_iov, offset); +} + +static ssize_t +qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset) +{ + return pwritev(fd, iov, nr_iov, offset); +} + +#else + +static ssize_t +qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset) +{ + return -ENOSYS; +} + +static ssize_t +qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset) +{ + return -ENOSYS; +} + +#endif + /* * Check if we need to copy the data in the aiocb into a new * properly aligned buffer. @@ -104,6 +140,29 @@ static int aiocb_needs_copy(struct qemu_paiocb *aiocb) return 0; } +static size_t handle_aiocb_rw_vector(struct qemu_paiocb *aiocb) +{ + size_t offset = 0; + ssize_t len; + + do { + if (aiocb->aio_type == QEMU_PAIO_WRITE) + len = qemu_pwritev(aiocb->aio_fildes, + aiocb->aio_iov, + aiocb->aio_niov, + aiocb->aio_offset + offset); + else + len = qemu_preadv(aiocb->aio_fildes, + aiocb->aio_iov, + aiocb->aio_niov, + aiocb->aio_offset + offset); + } while (len == -1 && errno == EINTR); + + if (len == -1) + return -errno; + return len; +} + static size_t handle_aiocb_rw_linear(struct qemu_paiocb *aiocb, char *buf) { size_t offset = 0; @@ -140,12 +199,34 @@ static size_t handle_aiocb_rw(struct qemu_paiocb *aiocb) size_t nbytes; char *buf; - if (!aiocb_needs_copy(aiocb) && aiocb->aio_niov == 1) { + if (!aiocb_needs_copy(aiocb)) { /* * If there is just a single buffer, and it is properly aligned * we can just use plain pread/pwrite without any problems. */ - return handle_aiocb_rw_linear(aiocb, aiocb->aio_iov->iov_base); + if (aiocb->aio_niov == 1) + return handle_aiocb_rw_linear(aiocb, aiocb->aio_iov->iov_base); + + /* + * We have more than one iovec, and all are properly aligned. + * + * Try preadv/pwritev first and fall back to linearizing the + * buffer if it's not supported. + */ + if (preadv_present) { + nbytes = handle_aiocb_rw_vector(aiocb); + if (nbytes == aiocb->aio_nbytes) + return nbytes; + if (nbytes < 0 && nbytes != -ENOSYS) + return nbytes; + preadv_present = 0; + } + + /* + * XXX(hch): short read/write. no easy way to handle the reminder + * using these interfaces. For now retry using plain + * pread/pwrite? + */ } /* |