1488570ebSJim Harris /* SPDX-License-Identifier: BSD-3-Clause 2a6dbe372Spaul luse * Copyright (C) 2016 Intel Corporation. 37991eb19SZiye Yang * All rights reserved. 47991eb19SZiye Yang */ 57991eb19SZiye Yang 6b961d9ccSBen Walker #include "spdk/stdinc.h" 77991eb19SZiye Yang 84518e4c3SChangpeng Liu #include "spdk/log.h" 97991eb19SZiye Yang #include "spdk/nvme.h" 100dd80395SBen Walker #include "spdk/env.h" 117991eb19SZiye Yang #include "spdk/string.h" 127991eb19SZiye Yang #include "spdk/nvme_intel.h" 137991eb19SZiye Yang 147991eb19SZiye Yang struct ctrlr_entry { 157991eb19SZiye Yang struct spdk_nvme_ctrlr *ctrlr; 16df56ab77SDaniel Verkamp struct spdk_nvme_intel_rw_latency_page latency_page; 174c3fd228SShuhei Matsumoto TAILQ_ENTRY(ctrlr_entry) link; 187991eb19SZiye Yang char name[1024]; 197991eb19SZiye Yang }; 207991eb19SZiye Yang 217991eb19SZiye Yang struct ns_entry { 227991eb19SZiye Yang struct { 237991eb19SZiye Yang struct spdk_nvme_ctrlr *ctrlr; 247991eb19SZiye Yang struct spdk_nvme_ns *ns; 257991eb19SZiye Yang } nvme; 267991eb19SZiye Yang 274c3fd228SShuhei Matsumoto TAILQ_ENTRY(ns_entry) link; 287991eb19SZiye Yang uint32_t io_size_blocks; 297991eb19SZiye Yang uint64_t size_in_ios; 307991eb19SZiye Yang char name[1024]; 317991eb19SZiye Yang }; 327991eb19SZiye Yang 337991eb19SZiye Yang struct ns_worker_ctx { 347991eb19SZiye Yang struct ns_entry *entry; 357991eb19SZiye Yang uint64_t io_completed; 367991eb19SZiye Yang uint64_t current_queue_depth; 377991eb19SZiye Yang uint64_t offset_in_ios; 387991eb19SZiye Yang bool is_draining; 397991eb19SZiye Yang struct spdk_nvme_qpair *qpair; 404c3fd228SShuhei Matsumoto TAILQ_ENTRY(ns_worker_ctx) link; 417991eb19SZiye Yang }; 427991eb19SZiye Yang 437991eb19SZiye Yang struct arb_task { 447991eb19SZiye Yang struct ns_worker_ctx *ns_ctx; 457991eb19SZiye Yang void *buf; 467991eb19SZiye Yang }; 477991eb19SZiye Yang 487991eb19SZiye Yang struct worker_thread { 494c3fd228SShuhei Matsumoto TAILQ_HEAD(, ns_worker_ctx) ns_ctx; 504c3fd228SShuhei Matsumoto TAILQ_ENTRY(worker_thread) link; 517991eb19SZiye Yang unsigned lcore; 527991eb19SZiye Yang enum spdk_nvme_qprio qprio; 537991eb19SZiye Yang }; 547991eb19SZiye Yang 557991eb19SZiye Yang struct arb_context { 5625270f1dSBen Walker int shm_id; 577991eb19SZiye Yang int outstanding_commands; 587991eb19SZiye Yang int num_namespaces; 597991eb19SZiye Yang int num_workers; 607991eb19SZiye Yang int rw_percentage; 617991eb19SZiye Yang int is_random; 627991eb19SZiye Yang int queue_depth; 637991eb19SZiye Yang int time_in_sec; 647991eb19SZiye Yang int io_count; 657991eb19SZiye Yang uint8_t latency_tracking_enable; 667991eb19SZiye Yang uint8_t arbitration_mechanism; 677991eb19SZiye Yang uint8_t arbitration_config; 687991eb19SZiye Yang uint32_t io_size_bytes; 697991eb19SZiye Yang uint32_t max_completions; 707991eb19SZiye Yang uint64_t tsc_rate; 717991eb19SZiye Yang const char *core_mask; 727991eb19SZiye Yang const char *workload_type; 737991eb19SZiye Yang }; 747991eb19SZiye Yang 757991eb19SZiye Yang struct feature { 767991eb19SZiye Yang uint32_t result; 777991eb19SZiye Yang bool valid; 787991eb19SZiye Yang }; 797991eb19SZiye Yang 8063c1c9d5SZiye Yang static struct spdk_mempool *task_pool = NULL; 817991eb19SZiye Yang 824c3fd228SShuhei Matsumoto static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers); 834c3fd228SShuhei Matsumoto static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces); 844c3fd228SShuhei Matsumoto static TAILQ_HEAD(, worker_thread) g_workers = TAILQ_HEAD_INITIALIZER(g_workers); 857991eb19SZiye Yang 863630f477SChangpeng Liu static struct feature features[SPDK_NVME_FEAT_ARBITRATION + 1] = {}; 8774dcf4aaSMao Jiang static struct spdk_nvme_transport_id g_trid = {}; 887991eb19SZiye Yang 897991eb19SZiye Yang static struct arb_context g_arbitration = { 9025270f1dSBen Walker .shm_id = -1, 917991eb19SZiye Yang .outstanding_commands = 0, 927991eb19SZiye Yang .num_workers = 0, 937991eb19SZiye Yang .num_namespaces = 0, 947991eb19SZiye Yang .rw_percentage = 50, 957991eb19SZiye Yang .queue_depth = 64, 967991eb19SZiye Yang .time_in_sec = 60, 977991eb19SZiye Yang .io_count = 100000, 987991eb19SZiye Yang .latency_tracking_enable = 0, 997991eb19SZiye Yang .arbitration_mechanism = SPDK_NVME_CC_AMS_RR, 1007991eb19SZiye Yang .arbitration_config = 0, 1017991eb19SZiye Yang .io_size_bytes = 131072, 1027991eb19SZiye Yang .max_completions = 0, 10318d26e42SBen Walker /* Default 4 cores for urgent/high/medium/low */ 1047991eb19SZiye Yang .core_mask = "0xf", 1057991eb19SZiye Yang .workload_type = "randrw", 1067991eb19SZiye Yang }; 1077991eb19SZiye Yang 1084518e4c3SChangpeng Liu static int g_dpdk_mem = 0; 1094518e4c3SChangpeng Liu static bool g_dpdk_mem_single_seg = false; 1104518e4c3SChangpeng Liu 1117991eb19SZiye Yang /* 1127991eb19SZiye Yang * For weighted round robin arbitration mechanism, the smaller value between 1137991eb19SZiye Yang * weight and burst will be picked to execute the commands in one queue. 1147991eb19SZiye Yang */ 1157991eb19SZiye Yang #define USER_SPECIFIED_HIGH_PRIORITY_WEIGHT 32 1167991eb19SZiye Yang #define USER_SPECIFIED_MEDIUM_PRIORITY_WEIGHT 16 1177991eb19SZiye Yang #define USER_SPECIFIED_LOW_PRIORITY_WEIGHT 8 1187991eb19SZiye Yang 1197991eb19SZiye Yang static void task_complete(struct arb_task *task); 1207991eb19SZiye Yang 1217991eb19SZiye Yang static void io_complete(void *ctx, const struct spdk_nvme_cpl *completion); 1227991eb19SZiye Yang 1237991eb19SZiye Yang static void get_arb_feature(struct spdk_nvme_ctrlr *ctrlr); 1247991eb19SZiye Yang 1257991eb19SZiye Yang static int set_arb_feature(struct spdk_nvme_ctrlr *ctrlr); 1267991eb19SZiye Yang 1277991eb19SZiye Yang static const char *print_qprio(enum spdk_nvme_qprio); 1287991eb19SZiye Yang 1297991eb19SZiye Yang 1307991eb19SZiye Yang static void 1317991eb19SZiye Yang register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns) 1327991eb19SZiye Yang { 1337991eb19SZiye Yang struct ns_entry *entry; 1347991eb19SZiye Yang const struct spdk_nvme_ctrlr_data *cdata; 1357991eb19SZiye Yang 1367991eb19SZiye Yang cdata = spdk_nvme_ctrlr_get_data(ctrlr); 1377991eb19SZiye Yang 1387991eb19SZiye Yang if (spdk_nvme_ns_get_size(ns) < g_arbitration.io_size_bytes || 1396366e361SChangpeng Liu spdk_nvme_ns_get_extended_sector_size(ns) > g_arbitration.io_size_bytes || 1406366e361SChangpeng Liu g_arbitration.io_size_bytes % spdk_nvme_ns_get_extended_sector_size(ns)) { 1417991eb19SZiye Yang printf("WARNING: controller %-20.20s (%-20.20s) ns %u has invalid " 1427991eb19SZiye Yang "ns size %" PRIu64 " / block size %u for I/O size %u\n", 1437991eb19SZiye Yang cdata->mn, cdata->sn, spdk_nvme_ns_get_id(ns), 1446366e361SChangpeng Liu spdk_nvme_ns_get_size(ns), spdk_nvme_ns_get_extended_sector_size(ns), 1457991eb19SZiye Yang g_arbitration.io_size_bytes); 1467991eb19SZiye Yang return; 1477991eb19SZiye Yang } 1487991eb19SZiye Yang 1497991eb19SZiye Yang entry = malloc(sizeof(struct ns_entry)); 1507991eb19SZiye Yang if (entry == NULL) { 1517991eb19SZiye Yang perror("ns_entry malloc"); 1527991eb19SZiye Yang exit(1); 1537991eb19SZiye Yang } 1547991eb19SZiye Yang 1557991eb19SZiye Yang entry->nvme.ctrlr = ctrlr; 1567991eb19SZiye Yang entry->nvme.ns = ns; 1577991eb19SZiye Yang 1587991eb19SZiye Yang entry->size_in_ios = spdk_nvme_ns_get_size(ns) / g_arbitration.io_size_bytes; 1597991eb19SZiye Yang entry->io_size_blocks = g_arbitration.io_size_bytes / spdk_nvme_ns_get_sector_size(ns); 1607991eb19SZiye Yang 1617991eb19SZiye Yang snprintf(entry->name, 44, "%-20.20s (%-20.20s)", cdata->mn, cdata->sn); 1627991eb19SZiye Yang 1637991eb19SZiye Yang g_arbitration.num_namespaces++; 1644c3fd228SShuhei Matsumoto TAILQ_INSERT_TAIL(&g_namespaces, entry, link); 1657991eb19SZiye Yang } 1667991eb19SZiye Yang 1677991eb19SZiye Yang static void 1687991eb19SZiye Yang enable_latency_tracking_complete(void *cb_arg, const struct spdk_nvme_cpl *cpl) 1697991eb19SZiye Yang { 1707991eb19SZiye Yang if (spdk_nvme_cpl_is_error(cpl)) { 1717991eb19SZiye Yang printf("enable_latency_tracking_complete failed\n"); 1727991eb19SZiye Yang } 1737991eb19SZiye Yang g_arbitration.outstanding_commands--; 1747991eb19SZiye Yang } 1757991eb19SZiye Yang 1767991eb19SZiye Yang static void 1777991eb19SZiye Yang set_latency_tracking_feature(struct spdk_nvme_ctrlr *ctrlr, bool enable) 1787991eb19SZiye Yang { 1797991eb19SZiye Yang int res; 1807991eb19SZiye Yang union spdk_nvme_intel_feat_latency_tracking latency_tracking; 1817991eb19SZiye Yang 1827991eb19SZiye Yang if (enable) { 1837991eb19SZiye Yang latency_tracking.bits.enable = 0x01; 1847991eb19SZiye Yang } else { 1857991eb19SZiye Yang latency_tracking.bits.enable = 0x00; 1867991eb19SZiye Yang } 1877991eb19SZiye Yang 1887991eb19SZiye Yang res = spdk_nvme_ctrlr_cmd_set_feature(ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING, 1897991eb19SZiye Yang latency_tracking.raw, 0, NULL, 0, enable_latency_tracking_complete, NULL); 1907991eb19SZiye Yang if (res) { 1917991eb19SZiye Yang printf("fail to allocate nvme request.\n"); 1927991eb19SZiye Yang return; 1937991eb19SZiye Yang } 1947991eb19SZiye Yang g_arbitration.outstanding_commands++; 1957991eb19SZiye Yang 1967991eb19SZiye Yang while (g_arbitration.outstanding_commands) { 1977991eb19SZiye Yang spdk_nvme_ctrlr_process_admin_completions(ctrlr); 1987991eb19SZiye Yang } 1997991eb19SZiye Yang } 2007991eb19SZiye Yang 2017991eb19SZiye Yang static void 2027991eb19SZiye Yang register_ctrlr(struct spdk_nvme_ctrlr *ctrlr) 2037991eb19SZiye Yang { 204bd7689bdSChangpeng Liu uint32_t nsid; 20534369a12SChangpeng Liu struct spdk_nvme_ns *ns; 206df56ab77SDaniel Verkamp struct ctrlr_entry *entry = calloc(1, sizeof(struct ctrlr_entry)); 2075a8033a5SChangpeng Liu union spdk_nvme_cap_register cap = spdk_nvme_ctrlr_get_regs_cap(ctrlr); 2087991eb19SZiye Yang const struct spdk_nvme_ctrlr_data *cdata = spdk_nvme_ctrlr_get_data(ctrlr); 2097991eb19SZiye Yang 2107991eb19SZiye Yang if (entry == NULL) { 2117991eb19SZiye Yang perror("ctrlr_entry malloc"); 2127991eb19SZiye Yang exit(1); 2137991eb19SZiye Yang } 2147991eb19SZiye Yang 2157991eb19SZiye Yang snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn); 2167991eb19SZiye Yang 2177991eb19SZiye Yang entry->ctrlr = ctrlr; 2184c3fd228SShuhei Matsumoto TAILQ_INSERT_TAIL(&g_controllers, entry, link); 2197991eb19SZiye Yang 2207991eb19SZiye Yang if ((g_arbitration.latency_tracking_enable != 0) && 22159970a89SDaniel Verkamp spdk_nvme_ctrlr_is_feature_supported(ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING)) { 2227991eb19SZiye Yang set_latency_tracking_feature(ctrlr, true); 22359970a89SDaniel Verkamp } 2247991eb19SZiye Yang 225bd7689bdSChangpeng Liu for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr); nsid != 0; 226bd7689bdSChangpeng Liu nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) { 22734369a12SChangpeng Liu ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid); 22834369a12SChangpeng Liu if (ns == NULL) { 22934369a12SChangpeng Liu continue; 23034369a12SChangpeng Liu } 23134369a12SChangpeng Liu register_ns(ctrlr, ns); 2327991eb19SZiye Yang } 2337991eb19SZiye Yang 2345a8033a5SChangpeng Liu if (g_arbitration.arbitration_mechanism == SPDK_NVME_CAP_AMS_WRR && 2355a8033a5SChangpeng Liu (cap.bits.ams & SPDK_NVME_CAP_AMS_WRR)) { 2367991eb19SZiye Yang get_arb_feature(ctrlr); 2377991eb19SZiye Yang 2387991eb19SZiye Yang if (g_arbitration.arbitration_config != 0) { 2397991eb19SZiye Yang set_arb_feature(ctrlr); 2407991eb19SZiye Yang get_arb_feature(ctrlr); 2417991eb19SZiye Yang } 2427991eb19SZiye Yang } 2437991eb19SZiye Yang } 2447991eb19SZiye Yang 2457991eb19SZiye Yang static __thread unsigned int seed = 0; 2467991eb19SZiye Yang 2477991eb19SZiye Yang static void 2487991eb19SZiye Yang submit_single_io(struct ns_worker_ctx *ns_ctx) 2497991eb19SZiye Yang { 2507991eb19SZiye Yang struct arb_task *task = NULL; 2517991eb19SZiye Yang uint64_t offset_in_ios; 2527991eb19SZiye Yang int rc; 2537991eb19SZiye Yang struct ns_entry *entry = ns_ctx->entry; 2547991eb19SZiye Yang 25563c1c9d5SZiye Yang task = spdk_mempool_get(task_pool); 25663c1c9d5SZiye Yang if (!task) { 25763c1c9d5SZiye Yang fprintf(stderr, "Failed to get task from task_pool\n"); 25863c1c9d5SZiye Yang exit(1); 25963c1c9d5SZiye Yang } 26063c1c9d5SZiye Yang 26163c1c9d5SZiye Yang task->buf = spdk_dma_zmalloc(g_arbitration.io_size_bytes, 0x200, NULL); 26263c1c9d5SZiye Yang if (!task->buf) { 26363c1c9d5SZiye Yang spdk_mempool_put(task_pool, task); 26463c1c9d5SZiye Yang fprintf(stderr, "task->buf spdk_dma_zmalloc failed\n"); 2657991eb19SZiye Yang exit(1); 2667991eb19SZiye Yang } 2677991eb19SZiye Yang 2687991eb19SZiye Yang task->ns_ctx = ns_ctx; 2697991eb19SZiye Yang 2707991eb19SZiye Yang if (g_arbitration.is_random) { 2717991eb19SZiye Yang offset_in_ios = rand_r(&seed) % entry->size_in_ios; 2727991eb19SZiye Yang } else { 2737991eb19SZiye Yang offset_in_ios = ns_ctx->offset_in_ios++; 2747991eb19SZiye Yang if (ns_ctx->offset_in_ios == entry->size_in_ios) { 2757991eb19SZiye Yang ns_ctx->offset_in_ios = 0; 2767991eb19SZiye Yang } 2777991eb19SZiye Yang } 2787991eb19SZiye Yang 2797991eb19SZiye Yang if ((g_arbitration.rw_percentage == 100) || 2807991eb19SZiye Yang (g_arbitration.rw_percentage != 0 && 2817991eb19SZiye Yang ((rand_r(&seed) % 100) < g_arbitration.rw_percentage))) { 2827991eb19SZiye Yang rc = spdk_nvme_ns_cmd_read(entry->nvme.ns, ns_ctx->qpair, task->buf, 2837991eb19SZiye Yang offset_in_ios * entry->io_size_blocks, 2847991eb19SZiye Yang entry->io_size_blocks, io_complete, task, 0); 2857991eb19SZiye Yang } else { 2867991eb19SZiye Yang rc = spdk_nvme_ns_cmd_write(entry->nvme.ns, ns_ctx->qpair, task->buf, 2877991eb19SZiye Yang offset_in_ios * entry->io_size_blocks, 2887991eb19SZiye Yang entry->io_size_blocks, io_complete, task, 0); 2897991eb19SZiye Yang } 2907991eb19SZiye Yang 2917991eb19SZiye Yang if (rc != 0) { 2927991eb19SZiye Yang fprintf(stderr, "starting I/O failed\n"); 2933d857e36SShuhei Matsumoto } else { 2947991eb19SZiye Yang ns_ctx->current_queue_depth++; 2957991eb19SZiye Yang } 2963d857e36SShuhei Matsumoto } 2977991eb19SZiye Yang 2987991eb19SZiye Yang static void 2997991eb19SZiye Yang task_complete(struct arb_task *task) 3007991eb19SZiye Yang { 3017991eb19SZiye Yang struct ns_worker_ctx *ns_ctx; 3027991eb19SZiye Yang 3037991eb19SZiye Yang ns_ctx = task->ns_ctx; 3047991eb19SZiye Yang ns_ctx->current_queue_depth--; 3057991eb19SZiye Yang ns_ctx->io_completed++; 3067991eb19SZiye Yang 30763c1c9d5SZiye Yang spdk_dma_free(task->buf); 30863c1c9d5SZiye Yang spdk_mempool_put(task_pool, task); 3097991eb19SZiye Yang 3107991eb19SZiye Yang /* 3117991eb19SZiye Yang * is_draining indicates when time has expired for the test run 3127991eb19SZiye Yang * and we are just waiting for the previously submitted I/O 3137991eb19SZiye Yang * to complete. In this case, do not submit a new I/O to replace 3147991eb19SZiye Yang * the one just completed. 3157991eb19SZiye Yang */ 3167991eb19SZiye Yang if (!ns_ctx->is_draining) { 3177991eb19SZiye Yang submit_single_io(ns_ctx); 3187991eb19SZiye Yang } 3197991eb19SZiye Yang } 3207991eb19SZiye Yang 3217991eb19SZiye Yang static void 3227991eb19SZiye Yang io_complete(void *ctx, const struct spdk_nvme_cpl *completion) 3237991eb19SZiye Yang { 3247991eb19SZiye Yang task_complete((struct arb_task *)ctx); 3257991eb19SZiye Yang } 3267991eb19SZiye Yang 3277991eb19SZiye Yang static void 3287991eb19SZiye Yang check_io(struct ns_worker_ctx *ns_ctx) 3297991eb19SZiye Yang { 3307991eb19SZiye Yang spdk_nvme_qpair_process_completions(ns_ctx->qpair, g_arbitration.max_completions); 3317991eb19SZiye Yang } 3327991eb19SZiye Yang 3337991eb19SZiye Yang static void 3347991eb19SZiye Yang submit_io(struct ns_worker_ctx *ns_ctx, int queue_depth) 3357991eb19SZiye Yang { 3367991eb19SZiye Yang while (queue_depth-- > 0) { 3377991eb19SZiye Yang submit_single_io(ns_ctx); 3387991eb19SZiye Yang } 3397991eb19SZiye Yang } 3407991eb19SZiye Yang 3417991eb19SZiye Yang static void 3427991eb19SZiye Yang drain_io(struct ns_worker_ctx *ns_ctx) 3437991eb19SZiye Yang { 3447991eb19SZiye Yang ns_ctx->is_draining = true; 3457991eb19SZiye Yang while (ns_ctx->current_queue_depth > 0) { 3467991eb19SZiye Yang check_io(ns_ctx); 3477991eb19SZiye Yang } 3487991eb19SZiye Yang } 3497991eb19SZiye Yang 3507991eb19SZiye Yang static int 3517991eb19SZiye Yang init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx, enum spdk_nvme_qprio qprio) 3527991eb19SZiye Yang { 353ce4fcbceSDaniel Verkamp struct spdk_nvme_ctrlr *ctrlr = ns_ctx->entry->nvme.ctrlr; 354ce4fcbceSDaniel Verkamp struct spdk_nvme_io_qpair_opts opts; 355ce4fcbceSDaniel Verkamp 356ce4fcbceSDaniel Verkamp spdk_nvme_ctrlr_get_default_io_qpair_opts(ctrlr, &opts, sizeof(opts)); 357ce4fcbceSDaniel Verkamp opts.qprio = qprio; 358ce4fcbceSDaniel Verkamp 359ce4fcbceSDaniel Verkamp ns_ctx->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, &opts, sizeof(opts)); 3607991eb19SZiye Yang if (!ns_ctx->qpair) { 3617991eb19SZiye Yang printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair failed\n"); 3627991eb19SZiye Yang return 1; 3637991eb19SZiye Yang } 3647991eb19SZiye Yang 3657991eb19SZiye Yang return 0; 3667991eb19SZiye Yang } 3677991eb19SZiye Yang 3687991eb19SZiye Yang static void 3697991eb19SZiye Yang cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx) 3707991eb19SZiye Yang { 3717991eb19SZiye Yang spdk_nvme_ctrlr_free_io_qpair(ns_ctx->qpair); 3727991eb19SZiye Yang } 3737991eb19SZiye Yang 3747991eb19SZiye Yang static void 37563c1c9d5SZiye Yang cleanup(uint32_t task_count) 3767991eb19SZiye Yang { 3774c3fd228SShuhei Matsumoto struct ns_entry *entry, *tmp_entry; 3784c3fd228SShuhei Matsumoto struct worker_thread *worker, *tmp_worker; 3794c3fd228SShuhei Matsumoto struct ns_worker_ctx *ns_ctx, *tmp_ns_ctx; 3807991eb19SZiye Yang 3814c3fd228SShuhei Matsumoto TAILQ_FOREACH_SAFE(entry, &g_namespaces, link, tmp_entry) { 3824c3fd228SShuhei Matsumoto TAILQ_REMOVE(&g_namespaces, entry, link); 3837991eb19SZiye Yang free(entry); 38484230409SBen Walker }; 3857991eb19SZiye Yang 3864c3fd228SShuhei Matsumoto TAILQ_FOREACH_SAFE(worker, &g_workers, link, tmp_worker) { 3874c3fd228SShuhei Matsumoto TAILQ_REMOVE(&g_workers, worker, link); 3885446c541SJinYu 3895446c541SJinYu /* ns_worker_ctx is a list in the worker */ 3904c3fd228SShuhei Matsumoto TAILQ_FOREACH_SAFE(ns_ctx, &worker->ns_ctx, link, tmp_ns_ctx) { 3914c3fd228SShuhei Matsumoto TAILQ_REMOVE(&worker->ns_ctx, ns_ctx, link); 3925446c541SJinYu free(ns_ctx); 3935446c541SJinYu } 3945446c541SJinYu 3957991eb19SZiye Yang free(worker); 39684230409SBen Walker }; 3977991eb19SZiye Yang 39863c1c9d5SZiye Yang if (spdk_mempool_count(task_pool) != (size_t)task_count) { 39963c1c9d5SZiye Yang fprintf(stderr, "task_pool count is %zu but should be %u\n", 40063c1c9d5SZiye Yang spdk_mempool_count(task_pool), task_count); 4017991eb19SZiye Yang } 40263c1c9d5SZiye Yang spdk_mempool_free(task_pool); 4037991eb19SZiye Yang } 4047991eb19SZiye Yang 4057991eb19SZiye Yang static int 4067991eb19SZiye Yang work_fn(void *arg) 4077991eb19SZiye Yang { 4087991eb19SZiye Yang uint64_t tsc_end; 4097991eb19SZiye Yang struct worker_thread *worker = (struct worker_thread *)arg; 4104c3fd228SShuhei Matsumoto struct ns_worker_ctx *ns_ctx; 4117991eb19SZiye Yang 4127991eb19SZiye Yang printf("Starting thread on core %u with %s\n", worker->lcore, print_qprio(worker->qprio)); 4137991eb19SZiye Yang 4147991eb19SZiye Yang /* Allocate a queue pair for each namespace. */ 4154c3fd228SShuhei Matsumoto TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) { 4167991eb19SZiye Yang if (init_ns_worker_ctx(ns_ctx, worker->qprio) != 0) { 4177991eb19SZiye Yang printf("ERROR: init_ns_worker_ctx() failed\n"); 4187991eb19SZiye Yang return 1; 4197991eb19SZiye Yang } 4207991eb19SZiye Yang } 4217991eb19SZiye Yang 4220aa29864SBen Walker tsc_end = spdk_get_ticks() + g_arbitration.time_in_sec * g_arbitration.tsc_rate; 4237991eb19SZiye Yang 4247991eb19SZiye Yang /* Submit initial I/O for each namespace. */ 4254c3fd228SShuhei Matsumoto TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) { 4267991eb19SZiye Yang submit_io(ns_ctx, g_arbitration.queue_depth); 4277991eb19SZiye Yang } 4287991eb19SZiye Yang 4297991eb19SZiye Yang while (1) { 4307991eb19SZiye Yang /* 4317991eb19SZiye Yang * Check for completed I/O for each controller. A new 4327991eb19SZiye Yang * I/O will be submitted in the io_complete callback 4337991eb19SZiye Yang * to replace each I/O that is completed. 4347991eb19SZiye Yang */ 4354c3fd228SShuhei Matsumoto TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) { 4367991eb19SZiye Yang check_io(ns_ctx); 4377991eb19SZiye Yang } 4387991eb19SZiye Yang 4390aa29864SBen Walker if (spdk_get_ticks() > tsc_end) { 4407991eb19SZiye Yang break; 4417991eb19SZiye Yang } 4427991eb19SZiye Yang } 4437991eb19SZiye Yang 4444c3fd228SShuhei Matsumoto TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) { 4457991eb19SZiye Yang drain_io(ns_ctx); 4467991eb19SZiye Yang cleanup_ns_worker_ctx(ns_ctx); 4477991eb19SZiye Yang } 4487991eb19SZiye Yang 4497991eb19SZiye Yang return 0; 4507991eb19SZiye Yang } 4517991eb19SZiye Yang 4527991eb19SZiye Yang static void 4537991eb19SZiye Yang usage(char *program_name) 4547991eb19SZiye Yang { 4557991eb19SZiye Yang printf("%s options", program_name); 4564518e4c3SChangpeng Liu printf("\t\n"); 4574518e4c3SChangpeng Liu printf("\t[-d DPDK huge memory size in MB]\n"); 4587991eb19SZiye Yang printf("\t[-q io depth]\n"); 45969b4e8b1Swanghailiangx printf("\t[-o io size in bytes]\n"); 4607991eb19SZiye Yang printf("\t[-w io pattern type, must be one of\n"); 4617991eb19SZiye Yang printf("\t\t(read, write, randread, randwrite, rw, randrw)]\n"); 4627991eb19SZiye Yang printf("\t[-M rwmixread (100 for reads, 0 for writes)]\n"); 4634518e4c3SChangpeng Liu #ifdef DEBUG 4644518e4c3SChangpeng Liu printf("\t[-L enable debug logging]\n"); 4654518e4c3SChangpeng Liu #else 466809ae055Swanghailiangx printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n"); 4674518e4c3SChangpeng Liu #endif 4684518e4c3SChangpeng Liu spdk_log_usage(stdout, "\t\t-L"); 4697991eb19SZiye Yang printf("\t[-l enable latency tracking, default: disabled]\n"); 4707991eb19SZiye Yang printf("\t\t(0 - disabled; 1 - enabled)\n"); 4717991eb19SZiye Yang printf("\t[-t time in seconds]\n"); 4727991eb19SZiye Yang printf("\t[-c core mask for I/O submission/completion.]\n"); 4737991eb19SZiye Yang printf("\t\t(default: 0xf - 4 cores)]\n"); 4747991eb19SZiye Yang printf("\t[-m max completions per poll]\n"); 4757991eb19SZiye Yang printf("\t\t(default: 0 - unlimited)\n"); 4767991eb19SZiye Yang printf("\t[-a arbitration mechanism, must be one of below]\n"); 4777991eb19SZiye Yang printf("\t\t(0, 1, 2)]\n"); 4787991eb19SZiye Yang printf("\t\t(0: default round robin mechanism)]\n"); 4797991eb19SZiye Yang printf("\t\t(1: weighted round robin mechanism)]\n"); 4807991eb19SZiye Yang printf("\t\t(2: vendor specific mechanism)]\n"); 4817991eb19SZiye Yang printf("\t[-b enable arbitration user configuration, default: disabled]\n"); 4827991eb19SZiye Yang printf("\t\t(0 - disabled; 1 - enabled)\n"); 48325270f1dSBen Walker printf("\t[-n subjected IOs for performance comparison]\n"); 48425270f1dSBen Walker printf("\t[-i shared memory group ID]\n"); 48574dcf4aaSMao Jiang printf("\t[-r remote NVMe over Fabrics target address]\n"); 48674dcf4aaSMao Jiang printf("\t[-g use single file descriptor for DPDK memory segments]\n"); 4877991eb19SZiye Yang } 4887991eb19SZiye Yang 4897991eb19SZiye Yang static const char * 4907991eb19SZiye Yang print_qprio(enum spdk_nvme_qprio qprio) 4917991eb19SZiye Yang { 4927991eb19SZiye Yang switch (qprio) { 4937991eb19SZiye Yang case SPDK_NVME_QPRIO_URGENT: 4947991eb19SZiye Yang return "urgent priority queue"; 4957991eb19SZiye Yang case SPDK_NVME_QPRIO_HIGH: 4967991eb19SZiye Yang return "high priority queue"; 4977991eb19SZiye Yang case SPDK_NVME_QPRIO_MEDIUM: 4987991eb19SZiye Yang return "medium priority queue"; 4997991eb19SZiye Yang case SPDK_NVME_QPRIO_LOW: 5007991eb19SZiye Yang return "low priority queue"; 5017991eb19SZiye Yang default: 5027991eb19SZiye Yang return "invalid priority queue"; 5037991eb19SZiye Yang } 5047991eb19SZiye Yang } 5057991eb19SZiye Yang 5067991eb19SZiye Yang 5077991eb19SZiye Yang static void 5087991eb19SZiye Yang print_configuration(char *program_name) 5097991eb19SZiye Yang { 5107991eb19SZiye Yang printf("%s run with configuration:\n", program_name); 5119b847c15SGangCao printf("%s -q %d -s %d -w %s -M %d -l %d -t %d -c %s -m %d -a %d -b %d -n %d -i %d\n", 5127991eb19SZiye Yang program_name, 5137991eb19SZiye Yang g_arbitration.queue_depth, 5147991eb19SZiye Yang g_arbitration.io_size_bytes, 5157991eb19SZiye Yang g_arbitration.workload_type, 5167991eb19SZiye Yang g_arbitration.rw_percentage, 5177991eb19SZiye Yang g_arbitration.latency_tracking_enable, 5187991eb19SZiye Yang g_arbitration.time_in_sec, 5197991eb19SZiye Yang g_arbitration.core_mask, 5207991eb19SZiye Yang g_arbitration.max_completions, 5217991eb19SZiye Yang g_arbitration.arbitration_mechanism, 5227991eb19SZiye Yang g_arbitration.arbitration_config, 5239b847c15SGangCao g_arbitration.io_count, 5249b847c15SGangCao g_arbitration.shm_id); 5257991eb19SZiye Yang } 5267991eb19SZiye Yang 5277991eb19SZiye Yang 5287991eb19SZiye Yang static void 5297991eb19SZiye Yang print_performance(void) 5307991eb19SZiye Yang { 5317991eb19SZiye Yang float io_per_second, sent_all_io_in_secs; 5327991eb19SZiye Yang struct worker_thread *worker; 5337991eb19SZiye Yang struct ns_worker_ctx *ns_ctx; 5347991eb19SZiye Yang 5354c3fd228SShuhei Matsumoto TAILQ_FOREACH(worker, &g_workers, link) { 5364c3fd228SShuhei Matsumoto TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) { 5377991eb19SZiye Yang io_per_second = (float)ns_ctx->io_completed / g_arbitration.time_in_sec; 5387991eb19SZiye Yang sent_all_io_in_secs = g_arbitration.io_count / io_per_second; 5397991eb19SZiye Yang printf("%-43.43s core %u: %8.2f IO/s %8.2f secs/%d ios\n", 5407991eb19SZiye Yang ns_ctx->entry->name, worker->lcore, 5417991eb19SZiye Yang io_per_second, sent_all_io_in_secs, g_arbitration.io_count); 5427991eb19SZiye Yang } 5437991eb19SZiye Yang } 5447991eb19SZiye Yang printf("========================================================\n"); 5457991eb19SZiye Yang 5467991eb19SZiye Yang printf("\n"); 5477991eb19SZiye Yang } 5487991eb19SZiye Yang 5497991eb19SZiye Yang static void 5507991eb19SZiye Yang print_latency_page(struct ctrlr_entry *entry) 5517991eb19SZiye Yang { 5527991eb19SZiye Yang int i; 5537991eb19SZiye Yang 5547991eb19SZiye Yang printf("\n"); 5557991eb19SZiye Yang printf("%s\n", entry->name); 5567991eb19SZiye Yang printf("--------------------------------------------------------\n"); 5577991eb19SZiye Yang 5587991eb19SZiye Yang for (i = 0; i < 32; i++) { 559df56ab77SDaniel Verkamp if (entry->latency_page.buckets_32us[i]) 5607991eb19SZiye Yang printf("Bucket %dus - %dus: %d\n", i * 32, (i + 1) * 32, 561df56ab77SDaniel Verkamp entry->latency_page.buckets_32us[i]); 5627991eb19SZiye Yang } 5637991eb19SZiye Yang for (i = 0; i < 31; i++) { 564df56ab77SDaniel Verkamp if (entry->latency_page.buckets_1ms[i]) 5657991eb19SZiye Yang printf("Bucket %dms - %dms: %d\n", i + 1, i + 2, 566df56ab77SDaniel Verkamp entry->latency_page.buckets_1ms[i]); 5677991eb19SZiye Yang } 5687991eb19SZiye Yang for (i = 0; i < 31; i++) { 569df56ab77SDaniel Verkamp if (entry->latency_page.buckets_32ms[i]) 5707991eb19SZiye Yang printf("Bucket %dms - %dms: %d\n", (i + 1) * 32, (i + 2) * 32, 571df56ab77SDaniel Verkamp entry->latency_page.buckets_32ms[i]); 5727991eb19SZiye Yang } 5737991eb19SZiye Yang } 5747991eb19SZiye Yang 5757991eb19SZiye Yang static void 5767991eb19SZiye Yang print_latency_statistics(const char *op_name, enum spdk_nvme_intel_log_page log_page) 5777991eb19SZiye Yang { 5787991eb19SZiye Yang struct ctrlr_entry *ctrlr; 5797991eb19SZiye Yang 5807991eb19SZiye Yang printf("%s Latency Statistics:\n", op_name); 5817991eb19SZiye Yang printf("========================================================\n"); 5824c3fd228SShuhei Matsumoto TAILQ_FOREACH(ctrlr, &g_controllers, link) { 5837991eb19SZiye Yang if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr->ctrlr, log_page)) { 5847991eb19SZiye Yang if (spdk_nvme_ctrlr_cmd_get_log_page( 5857991eb19SZiye Yang ctrlr->ctrlr, log_page, 5867991eb19SZiye Yang SPDK_NVME_GLOBAL_NS_TAG, 587df56ab77SDaniel Verkamp &ctrlr->latency_page, 5887991eb19SZiye Yang sizeof(struct spdk_nvme_intel_rw_latency_page), 589d24ea900SDaniel Verkamp 0, 5907991eb19SZiye Yang enable_latency_tracking_complete, 5917991eb19SZiye Yang NULL)) { 5927991eb19SZiye Yang printf("nvme_ctrlr_cmd_get_log_page() failed\n"); 5937991eb19SZiye Yang exit(1); 5947991eb19SZiye Yang } 5957991eb19SZiye Yang 5967991eb19SZiye Yang g_arbitration.outstanding_commands++; 5977991eb19SZiye Yang } else { 5987991eb19SZiye Yang printf("Controller %s: %s latency statistics not supported\n", 5997991eb19SZiye Yang ctrlr->name, op_name); 6007991eb19SZiye Yang } 6017991eb19SZiye Yang } 6027991eb19SZiye Yang 6037991eb19SZiye Yang while (g_arbitration.outstanding_commands) { 6044c3fd228SShuhei Matsumoto TAILQ_FOREACH(ctrlr, &g_controllers, link) { 6057991eb19SZiye Yang spdk_nvme_ctrlr_process_admin_completions(ctrlr->ctrlr); 6067991eb19SZiye Yang } 6077991eb19SZiye Yang } 6087991eb19SZiye Yang 6094c3fd228SShuhei Matsumoto TAILQ_FOREACH(ctrlr, &g_controllers, link) { 6107991eb19SZiye Yang if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr->ctrlr, log_page)) { 6117991eb19SZiye Yang print_latency_page(ctrlr); 6127991eb19SZiye Yang } 6137991eb19SZiye Yang } 6147991eb19SZiye Yang printf("\n"); 6157991eb19SZiye Yang } 6167991eb19SZiye Yang 6177991eb19SZiye Yang static void 6187991eb19SZiye Yang print_stats(void) 6197991eb19SZiye Yang { 6207991eb19SZiye Yang print_performance(); 6217991eb19SZiye Yang if (g_arbitration.latency_tracking_enable) { 6227991eb19SZiye Yang if (g_arbitration.rw_percentage != 0) { 6237991eb19SZiye Yang print_latency_statistics("Read", SPDK_NVME_INTEL_LOG_READ_CMD_LATENCY); 6247991eb19SZiye Yang } 6257991eb19SZiye Yang if (g_arbitration.rw_percentage != 100) { 6267991eb19SZiye Yang print_latency_statistics("Write", SPDK_NVME_INTEL_LOG_WRITE_CMD_LATENCY); 6277991eb19SZiye Yang } 6287991eb19SZiye Yang } 6297991eb19SZiye Yang } 6307991eb19SZiye Yang 6317991eb19SZiye Yang static int 6327991eb19SZiye Yang parse_args(int argc, char **argv) 6337991eb19SZiye Yang { 6347991eb19SZiye Yang const char *workload_type = NULL; 6357991eb19SZiye Yang int op = 0; 6367991eb19SZiye Yang bool mix_specified = false; 6374518e4c3SChangpeng Liu int rc; 6380f9dc2afSShuhei Matsumoto long int val; 6397991eb19SZiye Yang 64074dcf4aaSMao Jiang spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE); 64174dcf4aaSMao Jiang snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN); 64274dcf4aaSMao Jiang 64369b4e8b1Swanghailiangx while ((op = getopt(argc, argv, "a:b:c:d:ghi:l:m:n:o:q:r:t:w:M:L:")) != -1) { 6447991eb19SZiye Yang switch (op) { 6457991eb19SZiye Yang case 'c': 6467991eb19SZiye Yang g_arbitration.core_mask = optarg; 6477991eb19SZiye Yang break; 6484518e4c3SChangpeng Liu case 'd': 6494518e4c3SChangpeng Liu g_dpdk_mem = spdk_strtol(optarg, 10); 6504518e4c3SChangpeng Liu if (g_dpdk_mem < 0) { 6514518e4c3SChangpeng Liu fprintf(stderr, "Invalid DPDK memory size\n"); 6524518e4c3SChangpeng Liu return g_dpdk_mem; 6534518e4c3SChangpeng Liu } 6544518e4c3SChangpeng Liu break; 6557991eb19SZiye Yang case 'w': 6567991eb19SZiye Yang g_arbitration.workload_type = optarg; 6577991eb19SZiye Yang break; 65874dcf4aaSMao Jiang case 'r': 65974dcf4aaSMao Jiang if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) { 66074dcf4aaSMao Jiang fprintf(stderr, "Error parsing transport address\n"); 66174dcf4aaSMao Jiang return 1; 66274dcf4aaSMao Jiang } 66374dcf4aaSMao Jiang break; 66474dcf4aaSMao Jiang case 'g': 6654518e4c3SChangpeng Liu g_dpdk_mem_single_seg = true; 66674dcf4aaSMao Jiang break; 6670f9dc2afSShuhei Matsumoto case 'h': 6680f9dc2afSShuhei Matsumoto case '?': 6690f9dc2afSShuhei Matsumoto usage(argv[0]); 6700f9dc2afSShuhei Matsumoto return 1; 6714518e4c3SChangpeng Liu case 'L': 6724518e4c3SChangpeng Liu rc = spdk_log_set_flag(optarg); 6734518e4c3SChangpeng Liu if (rc < 0) { 6744518e4c3SChangpeng Liu fprintf(stderr, "unknown flag\n"); 6754518e4c3SChangpeng Liu usage(argv[0]); 6764518e4c3SChangpeng Liu exit(EXIT_FAILURE); 6774518e4c3SChangpeng Liu } 6784518e4c3SChangpeng Liu #ifdef DEBUG 6794518e4c3SChangpeng Liu spdk_log_set_print_level(SPDK_LOG_DEBUG); 6804518e4c3SChangpeng Liu #endif 6814518e4c3SChangpeng Liu break; 6820f9dc2afSShuhei Matsumoto default: 6830f9dc2afSShuhei Matsumoto val = spdk_strtol(optarg, 10); 6840f9dc2afSShuhei Matsumoto if (val < 0) { 6850f9dc2afSShuhei Matsumoto fprintf(stderr, "Converting a string to integer failed\n"); 6860f9dc2afSShuhei Matsumoto return val; 6870f9dc2afSShuhei Matsumoto } 6880f9dc2afSShuhei Matsumoto switch (op) { 6890f9dc2afSShuhei Matsumoto case 'i': 6900f9dc2afSShuhei Matsumoto g_arbitration.shm_id = val; 6910f9dc2afSShuhei Matsumoto break; 6920f9dc2afSShuhei Matsumoto case 'l': 6930f9dc2afSShuhei Matsumoto g_arbitration.latency_tracking_enable = val; 6940f9dc2afSShuhei Matsumoto break; 6950f9dc2afSShuhei Matsumoto case 'm': 6960f9dc2afSShuhei Matsumoto g_arbitration.max_completions = val; 6970f9dc2afSShuhei Matsumoto break; 6980f9dc2afSShuhei Matsumoto case 'q': 6990f9dc2afSShuhei Matsumoto g_arbitration.queue_depth = val; 7000f9dc2afSShuhei Matsumoto break; 70169b4e8b1Swanghailiangx case 'o': 7020f9dc2afSShuhei Matsumoto g_arbitration.io_size_bytes = val; 7030f9dc2afSShuhei Matsumoto break; 7040f9dc2afSShuhei Matsumoto case 't': 7050f9dc2afSShuhei Matsumoto g_arbitration.time_in_sec = val; 7060f9dc2afSShuhei Matsumoto break; 7077991eb19SZiye Yang case 'M': 7080f9dc2afSShuhei Matsumoto g_arbitration.rw_percentage = val; 7097991eb19SZiye Yang mix_specified = true; 7107991eb19SZiye Yang break; 7117991eb19SZiye Yang case 'a': 7120f9dc2afSShuhei Matsumoto g_arbitration.arbitration_mechanism = val; 7137991eb19SZiye Yang break; 7147991eb19SZiye Yang case 'b': 7150f9dc2afSShuhei Matsumoto g_arbitration.arbitration_config = val; 7167991eb19SZiye Yang break; 71725270f1dSBen Walker case 'n': 7180f9dc2afSShuhei Matsumoto g_arbitration.io_count = val; 7197991eb19SZiye Yang break; 7207991eb19SZiye Yang default: 7217991eb19SZiye Yang usage(argv[0]); 7220f9dc2afSShuhei Matsumoto return -EINVAL; 7230f9dc2afSShuhei Matsumoto } 7247991eb19SZiye Yang } 7257991eb19SZiye Yang } 7267991eb19SZiye Yang 7277991eb19SZiye Yang workload_type = g_arbitration.workload_type; 7287991eb19SZiye Yang 7297991eb19SZiye Yang if (strcmp(workload_type, "read") && 7307991eb19SZiye Yang strcmp(workload_type, "write") && 7317991eb19SZiye Yang strcmp(workload_type, "randread") && 7327991eb19SZiye Yang strcmp(workload_type, "randwrite") && 7337991eb19SZiye Yang strcmp(workload_type, "rw") && 7347991eb19SZiye Yang strcmp(workload_type, "randrw")) { 7357991eb19SZiye Yang fprintf(stderr, 7367991eb19SZiye Yang "io pattern type must be one of\n" 7377991eb19SZiye Yang "(read, write, randread, randwrite, rw, randrw)\n"); 7387991eb19SZiye Yang return 1; 7397991eb19SZiye Yang } 7407991eb19SZiye Yang 7417991eb19SZiye Yang if (!strcmp(workload_type, "read") || 7427991eb19SZiye Yang !strcmp(workload_type, "randread")) { 7437991eb19SZiye Yang g_arbitration.rw_percentage = 100; 7447991eb19SZiye Yang } 7457991eb19SZiye Yang 7467991eb19SZiye Yang if (!strcmp(workload_type, "write") || 7477991eb19SZiye Yang !strcmp(workload_type, "randwrite")) { 7487991eb19SZiye Yang g_arbitration.rw_percentage = 0; 7497991eb19SZiye Yang } 7507991eb19SZiye Yang 7517991eb19SZiye Yang if (!strcmp(workload_type, "read") || 7527991eb19SZiye Yang !strcmp(workload_type, "randread") || 7537991eb19SZiye Yang !strcmp(workload_type, "write") || 7547991eb19SZiye Yang !strcmp(workload_type, "randwrite")) { 7557991eb19SZiye Yang if (mix_specified) { 7567991eb19SZiye Yang fprintf(stderr, "Ignoring -M option... Please use -M option" 7577991eb19SZiye Yang " only when using rw or randrw.\n"); 7587991eb19SZiye Yang } 7597991eb19SZiye Yang } 7607991eb19SZiye Yang 7617991eb19SZiye Yang if (!strcmp(workload_type, "rw") || 7627991eb19SZiye Yang !strcmp(workload_type, "randrw")) { 7637991eb19SZiye Yang if (g_arbitration.rw_percentage < 0 || g_arbitration.rw_percentage > 100) { 7647991eb19SZiye Yang fprintf(stderr, 7657991eb19SZiye Yang "-M must be specified to value from 0 to 100 " 7667991eb19SZiye Yang "for rw or randrw.\n"); 7677991eb19SZiye Yang return 1; 7687991eb19SZiye Yang } 7697991eb19SZiye Yang } 7707991eb19SZiye Yang 7717991eb19SZiye Yang if (!strcmp(workload_type, "read") || 7727991eb19SZiye Yang !strcmp(workload_type, "write") || 7737991eb19SZiye Yang !strcmp(workload_type, "rw")) { 7747991eb19SZiye Yang g_arbitration.is_random = 0; 7757991eb19SZiye Yang } else { 7767991eb19SZiye Yang g_arbitration.is_random = 1; 7777991eb19SZiye Yang } 7787991eb19SZiye Yang 7797991eb19SZiye Yang if (g_arbitration.latency_tracking_enable != 0 && 7807991eb19SZiye Yang g_arbitration.latency_tracking_enable != 1) { 7817991eb19SZiye Yang fprintf(stderr, 7827991eb19SZiye Yang "-l must be specified to value 0 or 1.\n"); 7837991eb19SZiye Yang return 1; 7847991eb19SZiye Yang } 7857991eb19SZiye Yang 7867991eb19SZiye Yang switch (g_arbitration.arbitration_mechanism) { 7877991eb19SZiye Yang case SPDK_NVME_CC_AMS_RR: 7887991eb19SZiye Yang case SPDK_NVME_CC_AMS_WRR: 7897991eb19SZiye Yang case SPDK_NVME_CC_AMS_VS: 7907991eb19SZiye Yang break; 7917991eb19SZiye Yang default: 7927991eb19SZiye Yang fprintf(stderr, 7937991eb19SZiye Yang "-a must be specified to value 0, 1, or 7.\n"); 7947991eb19SZiye Yang return 1; 7957991eb19SZiye Yang } 7967991eb19SZiye Yang 7977991eb19SZiye Yang if (g_arbitration.arbitration_config != 0 && 7987991eb19SZiye Yang g_arbitration.arbitration_config != 1) { 7997991eb19SZiye Yang fprintf(stderr, 8007991eb19SZiye Yang "-b must be specified to value 0 or 1.\n"); 8017991eb19SZiye Yang return 1; 8027991eb19SZiye Yang } else if (g_arbitration.arbitration_config == 1 && 8037991eb19SZiye Yang g_arbitration.arbitration_mechanism != SPDK_NVME_CC_AMS_WRR) { 8047991eb19SZiye Yang fprintf(stderr, 8057991eb19SZiye Yang "-a must be specified to 1 (WRR) together.\n"); 8067991eb19SZiye Yang return 1; 8077991eb19SZiye Yang } 8087991eb19SZiye Yang 8097991eb19SZiye Yang return 0; 8107991eb19SZiye Yang } 8117991eb19SZiye Yang 8127991eb19SZiye Yang static int 8137991eb19SZiye Yang register_workers(void) 8147991eb19SZiye Yang { 8157f7c03a9SBen Walker uint32_t i; 8167991eb19SZiye Yang struct worker_thread *worker; 8177991eb19SZiye Yang enum spdk_nvme_qprio qprio = SPDK_NVME_QPRIO_URGENT; 8187991eb19SZiye Yang 8197f7c03a9SBen Walker SPDK_ENV_FOREACH_CORE(i) { 82084230409SBen Walker worker = calloc(1, sizeof(*worker)); 8217991eb19SZiye Yang if (worker == NULL) { 82284230409SBen Walker fprintf(stderr, "Unable to allocate worker\n"); 82384230409SBen Walker return -1; 8247991eb19SZiye Yang } 8257991eb19SZiye Yang 8264c3fd228SShuhei Matsumoto TAILQ_INIT(&worker->ns_ctx); 8277f7c03a9SBen Walker worker->lcore = i; 8284c3fd228SShuhei Matsumoto TAILQ_INSERT_TAIL(&g_workers, worker, link); 8297991eb19SZiye Yang g_arbitration.num_workers++; 8307991eb19SZiye Yang 8317991eb19SZiye Yang if (g_arbitration.arbitration_mechanism == SPDK_NVME_CAP_AMS_WRR) { 8327991eb19SZiye Yang qprio++; 8337991eb19SZiye Yang } 83484230409SBen Walker 8353f2660d7SChangpeng Liu worker->qprio = qprio & SPDK_NVME_CREATE_IO_SQ_QPRIO_MASK; 8367991eb19SZiye Yang } 8377991eb19SZiye Yang 8387991eb19SZiye Yang return 0; 8397991eb19SZiye Yang } 8407991eb19SZiye Yang 8417991eb19SZiye Yang static bool 84232e838afSBen Walker probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 843fcb00f37SDaniel Verkamp struct spdk_nvme_ctrlr_opts *opts) 8447991eb19SZiye Yang { 8457991eb19SZiye Yang /* Update with user specified arbitration configuration */ 8467991eb19SZiye Yang opts->arb_mechanism = g_arbitration.arbitration_mechanism; 8477991eb19SZiye Yang 84832e838afSBen Walker printf("Attaching to %s\n", trid->traddr); 8497991eb19SZiye Yang 8507991eb19SZiye Yang return true; 8517991eb19SZiye Yang } 8527991eb19SZiye Yang 8537991eb19SZiye Yang static void 85432e838afSBen Walker attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 855fcb00f37SDaniel Verkamp struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 8567991eb19SZiye Yang { 85732e838afSBen Walker printf("Attached to %s\n", trid->traddr); 8587991eb19SZiye Yang 8597991eb19SZiye Yang /* Update with actual arbitration configuration in use */ 8607991eb19SZiye Yang g_arbitration.arbitration_mechanism = opts->arb_mechanism; 8617991eb19SZiye Yang 8627991eb19SZiye Yang register_ctrlr(ctrlr); 8637991eb19SZiye Yang } 8647991eb19SZiye Yang 8657991eb19SZiye Yang static int 8667991eb19SZiye Yang register_controllers(void) 8677991eb19SZiye Yang { 8687991eb19SZiye Yang printf("Initializing NVMe Controllers\n"); 8697991eb19SZiye Yang 87074dcf4aaSMao Jiang if (spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL) != 0) { 8717991eb19SZiye Yang fprintf(stderr, "spdk_nvme_probe() failed\n"); 8727991eb19SZiye Yang return 1; 8737991eb19SZiye Yang } 8747991eb19SZiye Yang 8757991eb19SZiye Yang if (g_arbitration.num_namespaces == 0) { 8767991eb19SZiye Yang fprintf(stderr, "No valid namespaces to continue IO testing\n"); 8777991eb19SZiye Yang return 1; 8787991eb19SZiye Yang } 8797991eb19SZiye Yang 8807991eb19SZiye Yang return 0; 8817991eb19SZiye Yang } 8827991eb19SZiye Yang 8837991eb19SZiye Yang static void 8847991eb19SZiye Yang unregister_controllers(void) 8857991eb19SZiye Yang { 8864c3fd228SShuhei Matsumoto struct ctrlr_entry *entry, *tmp; 8870a903c91SShuhei Matsumoto struct spdk_nvme_detach_ctx *detach_ctx = NULL; 8887991eb19SZiye Yang 8894c3fd228SShuhei Matsumoto TAILQ_FOREACH_SAFE(entry, &g_controllers, link, tmp) { 8904c3fd228SShuhei Matsumoto TAILQ_REMOVE(&g_controllers, entry, link); 8917991eb19SZiye Yang if (g_arbitration.latency_tracking_enable && 89259970a89SDaniel Verkamp spdk_nvme_ctrlr_is_feature_supported(entry->ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING)) { 8937991eb19SZiye Yang set_latency_tracking_feature(entry->ctrlr, false); 89459970a89SDaniel Verkamp } 8950a903c91SShuhei Matsumoto spdk_nvme_detach_async(entry->ctrlr, &detach_ctx); 8967991eb19SZiye Yang free(entry); 8977991eb19SZiye Yang } 8980a903c91SShuhei Matsumoto 8990a903c91SShuhei Matsumoto while (detach_ctx && spdk_nvme_detach_poll_async(detach_ctx) == -EAGAIN) { 9000a903c91SShuhei Matsumoto ; 9010a903c91SShuhei Matsumoto } 9027991eb19SZiye Yang } 9037991eb19SZiye Yang 9047991eb19SZiye Yang static int 9057991eb19SZiye Yang associate_workers_with_ns(void) 9067991eb19SZiye Yang { 9074c3fd228SShuhei Matsumoto struct ns_entry *entry = TAILQ_FIRST(&g_namespaces); 9084c3fd228SShuhei Matsumoto struct worker_thread *worker = TAILQ_FIRST(&g_workers); 9097991eb19SZiye Yang struct ns_worker_ctx *ns_ctx; 9107991eb19SZiye Yang int i, count; 9117991eb19SZiye Yang 9127991eb19SZiye Yang count = g_arbitration.num_namespaces > g_arbitration.num_workers ? 9137991eb19SZiye Yang g_arbitration.num_namespaces : g_arbitration.num_workers; 9147991eb19SZiye Yang 9157991eb19SZiye Yang for (i = 0; i < count; i++) { 9167991eb19SZiye Yang if (entry == NULL) { 9177991eb19SZiye Yang break; 9187991eb19SZiye Yang } 9197991eb19SZiye Yang 9207991eb19SZiye Yang ns_ctx = malloc(sizeof(struct ns_worker_ctx)); 9217991eb19SZiye Yang if (!ns_ctx) { 9227991eb19SZiye Yang return 1; 9237991eb19SZiye Yang } 9247991eb19SZiye Yang memset(ns_ctx, 0, sizeof(*ns_ctx)); 9257991eb19SZiye Yang 9267991eb19SZiye Yang printf("Associating %s with lcore %d\n", entry->name, worker->lcore); 9277991eb19SZiye Yang ns_ctx->entry = entry; 9284c3fd228SShuhei Matsumoto TAILQ_INSERT_TAIL(&worker->ns_ctx, ns_ctx, link); 9297991eb19SZiye Yang 9304c3fd228SShuhei Matsumoto worker = TAILQ_NEXT(worker, link); 9317991eb19SZiye Yang if (worker == NULL) { 9324c3fd228SShuhei Matsumoto worker = TAILQ_FIRST(&g_workers); 9337991eb19SZiye Yang } 9347991eb19SZiye Yang 9354c3fd228SShuhei Matsumoto entry = TAILQ_NEXT(entry, link); 9367991eb19SZiye Yang if (entry == NULL) { 9374c3fd228SShuhei Matsumoto entry = TAILQ_FIRST(&g_namespaces); 9387991eb19SZiye Yang } 9397991eb19SZiye Yang 9407991eb19SZiye Yang } 9417991eb19SZiye Yang 9427991eb19SZiye Yang return 0; 9437991eb19SZiye Yang } 9447991eb19SZiye Yang 9457991eb19SZiye Yang static void 9467991eb19SZiye Yang get_feature_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl) 9477991eb19SZiye Yang { 9487991eb19SZiye Yang struct feature *feature = cb_arg; 9497991eb19SZiye Yang int fid = feature - features; 9507991eb19SZiye Yang 9517991eb19SZiye Yang if (spdk_nvme_cpl_is_error(cpl)) { 9527991eb19SZiye Yang printf("get_feature(0x%02X) failed\n", fid); 9537991eb19SZiye Yang } else { 9547991eb19SZiye Yang feature->result = cpl->cdw0; 9557991eb19SZiye Yang feature->valid = true; 9567991eb19SZiye Yang } 9577991eb19SZiye Yang 9587991eb19SZiye Yang g_arbitration.outstanding_commands--; 9597991eb19SZiye Yang } 9607991eb19SZiye Yang 9617991eb19SZiye Yang static int 9627991eb19SZiye Yang get_feature(struct spdk_nvme_ctrlr *ctrlr, uint8_t fid) 9637991eb19SZiye Yang { 9647991eb19SZiye Yang struct spdk_nvme_cmd cmd = {}; 965b97aa6daSChangpeng Liu struct feature *feature = &features[fid]; 966b97aa6daSChangpeng Liu 967b97aa6daSChangpeng Liu feature->valid = false; 9687991eb19SZiye Yang 9697991eb19SZiye Yang cmd.opc = SPDK_NVME_OPC_GET_FEATURES; 9701fea1fccSChangpeng Liu cmd.cdw10_bits.get_features.fid = fid; 9717991eb19SZiye Yang 972b97aa6daSChangpeng Liu return spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, NULL, 0, get_feature_completion, feature); 9737991eb19SZiye Yang } 9747991eb19SZiye Yang 9757991eb19SZiye Yang static void 9767991eb19SZiye Yang get_arb_feature(struct spdk_nvme_ctrlr *ctrlr) 9777991eb19SZiye Yang { 9787991eb19SZiye Yang get_feature(ctrlr, SPDK_NVME_FEAT_ARBITRATION); 9797991eb19SZiye Yang 9807991eb19SZiye Yang g_arbitration.outstanding_commands++; 9817991eb19SZiye Yang 9827991eb19SZiye Yang while (g_arbitration.outstanding_commands) { 9837991eb19SZiye Yang spdk_nvme_ctrlr_process_admin_completions(ctrlr); 9847991eb19SZiye Yang } 9857991eb19SZiye Yang 9867991eb19SZiye Yang if (features[SPDK_NVME_FEAT_ARBITRATION].valid) { 987d7b9a14cSChangpeng Liu union spdk_nvme_cmd_cdw11 arb; 988d7b9a14cSChangpeng Liu arb.feat_arbitration.raw = features[SPDK_NVME_FEAT_ARBITRATION].result; 9897991eb19SZiye Yang 9907991eb19SZiye Yang printf("Current Arbitration Configuration\n"); 9917991eb19SZiye Yang printf("===========\n"); 9927991eb19SZiye Yang printf("Arbitration Burst: "); 9933f2660d7SChangpeng Liu if (arb.feat_arbitration.bits.ab == SPDK_NVME_ARBITRATION_BURST_UNLIMITED) { 9947991eb19SZiye Yang printf("no limit\n"); 9957991eb19SZiye Yang } else { 996d7b9a14cSChangpeng Liu printf("%u\n", 1u << arb.feat_arbitration.bits.ab); 9977991eb19SZiye Yang } 9987991eb19SZiye Yang 999d7b9a14cSChangpeng Liu printf("Low Priority Weight: %u\n", arb.feat_arbitration.bits.lpw + 1); 1000d7b9a14cSChangpeng Liu printf("Medium Priority Weight: %u\n", arb.feat_arbitration.bits.mpw + 1); 1001d7b9a14cSChangpeng Liu printf("High Priority Weight: %u\n", arb.feat_arbitration.bits.hpw + 1); 10027991eb19SZiye Yang printf("\n"); 10037991eb19SZiye Yang } 10047991eb19SZiye Yang } 10057991eb19SZiye Yang 10067991eb19SZiye Yang static void 10077991eb19SZiye Yang set_feature_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl) 10087991eb19SZiye Yang { 10097991eb19SZiye Yang struct feature *feature = cb_arg; 10107991eb19SZiye Yang int fid = feature - features; 10117991eb19SZiye Yang 10127991eb19SZiye Yang if (spdk_nvme_cpl_is_error(cpl)) { 10137991eb19SZiye Yang printf("set_feature(0x%02X) failed\n", fid); 10147991eb19SZiye Yang feature->valid = false; 10157991eb19SZiye Yang } else { 10167991eb19SZiye Yang printf("Set Arbitration Feature Successfully\n"); 10177991eb19SZiye Yang } 10187991eb19SZiye Yang 10197991eb19SZiye Yang g_arbitration.outstanding_commands--; 10207991eb19SZiye Yang } 10217991eb19SZiye Yang 10227991eb19SZiye Yang static int 10237991eb19SZiye Yang set_arb_feature(struct spdk_nvme_ctrlr *ctrlr) 10247991eb19SZiye Yang { 10257991eb19SZiye Yang int ret; 10267991eb19SZiye Yang struct spdk_nvme_cmd cmd = {}; 10277991eb19SZiye Yang 10287991eb19SZiye Yang cmd.opc = SPDK_NVME_OPC_SET_FEATURES; 10291fea1fccSChangpeng Liu cmd.cdw10_bits.set_features.fid = SPDK_NVME_FEAT_ARBITRATION; 10307991eb19SZiye Yang 10317991eb19SZiye Yang g_arbitration.outstanding_commands = 0; 10327991eb19SZiye Yang 10337991eb19SZiye Yang if (features[SPDK_NVME_FEAT_ARBITRATION].valid) { 10343f2660d7SChangpeng Liu cmd.cdw11_bits.feat_arbitration.bits.ab = SPDK_NVME_ARBITRATION_BURST_UNLIMITED; 10350c9057f0SChangpeng Liu cmd.cdw11_bits.feat_arbitration.bits.lpw = USER_SPECIFIED_LOW_PRIORITY_WEIGHT; 10360c9057f0SChangpeng Liu cmd.cdw11_bits.feat_arbitration.bits.mpw = USER_SPECIFIED_MEDIUM_PRIORITY_WEIGHT; 10370c9057f0SChangpeng Liu cmd.cdw11_bits.feat_arbitration.bits.hpw = USER_SPECIFIED_HIGH_PRIORITY_WEIGHT; 10387991eb19SZiye Yang } 10397991eb19SZiye Yang 10407991eb19SZiye Yang ret = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, NULL, 0, 10417991eb19SZiye Yang set_feature_completion, &features[SPDK_NVME_FEAT_ARBITRATION]); 10427991eb19SZiye Yang if (ret) { 10437991eb19SZiye Yang printf("Set Arbitration Feature: Failed 0x%x\n", ret); 10447991eb19SZiye Yang return 1; 10457991eb19SZiye Yang } 10467991eb19SZiye Yang 10477991eb19SZiye Yang g_arbitration.outstanding_commands++; 10487991eb19SZiye Yang 10497991eb19SZiye Yang while (g_arbitration.outstanding_commands) { 10507991eb19SZiye Yang spdk_nvme_ctrlr_process_admin_completions(ctrlr); 10517991eb19SZiye Yang } 10527991eb19SZiye Yang 10537991eb19SZiye Yang if (!features[SPDK_NVME_FEAT_ARBITRATION].valid) { 10547991eb19SZiye Yang printf("Set Arbitration Feature failed and use default configuration\n"); 10557991eb19SZiye Yang } 10567991eb19SZiye Yang 10577991eb19SZiye Yang return 0; 10587991eb19SZiye Yang } 10597991eb19SZiye Yang 10607991eb19SZiye Yang int 10617991eb19SZiye Yang main(int argc, char **argv) 10627991eb19SZiye Yang { 10637991eb19SZiye Yang int rc; 106487b21afdSJim Harris struct worker_thread *worker, *main_worker; 106587b21afdSJim Harris unsigned main_core; 1066987ba616SGangCao char task_pool_name[30]; 10679ec9c8b3SChangpeng Liu uint32_t task_count = 0; 106818d26e42SBen Walker struct spdk_env_opts opts; 10697991eb19SZiye Yang 10707991eb19SZiye Yang rc = parse_args(argc, argv); 10717991eb19SZiye Yang if (rc != 0) { 10727991eb19SZiye Yang return rc; 10737991eb19SZiye Yang } 10747991eb19SZiye Yang 107557fd99b9SJim Harris opts.opts_size = sizeof(opts); 107618d26e42SBen Walker spdk_env_opts_init(&opts); 107718d26e42SBen Walker opts.name = "arb"; 10784518e4c3SChangpeng Liu opts.mem_size = g_dpdk_mem; 10794518e4c3SChangpeng Liu opts.hugepage_single_segments = g_dpdk_mem_single_seg; 108018d26e42SBen Walker opts.core_mask = g_arbitration.core_mask; 108118d26e42SBen Walker opts.shm_id = g_arbitration.shm_id; 1082095f4254SLance Hartmann if (spdk_env_init(&opts) < 0) { 1083095f4254SLance Hartmann return 1; 1084095f4254SLance Hartmann } 10857991eb19SZiye Yang 10860aa29864SBen Walker g_arbitration.tsc_rate = spdk_get_ticks_hz(); 10877991eb19SZiye Yang 10887991eb19SZiye Yang if (register_workers() != 0) { 10899ec9c8b3SChangpeng Liu rc = 1; 10909ec9c8b3SChangpeng Liu goto exit; 10917991eb19SZiye Yang } 10927991eb19SZiye Yang 10937991eb19SZiye Yang if (register_controllers() != 0) { 10949ec9c8b3SChangpeng Liu rc = 1; 10959ec9c8b3SChangpeng Liu goto exit; 10967991eb19SZiye Yang } 10977991eb19SZiye Yang 10987991eb19SZiye Yang if (associate_workers_with_ns() != 0) { 10999ec9c8b3SChangpeng Liu rc = 1; 11009ec9c8b3SChangpeng Liu goto exit; 11017991eb19SZiye Yang } 11027991eb19SZiye Yang 110311f43621SGangCao snprintf(task_pool_name, sizeof(task_pool_name), "task_pool_%d", getpid()); 110411f43621SGangCao 110511f43621SGangCao /* 110611f43621SGangCao * The task_count will be dynamically calculated based on the 110711f43621SGangCao * number of attached active namespaces, queue depth and number 110811f43621SGangCao * of cores (workers) involved in the IO perations. 110911f43621SGangCao */ 111011f43621SGangCao task_count = g_arbitration.num_namespaces > g_arbitration.num_workers ? 111111f43621SGangCao g_arbitration.num_namespaces : g_arbitration.num_workers; 111211f43621SGangCao task_count *= g_arbitration.queue_depth; 111311f43621SGangCao 111463c1c9d5SZiye Yang task_pool = spdk_mempool_create(task_pool_name, task_count, 1115*186b109dSJim Harris sizeof(struct arb_task), 0, SPDK_ENV_NUMA_ID_ANY); 111611f43621SGangCao if (task_pool == NULL) { 111711f43621SGangCao fprintf(stderr, "could not initialize task pool\n"); 11189ec9c8b3SChangpeng Liu rc = 1; 11199ec9c8b3SChangpeng Liu goto exit; 112011f43621SGangCao } 112111f43621SGangCao 11227991eb19SZiye Yang print_configuration(argv[0]); 11237991eb19SZiye Yang 11247991eb19SZiye Yang printf("Initialization complete. Launching workers.\n"); 11257991eb19SZiye Yang 112687b21afdSJim Harris /* Launch all of the secondary workers */ 112787b21afdSJim Harris main_core = spdk_env_get_current_core(); 112887b21afdSJim Harris main_worker = NULL; 11294c3fd228SShuhei Matsumoto TAILQ_FOREACH(worker, &g_workers, link) { 113087b21afdSJim Harris if (worker->lcore != main_core) { 113163c1c9d5SZiye Yang spdk_env_thread_launch_pinned(worker->lcore, work_fn, worker); 113284230409SBen Walker } else { 113387b21afdSJim Harris assert(main_worker == NULL); 113487b21afdSJim Harris main_worker = worker; 11357991eb19SZiye Yang } 11367991eb19SZiye Yang } 11377991eb19SZiye Yang 113887b21afdSJim Harris assert(main_worker != NULL); 113987b21afdSJim Harris rc = work_fn(main_worker); 114084230409SBen Walker 114163c1c9d5SZiye Yang spdk_env_thread_wait_all(); 114284230409SBen Walker 11437991eb19SZiye Yang print_stats(); 11447991eb19SZiye Yang 11459ec9c8b3SChangpeng Liu exit: 11467991eb19SZiye Yang unregister_controllers(); 114763c1c9d5SZiye Yang cleanup(task_count); 11487991eb19SZiye Yang 11499ec9c8b3SChangpeng Liu spdk_env_fini(); 11509ec9c8b3SChangpeng Liu 11517991eb19SZiye Yang if (rc != 0) { 1152b7876f9aSJosh Soref fprintf(stderr, "%s: errors occurred\n", argv[0]); 11537991eb19SZiye Yang } 11547991eb19SZiye Yang 11557991eb19SZiye Yang return rc; 11567991eb19SZiye Yang } 1157