xref: /spdk/test/nvme/simple_copy/simple_copy.c (revision 57fd99b91e71a4baa5543e19ff83958dc99d4dac)
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