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