xref: /spdk/examples/nvme/cmb_copy/cmb_copy.c (revision 00bbcea09ebf0738680e69d794cbbe340f1499b3)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2020 Intel Corporation.
3  *   Copyright (c) Eideticom Inc.
4  *   All rights reserved.
5  */
6 
7 #include "spdk/stdinc.h"
8 
9 #include "spdk/env.h"
10 #include "spdk/nvme.h"
11 #include "spdk/string.h"
12 
13 #define CMB_COPY_DELIM "-"
14 #define CMB_COPY_READ 0
15 #define CMB_COPY_WRITE 1
16 
17 struct nvme_io {
18 	struct spdk_nvme_ctrlr *ctrlr;
19 	struct spdk_nvme_transport_id trid;
20 	struct spdk_nvme_qpair	*qpair;
21 	struct spdk_nvme_ns *ns;
22 	unsigned nsid;
23 	unsigned slba;
24 	unsigned nlbas;
25 	uint32_t lba_size;
26 	unsigned done;
27 };
28 
29 struct cmb_t {
30 	struct spdk_nvme_transport_id trid;
31 	struct spdk_nvme_ctrlr *ctrlr;
32 };
33 
34 struct config {
35 	struct nvme_io read;
36 	struct nvme_io write;
37 	struct cmb_t   cmb;
38 	size_t         copy_size;
39 };
40 
41 static struct config g_config;
42 
43 /* Namespaces index from 1. Return 0 to invoke an error */
44 static unsigned
45 get_nsid(const struct spdk_nvme_transport_id *trid)
46 {
47 	if (!strcmp(trid->traddr, g_config.read.trid.traddr)) {
48 		return g_config.read.nsid;
49 	}
50 	if (!strcmp(trid->traddr, g_config.write.trid.traddr)) {
51 		return g_config.write.nsid;
52 	}
53 	return 0;
54 }
55 
56 static int
57 get_rw(const struct spdk_nvme_transport_id *trid)
58 {
59 	if (!strcmp(trid->traddr, g_config.read.trid.traddr)) {
60 		return CMB_COPY_READ;
61 	}
62 	if (!strcmp(trid->traddr, g_config.write.trid.traddr)) {
63 		return CMB_COPY_WRITE;
64 	}
65 	return -1;
66 }
67 
68 static void
69 check_io(void *arg, const struct spdk_nvme_cpl *completion)
70 {
71 	int *rw = (unsigned *)arg;
72 
73 	if (*rw == CMB_COPY_READ) {
74 		g_config.read.done = 1;
75 	} else {
76 		g_config.write.done = 1;
77 	}
78 }
79 
80 static int
81 cmb_copy(void)
82 {
83 	int rc = 0, rw;
84 	void *buf;
85 	size_t sz;
86 
87 	/* Allocate QPs for the read and write controllers */
88 	g_config.read.qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.read.ctrlr, NULL, 0);
89 	g_config.write.qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.write.ctrlr, NULL, 0);
90 	if (g_config.read.qpair == NULL || g_config.read.qpair == NULL) {
91 		printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
92 		return -ENOMEM;
93 	}
94 
95 	/* Allocate a buffer from our CMB */
96 	buf = spdk_nvme_ctrlr_map_cmb(g_config.cmb.ctrlr, &sz);
97 	if (buf == NULL || sz < g_config.copy_size) {
98 		printf("ERROR: buffer allocation failed\n");
99 		printf("Are you sure %s has a valid CMB?\n",
100 		       g_config.cmb.trid.traddr);
101 		return -ENOMEM;
102 	}
103 
104 	/* Clear the done flags */
105 	g_config.read.done = 0;
106 	g_config.write.done = 0;
107 
108 	rw = CMB_COPY_READ;
109 	/* Do the read to the CMB IO buffer */
110 	rc = spdk_nvme_ns_cmd_read(g_config.read.ns, g_config.read.qpair, buf,
111 				   g_config.read.slba, g_config.read.nlbas,
112 				   check_io, &rw, 0);
113 	if (rc != 0) {
114 		fprintf(stderr, "starting read I/O failed\n");
115 		return -EIO;
116 	}
117 	while (!g_config.read.done) {
118 		spdk_nvme_qpair_process_completions(g_config.read.qpair, 0);
119 	}
120 
121 	/* Do the write from the CMB IO buffer */
122 	rw = CMB_COPY_WRITE;
123 	rc = spdk_nvme_ns_cmd_write(g_config.write.ns, g_config.write.qpair, buf,
124 				    g_config.write.slba, g_config.write.nlbas,
125 				    check_io, &rw, 0);
126 	if (rc != 0) {
127 		fprintf(stderr, "starting write I/O failed\n");
128 		return -EIO;
129 	}
130 	while (!g_config.write.done) {
131 		spdk_nvme_qpair_process_completions(g_config.write.qpair, 0);
132 	}
133 
134 	/* Clear the done flags */
135 	g_config.read.done = 0;
136 	g_config.write.done = 0;
137 
138 	/* Free CMB buffer */
139 	spdk_nvme_ctrlr_unmap_cmb(g_config.cmb.ctrlr);
140 
141 	/* Free the queues */
142 	spdk_nvme_ctrlr_free_io_qpair(g_config.read.qpair);
143 	spdk_nvme_ctrlr_free_io_qpair(g_config.write.qpair);
144 
145 	return rc;
146 }
147 
148 static bool
149 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
150 	 struct spdk_nvme_ctrlr_opts *opts)
151 {
152 	/* We will only attach to the read or write controller */
153 	if (strcmp(trid->traddr, g_config.read.trid.traddr) &&
154 	    strcmp(trid->traddr, g_config.write.trid.traddr)) {
155 		printf("%s - not probed %s!\n", __func__, trid->traddr);
156 		return 0;
157 	}
158 
159 	opts->use_cmb_sqs = false;
160 
161 	printf("%s - probed %s!\n", __func__, trid->traddr);
162 	return 1;
163 }
164 
165 static void
166 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
167 	  struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
168 {
169 	struct spdk_nvme_ns *ns;
170 
171 	ns = spdk_nvme_ctrlr_get_ns(ctrlr, get_nsid(trid));
172 	if (ns == NULL) {
173 		fprintf(stderr, "Could not locate namespace %d on controller %s.\n",
174 			get_nsid(trid), trid->traddr);
175 		exit(-1);
176 	}
177 	if (get_rw(trid) == CMB_COPY_READ) {
178 		g_config.read.ctrlr    = ctrlr;
179 		g_config.read.ns       = ns;
180 		g_config.read.lba_size = spdk_nvme_ns_get_sector_size(ns);
181 	} else {
182 		g_config.write.ctrlr    = ctrlr;
183 		g_config.write.ns       = ns;
184 		g_config.write.lba_size = spdk_nvme_ns_get_sector_size(ns);
185 	}
186 	printf("%s - attached %s!\n", __func__, trid->traddr);
187 
188 	return;
189 }
190 
191 static void
192 usage(char *program_name)
193 {
194 	printf("%s options (all mandatory)", program_name);
195 	printf("\n");
196 	printf("\t[-r NVMe read parameters]\n");
197 	printf("\t[-w NVMe write parameters]\n");
198 	printf("\t[-c CMB to use for data buffers]\n");
199 	printf("\n");
200 	printf("Read/Write params:\n");
201 	printf("  <pci id>-<namespace>-<start LBA>-<number of LBAs>\n");
202 }
203 
204 static void
205 parse(char *in, struct nvme_io *io)
206 {
207 	char *tok = NULL;
208 	long int val;
209 
210 	tok = strtok(in, CMB_COPY_DELIM);
211 	if (tok == NULL) {
212 		goto err;
213 	}
214 	snprintf(&io->trid.traddr[0], SPDK_NVMF_TRADDR_MAX_LEN + 1,
215 		 "%s", tok);
216 
217 	tok = strtok(NULL, CMB_COPY_DELIM);
218 	if (tok == NULL) {
219 		goto err;
220 	}
221 	val = spdk_strtol(tok, 10);
222 	if (val < 0) {
223 		goto err;
224 	}
225 	io->nsid  = (unsigned)val;
226 
227 	tok = strtok(NULL, CMB_COPY_DELIM);
228 	if (tok == NULL) {
229 		goto err;
230 	}
231 	val = spdk_strtol(tok, 10);
232 	if (val < 0) {
233 		goto err;
234 	}
235 	io->slba  = (unsigned)val;
236 
237 	tok = strtok(NULL, CMB_COPY_DELIM);
238 	if (tok == NULL) {
239 		goto err;
240 	}
241 	val = spdk_strtol(tok, 10);
242 	if (val < 0) {
243 		goto err;
244 	}
245 	io->nlbas = (unsigned)val;
246 
247 	tok = strtok(NULL, CMB_COPY_DELIM);
248 	if (tok != NULL) {
249 		goto err;
250 	}
251 	return;
252 
253 err:
254 	fprintf(stderr, "%s: error parsing %s\n", __func__, in);
255 	exit(-1);
256 
257 }
258 
259 static int
260 parse_args(int argc, char **argv)
261 {
262 	int op;
263 	unsigned read = 0, write = 0, cmb = 0;
264 
265 	while ((op = getopt(argc, argv, "r:w:c:")) != -1) {
266 		switch (op) {
267 		case 'r':
268 			parse(optarg, &g_config.read);
269 			read = 1;
270 			break;
271 		case 'w':
272 			parse(optarg, &g_config.write);
273 			write = 1;
274 			break;
275 		case 'c':
276 			snprintf(g_config.cmb.trid.traddr, SPDK_NVMF_TRADDR_MAX_LEN + 1,
277 				 "%s", optarg);
278 			cmb = 1;
279 			break;
280 		default:
281 			usage(argv[0]);
282 			return 1;
283 		}
284 	}
285 
286 	if ((!read || !write || !cmb)) {
287 		usage(argv[0]);
288 		return 1;
289 	}
290 
291 	return 0;
292 }
293 
294 int
295 main(int argc, char **argv)
296 {
297 	int rc = 0;
298 	struct spdk_env_opts opts;
299 
300 	/*
301 	 * Parse the input arguments. For now we use the following
302 	 * format list:
303 	 *
304 	 * <pci id>-<namespace>-<start LBA>-<number of LBAs>
305 	 *
306 	 */
307 	rc = parse_args(argc, argv);
308 	if (rc) {
309 		fprintf(stderr, "Error in parse_args(): %d\n",
310 			rc);
311 		return -1;
312 	}
313 
314 	/*
315 	 * SPDK relies on an abstraction around the local environment
316 	 * named env that handles memory allocation and PCI device operations.
317 	 * This library must be initialized first.
318 	 *
319 	 */
320 	opts.opts_size = sizeof(opts);
321 	spdk_env_opts_init(&opts);
322 	opts.name = "cmb_copy";
323 	opts.shm_id = 0;
324 	if (spdk_env_init(&opts) < 0) {
325 		fprintf(stderr, "Unable to initialize SPDK env\n");
326 		return 1;
327 	}
328 
329 	/*
330 	 * CMBs only apply to PCIe attached NVMe controllers so we
331 	 * only probe the PCIe bus. This is the default when we pass
332 	 * in NULL for the first argument.
333 	 */
334 
335 	rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL);
336 	if (rc) {
337 		fprintf(stderr, "Error in spdk_nvme_probe(): %d\n",
338 			rc);
339 		return -1;
340 	}
341 
342 	/*
343 	 * For now enforce that the read and write controller are not
344 	 * the same. This avoids an internal only DMA.
345 	 */
346 	if (!strcmp(g_config.write.trid.traddr, g_config.read.trid.traddr)) {
347 		fprintf(stderr, "Read and Write controllers must differ!\n");
348 		return -1;
349 	}
350 
351 	/*
352 	 * Perform a few sanity checks and set the buffer size for the
353 	 * CMB.
354 	 */
355 	if (g_config.read.nlbas * g_config.read.lba_size !=
356 	    g_config.write.nlbas * g_config.write.lba_size) {
357 		fprintf(stderr, "Read and write sizes do not match!\n");
358 		return -1;
359 	}
360 	g_config.copy_size = g_config.read.nlbas * g_config.read.lba_size;
361 
362 	/*
363 	 * Get the ctrlr pointer for the CMB. For now we assume this
364 	 * is either the read or write NVMe controller though in
365 	 * theory that is not a necessary condition.
366 	 */
367 
368 	if (!strcmp(g_config.cmb.trid.traddr, g_config.read.trid.traddr)) {
369 		g_config.cmb.ctrlr = g_config.read.ctrlr;
370 	}
371 	if (!strcmp(g_config.cmb.trid.traddr, g_config.write.trid.traddr)) {
372 		g_config.cmb.ctrlr = g_config.write.ctrlr;
373 	}
374 
375 	if (!g_config.read.ctrlr || !g_config.write.ctrlr) {
376 		fprintf(stderr, "No NVMe controller that support CMB was found!\n");
377 		return -1;
378 	}
379 
380 	/*
381 	 * Call the cmb_copy() function which performs the CMB
382 	 * based copy or returns an error code if it fails.
383 	 */
384 	rc = cmb_copy();
385 	if (rc) {
386 		fprintf(stderr, "Error in spdk_cmb_copy(): %d\n",
387 			rc);
388 		return -1;
389 	}
390 
391 	return rc;
392 }
393