1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2017 Intel Corporation. 3 * All rights reserved. 4 * Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 5 */ 6 7 #include "spdk/stdinc.h" 8 9 #include "spdk/bdev.h" 10 #include "spdk/bdev_zone.h" 11 #include "spdk/accel.h" 12 #include "spdk/env.h" 13 #include "spdk/init.h" 14 #include "spdk/thread.h" 15 #include "spdk/log.h" 16 #include "spdk/string.h" 17 #include "spdk/queue.h" 18 #include "spdk/util.h" 19 #include "spdk/rpc.h" 20 21 #include "spdk_internal/event.h" 22 23 #include "config-host.h" 24 #include "fio.h" 25 #include "optgroup.h" 26 27 #ifdef for_each_rw_ddir 28 #define FIO_HAS_ZBD (FIO_IOOPS_VERSION >= 26) 29 #else 30 #define FIO_HAS_ZBD (0) 31 #endif 32 33 /* FreeBSD is missing CLOCK_MONOTONIC_RAW, 34 * so alternative is provided. */ 35 #ifndef CLOCK_MONOTONIC_RAW /* Defined in glibc bits/time.h */ 36 #define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC 37 #endif 38 39 struct spdk_fio_options { 40 void *pad; 41 char *conf; 42 char *json_conf; 43 char *env_context; 44 char *log_flags; 45 unsigned mem_mb; 46 int mem_single_seg; 47 int initial_zone_reset; 48 int zone_append; 49 char *rpc_listen_addr; 50 }; 51 52 struct spdk_fio_request { 53 struct io_u *io; 54 struct thread_data *td; 55 }; 56 57 struct spdk_fio_target { 58 struct spdk_bdev *bdev; 59 struct spdk_bdev_desc *desc; 60 struct spdk_io_channel *ch; 61 bool zone_append_enabled; 62 63 TAILQ_ENTRY(spdk_fio_target) link; 64 }; 65 66 struct spdk_fio_thread { 67 struct thread_data *td; /* fio thread context */ 68 struct spdk_thread *thread; /* spdk thread context */ 69 70 TAILQ_HEAD(, spdk_fio_target) targets; 71 bool failed; /* true if the thread failed to initialize */ 72 73 struct io_u **iocq; /* io completion queue */ 74 unsigned int iocq_count; /* number of iocq entries filled by last getevents */ 75 unsigned int iocq_size; /* number of iocq entries allocated */ 76 77 TAILQ_ENTRY(spdk_fio_thread) link; 78 }; 79 80 struct spdk_fio_zone_cb_arg { 81 struct spdk_fio_target *target; 82 struct spdk_bdev_zone_info *spdk_zones; 83 int completed; 84 uint64_t offset_blocks; 85 struct zbd_zone *fio_zones; 86 unsigned int nr_zones; 87 }; 88 89 /* On App Thread (oat) context used for making sync calls from async calls. */ 90 struct spdk_fio_oat_ctx { 91 union { 92 struct spdk_fio_setup_args { 93 struct thread_data *td; 94 } sa; 95 struct spdk_fio_bdev_get_zoned_model_args { 96 struct fio_file *f; 97 enum zbd_zoned_model *model; 98 } zma; 99 struct spdk_fio_bdev_get_max_open_zones_args { 100 struct fio_file *f; 101 unsigned int *max_open_zones; 102 } moza; 103 } u; 104 pthread_mutex_t mutex; 105 pthread_cond_t cond; 106 int ret; 107 }; 108 109 static bool g_spdk_env_initialized = false; 110 static const char *g_json_config_file = NULL; 111 static const char *g_rpc_listen_addr = NULL; 112 113 static int spdk_fio_init(struct thread_data *td); 114 static void spdk_fio_cleanup(struct thread_data *td); 115 static size_t spdk_fio_poll_thread(struct spdk_fio_thread *fio_thread); 116 static int spdk_fio_handle_options(struct thread_data *td, struct fio_file *f, 117 struct spdk_bdev *bdev); 118 static int spdk_fio_handle_options_per_target(struct thread_data *td, struct fio_file *f); 119 static void spdk_fio_setup_oat(void *ctx); 120 121 static pthread_t g_init_thread_id = 0; 122 static pthread_mutex_t g_init_mtx = PTHREAD_MUTEX_INITIALIZER; 123 static pthread_cond_t g_init_cond; 124 static bool g_poll_loop = true; 125 static TAILQ_HEAD(, spdk_fio_thread) g_threads = TAILQ_HEAD_INITIALIZER(g_threads); 126 127 /* Default polling timeout (ns) */ 128 #define SPDK_FIO_POLLING_TIMEOUT 1000000000ULL 129 130 static __thread bool g_internal_thread = false; 131 132 /* Run msg_fn on app thread ("oat") and wait for it to call spdk_fio_wake_oat_waiter() */ 133 static void 134 spdk_fio_sync_run_oat(void (*msg_fn)(void *), struct spdk_fio_oat_ctx *ctx) 135 { 136 assert(!spdk_thread_is_app_thread(NULL)); 137 138 pthread_mutex_init(&ctx->mutex, NULL); 139 pthread_cond_init(&ctx->cond, NULL); 140 pthread_mutex_lock(&ctx->mutex); 141 142 spdk_thread_send_msg(spdk_thread_get_app_thread(), msg_fn, ctx); 143 144 /* Wake up the poll loop in spdk_init_thread_poll() */ 145 pthread_mutex_lock(&g_init_mtx); 146 pthread_cond_signal(&g_init_cond); 147 pthread_mutex_unlock(&g_init_mtx); 148 149 /* Wait for msg_fn() to call spdk_fio_wake_oat_waiter() */ 150 pthread_cond_wait(&ctx->cond, &ctx->mutex); 151 pthread_mutex_unlock(&ctx->mutex); 152 153 pthread_mutex_destroy(&ctx->mutex); 154 pthread_cond_destroy(&ctx->cond); 155 } 156 157 static void 158 spdk_fio_wake_oat_waiter(struct spdk_fio_oat_ctx *ctx) 159 { 160 pthread_mutex_lock(&ctx->mutex); 161 pthread_cond_signal(&ctx->cond); 162 pthread_mutex_unlock(&ctx->mutex); 163 } 164 165 static int 166 spdk_fio_schedule_thread(struct spdk_thread *thread) 167 { 168 struct spdk_fio_thread *fio_thread; 169 170 if (g_internal_thread) { 171 /* Do nothing. */ 172 return 0; 173 } 174 175 fio_thread = spdk_thread_get_ctx(thread); 176 177 pthread_mutex_lock(&g_init_mtx); 178 TAILQ_INSERT_TAIL(&g_threads, fio_thread, link); 179 pthread_mutex_unlock(&g_init_mtx); 180 181 return 0; 182 } 183 184 static int 185 spdk_fio_init_thread(struct thread_data *td) 186 { 187 struct spdk_fio_thread *fio_thread; 188 struct spdk_thread *thread; 189 190 g_internal_thread = true; 191 thread = spdk_thread_create("fio_thread", NULL); 192 g_internal_thread = false; 193 if (!thread) { 194 SPDK_ERRLOG("failed to allocate thread\n"); 195 return -1; 196 } 197 198 fio_thread = spdk_thread_get_ctx(thread); 199 fio_thread->td = td; 200 fio_thread->thread = thread; 201 td->io_ops_data = fio_thread; 202 203 spdk_set_thread(thread); 204 205 fio_thread->iocq_size = td->o.iodepth; 206 fio_thread->iocq = calloc(fio_thread->iocq_size, sizeof(struct io_u *)); 207 assert(fio_thread->iocq != NULL); 208 209 TAILQ_INIT(&fio_thread->targets); 210 211 return 0; 212 } 213 214 static void 215 spdk_fio_bdev_close_targets(void *arg) 216 { 217 struct spdk_fio_thread *fio_thread = arg; 218 struct spdk_fio_target *target, *tmp; 219 220 TAILQ_FOREACH_SAFE(target, &fio_thread->targets, link, tmp) { 221 TAILQ_REMOVE(&fio_thread->targets, target, link); 222 spdk_put_io_channel(target->ch); 223 spdk_bdev_close(target->desc); 224 free(target); 225 } 226 } 227 228 static void 229 spdk_fio_cleanup_thread(struct spdk_fio_thread *fio_thread) 230 { 231 spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_close_targets, fio_thread); 232 233 pthread_mutex_lock(&g_init_mtx); 234 TAILQ_INSERT_TAIL(&g_threads, fio_thread, link); 235 pthread_mutex_unlock(&g_init_mtx); 236 } 237 238 static void 239 spdk_fio_calc_timeout(struct spdk_fio_thread *fio_thread, struct timespec *ts) 240 { 241 uint64_t timeout, now; 242 243 if (spdk_thread_has_active_pollers(fio_thread->thread)) { 244 return; 245 } 246 247 timeout = spdk_thread_next_poller_expiration(fio_thread->thread); 248 now = spdk_get_ticks(); 249 250 if (timeout == 0) { 251 timeout = now + (SPDK_FIO_POLLING_TIMEOUT * spdk_get_ticks_hz()) / SPDK_SEC_TO_NSEC; 252 } 253 254 if (timeout > now) { 255 timeout = ((timeout - now) * SPDK_SEC_TO_NSEC) / spdk_get_ticks_hz() + 256 ts->tv_sec * SPDK_SEC_TO_NSEC + ts->tv_nsec; 257 258 ts->tv_sec = timeout / SPDK_SEC_TO_NSEC; 259 ts->tv_nsec = timeout % SPDK_SEC_TO_NSEC; 260 } 261 } 262 263 static void 264 spdk_fio_bdev_init_done(int rc, void *cb_arg) 265 { 266 *(bool *)cb_arg = true; 267 268 if (g_rpc_listen_addr != NULL) { 269 if (spdk_rpc_initialize(g_rpc_listen_addr, NULL) == 0) { 270 spdk_rpc_set_state(SPDK_RPC_RUNTIME); 271 } 272 } 273 } 274 275 static void 276 spdk_fio_bdev_init_start(void *arg) 277 { 278 bool *done = arg; 279 280 spdk_subsystem_init_from_json_config(g_json_config_file, SPDK_DEFAULT_RPC_ADDR, 281 spdk_fio_bdev_init_done, done, true); 282 } 283 284 static void 285 spdk_fio_bdev_fini_done(void *cb_arg) 286 { 287 *(bool *)cb_arg = true; 288 289 spdk_rpc_finish(); 290 } 291 292 static void 293 spdk_fio_bdev_fini_start(void *arg) 294 { 295 bool *done = arg; 296 297 spdk_subsystem_fini(spdk_fio_bdev_fini_done, done); 298 } 299 300 static void * 301 spdk_init_thread_poll(void *arg) 302 { 303 struct spdk_fio_options *eo = arg; 304 struct spdk_fio_thread *fio_thread; 305 struct spdk_fio_thread *thread, *tmp; 306 struct spdk_env_opts opts; 307 bool done; 308 int rc; 309 struct timespec ts; 310 struct thread_data td = {}; 311 312 /* Create a dummy thread data for use on the initialization thread. */ 313 td.o.iodepth = 32; 314 td.eo = eo; 315 316 /* Parse the SPDK configuration file */ 317 eo = arg; 318 319 if (eo->conf && eo->json_conf) { 320 SPDK_ERRLOG("Cannot provide two types of configuration files\n"); 321 rc = EINVAL; 322 goto err_exit; 323 } else if (eo->conf && strlen(eo->conf)) { 324 g_json_config_file = eo->conf; 325 } else if (eo->json_conf && strlen(eo->json_conf)) { 326 g_json_config_file = eo->json_conf; 327 } else { 328 SPDK_ERRLOG("No configuration file provided\n"); 329 rc = EINVAL; 330 goto err_exit; 331 } 332 333 /* Initialize the RPC listen address */ 334 if (eo->rpc_listen_addr) { 335 g_rpc_listen_addr = eo->rpc_listen_addr; 336 } 337 338 /* Initialize the environment library */ 339 opts.opts_size = sizeof(opts); 340 spdk_env_opts_init(&opts); 341 opts.name = "fio"; 342 343 if (eo->mem_mb) { 344 opts.mem_size = eo->mem_mb; 345 } 346 opts.hugepage_single_segments = eo->mem_single_seg; 347 if (eo->env_context) { 348 opts.env_context = eo->env_context; 349 } 350 351 if (spdk_env_init(&opts) < 0) { 352 SPDK_ERRLOG("Unable to initialize SPDK env\n"); 353 rc = EINVAL; 354 goto err_exit; 355 } 356 spdk_unaffinitize_thread(); 357 358 if (eo->log_flags) { 359 char *tok = strtok(eo->log_flags, ","); 360 do { 361 rc = spdk_log_set_flag(tok); 362 if (rc < 0) { 363 SPDK_ERRLOG("unknown spdk log flag %s\n", tok); 364 rc = EINVAL; 365 goto err_exit; 366 } 367 } while ((tok = strtok(NULL, ",")) != NULL); 368 #ifdef DEBUG 369 spdk_log_set_print_level(SPDK_LOG_DEBUG); 370 #endif 371 } 372 373 spdk_thread_lib_init(spdk_fio_schedule_thread, sizeof(struct spdk_fio_thread)); 374 375 /* Create an SPDK thread temporarily */ 376 rc = spdk_fio_init_thread(&td); 377 if (rc < 0) { 378 SPDK_ERRLOG("Failed to create initialization thread\n"); 379 goto err_exit; 380 } 381 382 fio_thread = td.io_ops_data; 383 384 /* Initialize the bdev layer */ 385 done = false; 386 spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_init_start, &done); 387 388 do { 389 spdk_fio_poll_thread(fio_thread); 390 } while (!done); 391 392 /* 393 * Continue polling until there are no more events. 394 * This handles any final events posted by pollers. 395 */ 396 while (spdk_fio_poll_thread(fio_thread) > 0) {}; 397 398 /* Set condition variable */ 399 pthread_mutex_lock(&g_init_mtx); 400 pthread_cond_signal(&g_init_cond); 401 402 pthread_mutex_unlock(&g_init_mtx); 403 404 while (g_poll_loop) { 405 spdk_fio_poll_thread(fio_thread); 406 407 pthread_mutex_lock(&g_init_mtx); 408 if (!TAILQ_EMPTY(&g_threads)) { 409 TAILQ_FOREACH_SAFE(thread, &g_threads, link, tmp) { 410 if (spdk_thread_is_exited(thread->thread)) { 411 TAILQ_REMOVE(&g_threads, thread, link); 412 free(thread->iocq); 413 spdk_thread_destroy(thread->thread); 414 } else { 415 spdk_fio_poll_thread(thread); 416 } 417 } 418 419 /* If there are exiting threads to poll, don't sleep. */ 420 pthread_mutex_unlock(&g_init_mtx); 421 continue; 422 } 423 424 /* Figure out how long to sleep. */ 425 clock_gettime(CLOCK_MONOTONIC, &ts); 426 spdk_fio_calc_timeout(fio_thread, &ts); 427 428 rc = pthread_cond_timedwait(&g_init_cond, &g_init_mtx, &ts); 429 pthread_mutex_unlock(&g_init_mtx); 430 431 if (rc != 0 && rc != ETIMEDOUT) { 432 break; 433 } 434 } 435 436 spdk_fio_cleanup_thread(fio_thread); 437 438 /* Finalize the bdev layer */ 439 done = false; 440 spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_fini_start, &done); 441 442 do { 443 spdk_fio_poll_thread(fio_thread); 444 445 TAILQ_FOREACH_SAFE(thread, &g_threads, link, tmp) { 446 spdk_fio_poll_thread(thread); 447 } 448 } while (!done); 449 450 /* Now exit all the threads */ 451 TAILQ_FOREACH(thread, &g_threads, link) { 452 spdk_set_thread(thread->thread); 453 spdk_thread_exit(thread->thread); 454 spdk_set_thread(NULL); 455 } 456 457 /* And wait for them to gracefully exit */ 458 while (!TAILQ_EMPTY(&g_threads)) { 459 TAILQ_FOREACH_SAFE(thread, &g_threads, link, tmp) { 460 if (spdk_thread_is_exited(thread->thread)) { 461 TAILQ_REMOVE(&g_threads, thread, link); 462 free(thread->iocq); 463 spdk_thread_destroy(thread->thread); 464 } else { 465 spdk_thread_poll(thread->thread, 0, 0); 466 } 467 } 468 } 469 470 pthread_exit(NULL); 471 472 err_exit: 473 exit(rc); 474 return NULL; 475 } 476 477 static int 478 spdk_fio_init_env(struct thread_data *td) 479 { 480 pthread_condattr_t attr; 481 int rc = -1; 482 483 if (pthread_condattr_init(&attr)) { 484 SPDK_ERRLOG("Unable to initialize condition variable\n"); 485 return -1; 486 } 487 488 if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) { 489 SPDK_ERRLOG("Unable to initialize condition variable\n"); 490 goto out; 491 } 492 493 if (pthread_cond_init(&g_init_cond, &attr)) { 494 SPDK_ERRLOG("Unable to initialize condition variable\n"); 495 goto out; 496 } 497 498 /* 499 * Spawn a thread to handle initialization operations and to poll things 500 * like the admin queues periodically. 501 */ 502 rc = pthread_create(&g_init_thread_id, NULL, &spdk_init_thread_poll, td->eo); 503 if (rc != 0) { 504 SPDK_ERRLOG("Unable to spawn thread to poll admin queue. It won't be polled.\n"); 505 } 506 507 /* Wait for background thread to advance past the initialization */ 508 pthread_mutex_lock(&g_init_mtx); 509 pthread_cond_wait(&g_init_cond, &g_init_mtx); 510 pthread_mutex_unlock(&g_init_mtx); 511 out: 512 pthread_condattr_destroy(&attr); 513 return rc; 514 } 515 516 static bool 517 fio_redirected_to_dev_null(void) 518 { 519 char path[PATH_MAX] = ""; 520 ssize_t ret; 521 522 ret = readlink("/proc/self/fd/1", path, sizeof(path)); 523 524 if (ret == -1 || strcmp(path, "/dev/null") != 0) { 525 return false; 526 } 527 528 ret = readlink("/proc/self/fd/2", path, sizeof(path)); 529 530 if (ret == -1 || strcmp(path, "/dev/null") != 0) { 531 return false; 532 } 533 534 return true; 535 } 536 537 static int 538 spdk_fio_init_spdk_env(struct thread_data *td) 539 { 540 static pthread_mutex_t setup_lock = PTHREAD_MUTEX_INITIALIZER; 541 542 pthread_mutex_lock(&setup_lock); 543 if (!g_spdk_env_initialized) { 544 if (spdk_fio_init_env(td)) { 545 pthread_mutex_unlock(&setup_lock); 546 SPDK_ERRLOG("failed to initialize\n"); 547 return -1; 548 } 549 550 g_spdk_env_initialized = true; 551 } 552 pthread_mutex_unlock(&setup_lock); 553 554 return 0; 555 } 556 557 /* Called for each thread to fill in the 'real_file_size' member for 558 * each file associated with this thread. This is called prior to 559 * the init operation (spdk_fio_init()) below. This call will occur 560 * on the initial start up thread if 'create_serialize' is true, or 561 * on the thread actually associated with 'thread_data' if 'create_serialize' 562 * is false. 563 */ 564 static int 565 spdk_fio_setup(struct thread_data *td) 566 { 567 struct spdk_fio_oat_ctx ctx = { 0 }; 568 569 /* 570 * If we're running in a daemonized FIO instance, it's possible 571 * fd 1/2 were re-used for something important by FIO. Newer fio 572 * versions are careful to redirect those to /dev/null, but if we're 573 * not, we'll abort early, so we don't accidentally write messages to 574 * an important file, etc. 575 */ 576 if (is_backend && !fio_redirected_to_dev_null()) { 577 char buf[1024]; 578 snprintf(buf, sizeof(buf), 579 "SPDK FIO plugin is in daemon mode, but stdout/stderr " 580 "aren't redirected to /dev/null. Aborting."); 581 fio_server_text_output(FIO_LOG_ERR, buf, sizeof(buf)); 582 return -1; 583 } 584 585 if (!td->o.use_thread) { 586 SPDK_ERRLOG("must set thread=1 when using spdk plugin\n"); 587 return -1; 588 } 589 590 if (spdk_fio_init_spdk_env(td) != 0) { 591 return -1; 592 } 593 594 ctx.u.sa.td = td; 595 spdk_fio_sync_run_oat(spdk_fio_setup_oat, &ctx); 596 return ctx.ret; 597 } 598 599 static int 600 _spdk_fio_add_file(void *ctx, struct spdk_bdev *bdev) 601 { 602 struct thread_data *td = ctx; 603 604 add_file(td, spdk_bdev_get_name(bdev), 0, 1); 605 return 0; 606 } 607 608 static void 609 spdk_fio_setup_oat(void *_ctx) 610 { 611 struct spdk_fio_oat_ctx *ctx = _ctx; 612 struct thread_data *td = ctx->u.sa.td; 613 unsigned int i; 614 struct fio_file *f; 615 616 if (td->o.nr_files == 1 && strcmp(td->files[0]->file_name, "*") == 0) { 617 /* add all available bdevs as fio targets */ 618 spdk_for_each_bdev_leaf(td, _spdk_fio_add_file); 619 } 620 621 for_each_file(td, f, i) { 622 struct spdk_bdev *bdev; 623 624 if (strcmp(f->file_name, "*") == 0) { 625 /* Explicitly set file size to 0 here to make sure fio doesn't try to 626 * actually send I/O to this "*" file. 627 */ 628 f->real_file_size = 0; 629 continue; 630 } 631 632 bdev = spdk_bdev_get_by_name(f->file_name); 633 if (!bdev) { 634 SPDK_ERRLOG("Unable to find bdev with name %s\n", f->file_name); 635 ctx->ret = -1; 636 goto out; 637 } 638 639 f->real_file_size = spdk_bdev_get_num_blocks(bdev) * 640 spdk_bdev_get_block_size(bdev); 641 f->filetype = FIO_TYPE_BLOCK; 642 fio_file_set_size_known(f); 643 644 ctx->ret = spdk_fio_handle_options(td, f, bdev); 645 if (ctx->ret) { 646 goto out; 647 } 648 } 649 650 ctx->ret = 0; 651 out: 652 spdk_fio_wake_oat_waiter(ctx); 653 } 654 655 static void 656 fio_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, 657 void *event_ctx) 658 { 659 SPDK_WARNLOG("Unsupported bdev event: type %d\n", type); 660 } 661 662 static void 663 spdk_fio_bdev_open(void *arg) 664 { 665 struct thread_data *td = arg; 666 struct spdk_fio_thread *fio_thread; 667 unsigned int i; 668 struct fio_file *f; 669 int rc; 670 671 fio_thread = td->io_ops_data; 672 673 for_each_file(td, f, i) { 674 struct spdk_fio_target *target; 675 676 if (strcmp(f->file_name, "*") == 0) { 677 continue; 678 } 679 680 target = calloc(1, sizeof(*target)); 681 if (!target) { 682 SPDK_ERRLOG("Unable to allocate memory for I/O target.\n"); 683 fio_thread->failed = true; 684 return; 685 } 686 687 rc = spdk_bdev_open_ext(f->file_name, true, fio_bdev_event_cb, NULL, 688 &target->desc); 689 if (rc) { 690 SPDK_ERRLOG("Unable to open bdev %s\n", f->file_name); 691 free(target); 692 fio_thread->failed = true; 693 return; 694 } 695 696 target->bdev = spdk_bdev_desc_get_bdev(target->desc); 697 698 target->ch = spdk_bdev_get_io_channel(target->desc); 699 if (!target->ch) { 700 SPDK_ERRLOG("Unable to get I/O channel for bdev.\n"); 701 spdk_bdev_close(target->desc); 702 free(target); 703 fio_thread->failed = true; 704 return; 705 } 706 707 f->engine_data = target; 708 709 rc = spdk_fio_handle_options_per_target(td, f); 710 if (rc) { 711 SPDK_ERRLOG("Failed to handle options for: %s\n", f->file_name); 712 f->engine_data = NULL; 713 spdk_put_io_channel(target->ch); 714 spdk_bdev_close(target->desc); 715 free(target); 716 fio_thread->failed = true; 717 return; 718 } 719 720 TAILQ_INSERT_TAIL(&fio_thread->targets, target, link); 721 } 722 } 723 724 /* Called for each thread, on that thread, shortly after the thread 725 * starts. 726 * 727 * Also called by spdk_fio_report_zones(), since we need an I/O channel 728 * in order to get the zone report. (fio calls the .report_zones callback 729 * before it calls the .init callback.) 730 * Therefore, if fio was run with --zonemode=zbd, the thread will already 731 * be initialized by the time that fio calls the .init callback. 732 */ 733 static int 734 spdk_fio_init(struct thread_data *td) 735 { 736 struct spdk_fio_thread *fio_thread; 737 int rc; 738 739 if (spdk_fio_init_spdk_env(td) != 0) { 740 return -1; 741 } 742 743 /* If thread has already been initialized, do nothing. */ 744 if (td->io_ops_data) { 745 return 0; 746 } 747 748 rc = spdk_fio_init_thread(td); 749 if (rc) { 750 return rc; 751 } 752 753 fio_thread = td->io_ops_data; 754 assert(fio_thread); 755 fio_thread->failed = false; 756 757 spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_open, td); 758 759 while (spdk_fio_poll_thread(fio_thread) > 0) {} 760 761 if (fio_thread->failed) { 762 return -1; 763 } 764 765 return 0; 766 } 767 768 static void 769 spdk_fio_cleanup(struct thread_data *td) 770 { 771 struct spdk_fio_thread *fio_thread = td->io_ops_data; 772 773 spdk_fio_cleanup_thread(fio_thread); 774 td->io_ops_data = NULL; 775 } 776 777 static int 778 spdk_fio_open(struct thread_data *td, struct fio_file *f) 779 { 780 781 return 0; 782 } 783 784 static int 785 spdk_fio_close(struct thread_data *td, struct fio_file *f) 786 { 787 return 0; 788 } 789 790 static int 791 spdk_fio_iomem_alloc(struct thread_data *td, size_t total_mem) 792 { 793 td->orig_buffer = spdk_dma_zmalloc(total_mem, 0x1000, NULL); 794 return td->orig_buffer == NULL; 795 } 796 797 static void 798 spdk_fio_iomem_free(struct thread_data *td) 799 { 800 spdk_dma_free(td->orig_buffer); 801 } 802 803 static int 804 spdk_fio_io_u_init(struct thread_data *td, struct io_u *io_u) 805 { 806 struct spdk_fio_request *fio_req; 807 808 io_u->engine_data = NULL; 809 810 fio_req = calloc(1, sizeof(*fio_req)); 811 if (fio_req == NULL) { 812 return 1; 813 } 814 fio_req->io = io_u; 815 fio_req->td = td; 816 817 io_u->engine_data = fio_req; 818 819 return 0; 820 } 821 822 static void 823 spdk_fio_io_u_free(struct thread_data *td, struct io_u *io_u) 824 { 825 struct spdk_fio_request *fio_req = io_u->engine_data; 826 827 if (fio_req) { 828 assert(fio_req->io == io_u); 829 free(fio_req); 830 io_u->engine_data = NULL; 831 } 832 } 833 834 static void 835 spdk_fio_completion_cb(struct spdk_bdev_io *bdev_io, 836 bool success, 837 void *cb_arg) 838 { 839 struct spdk_fio_request *fio_req = cb_arg; 840 struct thread_data *td = fio_req->td; 841 struct spdk_fio_thread *fio_thread = td->io_ops_data; 842 843 assert(fio_thread->iocq_count < fio_thread->iocq_size); 844 fio_req->io->error = success ? 0 : EIO; 845 fio_thread->iocq[fio_thread->iocq_count++] = fio_req->io; 846 847 spdk_bdev_free_io(bdev_io); 848 } 849 850 #if FIO_IOOPS_VERSION >= 24 851 typedef enum fio_q_status fio_q_status_t; 852 #else 853 typedef int fio_q_status_t; 854 #endif 855 856 static uint64_t 857 spdk_fio_zone_bytes_to_blocks(struct spdk_bdev *bdev, uint64_t offset_bytes, uint64_t *zone_start, 858 uint64_t num_bytes, uint64_t *num_blocks) 859 { 860 uint32_t block_size = spdk_bdev_get_block_size(bdev); 861 *zone_start = spdk_bdev_get_zone_id(bdev, offset_bytes / block_size); 862 *num_blocks = num_bytes / block_size; 863 return (offset_bytes % block_size) | (num_bytes % block_size); 864 } 865 866 static fio_q_status_t 867 spdk_fio_queue(struct thread_data *td, struct io_u *io_u) 868 { 869 int rc = 1; 870 struct spdk_fio_request *fio_req = io_u->engine_data; 871 struct spdk_fio_target *target = io_u->file->engine_data; 872 873 assert(fio_req->td == td); 874 875 if (!target) { 876 SPDK_ERRLOG("Unable to look up correct I/O target.\n"); 877 fio_req->io->error = ENODEV; 878 return FIO_Q_COMPLETED; 879 } 880 881 switch (io_u->ddir) { 882 case DDIR_READ: 883 rc = spdk_bdev_read(target->desc, target->ch, 884 io_u->buf, io_u->offset, io_u->xfer_buflen, 885 spdk_fio_completion_cb, fio_req); 886 break; 887 case DDIR_WRITE: 888 if (!target->zone_append_enabled) { 889 rc = spdk_bdev_write(target->desc, target->ch, 890 io_u->buf, io_u->offset, io_u->xfer_buflen, 891 spdk_fio_completion_cb, fio_req); 892 } else { 893 uint64_t zone_start, num_blocks; 894 if (spdk_fio_zone_bytes_to_blocks(target->bdev, io_u->offset, &zone_start, 895 io_u->xfer_buflen, &num_blocks) != 0) { 896 rc = -EINVAL; 897 break; 898 } 899 rc = spdk_bdev_zone_append(target->desc, target->ch, io_u->buf, 900 zone_start, num_blocks, spdk_fio_completion_cb, 901 fio_req); 902 } 903 break; 904 case DDIR_TRIM: 905 rc = spdk_bdev_unmap(target->desc, target->ch, 906 io_u->offset, io_u->xfer_buflen, 907 spdk_fio_completion_cb, fio_req); 908 break; 909 case DDIR_SYNC: 910 rc = spdk_bdev_flush(target->desc, target->ch, 911 io_u->offset, io_u->xfer_buflen, 912 spdk_fio_completion_cb, fio_req); 913 break; 914 default: 915 assert(false); 916 break; 917 } 918 919 if (rc == -ENOMEM) { 920 return FIO_Q_BUSY; 921 } 922 923 if (rc != 0) { 924 fio_req->io->error = abs(rc); 925 return FIO_Q_COMPLETED; 926 } 927 928 return FIO_Q_QUEUED; 929 } 930 931 static struct io_u * 932 spdk_fio_event(struct thread_data *td, int event) 933 { 934 struct spdk_fio_thread *fio_thread = td->io_ops_data; 935 936 assert(event >= 0); 937 assert((unsigned)event < fio_thread->iocq_count); 938 return fio_thread->iocq[event]; 939 } 940 941 static size_t 942 spdk_fio_poll_thread(struct spdk_fio_thread *fio_thread) 943 { 944 return spdk_thread_poll(fio_thread->thread, 0, 0); 945 } 946 947 static int 948 spdk_fio_getevents(struct thread_data *td, unsigned int min, 949 unsigned int max, const struct timespec *t) 950 { 951 struct spdk_fio_thread *fio_thread = td->io_ops_data; 952 struct timespec t0, t1; 953 uint64_t timeout = 0; 954 955 if (t) { 956 timeout = t->tv_sec * SPDK_SEC_TO_NSEC + t->tv_nsec; 957 clock_gettime(CLOCK_MONOTONIC_RAW, &t0); 958 } 959 960 fio_thread->iocq_count = 0; 961 962 for (;;) { 963 spdk_fio_poll_thread(fio_thread); 964 965 if (fio_thread->iocq_count >= min) { 966 return fio_thread->iocq_count; 967 } 968 969 if (t) { 970 clock_gettime(CLOCK_MONOTONIC_RAW, &t1); 971 uint64_t elapse = ((t1.tv_sec - t0.tv_sec) * SPDK_SEC_TO_NSEC) 972 + t1.tv_nsec - t0.tv_nsec; 973 if (elapse > timeout) { 974 break; 975 } 976 } 977 } 978 979 return fio_thread->iocq_count; 980 } 981 982 static int 983 spdk_fio_invalidate(struct thread_data *td, struct fio_file *f) 984 { 985 /* TODO: This should probably send a flush to the device, but for now just return successful. */ 986 return 0; 987 } 988 989 #if FIO_HAS_ZBD 990 /* Runs on app thread (oat) */ 991 static void 992 spdk_fio_get_zoned_model_oat(void *arg) 993 { 994 struct spdk_fio_oat_ctx *ctx = arg; 995 struct fio_file *f = ctx->u.zma.f; 996 enum zbd_zoned_model *model = ctx->u.zma.model; 997 struct spdk_bdev *bdev; 998 999 if (f->filetype != FIO_TYPE_BLOCK) { 1000 SPDK_ERRLOG("Unsupported filetype: %d\n", f->filetype); 1001 ctx->ret = -EINVAL; 1002 goto out; 1003 } 1004 1005 bdev = spdk_bdev_get_by_name(f->file_name); 1006 if (!bdev) { 1007 SPDK_ERRLOG("Cannot get zoned model, no bdev with name: %s\n", f->file_name); 1008 ctx->ret = -ENODEV; 1009 goto out; 1010 } 1011 1012 if (spdk_bdev_is_zoned(bdev)) { 1013 *model = ZBD_HOST_MANAGED; 1014 } else { 1015 *model = ZBD_NONE; 1016 } 1017 1018 ctx->ret = 0; 1019 out: 1020 spdk_fio_wake_oat_waiter(ctx); 1021 } 1022 1023 static int 1024 spdk_fio_get_zoned_model(struct thread_data *td, struct fio_file *f, enum zbd_zoned_model *model) 1025 { 1026 struct spdk_fio_oat_ctx ctx = { 0 }; 1027 1028 ctx.u.zma.f = f; 1029 ctx.u.zma.model = model; 1030 1031 spdk_fio_sync_run_oat(spdk_fio_get_zoned_model_oat, &ctx); 1032 1033 return ctx.ret; 1034 } 1035 1036 1037 static void 1038 spdk_fio_bdev_get_zone_info_done(struct spdk_bdev_io *bdev_io, bool success, void *arg) 1039 { 1040 struct spdk_fio_zone_cb_arg *cb_arg = arg; 1041 unsigned int i; 1042 int handled_zones = 0; 1043 1044 if (!success) { 1045 spdk_bdev_free_io(bdev_io); 1046 cb_arg->completed = -EIO; 1047 return; 1048 } 1049 1050 for (i = 0; i < cb_arg->nr_zones; i++) { 1051 struct spdk_bdev_zone_info *zone_src = &cb_arg->spdk_zones[handled_zones]; 1052 struct zbd_zone *zone_dest = &cb_arg->fio_zones[handled_zones]; 1053 uint32_t block_size = spdk_bdev_get_block_size(cb_arg->target->bdev); 1054 1055 switch (zone_src->type) { 1056 case SPDK_BDEV_ZONE_TYPE_SEQWR: 1057 zone_dest->type = ZBD_ZONE_TYPE_SWR; 1058 break; 1059 case SPDK_BDEV_ZONE_TYPE_SEQWP: 1060 zone_dest->type = ZBD_ZONE_TYPE_SWP; 1061 break; 1062 case SPDK_BDEV_ZONE_TYPE_CNV: 1063 zone_dest->type = ZBD_ZONE_TYPE_CNV; 1064 break; 1065 default: 1066 spdk_bdev_free_io(bdev_io); 1067 cb_arg->completed = -EIO; 1068 return; 1069 } 1070 1071 zone_dest->len = spdk_bdev_get_zone_size(cb_arg->target->bdev) * block_size; 1072 zone_dest->capacity = zone_src->capacity * block_size; 1073 zone_dest->start = zone_src->zone_id * block_size; 1074 zone_dest->wp = zone_src->write_pointer * block_size; 1075 1076 switch (zone_src->state) { 1077 case SPDK_BDEV_ZONE_STATE_EMPTY: 1078 zone_dest->cond = ZBD_ZONE_COND_EMPTY; 1079 break; 1080 case SPDK_BDEV_ZONE_STATE_IMP_OPEN: 1081 zone_dest->cond = ZBD_ZONE_COND_IMP_OPEN; 1082 break; 1083 case SPDK_BDEV_ZONE_STATE_EXP_OPEN: 1084 zone_dest->cond = ZBD_ZONE_COND_EXP_OPEN; 1085 break; 1086 case SPDK_BDEV_ZONE_STATE_FULL: 1087 zone_dest->cond = ZBD_ZONE_COND_FULL; 1088 break; 1089 case SPDK_BDEV_ZONE_STATE_CLOSED: 1090 zone_dest->cond = ZBD_ZONE_COND_CLOSED; 1091 break; 1092 case SPDK_BDEV_ZONE_STATE_READ_ONLY: 1093 zone_dest->cond = ZBD_ZONE_COND_READONLY; 1094 break; 1095 case SPDK_BDEV_ZONE_STATE_OFFLINE: 1096 zone_dest->cond = ZBD_ZONE_COND_OFFLINE; 1097 break; 1098 case SPDK_BDEV_ZONE_STATE_NOT_WP: 1099 zone_dest->cond = ZBD_ZONE_COND_NOT_WP; 1100 /* Set WP to end of zone for zone types w/o WP (e.g. Conv. zones in SMR) */ 1101 zone_dest->wp = zone_dest->start + zone_dest->capacity; 1102 break; 1103 default: 1104 spdk_bdev_free_io(bdev_io); 1105 cb_arg->completed = -EIO; 1106 return; 1107 } 1108 handled_zones++; 1109 } 1110 1111 spdk_bdev_free_io(bdev_io); 1112 cb_arg->completed = handled_zones; 1113 } 1114 1115 static void 1116 spdk_fio_bdev_get_zone_info(void *arg) 1117 { 1118 struct spdk_fio_zone_cb_arg *cb_arg = arg; 1119 struct spdk_fio_target *target = cb_arg->target; 1120 int rc; 1121 1122 rc = spdk_bdev_get_zone_info(target->desc, target->ch, cb_arg->offset_blocks, 1123 cb_arg->nr_zones, cb_arg->spdk_zones, 1124 spdk_fio_bdev_get_zone_info_done, cb_arg); 1125 if (rc < 0) { 1126 cb_arg->completed = rc; 1127 } 1128 } 1129 1130 static int 1131 spdk_fio_report_zones(struct thread_data *td, struct fio_file *f, uint64_t offset, 1132 struct zbd_zone *zones, unsigned int nr_zones) 1133 { 1134 struct spdk_fio_target *target; 1135 struct spdk_fio_thread *fio_thread; 1136 struct spdk_fio_zone_cb_arg cb_arg; 1137 uint32_t block_size; 1138 int rc; 1139 1140 if (nr_zones == 0) { 1141 return 0; 1142 } 1143 1144 /* spdk_fio_report_zones() is only called before the bdev I/O channels have been created. 1145 * Since we need an I/O channel for report_zones(), call spdk_fio_init() to initialize 1146 * the thread early. 1147 * spdk_fio_report_zones() might be called several times by fio, if e.g. the zone report 1148 * for all zones does not fit in the buffer that fio has allocated for the zone report. 1149 * It is safe to call spdk_fio_init(), even if the thread has already been initialized. 1150 */ 1151 rc = spdk_fio_init(td); 1152 if (rc) { 1153 return rc; 1154 } 1155 fio_thread = td->io_ops_data; 1156 target = f->engine_data; 1157 1158 assert(fio_thread); 1159 assert(target); 1160 1161 block_size = spdk_bdev_get_block_size(target->bdev); 1162 1163 cb_arg.target = target; 1164 cb_arg.completed = 0; 1165 cb_arg.offset_blocks = offset / block_size; 1166 cb_arg.fio_zones = zones; 1167 cb_arg.nr_zones = spdk_min(nr_zones, spdk_bdev_get_num_zones(target->bdev)); 1168 1169 cb_arg.spdk_zones = calloc(1, sizeof(*cb_arg.spdk_zones) * cb_arg.nr_zones); 1170 if (!cb_arg.spdk_zones) { 1171 SPDK_ERRLOG("Could not allocate memory for zone report!\n"); 1172 rc = -ENOMEM; 1173 goto cleanup_thread; 1174 } 1175 1176 spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_get_zone_info, &cb_arg); 1177 do { 1178 spdk_fio_poll_thread(fio_thread); 1179 } while (!cb_arg.completed); 1180 1181 /* Free cb_arg.spdk_zones. The report in fio format is stored in cb_arg.fio_zones/zones. */ 1182 free(cb_arg.spdk_zones); 1183 1184 rc = cb_arg.completed; 1185 if (rc < 0) { 1186 SPDK_ERRLOG("Failed to get zone info: %d\n", rc); 1187 goto cleanup_thread; 1188 } 1189 1190 /* Return the amount of zones successfully copied. */ 1191 return rc; 1192 1193 cleanup_thread: 1194 spdk_fio_cleanup(td); 1195 1196 return rc; 1197 } 1198 1199 static void 1200 spdk_fio_bdev_zone_reset_done(struct spdk_bdev_io *bdev_io, bool success, void *arg) 1201 { 1202 struct spdk_fio_zone_cb_arg *cb_arg = arg; 1203 1204 spdk_bdev_free_io(bdev_io); 1205 1206 if (!success) { 1207 cb_arg->completed = -EIO; 1208 } else { 1209 cb_arg->completed = 1; 1210 } 1211 } 1212 1213 static void 1214 spdk_fio_bdev_zone_reset(void *arg) 1215 { 1216 struct spdk_fio_zone_cb_arg *cb_arg = arg; 1217 struct spdk_fio_target *target = cb_arg->target; 1218 int rc; 1219 1220 rc = spdk_bdev_zone_management(target->desc, target->ch, cb_arg->offset_blocks, 1221 SPDK_BDEV_ZONE_RESET, 1222 spdk_fio_bdev_zone_reset_done, cb_arg); 1223 if (rc < 0) { 1224 cb_arg->completed = rc; 1225 } 1226 } 1227 1228 static int 1229 spdk_fio_reset_zones(struct spdk_fio_thread *fio_thread, struct spdk_fio_target *target, 1230 uint64_t offset, uint64_t length) 1231 { 1232 uint64_t zone_size_bytes; 1233 uint32_t block_size; 1234 int rc; 1235 1236 assert(fio_thread); 1237 assert(target); 1238 1239 block_size = spdk_bdev_get_block_size(target->bdev); 1240 zone_size_bytes = spdk_bdev_get_zone_size(target->bdev) * block_size; 1241 1242 for (uint64_t cur = offset; cur < offset + length; cur += zone_size_bytes) { 1243 struct spdk_fio_zone_cb_arg cb_arg = { 1244 .target = target, 1245 .completed = 0, 1246 .offset_blocks = cur / block_size, 1247 }; 1248 1249 spdk_thread_send_msg(fio_thread->thread, spdk_fio_bdev_zone_reset, &cb_arg); 1250 do { 1251 spdk_fio_poll_thread(fio_thread); 1252 } while (!cb_arg.completed); 1253 1254 rc = cb_arg.completed; 1255 if (rc < 0) { 1256 SPDK_ERRLOG("Failed to reset zone: %d\n", rc); 1257 return rc; 1258 } 1259 } 1260 1261 return 0; 1262 } 1263 1264 static int 1265 spdk_fio_reset_wp(struct thread_data *td, struct fio_file *f, uint64_t offset, uint64_t length) 1266 { 1267 return spdk_fio_reset_zones(td->io_ops_data, f->engine_data, offset, length); 1268 } 1269 #endif 1270 1271 #if FIO_IOOPS_VERSION >= 30 1272 static void 1273 spdk_fio_get_max_open_zones_oat(void *_ctx) 1274 { 1275 struct spdk_fio_oat_ctx *ctx = _ctx; 1276 struct fio_file *f = ctx->u.moza.f; 1277 struct spdk_bdev *bdev; 1278 1279 bdev = spdk_bdev_get_by_name(f->file_name); 1280 if (!bdev) { 1281 SPDK_ERRLOG("Cannot get max open zones, no bdev with name: %s\n", f->file_name); 1282 ctx->ret = -ENODEV; 1283 } else { 1284 *ctx->u.moza.max_open_zones = spdk_bdev_get_max_open_zones(bdev); 1285 ctx->ret = 0; 1286 } 1287 1288 spdk_fio_wake_oat_waiter(ctx); 1289 } 1290 1291 static int 1292 spdk_fio_get_max_open_zones(struct thread_data *td, struct fio_file *f, 1293 unsigned int *max_open_zones) 1294 { 1295 struct spdk_fio_oat_ctx ctx = { 0 }; 1296 1297 ctx.u.moza.f = f; 1298 ctx.u.moza.max_open_zones = max_open_zones; 1299 1300 spdk_fio_sync_run_oat(spdk_fio_get_max_open_zones_oat, &ctx); 1301 1302 return ctx.ret; 1303 } 1304 #endif 1305 1306 static int 1307 spdk_fio_handle_options(struct thread_data *td, struct fio_file *f, struct spdk_bdev *bdev) 1308 { 1309 struct spdk_fio_options *fio_options = td->eo; 1310 1311 if (fio_options->initial_zone_reset && spdk_bdev_is_zoned(bdev)) { 1312 #if FIO_HAS_ZBD 1313 int rc = spdk_fio_init(td); 1314 if (rc) { 1315 return rc; 1316 } 1317 /* offset used to indicate conventional zones that need to be skipped (reset not allowed) */ 1318 rc = spdk_fio_reset_zones(td->io_ops_data, f->engine_data, td->o.start_offset, 1319 f->real_file_size - td->o.start_offset); 1320 if (rc) { 1321 spdk_fio_cleanup(td); 1322 return rc; 1323 } 1324 #else 1325 SPDK_ERRLOG("fio version is too old to support zoned block devices\n"); 1326 #endif 1327 } 1328 1329 return 0; 1330 } 1331 1332 static int 1333 spdk_fio_handle_options_per_target(struct thread_data *td, struct fio_file *f) 1334 { 1335 struct spdk_fio_target *target = f->engine_data; 1336 struct spdk_fio_options *fio_options = td->eo; 1337 1338 if (fio_options->zone_append && spdk_bdev_is_zoned(target->bdev)) { 1339 if (spdk_bdev_io_type_supported(target->bdev, SPDK_BDEV_IO_TYPE_ZONE_APPEND)) { 1340 SPDK_DEBUGLOG(fio_bdev, "Using zone appends instead of writes on: '%s'\n", 1341 f->file_name); 1342 target->zone_append_enabled = true; 1343 } else { 1344 SPDK_WARNLOG("Falling back to writes on: '%s' - bdev lacks zone append cmd\n", 1345 f->file_name); 1346 } 1347 } 1348 1349 return 0; 1350 } 1351 1352 static struct fio_option options[] = { 1353 { 1354 .name = "spdk_conf", 1355 .lname = "SPDK configuration file", 1356 .type = FIO_OPT_STR_STORE, 1357 .off1 = offsetof(struct spdk_fio_options, conf), 1358 .help = "A SPDK JSON configuration file", 1359 .category = FIO_OPT_C_ENGINE, 1360 .group = FIO_OPT_G_INVALID, 1361 }, 1362 { 1363 .name = "spdk_json_conf", 1364 .lname = "SPDK JSON configuration file", 1365 .type = FIO_OPT_STR_STORE, 1366 .off1 = offsetof(struct spdk_fio_options, json_conf), 1367 .help = "A SPDK JSON configuration file", 1368 .category = FIO_OPT_C_ENGINE, 1369 .group = FIO_OPT_G_INVALID, 1370 }, 1371 { 1372 .name = "spdk_mem", 1373 .lname = "SPDK memory in MB", 1374 .type = FIO_OPT_INT, 1375 .off1 = offsetof(struct spdk_fio_options, mem_mb), 1376 .help = "Amount of memory in MB to allocate for SPDK", 1377 .category = FIO_OPT_C_ENGINE, 1378 .group = FIO_OPT_G_INVALID, 1379 }, 1380 { 1381 .name = "spdk_single_seg", 1382 .lname = "SPDK switch to create just a single hugetlbfs file", 1383 .type = FIO_OPT_BOOL, 1384 .off1 = offsetof(struct spdk_fio_options, mem_single_seg), 1385 .help = "If set to 1, SPDK will use just a single hugetlbfs file", 1386 .def = "0", 1387 .category = FIO_OPT_C_ENGINE, 1388 .group = FIO_OPT_G_INVALID, 1389 }, 1390 { 1391 .name = "log_flags", 1392 .lname = "log flags", 1393 .type = FIO_OPT_STR_STORE, 1394 .off1 = offsetof(struct spdk_fio_options, log_flags), 1395 .help = "SPDK log flags to enable", 1396 .category = FIO_OPT_C_ENGINE, 1397 .group = FIO_OPT_G_INVALID, 1398 }, 1399 { 1400 .name = "initial_zone_reset", 1401 .lname = "Reset Zones on initialization", 1402 .type = FIO_OPT_INT, 1403 .off1 = offsetof(struct spdk_fio_options, initial_zone_reset), 1404 .def = "0", 1405 .help = "Reset Zones on initialization (0=disable, 1=Reset All Zones)", 1406 .category = FIO_OPT_C_ENGINE, 1407 .group = FIO_OPT_G_INVALID, 1408 }, 1409 { 1410 .name = "zone_append", 1411 .lname = "Use zone append instead of write", 1412 .type = FIO_OPT_INT, 1413 .off1 = offsetof(struct spdk_fio_options, zone_append), 1414 .def = "0", 1415 .help = "Use zone append instead of write (1=zone append, 0=write)", 1416 .category = FIO_OPT_C_ENGINE, 1417 .group = FIO_OPT_G_INVALID, 1418 }, 1419 { 1420 .name = "env_context", 1421 .lname = "Environment context options", 1422 .type = FIO_OPT_STR_STORE, 1423 .off1 = offsetof(struct spdk_fio_options, env_context), 1424 .help = "Opaque context for use of the env implementation", 1425 .category = FIO_OPT_C_ENGINE, 1426 .group = FIO_OPT_G_INVALID, 1427 }, 1428 { 1429 .name = "spdk_rpc_listen_addr", 1430 .lname = "SPDK RPC listen address", 1431 .type = FIO_OPT_STR_STORE, 1432 .off1 = offsetof(struct spdk_fio_options, rpc_listen_addr), 1433 .help = "The address to listen the RPC operations", 1434 .category = FIO_OPT_C_ENGINE, 1435 .group = FIO_OPT_G_INVALID, 1436 }, 1437 { 1438 .name = NULL, 1439 }, 1440 }; 1441 1442 /* FIO imports this structure using dlsym */ 1443 struct ioengine_ops ioengine = { 1444 .name = "spdk_bdev", 1445 .version = FIO_IOOPS_VERSION, 1446 .flags = FIO_RAWIO | FIO_NOEXTEND | FIO_NODISKUTIL | FIO_MEMALIGN | FIO_DISKLESSIO, 1447 .setup = spdk_fio_setup, 1448 .init = spdk_fio_init, 1449 /* .prep = unused, */ 1450 .queue = spdk_fio_queue, 1451 /* .commit = unused, */ 1452 .getevents = spdk_fio_getevents, 1453 .event = spdk_fio_event, 1454 /* .errdetails = unused, */ 1455 /* .cancel = unused, */ 1456 .cleanup = spdk_fio_cleanup, 1457 .open_file = spdk_fio_open, 1458 .close_file = spdk_fio_close, 1459 .invalidate = spdk_fio_invalidate, 1460 /* .unlink_file = unused, */ 1461 /* .get_file_size = unused, */ 1462 /* .terminate = unused, */ 1463 .iomem_alloc = spdk_fio_iomem_alloc, 1464 .iomem_free = spdk_fio_iomem_free, 1465 .io_u_init = spdk_fio_io_u_init, 1466 .io_u_free = spdk_fio_io_u_free, 1467 #if FIO_HAS_ZBD 1468 .get_zoned_model = spdk_fio_get_zoned_model, 1469 .report_zones = spdk_fio_report_zones, 1470 .reset_wp = spdk_fio_reset_wp, 1471 #endif 1472 #if FIO_IOOPS_VERSION >= 30 1473 .get_max_open_zones = spdk_fio_get_max_open_zones, 1474 #endif 1475 .option_struct_size = sizeof(struct spdk_fio_options), 1476 .options = options, 1477 }; 1478 1479 static void fio_init 1480 spdk_fio_register(void) 1481 { 1482 register_ioengine(&ioengine); 1483 } 1484 1485 static void 1486 spdk_fio_finish_env(void) 1487 { 1488 pthread_mutex_lock(&g_init_mtx); 1489 g_poll_loop = false; 1490 pthread_cond_signal(&g_init_cond); 1491 pthread_mutex_unlock(&g_init_mtx); 1492 pthread_join(g_init_thread_id, NULL); 1493 1494 spdk_thread_lib_fini(); 1495 spdk_env_fini(); 1496 } 1497 1498 static void fio_exit 1499 spdk_fio_unregister(void) 1500 { 1501 if (g_spdk_env_initialized) { 1502 spdk_fio_finish_env(); 1503 g_spdk_env_initialized = false; 1504 } 1505 unregister_ioengine(&ioengine); 1506 } 1507 1508 SPDK_LOG_REGISTER_COMPONENT(fio_bdev) 1509