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