]> git.proxmox.com Git - ceph.git/blob - ceph/src/spdk/examples/bdev/fio_plugin/fio_plugin.c
import 15.2.0 Octopus source
[ceph.git] / ceph / src / spdk / examples / bdev / fio_plugin / fio_plugin.c
1 /*-
2 * BSD LICENSE
3 *
4 * Copyright (c) Intel Corporation.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include "spdk/stdinc.h"
35
36 #include "spdk/bdev.h"
37 #include "spdk/copy_engine.h"
38 #include "spdk/conf.h"
39 #include "spdk/env.h"
40 #include "spdk/thread.h"
41 #include "spdk/log.h"
42 #include "spdk/string.h"
43 #include "spdk/queue.h"
44 #include "spdk/util.h"
45
46 #include "spdk_internal/thread.h"
47
48 #include "config-host.h"
49 #include "fio.h"
50 #include "optgroup.h"
51
52 struct spdk_fio_options {
53 void *pad;
54 char *conf;
55 unsigned mem_mb;
56 bool mem_single_seg;
57 };
58
59 struct spdk_fio_request {
60 struct io_u *io;
61 struct thread_data *td;
62 };
63
64 struct spdk_fio_target {
65 struct spdk_bdev *bdev;
66 struct spdk_bdev_desc *desc;
67 struct spdk_io_channel *ch;
68
69 TAILQ_ENTRY(spdk_fio_target) link;
70 };
71
72 struct spdk_fio_thread {
73 struct thread_data *td; /* fio thread context */
74 struct spdk_thread *thread; /* spdk thread context */
75
76 TAILQ_HEAD(, spdk_fio_target) targets;
77 bool failed; /* true if the thread failed to initialize */
78
79 struct io_u **iocq; /* io completion queue */
80 unsigned int iocq_count; /* number of iocq entries filled by last getevents */
81 unsigned int iocq_size; /* number of iocq entries allocated */
82 };
83
84 static bool g_spdk_env_initialized = false;
85
86 static int spdk_fio_init(struct thread_data *td);
87 static void spdk_fio_cleanup(struct thread_data *td);
88 static size_t spdk_fio_poll_thread(struct spdk_fio_thread *fio_thread);
89
90 /* Default polling timeout (ns) */
91 #define SPDK_FIO_POLLING_TIMEOUT 1000000000ULL
92
93 static int
94 spdk_fio_init_thread(struct thread_data *td)
95 {
96 struct spdk_fio_thread *fio_thread;
97
98 fio_thread = calloc(1, sizeof(*fio_thread));
99 if (!fio_thread) {
100 SPDK_ERRLOG("failed to allocate thread local context\n");
101 return -1;
102 }
103
104 fio_thread->td = td;
105 td->io_ops_data = fio_thread;
106
107 fio_thread->thread = spdk_thread_create("fio_thread", NULL);
108 if (!fio_thread->thread) {
109 free(fio_thread);
110 SPDK_ERRLOG("failed to allocate thread\n");
111 return -1;
112 }
113 spdk_set_thread(fio_thread->thread);
114
115 fio_thread->iocq_size = td->o.iodepth;
116 fio_thread->iocq = calloc(fio_thread->iocq_size, sizeof(struct io_u *));
117 assert(fio_thread->iocq != NULL);
118
119 TAILQ_INIT(&fio_thread->targets);
120
121 return 0;
122 }
123
124 static void
125 spdk_fio_bdev_close_targets(void *arg)
126 {
127 struct spdk_fio_thread *fio_thread = arg;
128 struct spdk_fio_target *target, *tmp;
129
130 TAILQ_FOREACH_SAFE(target, &fio_thread->targets, link, tmp) {
131 TAILQ_REMOVE(&fio_thread->targets, target, link);
132 spdk_put_io_channel(target->ch);
133 spdk_bdev_close(target->desc);
134 free(target);
135 }
136 }
137
138 static void
139 spdk_fio_cleanup_thread(struct spdk_fio_thread *fio_thread)
140 {
141 spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_close_targets, fio_thread);
142
143 while (!spdk_thread_is_idle(fio_thread->thread)) {
144 spdk_fio_poll_thread(fio_thread);
145 }
146
147 spdk_set_thread(fio_thread->thread);
148
149 spdk_thread_exit(fio_thread->thread);
150 spdk_thread_destroy(fio_thread->thread);
151 free(fio_thread->iocq);
152 free(fio_thread);
153 }
154
155 static void
156 spdk_fio_calc_timeout(struct spdk_fio_thread *fio_thread, struct timespec *ts)
157 {
158 uint64_t timeout, now;
159
160 if (spdk_thread_has_active_pollers(fio_thread->thread)) {
161 return;
162 }
163
164 timeout = spdk_thread_next_poller_expiration(fio_thread->thread);
165 now = spdk_get_ticks();
166
167 if (timeout == 0) {
168 timeout = now + (SPDK_FIO_POLLING_TIMEOUT * spdk_get_ticks_hz()) / SPDK_SEC_TO_NSEC;
169 }
170
171 if (timeout > now) {
172 timeout = ((timeout - now) * SPDK_SEC_TO_NSEC) / spdk_get_ticks_hz() +
173 ts->tv_sec * SPDK_SEC_TO_NSEC + ts->tv_nsec;
174
175 ts->tv_sec = timeout / SPDK_SEC_TO_NSEC;
176 ts->tv_nsec = timeout % SPDK_SEC_TO_NSEC;
177 }
178 }
179
180 static pthread_t g_init_thread_id = 0;
181 static pthread_mutex_t g_init_mtx = PTHREAD_MUTEX_INITIALIZER;
182 static pthread_cond_t g_init_cond;
183 static bool g_poll_loop = true;
184
185 static void
186 spdk_fio_bdev_init_done(void *cb_arg, int rc)
187 {
188 *(bool *)cb_arg = true;
189 }
190
191 static void
192 spdk_fio_bdev_init_start(void *arg)
193 {
194 bool *done = arg;
195
196 /* Initialize the copy engine */
197 spdk_copy_engine_initialize();
198
199 /* Initialize the bdev layer */
200 spdk_bdev_initialize(spdk_fio_bdev_init_done, done);
201 }
202
203 static void
204 spdk_fio_bdev_fini_done(void *cb_arg)
205 {
206 *(bool *)cb_arg = true;
207 }
208
209 static void
210 spdk_fio_copy_fini_start(void *arg)
211 {
212 bool *done = arg;
213
214 spdk_copy_engine_finish(spdk_fio_bdev_fini_done, done);
215 }
216
217 static void
218 spdk_fio_bdev_fini_start(void *arg)
219 {
220 bool *done = arg;
221
222 spdk_bdev_finish(spdk_fio_copy_fini_start, done);
223 }
224
225 static void *
226 spdk_init_thread_poll(void *arg)
227 {
228 struct spdk_fio_options *eo = arg;
229 struct spdk_fio_thread *fio_thread;
230 struct spdk_conf *config;
231 struct spdk_env_opts opts;
232 bool done;
233 int rc;
234 struct timespec ts;
235 struct thread_data td = {};
236
237 /* Create a dummy thread data for use on the initialization thread. */
238 td.o.iodepth = 32;
239 td.eo = eo;
240
241 /* Parse the SPDK configuration file */
242 eo = arg;
243 if (!eo->conf || !strlen(eo->conf)) {
244 SPDK_ERRLOG("No configuration file provided\n");
245 rc = EINVAL;
246 goto err_exit;
247 }
248
249 config = spdk_conf_allocate();
250 if (!config) {
251 SPDK_ERRLOG("Unable to allocate configuration file\n");
252 rc = ENOMEM;
253 goto err_exit;
254 }
255
256 rc = spdk_conf_read(config, eo->conf);
257 if (rc != 0) {
258 SPDK_ERRLOG("Invalid configuration file format\n");
259 spdk_conf_free(config);
260 goto err_exit;
261 }
262 if (spdk_conf_first_section(config) == NULL) {
263 SPDK_ERRLOG("Invalid configuration file format\n");
264 spdk_conf_free(config);
265 rc = EINVAL;
266 goto err_exit;
267 }
268 spdk_conf_set_as_default(config);
269
270 /* Initialize the environment library */
271 spdk_env_opts_init(&opts);
272 opts.name = "fio";
273
274 if (eo->mem_mb) {
275 opts.mem_size = eo->mem_mb;
276 }
277 opts.hugepage_single_segments = eo->mem_single_seg;
278
279 if (spdk_env_init(&opts) < 0) {
280 SPDK_ERRLOG("Unable to initialize SPDK env\n");
281 spdk_conf_free(config);
282 rc = EINVAL;
283 goto err_exit;
284 }
285 spdk_unaffinitize_thread();
286
287 spdk_thread_lib_init(NULL, 0);
288
289 /* Create an SPDK thread temporarily */
290 rc = spdk_fio_init_thread(&td);
291 if (rc < 0) {
292 SPDK_ERRLOG("Failed to create initialization thread\n");
293 goto err_exit;
294 }
295
296 fio_thread = td.io_ops_data;
297
298 /* Initialize the bdev layer */
299 done = false;
300 spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_init_start, &done);
301
302 do {
303 spdk_fio_poll_thread(fio_thread);
304 } while (!done);
305
306 /*
307 * Continue polling until there are no more events.
308 * This handles any final events posted by pollers.
309 */
310 while (spdk_fio_poll_thread(fio_thread) > 0) {};
311
312 /* Set condition variable */
313 pthread_mutex_lock(&g_init_mtx);
314 pthread_cond_signal(&g_init_cond);
315
316 while (g_poll_loop) {
317 spdk_fio_poll_thread(fio_thread);
318
319 clock_gettime(CLOCK_MONOTONIC, &ts);
320 spdk_fio_calc_timeout(fio_thread, &ts);
321
322 rc = pthread_cond_timedwait(&g_init_cond, &g_init_mtx, &ts);
323 if (rc != ETIMEDOUT) {
324 break;
325 }
326 }
327
328 pthread_mutex_unlock(&g_init_mtx);
329
330 /* Finalize the bdev layer */
331 done = false;
332 spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_fini_start, &done);
333
334 do {
335 spdk_fio_poll_thread(fio_thread);
336 } while (!done && !spdk_thread_is_idle(fio_thread->thread));
337
338 spdk_fio_cleanup_thread(fio_thread);
339
340 pthread_exit(NULL);
341
342 err_exit:
343 exit(rc);
344 return NULL;
345 }
346
347 static int
348 spdk_fio_init_env(struct thread_data *td)
349 {
350 pthread_condattr_t attr;
351 int rc = -1;
352
353 if (pthread_condattr_init(&attr)) {
354 SPDK_ERRLOG("Unable to initialize condition variable\n");
355 return -1;
356 }
357
358 if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) {
359 SPDK_ERRLOG("Unable to initialize condition variable\n");
360 goto out;
361 }
362
363 if (pthread_cond_init(&g_init_cond, &attr)) {
364 SPDK_ERRLOG("Unable to initialize condition variable\n");
365 goto out;
366 }
367
368 /*
369 * Spawn a thread to handle initialization operations and to poll things
370 * like the admin queues periodically.
371 */
372 rc = pthread_create(&g_init_thread_id, NULL, &spdk_init_thread_poll, td->eo);
373 if (rc != 0) {
374 SPDK_ERRLOG("Unable to spawn thread to poll admin queue. It won't be polled.\n");
375 }
376
377 /* Wait for background thread to advance past the initialization */
378 pthread_mutex_lock(&g_init_mtx);
379 pthread_cond_wait(&g_init_cond, &g_init_mtx);
380 pthread_mutex_unlock(&g_init_mtx);
381 out:
382 pthread_condattr_destroy(&attr);
383 return rc;
384 }
385
386 /* Called for each thread to fill in the 'real_file_size' member for
387 * each file associated with this thread. This is called prior to
388 * the init operation (spdk_fio_init()) below. This call will occur
389 * on the initial start up thread if 'create_serialize' is true, or
390 * on the thread actually associated with 'thread_data' if 'create_serialize'
391 * is false.
392 */
393 static int
394 spdk_fio_setup(struct thread_data *td)
395 {
396 unsigned int i;
397 struct fio_file *f;
398
399 if (!td->o.use_thread) {
400 SPDK_ERRLOG("must set thread=1 when using spdk plugin\n");
401 return -1;
402 }
403
404 if (!g_spdk_env_initialized) {
405 if (spdk_fio_init_env(td)) {
406 SPDK_ERRLOG("failed to initialize\n");
407 return -1;
408 }
409
410 g_spdk_env_initialized = true;
411 }
412
413 for_each_file(td, f, i) {
414 struct spdk_bdev *bdev;
415
416 bdev = spdk_bdev_get_by_name(f->file_name);
417 if (!bdev) {
418 SPDK_ERRLOG("Unable to find bdev with name %s\n", f->file_name);
419 return -1;
420 }
421
422 f->real_file_size = spdk_bdev_get_num_blocks(bdev) *
423 spdk_bdev_get_block_size(bdev);
424
425 }
426
427 return 0;
428 }
429
430 static void
431 spdk_fio_bdev_open(void *arg)
432 {
433 struct thread_data *td = arg;
434 struct spdk_fio_thread *fio_thread;
435 unsigned int i;
436 struct fio_file *f;
437 int rc;
438
439 fio_thread = td->io_ops_data;
440
441 for_each_file(td, f, i) {
442 struct spdk_fio_target *target;
443
444 target = calloc(1, sizeof(*target));
445 if (!target) {
446 SPDK_ERRLOG("Unable to allocate memory for I/O target.\n");
447 fio_thread->failed = true;
448 return;
449 }
450
451 target->bdev = spdk_bdev_get_by_name(f->file_name);
452 if (!target->bdev) {
453 SPDK_ERRLOG("Unable to find bdev with name %s\n", f->file_name);
454 free(target);
455 fio_thread->failed = true;
456 return;
457 }
458
459 rc = spdk_bdev_open(target->bdev, true, NULL, NULL, &target->desc);
460 if (rc) {
461 SPDK_ERRLOG("Unable to open bdev %s\n", f->file_name);
462 free(target);
463 fio_thread->failed = true;
464 return;
465 }
466
467 target->ch = spdk_bdev_get_io_channel(target->desc);
468 if (!target->ch) {
469 SPDK_ERRLOG("Unable to get I/O channel for bdev.\n");
470 spdk_bdev_close(target->desc);
471 free(target);
472 fio_thread->failed = true;
473 return;
474 }
475
476 f->engine_data = target;
477
478 TAILQ_INSERT_TAIL(&fio_thread->targets, target, link);
479 }
480 }
481
482 /* Called for each thread, on that thread, shortly after the thread
483 * starts.
484 */
485 static int
486 spdk_fio_init(struct thread_data *td)
487 {
488 struct spdk_fio_thread *fio_thread;
489
490 spdk_fio_init_thread(td);
491
492 fio_thread = td->io_ops_data;
493 fio_thread->failed = false;
494
495 spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_open, td);
496
497 while (spdk_fio_poll_thread(fio_thread) > 0) {}
498
499 if (fio_thread->failed) {
500 return -1;
501 }
502
503 return 0;
504 }
505
506 static void
507 spdk_fio_cleanup(struct thread_data *td)
508 {
509 struct spdk_fio_thread *fio_thread = td->io_ops_data;
510
511 spdk_fio_cleanup_thread(fio_thread);
512 td->io_ops_data = NULL;
513 }
514
515 static int
516 spdk_fio_open(struct thread_data *td, struct fio_file *f)
517 {
518
519 return 0;
520 }
521
522 static int
523 spdk_fio_close(struct thread_data *td, struct fio_file *f)
524 {
525 return 0;
526 }
527
528 static int
529 spdk_fio_iomem_alloc(struct thread_data *td, size_t total_mem)
530 {
531 td->orig_buffer = spdk_dma_zmalloc(total_mem, 0x1000, NULL);
532 return td->orig_buffer == NULL;
533 }
534
535 static void
536 spdk_fio_iomem_free(struct thread_data *td)
537 {
538 spdk_dma_free(td->orig_buffer);
539 }
540
541 static int
542 spdk_fio_io_u_init(struct thread_data *td, struct io_u *io_u)
543 {
544 struct spdk_fio_request *fio_req;
545
546 fio_req = calloc(1, sizeof(*fio_req));
547 if (fio_req == NULL) {
548 return 1;
549 }
550 fio_req->io = io_u;
551 fio_req->td = td;
552
553 io_u->engine_data = fio_req;
554
555 return 0;
556 }
557
558 static void
559 spdk_fio_io_u_free(struct thread_data *td, struct io_u *io_u)
560 {
561 struct spdk_fio_request *fio_req = io_u->engine_data;
562
563 if (fio_req) {
564 assert(fio_req->io == io_u);
565 free(fio_req);
566 io_u->engine_data = NULL;
567 }
568 }
569
570 static void
571 spdk_fio_completion_cb(struct spdk_bdev_io *bdev_io,
572 bool success,
573 void *cb_arg)
574 {
575 struct spdk_fio_request *fio_req = cb_arg;
576 struct thread_data *td = fio_req->td;
577 struct spdk_fio_thread *fio_thread = td->io_ops_data;
578
579 assert(fio_thread->iocq_count < fio_thread->iocq_size);
580 fio_req->io->error = success ? 0 : EIO;
581 fio_thread->iocq[fio_thread->iocq_count++] = fio_req->io;
582
583 spdk_bdev_free_io(bdev_io);
584 }
585
586 #if FIO_IOOPS_VERSION >= 24
587 typedef enum fio_q_status fio_q_status_t;
588 #else
589 typedef int fio_q_status_t;
590 #endif
591
592 static fio_q_status_t
593 spdk_fio_queue(struct thread_data *td, struct io_u *io_u)
594 {
595 int rc = 1;
596 struct spdk_fio_request *fio_req = io_u->engine_data;
597 struct spdk_fio_target *target = io_u->file->engine_data;
598
599 assert(fio_req->td == td);
600
601 if (!target) {
602 SPDK_ERRLOG("Unable to look up correct I/O target.\n");
603 fio_req->io->error = ENODEV;
604 return FIO_Q_COMPLETED;
605 }
606
607 switch (io_u->ddir) {
608 case DDIR_READ:
609 rc = spdk_bdev_read(target->desc, target->ch,
610 io_u->buf, io_u->offset, io_u->xfer_buflen,
611 spdk_fio_completion_cb, fio_req);
612 break;
613 case DDIR_WRITE:
614 rc = spdk_bdev_write(target->desc, target->ch,
615 io_u->buf, io_u->offset, io_u->xfer_buflen,
616 spdk_fio_completion_cb, fio_req);
617 break;
618 case DDIR_TRIM:
619 rc = spdk_bdev_unmap(target->desc, target->ch,
620 io_u->offset, io_u->xfer_buflen,
621 spdk_fio_completion_cb, fio_req);
622 break;
623 default:
624 assert(false);
625 break;
626 }
627
628 if (rc == -ENOMEM) {
629 return FIO_Q_BUSY;
630 }
631
632 if (rc != 0) {
633 fio_req->io->error = abs(rc);
634 return FIO_Q_COMPLETED;
635 }
636
637 return FIO_Q_QUEUED;
638 }
639
640 static struct io_u *
641 spdk_fio_event(struct thread_data *td, int event)
642 {
643 struct spdk_fio_thread *fio_thread = td->io_ops_data;
644
645 assert(event >= 0);
646 assert((unsigned)event < fio_thread->iocq_count);
647 return fio_thread->iocq[event];
648 }
649
650 static size_t
651 spdk_fio_poll_thread(struct spdk_fio_thread *fio_thread)
652 {
653 return spdk_thread_poll(fio_thread->thread, 0, 0);
654 }
655
656 static int
657 spdk_fio_getevents(struct thread_data *td, unsigned int min,
658 unsigned int max, const struct timespec *t)
659 {
660 struct spdk_fio_thread *fio_thread = td->io_ops_data;
661 struct timespec t0, t1;
662 uint64_t timeout = 0;
663
664 if (t) {
665 timeout = t->tv_sec * SPDK_SEC_TO_NSEC + t->tv_nsec;
666 clock_gettime(CLOCK_MONOTONIC_RAW, &t0);
667 }
668
669 fio_thread->iocq_count = 0;
670
671 for (;;) {
672 spdk_fio_poll_thread(fio_thread);
673
674 if (fio_thread->iocq_count >= min) {
675 return fio_thread->iocq_count;
676 }
677
678 if (t) {
679 clock_gettime(CLOCK_MONOTONIC_RAW, &t1);
680 uint64_t elapse = ((t1.tv_sec - t0.tv_sec) * SPDK_SEC_TO_NSEC)
681 + t1.tv_nsec - t0.tv_nsec;
682 if (elapse > timeout) {
683 break;
684 }
685 }
686 }
687
688 return fio_thread->iocq_count;
689 }
690
691 static int
692 spdk_fio_invalidate(struct thread_data *td, struct fio_file *f)
693 {
694 /* TODO: This should probably send a flush to the device, but for now just return successful. */
695 return 0;
696 }
697
698 static struct fio_option options[] = {
699 {
700 .name = "spdk_conf",
701 .lname = "SPDK configuration file",
702 .type = FIO_OPT_STR_STORE,
703 .off1 = offsetof(struct spdk_fio_options, conf),
704 .help = "A SPDK configuration file",
705 .category = FIO_OPT_C_ENGINE,
706 .group = FIO_OPT_G_INVALID,
707 },
708 {
709 .name = "spdk_mem",
710 .lname = "SPDK memory in MB",
711 .type = FIO_OPT_INT,
712 .off1 = offsetof(struct spdk_fio_options, mem_mb),
713 .help = "Amount of memory in MB to allocate for SPDK",
714 .category = FIO_OPT_C_ENGINE,
715 .group = FIO_OPT_G_INVALID,
716 },
717 {
718 .name = "spdk_single_seg",
719 .lname = "SPDK switch to create just a single hugetlbfs file",
720 .type = FIO_OPT_BOOL,
721 .off1 = offsetof(struct spdk_fio_options, mem_single_seg),
722 .help = "If set to 1, SPDK will use just a single hugetlbfs file",
723 .category = FIO_OPT_C_ENGINE,
724 .group = FIO_OPT_G_INVALID,
725 },
726 {
727 .name = NULL,
728 },
729 };
730
731 /* FIO imports this structure using dlsym */
732 struct ioengine_ops ioengine = {
733 .name = "spdk_bdev",
734 .version = FIO_IOOPS_VERSION,
735 .flags = FIO_RAWIO | FIO_NOEXTEND | FIO_NODISKUTIL | FIO_MEMALIGN,
736 .setup = spdk_fio_setup,
737 .init = spdk_fio_init,
738 /* .prep = unused, */
739 .queue = spdk_fio_queue,
740 /* .commit = unused, */
741 .getevents = spdk_fio_getevents,
742 .event = spdk_fio_event,
743 /* .errdetails = unused, */
744 /* .cancel = unused, */
745 .cleanup = spdk_fio_cleanup,
746 .open_file = spdk_fio_open,
747 .close_file = spdk_fio_close,
748 .invalidate = spdk_fio_invalidate,
749 /* .unlink_file = unused, */
750 /* .get_file_size = unused, */
751 /* .terminate = unused, */
752 .iomem_alloc = spdk_fio_iomem_alloc,
753 .iomem_free = spdk_fio_iomem_free,
754 .io_u_init = spdk_fio_io_u_init,
755 .io_u_free = spdk_fio_io_u_free,
756 .option_struct_size = sizeof(struct spdk_fio_options),
757 .options = options,
758 };
759
760 static void fio_init spdk_fio_register(void)
761 {
762 register_ioengine(&ioengine);
763 }
764
765 static void
766 spdk_fio_finish_env(void)
767 {
768 pthread_mutex_lock(&g_init_mtx);
769 g_poll_loop = false;
770 pthread_cond_signal(&g_init_cond);
771 pthread_mutex_unlock(&g_init_mtx);
772 pthread_join(g_init_thread_id, NULL);
773
774 spdk_thread_lib_fini();
775 }
776
777 static void fio_exit spdk_fio_unregister(void)
778 {
779 if (g_spdk_env_initialized) {
780 spdk_fio_finish_env();
781 g_spdk_env_initialized = false;
782 }
783 unregister_ioengine(&ioengine);
784 }