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