1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * * Neither the name of Nvidia Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include "spdk/stdinc.h" 34 35 #include "spdk/dma.h" 36 #include "spdk/bdev.h" 37 #include "spdk/env.h" 38 #include "spdk/event.h" 39 #include "spdk/likely.h" 40 #include "spdk/string.h" 41 #include "spdk/util.h" 42 43 #include <infiniband/verbs.h> 44 45 struct dma_test_task; 46 47 struct dma_test_req { 48 struct iovec iov; 49 struct spdk_bdev_ext_io_opts io_opts; 50 uint64_t submit_tsc; 51 struct ibv_mr *mr; 52 struct dma_test_task *task; 53 }; 54 55 struct dma_test_task_stats { 56 uint64_t io_completed; 57 uint64_t total_tsc; 58 uint64_t min_tsc; 59 uint64_t max_tsc; 60 }; 61 62 struct dma_test_task { 63 struct spdk_bdev_desc *desc; 64 struct spdk_io_channel *channel; 65 uint64_t cur_io_offset; 66 uint64_t max_offset_in_ios; 67 uint64_t num_blocks_per_io; 68 int rw_percentage; 69 uint32_t seed; 70 uint32_t io_inflight; 71 struct dma_test_task_stats stats; 72 struct dma_test_task_stats last_stats; 73 bool is_draining; 74 bool is_random; 75 struct dma_test_req *reqs; 76 struct spdk_thread *thread; 77 const char *bdev_name; 78 uint32_t lcore; 79 80 TAILQ_ENTRY(dma_test_task) link; 81 }; 82 83 TAILQ_HEAD(, dma_test_task) g_tasks = TAILQ_HEAD_INITIALIZER(g_tasks); 84 85 /* User's input */ 86 static char *g_bdev_name; 87 static const char *g_rw_mode_str; 88 static int g_rw_percentage = -1; 89 static uint32_t g_queue_depth; 90 static uint32_t g_io_size; 91 static uint32_t g_run_time_sec; 92 static uint32_t g_run_count; 93 static bool g_is_random; 94 95 static struct spdk_thread *g_main_thread; 96 static struct spdk_poller *g_runtime_poller; 97 static struct spdk_memory_domain *g_domain; 98 static uint64_t g_num_blocks_per_io; 99 static uint32_t g_num_construct_tasks; 100 static uint32_t g_num_complete_tasks; 101 static uint64_t g_start_tsc; 102 static int g_run_rc; 103 104 static void destroy_tasks(void); 105 static int dma_test_submit_io(struct dma_test_req *req); 106 107 static void 108 print_total_stats(void) 109 { 110 struct dma_test_task *task; 111 uint64_t tsc_rate = spdk_get_ticks_hz(); 112 uint64_t test_time_usec = (spdk_get_ticks() - g_start_tsc) * SPDK_SEC_TO_USEC / tsc_rate; 113 uint64_t total_tsc = 0, total_io_completed = 0; 114 double task_iops, task_bw, task_min_lat, task_avg_lat, task_max_lat; 115 double total_iops = 0, total_bw = 0, total_min_lat = (double)UINT64_MAX, total_max_lat = 0, 116 total_avg_lat; 117 118 printf("==========================================================================\n"); 119 printf("%*s\n", 55, "Latency [us]"); 120 printf("%*s %10s %10s %10s %10s\n", 19, "IOPS", "MiB/s", "Average", "min", "max"); 121 122 TAILQ_FOREACH(task, &g_tasks, link) { 123 if (!task->stats.io_completed) { 124 continue; 125 } 126 task_iops = (double)task->stats.io_completed * SPDK_SEC_TO_USEC / test_time_usec; 127 task_bw = task_iops * g_io_size / (1024 * 1024); 128 task_avg_lat = (double)task->stats.total_tsc / task->stats.io_completed * SPDK_SEC_TO_USEC / 129 tsc_rate; 130 task_min_lat = (double)task->stats.min_tsc * SPDK_SEC_TO_USEC / tsc_rate; 131 task_max_lat = (double)task->stats.max_tsc * SPDK_SEC_TO_USEC / tsc_rate; 132 133 total_iops += task_iops; 134 total_bw += task_bw; 135 total_io_completed += task->stats.io_completed; 136 total_tsc += task->stats.total_tsc; 137 if (task_min_lat < total_min_lat) { 138 total_min_lat = task_min_lat; 139 } 140 if (task_max_lat > total_max_lat) { 141 total_max_lat = task_max_lat; 142 } 143 printf("Core %2u: %10.2f %10.2f %10.2f %10.2f %10.2f\n", 144 task->lcore, task_iops, task_bw, task_avg_lat, task_min_lat, task_max_lat); 145 } 146 147 if (total_io_completed) { 148 total_avg_lat = (double)total_tsc / total_io_completed * SPDK_SEC_TO_USEC / tsc_rate; 149 printf("==========================================================================\n"); 150 printf("%-*s %10.2f %10.2f %10.2f %10.2f %10.2f\n", 151 8, "Total :", total_iops, total_bw, total_avg_lat, total_min_lat, total_max_lat); 152 printf("\n"); 153 } 154 } 155 156 static void 157 print_periodic_stats(void) 158 { 159 struct dma_test_task *task; 160 uint64_t io_last_sec = 0, tsc_last_sec = 0; 161 double lat_last_sec, bw_last_sec; 162 163 TAILQ_FOREACH(task, &g_tasks, link) { 164 io_last_sec += task->stats.io_completed - task->last_stats.io_completed; 165 tsc_last_sec += task->stats.total_tsc - task->last_stats.total_tsc; 166 memcpy(&task->last_stats, &task->stats, sizeof(task->stats)); 167 } 168 169 printf("Running %3u/%-3u sec", g_run_count, g_run_time_sec); 170 if (io_last_sec) { 171 lat_last_sec = (double)tsc_last_sec / io_last_sec * SPDK_SEC_TO_USEC / spdk_get_ticks_hz(); 172 bw_last_sec = (double)io_last_sec * g_io_size / (1024 * 1024); 173 printf(" IOPS: %-8"PRIu64" BW: %-6.2f [MiB/s] avg.lat %-5.2f [us]", 174 io_last_sec, bw_last_sec, lat_last_sec); 175 } 176 177 printf("\r"); 178 fflush(stdout); 179 } 180 181 static void 182 dma_test_task_complete(void *ctx) 183 { 184 assert(g_num_complete_tasks > 0); 185 186 if (--g_num_complete_tasks == 0) { 187 spdk_poller_unregister(&g_runtime_poller); 188 print_total_stats(); 189 spdk_app_stop(g_run_rc); 190 } 191 } 192 193 static inline void 194 dma_test_check_and_signal_task_done(struct dma_test_task *task) 195 { 196 if (task->io_inflight == 0) { 197 spdk_put_io_channel(task->channel); 198 spdk_bdev_close(task->desc); 199 spdk_thread_send_msg(g_main_thread, dma_test_task_complete, task); 200 } 201 } 202 203 static inline void 204 dma_test_task_update_stats(struct dma_test_task *task, uint64_t submit_tsc) 205 { 206 uint64_t tsc_diff = spdk_get_ticks() - submit_tsc; 207 208 task->stats.io_completed++; 209 task->stats.total_tsc += tsc_diff; 210 if (spdk_unlikely(tsc_diff < task->stats.min_tsc)) { 211 task->stats.min_tsc = tsc_diff; 212 } 213 if (spdk_unlikely(tsc_diff > task->stats.max_tsc)) { 214 task->stats.max_tsc = tsc_diff; 215 } 216 } 217 218 static void 219 dma_test_bdev_io_completion_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) 220 { 221 struct dma_test_req *req = cb_arg; 222 struct dma_test_task *task = req->task; 223 224 assert(task->io_inflight > 0); 225 --task->io_inflight; 226 dma_test_task_update_stats(task, req->submit_tsc); 227 228 if (!success) { 229 if (!g_run_rc) { 230 fprintf(stderr, "IO completed with error\n"); 231 g_run_rc = -1; 232 } 233 task->is_draining = true; 234 } 235 236 spdk_bdev_free_io(bdev_io); 237 238 if (spdk_unlikely(task->is_draining)) { 239 dma_test_check_and_signal_task_done(task); 240 return; 241 } 242 243 dma_test_submit_io(req); 244 } 245 246 static inline uint64_t 247 dma_test_get_offset_in_ios(struct dma_test_task *task) 248 { 249 uint64_t offset; 250 251 if (task->is_random) { 252 offset = rand_r(&task->seed) % task->max_offset_in_ios; 253 } else { 254 offset = task->cur_io_offset++; 255 if (spdk_unlikely(task->cur_io_offset == task->max_offset_in_ios)) { 256 task->cur_io_offset = 0; 257 } 258 } 259 260 return offset; 261 } 262 263 static inline bool 264 dma_test_task_is_read(struct dma_test_task *task) 265 { 266 if (task->rw_percentage == 100) { 267 return true; 268 } 269 if (task->rw_percentage != 0 && (rand_r(&task->seed) % 100) < task->rw_percentage) { 270 return true; 271 } 272 return false; 273 } 274 275 static int 276 dma_test_translate_memory_cb(struct spdk_memory_domain *src_domain, void *src_domain_ctx, 277 struct spdk_memory_domain *dst_domain, struct spdk_memory_domain_translation_ctx *dst_domain_ctx, 278 void *addr, size_t len, struct spdk_memory_domain_translation_result *result) 279 { 280 struct dma_test_req *req = src_domain_ctx; 281 struct ibv_qp *dst_domain_qp = (struct ibv_qp *)dst_domain_ctx->rdma.ibv_qp; 282 283 if (spdk_unlikely(!req->mr)) { 284 req->mr = ibv_reg_mr(dst_domain_qp->pd, addr, len, IBV_ACCESS_LOCAL_WRITE | 285 IBV_ACCESS_REMOTE_READ | 286 IBV_ACCESS_REMOTE_WRITE); 287 if (!req->mr) { 288 fprintf(stderr, "Failed to register memory region, errno %d\n", errno); 289 return -1; 290 } 291 } 292 293 result->iov.iov_base = addr; 294 result->iov.iov_len = len; 295 result->iov_count = 1; 296 result->rdma.lkey = req->mr->lkey; 297 result->rdma.rkey = req->mr->rkey; 298 result->dst_domain = dst_domain; 299 300 return 0; 301 } 302 303 static int 304 dma_test_submit_io(struct dma_test_req *req) 305 { 306 struct dma_test_task *task = req->task; 307 uint64_t offset_in_ios; 308 int rc; 309 bool is_read; 310 311 offset_in_ios = dma_test_get_offset_in_ios(task); 312 is_read = dma_test_task_is_read(task); 313 req->submit_tsc = spdk_get_ticks(); 314 if (is_read) { 315 rc = spdk_bdev_readv_blocks_ext(task->desc, task->channel, &req->iov, 1, 316 offset_in_ios * task->num_blocks_per_io, task->num_blocks_per_io, 317 dma_test_bdev_io_completion_cb, req, &req->io_opts); 318 } else { 319 rc = spdk_bdev_writev_blocks_ext(task->desc, task->channel, &req->iov, 1, 320 offset_in_ios * task->num_blocks_per_io, task->num_blocks_per_io, 321 dma_test_bdev_io_completion_cb, req, &req->io_opts); 322 } 323 324 if (spdk_unlikely(rc)) { 325 if (!g_run_rc) { 326 /* log an error only once */ 327 fprintf(stderr, "Failed to submit %s IO, rc %d, stop sending IO\n", is_read ? "read" : "write", rc); 328 g_run_rc = rc; 329 } 330 task->is_draining = true; 331 dma_test_check_and_signal_task_done(task); 332 return rc; 333 } 334 335 task->io_inflight++; 336 337 return 0; 338 } 339 340 static void 341 dma_test_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) 342 { 343 struct dma_test_task *task = event_ctx; 344 345 if (type == SPDK_BDEV_EVENT_REMOVE) { 346 task->is_draining = true; 347 } 348 } 349 350 static void 351 dma_test_bdev_dummy_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, 352 void *event_ctx) 353 { 354 } 355 356 static void dma_test_task_run(void *ctx) 357 { 358 struct dma_test_task *task = ctx; 359 uint32_t i; 360 int rc = 0; 361 362 for (i = 0; i < g_queue_depth && rc == 0; i++) { 363 rc = dma_test_submit_io(&task->reqs[i]); 364 } 365 } 366 367 static void 368 dma_test_drain_task(void *ctx) 369 { 370 struct dma_test_task *task = ctx; 371 372 task->is_draining = true; 373 } 374 375 static void 376 dma_test_shutdown_cb(void) 377 { 378 struct dma_test_task *task; 379 380 spdk_poller_unregister(&g_runtime_poller); 381 382 TAILQ_FOREACH(task, &g_tasks, link) { 383 spdk_thread_send_msg(task->thread, dma_test_drain_task, task); 384 } 385 } 386 387 static int 388 dma_test_run_time_poller(void *ctx) 389 { 390 g_run_count++; 391 392 if (g_run_count < g_run_time_sec) { 393 if (isatty(STDOUT_FILENO)) { 394 print_periodic_stats(); 395 } 396 } else { 397 dma_test_shutdown_cb(); 398 } 399 400 return SPDK_POLLER_BUSY; 401 } 402 403 static void 404 dma_test_construct_task_done(void *ctx) 405 { 406 struct dma_test_task *task; 407 408 assert(g_num_construct_tasks > 0); 409 --g_num_construct_tasks; 410 411 if (g_num_construct_tasks != 0) { 412 return; 413 } 414 415 if (g_run_rc) { 416 fprintf(stderr, "Initialization failed with error %d\n", g_run_rc); 417 spdk_app_stop(g_run_rc); 418 return; 419 } 420 421 g_runtime_poller = spdk_poller_register_named(dma_test_run_time_poller, NULL, 1 * 1000 * 1000, 422 "dma_test_run_time_poller"); 423 if (!g_runtime_poller) { 424 fprintf(stderr, "Failed to run timer\n"); 425 spdk_app_stop(-1); 426 return; 427 } 428 429 printf("Initialization complete, running %s IO for %u sec on %u cores\n", g_rw_mode_str, 430 g_run_time_sec, spdk_env_get_core_count()); 431 g_start_tsc = spdk_get_ticks(); 432 TAILQ_FOREACH(task, &g_tasks, link) { 433 spdk_thread_send_msg(task->thread, dma_test_task_run, task); 434 } 435 } 436 437 static void 438 dma_test_construct_task_on_thread(void *ctx) 439 { 440 struct dma_test_task *task = ctx; 441 int rc; 442 443 rc = spdk_bdev_open_ext(task->bdev_name, true, dma_test_bdev_event_cb, task, &task->desc); 444 if (rc) { 445 fprintf(stderr, "Failed to open bdev %s, rc %d\n", task->bdev_name, rc); 446 g_run_rc = rc; 447 spdk_thread_send_msg(g_main_thread, dma_test_construct_task_done, NULL); 448 return; 449 } 450 451 task->channel = spdk_bdev_get_io_channel(task->desc); 452 if (!task->channel) { 453 spdk_bdev_close(task->desc); 454 task->desc = NULL; 455 fprintf(stderr, "Failed to open bdev %s, rc %d\n", task->bdev_name, rc); 456 g_run_rc = rc; 457 spdk_thread_send_msg(g_main_thread, dma_test_construct_task_done, NULL); 458 return; 459 } 460 461 task->max_offset_in_ios = spdk_bdev_get_num_blocks(spdk_bdev_desc_get_bdev( 462 task->desc)) / task->num_blocks_per_io; 463 464 spdk_thread_send_msg(g_main_thread, dma_test_construct_task_done, task); 465 } 466 467 static bool 468 dma_test_check_bdev_supports_rdma_memory_domain(struct spdk_bdev *bdev) 469 { 470 struct spdk_memory_domain **bdev_domains; 471 int bdev_domains_count, bdev_domains_count_tmp, i; 472 bool rdma_domain_supported = false; 473 474 bdev_domains_count = spdk_bdev_get_memory_domains(bdev, NULL, 0); 475 476 if (bdev_domains_count < 0) { 477 fprintf(stderr, "Failed to get bdev memory domains count, rc %d\n", bdev_domains_count); 478 return false; 479 } else if (bdev_domains_count == 0) { 480 fprintf(stderr, "bdev %s doesn't support any memory domains\n", spdk_bdev_get_name(bdev)); 481 return false; 482 } 483 484 fprintf(stdout, "bdev %s reports %d memory domains\n", spdk_bdev_get_name(bdev), 485 bdev_domains_count); 486 487 bdev_domains = calloc((size_t)bdev_domains_count, sizeof(*bdev_domains)); 488 if (!bdev_domains) { 489 fprintf(stderr, "Failed to allocate memory domains\n"); 490 return false; 491 } 492 493 bdev_domains_count_tmp = spdk_bdev_get_memory_domains(bdev, bdev_domains, bdev_domains_count); 494 if (bdev_domains_count_tmp != bdev_domains_count) { 495 fprintf(stderr, "Unexpected bdev domains return value %d\n", bdev_domains_count_tmp); 496 return false; 497 } 498 499 for (i = 0; i < bdev_domains_count; i++) { 500 if (spdk_memory_domain_get_dma_device_type(bdev_domains[i]) == SPDK_DMA_DEVICE_TYPE_RDMA) { 501 /* Bdev supports memory domain of RDMA type, we can try to submit IO request to it using 502 * bdev ext API */ 503 rdma_domain_supported = true; 504 break; 505 } 506 } 507 508 fprintf(stdout, "bdev %s %s RDMA memory domain\n", spdk_bdev_get_name(bdev), 509 rdma_domain_supported ? "supports" : "doesn't support"); 510 free(bdev_domains); 511 512 return rdma_domain_supported; 513 } 514 515 static int 516 allocate_task(uint32_t core, const char *bdev_name) 517 { 518 char thread_name[32]; 519 struct spdk_cpuset cpu_set; 520 uint32_t i; 521 struct dma_test_task *task; 522 struct dma_test_req *req; 523 524 task = calloc(1, sizeof(*task)); 525 if (!task) { 526 fprintf(stderr, "Failed to allocate per thread task\n"); 527 return -ENOMEM; 528 } 529 530 TAILQ_INSERT_TAIL(&g_tasks, task, link); 531 532 task->reqs = calloc(g_queue_depth, sizeof(*task->reqs)); 533 if (!task->reqs) { 534 fprintf(stderr, "Failed to allocate requests\n"); 535 return -ENOMEM; 536 } 537 538 for (i = 0; i < g_queue_depth; i++) { 539 req = &task->reqs[i]; 540 req->task = task; 541 req->iov.iov_len = g_io_size; 542 req->iov.iov_base = malloc(req->iov.iov_len); 543 if (!req->iov.iov_base) { 544 fprintf(stderr, "Failed to allocate request data buffer\n"); 545 return -ENOMEM; 546 } 547 memset(req->iov.iov_base, 0xc, req->iov.iov_len); 548 req->io_opts.size = sizeof(req->io_opts); 549 req->io_opts.memory_domain = g_domain; 550 req->io_opts.memory_domain_ctx = req; 551 } 552 553 snprintf(thread_name, 32, "task_%u", core); 554 spdk_cpuset_zero(&cpu_set); 555 spdk_cpuset_set_cpu(&cpu_set, core, true); 556 task->thread = spdk_thread_create(thread_name, &cpu_set); 557 if (!task->thread) { 558 fprintf(stderr, "Failed to create SPDK thread, core %u, cpu_mask %s\n", core, 559 spdk_cpuset_fmt(&cpu_set)); 560 return -ENOMEM; 561 } 562 563 task->seed = core; 564 task->lcore = core; 565 task->bdev_name = bdev_name; 566 task->is_random = g_is_random; 567 task->rw_percentage = g_rw_percentage; 568 task->num_blocks_per_io = g_num_blocks_per_io; 569 task->stats.min_tsc = UINT64_MAX; 570 571 return 0; 572 } 573 574 static void 575 destroy_task(struct dma_test_task *task) 576 { 577 struct dma_test_req *req; 578 uint32_t i; 579 580 for (i = 0; i < g_queue_depth; i++) { 581 req = &task->reqs[i]; 582 if (req->mr) { 583 ibv_dereg_mr(req->mr); 584 } 585 free(req->iov.iov_base); 586 } 587 free(task->reqs); 588 TAILQ_REMOVE(&g_tasks, task, link); 589 free(task); 590 } 591 592 static void 593 destroy_tasks(void) 594 { 595 struct dma_test_task *task, *tmp_task; 596 597 TAILQ_FOREACH_SAFE(task, &g_tasks, link, tmp_task) { 598 destroy_task(task); 599 } 600 } 601 602 static void 603 dma_test_start(void *arg) 604 { 605 struct spdk_bdev_desc *desc; 606 struct spdk_bdev *bdev; 607 struct dma_test_task *task; 608 uint32_t block_size, i; 609 int rc; 610 611 rc = spdk_bdev_open_ext(g_bdev_name, true, dma_test_bdev_dummy_event_cb, NULL, &desc); 612 if (rc) { 613 fprintf(stderr, "Can't find bdev %s\n", g_bdev_name); 614 spdk_app_stop(-ENODEV); 615 return; 616 } 617 bdev = spdk_bdev_desc_get_bdev(desc); 618 if (!dma_test_check_bdev_supports_rdma_memory_domain(bdev)) { 619 spdk_bdev_close(desc); 620 spdk_app_stop(-ENODEV); 621 return; 622 } 623 624 g_main_thread = spdk_get_thread(); 625 626 block_size = spdk_bdev_get_block_size(bdev); 627 if (g_io_size < block_size || g_io_size % block_size != 0) { 628 fprintf(stderr, "Invalid io_size %u requested, bdev block size %u\n", g_io_size, block_size); 629 spdk_bdev_close(desc); 630 spdk_app_stop(-EINVAL); 631 return; 632 } 633 g_num_blocks_per_io = g_io_size / block_size; 634 635 /* Create a memory domain to represent the source memory domain. 636 * Since we don't actually have a remote memory domain in this test, this will describe memory 637 * on the local system and the translation to the destination memory domain will be trivial. 638 * But this at least allows us to demonstrate the flow and test the functionality. */ 639 rc = spdk_memory_domain_create(&g_domain, SPDK_DMA_DEVICE_TYPE_RDMA, NULL, "test_dma"); 640 if (rc != 0) { 641 spdk_bdev_close(desc); 642 spdk_app_stop(rc); 643 return; 644 } 645 spdk_memory_domain_set_translation(g_domain, dma_test_translate_memory_cb); 646 647 SPDK_ENV_FOREACH_CORE(i) { 648 rc = allocate_task(i, g_bdev_name); 649 if (rc) { 650 destroy_tasks(); 651 spdk_bdev_close(desc); 652 spdk_app_stop(rc); 653 return; 654 } 655 g_num_construct_tasks++; 656 g_num_complete_tasks++; 657 } 658 659 TAILQ_FOREACH(task, &g_tasks, link) { 660 spdk_thread_send_msg(task->thread, dma_test_construct_task_on_thread, task); 661 } 662 663 spdk_bdev_close(desc); 664 } 665 666 static void 667 print_usage(void) 668 { 669 printf(" -b <bdev> bdev name for test\n"); 670 printf(" -q <val> io depth\n"); 671 printf(" -o <val> io size in bytes\n"); 672 printf(" -t <val> run time in seconds\n"); 673 printf(" -w <str> io pattern (read, write, randread, randwrite, randrw)\n"); 674 printf(" -M <0-100> rw percentage (100 for reads, 0 for writes)\n"); 675 } 676 677 static int 678 parse_arg(int ch, char *arg) 679 { 680 long tmp; 681 682 switch (ch) { 683 case 'q': 684 case 'o': 685 case 't': 686 case 'M': 687 tmp = spdk_strtol(arg, 10); 688 if (tmp < 0) { 689 fprintf(stderr, "Invalid option %c value %s\n", ch, arg); 690 return 1; 691 } 692 693 switch (ch) { 694 case 'q': 695 g_queue_depth = (uint32_t) tmp; 696 break; 697 case 'o': 698 g_io_size = (uint32_t) tmp; 699 break; 700 case 't': 701 g_run_time_sec = (uint32_t) tmp; 702 break; 703 case 'M': 704 g_rw_percentage = (uint32_t) tmp; 705 break; 706 } 707 break; 708 case 'w': 709 g_rw_mode_str = arg; 710 break; 711 case 'b': 712 g_bdev_name = arg; 713 break; 714 715 default: 716 fprintf(stderr, "Unknown option %c\n", ch); 717 return 1; 718 } 719 720 return 0; 721 } 722 723 static int 724 verify_args(void) 725 { 726 const char *rw_mode = g_rw_mode_str; 727 728 if (g_queue_depth == 0) { 729 fprintf(stderr, "queue depth (-q) is not set\n"); 730 return 1; 731 } 732 if (g_io_size == 0) { 733 fprintf(stderr, "io size (-o) is not set\n"); 734 return 1; 735 } 736 if (g_run_time_sec == 0) { 737 fprintf(stderr, "test run time (-t) is not set\n"); 738 return 1; 739 } 740 if (!rw_mode) { 741 fprintf(stderr, "io pattern (-w) is not set\n"); 742 return 1; 743 } 744 if (strncmp(rw_mode, "rand", 4) == 0) { 745 g_is_random = true; 746 rw_mode = &rw_mode[4]; 747 } 748 if (strcmp(rw_mode, "read") == 0 || strcmp(rw_mode, "write") == 0) { 749 if (g_rw_percentage > 0) { 750 fprintf(stderr, "Ignoring -M option\n"); 751 } 752 g_rw_percentage = strcmp(rw_mode, "read") == 0 ? 100 : 0; 753 } else if (strcmp(rw_mode, "rw") == 0) { 754 if (g_rw_percentage < 0 || g_rw_percentage > 100) { 755 fprintf(stderr, "Invalid -M value (%d) must be 0..100\n", g_rw_percentage); 756 return 1; 757 } 758 } else { 759 fprintf(stderr, "io pattern (-w) one of [read, write, randread, randwrite, rw, randrw]\n"); 760 return 1; 761 } 762 if (!g_bdev_name) { 763 fprintf(stderr, "bdev name (-b) is not set\n"); 764 return 1; 765 } 766 767 return 0; 768 } 769 770 int 771 main(int argc, char **argv) 772 { 773 struct spdk_app_opts opts = {}; 774 int rc; 775 776 spdk_app_opts_init(&opts, sizeof(opts)); 777 opts.name = "test_dma"; 778 opts.shutdown_cb = dma_test_shutdown_cb; 779 780 rc = spdk_app_parse_args(argc, argv, &opts, "b:q:o:t:w:M:", NULL, parse_arg, print_usage); 781 if (rc != SPDK_APP_PARSE_ARGS_SUCCESS) { 782 exit(rc); 783 } 784 785 rc = verify_args(); 786 if (rc) { 787 exit(rc); 788 } 789 790 rc = spdk_app_start(&opts, dma_test_start, NULL); 791 destroy_tasks(); 792 spdk_app_fini(); 793 794 return rc; 795 } 796