summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block.c3
-rwxr-xr-xconfigure17
-rw-r--r--posix-aio-compat.c85
3 files changed, 101 insertions, 4 deletions
diff --git a/block.c b/block.c
index 8a78f14ed5..74d19ad288 100644
--- a/block.c
+++ b/block.c
@@ -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);
diff --git a/configure b/configure
index 15b785604c..412828e9ba 100755
--- a/configure
+++ b/configure
@@ -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?
+ */
}
/*