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