4 * Copyright (c) Intel Corporation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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.
34 #include "spdk/stdinc.h"
36 #include "spdk/bdev.h"
37 #include "spdk/copy_engine.h"
38 #include "spdk/conf.h"
40 #include "spdk/thread.h"
42 #include "spdk/string.h"
43 #include "spdk/queue.h"
44 #include "spdk/util.h"
46 #include "spdk_internal/thread.h"
48 #include "config-host.h"
52 struct spdk_fio_options
{
59 struct spdk_fio_request
{
61 struct thread_data
*td
;
64 struct spdk_fio_target
{
65 struct spdk_bdev
*bdev
;
66 struct spdk_bdev_desc
*desc
;
67 struct spdk_io_channel
*ch
;
69 TAILQ_ENTRY(spdk_fio_target
) link
;
72 struct spdk_fio_thread
{
73 struct thread_data
*td
; /* fio thread context */
74 struct spdk_thread
*thread
; /* spdk thread context */
76 TAILQ_HEAD(, spdk_fio_target
) targets
;
77 bool failed
; /* true if the thread failed to initialize */
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 */
84 static bool g_spdk_env_initialized
= false;
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
);
90 /* Default polling timeout (ns) */
91 #define SPDK_FIO_POLLING_TIMEOUT 1000000000ULL
94 spdk_fio_init_thread(struct thread_data
*td
)
96 struct spdk_fio_thread
*fio_thread
;
98 fio_thread
= calloc(1, sizeof(*fio_thread
));
100 SPDK_ERRLOG("failed to allocate thread local context\n");
105 td
->io_ops_data
= fio_thread
;
107 fio_thread
->thread
= spdk_thread_create("fio_thread", NULL
);
108 if (!fio_thread
->thread
) {
110 SPDK_ERRLOG("failed to allocate thread\n");
113 spdk_set_thread(fio_thread
->thread
);
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
);
119 TAILQ_INIT(&fio_thread
->targets
);
125 spdk_fio_bdev_close_targets(void *arg
)
127 struct spdk_fio_thread
*fio_thread
= arg
;
128 struct spdk_fio_target
*target
, *tmp
;
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
);
139 spdk_fio_cleanup_thread(struct spdk_fio_thread
*fio_thread
)
141 spdk_thread_send_msg(fio_thread
->thread
, spdk_fio_bdev_close_targets
, fio_thread
);
143 while (!spdk_thread_is_idle(fio_thread
->thread
)) {
144 spdk_fio_poll_thread(fio_thread
);
147 spdk_set_thread(fio_thread
->thread
);
149 spdk_thread_exit(fio_thread
->thread
);
150 spdk_thread_destroy(fio_thread
->thread
);
151 free(fio_thread
->iocq
);
156 spdk_fio_calc_timeout(struct spdk_fio_thread
*fio_thread
, struct timespec
*ts
)
158 uint64_t timeout
, now
;
160 if (spdk_thread_has_active_pollers(fio_thread
->thread
)) {
164 timeout
= spdk_thread_next_poller_expiration(fio_thread
->thread
);
165 now
= spdk_get_ticks();
168 timeout
= now
+ (SPDK_FIO_POLLING_TIMEOUT
* spdk_get_ticks_hz()) / SPDK_SEC_TO_NSEC
;
172 timeout
= ((timeout
- now
) * SPDK_SEC_TO_NSEC
) / spdk_get_ticks_hz() +
173 ts
->tv_sec
* SPDK_SEC_TO_NSEC
+ ts
->tv_nsec
;
175 ts
->tv_sec
= timeout
/ SPDK_SEC_TO_NSEC
;
176 ts
->tv_nsec
= timeout
% SPDK_SEC_TO_NSEC
;
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;
186 spdk_fio_bdev_init_done(void *cb_arg
, int rc
)
188 *(bool *)cb_arg
= true;
192 spdk_fio_bdev_init_start(void *arg
)
196 /* Initialize the copy engine */
197 spdk_copy_engine_initialize();
199 /* Initialize the bdev layer */
200 spdk_bdev_initialize(spdk_fio_bdev_init_done
, done
);
204 spdk_fio_bdev_fini_done(void *cb_arg
)
206 *(bool *)cb_arg
= true;
210 spdk_fio_copy_fini_start(void *arg
)
214 spdk_copy_engine_finish(spdk_fio_bdev_fini_done
, done
);
218 spdk_fio_bdev_fini_start(void *arg
)
222 spdk_bdev_finish(spdk_fio_copy_fini_start
, done
);
226 spdk_init_thread_poll(void *arg
)
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
;
235 struct thread_data td
= {};
237 /* Create a dummy thread data for use on the initialization thread. */
241 /* Parse the SPDK configuration file */
243 if (!eo
->conf
|| !strlen(eo
->conf
)) {
244 SPDK_ERRLOG("No configuration file provided\n");
249 config
= spdk_conf_allocate();
251 SPDK_ERRLOG("Unable to allocate configuration file\n");
256 rc
= spdk_conf_read(config
, eo
->conf
);
258 SPDK_ERRLOG("Invalid configuration file format\n");
259 spdk_conf_free(config
);
262 if (spdk_conf_first_section(config
) == NULL
) {
263 SPDK_ERRLOG("Invalid configuration file format\n");
264 spdk_conf_free(config
);
268 spdk_conf_set_as_default(config
);
270 /* Initialize the environment library */
271 spdk_env_opts_init(&opts
);
275 opts
.mem_size
= eo
->mem_mb
;
277 opts
.hugepage_single_segments
= eo
->mem_single_seg
;
279 if (spdk_env_init(&opts
) < 0) {
280 SPDK_ERRLOG("Unable to initialize SPDK env\n");
281 spdk_conf_free(config
);
285 spdk_unaffinitize_thread();
287 spdk_thread_lib_init(NULL
, 0);
289 /* Create an SPDK thread temporarily */
290 rc
= spdk_fio_init_thread(&td
);
292 SPDK_ERRLOG("Failed to create initialization thread\n");
296 fio_thread
= td
.io_ops_data
;
298 /* Initialize the bdev layer */
300 spdk_thread_send_msg(fio_thread
->thread
, spdk_fio_bdev_init_start
, &done
);
303 spdk_fio_poll_thread(fio_thread
);
307 * Continue polling until there are no more events.
308 * This handles any final events posted by pollers.
310 while (spdk_fio_poll_thread(fio_thread
) > 0) {};
312 /* Set condition variable */
313 pthread_mutex_lock(&g_init_mtx
);
314 pthread_cond_signal(&g_init_cond
);
316 while (g_poll_loop
) {
317 spdk_fio_poll_thread(fio_thread
);
319 clock_gettime(CLOCK_MONOTONIC
, &ts
);
320 spdk_fio_calc_timeout(fio_thread
, &ts
);
322 rc
= pthread_cond_timedwait(&g_init_cond
, &g_init_mtx
, &ts
);
323 if (rc
!= ETIMEDOUT
) {
328 pthread_mutex_unlock(&g_init_mtx
);
330 /* Finalize the bdev layer */
332 spdk_thread_send_msg(fio_thread
->thread
, spdk_fio_bdev_fini_start
, &done
);
335 spdk_fio_poll_thread(fio_thread
);
336 } while (!done
&& !spdk_thread_is_idle(fio_thread
->thread
));
338 spdk_fio_cleanup_thread(fio_thread
);
348 spdk_fio_init_env(struct thread_data
*td
)
350 pthread_condattr_t attr
;
353 if (pthread_condattr_init(&attr
)) {
354 SPDK_ERRLOG("Unable to initialize condition variable\n");
358 if (pthread_condattr_setclock(&attr
, CLOCK_MONOTONIC
)) {
359 SPDK_ERRLOG("Unable to initialize condition variable\n");
363 if (pthread_cond_init(&g_init_cond
, &attr
)) {
364 SPDK_ERRLOG("Unable to initialize condition variable\n");
369 * Spawn a thread to handle initialization operations and to poll things
370 * like the admin queues periodically.
372 rc
= pthread_create(&g_init_thread_id
, NULL
, &spdk_init_thread_poll
, td
->eo
);
374 SPDK_ERRLOG("Unable to spawn thread to poll admin queue. It won't be polled.\n");
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
);
382 pthread_condattr_destroy(&attr
);
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'
394 spdk_fio_setup(struct thread_data
*td
)
399 if (!td
->o
.use_thread
) {
400 SPDK_ERRLOG("must set thread=1 when using spdk plugin\n");
404 if (!g_spdk_env_initialized
) {
405 if (spdk_fio_init_env(td
)) {
406 SPDK_ERRLOG("failed to initialize\n");
410 g_spdk_env_initialized
= true;
413 for_each_file(td
, f
, i
) {
414 struct spdk_bdev
*bdev
;
416 bdev
= spdk_bdev_get_by_name(f
->file_name
);
418 SPDK_ERRLOG("Unable to find bdev with name %s\n", f
->file_name
);
422 f
->real_file_size
= spdk_bdev_get_num_blocks(bdev
) *
423 spdk_bdev_get_block_size(bdev
);
431 spdk_fio_bdev_open(void *arg
)
433 struct thread_data
*td
= arg
;
434 struct spdk_fio_thread
*fio_thread
;
439 fio_thread
= td
->io_ops_data
;
441 for_each_file(td
, f
, i
) {
442 struct spdk_fio_target
*target
;
444 target
= calloc(1, sizeof(*target
));
446 SPDK_ERRLOG("Unable to allocate memory for I/O target.\n");
447 fio_thread
->failed
= true;
451 target
->bdev
= spdk_bdev_get_by_name(f
->file_name
);
453 SPDK_ERRLOG("Unable to find bdev with name %s\n", f
->file_name
);
455 fio_thread
->failed
= true;
459 rc
= spdk_bdev_open(target
->bdev
, true, NULL
, NULL
, &target
->desc
);
461 SPDK_ERRLOG("Unable to open bdev %s\n", f
->file_name
);
463 fio_thread
->failed
= true;
467 target
->ch
= spdk_bdev_get_io_channel(target
->desc
);
469 SPDK_ERRLOG("Unable to get I/O channel for bdev.\n");
470 spdk_bdev_close(target
->desc
);
472 fio_thread
->failed
= true;
476 f
->engine_data
= target
;
478 TAILQ_INSERT_TAIL(&fio_thread
->targets
, target
, link
);
482 /* Called for each thread, on that thread, shortly after the thread
486 spdk_fio_init(struct thread_data
*td
)
488 struct spdk_fio_thread
*fio_thread
;
490 spdk_fio_init_thread(td
);
492 fio_thread
= td
->io_ops_data
;
493 fio_thread
->failed
= false;
495 spdk_thread_send_msg(fio_thread
->thread
, spdk_fio_bdev_open
, td
);
497 while (spdk_fio_poll_thread(fio_thread
) > 0) {}
499 if (fio_thread
->failed
) {
507 spdk_fio_cleanup(struct thread_data
*td
)
509 struct spdk_fio_thread
*fio_thread
= td
->io_ops_data
;
511 spdk_fio_cleanup_thread(fio_thread
);
512 td
->io_ops_data
= NULL
;
516 spdk_fio_open(struct thread_data
*td
, struct fio_file
*f
)
523 spdk_fio_close(struct thread_data
*td
, struct fio_file
*f
)
529 spdk_fio_iomem_alloc(struct thread_data
*td
, size_t total_mem
)
531 td
->orig_buffer
= spdk_dma_zmalloc(total_mem
, 0x1000, NULL
);
532 return td
->orig_buffer
== NULL
;
536 spdk_fio_iomem_free(struct thread_data
*td
)
538 spdk_dma_free(td
->orig_buffer
);
542 spdk_fio_io_u_init(struct thread_data
*td
, struct io_u
*io_u
)
544 struct spdk_fio_request
*fio_req
;
546 fio_req
= calloc(1, sizeof(*fio_req
));
547 if (fio_req
== NULL
) {
553 io_u
->engine_data
= fio_req
;
559 spdk_fio_io_u_free(struct thread_data
*td
, struct io_u
*io_u
)
561 struct spdk_fio_request
*fio_req
= io_u
->engine_data
;
564 assert(fio_req
->io
== io_u
);
566 io_u
->engine_data
= NULL
;
571 spdk_fio_completion_cb(struct spdk_bdev_io
*bdev_io
,
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
;
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
;
583 spdk_bdev_free_io(bdev_io
);
586 #if FIO_IOOPS_VERSION >= 24
587 typedef enum fio_q_status fio_q_status_t
;
589 typedef int fio_q_status_t
;
592 static fio_q_status_t
593 spdk_fio_queue(struct thread_data
*td
, struct io_u
*io_u
)
596 struct spdk_fio_request
*fio_req
= io_u
->engine_data
;
597 struct spdk_fio_target
*target
= io_u
->file
->engine_data
;
599 assert(fio_req
->td
== td
);
602 SPDK_ERRLOG("Unable to look up correct I/O target.\n");
603 fio_req
->io
->error
= ENODEV
;
604 return FIO_Q_COMPLETED
;
607 switch (io_u
->ddir
) {
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
);
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
);
619 rc
= spdk_bdev_unmap(target
->desc
, target
->ch
,
620 io_u
->offset
, io_u
->xfer_buflen
,
621 spdk_fio_completion_cb
, fio_req
);
633 fio_req
->io
->error
= abs(rc
);
634 return FIO_Q_COMPLETED
;
641 spdk_fio_event(struct thread_data
*td
, int event
)
643 struct spdk_fio_thread
*fio_thread
= td
->io_ops_data
;
646 assert((unsigned)event
< fio_thread
->iocq_count
);
647 return fio_thread
->iocq
[event
];
651 spdk_fio_poll_thread(struct spdk_fio_thread
*fio_thread
)
653 return spdk_thread_poll(fio_thread
->thread
, 0, 0);
657 spdk_fio_getevents(struct thread_data
*td
, unsigned int min
,
658 unsigned int max
, const struct timespec
*t
)
660 struct spdk_fio_thread
*fio_thread
= td
->io_ops_data
;
661 struct timespec t0
, t1
;
662 uint64_t timeout
= 0;
665 timeout
= t
->tv_sec
* SPDK_SEC_TO_NSEC
+ t
->tv_nsec
;
666 clock_gettime(CLOCK_MONOTONIC_RAW
, &t0
);
669 fio_thread
->iocq_count
= 0;
672 spdk_fio_poll_thread(fio_thread
);
674 if (fio_thread
->iocq_count
>= min
) {
675 return fio_thread
->iocq_count
;
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
) {
688 return fio_thread
->iocq_count
;
692 spdk_fio_invalidate(struct thread_data
*td
, struct fio_file
*f
)
694 /* TODO: This should probably send a flush to the device, but for now just return successful. */
698 static struct fio_option options
[] = {
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
,
710 .lname
= "SPDK memory in MB",
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
,
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
,
731 /* FIO imports this structure using dlsym */
732 struct ioengine_ops ioengine
= {
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
),
760 static void fio_init
spdk_fio_register(void)
762 register_ioengine(&ioengine
);
766 spdk_fio_finish_env(void)
768 pthread_mutex_lock(&g_init_mtx
);
770 pthread_cond_signal(&g_init_cond
);
771 pthread_mutex_unlock(&g_init_mtx
);
772 pthread_join(g_init_thread_id
, NULL
);
774 spdk_thread_lib_fini();
777 static void fio_exit
spdk_fio_unregister(void)
779 if (g_spdk_env_initialized
) {
780 spdk_fio_finish_env();
781 g_spdk_env_initialized
= false;
783 unregister_ioengine(&ioengine
);