7a5d4271808eed0ad2c9b9178ca2f36abc6b614f
[fio.git] / engines / libblkio.c
1 /*
2  * libblkio engine
3  *
4  * IO engine using libblkio to access various block I/O interfaces:
5  * https://gitlab.com/libblkio/libblkio
6  */
7
8 #include <assert.h>
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <stddef.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include <blkio.h>
17
18 #include "../fio.h"
19 #include "../optgroup.h"
20 #include "../options.h"
21 #include "../parse.h"
22
23 /* per-thread state */
24 struct fio_blkio_data {
25         struct blkio *b;
26         struct blkioq *q;
27
28         bool has_mem_region; /* whether mem_region is valid */
29         struct blkio_mem_region mem_region;
30
31         struct blkio_completion *completions;
32 };
33
34 struct fio_blkio_options {
35         void *pad; /* option fields must not have offset 0 */
36
37         char *driver;
38         char *pre_connect_props;
39         char *pre_start_props;
40 };
41
42 static struct fio_option options[] = {
43         {
44                 .name   = "libblkio_driver",
45                 .lname  = "libblkio driver name",
46                 .type   = FIO_OPT_STR_STORE,
47                 .off1   = offsetof(struct fio_blkio_options, driver),
48                 .help   = "Name of the driver to be used by libblkio",
49                 .category = FIO_OPT_C_ENGINE,
50                 .group  = FIO_OPT_G_LIBBLKIO,
51         },
52         {
53                 .name   = "libblkio_pre_connect_props",
54                 .lname  = "Properties to be set before blkio_connect()",
55                 .type   = FIO_OPT_STR_STORE,
56                 .off1   = offsetof(struct fio_blkio_options, pre_connect_props),
57                 .help   = "",
58                 .category = FIO_OPT_C_ENGINE,
59                 .group  = FIO_OPT_G_LIBBLKIO,
60         },
61         {
62                 .name   = "libblkio_pre_start_props",
63                 .lname  = "Properties to be set before blkio_start()",
64                 .type   = FIO_OPT_STR_STORE,
65                 .off1   = offsetof(struct fio_blkio_options, pre_start_props),
66                 .help   = "",
67                 .category = FIO_OPT_C_ENGINE,
68                 .group  = FIO_OPT_G_LIBBLKIO,
69         },
70         {
71                 .name = NULL,
72         },
73 };
74
75 static int fio_blkio_set_props_from_str(struct blkio *b, const char *opt_name,
76                                         const char *str) {
77         int ret = 0;
78         char *new_str, *name, *value;
79
80         if (!str)
81                 return 0;
82
83         /* iteration can mutate string, so copy it */
84         new_str = strdup(str);
85         if (!new_str) {
86                 log_err("fio: strdup() failed\n");
87                 return 1;
88         }
89
90         /* iterate over property name-value pairs */
91         while ((name = get_next_str(&new_str))) {
92                 /* split into property name and value */
93                 value = strchr(name, '=');
94                 if (!value) {
95                         log_err("fio: missing '=' in option %s\n", opt_name);
96                         ret = 1;
97                         break;
98                 }
99
100                 *value = '\0';
101                 ++value;
102
103                 /* strip whitespace from property name */
104                 strip_blank_front(&name);
105                 strip_blank_end(name);
106
107                 if (name[0] == '\0') {
108                         log_err("fio: empty property name in option %s\n",
109                                 opt_name);
110                         ret = 1;
111                         break;
112                 }
113
114                 /* strip whitespace from property value */
115                 strip_blank_front(&value);
116                 strip_blank_end(value);
117
118                 /* set property */
119                 if (blkio_set_str(b, name, value) != 0) {
120                         log_err("fio: error setting property '%s' to '%s': %s\n",
121                                 name, value, blkio_get_error_msg());
122                         ret = 1;
123                         break;
124                 }
125         }
126
127         free(new_str);
128         return ret;
129 }
130
131 /*
132  * Log the failure of a libblkio function.
133  *
134  * `(void)func` is to ensure `func` exists and prevent typos
135  */
136 #define fio_blkio_log_err(func) \
137         ({ \
138                 (void)func; \
139                 log_err("fio: %s() failed: %s\n", #func, \
140                         blkio_get_error_msg()); \
141         })
142
143 static int fio_blkio_create_and_connect(struct thread_data *td,
144                                         struct blkio **out_blkio)
145 {
146         const struct fio_blkio_options *options = td->eo;
147         struct blkio *b;
148         int ret;
149
150         if (!options->driver) {
151                 log_err("fio: engine libblkio requires option libblkio_driver to be set\n");
152                 return 1;
153         }
154
155         if (blkio_create(options->driver, &b) != 0) {
156                 fio_blkio_log_err(blkio_create);
157                 return 1;
158         }
159
160         /* don't fail if driver doesn't have a "direct" property */
161         ret = blkio_set_bool(b, "direct", td->o.odirect);
162         if (ret != 0 && ret != -ENOENT) {
163                 fio_blkio_log_err(blkio_set_bool);
164                 goto err_blkio_destroy;
165         }
166
167         if (blkio_set_bool(b, "read-only", read_only) != 0) {
168                 fio_blkio_log_err(blkio_set_bool);
169                 goto err_blkio_destroy;
170         }
171
172         if (fio_blkio_set_props_from_str(b, "libblkio_pre_connect_props",
173                                          options->pre_connect_props) != 0)
174                 goto err_blkio_destroy;
175
176         if (blkio_connect(b) != 0) {
177                 fio_blkio_log_err(blkio_connect);
178                 goto err_blkio_destroy;
179         }
180
181         if (fio_blkio_set_props_from_str(b, "libblkio_pre_start_props",
182                                          options->pre_start_props) != 0)
183                 goto err_blkio_destroy;
184
185         *out_blkio = b;
186         return 0;
187
188 err_blkio_destroy:
189         blkio_destroy(&b);
190         return 1;
191 }
192
193 /*
194  * This callback determines the device/file size, so it creates and connects a
195  * blkio instance. But it is invoked from the main thread in the original fio
196  * process, not from the processes in which jobs will actually run. It thus
197  * subsequently destroys the blkio, which is recreated in the init() callback.
198  */
199 static int fio_blkio_setup(struct thread_data *td)
200 {
201         struct blkio *b;
202         int ret = 0;
203         uint64_t capacity;
204
205         assert(td->files_index == 1);
206
207         if (fio_blkio_create_and_connect(td, &b) != 0)
208                 return 1;
209
210         if (blkio_get_uint64(b, "capacity", &capacity) != 0) {
211                 fio_blkio_log_err(blkio_get_uint64);
212                 ret = 1;
213                 goto out_blkio_destroy;
214         }
215
216         td->files[0]->real_file_size = capacity;
217         fio_file_set_size_known(td->files[0]);
218
219 out_blkio_destroy:
220         blkio_destroy(&b);
221         return ret;
222 }
223
224 static int fio_blkio_init(struct thread_data *td)
225 {
226         struct fio_blkio_data *data;
227
228         /*
229          * Request enqueueing is fast, and it's not possible to know exactly
230          * when a request is submitted, so never report submission latencies.
231          */
232         td->o.disable_slat = 1;
233
234         data = calloc(1, sizeof(*data));
235         if (!data) {
236                 log_err("fio: calloc() failed\n");
237                 return 1;
238         }
239
240         data->completions = calloc(td->o.iodepth, sizeof(data->completions[0]));
241         if (!data->completions) {
242                 log_err("fio: calloc() failed\n");
243                 goto err_free;
244         }
245
246         if (fio_blkio_create_and_connect(td, &data->b) != 0)
247                 goto err_free;
248
249         if (blkio_set_int(data->b, "num-queues", 1) != 0) {
250                 fio_blkio_log_err(blkio_set_int);
251                 goto err_blkio_destroy;
252         }
253
254         if (blkio_start(data->b) != 0) {
255                 fio_blkio_log_err(blkio_start);
256                 goto err_blkio_destroy;
257         }
258
259         data->q = blkio_get_queue(data->b, 0);
260
261         /* Set data last so cleanup() does nothing if init() fails. */
262         td->io_ops_data = data;
263
264         return 0;
265
266 err_blkio_destroy:
267         blkio_destroy(&data->b);
268 err_free:
269         free(data->completions);
270         free(data);
271         return 1;
272 }
273
274 static void fio_blkio_cleanup(struct thread_data *td)
275 {
276         struct fio_blkio_data *data = td->io_ops_data;
277
278         if (data) {
279                 blkio_destroy(&data->b);
280                 free(data->completions);
281                 free(data);
282         }
283 }
284
285 #define align_up(x, y) ((((x) + (y) - 1) / (y)) * (y))
286
287 static int fio_blkio_iomem_alloc(struct thread_data *td, size_t size)
288 {
289         struct fio_blkio_data *data = td->io_ops_data;
290         int ret;
291         uint64_t mem_region_alignment;
292
293         if (blkio_get_uint64(data->b, "mem-region-alignment",
294                              &mem_region_alignment) != 0) {
295                 fio_blkio_log_err(blkio_get_uint64);
296                 return 1;
297         }
298
299         /* round up size to satisfy mem-region-alignment */
300         size = align_up(size, (size_t)mem_region_alignment);
301
302         if (blkio_alloc_mem_region(data->b, &data->mem_region, size) != 0) {
303                 fio_blkio_log_err(blkio_alloc_mem_region);
304                 ret = 1;
305                 goto out;
306         }
307
308         if (blkio_map_mem_region(data->b, &data->mem_region) != 0) {
309                 fio_blkio_log_err(blkio_map_mem_region);
310                 ret = 1;
311                 goto out_free;
312         }
313
314         td->orig_buffer = data->mem_region.addr;
315         data->has_mem_region = true;
316
317         ret = 0;
318         goto out;
319
320 out_free:
321         blkio_free_mem_region(data->b, &data->mem_region);
322 out:
323         return ret;
324 }
325
326 static void fio_blkio_iomem_free(struct thread_data *td)
327 {
328         struct fio_blkio_data *data = td->io_ops_data;
329
330         if (data && data->has_mem_region) {
331                 blkio_unmap_mem_region(data->b, &data->mem_region);
332                 blkio_free_mem_region(data->b, &data->mem_region);
333
334                 data->has_mem_region = false;
335         }
336 }
337
338 static int fio_blkio_open_file(struct thread_data *td, struct fio_file *f)
339 {
340         return 0;
341 }
342
343 static enum fio_q_status fio_blkio_queue(struct thread_data *td,
344                                          struct io_u *io_u)
345 {
346         struct fio_blkio_data *data = td->io_ops_data;
347
348         fio_ro_check(td, io_u);
349
350         switch (io_u->ddir) {
351                 case DDIR_READ:
352                         blkioq_read(data->q, io_u->offset, io_u->xfer_buf,
353                                     (size_t)io_u->xfer_buflen, io_u, 0);
354                         break;
355                 case DDIR_WRITE:
356                         blkioq_write(data->q, io_u->offset, io_u->xfer_buf,
357                                      (size_t)io_u->xfer_buflen, io_u, 0);
358                         break;
359                 case DDIR_TRIM:
360                         blkioq_discard(data->q, io_u->offset, io_u->xfer_buflen,
361                                        io_u, 0);
362                         break;
363                 case DDIR_SYNC:
364                 case DDIR_DATASYNC:
365                         blkioq_flush(data->q, io_u, 0);
366                         break;
367                 default:
368                         io_u->error = ENOTSUP;
369                         io_u_log_error(td, io_u);
370                         return FIO_Q_COMPLETED;
371         }
372
373         return FIO_Q_QUEUED;
374 }
375
376 static int fio_blkio_getevents(struct thread_data *td, unsigned int min,
377                                unsigned int max, const struct timespec *t)
378 {
379         struct fio_blkio_data *data = td->io_ops_data;
380         int n;
381
382         n = blkioq_do_io(data->q, data->completions, (int)min, (int)max, NULL);
383         if (n < 0) {
384                 fio_blkio_log_err(blkioq_do_io);
385                 return -1;
386         }
387
388         return n;
389 }
390
391 static struct io_u *fio_blkio_event(struct thread_data *td, int event)
392 {
393         struct fio_blkio_data *data = td->io_ops_data;
394         struct blkio_completion *completion = &data->completions[event];
395         struct io_u *io_u = completion->user_data;
396
397         io_u->error = -completion->ret;
398
399         return io_u;
400 }
401
402 FIO_STATIC struct ioengine_ops ioengine = {
403         .name                   = "libblkio",
404         .version                = FIO_IOOPS_VERSION,
405         .flags                  = FIO_DISKLESSIO | FIO_NOEXTEND |
406                                   FIO_NO_OFFLOAD,
407
408         .setup                  = fio_blkio_setup,
409         .init                   = fio_blkio_init,
410         .cleanup                = fio_blkio_cleanup,
411
412         .iomem_alloc            = fio_blkio_iomem_alloc,
413         .iomem_free             = fio_blkio_iomem_free,
414
415         .open_file              = fio_blkio_open_file,
416
417         .queue                  = fio_blkio_queue,
418         .getevents              = fio_blkio_getevents,
419         .event                  = fio_blkio_event,
420
421         .options                = options,
422         .option_struct_size     = sizeof(struct fio_blkio_options),
423 };
424
425 static void fio_init fio_blkio_register(void)
426 {
427         register_ioengine(&ioengine);
428 }
429
430 static void fio_exit fio_blkio_unregister(void)
431 {
432         unregister_ioengine(&ioengine);
433 }