xref: /spdk/examples/nvme/cmb_copy/cmb_copy.c (revision c55bec1e13acad69ef679093db141ee51440eabd)
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 	char *sp = NULL;
209 	long int val;
210 
211 	tok = strtok_r(in, CMB_COPY_DELIM, &sp);
212 	if (tok == NULL) {
213 		goto err;
214 	}
215 	snprintf(&io->trid.traddr[0], SPDK_NVMF_TRADDR_MAX_LEN + 1,
216 		 "%s", tok);
217 
218 	tok = strtok_r(NULL, CMB_COPY_DELIM, &sp);
219 	if (tok == NULL) {
220 		goto err;
221 	}
222 	val = spdk_strtol(tok, 10);
223 	if (val < 0) {
224 		goto err;
225 	}
226 	io->nsid  = (unsigned)val;
227 
228 	tok = strtok_r(NULL, CMB_COPY_DELIM, &sp);
229 	if (tok == NULL) {
230 		goto err;
231 	}
232 	val = spdk_strtol(tok, 10);
233 	if (val < 0) {
234 		goto err;
235 	}
236 	io->slba  = (unsigned)val;
237 
238 	tok = strtok_r(NULL, CMB_COPY_DELIM, &sp);
239 	if (tok == NULL) {
240 		goto err;
241 	}
242 	val = spdk_strtol(tok, 10);
243 	if (val < 0) {
244 		goto err;
245 	}
246 	io->nlbas = (unsigned)val;
247 
248 	tok = strtok_r(NULL, CMB_COPY_DELIM, &sp);
249 	if (tok != NULL) {
250 		goto err;
251 	}
252 	return;
253 
254 err:
255 	fprintf(stderr, "%s: error parsing %s\n", __func__, in);
256 	exit(-1);
257 
258 }
259 
260 static int
261 parse_args(int argc, char **argv)
262 {
263 	int op;
264 	unsigned read = 0, write = 0, cmb = 0;
265 
266 	while ((op = getopt(argc, argv, "r:w:c:")) != -1) {
267 		switch (op) {
268 		case 'r':
269 			parse(optarg, &g_config.read);
270 			read = 1;
271 			break;
272 		case 'w':
273 			parse(optarg, &g_config.write);
274 			write = 1;
275 			break;
276 		case 'c':
277 			snprintf(g_config.cmb.trid.traddr, SPDK_NVMF_TRADDR_MAX_LEN + 1,
278 				 "%s", optarg);
279 			cmb = 1;
280 			break;
281 		default:
282 			usage(argv[0]);
283 			return 1;
284 		}
285 	}
286 
287 	if ((!read || !write || !cmb)) {
288 		usage(argv[0]);
289 		return 1;
290 	}
291 
292 	return 0;
293 }
294 
295 int
296 main(int argc, char **argv)
297 {
298 	int rc = 0;
299 	struct spdk_env_opts opts;
300 
301 	/*
302 	 * Parse the input arguments. For now we use the following
303 	 * format list:
304 	 *
305 	 * <pci id>-<namespace>-<start LBA>-<number of LBAs>
306 	 *
307 	 */
308 	rc = parse_args(argc, argv);
309 	if (rc) {
310 		fprintf(stderr, "Error in parse_args(): %d\n",
311 			rc);
312 		return -1;
313 	}
314 
315 	/*
316 	 * SPDK relies on an abstraction around the local environment
317 	 * named env that handles memory allocation and PCI device operations.
318 	 * This library must be initialized first.
319 	 *
320 	 */
321 	opts.opts_size = sizeof(opts);
322 	spdk_env_opts_init(&opts);
323 	opts.name = "cmb_copy";
324 	opts.shm_id = 0;
325 	if (spdk_env_init(&opts) < 0) {
326 		fprintf(stderr, "Unable to initialize SPDK env\n");
327 		return 1;
328 	}
329 
330 	/*
331 	 * CMBs only apply to PCIe attached NVMe controllers so we
332 	 * only probe the PCIe bus. This is the default when we pass
333 	 * in NULL for the first argument.
334 	 */
335 
336 	rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL);
337 	if (rc) {
338 		fprintf(stderr, "Error in spdk_nvme_probe(): %d\n",
339 			rc);
340 		return -1;
341 	}
342 
343 	/*
344 	 * For now enforce that the read and write controller are not
345 	 * the same. This avoids an internal only DMA.
346 	 */
347 	if (!strcmp(g_config.write.trid.traddr, g_config.read.trid.traddr)) {
348 		fprintf(stderr, "Read and Write controllers must differ!\n");
349 		return -1;
350 	}
351 
352 	/*
353 	 * Perform a few sanity checks and set the buffer size for the
354 	 * CMB.
355 	 */
356 	if (g_config.read.nlbas * g_config.read.lba_size !=
357 	    g_config.write.nlbas * g_config.write.lba_size) {
358 		fprintf(stderr, "Read and write sizes do not match!\n");
359 		return -1;
360 	}
361 	g_config.copy_size = g_config.read.nlbas * g_config.read.lba_size;
362 
363 	/*
364 	 * Get the ctrlr pointer for the CMB. For now we assume this
365 	 * is either the read or write NVMe controller though in
366 	 * theory that is not a necessary condition.
367 	 */
368 
369 	if (!strcmp(g_config.cmb.trid.traddr, g_config.read.trid.traddr)) {
370 		g_config.cmb.ctrlr = g_config.read.ctrlr;
371 	}
372 	if (!strcmp(g_config.cmb.trid.traddr, g_config.write.trid.traddr)) {
373 		g_config.cmb.ctrlr = g_config.write.ctrlr;
374 	}
375 
376 	if (!g_config.read.ctrlr || !g_config.write.ctrlr) {
377 		fprintf(stderr, "No NVMe controller that support CMB was found!\n");
378 		return -1;
379 	}
380 
381 	/*
382 	 * Call the cmb_copy() function which performs the CMB
383 	 * based copy or returns an error code if it fails.
384 	 */
385 	rc = cmb_copy();
386 	if (rc) {
387 		fprintf(stderr, "Error in spdk_cmb_copy(): %d\n",
388 			rc);
389 		return -1;
390 	}
391 
392 	return rc;
393 }
394