1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) Samsung Electronics Co., Ltd. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 #include "spdk/nvme.h" 8 #include "spdk/env.h" 9 10 #define NUM_LBAS 64 11 #define DEST_LBA 256 12 13 struct ns_entry { 14 struct spdk_nvme_ctrlr *ctrlr; 15 struct spdk_nvme_ns *ns; 16 struct ns_entry *next; 17 struct spdk_nvme_qpair *qpair; 18 }; 19 20 struct simple_copy_context { 21 struct ns_entry *ns_entry; 22 char **write_bufs; 23 char **read_bufs; 24 int writes_completed; 25 int reads_completed; 26 int simple_copy_completed; 27 int matches_written_data; 28 int error; 29 }; 30 31 static struct ns_entry *g_namespaces = NULL; 32 static struct spdk_nvme_transport_id g_trid; 33 static bool g_use_trid = false; 34 35 static void cleanup(struct simple_copy_context *context); 36 37 static void 38 fill_random(char *buf, size_t num_bytes) 39 { 40 size_t i; 41 42 srand((unsigned) time(NULL)); 43 for (i = 0; i < num_bytes; i++) { 44 buf[i] = rand() % 0x100; 45 } 46 } 47 48 static void 49 register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns) 50 { 51 struct ns_entry *entry; 52 const struct spdk_nvme_ctrlr_data *cdata; 53 54 cdata = spdk_nvme_ctrlr_get_data(ctrlr); 55 56 if (!spdk_nvme_ns_is_active(ns)) { 57 printf("Controller %-20.20s (%-20.20s): Skipping inactive NS %u\n", 58 cdata->mn, cdata->sn, 59 spdk_nvme_ns_get_id(ns)); 60 return; 61 } 62 63 entry = malloc(sizeof(struct ns_entry)); 64 if (entry == NULL) { 65 perror("ns_entry malloc"); 66 exit(1); 67 } 68 69 entry->ctrlr = ctrlr; 70 entry->ns = ns; 71 entry->next = g_namespaces; 72 g_namespaces = entry; 73 74 printf(" Namespace ID: %d size: %juGB\n", spdk_nvme_ns_get_id(ns), 75 spdk_nvme_ns_get_size(ns) / 1000000000); 76 } 77 78 static uint32_t 79 get_max_block_size(void) 80 { 81 struct ns_entry *ns; 82 uint32_t max_block_size, temp_block_size; 83 84 ns = g_namespaces; 85 max_block_size = 0; 86 87 while (ns != NULL) { 88 temp_block_size = spdk_nvme_ns_get_sector_size(ns->ns); 89 max_block_size = temp_block_size > max_block_size ? temp_block_size : max_block_size; 90 ns = ns->next; 91 } 92 93 return max_block_size; 94 } 95 96 static void 97 write_complete(void *arg, const struct spdk_nvme_cpl *cpl) 98 { 99 struct simple_copy_context *context = arg; 100 101 context->writes_completed++; 102 103 if (spdk_nvme_cpl_is_error(cpl)) { 104 printf("write cpl error. SC 0x%x SCT 0x%x\n", cpl->status.sc, cpl->status.sct); 105 context->error++; 106 return; 107 } 108 } 109 110 static void 111 read_complete(void *arg, const struct spdk_nvme_cpl *cpl) 112 { 113 struct simple_copy_context *context = arg; 114 struct ns_entry *ns_entry = context->ns_entry; 115 int rc; 116 117 if (spdk_nvme_cpl_is_error(cpl)) { 118 printf("read cpl error. SC 0x%x SCT 0x%x\n", cpl->status.sc, cpl->status.sct); 119 context->reads_completed++; 120 context->error++; 121 return; 122 } 123 124 rc = memcmp(context->write_bufs[context->reads_completed], 125 context->read_bufs[context->reads_completed], spdk_nvme_ns_get_sector_size(ns_entry->ns)); 126 if (rc == 0) { 127 context->matches_written_data++; 128 } 129 130 context->reads_completed++; 131 } 132 133 static void 134 simple_copy_complete(void *arg, const struct spdk_nvme_cpl *cpl) 135 { 136 struct simple_copy_context *context = arg; 137 138 context->simple_copy_completed = 1; 139 140 if (spdk_nvme_cpl_is_error(cpl)) { 141 printf("scc cpl error. SC 0x%x SCT 0x%x\n", cpl->status.sc, cpl->status.sct); 142 context->error++; 143 return; 144 } 145 146 printf("Copied LBAs from 0 - %d to the Destination LBA %d\n", NUM_LBAS - 1, DEST_LBA); 147 context->reads_completed = 0; 148 context->matches_written_data = 0; 149 } 150 151 static void 152 simple_copy_test(void) 153 { 154 struct ns_entry *ns_entry; 155 struct spdk_nvme_ctrlr *ctrlr; 156 const struct spdk_nvme_ctrlr_data *data; 157 struct simple_copy_context context; 158 struct spdk_nvme_scc_source_range range = {}; 159 uint32_t max_block_size; 160 int rc, i; 161 162 memset(&context, 0, sizeof(struct simple_copy_context)); 163 max_block_size = get_max_block_size(); 164 ns_entry = g_namespaces; 165 166 context.write_bufs = calloc(NUM_LBAS, sizeof(char *)); 167 if (context.write_bufs == NULL) { 168 printf("could not allocate write buffer pointers for test\n"); 169 cleanup(&context); 170 return; 171 } 172 173 context.read_bufs = calloc(NUM_LBAS, sizeof(char *)); 174 if (context.read_bufs == NULL) { 175 printf("could not allocate read buffer pointers for test\n"); 176 cleanup(&context); 177 return; 178 } 179 180 for (i = 0; i < NUM_LBAS; i++) { 181 context.write_bufs[i] = spdk_zmalloc(0x1000, max_block_size, NULL, SPDK_ENV_LCORE_ID_ANY, 182 SPDK_MALLOC_DMA); 183 if (context.write_bufs[i] == NULL) { 184 printf("could not allocate write buffer %d for test\n", i); 185 cleanup(&context); 186 return; 187 } 188 189 fill_random(context.write_bufs[i], 0x1000); 190 context.read_bufs[i] = spdk_zmalloc(0x1000, max_block_size, NULL, SPDK_ENV_LCORE_ID_ANY, 191 SPDK_MALLOC_DMA); 192 if (context.read_bufs[i] == NULL) { 193 printf("could not allocate read buffer %d for test\n", i); 194 cleanup(&context); 195 return; 196 } 197 } 198 199 while (ns_entry != NULL) { 200 201 ns_entry->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ns_entry->ctrlr, NULL, 0); 202 if (ns_entry->qpair == NULL) { 203 printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n"); 204 cleanup(&context); 205 return; 206 } 207 208 ctrlr = spdk_nvme_ns_get_ctrlr(ns_entry->ns); 209 data = spdk_nvme_ctrlr_get_data(ctrlr); 210 211 printf("\nController %-20.20s (%-20.20s)\n", data->mn, data->sn); 212 printf("Controller PCI vendor:%u PCI subsystem vendor:%u\n", data->vid, data->ssvid); 213 printf("Namespace Block Size:%u\n", spdk_nvme_ns_get_sector_size(ns_entry->ns)); 214 printf("Writing LBAs 0 to %d with Random Data\n", NUM_LBAS - 1); 215 216 context.ns_entry = ns_entry; 217 218 for (i = 0; i < NUM_LBAS; i++) { 219 rc = spdk_nvme_ns_cmd_write(ns_entry->ns, ns_entry->qpair, context.write_bufs[i], 220 i, 221 1, 222 write_complete, &context, 0); 223 if (rc) { 224 printf("submission of write I/O failed\n"); 225 } 226 } 227 while (context.writes_completed < NUM_LBAS) { 228 rc = spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 229 if (rc < 0) { 230 printf("Error processing write completions, rc: %d\n", rc); 231 break; 232 } 233 } 234 235 if (context.error) { 236 printf("Error : %d Write completions failed\n", 237 context.error); 238 spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 239 cleanup(&context); 240 exit(1); 241 } 242 243 range.nlb = NUM_LBAS - 1; 244 range.slba = 0; 245 246 rc = spdk_nvme_ns_cmd_copy(ns_entry->ns, ns_entry->qpair, 247 &range, 1, DEST_LBA, simple_copy_complete, &context); 248 249 if (rc) { 250 printf("submission of copy I/O failed\n"); 251 } 252 253 while (!context.simple_copy_completed) { 254 rc = spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 255 if (rc < 0) { 256 printf("Error processing copy completions, rc: %d\n", rc); 257 break; 258 } 259 } 260 261 if (context.error) { 262 printf("Error : Copy completion failed\n"); 263 spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 264 cleanup(&context); 265 exit(1); 266 } 267 268 for (i = 0; i < NUM_LBAS; i++) { 269 rc = spdk_nvme_ns_cmd_read(ns_entry->ns, ns_entry->qpair, context.read_bufs[i], 270 DEST_LBA + i, /* LBA start */ 271 1, /* number of LBAs */ 272 read_complete, &context, 0); 273 if (rc) { 274 printf("submission of read I/O failed\n"); 275 } 276 /* block after each read command so that we can match the block to the write buffer. */ 277 while (context.reads_completed <= i) { 278 rc = spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 279 if (rc < 0) { 280 printf("Error processing read completions, rc: %d\n", rc); 281 break; 282 } 283 } 284 } 285 286 if (context.error) { 287 printf("Error : %d Read completions failed\n", 288 context.error); 289 spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 290 cleanup(&context); 291 exit(1); 292 } 293 294 printf("LBAs matching Written Data: %d\n", context.matches_written_data); 295 296 if (context.matches_written_data != NUM_LBAS) { 297 printf("Error : %d LBAs are copied correctly out of %d LBAs\n", 298 context.matches_written_data, NUM_LBAS); 299 spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 300 cleanup(&context); 301 exit(1); 302 } 303 304 /* reset counters in between each namespace. */ 305 context.matches_written_data = 0; 306 context.writes_completed = 0; 307 context.reads_completed = 0; 308 context.simple_copy_completed = 0; 309 310 spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 311 ns_entry = ns_entry->next; 312 } 313 cleanup(&context); 314 } 315 316 static bool 317 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 318 struct spdk_nvme_ctrlr_opts *opts) 319 { 320 printf("Attaching to %s\n", trid->traddr); 321 322 return true; 323 } 324 325 static void 326 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 327 struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 328 { 329 int num_ns; 330 struct spdk_nvme_ns *ns; 331 const struct spdk_nvme_ctrlr_data *cdata; 332 333 cdata = spdk_nvme_ctrlr_get_data(ctrlr); 334 335 if (cdata->oncs.copy) { 336 printf("Controller supports SCC. Attached to %s\n", trid->traddr); 337 /* 338 * Use only the first namespace from each controller since we are testing controller level functionality. 339 */ 340 num_ns = spdk_nvme_ctrlr_get_num_ns(ctrlr); 341 if (num_ns < 1) { 342 printf("No valid namespaces in controller\n"); 343 } else { 344 ns = spdk_nvme_ctrlr_get_ns(ctrlr, 1); 345 register_ns(ctrlr, ns); 346 } 347 } else { 348 printf("Controller doesn't support SCC. Not Attached to %s\n", trid->traddr); 349 } 350 } 351 352 static void 353 cleanup(struct simple_copy_context *context) 354 { 355 struct ns_entry *ns_entry = g_namespaces; 356 struct spdk_nvme_detach_ctx *detach_ctx = NULL; 357 int i; 358 359 while (ns_entry) { 360 struct ns_entry *next = ns_entry->next; 361 362 spdk_nvme_detach_async(ns_entry->ctrlr, &detach_ctx); 363 364 free(ns_entry); 365 ns_entry = next; 366 } 367 368 if (detach_ctx) { 369 spdk_nvme_detach_poll(detach_ctx); 370 } 371 372 for (i = 0; i < NUM_LBAS; i++) { 373 if (context->write_bufs && context->write_bufs[i]) { 374 spdk_free(context->write_bufs[i]); 375 } else { 376 break; 377 } 378 if (context->read_bufs && context->read_bufs[i]) { 379 spdk_free(context->read_bufs[i]); 380 } else { 381 break; 382 } 383 } 384 385 free(context->write_bufs); 386 free(context->read_bufs); 387 } 388 389 static void 390 usage(const char *program_name) 391 { 392 printf("%s [options]", program_name); 393 printf("\n"); 394 printf("options:\n"); 395 printf(" -r trid remote NVMe over Fabrics target address\n"); 396 printf(" Format: 'key:value [key:value] ...'\n"); 397 printf(" Keys:\n"); 398 printf(" trtype Transport type (e.g. RDMA)\n"); 399 printf(" adrfam Address family (e.g. IPv4, IPv6)\n"); 400 printf(" traddr Transport address (e.g. 192.168.100.8)\n"); 401 printf(" trsvcid Transport service identifier (e.g. 4420)\n"); 402 printf(" subnqn Subsystem NQN (default: %s)\n", SPDK_NVMF_DISCOVERY_NQN); 403 printf(" Example: -r 'trtype:RDMA adrfam:IPv4 traddr:192.168.100.8 trsvcid:4420'\n"); 404 printf(" -h show this usage\n"); 405 } 406 407 static int 408 parse_args(int argc, char **argv, struct spdk_env_opts *env_opts) 409 { 410 int op; 411 412 spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE); 413 snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN); 414 415 while ((op = getopt(argc, argv, "r:h")) != -1) { 416 switch (op) { 417 case 'r': 418 if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) { 419 fprintf(stderr, "Error parsing transport address\n"); 420 return 1; 421 } 422 423 g_use_trid = true; 424 break; 425 case 'h': 426 usage(argv[0]); 427 exit(EXIT_SUCCESS); 428 default: 429 usage(argv[0]); 430 return 1; 431 } 432 } 433 434 return 0; 435 } 436 437 int 438 main(int argc, char **argv) 439 { 440 int rc; 441 struct spdk_env_opts opts; 442 443 opts.opts_size = sizeof(opts); 444 spdk_env_opts_init(&opts); 445 rc = parse_args(argc, argv, &opts); 446 if (rc != 0) { 447 return rc; 448 } 449 450 opts.name = "simple_copy"; 451 opts.shm_id = 0; 452 if (spdk_env_init(&opts) < 0) { 453 fprintf(stderr, "Unable to initialize SPDK env\n"); 454 return 1; 455 } 456 457 printf("Initializing NVMe Controllers\n"); 458 459 rc = spdk_nvme_probe(g_use_trid ? &g_trid : NULL, NULL, probe_cb, attach_cb, NULL); 460 if (rc != 0) { 461 fprintf(stderr, "spdk_nvme_probe() failed\n"); 462 return 1; 463 } 464 465 if (g_namespaces == NULL) { 466 fprintf(stderr, "no NVMe controllers found\n"); 467 return 1; 468 } 469 470 printf("Initialization complete.\n"); 471 simple_copy_test(); 472 return 0; 473 } 474