Cosmetics
[qemu] / posix-aio-compat.c
1 /*
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
14 #include <pthread.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <time.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <stdio.h>
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;
28 static pthread_attr_t attr;
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
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
65 static void cond_signal(pthread_cond_t *cond)
66 {
67     int ret = pthread_cond_signal(cond);
68     if (ret) die2(ret, "pthread_cond_signal");
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
78 static void *aio_thread(void *unused)
79 {
80     pid_t pid;
81     sigset_t set;
82
83     pid = getpid();
84
85     /* block all signals */
86     if (sigfillset(&set)) die("sigfillset");
87     if (sigprocmask(SIG_BLOCK, &set, NULL)) die("sigprocmask");
88
89     while (1) {
90         struct qemu_paiocb *aiocb;
91         size_t offset;
92         int ret = 0;
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;
99
100         mutex_lock(&lock);
101
102         while (TAILQ_EMPTY(&request_list) &&
103                !(ret == ETIMEDOUT)) {
104             ret = cond_timedwait(&cond, &lock, &ts);
105         }
106
107         if (ret == ETIMEDOUT)
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--;
117         mutex_unlock(&lock);
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) {
136                 offset = -errno;
137                 break;
138             } else if (len == 0)
139                 break;
140
141             offset += len;
142         }
143
144         mutex_lock(&lock);
145         aiocb->ret = offset;
146         idle_threads++;
147         mutex_unlock(&lock);
148
149         if (kill(pid, aiocb->ev_signo)) die("kill failed");
150     }
151
152     idle_threads--;
153     cur_threads--;
154     mutex_unlock(&lock);
155
156     return NULL;
157 }
158
159 static void spawn_thread(void)
160 {
161     cur_threads++;
162     idle_threads++;
163     thread_create(&thread_id, &attr, aio_thread, NULL);
164 }
165
166 int qemu_paio_init(struct qemu_paioinit *aioinit)
167 {
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
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;
186     mutex_lock(&lock);
187     if (idle_threads == 0 && cur_threads < max_threads)
188         spawn_thread();
189     TAILQ_INSERT_TAIL(&request_list, aiocb, node);
190     mutex_unlock(&lock);
191     cond_signal(&cond);
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
210     mutex_lock(&lock);
211     ret = aiocb->ret;
212     mutex_unlock(&lock);
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
233     mutex_lock(&lock);
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;
242     mutex_unlock(&lock);
243
244     return ret;
245 }