]>
Commit | Line | Data |
---|---|---|
3c529d93 AL |
1 | /* |
2 | * QEMU posix-aio emulation | |
3 | * | |
4 | * Copyright IBM, Corp. 2008 | |
5 | * | |
6 | * Authors: | |
7 | * Anthony Liguori <[email protected]> | |
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 | ||
14 | #include <pthread.h> | |
15 | #include <unistd.h> | |
16 | #include <errno.h> | |
30525aff | 17 | #include <time.h> |
8653c015 | 18 | #include <string.h> |
19 | #include <stdlib.h> | |
20 | #include <stdio.h> | |
3c529d93 AL |
21 | #include "osdep.h" |
22 | ||
23 | #include "posix-aio-compat.h" | |
24 | ||
25 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; | |
26 | static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; | |
27 | static pthread_t thread_id; | |
a8227a5a | 28 | static pthread_attr_t attr; |
3c529d93 AL |
29 | static int max_threads = 64; |
30 | static int cur_threads = 0; | |
31 | static int idle_threads = 0; | |
32 | static TAILQ_HEAD(, qemu_paiocb) request_list; | |
33 | ||
8653c015 | 34 | static void die2(int err, const char *what) |
35 | { | |
36 | fprintf(stderr, "%s failed: %s\n", what, strerror(err)); | |
37 | abort(); | |
38 | } | |
39 | ||
40 | static void die(const char *what) | |
41 | { | |
42 | die2(errno, what); | |
43 | } | |
44 | ||
45 | static void mutex_lock(pthread_mutex_t *mutex) | |
46 | { | |
47 | int ret = pthread_mutex_lock(mutex); | |
48 | if (ret) die2(ret, "pthread_mutex_lock"); | |
49 | } | |
50 | ||
51 | static void mutex_unlock(pthread_mutex_t *mutex) | |
52 | { | |
53 | int ret = pthread_mutex_unlock(mutex); | |
54 | if (ret) die2(ret, "pthread_mutex_unlock"); | |
55 | } | |
56 | ||
57 | static int cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, | |
58 | struct timespec *ts) | |
59 | { | |
60 | int ret = pthread_cond_timedwait(cond, mutex, ts); | |
61 | if (ret && ret != ETIMEDOUT) die2(ret, "pthread_cond_timedwait"); | |
62 | return ret; | |
63 | } | |
64 | ||
5d47e372 | 65 | static void cond_signal(pthread_cond_t *cond) |
8653c015 | 66 | { |
5d47e372 | 67 | int ret = pthread_cond_signal(cond); |
68 | if (ret) die2(ret, "pthread_cond_signal"); | |
8653c015 | 69 | } |
70 | ||
71 | static void thread_create(pthread_t *thread, pthread_attr_t *attr, | |
72 | void *(*start_routine)(void*), void *arg) | |
73 | { | |
74 | int ret = pthread_create(thread, attr, start_routine, arg); | |
75 | if (ret) die2(ret, "pthread_create"); | |
76 | } | |
77 | ||
3c529d93 AL |
78 | static void *aio_thread(void *unused) |
79 | { | |
a8227a5a | 80 | pid_t pid; |
3c529d93 AL |
81 | sigset_t set; |
82 | ||
a8227a5a | 83 | pid = getpid(); |
84 | ||
3c529d93 | 85 | /* block all signals */ |
8653c015 | 86 | if (sigfillset(&set)) die("sigfillset"); |
87 | if (sigprocmask(SIG_BLOCK, &set, NULL)) die("sigprocmask"); | |
3c529d93 AL |
88 | |
89 | while (1) { | |
90 | struct qemu_paiocb *aiocb; | |
91 | size_t offset; | |
92 | int ret = 0; | |
30525aff | 93 | qemu_timeval tv; |
94 | struct timespec ts; | |
95 | ||
96 | qemu_gettimeofday(&tv); | |
97 | ts.tv_sec = tv.tv_sec + 10; | |
98 | ts.tv_nsec = 0; | |
3c529d93 | 99 | |
8653c015 | 100 | mutex_lock(&lock); |
3c529d93 AL |
101 | |
102 | while (TAILQ_EMPTY(&request_list) && | |
103 | !(ret == ETIMEDOUT)) { | |
8653c015 | 104 | ret = cond_timedwait(&cond, &lock, &ts); |
3c529d93 AL |
105 | } |
106 | ||
514f7a27 | 107 | if (TAILQ_EMPTY(&request_list)) |
3c529d93 AL |
108 | break; |
109 | ||
110 | aiocb = TAILQ_FIRST(&request_list); | |
111 | TAILQ_REMOVE(&request_list, aiocb, node); | |
112 | ||
113 | offset = 0; | |
114 | aiocb->active = 1; | |
115 | ||
116 | idle_threads--; | |
8653c015 | 117 | mutex_unlock(&lock); |
3c529d93 AL |
118 | |
119 | while (offset < aiocb->aio_nbytes) { | |
120 | ssize_t len; | |
121 | ||
122 | if (aiocb->is_write) | |
123 | len = pwrite(aiocb->aio_fildes, | |
124 | (const char *)aiocb->aio_buf + offset, | |
125 | aiocb->aio_nbytes - offset, | |
126 | aiocb->aio_offset + offset); | |
127 | else | |
128 | len = pread(aiocb->aio_fildes, | |
129 | (char *)aiocb->aio_buf + offset, | |
130 | aiocb->aio_nbytes - offset, | |
131 | aiocb->aio_offset + offset); | |
132 | ||
133 | if (len == -1 && errno == EINTR) | |
134 | continue; | |
135 | else if (len == -1) { | |
f094a782 | 136 | offset = -errno; |
3c529d93 AL |
137 | break; |
138 | } else if (len == 0) | |
139 | break; | |
140 | ||
141 | offset += len; | |
3c529d93 AL |
142 | } |
143 | ||
8653c015 | 144 | mutex_lock(&lock); |
f094a782 | 145 | aiocb->ret = offset; |
3c529d93 | 146 | idle_threads++; |
8653c015 | 147 | mutex_unlock(&lock); |
3c529d93 | 148 | |
a8227a5a | 149 | if (kill(pid, aiocb->ev_signo)) die("kill failed"); |
3c529d93 AL |
150 | } |
151 | ||
152 | idle_threads--; | |
153 | cur_threads--; | |
8653c015 | 154 | mutex_unlock(&lock); |
3c529d93 AL |
155 | |
156 | return NULL; | |
157 | } | |
158 | ||
8653c015 | 159 | static void spawn_thread(void) |
3c529d93 | 160 | { |
3c529d93 AL |
161 | cur_threads++; |
162 | idle_threads++; | |
8653c015 | 163 | thread_create(&thread_id, &attr, aio_thread, NULL); |
3c529d93 AL |
164 | } |
165 | ||
166 | int qemu_paio_init(struct qemu_paioinit *aioinit) | |
167 | { | |
a8227a5a | 168 | int ret; |
169 | ||
170 | ret = pthread_attr_init(&attr); | |
171 | if (ret) die2(ret, "pthread_attr_init"); | |
172 | ||
173 | ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | |
174 | if (ret) die2(ret, "pthread_attr_setdetachstate"); | |
175 | ||
3c529d93 AL |
176 | TAILQ_INIT(&request_list); |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | static int qemu_paio_submit(struct qemu_paiocb *aiocb, int is_write) | |
182 | { | |
183 | aiocb->is_write = is_write; | |
184 | aiocb->ret = -EINPROGRESS; | |
185 | aiocb->active = 0; | |
8653c015 | 186 | mutex_lock(&lock); |
3c529d93 AL |
187 | if (idle_threads == 0 && cur_threads < max_threads) |
188 | spawn_thread(); | |
189 | TAILQ_INSERT_TAIL(&request_list, aiocb, node); | |
8653c015 | 190 | mutex_unlock(&lock); |
5d47e372 | 191 | cond_signal(&cond); |
3c529d93 AL |
192 | |
193 | return 0; | |
194 | } | |
195 | ||
196 | int qemu_paio_read(struct qemu_paiocb *aiocb) | |
197 | { | |
198 | return qemu_paio_submit(aiocb, 0); | |
199 | } | |
200 | ||
201 | int qemu_paio_write(struct qemu_paiocb *aiocb) | |
202 | { | |
203 | return qemu_paio_submit(aiocb, 1); | |
204 | } | |
205 | ||
206 | ssize_t qemu_paio_return(struct qemu_paiocb *aiocb) | |
207 | { | |
208 | ssize_t ret; | |
209 | ||
8653c015 | 210 | mutex_lock(&lock); |
3c529d93 | 211 | ret = aiocb->ret; |
8653c015 | 212 | mutex_unlock(&lock); |
3c529d93 AL |
213 | |
214 | return ret; | |
215 | } | |
216 | ||
217 | int qemu_paio_error(struct qemu_paiocb *aiocb) | |
218 | { | |
219 | ssize_t ret = qemu_paio_return(aiocb); | |
220 | ||
221 | if (ret < 0) | |
222 | ret = -ret; | |
223 | else | |
224 | ret = 0; | |
225 | ||
226 | return ret; | |
227 | } | |
228 | ||
229 | int qemu_paio_cancel(int fd, struct qemu_paiocb *aiocb) | |
230 | { | |
231 | int ret; | |
232 | ||
8653c015 | 233 | mutex_lock(&lock); |
3c529d93 AL |
234 | if (!aiocb->active) { |
235 | TAILQ_REMOVE(&request_list, aiocb, node); | |
236 | aiocb->ret = -ECANCELED; | |
237 | ret = QEMU_PAIO_CANCELED; | |
238 | } else if (aiocb->ret == -EINPROGRESS) | |
239 | ret = QEMU_PAIO_NOTCANCELED; | |
240 | else | |
241 | ret = QEMU_PAIO_ALLDONE; | |
8653c015 | 242 | mutex_unlock(&lock); |
3c529d93 AL |
243 | |
244 | return ret; | |
245 | } |