blob: 65c80ecc3e16521b5f0054621bb93971c8003e38 [file] [log] [blame]
aliguori3c529d92008-12-12 16:41:40 +00001/*
2 * QEMU posix-aio emulation
3 *
4 * Copyright IBM, Corp. 2008
5 *
6 * Authors:
7 * Anthony Liguori <aliguori@us.ibm.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
11 *
12 */
13
aliguori221f7152009-03-28 17:28:41 +000014#include <sys/ioctl.h>
aliguori3c529d92008-12-12 16:41:40 +000015#include <pthread.h>
16#include <unistd.h>
17#include <errno.h>
malc30525af2009-02-21 05:48:13 +000018#include <time.h>
malc8653c012009-02-21 05:48:11 +000019#include <string.h>
20#include <stdlib.h>
21#include <stdio.h>
aliguori3c529d92008-12-12 16:41:40 +000022#include "osdep.h"
23
24#include "posix-aio-compat.h"
25
26static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
27static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
28static pthread_t thread_id;
malca8227a52009-02-21 05:48:17 +000029static pthread_attr_t attr;
aliguori3c529d92008-12-12 16:41:40 +000030static int max_threads = 64;
31static int cur_threads = 0;
32static int idle_threads = 0;
33static TAILQ_HEAD(, qemu_paiocb) request_list;
34
malc8653c012009-02-21 05:48:11 +000035static void die2(int err, const char *what)
36{
37 fprintf(stderr, "%s failed: %s\n", what, strerror(err));
38 abort();
39}
40
41static void die(const char *what)
42{
43 die2(errno, what);
44}
45
46static void mutex_lock(pthread_mutex_t *mutex)
47{
48 int ret = pthread_mutex_lock(mutex);
49 if (ret) die2(ret, "pthread_mutex_lock");
50}
51
52static void mutex_unlock(pthread_mutex_t *mutex)
53{
54 int ret = pthread_mutex_unlock(mutex);
55 if (ret) die2(ret, "pthread_mutex_unlock");
56}
57
58static int cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
59 struct timespec *ts)
60{
61 int ret = pthread_cond_timedwait(cond, mutex, ts);
62 if (ret && ret != ETIMEDOUT) die2(ret, "pthread_cond_timedwait");
63 return ret;
64}
65
malc5d47e372009-02-21 05:48:15 +000066static void cond_signal(pthread_cond_t *cond)
malc8653c012009-02-21 05:48:11 +000067{
malc5d47e372009-02-21 05:48:15 +000068 int ret = pthread_cond_signal(cond);
69 if (ret) die2(ret, "pthread_cond_signal");
malc8653c012009-02-21 05:48:11 +000070}
71
72static void thread_create(pthread_t *thread, pthread_attr_t *attr,
73 void *(*start_routine)(void*), void *arg)
74{
75 int ret = pthread_create(thread, attr, start_routine, arg);
76 if (ret) die2(ret, "pthread_create");
77}
78
aliguori221f7152009-03-28 17:28:41 +000079static size_t handle_aiocb_readwrite(struct qemu_paiocb *aiocb)
80{
81 size_t offset = 0;
82 ssize_t len;
83
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);
90 else
91 len = pread(aiocb->aio_fildes,
92 (char *)aiocb->aio_buf + offset,
93 aiocb->aio_nbytes - offset,
94 aiocb->aio_offset + offset);
95
96 if (len == -1 && errno == EINTR)
97 continue;
98 else if (len == -1) {
99 offset = -errno;
100 break;
101 } else if (len == 0)
102 break;
103
104 offset += len;
105 }
106
107 return offset;
108}
109
110static size_t handle_aiocb_ioctl(struct qemu_paiocb *aiocb)
111{
112 int ret;
113
114 ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_buf);
115 if (ret == -1)
116 return -errno;
117 return ret;
118}
119
aliguori3c529d92008-12-12 16:41:40 +0000120static void *aio_thread(void *unused)
121{
malca8227a52009-02-21 05:48:17 +0000122 pid_t pid;
aliguori3c529d92008-12-12 16:41:40 +0000123 sigset_t set;
124
malca8227a52009-02-21 05:48:17 +0000125 pid = getpid();
126
aliguori3c529d92008-12-12 16:41:40 +0000127 /* block all signals */
malc8653c012009-02-21 05:48:11 +0000128 if (sigfillset(&set)) die("sigfillset");
129 if (sigprocmask(SIG_BLOCK, &set, NULL)) die("sigprocmask");
aliguori3c529d92008-12-12 16:41:40 +0000130
131 while (1) {
132 struct qemu_paiocb *aiocb;
aliguori221f7152009-03-28 17:28:41 +0000133 size_t ret = 0;
malc30525af2009-02-21 05:48:13 +0000134 qemu_timeval tv;
135 struct timespec ts;
136
137 qemu_gettimeofday(&tv);
138 ts.tv_sec = tv.tv_sec + 10;
139 ts.tv_nsec = 0;
aliguori3c529d92008-12-12 16:41:40 +0000140
malc8653c012009-02-21 05:48:11 +0000141 mutex_lock(&lock);
aliguori3c529d92008-12-12 16:41:40 +0000142
143 while (TAILQ_EMPTY(&request_list) &&
144 !(ret == ETIMEDOUT)) {
malc8653c012009-02-21 05:48:11 +0000145 ret = cond_timedwait(&cond, &lock, &ts);
aliguori3c529d92008-12-12 16:41:40 +0000146 }
147
malc514f7a22009-02-21 05:48:19 +0000148 if (TAILQ_EMPTY(&request_list))
aliguori3c529d92008-12-12 16:41:40 +0000149 break;
150
151 aiocb = TAILQ_FIRST(&request_list);
152 TAILQ_REMOVE(&request_list, aiocb, node);
aliguori3c529d92008-12-12 16:41:40 +0000153 aiocb->active = 1;
aliguori3c529d92008-12-12 16:41:40 +0000154 idle_threads--;
malc8653c012009-02-21 05:48:11 +0000155 mutex_unlock(&lock);
aliguori3c529d92008-12-12 16:41:40 +0000156
aliguori221f7152009-03-28 17:28:41 +0000157 switch (aiocb->aio_type) {
158 case QEMU_PAIO_READ:
159 case QEMU_PAIO_WRITE:
160 ret = handle_aiocb_readwrite(aiocb);
161 break;
162 case QEMU_PAIO_IOCTL:
163 ret = handle_aiocb_ioctl(aiocb);
164 break;
165 default:
166 fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
167 ret = -EINVAL;
168 break;
169 }
aliguori3c529d92008-12-12 16:41:40 +0000170
malc8653c012009-02-21 05:48:11 +0000171 mutex_lock(&lock);
aliguori221f7152009-03-28 17:28:41 +0000172 aiocb->ret = ret;
aliguori3c529d92008-12-12 16:41:40 +0000173 idle_threads++;
malc8653c012009-02-21 05:48:11 +0000174 mutex_unlock(&lock);
aliguori3c529d92008-12-12 16:41:40 +0000175
malca8227a52009-02-21 05:48:17 +0000176 if (kill(pid, aiocb->ev_signo)) die("kill failed");
aliguori3c529d92008-12-12 16:41:40 +0000177 }
178
179 idle_threads--;
180 cur_threads--;
malc8653c012009-02-21 05:48:11 +0000181 mutex_unlock(&lock);
aliguori3c529d92008-12-12 16:41:40 +0000182
183 return NULL;
184}
185
malc8653c012009-02-21 05:48:11 +0000186static void spawn_thread(void)
aliguori3c529d92008-12-12 16:41:40 +0000187{
aliguori3c529d92008-12-12 16:41:40 +0000188 cur_threads++;
189 idle_threads++;
malc8653c012009-02-21 05:48:11 +0000190 thread_create(&thread_id, &attr, aio_thread, NULL);
aliguori3c529d92008-12-12 16:41:40 +0000191}
192
193int qemu_paio_init(struct qemu_paioinit *aioinit)
194{
malca8227a52009-02-21 05:48:17 +0000195 int ret;
196
197 ret = pthread_attr_init(&attr);
198 if (ret) die2(ret, "pthread_attr_init");
199
200 ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
201 if (ret) die2(ret, "pthread_attr_setdetachstate");
202
aliguori3c529d92008-12-12 16:41:40 +0000203 TAILQ_INIT(&request_list);
204
205 return 0;
206}
207
aliguori221f7152009-03-28 17:28:41 +0000208static int qemu_paio_submit(struct qemu_paiocb *aiocb, int type)
aliguori3c529d92008-12-12 16:41:40 +0000209{
aliguori221f7152009-03-28 17:28:41 +0000210 aiocb->aio_type = type;
aliguori3c529d92008-12-12 16:41:40 +0000211 aiocb->ret = -EINPROGRESS;
212 aiocb->active = 0;
malc8653c012009-02-21 05:48:11 +0000213 mutex_lock(&lock);
aliguori3c529d92008-12-12 16:41:40 +0000214 if (idle_threads == 0 && cur_threads < max_threads)
215 spawn_thread();
216 TAILQ_INSERT_TAIL(&request_list, aiocb, node);
malc8653c012009-02-21 05:48:11 +0000217 mutex_unlock(&lock);
malc5d47e372009-02-21 05:48:15 +0000218 cond_signal(&cond);
aliguori3c529d92008-12-12 16:41:40 +0000219
220 return 0;
221}
222
223int qemu_paio_read(struct qemu_paiocb *aiocb)
224{
aliguori221f7152009-03-28 17:28:41 +0000225 return qemu_paio_submit(aiocb, QEMU_PAIO_READ);
aliguori3c529d92008-12-12 16:41:40 +0000226}
227
228int qemu_paio_write(struct qemu_paiocb *aiocb)
229{
aliguori221f7152009-03-28 17:28:41 +0000230 return qemu_paio_submit(aiocb, QEMU_PAIO_WRITE);
231}
232
233int qemu_paio_ioctl(struct qemu_paiocb *aiocb)
234{
235 return qemu_paio_submit(aiocb, QEMU_PAIO_IOCTL);
aliguori3c529d92008-12-12 16:41:40 +0000236}
237
238ssize_t qemu_paio_return(struct qemu_paiocb *aiocb)
239{
240 ssize_t ret;
241
malc8653c012009-02-21 05:48:11 +0000242 mutex_lock(&lock);
aliguori3c529d92008-12-12 16:41:40 +0000243 ret = aiocb->ret;
malc8653c012009-02-21 05:48:11 +0000244 mutex_unlock(&lock);
aliguori3c529d92008-12-12 16:41:40 +0000245
246 return ret;
247}
248
249int qemu_paio_error(struct qemu_paiocb *aiocb)
250{
251 ssize_t ret = qemu_paio_return(aiocb);
252
253 if (ret < 0)
254 ret = -ret;
255 else
256 ret = 0;
257
258 return ret;
259}
260
261int qemu_paio_cancel(int fd, struct qemu_paiocb *aiocb)
262{
263 int ret;
264
malc8653c012009-02-21 05:48:11 +0000265 mutex_lock(&lock);
aliguori3c529d92008-12-12 16:41:40 +0000266 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;
272 else
273 ret = QEMU_PAIO_ALLDONE;
malc8653c012009-02-21 05:48:11 +0000274 mutex_unlock(&lock);
aliguori3c529d92008-12-12 16:41:40 +0000275
276 return ret;
277}