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