1488570ebSJim Harris /* SPDX-License-Identifier: BSD-3-Clause 2a6dbe372Spaul luse * Copyright (C) 2016 Intel Corporation. 303823694SCunyin Chang * All rights reserved. 403823694SCunyin Chang */ 503823694SCunyin Chang 6b961d9ccSBen Walker #include "spdk/stdinc.h" 703823694SCunyin Chang 803823694SCunyin Chang #include "spdk/nvme.h" 9104356c9SDaniel Verkamp #include "spdk/queue.h" 100f9dc2afSShuhei Matsumoto #include "spdk/string.h" 11885bc995SJinYu #include "spdk/util.h" 12bace0549SKrzysztof Karas #include "spdk/log.h" 13ce29e013SKrzysztof Karas #include "spdk/rpc.h" 14ce29e013SKrzysztof Karas 15ce29e013SKrzysztof Karas static const char *g_rpc_addr = "/var/tmp/spdk.sock"; 1603823694SCunyin Chang 17104356c9SDaniel Verkamp struct dev_ctx { 18104356c9SDaniel Verkamp TAILQ_ENTRY(dev_ctx) tailq; 19104356c9SDaniel Verkamp bool is_new; 2003823694SCunyin Chang bool is_removed; 21104356c9SDaniel Verkamp bool is_draining; 2203823694SCunyin Chang struct spdk_nvme_ctrlr *ctrlr; 2303823694SCunyin Chang struct spdk_nvme_ns *ns; 24104356c9SDaniel Verkamp struct spdk_nvme_qpair *qpair; 2503823694SCunyin Chang uint32_t io_size_blocks; 2603823694SCunyin Chang uint64_t size_in_ios; 2703823694SCunyin Chang uint64_t io_completed; 28104356c9SDaniel Verkamp uint64_t prev_io_completed; 2903823694SCunyin Chang uint64_t current_queue_depth; 3003823694SCunyin Chang uint64_t offset_in_ios; 31104356c9SDaniel Verkamp char name[1024]; 3203823694SCunyin Chang }; 3303823694SCunyin Chang 3403823694SCunyin Chang struct perf_task { 35104356c9SDaniel Verkamp struct dev_ctx *dev; 3603823694SCunyin Chang void *buf; 3703823694SCunyin Chang }; 3803823694SCunyin Chang 39104356c9SDaniel Verkamp static TAILQ_HEAD(, dev_ctx) g_devs = TAILQ_HEAD_INITIALIZER(g_devs); 4003823694SCunyin Chang 4103823694SCunyin Chang static uint64_t g_tsc_rate; 4203823694SCunyin Chang 43104356c9SDaniel Verkamp static uint32_t g_io_size_bytes = 4096; 44104356c9SDaniel Verkamp static int g_queue_depth = 4; 4503823694SCunyin Chang static int g_time_in_sec; 462bd8c87aSCunyin Chang static int g_expected_insert_times = -1; 472bd8c87aSCunyin Chang static int g_expected_removal_times = -1; 482bd8c87aSCunyin Chang static int g_insert_times; 492bd8c87aSCunyin Chang static int g_removal_times; 5018d26e42SBen Walker static int g_shm_id = -1; 51ac1abb5bSKrzysztof Karas static const char *g_iova_mode = NULL; 52885bc995SJinYu static uint64_t g_timeout_in_us = SPDK_SEC_TO_USEC; 539a99ff90SShuhei Matsumoto static struct spdk_nvme_detach_ctx *g_detach_ctx; 5403823694SCunyin Chang 55ce29e013SKrzysztof Karas static bool g_wait_for_rpc = false; 56ce29e013SKrzysztof Karas static bool g_rpc_received = false; 57ce29e013SKrzysztof Karas 588dd1cd21SBen Walker static void task_complete(struct perf_task *task); 5903823694SCunyin Chang 608dd1cd21SBen Walker static void timeout_cb(void *cb_arg, struct spdk_nvme_ctrlr *ctrlr, 614cfd17a7SChangpeng Liu struct spdk_nvme_qpair *qpair, uint16_t cid); 62885bc995SJinYu 63885bc995SJinYu static void 64104356c9SDaniel Verkamp register_dev(struct spdk_nvme_ctrlr *ctrlr) 6503823694SCunyin Chang { 66104356c9SDaniel Verkamp struct dev_ctx *dev; 6703823694SCunyin Chang const struct spdk_nvme_ctrlr_data *cdata = spdk_nvme_ctrlr_get_data(ctrlr); 6803823694SCunyin Chang 69104356c9SDaniel Verkamp dev = calloc(1, sizeof(*dev)); 70104356c9SDaniel Verkamp if (dev == NULL) { 71104356c9SDaniel Verkamp perror("dev_ctx malloc"); 7203823694SCunyin Chang exit(1); 7303823694SCunyin Chang } 7403823694SCunyin Chang 75104356c9SDaniel Verkamp snprintf(dev->name, sizeof(dev->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn); 76104356c9SDaniel Verkamp 77104356c9SDaniel Verkamp dev->ctrlr = ctrlr; 78104356c9SDaniel Verkamp dev->is_new = true; 79104356c9SDaniel Verkamp dev->is_removed = false; 80104356c9SDaniel Verkamp dev->is_draining = false; 81104356c9SDaniel Verkamp 825288c4dfSMatt Dumm spdk_nvme_ctrlr_register_timeout_callback(ctrlr, g_timeout_in_us, g_timeout_in_us, timeout_cb, 835288c4dfSMatt Dumm NULL); 84885bc995SJinYu 85104356c9SDaniel Verkamp dev->ns = spdk_nvme_ctrlr_get_ns(ctrlr, 1); 86104356c9SDaniel Verkamp 87104356c9SDaniel Verkamp if (!dev->ns || !spdk_nvme_ns_is_active(dev->ns)) { 88d77a04d1SCunyin Chang fprintf(stderr, "Controller %s: No active namespace; skipping\n", dev->name); 89104356c9SDaniel Verkamp goto skip; 9003823694SCunyin Chang } 9103823694SCunyin Chang 92104356c9SDaniel Verkamp if (spdk_nvme_ns_get_size(dev->ns) < g_io_size_bytes || 93104356c9SDaniel Verkamp spdk_nvme_ns_get_sector_size(dev->ns) > g_io_size_bytes) { 94d77a04d1SCunyin Chang fprintf(stderr, "Controller %s: Invalid " 95104356c9SDaniel Verkamp "ns size %" PRIu64 " / block size %u for I/O size %u\n", 96104356c9SDaniel Verkamp dev->name, 97104356c9SDaniel Verkamp spdk_nvme_ns_get_size(dev->ns), 98104356c9SDaniel Verkamp spdk_nvme_ns_get_sector_size(dev->ns), 99104356c9SDaniel Verkamp g_io_size_bytes); 100104356c9SDaniel Verkamp goto skip; 10103823694SCunyin Chang } 10203823694SCunyin Chang 103104356c9SDaniel Verkamp dev->size_in_ios = spdk_nvme_ns_get_size(dev->ns) / g_io_size_bytes; 104104356c9SDaniel Verkamp dev->io_size_blocks = g_io_size_bytes / spdk_nvme_ns_get_sector_size(dev->ns); 105104356c9SDaniel Verkamp 106ce4fcbceSDaniel Verkamp dev->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, NULL, 0); 107104356c9SDaniel Verkamp if (!dev->qpair) { 108d77a04d1SCunyin Chang fprintf(stderr, "ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n"); 109104356c9SDaniel Verkamp goto skip; 110104356c9SDaniel Verkamp } 1112bd8c87aSCunyin Chang g_insert_times++; 112104356c9SDaniel Verkamp TAILQ_INSERT_TAIL(&g_devs, dev, tailq); 113104356c9SDaniel Verkamp return; 114104356c9SDaniel Verkamp 115104356c9SDaniel Verkamp skip: 116104356c9SDaniel Verkamp free(dev); 117104356c9SDaniel Verkamp } 118104356c9SDaniel Verkamp 119104356c9SDaniel Verkamp static void 120104356c9SDaniel Verkamp unregister_dev(struct dev_ctx *dev) 121104356c9SDaniel Verkamp { 122d77a04d1SCunyin Chang fprintf(stderr, "unregister_dev: %s\n", dev->name); 123104356c9SDaniel Verkamp 124104356c9SDaniel Verkamp spdk_nvme_ctrlr_free_io_qpair(dev->qpair); 1259a99ff90SShuhei Matsumoto spdk_nvme_detach_async(dev->ctrlr, &g_detach_ctx); 126104356c9SDaniel Verkamp 127104356c9SDaniel Verkamp TAILQ_REMOVE(&g_devs, dev, tailq); 128104356c9SDaniel Verkamp free(dev); 12903823694SCunyin Chang } 13003823694SCunyin Chang 131455525f5SDaniel Verkamp static struct perf_task * 132455525f5SDaniel Verkamp alloc_task(struct dev_ctx *dev) 13303823694SCunyin Chang { 134455525f5SDaniel Verkamp struct perf_task *task; 135455525f5SDaniel Verkamp 136455525f5SDaniel Verkamp task = calloc(1, sizeof(*task)); 137455525f5SDaniel Verkamp if (task == NULL) { 138455525f5SDaniel Verkamp return NULL; 139455525f5SDaniel Verkamp } 140455525f5SDaniel Verkamp 1418a44220bSJohn Meneghini task->buf = spdk_dma_zmalloc(g_io_size_bytes, 0x200, NULL); 14203823694SCunyin Chang if (task->buf == NULL) { 14369a0f437SDaniel Verkamp free(task); 144455525f5SDaniel Verkamp return NULL; 14503823694SCunyin Chang } 146455525f5SDaniel Verkamp 147455525f5SDaniel Verkamp task->dev = dev; 148455525f5SDaniel Verkamp 149455525f5SDaniel Verkamp return task; 150455525f5SDaniel Verkamp } 151455525f5SDaniel Verkamp 152455525f5SDaniel Verkamp static void 153455525f5SDaniel Verkamp free_task(struct perf_task *task) 154455525f5SDaniel Verkamp { 155455525f5SDaniel Verkamp spdk_dma_free(task->buf); 156455525f5SDaniel Verkamp free(task); 15703823694SCunyin Chang } 15803823694SCunyin Chang 15903823694SCunyin Chang static void io_complete(void *ctx, const struct spdk_nvme_cpl *completion); 16003823694SCunyin Chang 16103823694SCunyin Chang static void 162455525f5SDaniel Verkamp submit_single_io(struct perf_task *task) 16303823694SCunyin Chang { 164455525f5SDaniel Verkamp struct dev_ctx *dev = task->dev; 16503823694SCunyin Chang uint64_t offset_in_ios; 16603823694SCunyin Chang int rc; 16703823694SCunyin Chang 168104356c9SDaniel Verkamp offset_in_ios = dev->offset_in_ios++; 169104356c9SDaniel Verkamp if (dev->offset_in_ios == dev->size_in_ios) { 170104356c9SDaniel Verkamp dev->offset_in_ios = 0; 17103823694SCunyin Chang } 17203823694SCunyin Chang 173104356c9SDaniel Verkamp rc = spdk_nvme_ns_cmd_read(dev->ns, dev->qpair, task->buf, 174104356c9SDaniel Verkamp offset_in_ios * dev->io_size_blocks, 175104356c9SDaniel Verkamp dev->io_size_blocks, io_complete, task, 0); 17603823694SCunyin Chang 17703823694SCunyin Chang if (rc != 0) { 17803823694SCunyin Chang fprintf(stderr, "starting I/O failed\n"); 179455525f5SDaniel Verkamp free_task(task); 18003823694SCunyin Chang } else { 181104356c9SDaniel Verkamp dev->current_queue_depth++; 18203823694SCunyin Chang } 18303823694SCunyin Chang } 18403823694SCunyin Chang 18503823694SCunyin Chang static void 18603823694SCunyin Chang task_complete(struct perf_task *task) 18703823694SCunyin Chang { 188104356c9SDaniel Verkamp struct dev_ctx *dev; 18903823694SCunyin Chang 190104356c9SDaniel Verkamp dev = task->dev; 191104356c9SDaniel Verkamp dev->current_queue_depth--; 192104356c9SDaniel Verkamp dev->io_completed++; 19303823694SCunyin Chang 19403823694SCunyin Chang /* 19503823694SCunyin Chang * is_draining indicates when time has expired for the test run 19603823694SCunyin Chang * and we are just waiting for the previously submitted I/O 19703823694SCunyin Chang * to complete. In this case, do not submit a new I/O to replace 19803823694SCunyin Chang * the one just completed. 19903823694SCunyin Chang */ 200104356c9SDaniel Verkamp if (!dev->is_draining && !dev->is_removed) { 201455525f5SDaniel Verkamp submit_single_io(task); 202455525f5SDaniel Verkamp } else { 203455525f5SDaniel Verkamp free_task(task); 20403823694SCunyin Chang } 20503823694SCunyin Chang } 20603823694SCunyin Chang 20703823694SCunyin Chang static void 20803823694SCunyin Chang io_complete(void *ctx, const struct spdk_nvme_cpl *completion) 20903823694SCunyin Chang { 21003823694SCunyin Chang task_complete((struct perf_task *)ctx); 21103823694SCunyin Chang } 21203823694SCunyin Chang 21303823694SCunyin Chang static void 214104356c9SDaniel Verkamp check_io(struct dev_ctx *dev) 21503823694SCunyin Chang { 216104356c9SDaniel Verkamp spdk_nvme_qpair_process_completions(dev->qpair, 0); 21703823694SCunyin Chang } 21803823694SCunyin Chang 21903823694SCunyin Chang static void 220104356c9SDaniel Verkamp submit_io(struct dev_ctx *dev, int queue_depth) 22103823694SCunyin Chang { 222455525f5SDaniel Verkamp struct perf_task *task; 223455525f5SDaniel Verkamp 22403823694SCunyin Chang while (queue_depth-- > 0) { 225455525f5SDaniel Verkamp task = alloc_task(dev); 226455525f5SDaniel Verkamp if (task == NULL) { 227455525f5SDaniel Verkamp fprintf(stderr, "task allocation failed\n"); 228455525f5SDaniel Verkamp exit(1); 229455525f5SDaniel Verkamp } 230455525f5SDaniel Verkamp 231455525f5SDaniel Verkamp submit_single_io(task); 23203823694SCunyin Chang } 23303823694SCunyin Chang } 23403823694SCunyin Chang 23503823694SCunyin Chang static void 236104356c9SDaniel Verkamp drain_io(struct dev_ctx *dev) 23703823694SCunyin Chang { 238104356c9SDaniel Verkamp dev->is_draining = true; 239104356c9SDaniel Verkamp while (dev->current_queue_depth > 0) { 240104356c9SDaniel Verkamp check_io(dev); 24103823694SCunyin Chang } 24203823694SCunyin Chang } 24303823694SCunyin Chang 24403823694SCunyin Chang static void 24503823694SCunyin Chang print_stats(void) 24603823694SCunyin Chang { 247104356c9SDaniel Verkamp struct dev_ctx *dev; 248104356c9SDaniel Verkamp 249104356c9SDaniel Verkamp TAILQ_FOREACH(dev, &g_devs, tailq) { 250d77a04d1SCunyin Chang fprintf(stderr, "%-43.43s: %10" PRIu64 " I/Os completed (+%" PRIu64 ")\n", 251104356c9SDaniel Verkamp dev->name, 252104356c9SDaniel Verkamp dev->io_completed, 253104356c9SDaniel Verkamp dev->io_completed - dev->prev_io_completed); 254104356c9SDaniel Verkamp dev->prev_io_completed = dev->io_completed; 25503823694SCunyin Chang } 25603823694SCunyin Chang 257d77a04d1SCunyin Chang fprintf(stderr, "\n"); 25803823694SCunyin Chang } 25903823694SCunyin Chang 26003823694SCunyin Chang static bool 26132e838afSBen Walker probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 26203823694SCunyin Chang struct spdk_nvme_ctrlr_opts *opts) 26303823694SCunyin Chang { 264d77a04d1SCunyin Chang fprintf(stderr, "Attaching to %s\n", trid->traddr); 26503823694SCunyin Chang 26603823694SCunyin Chang return true; 26703823694SCunyin Chang } 26803823694SCunyin Chang 26903823694SCunyin Chang static void 27032e838afSBen Walker attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 27103823694SCunyin Chang struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 27203823694SCunyin Chang { 273d77a04d1SCunyin Chang fprintf(stderr, "Attached to %s\n", trid->traddr); 27403823694SCunyin Chang 275104356c9SDaniel Verkamp register_dev(ctrlr); 27603823694SCunyin Chang } 27703823694SCunyin Chang 27803823694SCunyin Chang static void 27903823694SCunyin Chang remove_cb(void *cb_ctx, struct spdk_nvme_ctrlr *ctrlr) 28003823694SCunyin Chang { 281104356c9SDaniel Verkamp struct dev_ctx *dev; 28203823694SCunyin Chang 283104356c9SDaniel Verkamp TAILQ_FOREACH(dev, &g_devs, tailq) { 284104356c9SDaniel Verkamp if (dev->ctrlr == ctrlr) { 285104356c9SDaniel Verkamp /* 286104356c9SDaniel Verkamp * Mark the device as removed, but don't detach yet. 287104356c9SDaniel Verkamp * 288104356c9SDaniel Verkamp * The I/O handling code will detach once it sees that 289104356c9SDaniel Verkamp * is_removed is true and all outstanding I/O have been completed. 290104356c9SDaniel Verkamp */ 291104356c9SDaniel Verkamp dev->is_removed = true; 292d77a04d1SCunyin Chang fprintf(stderr, "Controller removed: %s\n", dev->name); 293104356c9SDaniel Verkamp return; 294104356c9SDaniel Verkamp } 295104356c9SDaniel Verkamp } 296104356c9SDaniel Verkamp 297104356c9SDaniel Verkamp /* 298104356c9SDaniel Verkamp * If we get here, this remove_cb is for a controller that we are not tracking 299104356c9SDaniel Verkamp * in g_devs (for example, because we skipped it during register_dev), 300104356c9SDaniel Verkamp * so immediately detach it. 301104356c9SDaniel Verkamp */ 3029a99ff90SShuhei Matsumoto spdk_nvme_detach_async(ctrlr, &g_detach_ctx); 303104356c9SDaniel Verkamp } 304104356c9SDaniel Verkamp 3052bd8c87aSCunyin Chang static void 3064cfd17a7SChangpeng Liu timeout_cb(void *cb_arg, struct spdk_nvme_ctrlr *ctrlr, 3074cfd17a7SChangpeng Liu struct spdk_nvme_qpair *qpair, uint16_t cid) 3084cfd17a7SChangpeng Liu { 3094cfd17a7SChangpeng Liu /* leave hotplug monitor loop, use the timeout_cb to monitor the hotplug */ 3104cfd17a7SChangpeng Liu if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, remove_cb) != 0) { 3114cfd17a7SChangpeng Liu fprintf(stderr, "spdk_nvme_probe() failed\n"); 3124cfd17a7SChangpeng Liu } 3134cfd17a7SChangpeng Liu } 3144cfd17a7SChangpeng Liu 3154cfd17a7SChangpeng Liu static void 316104356c9SDaniel Verkamp io_loop(void) 317104356c9SDaniel Verkamp { 318104356c9SDaniel Verkamp struct dev_ctx *dev, *dev_tmp; 319104356c9SDaniel Verkamp uint64_t tsc_end; 320104356c9SDaniel Verkamp uint64_t next_stats_tsc; 3219a99ff90SShuhei Matsumoto int rc; 322104356c9SDaniel Verkamp 3230de6ed9dSJim Harris if (g_time_in_sec > 0) { 324104356c9SDaniel Verkamp tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate; 3250de6ed9dSJim Harris } else { 3260de6ed9dSJim Harris /* User specified 0 seconds for timeout, which means no timeout. 3270de6ed9dSJim Harris * So just set tsc_end to UINT64_MAX which ensures the loop 3280de6ed9dSJim Harris * will never time out. 3290de6ed9dSJim Harris */ 3300de6ed9dSJim Harris tsc_end = UINT64_MAX; 3310de6ed9dSJim Harris } 3320de6ed9dSJim Harris 333104356c9SDaniel Verkamp next_stats_tsc = spdk_get_ticks(); 334104356c9SDaniel Verkamp 335104356c9SDaniel Verkamp while (1) { 336104356c9SDaniel Verkamp uint64_t now; 337104356c9SDaniel Verkamp 338104356c9SDaniel Verkamp /* 339104356c9SDaniel Verkamp * Check for completed I/O for each controller. A new 340104356c9SDaniel Verkamp * I/O will be submitted in the io_complete callback 341104356c9SDaniel Verkamp * to replace each I/O that is completed. 342104356c9SDaniel Verkamp */ 343104356c9SDaniel Verkamp TAILQ_FOREACH(dev, &g_devs, tailq) { 344104356c9SDaniel Verkamp if (dev->is_new) { 345104356c9SDaniel Verkamp /* Submit initial I/O for this controller. */ 346104356c9SDaniel Verkamp submit_io(dev, g_queue_depth); 347104356c9SDaniel Verkamp dev->is_new = false; 348104356c9SDaniel Verkamp } 349104356c9SDaniel Verkamp 350104356c9SDaniel Verkamp check_io(dev); 351104356c9SDaniel Verkamp } 352104356c9SDaniel Verkamp 353104356c9SDaniel Verkamp /* 354104356c9SDaniel Verkamp * Check for hotplug events. 355104356c9SDaniel Verkamp */ 356df46c41aSBen Walker if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, remove_cb) != 0) { 357104356c9SDaniel Verkamp fprintf(stderr, "spdk_nvme_probe() failed\n"); 35803823694SCunyin Chang break; 35903823694SCunyin Chang } 360104356c9SDaniel Verkamp 361104356c9SDaniel Verkamp /* 362104356c9SDaniel Verkamp * Check for devices which were hot-removed and have finished 363104356c9SDaniel Verkamp * processing outstanding I/Os. 364104356c9SDaniel Verkamp * 365104356c9SDaniel Verkamp * unregister_dev() may remove devs from the list, so use the 366104356c9SDaniel Verkamp * removal-safe iterator. 367104356c9SDaniel Verkamp */ 368104356c9SDaniel Verkamp TAILQ_FOREACH_SAFE(dev, &g_devs, tailq, dev_tmp) { 369104356c9SDaniel Verkamp if (dev->is_removed && dev->current_queue_depth == 0) { 3702bd8c87aSCunyin Chang g_removal_times++; 371104356c9SDaniel Verkamp unregister_dev(dev); 372104356c9SDaniel Verkamp } 37303823694SCunyin Chang } 37403823694SCunyin Chang 3759a99ff90SShuhei Matsumoto if (g_detach_ctx) { 3769a99ff90SShuhei Matsumoto rc = spdk_nvme_detach_poll_async(g_detach_ctx); 3779a99ff90SShuhei Matsumoto if (rc == 0) { 3789a99ff90SShuhei Matsumoto g_detach_ctx = NULL; 3799a99ff90SShuhei Matsumoto } 3809a99ff90SShuhei Matsumoto } 3819a99ff90SShuhei Matsumoto 38291b8f349SKrzysztof Karas if (g_insert_times == g_expected_insert_times && g_removal_times == g_expected_removal_times) { 38391b8f349SKrzysztof Karas break; 38491b8f349SKrzysztof Karas } 38591b8f349SKrzysztof Karas 386104356c9SDaniel Verkamp now = spdk_get_ticks(); 387104356c9SDaniel Verkamp if (now > tsc_end) { 38891b8f349SKrzysztof Karas SPDK_ERRLOG("Timing out hotplug application!\n"); 389104356c9SDaniel Verkamp break; 390104356c9SDaniel Verkamp } 391104356c9SDaniel Verkamp if (now > next_stats_tsc) { 392104356c9SDaniel Verkamp print_stats(); 393104356c9SDaniel Verkamp next_stats_tsc += g_tsc_rate; 394104356c9SDaniel Verkamp } 39503823694SCunyin Chang } 39603823694SCunyin Chang 397104356c9SDaniel Verkamp TAILQ_FOREACH_SAFE(dev, &g_devs, tailq, dev_tmp) { 398104356c9SDaniel Verkamp drain_io(dev); 399104356c9SDaniel Verkamp unregister_dev(dev); 40003823694SCunyin Chang } 4019a99ff90SShuhei Matsumoto 4029a99ff90SShuhei Matsumoto if (g_detach_ctx) { 4039a99ff90SShuhei Matsumoto spdk_nvme_detach_poll(g_detach_ctx); 4049a99ff90SShuhei Matsumoto } 40503823694SCunyin Chang } 406104356c9SDaniel Verkamp 4078dd1cd21SBen Walker static void 4088dd1cd21SBen Walker usage(char *program_name) 409104356c9SDaniel Verkamp { 410104356c9SDaniel Verkamp printf("%s options", program_name); 411104356c9SDaniel Verkamp printf("\n"); 412885bc995SJinYu printf("\t[-c timeout for each command in second(default:1s)]\n"); 41318d26e42SBen Walker printf("\t[-i shm id (optional)]\n"); 41418d26e42SBen Walker printf("\t[-n expected hot insert times]\n"); 4152bd8c87aSCunyin Chang printf("\t[-r expected hot removal times]\n"); 4160de6ed9dSJim Harris printf("\t[-t time in seconds to wait for all events (default: forever)]\n"); 417ac1abb5bSKrzysztof Karas printf("\t[-m iova mode: pa or va (optional)\n"); 418bace0549SKrzysztof Karas printf("\t[-l log level]\n"); 419bace0549SKrzysztof Karas printf("\t Available log levels:\n"); 420bace0549SKrzysztof Karas printf("\t disabled, error, warning, notice, info, debug\n"); 421ce29e013SKrzysztof Karas printf("\t[--wait-for-rpc wait for RPC perform_tests\n"); 422ce29e013SKrzysztof Karas printf("\t to proceed with starting IO on NVMe disks]\n"); 42303823694SCunyin Chang } 424104356c9SDaniel Verkamp 425ce29e013SKrzysztof Karas static const struct option g_wait_option[] = { 426ce29e013SKrzysztof Karas #define WAIT_FOR_RPC_OPT_IDX 257 427ce29e013SKrzysztof Karas {"wait-for-rpc", no_argument, NULL, WAIT_FOR_RPC_OPT_IDX}, 428ce29e013SKrzysztof Karas }; 429ce29e013SKrzysztof Karas 430104356c9SDaniel Verkamp static int 431104356c9SDaniel Verkamp parse_args(int argc, char **argv) 432104356c9SDaniel Verkamp { 433ce29e013SKrzysztof Karas int op, opt_idx; 4340f9dc2afSShuhei Matsumoto long int val; 435104356c9SDaniel Verkamp 436104356c9SDaniel Verkamp /* default value */ 437104356c9SDaniel Verkamp g_time_in_sec = 0; 438104356c9SDaniel Verkamp 439ce29e013SKrzysztof Karas while ((op = getopt_long(argc, argv, "c:i:l:m:n:r:t:", g_wait_option, &opt_idx)) != -1) { 4400f9dc2afSShuhei Matsumoto if (op == '?') { 4410f9dc2afSShuhei Matsumoto usage(argv[0]); 4420f9dc2afSShuhei Matsumoto return 1; 4430f9dc2afSShuhei Matsumoto } 4440f9dc2afSShuhei Matsumoto 445eb48f76dSKrzysztof Karas switch (op) { 446ce29e013SKrzysztof Karas case WAIT_FOR_RPC_OPT_IDX: 447ce29e013SKrzysztof Karas g_wait_for_rpc = true; 448ce29e013SKrzysztof Karas break; 449eb48f76dSKrzysztof Karas case 'c': 450eb48f76dSKrzysztof Karas case 'i': 451eb48f76dSKrzysztof Karas case 'n': 452eb48f76dSKrzysztof Karas case 'r': 453eb48f76dSKrzysztof Karas case 't': 4540f9dc2afSShuhei Matsumoto val = spdk_strtol(optarg, 10); 4550f9dc2afSShuhei Matsumoto if (val < 0) { 4560f9dc2afSShuhei Matsumoto fprintf(stderr, "Converting a string to integer failed\n"); 4570f9dc2afSShuhei Matsumoto return val; 4580f9dc2afSShuhei Matsumoto } 459104356c9SDaniel Verkamp switch (op) { 460885bc995SJinYu case 'c': 461885bc995SJinYu g_timeout_in_us = val * SPDK_SEC_TO_USEC; 462885bc995SJinYu break; 4632bd8c87aSCunyin Chang case 'i': 4640f9dc2afSShuhei Matsumoto g_shm_id = val; 46518d26e42SBen Walker break; 46618d26e42SBen Walker case 'n': 4670f9dc2afSShuhei Matsumoto g_expected_insert_times = val; 4682bd8c87aSCunyin Chang break; 4692bd8c87aSCunyin Chang case 'r': 4700f9dc2afSShuhei Matsumoto g_expected_removal_times = val; 4712bd8c87aSCunyin Chang break; 472104356c9SDaniel Verkamp case 't': 4730f9dc2afSShuhei Matsumoto g_time_in_sec = val; 474104356c9SDaniel Verkamp break; 475eb48f76dSKrzysztof Karas } 476eb48f76dSKrzysztof Karas break; 477eb48f76dSKrzysztof Karas case 'm': 478eb48f76dSKrzysztof Karas g_iova_mode = optarg; 479eb48f76dSKrzysztof Karas break; 480bace0549SKrzysztof Karas case 'l': 481bace0549SKrzysztof Karas if (!strcmp(optarg, "disabled")) { 482bace0549SKrzysztof Karas spdk_log_set_print_level(SPDK_LOG_DISABLED); 483bace0549SKrzysztof Karas } else if (!strcmp(optarg, "error")) { 484bace0549SKrzysztof Karas spdk_log_set_print_level(SPDK_LOG_ERROR); 485bace0549SKrzysztof Karas } else if (!strcmp(optarg, "warning")) { 486bace0549SKrzysztof Karas spdk_log_set_print_level(SPDK_LOG_WARN); 487bace0549SKrzysztof Karas } else if (!strcmp(optarg, "notice")) { 488bace0549SKrzysztof Karas spdk_log_set_print_level(SPDK_LOG_NOTICE); 489bace0549SKrzysztof Karas } else if (!strcmp(optarg, "info")) { 490bace0549SKrzysztof Karas spdk_log_set_print_level(SPDK_LOG_INFO); 491bace0549SKrzysztof Karas } else if (!strcmp(optarg, "debug")) { 492bace0549SKrzysztof Karas spdk_log_set_print_level(SPDK_LOG_DEBUG); 493bace0549SKrzysztof Karas } else { 494bace0549SKrzysztof Karas fprintf(stderr, "Unrecognized log level: %s\n", optarg); 495bace0549SKrzysztof Karas return 1; 496bace0549SKrzysztof Karas } 497bace0549SKrzysztof Karas break; 498104356c9SDaniel Verkamp default: 499104356c9SDaniel Verkamp usage(argv[0]); 500104356c9SDaniel Verkamp return 1; 50103823694SCunyin Chang } 50203823694SCunyin Chang } 50303823694SCunyin Chang 504104356c9SDaniel Verkamp return 0; 505104356c9SDaniel Verkamp } 506104356c9SDaniel Verkamp 507104356c9SDaniel Verkamp 50803823694SCunyin Chang static int 50903823694SCunyin Chang register_controllers(void) 51003823694SCunyin Chang { 511d77a04d1SCunyin Chang fprintf(stderr, "Initializing NVMe Controllers\n"); 51203823694SCunyin Chang 513df46c41aSBen Walker if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, remove_cb) != 0) { 51403823694SCunyin Chang fprintf(stderr, "spdk_nvme_probe() failed\n"); 51503823694SCunyin Chang return 1; 51603823694SCunyin Chang } 5172bd8c87aSCunyin Chang /* Reset g_insert_times to 0 so that we do not count controllers attached at start as hotplug events. */ 5182bd8c87aSCunyin Chang g_insert_times = 0; 51903823694SCunyin Chang return 0; 52003823694SCunyin Chang } 52103823694SCunyin Chang 522ce29e013SKrzysztof Karas /* Hotplug RPC */ 523ce29e013SKrzysztof Karas static void 524ce29e013SKrzysztof Karas rpc_perform_tests(struct spdk_jsonrpc_request *request, 525ce29e013SKrzysztof Karas const struct spdk_json_val *params) 526ce29e013SKrzysztof Karas { 527ce29e013SKrzysztof Karas if (params) { 528ce29e013SKrzysztof Karas spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, 529ce29e013SKrzysztof Karas "'perform_tests' requires no arguments"); 530ce29e013SKrzysztof Karas return; 531ce29e013SKrzysztof Karas } 532ce29e013SKrzysztof Karas 533ce29e013SKrzysztof Karas spdk_jsonrpc_send_bool_response(request, true); 534ce29e013SKrzysztof Karas 535ce29e013SKrzysztof Karas g_rpc_received = true; 536ce29e013SKrzysztof Karas } 537ce29e013SKrzysztof Karas SPDK_RPC_REGISTER("perform_tests", rpc_perform_tests, SPDK_RPC_RUNTIME); 538ce29e013SKrzysztof Karas 539ce29e013SKrzysztof Karas static void 540ce29e013SKrzysztof Karas wait_for_rpc_call(void) 541ce29e013SKrzysztof Karas { 542ce29e013SKrzysztof Karas fprintf(stderr, 543ce29e013SKrzysztof Karas "Listening for perform_tests to start the application...\n"); 544ce29e013SKrzysztof Karas spdk_rpc_listen(g_rpc_addr); 545ce29e013SKrzysztof Karas spdk_rpc_set_state(SPDK_RPC_RUNTIME); 546ce29e013SKrzysztof Karas 547ce29e013SKrzysztof Karas while (!g_rpc_received) { 548ce29e013SKrzysztof Karas spdk_rpc_accept(); 549ce29e013SKrzysztof Karas } 550ce29e013SKrzysztof Karas /* Run spdk_rpc_accept() one more time to trigger 551ce29e013SKrzysztof Karas * spdk_jsonrpv_server_poll() and send the RPC response. */ 552ce29e013SKrzysztof Karas spdk_rpc_accept(); 553ce29e013SKrzysztof Karas } 554ce29e013SKrzysztof Karas 5558dd1cd21SBen Walker int 5568dd1cd21SBen Walker main(int argc, char **argv) 55703823694SCunyin Chang { 55803823694SCunyin Chang int rc; 55918d26e42SBen Walker struct spdk_env_opts opts; 56003823694SCunyin Chang 56103823694SCunyin Chang rc = parse_args(argc, argv); 56203823694SCunyin Chang if (rc != 0) { 56303823694SCunyin Chang return rc; 56403823694SCunyin Chang } 56503823694SCunyin Chang 566*57fd99b9SJim Harris opts.opts_size = sizeof(opts); 56718d26e42SBen Walker spdk_env_opts_init(&opts); 56818d26e42SBen Walker opts.name = "hotplug"; 56918d26e42SBen Walker opts.core_mask = "0x1"; 57018d26e42SBen Walker if (g_shm_id > -1) { 57118d26e42SBen Walker opts.shm_id = g_shm_id; 57203823694SCunyin Chang } 573ac1abb5bSKrzysztof Karas if (g_iova_mode) { 574ac1abb5bSKrzysztof Karas opts.iova_mode = g_iova_mode; 575ac1abb5bSKrzysztof Karas } 576095f4254SLance Hartmann if (spdk_env_init(&opts) < 0) { 577095f4254SLance Hartmann fprintf(stderr, "Unable to initialize SPDK env\n"); 578095f4254SLance Hartmann return 1; 579095f4254SLance Hartmann } 58003823694SCunyin Chang 58103823694SCunyin Chang g_tsc_rate = spdk_get_ticks_hz(); 58203823694SCunyin Chang 583104356c9SDaniel Verkamp /* Detect the controllers that are plugged in at startup. */ 58403823694SCunyin Chang if (register_controllers() != 0) { 5859ec9c8b3SChangpeng Liu rc = 1; 5869ec9c8b3SChangpeng Liu goto cleanup; 58703823694SCunyin Chang } 58803823694SCunyin Chang 589ce29e013SKrzysztof Karas if (g_wait_for_rpc) { 590ce29e013SKrzysztof Karas wait_for_rpc_call(); 591ce29e013SKrzysztof Karas } 592ce29e013SKrzysztof Karas 593d77a04d1SCunyin Chang fprintf(stderr, "Initialization complete. Starting I/O...\n"); 5942bd8c87aSCunyin Chang io_loop(); 59503823694SCunyin Chang 596f8baa259SDaniel Verkamp if (g_expected_insert_times != -1 && g_insert_times != g_expected_insert_times) { 597f8baa259SDaniel Verkamp fprintf(stderr, "Expected inserts %d != actual inserts %d\n", 598f8baa259SDaniel Verkamp g_expected_insert_times, g_insert_times); 5999ec9c8b3SChangpeng Liu rc = 1; 6009ec9c8b3SChangpeng Liu goto cleanup; 601f8baa259SDaniel Verkamp } 602f8baa259SDaniel Verkamp 603f8baa259SDaniel Verkamp if (g_expected_removal_times != -1 && g_removal_times != g_expected_removal_times) { 604f8baa259SDaniel Verkamp fprintf(stderr, "Expected removals %d != actual removals %d\n", 605f8baa259SDaniel Verkamp g_expected_removal_times, g_removal_times); 6069ec9c8b3SChangpeng Liu rc = 1; 6072bd8c87aSCunyin Chang } 6082bd8c87aSCunyin Chang 6099ec9c8b3SChangpeng Liu cleanup: 610b0b407aeSKrzysztof Karas spdk_rpc_close(); 6119ec9c8b3SChangpeng Liu spdk_env_fini(); 6129ec9c8b3SChangpeng Liu return rc; 61303823694SCunyin Chang } 614