Expose guasi IO engine in ioengine=
[fio.git] / engines / guasi.c
1 /*
2  * guasi engine
3  *
4  * IO engine using the GUASI library.
5  *
6  * This is currently disabled, add -lguasi to the fio Makefile target
7  * and add a #define FIO_HAVE_GUASI to os-linux.h to enable it. You'll
8  * need the GUASI lib as well:
9  *
10  * http://www.xmailserver.org/guasi-lib.html
11  *
12  */
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <assert.h>
18
19 #include "../fio.h"
20 #include "../os.h"
21
22 #ifdef FIO_HAVE_GUASI
23
24 #define GFIO_MIN_THREADS 32
25
26 #include <guasi.h>
27 #include <guasi_syscalls.h>
28
29 #ifdef GFIO_DEBUG
30 #define GDBG_PRINT(a) printf a
31 #else
32 #define GDBG_PRINT(a) (void) 0
33 #endif
34
35 #define STFU_GCC(a) a = a
36
37
38 struct guasi_data {
39         guasi_t hctx;
40         int max_reqs;
41         guasi_req_t *reqs;
42         struct io_u **io_us;
43         int reqs_nr;
44 };
45
46 static int fio_guasi_prep(struct thread_data fio_unused *td, struct io_u *io_u)
47 {
48         STFU_GCC(io_u);
49
50         GDBG_PRINT(("fio_guasi_prep(%p)\n", io_u));
51
52         return 0;
53 }
54
55 static struct io_u *fio_guasi_event(struct thread_data *td, int event)
56 {
57         struct guasi_data *ld = td->io_ops->data;
58         struct io_u *io_u;
59         struct guasi_reqinfo rinf;
60
61         GDBG_PRINT(("fio_guasi_event(%d)\n", event));
62         if (guasi_req_info(ld->reqs[event], &rinf) < 0) {
63                 fprintf(stderr, "guasi_req_info(%d) FAILED!\n", event);
64                 return NULL;
65         }
66         io_u = rinf.asid;
67         GDBG_PRINT(("fio_guasi_event(%d) -> %p\n", event, io_u));
68
69         if (io_u->ddir == DDIR_READ ||
70             io_u->ddir == DDIR_WRITE) {
71                 if (rinf.result != (long) io_u->xfer_buflen) {
72                         if (rinf.result < 0)
73                                 io_u->error = rinf.error;
74                         else
75                                 io_u->resid = io_u->xfer_buflen - rinf.result;
76                 } else
77                         io_u->error = 0;
78         } else
79                 io_u->error = rinf.result;
80
81         return io_u;
82 }
83
84 static int fio_guasi_getevents(struct thread_data *td, int min, int max,
85                                struct timespec *t)
86 {
87         struct guasi_data *ld = td->io_ops->data;
88         int n = 0, r;
89         long timeo = -1;
90
91         GDBG_PRINT(("fio_guasi_getevents(%d, %d)\n", min, max));
92         if (min > ld->max_reqs)
93                 min = ld->max_reqs;
94         if (max > ld->max_reqs)
95                 max = ld->max_reqs;
96         if (t)
97                 timeo = t->tv_sec * 1000L + t->tv_nsec / 1000000L;
98         do {
99                 r = guasi_fetch(ld->hctx, ld->reqs + n, max - n, timeo);
100                 if (r < 0)
101                         break;
102                 n += r;
103                 if (n >= min)
104                         break;
105         } while (1);
106         GDBG_PRINT(("fio_guasi_getevents() -> %d\n", n));
107
108         return n;
109 }
110
111 static int fio_guasi_queue(struct thread_data *td, struct io_u *io_u)
112 {
113         struct guasi_data *ld = td->io_ops->data;
114
115         GDBG_PRINT(("fio_guasi_queue(%p)\n", io_u));
116         if (ld->reqs_nr == (int) td->iodepth)
117                 return FIO_Q_BUSY;
118
119         ld->io_us[ld->reqs_nr] = io_u;
120         ld->reqs_nr++;
121         return FIO_Q_QUEUED;
122 }
123
124 static void fio_guasi_queued(struct thread_data *td, struct io_u **io_us,
125                              unsigned int nr)
126 {
127         struct timeval now;
128         struct io_u *io_u = io_us[nr];
129
130         fio_gettime(&now, NULL);
131         memcpy(&io_u->issue_time, &now, sizeof(now));
132         io_u_queued(td, io_u);
133 }
134
135 static int fio_guasi_commit(struct thread_data *td)
136 {
137         struct guasi_data *ld = td->io_ops->data;
138         int i;
139         struct io_u *io_u;
140         struct fio_file *f;
141
142         GDBG_PRINT(("fio_guasi_commit()\n"));
143         for (i = 0; i < ld->reqs_nr; i++) {
144                 io_u = ld->io_us[i];
145                 f = io_u->file;
146                 io_u->greq = NULL;
147                 if (io_u->ddir == DDIR_READ)
148                         io_u->greq = guasi__pread(ld->hctx, ld, io_u, 0,
149                                                   f->fd, io_u->xfer_buf, io_u->xfer_buflen,
150                                                   io_u->offset);
151                 else if (io_u->ddir == DDIR_WRITE)
152                         io_u->greq = guasi__pwrite(ld->hctx, ld, io_u, 0,
153                                                    f->fd, io_u->xfer_buf, io_u->xfer_buflen,
154                                                    io_u->offset);
155                 else if (io_u->ddir == DDIR_SYNC)
156                         io_u->greq = guasi__fsync(ld->hctx, ld, io_u, 0, f->fd);
157                 else {
158                         fprintf(stderr, "fio_guasi_commit() FAILED: %d\n", io_u->ddir);
159                 }
160                 if (io_u->greq != NULL)
161                         fio_guasi_queued(td, ld->io_us, i);
162         }
163         ld->reqs_nr = 0;
164         GDBG_PRINT(("fio_guasi_commit() -> %d\n", i));
165
166         return 0;
167 }
168
169 static int fio_guasi_cancel(struct thread_data *td, struct io_u *io_u)
170 {
171         struct guasi_data *ld = td->io_ops->data;
172
173         STFU_GCC(ld);
174         GDBG_PRINT(("fio_guasi_cancel(%p)\n", io_u));
175
176         return guasi_req_cancel(io_u->greq);
177 }
178
179 static void fio_guasi_cleanup(struct thread_data *td)
180 {
181         struct guasi_data *ld = td->io_ops->data;
182
183         if (ld) {
184                 guasi_free(ld->hctx);
185                 free(ld->reqs);
186                 free(ld->io_us);
187                 free(ld);
188                 td->io_ops->data = NULL;
189         }
190 }
191
192 static int fio_guasi_init(struct thread_data *td)
193 {
194         int maxthr;
195         struct guasi_data *ld = malloc(sizeof(*ld));
196
197         GDBG_PRINT(("fio_guasi_init(): depth=%d\n", td->iodepth));
198         memset(ld, 0, sizeof(*ld));
199         maxthr = td->iodepth > GFIO_MIN_THREADS ? td->iodepth: GFIO_MIN_THREADS;
200         if ((ld->hctx = guasi_create(GFIO_MIN_THREADS, maxthr, 1)) == NULL) {
201                 td_verror(td, errno, "guasi_create");
202                 free(ld);
203                 return 1;
204         }
205         ld->max_reqs = td->iodepth;
206         ld->reqs = malloc(ld->max_reqs * sizeof(guasi_req_t));
207         ld->io_us = malloc(ld->max_reqs * sizeof(struct io_u *));
208         memset(ld->io_us, 0, ld->max_reqs * sizeof(struct io_u *));
209         ld->reqs_nr = 0;
210
211         td->io_ops->data = ld;
212         GDBG_PRINT(("fio_guasi_init(): depth=%d -> %p\n", td->iodepth, ld));
213
214         return 0;
215 }
216
217 static struct ioengine_ops ioengine = {
218         .name           = "guasi",
219         .version        = FIO_IOOPS_VERSION,
220         .init           = fio_guasi_init,
221         .prep           = fio_guasi_prep,
222         .queue          = fio_guasi_queue,
223         .commit         = fio_guasi_commit,
224         .cancel         = fio_guasi_cancel,
225         .getevents      = fio_guasi_getevents,
226         .event          = fio_guasi_event,
227         .cleanup        = fio_guasi_cleanup,
228         .open_file      = generic_open_file,
229         .close_file     = generic_close_file,
230 };
231
232 #else /* FIO_HAVE_GUASI */
233
234 /*
235  * When we have a proper configure system in place, we simply wont build
236  * and install this io engine. For now install a crippled version that
237  * just complains and fails to load.
238  */
239 static int fio_guasi_init(struct thread_data fio_unused *td)
240 {
241         fprintf(stderr, "fio: guasi not available\n");
242         return 1;
243 }
244
245 static struct ioengine_ops ioengine = {
246         .name           = "guasi",
247         .version        = FIO_IOOPS_VERSION,
248         .init           = fio_guasi_init,
249 };
250
251 #endif
252
253 static void fio_init fio_guasi_register(void)
254 {
255         register_ioengine(&ioengine);
256 }
257
258 static void fio_exit fio_guasi_unregister(void)
259 {
260         unregister_ioengine(&ioengine);
261 }
262