X-Git-Url: http://git.maemo.org/git/?p=qemu;a=blobdiff_plain;f=posix-aio-compat.c;h=e9fc9fa60a1d26aabac3f9222ff5940e13a3ade0;hp=19569174429ff3936d5542507a5ac26dc637bda7;hb=HEAD;hpb=a8227a5a200f1bb52ecd0f1a96b24d2e2dbf39f5 diff --git a/posix-aio-compat.c b/posix-aio-compat.c index 1956917..e9fc9fa 100644 --- a/posix-aio-compat.c +++ b/posix-aio-compat.c @@ -11,6 +11,7 @@ * */ +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include "osdep.h" +#include "qemu-common.h" #include "posix-aio-compat.h" @@ -31,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)); @@ -75,6 +83,196 @@ static void thread_create(pthread_t *thread, pthread_attr_t *attr, if (ret) die2(ret, "pthread_create"); } +static size_t handle_aiocb_ioctl(struct qemu_paiocb *aiocb) +{ + int ret; + + ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_ioctl_buf); + if (ret == -1) + return -errno; + + /* + * This looks weird, but the aio code only consideres a request + * successfull if it has written the number full number of bytes. + * + * Now we overload aio_nbytes as aio_ioctl_cmd for the ioctl command, + * so in fact we return the ioctl command here to make posix_aio_read() + * happy.. + */ + return aiocb->aio_nbytes; +} + +#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. + */ +static int aiocb_needs_copy(struct qemu_paiocb *aiocb) +{ + if (aiocb->aio_flags & QEMU_AIO_SECTOR_ALIGNED) { + int i; + + for (i = 0; i < aiocb->aio_niov; i++) + if ((uintptr_t) aiocb->aio_iov[i].iov_base % 512) + return 1; + } + + 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; + size_t len; + + while (offset < aiocb->aio_nbytes) { + if (aiocb->aio_type == QEMU_PAIO_WRITE) + len = pwrite(aiocb->aio_fildes, + (const char *)buf + offset, + aiocb->aio_nbytes - offset, + aiocb->aio_offset + offset); + else + len = pread(aiocb->aio_fildes, + buf + offset, + aiocb->aio_nbytes - offset, + aiocb->aio_offset + offset); + + if (len == -1 && errno == EINTR) + continue; + else if (len == -1) { + offset = -errno; + break; + } else if (len == 0) + break; + + offset += len; + } + + return offset; +} + +static size_t handle_aiocb_rw(struct qemu_paiocb *aiocb) +{ + size_t nbytes; + char *buf; + + 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. + */ + 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? + */ + } + + /* + * Ok, we have to do it the hard way, copy all segments into + * a single aligned buffer. + */ + buf = qemu_memalign(512, aiocb->aio_nbytes); + if (aiocb->aio_type == QEMU_PAIO_WRITE) { + char *p = buf; + int i; + + for (i = 0; i < aiocb->aio_niov; ++i) { + memcpy(p, aiocb->aio_iov[i].iov_base, aiocb->aio_iov[i].iov_len); + p += aiocb->aio_iov[i].iov_len; + } + } + + nbytes = handle_aiocb_rw_linear(aiocb, buf); + if (aiocb->aio_type != QEMU_PAIO_WRITE) { + char *p = buf; + size_t count = aiocb->aio_nbytes, copy; + int i; + + for (i = 0; i < aiocb->aio_niov && count; ++i) { + copy = count; + if (copy > aiocb->aio_iov[i].iov_len) + copy = aiocb->aio_iov[i].iov_len; + memcpy(aiocb->aio_iov[i].iov_base, p, copy); + p += copy; + count -= copy; + } + } + qemu_vfree(buf); + + return nbytes; +} + static void *aio_thread(void *unused) { pid_t pid; @@ -88,8 +286,7 @@ static void *aio_thread(void *unused) while (1) { struct qemu_paiocb *aiocb; - size_t offset; - int ret = 0; + size_t ret = 0; qemu_timeval tv; struct timespec ts; @@ -104,45 +301,31 @@ static void *aio_thread(void *unused) ret = cond_timedwait(&cond, &lock, &ts); } - if (ret == ETIMEDOUT) + if (TAILQ_EMPTY(&request_list)) break; aiocb = TAILQ_FIRST(&request_list); TAILQ_REMOVE(&request_list, aiocb, node); - - offset = 0; aiocb->active = 1; - idle_threads--; mutex_unlock(&lock); - while (offset < aiocb->aio_nbytes) { - ssize_t len; - - if (aiocb->is_write) - len = pwrite(aiocb->aio_fildes, - (const char *)aiocb->aio_buf + offset, - aiocb->aio_nbytes - offset, - aiocb->aio_offset + offset); - else - len = pread(aiocb->aio_fildes, - (char *)aiocb->aio_buf + offset, - aiocb->aio_nbytes - offset, - aiocb->aio_offset + offset); - - if (len == -1 && errno == EINTR) - continue; - else if (len == -1) { - offset = -errno; - break; - } else if (len == 0) - break; - - offset += len; - } + switch (aiocb->aio_type) { + case QEMU_PAIO_READ: + case QEMU_PAIO_WRITE: + ret = handle_aiocb_rw(aiocb); + break; + case QEMU_PAIO_IOCTL: + ret = handle_aiocb_ioctl(aiocb); + break; + default: + fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); + ret = -EINVAL; + break; + } mutex_lock(&lock); - aiocb->ret = offset; + aiocb->ret = ret; idle_threads++; mutex_unlock(&lock); @@ -178,9 +361,9 @@ int qemu_paio_init(struct qemu_paioinit *aioinit) return 0; } -static int qemu_paio_submit(struct qemu_paiocb *aiocb, int is_write) +static int qemu_paio_submit(struct qemu_paiocb *aiocb, int type) { - aiocb->is_write = is_write; + aiocb->aio_type = type; aiocb->ret = -EINPROGRESS; aiocb->active = 0; mutex_lock(&lock); @@ -195,12 +378,17 @@ static int qemu_paio_submit(struct qemu_paiocb *aiocb, int is_write) int qemu_paio_read(struct qemu_paiocb *aiocb) { - return qemu_paio_submit(aiocb, 0); + return qemu_paio_submit(aiocb, QEMU_PAIO_READ); } int qemu_paio_write(struct qemu_paiocb *aiocb) { - return qemu_paio_submit(aiocb, 1); + return qemu_paio_submit(aiocb, QEMU_PAIO_WRITE); +} + +int qemu_paio_ioctl(struct qemu_paiocb *aiocb) +{ + return qemu_paio_submit(aiocb, QEMU_PAIO_IOCTL); } ssize_t qemu_paio_return(struct qemu_paiocb *aiocb)