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