8a4828c394800873e47bbed2235caea0c139b93e
[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         int completion_fd; /* -1 if not FIO_BLKIO_WAIT_MODE_EVENTFD */
28
29         bool has_mem_region; /* whether mem_region is valid */
30         struct blkio_mem_region mem_region; /* only if allocated by libblkio */
31
32         struct iovec *iovecs; /* for vectored requests */
33         struct blkio_completion *completions;
34 };
35
36 enum fio_blkio_wait_mode {
37         FIO_BLKIO_WAIT_MODE_BLOCK,
38         FIO_BLKIO_WAIT_MODE_EVENTFD,
39         FIO_BLKIO_WAIT_MODE_LOOP,
40 };
41
42 struct fio_blkio_options {
43         void *pad; /* option fields must not have offset 0 */
44
45         char *driver;
46         char *pre_connect_props;
47         char *pre_start_props;
48
49         unsigned int hipri;
50         unsigned int vectored;
51         unsigned int write_zeroes_on_trim;
52         enum fio_blkio_wait_mode wait_mode;
53 };
54
55 static struct fio_option options[] = {
56         {
57                 .name   = "libblkio_driver",
58                 .lname  = "libblkio driver name",
59                 .type   = FIO_OPT_STR_STORE,
60                 .off1   = offsetof(struct fio_blkio_options, driver),
61                 .help   = "Name of the driver to be used by libblkio",
62                 .category = FIO_OPT_C_ENGINE,
63                 .group  = FIO_OPT_G_LIBBLKIO,
64         },
65         {
66                 .name   = "libblkio_pre_connect_props",
67                 .lname  = "Properties to be set before blkio_connect()",
68                 .type   = FIO_OPT_STR_STORE,
69                 .off1   = offsetof(struct fio_blkio_options, pre_connect_props),
70                 .help   = "",
71                 .category = FIO_OPT_C_ENGINE,
72                 .group  = FIO_OPT_G_LIBBLKIO,
73         },
74         {
75                 .name   = "libblkio_pre_start_props",
76                 .lname  = "Properties to be set before blkio_start()",
77                 .type   = FIO_OPT_STR_STORE,
78                 .off1   = offsetof(struct fio_blkio_options, pre_start_props),
79                 .help   = "",
80                 .category = FIO_OPT_C_ENGINE,
81                 .group  = FIO_OPT_G_LIBBLKIO,
82         },
83         {
84                 .name   = "hipri",
85                 .lname  = "Use poll queues",
86                 .type   = FIO_OPT_STR_SET,
87                 .off1   = offsetof(struct fio_blkio_options, hipri),
88                 .help   = "Use poll queues",
89                 .category = FIO_OPT_C_ENGINE,
90                 .group  = FIO_OPT_G_LIBBLKIO,
91         },
92         {
93                 .name   = "libblkio_vectored",
94                 .lname  = "Use blkioq_{readv,writev}()",
95                 .type   = FIO_OPT_STR_SET,
96                 .off1   = offsetof(struct fio_blkio_options, vectored),
97                 .help   = "Use blkioq_{readv,writev}() instead of blkioq_{read,write}()",
98                 .category = FIO_OPT_C_ENGINE,
99                 .group  = FIO_OPT_G_LIBBLKIO,
100         },
101         {
102                 .name   = "libblkio_write_zeroes_on_trim",
103                 .lname  = "Use blkioq_write_zeroes() for TRIM",
104                 .type   = FIO_OPT_STR_SET,
105                 .off1   = offsetof(struct fio_blkio_options,
106                                    write_zeroes_on_trim),
107                 .help   = "Use blkioq_write_zeroes() for TRIM instead of blkioq_discard()",
108                 .category = FIO_OPT_C_ENGINE,
109                 .group  = FIO_OPT_G_LIBBLKIO,
110         },
111         {
112                 .name   = "libblkio_wait_mode",
113                 .lname  = "How to wait for completions",
114                 .type   = FIO_OPT_STR,
115                 .off1   = offsetof(struct fio_blkio_options, wait_mode),
116                 .help   = "How to wait for completions",
117                 .def    = "block",
118                 .posval = {
119                           { .ival = "block",
120                             .oval = FIO_BLKIO_WAIT_MODE_BLOCK,
121                             .help = "Blocking blkioq_do_io()",
122                           },
123                           { .ival = "eventfd",
124                             .oval = FIO_BLKIO_WAIT_MODE_EVENTFD,
125                             .help = "Blocking read() on the completion eventfd",
126                           },
127                           { .ival = "loop",
128                             .oval = FIO_BLKIO_WAIT_MODE_LOOP,
129                             .help = "Busy loop with non-blocking blkioq_do_io()",
130                           },
131                 },
132                 .category = FIO_OPT_C_ENGINE,
133                 .group  = FIO_OPT_G_LIBBLKIO,
134         },
135         {
136                 .name = NULL,
137         },
138 };
139
140 static int fio_blkio_set_props_from_str(struct blkio *b, const char *opt_name,
141                                         const char *str) {
142         int ret = 0;
143         char *new_str, *name, *value;
144
145         if (!str)
146                 return 0;
147
148         /* iteration can mutate string, so copy it */
149         new_str = strdup(str);
150         if (!new_str) {
151                 log_err("fio: strdup() failed\n");
152                 return 1;
153         }
154
155         /* iterate over property name-value pairs */
156         while ((name = get_next_str(&new_str))) {
157                 /* split into property name and value */
158                 value = strchr(name, '=');
159                 if (!value) {
160                         log_err("fio: missing '=' in option %s\n", opt_name);
161                         ret = 1;
162                         break;
163                 }
164
165                 *value = '\0';
166                 ++value;
167
168                 /* strip whitespace from property name */
169                 strip_blank_front(&name);
170                 strip_blank_end(name);
171
172                 if (name[0] == '\0') {
173                         log_err("fio: empty property name in option %s\n",
174                                 opt_name);
175                         ret = 1;
176                         break;
177                 }
178
179                 /* strip whitespace from property value */
180                 strip_blank_front(&value);
181                 strip_blank_end(value);
182
183                 /* set property */
184                 if (blkio_set_str(b, name, value) != 0) {
185                         log_err("fio: error setting property '%s' to '%s': %s\n",
186                                 name, value, blkio_get_error_msg());
187                         ret = 1;
188                         break;
189                 }
190         }
191
192         free(new_str);
193         return ret;
194 }
195
196 /*
197  * Log the failure of a libblkio function.
198  *
199  * `(void)func` is to ensure `func` exists and prevent typos
200  */
201 #define fio_blkio_log_err(func) \
202         ({ \
203                 (void)func; \
204                 log_err("fio: %s() failed: %s\n", #func, \
205                         blkio_get_error_msg()); \
206         })
207
208 static int fio_blkio_create_and_connect(struct thread_data *td,
209                                         struct blkio **out_blkio)
210 {
211         const struct fio_blkio_options *options = td->eo;
212         struct blkio *b;
213         int ret;
214
215         if (!options->driver) {
216                 log_err("fio: engine libblkio requires option libblkio_driver to be set\n");
217                 return 1;
218         }
219
220         if (blkio_create(options->driver, &b) != 0) {
221                 fio_blkio_log_err(blkio_create);
222                 return 1;
223         }
224
225         /* don't fail if driver doesn't have a "direct" property */
226         ret = blkio_set_bool(b, "direct", td->o.odirect);
227         if (ret != 0 && ret != -ENOENT) {
228                 fio_blkio_log_err(blkio_set_bool);
229                 goto err_blkio_destroy;
230         }
231
232         if (blkio_set_bool(b, "read-only", read_only) != 0) {
233                 fio_blkio_log_err(blkio_set_bool);
234                 goto err_blkio_destroy;
235         }
236
237         if (fio_blkio_set_props_from_str(b, "libblkio_pre_connect_props",
238                                          options->pre_connect_props) != 0)
239                 goto err_blkio_destroy;
240
241         if (blkio_connect(b) != 0) {
242                 fio_blkio_log_err(blkio_connect);
243                 goto err_blkio_destroy;
244         }
245
246         if (fio_blkio_set_props_from_str(b, "libblkio_pre_start_props",
247                                          options->pre_start_props) != 0)
248                 goto err_blkio_destroy;
249
250         *out_blkio = b;
251         return 0;
252
253 err_blkio_destroy:
254         blkio_destroy(&b);
255         return 1;
256 }
257
258 /*
259  * This callback determines the device/file size, so it creates and connects a
260  * blkio instance. But it is invoked from the main thread in the original fio
261  * process, not from the processes in which jobs will actually run. It thus
262  * subsequently destroys the blkio, which is recreated in the init() callback.
263  */
264 static int fio_blkio_setup(struct thread_data *td)
265 {
266         const struct fio_blkio_options *options = td->eo;
267         struct blkio *b;
268         int ret = 0;
269         uint64_t capacity;
270
271         assert(td->files_index == 1);
272
273         if (options->hipri &&
274                 options->wait_mode == FIO_BLKIO_WAIT_MODE_EVENTFD) {
275                 log_err("fio: option hipri is incompatible with option libblkio_wait_mode=eventfd\n");
276                 return 1;
277         }
278
279         if (fio_blkio_create_and_connect(td, &b) != 0)
280                 return 1;
281
282         if (blkio_get_uint64(b, "capacity", &capacity) != 0) {
283                 fio_blkio_log_err(blkio_get_uint64);
284                 ret = 1;
285                 goto out_blkio_destroy;
286         }
287
288         td->files[0]->real_file_size = capacity;
289         fio_file_set_size_known(td->files[0]);
290
291 out_blkio_destroy:
292         blkio_destroy(&b);
293         return ret;
294 }
295
296 static int fio_blkio_init(struct thread_data *td)
297 {
298         const struct fio_blkio_options *options = td->eo;
299         struct fio_blkio_data *data;
300         int flags;
301
302         /*
303          * Request enqueueing is fast, and it's not possible to know exactly
304          * when a request is submitted, so never report submission latencies.
305          */
306         td->o.disable_slat = 1;
307
308         data = calloc(1, sizeof(*data));
309         if (!data) {
310                 log_err("fio: calloc() failed\n");
311                 return 1;
312         }
313
314         data->iovecs = calloc(td->o.iodepth, sizeof(data->iovecs[0]));
315         data->completions = calloc(td->o.iodepth, sizeof(data->completions[0]));
316         if (!data->iovecs || !data->completions) {
317                 log_err("fio: calloc() failed\n");
318                 goto err_free;
319         }
320
321         if (fio_blkio_create_and_connect(td, &data->b) != 0)
322                 goto err_free;
323
324         if (blkio_set_int(data->b, "num-queues", options->hipri ? 0 : 1) != 0) {
325                 fio_blkio_log_err(blkio_set_int);
326                 goto err_blkio_destroy;
327         }
328
329         if (blkio_set_int(data->b, "num-poll-queues",
330                           options->hipri ? 1 : 0) != 0) {
331                 fio_blkio_log_err(blkio_set_int);
332                 goto err_blkio_destroy;
333         }
334
335         if (blkio_start(data->b) != 0) {
336                 fio_blkio_log_err(blkio_start);
337                 goto err_blkio_destroy;
338         }
339
340         if (options->hipri)
341                 data->q = blkio_get_poll_queue(data->b, 0);
342         else
343                 data->q = blkio_get_queue(data->b, 0);
344
345         if (options->wait_mode == FIO_BLKIO_WAIT_MODE_EVENTFD) {
346                 /* enable completion fd and make it blocking */
347                 blkioq_set_completion_fd_enabled(data->q, true);
348                 data->completion_fd = blkioq_get_completion_fd(data->q);
349
350                 flags = fcntl(data->completion_fd, F_GETFL);
351                 if (flags < 0) {
352                         log_err("fio: fcntl(F_GETFL) failed: %s\n",
353                                 strerror(errno));
354                         goto err_blkio_destroy;
355                 }
356
357                 if (fcntl(data->completion_fd, F_SETFL,
358                           flags & ~O_NONBLOCK) != 0) {
359                         log_err("fio: fcntl(F_SETFL) failed: %s\n",
360                                 strerror(errno));
361                         goto err_blkio_destroy;
362                 }
363         } else {
364                 data->completion_fd = -1;
365         }
366
367         /* Set data last so cleanup() does nothing if init() fails. */
368         td->io_ops_data = data;
369
370         return 0;
371
372 err_blkio_destroy:
373         blkio_destroy(&data->b);
374 err_free:
375         free(data->completions);
376         free(data->iovecs);
377         free(data);
378         return 1;
379 }
380
381 static int fio_blkio_post_init(struct thread_data *td)
382 {
383         struct fio_blkio_data *data = td->io_ops_data;
384
385         if (!data->has_mem_region) {
386                 /*
387                  * Memory was allocated by the fio core and not iomem_alloc(),
388                  * so we need to register it as a memory region here.
389                  *
390                  * `td->orig_buffer_size` is computed like `len` below, but then
391                  * fio can add some padding to it to make sure it is
392                  * sufficiently aligned to the page size and the mem_align
393                  * option. However, this can make it become unaligned to the
394                  * "mem-region-alignment" property in ways that the user can't
395                  * control, so we essentially recompute `td->orig_buffer_size`
396                  * here but without adding that padding.
397                  */
398
399                 unsigned long long max_block_size;
400                 struct blkio_mem_region region;
401
402                 max_block_size = max(td->o.max_bs[DDIR_READ],
403                                      max(td->o.max_bs[DDIR_WRITE],
404                                          td->o.max_bs[DDIR_TRIM]));
405
406                 region = (struct blkio_mem_region) {
407                         .addr   = td->orig_buffer,
408                         .len    = (size_t)max_block_size *
409                                         (size_t)td->o.iodepth,
410                         .fd     = -1,
411                 };
412
413                 if (blkio_map_mem_region(data->b, &region) != 0) {
414                         fio_blkio_log_err(blkio_map_mem_region);
415                         return 1;
416                 }
417         }
418
419         return 0;
420 }
421
422 static void fio_blkio_cleanup(struct thread_data *td)
423 {
424         struct fio_blkio_data *data = td->io_ops_data;
425
426         if (data) {
427                 blkio_destroy(&data->b);
428                 free(data->completions);
429                 free(data->iovecs);
430                 free(data);
431         }
432 }
433
434 #define align_up(x, y) ((((x) + (y) - 1) / (y)) * (y))
435
436 static int fio_blkio_iomem_alloc(struct thread_data *td, size_t size)
437 {
438         struct fio_blkio_data *data = td->io_ops_data;
439         int ret;
440         uint64_t mem_region_alignment;
441
442         if (blkio_get_uint64(data->b, "mem-region-alignment",
443                              &mem_region_alignment) != 0) {
444                 fio_blkio_log_err(blkio_get_uint64);
445                 return 1;
446         }
447
448         /* round up size to satisfy mem-region-alignment */
449         size = align_up(size, (size_t)mem_region_alignment);
450
451         if (blkio_alloc_mem_region(data->b, &data->mem_region, size) != 0) {
452                 fio_blkio_log_err(blkio_alloc_mem_region);
453                 ret = 1;
454                 goto out;
455         }
456
457         if (blkio_map_mem_region(data->b, &data->mem_region) != 0) {
458                 fio_blkio_log_err(blkio_map_mem_region);
459                 ret = 1;
460                 goto out_free;
461         }
462
463         td->orig_buffer = data->mem_region.addr;
464         data->has_mem_region = true;
465
466         ret = 0;
467         goto out;
468
469 out_free:
470         blkio_free_mem_region(data->b, &data->mem_region);
471 out:
472         return ret;
473 }
474
475 static void fio_blkio_iomem_free(struct thread_data *td)
476 {
477         struct fio_blkio_data *data = td->io_ops_data;
478
479         if (data && data->has_mem_region) {
480                 blkio_unmap_mem_region(data->b, &data->mem_region);
481                 blkio_free_mem_region(data->b, &data->mem_region);
482
483                 data->has_mem_region = false;
484         }
485 }
486
487 static int fio_blkio_open_file(struct thread_data *td, struct fio_file *f)
488 {
489         return 0;
490 }
491
492 static enum fio_q_status fio_blkio_queue(struct thread_data *td,
493                                          struct io_u *io_u)
494 {
495         const struct fio_blkio_options *options = td->eo;
496         struct fio_blkio_data *data = td->io_ops_data;
497
498         fio_ro_check(td, io_u);
499
500         switch (io_u->ddir) {
501                 case DDIR_READ:
502                         if (options->vectored) {
503                                 struct iovec *iov = &data->iovecs[io_u->index];
504                                 iov->iov_base = io_u->xfer_buf;
505                                 iov->iov_len = (size_t)io_u->xfer_buflen;
506
507                                 blkioq_readv(data->q, io_u->offset, iov, 1,
508                                              io_u, 0);
509                         } else {
510                                 blkioq_read(data->q, io_u->offset,
511                                             io_u->xfer_buf,
512                                             (size_t)io_u->xfer_buflen, io_u, 0);
513                         }
514                         break;
515                 case DDIR_WRITE:
516                         if (options->vectored) {
517                                 struct iovec *iov = &data->iovecs[io_u->index];
518                                 iov->iov_base = io_u->xfer_buf;
519                                 iov->iov_len = (size_t)io_u->xfer_buflen;
520
521                                 blkioq_writev(data->q, io_u->offset, iov, 1,
522                                               io_u, 0);
523                         } else {
524                                 blkioq_write(data->q, io_u->offset,
525                                              io_u->xfer_buf,
526                                              (size_t)io_u->xfer_buflen, io_u,
527                                              0);
528                         }
529                         break;
530                 case DDIR_TRIM:
531                         if (options->write_zeroes_on_trim) {
532                                 blkioq_write_zeroes(data->q, io_u->offset,
533                                                     io_u->xfer_buflen, io_u, 0);
534                         } else {
535                                 blkioq_discard(data->q, io_u->offset,
536                                                io_u->xfer_buflen, io_u, 0);
537                         }
538                         break;
539                 case DDIR_SYNC:
540                 case DDIR_DATASYNC:
541                         blkioq_flush(data->q, io_u, 0);
542                         break;
543                 default:
544                         io_u->error = ENOTSUP;
545                         io_u_log_error(td, io_u);
546                         return FIO_Q_COMPLETED;
547         }
548
549         return FIO_Q_QUEUED;
550 }
551
552 static int fio_blkio_getevents(struct thread_data *td, unsigned int min,
553                                unsigned int max, const struct timespec *t)
554 {
555         const struct fio_blkio_options *options = td->eo;
556         struct fio_blkio_data *data = td->io_ops_data;
557         int ret, n;
558         uint64_t event;
559
560         switch (options->wait_mode) {
561         case FIO_BLKIO_WAIT_MODE_BLOCK:
562                 n = blkioq_do_io(data->q, data->completions, (int)min, (int)max,
563                                  NULL);
564                 if (n < 0) {
565                         fio_blkio_log_err(blkioq_do_io);
566                         return -1;
567                 }
568                 return n;
569         case FIO_BLKIO_WAIT_MODE_EVENTFD:
570                 n = blkioq_do_io(data->q, data->completions, 0, (int)max, NULL);
571                 if (n < 0) {
572                         fio_blkio_log_err(blkioq_do_io);
573                         return -1;
574                 }
575                 while (n < (int)min) {
576                         ret = read(data->completion_fd, &event, sizeof(event));
577                         if (ret != sizeof(event)) {
578                                 log_err("fio: read() on the completion fd returned %d\n",
579                                         ret);
580                                 return -1;
581                         }
582
583                         ret = blkioq_do_io(data->q, data->completions + n, 0,
584                                            (int)max - n, NULL);
585                         if (ret < 0) {
586                                 fio_blkio_log_err(blkioq_do_io);
587                                 return -1;
588                         }
589
590                         n += ret;
591                 }
592                 return n;
593         case FIO_BLKIO_WAIT_MODE_LOOP:
594                 for (n = 0; n < (int)min; ) {
595                         ret = blkioq_do_io(data->q, data->completions + n, 0,
596                                            (int)max - n, NULL);
597                         if (ret < 0) {
598                                 fio_blkio_log_err(blkioq_do_io);
599                                 return -1;
600                         }
601
602                         n += ret;
603                 }
604                 return n;
605         default:
606                 return -1;
607         }
608 }
609
610 static struct io_u *fio_blkio_event(struct thread_data *td, int event)
611 {
612         struct fio_blkio_data *data = td->io_ops_data;
613         struct blkio_completion *completion = &data->completions[event];
614         struct io_u *io_u = completion->user_data;
615
616         io_u->error = -completion->ret;
617
618         return io_u;
619 }
620
621 FIO_STATIC struct ioengine_ops ioengine = {
622         .name                   = "libblkio",
623         .version                = FIO_IOOPS_VERSION,
624         .flags                  = FIO_DISKLESSIO | FIO_NOEXTEND |
625                                   FIO_NO_OFFLOAD | FIO_SKIPPABLE_IOMEM_ALLOC,
626
627         .setup                  = fio_blkio_setup,
628         .init                   = fio_blkio_init,
629         .post_init              = fio_blkio_post_init,
630         .cleanup                = fio_blkio_cleanup,
631
632         .iomem_alloc            = fio_blkio_iomem_alloc,
633         .iomem_free             = fio_blkio_iomem_free,
634
635         .open_file              = fio_blkio_open_file,
636
637         .queue                  = fio_blkio_queue,
638         .getevents              = fio_blkio_getevents,
639         .event                  = fio_blkio_event,
640
641         .options                = options,
642         .option_struct_size     = sizeof(struct fio_blkio_options),
643 };
644
645 static void fio_init fio_blkio_register(void)
646 {
647         register_ioengine(&ioengine);
648 }
649
650 static void fio_exit fio_blkio_unregister(void)
651 {
652         unregister_ioengine(&ioengine);
653 }