aboutsummaryrefslogtreecommitdiff
path: root/posix-aio-compat.c
diff options
context:
space:
mode:
Diffstat (limited to 'posix-aio-compat.c')
-rw-r--r--posix-aio-compat.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/posix-aio-compat.c b/posix-aio-compat.c
new file mode 100644
index 0000000000..232b511f92
--- /dev/null
+++ b/posix-aio-compat.c
@@ -0,0 +1,202 @@
+/*
+ * QEMU posix-aio emulation
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include "osdep.h"
+
+#include "posix-aio-compat.h"
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+static pthread_t thread_id;
+static int max_threads = 64;
+static int cur_threads = 0;
+static int idle_threads = 0;
+static TAILQ_HEAD(, qemu_paiocb) request_list;
+
+static void *aio_thread(void *unused)
+{
+ sigset_t set;
+
+ /* block all signals */
+ sigfillset(&set);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+
+ while (1) {
+ struct qemu_paiocb *aiocb;
+ size_t offset;
+ int ret = 0;
+
+ pthread_mutex_lock(&lock);
+
+ while (TAILQ_EMPTY(&request_list) &&
+ !(ret == ETIMEDOUT)) {
+ struct timespec ts = { 0 };
+ qemu_timeval tv;
+
+ qemu_gettimeofday(&tv);
+ ts.tv_sec = tv.tv_sec + 10;
+ ret = pthread_cond_timedwait(&cond, &lock, &ts);
+ }
+
+ if (ret == ETIMEDOUT)
+ break;
+
+ aiocb = TAILQ_FIRST(&request_list);
+ TAILQ_REMOVE(&request_list, aiocb, node);
+
+ offset = 0;
+ aiocb->active = 1;
+
+ idle_threads--;
+ pthread_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) {
+ pthread_mutex_lock(&lock);
+ aiocb->ret = -errno;
+ pthread_mutex_unlock(&lock);
+ break;
+ } else if (len == 0)
+ break;
+
+ offset += len;
+
+ pthread_mutex_lock(&lock);
+ aiocb->ret = offset;
+ pthread_mutex_unlock(&lock);
+ }
+
+ pthread_mutex_lock(&lock);
+ idle_threads++;
+ pthread_mutex_unlock(&lock);
+
+ sigqueue(getpid(),
+ aiocb->aio_sigevent.sigev_signo,
+ aiocb->aio_sigevent.sigev_value);
+ }
+
+ idle_threads--;
+ cur_threads--;
+ pthread_mutex_unlock(&lock);
+
+ return NULL;
+}
+
+static int spawn_thread(void)
+{
+ pthread_attr_t attr;
+ int ret;
+
+ cur_threads++;
+ idle_threads++;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ ret = pthread_create(&thread_id, &attr, aio_thread, NULL);
+ pthread_attr_destroy(&attr);
+
+ return ret;
+}
+
+int qemu_paio_init(struct qemu_paioinit *aioinit)
+{
+ TAILQ_INIT(&request_list);
+
+ return 0;
+}
+
+static int qemu_paio_submit(struct qemu_paiocb *aiocb, int is_write)
+{
+ aiocb->is_write = is_write;
+ aiocb->ret = -EINPROGRESS;
+ aiocb->active = 0;
+ pthread_mutex_lock(&lock);
+ if (idle_threads == 0 && cur_threads < max_threads)
+ spawn_thread();
+ TAILQ_INSERT_TAIL(&request_list, aiocb, node);
+ pthread_mutex_unlock(&lock);
+ pthread_cond_broadcast(&cond);
+
+ return 0;
+}
+
+int qemu_paio_read(struct qemu_paiocb *aiocb)
+{
+ return qemu_paio_submit(aiocb, 0);
+}
+
+int qemu_paio_write(struct qemu_paiocb *aiocb)
+{
+ return qemu_paio_submit(aiocb, 1);
+}
+
+ssize_t qemu_paio_return(struct qemu_paiocb *aiocb)
+{
+ ssize_t ret;
+
+ pthread_mutex_lock(&lock);
+ ret = aiocb->ret;
+ pthread_mutex_unlock(&lock);
+
+ return ret;
+}
+
+int qemu_paio_error(struct qemu_paiocb *aiocb)
+{
+ ssize_t ret = qemu_paio_return(aiocb);
+
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+
+ return ret;
+}
+
+int qemu_paio_cancel(int fd, struct qemu_paiocb *aiocb)
+{
+ int ret;
+
+ pthread_mutex_lock(&lock);
+ if (!aiocb->active) {
+ TAILQ_REMOVE(&request_list, aiocb, node);
+ aiocb->ret = -ECANCELED;
+ ret = QEMU_PAIO_CANCELED;
+ } else if (aiocb->ret == -EINPROGRESS)
+ ret = QEMU_PAIO_NOTCANCELED;
+ else
+ ret = QEMU_PAIO_ALLDONE;
+ pthread_mutex_unlock(&lock);
+
+ return ret;
+}
+