2 * QEMU posix-aio emulation
4 * Copyright IBM, Corp. 2008
7 * Anthony Liguori <aliguori@us.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
14 #include <sys/ioctl.h>
24 #include "posix-aio-compat.h"
26 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
27 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
28 static pthread_t thread_id;
29 static pthread_attr_t attr;
30 static int max_threads = 64;
31 static int cur_threads = 0;
32 static int idle_threads = 0;
33 static TAILQ_HEAD(, qemu_paiocb) request_list;
35 static void die2(int err, const char *what)
37 fprintf(stderr, "%s failed: %s\n", what, strerror(err));
41 static void die(const char *what)
46 static void mutex_lock(pthread_mutex_t *mutex)
48 int ret = pthread_mutex_lock(mutex);
49 if (ret) die2(ret, "pthread_mutex_lock");
52 static void mutex_unlock(pthread_mutex_t *mutex)
54 int ret = pthread_mutex_unlock(mutex);
55 if (ret) die2(ret, "pthread_mutex_unlock");
58 static int cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
61 int ret = pthread_cond_timedwait(cond, mutex, ts);
62 if (ret && ret != ETIMEDOUT) die2(ret, "pthread_cond_timedwait");
66 static void cond_signal(pthread_cond_t *cond)
68 int ret = pthread_cond_signal(cond);
69 if (ret) die2(ret, "pthread_cond_signal");
72 static void thread_create(pthread_t *thread, pthread_attr_t *attr,
73 void *(*start_routine)(void*), void *arg)
75 int ret = pthread_create(thread, attr, start_routine, arg);
76 if (ret) die2(ret, "pthread_create");
79 static size_t handle_aiocb_readwrite(struct qemu_paiocb *aiocb)
84 while (offset < aiocb->aio_nbytes) {
85 if (aiocb->aio_type == QEMU_PAIO_WRITE)
86 len = pwrite(aiocb->aio_fildes,
87 (const char *)aiocb->aio_buf + offset,
88 aiocb->aio_nbytes - offset,
89 aiocb->aio_offset + offset);
91 len = pread(aiocb->aio_fildes,
92 (char *)aiocb->aio_buf + offset,
93 aiocb->aio_nbytes - offset,
94 aiocb->aio_offset + offset);
96 if (len == -1 && errno == EINTR)
110 static size_t handle_aiocb_ioctl(struct qemu_paiocb *aiocb)
114 ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_buf);
120 static void *aio_thread(void *unused)
127 /* block all signals */
128 if (sigfillset(&set)) die("sigfillset");
129 if (sigprocmask(SIG_BLOCK, &set, NULL)) die("sigprocmask");
132 struct qemu_paiocb *aiocb;
137 qemu_gettimeofday(&tv);
138 ts.tv_sec = tv.tv_sec + 10;
143 while (TAILQ_EMPTY(&request_list) &&
144 !(ret == ETIMEDOUT)) {
145 ret = cond_timedwait(&cond, &lock, &ts);
148 if (TAILQ_EMPTY(&request_list))
151 aiocb = TAILQ_FIRST(&request_list);
152 TAILQ_REMOVE(&request_list, aiocb, node);
157 switch (aiocb->aio_type) {
159 case QEMU_PAIO_WRITE:
160 ret = handle_aiocb_readwrite(aiocb);
162 case QEMU_PAIO_IOCTL:
163 ret = handle_aiocb_ioctl(aiocb);
166 fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
176 if (kill(pid, aiocb->ev_signo)) die("kill failed");
186 static void spawn_thread(void)
190 thread_create(&thread_id, &attr, aio_thread, NULL);
193 int qemu_paio_init(struct qemu_paioinit *aioinit)
197 ret = pthread_attr_init(&attr);
198 if (ret) die2(ret, "pthread_attr_init");
200 ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
201 if (ret) die2(ret, "pthread_attr_setdetachstate");
203 TAILQ_INIT(&request_list);
208 static int qemu_paio_submit(struct qemu_paiocb *aiocb, int type)
210 aiocb->aio_type = type;
211 aiocb->ret = -EINPROGRESS;
214 if (idle_threads == 0 && cur_threads < max_threads)
216 TAILQ_INSERT_TAIL(&request_list, aiocb, node);
223 int qemu_paio_read(struct qemu_paiocb *aiocb)
225 return qemu_paio_submit(aiocb, QEMU_PAIO_READ);
228 int qemu_paio_write(struct qemu_paiocb *aiocb)
230 return qemu_paio_submit(aiocb, QEMU_PAIO_WRITE);
233 int qemu_paio_ioctl(struct qemu_paiocb *aiocb)
235 return qemu_paio_submit(aiocb, QEMU_PAIO_IOCTL);
238 ssize_t qemu_paio_return(struct qemu_paiocb *aiocb)
249 int qemu_paio_error(struct qemu_paiocb *aiocb)
251 ssize_t ret = qemu_paio_return(aiocb);
261 int qemu_paio_cancel(int fd, struct qemu_paiocb *aiocb)
266 if (!aiocb->active) {
267 TAILQ_REMOVE(&request_list, aiocb, node);
268 aiocb->ret = -ECANCELED;
269 ret = QEMU_PAIO_CANCELED;
270 } else if (aiocb->ret == -EINPROGRESS)
271 ret = QEMU_PAIO_NOTCANCELED;
273 ret = QEMU_PAIO_ALLDONE;