1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2021 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 8 #include "spdk/env.h" 9 #include "spdk/nvme.h" 10 #include "spdk/string.h" 11 #include "spdk/util.h" 12 #include "spdk/log.h" 13 #include "spdk/likely.h" 14 #include "spdk/sock.h" 15 16 static int g_time_in_sec; 17 18 static struct spdk_nvme_transport_id g_trid; 19 20 static void 21 usage(char *program_name) 22 { 23 printf("%s options", program_name); 24 printf("\n"); 25 printf("\t[-t, --time <sec> time in seconds]\n"); 26 printf("\t[-c, --core-mask <mask>]\n"); 27 printf("\t\t(default: 1)\n"); 28 printf("\t[-r, --transport <fmt> Transport ID for local PCIe NVMe or NVMeoF]\n"); 29 printf("\t Format: 'key:value [key:value] ...'\n"); 30 printf("\t Keys:\n"); 31 printf("\t trtype Transport type (e.g. PCIe, RDMA)\n"); 32 printf("\t adrfam Address family (e.g. IPv4, IPv6)\n"); 33 printf("\t traddr Transport address (e.g. 0000:04:00.0 for PCIe or 192.168.100.8 for RDMA)\n"); 34 printf("\t trsvcid Transport service identifier (e.g. 4420)\n"); 35 printf("\t subnqn Subsystem NQN\n"); 36 printf("\t Example: -r 'trtype:PCIe traddr:0000:04:00.0' for PCIe or\n"); 37 printf("\t -r 'trtype:RDMA adrfam:IPv4 traddr:192.168.100.8 trsvcid:4420' for NVMeoF\n"); 38 printf("\t[-s, --hugemem-size <MB> DPDK huge memory size in MB.]\n"); 39 printf("\t\t(default: 0 - unlimited)\n"); 40 printf("\t[-i, --shmem-grp-id <id> shared memory group ID]\n"); 41 printf("\t"); 42 spdk_log_usage(stdout, "-T"); 43 printf("\t[-S, --default-sock-impl <impl> set the default sock impl, e.g. \"posix\"]\n"); 44 #ifdef DEBUG 45 printf("\t[-G, --enable-debug enable debug logging]\n"); 46 #else 47 printf("\t[-G, --enable-debug enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n"); 48 printf("\t[--iova-mode <mode> specify DPDK IOVA mode: va|pa]\n"); 49 printf("\t[--no-huge, SPDK is run without hugepages\n"); 50 #endif 51 } 52 53 static int 54 add_trid(const char *trid_str) 55 { 56 g_trid.trtype = SPDK_NVME_TRANSPORT_PCIE; 57 snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN); 58 59 if (spdk_nvme_transport_id_parse(&g_trid, trid_str) != 0) { 60 fprintf(stderr, "Invalid transport ID format '%s'\n", trid_str); 61 return 1; 62 } 63 64 spdk_nvme_transport_id_populate_trstring(&g_trid, 65 spdk_nvme_transport_id_trtype_str(g_trid.trtype)); 66 67 return 0; 68 } 69 70 #define PERF_GETOPT_SHORT "c:i:r:s:t:GS:T:" 71 72 static const struct option g_cmdline_opts[] = { 73 #define PERF_CORE_MASK 'c' 74 {"core-mask", required_argument, NULL, PERF_CORE_MASK}, 75 #define PERF_SHMEM_GROUP_ID 'i' 76 {"shmem-grp-id", required_argument, NULL, PERF_SHMEM_GROUP_ID}, 77 #define PERF_TRANSPORT 'r' 78 {"transport", required_argument, NULL, PERF_TRANSPORT}, 79 #define PERF_HUGEMEM_SIZE 's' 80 {"hugemem-size", required_argument, NULL, PERF_HUGEMEM_SIZE}, 81 #define PERF_TIME 't' 82 {"time", required_argument, NULL, PERF_TIME}, 83 #define PERF_ENABLE_DEBUG 'G' 84 {"enable-debug", no_argument, NULL, PERF_ENABLE_DEBUG}, 85 #define PERF_DEFAULT_SOCK_IMPL 'S' 86 {"default-sock-impl", required_argument, NULL, PERF_DEFAULT_SOCK_IMPL}, 87 #define PERF_LOG_FLAG 'T' 88 {"logflag", required_argument, NULL, PERF_LOG_FLAG}, 89 #define PERF_IOVA_MODE 258 90 {"iova-mode", required_argument, NULL, PERF_IOVA_MODE}, 91 #define PERF_NO_HUGE 259 92 {"no-huge", no_argument, NULL, PERF_NO_HUGE}, 93 /* Should be the last element */ 94 {0, 0, 0, 0} 95 }; 96 97 static int 98 parse_args(int argc, char **argv, struct spdk_env_opts *env_opts) 99 { 100 bool trid_set = false; 101 int op, long_idx; 102 long int val; 103 int rc; 104 105 while ((op = getopt_long(argc, argv, PERF_GETOPT_SHORT, g_cmdline_opts, &long_idx)) != -1) { 106 switch (op) { 107 case PERF_SHMEM_GROUP_ID: 108 case PERF_HUGEMEM_SIZE: 109 case PERF_TIME: 110 val = spdk_strtol(optarg, 10); 111 if (val < 0) { 112 fprintf(stderr, "Converting a string to integer failed\n"); 113 return val; 114 } 115 switch (op) { 116 case PERF_SHMEM_GROUP_ID: 117 env_opts->shm_id = val; 118 break; 119 case PERF_HUGEMEM_SIZE: 120 env_opts->mem_size = val; 121 break; 122 case PERF_TIME: 123 g_time_in_sec = val; 124 break; 125 } 126 break; 127 case PERF_CORE_MASK: 128 env_opts->core_mask = optarg; 129 break; 130 case PERF_TRANSPORT: 131 if (trid_set) { 132 fprintf(stderr, "Only one trid can be specified\n"); 133 usage(argv[0]); 134 return 1; 135 } 136 trid_set = true; 137 if (add_trid(optarg)) { 138 usage(argv[0]); 139 return 1; 140 } 141 break; 142 case PERF_ENABLE_DEBUG: 143 #ifndef DEBUG 144 fprintf(stderr, "%s must be configured with --enable-debug for -G flag\n", 145 argv[0]); 146 usage(argv[0]); 147 return 1; 148 #else 149 spdk_log_set_flag("nvme"); 150 spdk_log_set_print_level(SPDK_LOG_DEBUG); 151 break; 152 #endif 153 case PERF_LOG_FLAG: 154 rc = spdk_log_set_flag(optarg); 155 if (rc < 0) { 156 fprintf(stderr, "unknown flag\n"); 157 usage(argv[0]); 158 exit(EXIT_FAILURE); 159 } 160 #ifdef DEBUG 161 spdk_log_set_print_level(SPDK_LOG_DEBUG); 162 #endif 163 break; 164 case PERF_DEFAULT_SOCK_IMPL: 165 rc = spdk_sock_set_default_impl(optarg); 166 if (rc) { 167 fprintf(stderr, "Failed to set sock impl %s, err %d (%s)\n", optarg, errno, strerror(errno)); 168 return 1; 169 } 170 break; 171 case PERF_IOVA_MODE: 172 env_opts->iova_mode = optarg; 173 break; 174 case PERF_NO_HUGE: 175 env_opts->no_huge = true; 176 break; 177 default: 178 usage(argv[0]); 179 return 1; 180 } 181 } 182 183 if (!g_time_in_sec) { 184 fprintf(stderr, "missing -t (--time) operand\n"); 185 usage(argv[0]); 186 return 1; 187 } 188 189 if (!trid_set) { 190 fprintf(stderr, "missing -r operand\n"); 191 usage(argv[0]); 192 return 1; 193 } 194 195 env_opts->no_pci = true; 196 if (g_trid.trtype == SPDK_NVME_TRANSPORT_PCIE) { 197 env_opts->no_pci = false; 198 } 199 200 return 0; 201 } 202 203 static int 204 test_controller(void) 205 { 206 const int LOOP_COUNT = 5; 207 struct spdk_nvme_qpair *qpair[LOOP_COUNT]; 208 union spdk_nvme_csts_register csts; 209 struct spdk_nvme_ctrlr *ctrlr; 210 211 ctrlr = spdk_nvme_connect(&g_trid, NULL, 0); 212 if (ctrlr == NULL) { 213 fprintf(stderr, "spdk_nvme_connect() failed for transport address '%s'\n", 214 g_trid.traddr); 215 return -1; 216 } 217 if (spdk_nvme_ctrlr_is_discovery(ctrlr)) { 218 fprintf(stderr, "discovery controller not allowed for this test\n"); 219 spdk_nvme_detach(ctrlr); 220 return -1; 221 } 222 223 for (int i = 0; i < LOOP_COUNT; i++) { 224 csts = spdk_nvme_ctrlr_get_regs_csts(ctrlr); 225 if (csts.raw == 0xFFFFFFFF) { 226 fprintf(stderr, "could not read csts\n"); 227 spdk_nvme_detach(ctrlr); 228 return -1; 229 } 230 qpair[i] = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, NULL, 0); 231 if (qpair[i] == NULL) { 232 fprintf(stderr, "could not allocate io qpair\n"); 233 spdk_nvme_detach(ctrlr); 234 return -1; 235 } 236 } 237 for (int i = 0; i < LOOP_COUNT; i++) { 238 spdk_nvme_ctrlr_free_io_qpair(qpair[i]); 239 } 240 241 spdk_nvme_detach(ctrlr); 242 243 return 0; 244 } 245 246 int 247 main(int argc, char **argv) 248 { 249 struct spdk_env_opts opts; 250 uint64_t tsc_end; 251 int rc; 252 253 spdk_env_opts_init(&opts); 254 opts.name = "connect_stress"; 255 rc = parse_args(argc, argv, &opts); 256 if (rc != 0) { 257 return rc; 258 } 259 if (spdk_env_init(&opts) < 0) { 260 fprintf(stderr, "Unable to initialize SPDK env\n"); 261 return -1; 262 } 263 264 if (g_trid.trtype != SPDK_NVME_TRANSPORT_PCIE) { 265 printf("Testing NVMe over Fabrics controller at %s:%s: %s\n", 266 g_trid.traddr, g_trid.trsvcid, 267 g_trid.subnqn); 268 } else { 269 printf("Testing NVMe PCI controller at %s\n", g_trid.traddr); 270 } 271 272 tsc_end = spdk_get_ticks() + g_time_in_sec * spdk_get_ticks_hz(); 273 while (spdk_get_ticks() < tsc_end && rc == 0) { 274 rc = test_controller(); 275 } 276 277 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 278 } 279