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 if (!g_config.read.ctrlr || !g_config.write.ctrlr) { 401 fprintf(stderr, "No NVMe controller that support CMB was found!\n"); 402 return -1; 403 } 404 405 /* 406 * Call the cmb_copy() function which performs the CMB 407 * based copy or returns an error code if it fails. 408 */ 409 rc = cmb_copy(); 410 if (rc) { 411 fprintf(stderr, "Error in spdk_cmb_copy(): %d\n", 412 rc); 413 return -1; 414 } 415 416 return rc; 417 } 418