1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Samsung Electronics Co., Ltd. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Samsung Electronics Co., Ltd. nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "spdk/stdinc.h" 35 #include "spdk/nvme.h" 36 #include "spdk/env.h" 37 38 #define NUM_LBAS 64 39 #define DEST_LBA 256 40 41 struct ns_entry { 42 struct spdk_nvme_ctrlr *ctrlr; 43 struct spdk_nvme_ns *ns; 44 struct ns_entry *next; 45 struct spdk_nvme_qpair *qpair; 46 }; 47 48 struct simple_copy_context { 49 struct ns_entry *ns_entry; 50 char **write_bufs; 51 char **read_bufs; 52 int writes_completed; 53 int reads_completed; 54 int simple_copy_completed; 55 int matches_written_data; 56 int error; 57 }; 58 59 static struct ns_entry *g_namespaces = NULL; 60 61 static void cleanup(struct simple_copy_context *context); 62 63 static void 64 fill_random(char *buf, size_t num_bytes) 65 { 66 size_t i; 67 68 srand((unsigned) time(NULL)); 69 for (i = 0; i < num_bytes; i++) { 70 buf[i] = rand() % 0x100; 71 } 72 } 73 74 static void 75 register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns) 76 { 77 struct ns_entry *entry; 78 const struct spdk_nvme_ctrlr_data *cdata; 79 80 cdata = spdk_nvme_ctrlr_get_data(ctrlr); 81 82 if (!spdk_nvme_ns_is_active(ns)) { 83 printf("Controller %-20.20s (%-20.20s): Skipping inactive NS %u\n", 84 cdata->mn, cdata->sn, 85 spdk_nvme_ns_get_id(ns)); 86 return; 87 } 88 89 entry = malloc(sizeof(struct ns_entry)); 90 if (entry == NULL) { 91 perror("ns_entry malloc"); 92 exit(1); 93 } 94 95 entry->ctrlr = ctrlr; 96 entry->ns = ns; 97 entry->next = g_namespaces; 98 g_namespaces = entry; 99 100 printf(" Namespace ID: %d size: %juGB\n", spdk_nvme_ns_get_id(ns), 101 spdk_nvme_ns_get_size(ns) / 1000000000); 102 } 103 104 static uint32_t 105 get_max_block_size(void) 106 { 107 struct ns_entry *ns; 108 uint32_t max_block_size, temp_block_size; 109 110 ns = g_namespaces; 111 max_block_size = 0; 112 113 while (ns != NULL) { 114 temp_block_size = spdk_nvme_ns_get_sector_size(ns->ns); 115 max_block_size = temp_block_size > max_block_size ? temp_block_size : max_block_size; 116 ns = ns->next; 117 } 118 119 return max_block_size; 120 } 121 122 static void 123 write_complete(void *arg, const struct spdk_nvme_cpl *cpl) 124 { 125 struct simple_copy_context *context = arg; 126 127 context->writes_completed++; 128 129 if (spdk_nvme_cpl_is_error(cpl)) { 130 printf("write cpl error. SC 0x%x SCT 0x%x\n", cpl->status.sc, cpl->status.sct); 131 context->error++; 132 return; 133 } 134 } 135 136 static void 137 read_complete(void *arg, const struct spdk_nvme_cpl *cpl) 138 { 139 struct simple_copy_context *context = arg; 140 struct ns_entry *ns_entry = context->ns_entry; 141 int rc; 142 143 if (spdk_nvme_cpl_is_error(cpl)) { 144 printf("read cpl error. SC 0x%x SCT 0x%x\n", cpl->status.sc, cpl->status.sct); 145 context->reads_completed++; 146 context->error++; 147 return; 148 } 149 150 rc = memcmp(context->write_bufs[context->reads_completed], 151 context->read_bufs[context->reads_completed], spdk_nvme_ns_get_sector_size(ns_entry->ns)); 152 if (rc == 0) { 153 context->matches_written_data++; 154 } 155 156 context->reads_completed++; 157 } 158 159 static void 160 simple_copy_complete(void *arg, const struct spdk_nvme_cpl *cpl) 161 { 162 struct simple_copy_context *context = arg; 163 164 context->simple_copy_completed = 1; 165 166 if (spdk_nvme_cpl_is_error(cpl)) { 167 printf("scc cpl error. SC 0x%x SCT 0x%x\n", cpl->status.sc, cpl->status.sct); 168 context->error++; 169 return; 170 } 171 172 printf("Copied LBAs from 0 - %d to the Destination LBA %d\n", NUM_LBAS - 1, DEST_LBA); 173 context->reads_completed = 0; 174 context->matches_written_data = 0; 175 } 176 177 static void 178 simple_copy_test(void) 179 { 180 struct ns_entry *ns_entry; 181 struct spdk_nvme_ctrlr *ctrlr; 182 const struct spdk_nvme_ctrlr_data *data; 183 struct simple_copy_context context; 184 struct spdk_nvme_scc_source_range range; 185 uint32_t max_block_size; 186 int rc, i; 187 188 memset(&context, 0, sizeof(struct simple_copy_context)); 189 max_block_size = get_max_block_size(); 190 ns_entry = g_namespaces; 191 192 context.write_bufs = calloc(NUM_LBAS, sizeof(char *)); 193 if (context.write_bufs == NULL) { 194 printf("could not allocate write buffer pointers for test\n"); 195 cleanup(&context); 196 return; 197 } 198 199 context.read_bufs = calloc(NUM_LBAS, sizeof(char *)); 200 if (context.read_bufs == NULL) { 201 printf("could not allocate read buffer pointers for test\n"); 202 cleanup(&context); 203 return; 204 } 205 206 for (i = 0; i < NUM_LBAS; i++) { 207 context.write_bufs[i] = spdk_zmalloc(0x1000, max_block_size, NULL, SPDK_ENV_LCORE_ID_ANY, 208 SPDK_MALLOC_DMA); 209 if (context.write_bufs[i] == NULL) { 210 printf("could not allocate write buffer %d for test\n", i); 211 cleanup(&context); 212 return; 213 } 214 215 fill_random(context.write_bufs[i], 0x1000); 216 context.read_bufs[i] = spdk_zmalloc(0x1000, max_block_size, NULL, SPDK_ENV_LCORE_ID_ANY, 217 SPDK_MALLOC_DMA); 218 if (context.read_bufs[i] == NULL) { 219 printf("could not allocate read buffer %d for test\n", i); 220 cleanup(&context); 221 return; 222 } 223 } 224 225 while (ns_entry != NULL) { 226 227 ns_entry->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ns_entry->ctrlr, NULL, 0); 228 if (ns_entry->qpair == NULL) { 229 printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n"); 230 cleanup(&context); 231 return; 232 } 233 234 ctrlr = spdk_nvme_ns_get_ctrlr(ns_entry->ns); 235 data = spdk_nvme_ctrlr_get_data(ctrlr); 236 237 printf("\nController %-20.20s (%-20.20s)\n", data->mn, data->sn); 238 printf("Controller PCI vendor:%u PCI subsystem vendor:%u\n", data->vid, data->ssvid); 239 printf("Namespace Block Size:%u\n", spdk_nvme_ns_get_sector_size(ns_entry->ns)); 240 printf("Writing LBAs 0 to %d with Random Data\n", NUM_LBAS - 1); 241 242 context.ns_entry = ns_entry; 243 244 for (i = 0; i < NUM_LBAS; i++) { 245 rc = spdk_nvme_ns_cmd_write(ns_entry->ns, ns_entry->qpair, context.write_bufs[i], 246 i, 247 1, 248 write_complete, &context, 0); 249 if (rc) { 250 printf("submission of write I/O failed\n"); 251 } 252 } 253 while (context.writes_completed < NUM_LBAS) { 254 rc = spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 255 if (rc < 0) { 256 printf("Error processing write completions, rc: %d\n", rc); 257 break; 258 } 259 } 260 261 if (context.error) { 262 printf("Error : %d Write completions failed\n", 263 context.error); 264 spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 265 cleanup(&context); 266 exit(1); 267 } 268 269 range.nlb = NUM_LBAS - 1; 270 range.slba = 0; 271 272 rc = spdk_nvme_ns_cmd_copy(ns_entry->ns, ns_entry->qpair, 273 &range, 1, DEST_LBA, simple_copy_complete, &context); 274 275 if (rc) { 276 printf("submission of copy I/O failed\n"); 277 } 278 279 while (!context.simple_copy_completed) { 280 rc = spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 281 if (rc < 0) { 282 printf("Error processing copy completions, rc: %d\n", rc); 283 break; 284 } 285 } 286 287 if (context.error) { 288 printf("Error : Copy completion failed\n"); 289 spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 290 cleanup(&context); 291 exit(1); 292 } 293 294 for (i = 0; i < NUM_LBAS; i++) { 295 rc = spdk_nvme_ns_cmd_read(ns_entry->ns, ns_entry->qpair, context.read_bufs[i], 296 DEST_LBA + i, /* LBA start */ 297 1, /* number of LBAs */ 298 read_complete, &context, 0); 299 if (rc) { 300 printf("submission of read I/O failed\n"); 301 } 302 /* block after each read command so that we can match the block to the write buffer. */ 303 while (context.reads_completed <= i) { 304 rc = spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 305 if (rc < 0) { 306 printf("Error processing read completions, rc: %d\n", rc); 307 break; 308 } 309 } 310 } 311 312 if (context.error) { 313 printf("Error : %d Read completions failed\n", 314 context.error); 315 spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 316 cleanup(&context); 317 exit(1); 318 } 319 320 printf("LBAs matching Written Data: %d\n", context.matches_written_data); 321 322 if (context.matches_written_data != NUM_LBAS) { 323 printf("Error : %d LBAs are copied correctly out of %d LBAs\n", 324 context.matches_written_data, NUM_LBAS); 325 spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 326 cleanup(&context); 327 exit(1); 328 } 329 330 /* reset counters in between each namespace. */ 331 context.matches_written_data = 0; 332 context.writes_completed = 0; 333 context.reads_completed = 0; 334 context.simple_copy_completed = 0; 335 336 spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 337 ns_entry = ns_entry->next; 338 } 339 cleanup(&context); 340 } 341 342 static bool 343 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 344 struct spdk_nvme_ctrlr_opts *opts) 345 { 346 printf("Attaching to %s\n", trid->traddr); 347 348 return true; 349 } 350 351 static void 352 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 353 struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 354 { 355 int num_ns; 356 struct spdk_nvme_ns *ns; 357 const struct spdk_nvme_ctrlr_data *cdata; 358 359 cdata = spdk_nvme_ctrlr_get_data(ctrlr); 360 361 if (cdata->oncs.copy) { 362 printf("Controller supports SCC. Attached to %s\n", trid->traddr); 363 /* 364 * Use only the first namespace from each controller since we are testing controller level functionality. 365 */ 366 num_ns = spdk_nvme_ctrlr_get_num_ns(ctrlr); 367 if (num_ns < 1) { 368 printf("No valid namespaces in controller\n"); 369 } else { 370 ns = spdk_nvme_ctrlr_get_ns(ctrlr, 1); 371 register_ns(ctrlr, ns); 372 } 373 } else { 374 printf("Controller doesn't support SCC. Not Attached to %s\n", trid->traddr); 375 } 376 } 377 378 static void 379 cleanup(struct simple_copy_context *context) 380 { 381 struct ns_entry *ns_entry = g_namespaces; 382 struct spdk_nvme_detach_ctx *detach_ctx = NULL; 383 int i; 384 385 while (ns_entry) { 386 struct ns_entry *next = ns_entry->next; 387 388 spdk_nvme_detach_async(ns_entry->ctrlr, &detach_ctx); 389 390 free(ns_entry); 391 ns_entry = next; 392 } 393 394 if (detach_ctx) { 395 spdk_nvme_detach_poll(detach_ctx); 396 } 397 398 for (i = 0; i < NUM_LBAS; i++) { 399 if (context->write_bufs && context->write_bufs[i]) { 400 spdk_free(context->write_bufs[i]); 401 } else { 402 break; 403 } 404 if (context->read_bufs && context->read_bufs[i]) { 405 spdk_free(context->read_bufs[i]); 406 } else { 407 break; 408 } 409 } 410 411 free(context->write_bufs); 412 free(context->read_bufs); 413 } 414 415 int main(int argc, char **argv) 416 { 417 int rc; 418 struct spdk_env_opts opts; 419 420 spdk_env_opts_init(&opts); 421 opts.name = "simple_copy"; 422 opts.shm_id = 0; 423 if (spdk_env_init(&opts) < 0) { 424 fprintf(stderr, "Unable to initialize SPDK env\n"); 425 return 1; 426 } 427 428 printf("Initializing NVMe Controllers\n"); 429 430 rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL); 431 if (rc != 0) { 432 fprintf(stderr, "spdk_nvme_probe() failed\n"); 433 return 1; 434 } 435 436 if (g_namespaces == NULL) { 437 fprintf(stderr, "no NVMe controllers found\n"); 438 return 1; 439 } 440 441 printf("Initialization complete.\n"); 442 simple_copy_test(); 443 return 0; 444 } 445