One step closer to group reporting
[fio.git] / engines / syslet-rw.c
1 /*
2  * read/write() engine that uses syslet to be async
3  *
4  */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <assert.h>
10
11 #include "../fio.h"
12 #include "../os.h"
13
14 #ifdef FIO_HAVE_SYSLET
15
16 struct syslet_data {
17         struct io_u **events;
18         unsigned int nr_events;
19         
20         struct async_head_user ahu;
21         struct syslet_uatom **ring;
22
23         struct syslet_uatom *head, *tail;
24 };
25
26 static void fio_syslet_complete_atom(struct thread_data *td,
27                                      struct syslet_uatom *atom)
28 {
29         struct syslet_data *sd = td->io_ops->data;
30         struct syslet_uatom *last;
31         struct io_u *io_u;
32
33         /*
34          * complete from the beginning of the sequence up to (and
35          * including) this atom
36          */
37         last = atom;
38         io_u = atom->private;
39         atom = io_u->req.head;
40
41         /*
42          * now complete in right order
43          */
44         do {
45                 long ret;
46
47                 io_u = atom->private;
48                 ret = *atom->ret_ptr;
49                 if (ret > 0)
50                         io_u->resid = io_u->xfer_buflen - ret;
51                 else if (ret < 0)
52                         io_u->error = ret;
53
54                 assert(sd->nr_events < td->iodepth);
55                 sd->events[sd->nr_events++] = io_u;
56
57                 if (atom == last)
58                         break;
59
60                 atom = atom->next;
61         } while (1);
62
63         assert(!last->next);
64 }
65
66 /*
67  * Inspect the ring to see if we have completed events
68  */
69 static void fio_syslet_complete(struct thread_data *td)
70 {
71         struct syslet_data *sd = td->io_ops->data;
72
73         do {
74                 struct syslet_uatom *atom;
75
76                 atom = sd->ring[sd->ahu.user_ring_idx];
77                 if (!atom)
78                         break;
79
80                 sd->ring[sd->ahu.user_ring_idx] = NULL;
81                 if (++sd->ahu.user_ring_idx == td->iodepth)
82                         sd->ahu.user_ring_idx = 0;
83
84                 fio_syslet_complete_atom(td, atom);
85         } while (1);
86 }
87
88 static int fio_syslet_getevents(struct thread_data *td, int min,
89                                 int fio_unused max,
90                                 struct timespec fio_unused *t)
91 {
92         struct syslet_data *sd = td->io_ops->data;
93         long ret;
94
95         do {
96                 fio_syslet_complete(td);
97
98                 /*
99                  * do we have enough immediate completions?
100                  */
101                 if (sd->nr_events >= (unsigned int) min)
102                         break;
103
104                 /*
105                  * OK, we need to wait for some events...
106                  */
107                 ret = async_wait(1, sd->ahu.user_ring_idx, &sd->ahu);
108                 if (ret < 0)
109                         return -errno;
110         } while (1);
111
112         ret = sd->nr_events;
113         sd->nr_events = 0;
114         return ret;
115 }
116
117 static struct io_u *fio_syslet_event(struct thread_data *td, int event)
118 {
119         struct syslet_data *sd = td->io_ops->data;
120
121         return sd->events[event];
122 }
123
124 static void init_atom(struct syslet_uatom *atom, int nr, void *arg0,
125                       void *arg1, void *arg2, void *arg3, void *ret_ptr,
126                       unsigned long flags, void *priv)
127 {
128         atom->flags = flags;
129         atom->nr = nr;
130         atom->ret_ptr = ret_ptr;
131         atom->next = NULL;
132         atom->arg_ptr[0] = arg0;
133         atom->arg_ptr[1] = arg1;
134         atom->arg_ptr[2] = arg2;
135         atom->arg_ptr[3] = arg3;
136         atom->arg_ptr[4] = atom->arg_ptr[5] = NULL;
137         atom->private = priv;
138 }
139
140 /*
141  * Use seek atom for sync
142  */
143 static void fio_syslet_prep_sync(struct io_u *io_u, struct fio_file *f)
144 {
145         init_atom(&io_u->req.atom, __NR_fsync, &f->fd, NULL, NULL, NULL,
146                   &io_u->req.ret, 0, io_u);
147 }
148
149 static void fio_syslet_prep_rw(struct io_u *io_u, struct fio_file *f)
150 {
151         int nr;
152
153         /*
154          * prepare rw
155          */
156         if (io_u->ddir == DDIR_READ)
157                 nr = __NR_pread64;
158         else
159                 nr = __NR_pwrite64;
160
161         init_atom(&io_u->req.atom, nr, &f->fd, &io_u->xfer_buf,
162                   &io_u->xfer_buflen, &io_u->offset, &io_u->req.ret, 0, io_u);
163 }
164
165 static int fio_syslet_prep(struct thread_data fio_unused *td, struct io_u *io_u)
166 {
167         struct fio_file *f = io_u->file;
168
169         if (io_u->ddir == DDIR_SYNC)
170                 fio_syslet_prep_sync(io_u, f);
171         else
172                 fio_syslet_prep_rw(io_u, f);
173
174         return 0;
175 }
176
177 static void cachemiss_thread_start(void)
178 {
179         while (1)
180                 async_thread(NULL, NULL);
181 }
182
183 #define THREAD_STACK_SIZE (16384)
184
185 static unsigned long thread_stack_alloc()
186 {
187         return (unsigned long) malloc(THREAD_STACK_SIZE) + THREAD_STACK_SIZE;
188 }
189
190 static void fio_syslet_queued(struct thread_data *td, struct syslet_data *sd)
191 {
192         struct syslet_uatom *atom;
193         struct timeval now;
194
195         fio_gettime(&now, NULL);
196
197         atom = sd->head;
198         while (atom) {
199                 struct io_u *io_u = atom->private;
200
201                 memcpy(&io_u->issue_time, &now, sizeof(now));
202                 io_u_queued(td, io_u);
203                 atom = atom->next;
204         }
205 }
206
207 static int fio_syslet_commit(struct thread_data *td)
208 {
209         struct syslet_data *sd = td->io_ops->data;
210         struct syslet_uatom *done;
211
212         if (!sd->head)
213                 return 0;
214
215         assert(!sd->tail->next);
216
217         if (!sd->ahu.new_thread_stack)
218                 sd->ahu.new_thread_stack = thread_stack_alloc();
219
220         fio_syslet_queued(td, sd);
221
222         /*
223          * On sync completion, the atom is returned. So on NULL return
224          * it's queued asynchronously.
225          */
226         done = async_exec(sd->head, &sd->ahu);
227
228         sd->head = sd->tail = NULL;
229
230         if (done)
231                 fio_syslet_complete_atom(td, done);
232
233         return 0;
234 }
235
236 static int fio_syslet_queue(struct thread_data *td, struct io_u *io_u)
237 {
238         struct syslet_data *sd = td->io_ops->data;
239
240         if (sd->tail) {
241                 sd->tail->next = &io_u->req.atom;
242                 sd->tail = &io_u->req.atom;
243         } else
244                 sd->head = sd->tail = &io_u->req.atom;
245
246         io_u->req.head = sd->head;
247         return FIO_Q_QUEUED;
248 }
249
250 static int async_head_init(struct syslet_data *sd, unsigned int depth)
251 {
252         unsigned long ring_size;
253
254         memset(&sd->ahu, 0, sizeof(struct async_head_user));
255
256         ring_size = sizeof(struct syslet_uatom *) * depth;
257         sd->ring = malloc(ring_size);
258         memset(sd->ring, 0, ring_size);
259
260         sd->ahu.user_ring_idx = 0;
261         sd->ahu.completion_ring = sd->ring;
262         sd->ahu.ring_size_bytes = ring_size;
263         sd->ahu.head_stack = thread_stack_alloc();
264         sd->ahu.head_eip = (unsigned long) cachemiss_thread_start;
265         sd->ahu.new_thread_eip = (unsigned long) cachemiss_thread_start;
266
267         return 0;
268 }
269
270 static void async_head_exit(struct syslet_data *sd)
271 {
272         free(sd->ring);
273 }
274
275 static void fio_syslet_cleanup(struct thread_data *td)
276 {
277         struct syslet_data *sd = td->io_ops->data;
278
279         if (sd) {
280                 async_head_exit(sd);
281                 free(sd->events);
282                 free(sd);
283                 td->io_ops->data = NULL;
284         }
285 }
286
287 static int fio_syslet_init(struct thread_data *td)
288 {
289         struct syslet_data *sd;
290
291
292         sd = malloc(sizeof(*sd));
293         memset(sd, 0, sizeof(*sd));
294         sd->events = malloc(sizeof(struct io_u *) * td->iodepth);
295         memset(sd->events, 0, sizeof(struct io_u *) * td->iodepth);
296
297         /*
298          * This will handily fail for kernels where syslet isn't available
299          */
300         if (async_head_init(sd, td->iodepth)) {
301                 free(sd->events);
302                 free(sd);
303                 return 1;
304         }
305
306         td->io_ops->data = sd;
307         return 0;
308 }
309
310 static struct ioengine_ops ioengine = {
311         .name           = "syslet-rw",
312         .version        = FIO_IOOPS_VERSION,
313         .init           = fio_syslet_init,
314         .prep           = fio_syslet_prep,
315         .queue          = fio_syslet_queue,
316         .commit         = fio_syslet_commit,
317         .getevents      = fio_syslet_getevents,
318         .event          = fio_syslet_event,
319         .cleanup        = fio_syslet_cleanup,
320 };
321
322 #else /* FIO_HAVE_SYSLET */
323
324 /*
325  * When we have a proper configure system in place, we simply wont build
326  * and install this io engine. For now install a crippled version that
327  * just complains and fails to load.
328  */
329 static int fio_syslet_init(struct thread_data fio_unused *td)
330 {
331         fprintf(stderr, "fio: syslet not available\n");
332         return 1;
333 }
334
335 static struct ioengine_ops ioengine = {
336         .name           = "syslet-rw",
337         .version        = FIO_IOOPS_VERSION,
338         .init           = fio_syslet_init,
339 };
340
341 #endif /* FIO_HAVE_SYSLET */
342
343 static void fio_init fio_syslet_register(void)
344 {
345         register_ioengine(&ioengine);
346 }
347
348 static void fio_exit fio_syslet_unregister(void)
349 {
350         unregister_ioengine(&ioengine);
351 }