1488570ebSJim Harris /* SPDX-License-Identifier: BSD-3-Clause 2a6dbe372Spaul luse * Copyright (C) 2020 Intel Corporation. 31f9da54eSStephen Bates * Copyright (c) Eideticom Inc. 41f9da54eSStephen Bates * All rights reserved. 51f9da54eSStephen Bates */ 61f9da54eSStephen Bates 71f9da54eSStephen Bates #include "spdk/stdinc.h" 81f9da54eSStephen Bates 91f9da54eSStephen Bates #include "spdk/env.h" 101f9da54eSStephen Bates #include "spdk/nvme.h" 110f9dc2afSShuhei Matsumoto #include "spdk/string.h" 121f9da54eSStephen Bates 131f9da54eSStephen Bates #define CMB_COPY_DELIM "-" 141f9da54eSStephen Bates #define CMB_COPY_READ 0 151f9da54eSStephen Bates #define CMB_COPY_WRITE 1 161f9da54eSStephen Bates 171f9da54eSStephen Bates struct nvme_io { 181f9da54eSStephen Bates struct spdk_nvme_ctrlr *ctrlr; 191f9da54eSStephen Bates struct spdk_nvme_transport_id trid; 201f9da54eSStephen Bates struct spdk_nvme_qpair *qpair; 211f9da54eSStephen Bates struct spdk_nvme_ns *ns; 221f9da54eSStephen Bates unsigned nsid; 231f9da54eSStephen Bates unsigned slba; 241f9da54eSStephen Bates unsigned nlbas; 251f9da54eSStephen Bates uint32_t lba_size; 261f9da54eSStephen Bates unsigned done; 271f9da54eSStephen Bates }; 281f9da54eSStephen Bates 291f9da54eSStephen Bates struct cmb_t { 301f9da54eSStephen Bates struct spdk_nvme_transport_id trid; 311f9da54eSStephen Bates struct spdk_nvme_ctrlr *ctrlr; 321f9da54eSStephen Bates }; 331f9da54eSStephen Bates 341f9da54eSStephen Bates struct config { 351f9da54eSStephen Bates struct nvme_io read; 361f9da54eSStephen Bates struct nvme_io write; 371f9da54eSStephen Bates struct cmb_t cmb; 381f9da54eSStephen Bates size_t copy_size; 391f9da54eSStephen Bates }; 401f9da54eSStephen Bates 411f9da54eSStephen Bates static struct config g_config; 421f9da54eSStephen Bates 431f9da54eSStephen Bates /* Namespaces index from 1. Return 0 to invoke an error */ 4404470499Sdongx.yi static unsigned 4504470499Sdongx.yi get_nsid(const struct spdk_nvme_transport_id *trid) 461f9da54eSStephen Bates { 471f9da54eSStephen Bates if (!strcmp(trid->traddr, g_config.read.trid.traddr)) { 481f9da54eSStephen Bates return g_config.read.nsid; 491f9da54eSStephen Bates } 501f9da54eSStephen Bates if (!strcmp(trid->traddr, g_config.write.trid.traddr)) { 511f9da54eSStephen Bates return g_config.write.nsid; 521f9da54eSStephen Bates } 531f9da54eSStephen Bates return 0; 541f9da54eSStephen Bates } 551f9da54eSStephen Bates 5604470499Sdongx.yi static int 5704470499Sdongx.yi get_rw(const struct spdk_nvme_transport_id *trid) 581f9da54eSStephen Bates { 591f9da54eSStephen Bates if (!strcmp(trid->traddr, g_config.read.trid.traddr)) { 601f9da54eSStephen Bates return CMB_COPY_READ; 611f9da54eSStephen Bates } 621f9da54eSStephen Bates if (!strcmp(trid->traddr, g_config.write.trid.traddr)) { 631f9da54eSStephen Bates return CMB_COPY_WRITE; 641f9da54eSStephen Bates } 651f9da54eSStephen Bates return -1; 661f9da54eSStephen Bates } 671f9da54eSStephen Bates 681f9da54eSStephen Bates static void 691f9da54eSStephen Bates check_io(void *arg, const struct spdk_nvme_cpl *completion) 701f9da54eSStephen Bates { 711f9da54eSStephen Bates int *rw = (unsigned *)arg; 721f9da54eSStephen Bates 731f9da54eSStephen Bates if (*rw == CMB_COPY_READ) { 741f9da54eSStephen Bates g_config.read.done = 1; 751f9da54eSStephen Bates } else { 761f9da54eSStephen Bates g_config.write.done = 1; 771f9da54eSStephen Bates } 781f9da54eSStephen Bates } 791f9da54eSStephen Bates 801f9da54eSStephen Bates static int 811f9da54eSStephen Bates cmb_copy(void) 821f9da54eSStephen Bates { 831f9da54eSStephen Bates int rc = 0, rw; 841f9da54eSStephen Bates void *buf; 85265a8436SBen Walker size_t sz; 861f9da54eSStephen Bates 871f9da54eSStephen Bates /* Allocate QPs for the read and write controllers */ 881f9da54eSStephen Bates g_config.read.qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.read.ctrlr, NULL, 0); 891f9da54eSStephen Bates g_config.write.qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.write.ctrlr, NULL, 0); 901f9da54eSStephen Bates if (g_config.read.qpair == NULL || g_config.read.qpair == NULL) { 911f9da54eSStephen Bates printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n"); 921f9da54eSStephen Bates return -ENOMEM; 931f9da54eSStephen Bates } 941f9da54eSStephen Bates 951f9da54eSStephen Bates /* Allocate a buffer from our CMB */ 96265a8436SBen Walker buf = spdk_nvme_ctrlr_map_cmb(g_config.cmb.ctrlr, &sz); 97c34f224fSChangpeng Liu if (buf == NULL || sz < g_config.copy_size) { 981f9da54eSStephen Bates printf("ERROR: buffer allocation failed\n"); 991f9da54eSStephen Bates printf("Are you sure %s has a valid CMB?\n", 1001f9da54eSStephen Bates g_config.cmb.trid.traddr); 1011f9da54eSStephen Bates return -ENOMEM; 1021f9da54eSStephen Bates } 1031f9da54eSStephen Bates 1041f9da54eSStephen Bates /* Clear the done flags */ 1051f9da54eSStephen Bates g_config.read.done = 0; 1061f9da54eSStephen Bates g_config.write.done = 0; 1071f9da54eSStephen Bates 1081f9da54eSStephen Bates rw = CMB_COPY_READ; 1091f9da54eSStephen Bates /* Do the read to the CMB IO buffer */ 1101f9da54eSStephen Bates rc = spdk_nvme_ns_cmd_read(g_config.read.ns, g_config.read.qpair, buf, 1111f9da54eSStephen Bates g_config.read.slba, g_config.read.nlbas, 1121f9da54eSStephen Bates check_io, &rw, 0); 1131f9da54eSStephen Bates if (rc != 0) { 1141f9da54eSStephen Bates fprintf(stderr, "starting read I/O failed\n"); 1151f9da54eSStephen Bates return -EIO; 1161f9da54eSStephen Bates } 1171f9da54eSStephen Bates while (!g_config.read.done) { 1181f9da54eSStephen Bates spdk_nvme_qpair_process_completions(g_config.read.qpair, 0); 1191f9da54eSStephen Bates } 1201f9da54eSStephen Bates 1211f9da54eSStephen Bates /* Do the write from the CMB IO buffer */ 1221f9da54eSStephen Bates rw = CMB_COPY_WRITE; 1231f9da54eSStephen Bates rc = spdk_nvme_ns_cmd_write(g_config.write.ns, g_config.write.qpair, buf, 1241f9da54eSStephen Bates g_config.write.slba, g_config.write.nlbas, 1251f9da54eSStephen Bates check_io, &rw, 0); 1261f9da54eSStephen Bates if (rc != 0) { 1271f9da54eSStephen Bates fprintf(stderr, "starting write I/O failed\n"); 1281f9da54eSStephen Bates return -EIO; 1291f9da54eSStephen Bates } 1301f9da54eSStephen Bates while (!g_config.write.done) { 1311f9da54eSStephen Bates spdk_nvme_qpair_process_completions(g_config.write.qpair, 0); 1321f9da54eSStephen Bates } 1331f9da54eSStephen Bates 1341f9da54eSStephen Bates /* Clear the done flags */ 1351f9da54eSStephen Bates g_config.read.done = 0; 1361f9da54eSStephen Bates g_config.write.done = 0; 1371f9da54eSStephen Bates 1381f9da54eSStephen Bates /* Free CMB buffer */ 139265a8436SBen Walker spdk_nvme_ctrlr_unmap_cmb(g_config.cmb.ctrlr); 1401f9da54eSStephen Bates 1411f9da54eSStephen Bates /* Free the queues */ 1421f9da54eSStephen Bates spdk_nvme_ctrlr_free_io_qpair(g_config.read.qpair); 1431f9da54eSStephen Bates spdk_nvme_ctrlr_free_io_qpair(g_config.write.qpair); 1441f9da54eSStephen Bates 1451f9da54eSStephen Bates return rc; 1461f9da54eSStephen Bates } 1471f9da54eSStephen Bates 1481f9da54eSStephen Bates static bool 1491f9da54eSStephen Bates probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 1501f9da54eSStephen Bates struct spdk_nvme_ctrlr_opts *opts) 1511f9da54eSStephen Bates { 1521f9da54eSStephen Bates /* We will only attach to the read or write controller */ 1531f9da54eSStephen Bates if (strcmp(trid->traddr, g_config.read.trid.traddr) && 1541f9da54eSStephen Bates strcmp(trid->traddr, g_config.write.trid.traddr)) { 1551f9da54eSStephen Bates printf("%s - not probed %s!\n", __func__, trid->traddr); 1561f9da54eSStephen Bates return 0; 1571f9da54eSStephen Bates } 1581f9da54eSStephen Bates 159c34f224fSChangpeng Liu opts->use_cmb_sqs = false; 160c34f224fSChangpeng Liu 1611f9da54eSStephen Bates printf("%s - probed %s!\n", __func__, trid->traddr); 1621f9da54eSStephen Bates return 1; 1631f9da54eSStephen Bates } 1641f9da54eSStephen Bates 1651f9da54eSStephen Bates static void 1661f9da54eSStephen Bates attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 1671f9da54eSStephen Bates struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 1681f9da54eSStephen Bates { 1691f9da54eSStephen Bates struct spdk_nvme_ns *ns; 1701f9da54eSStephen Bates 1711f9da54eSStephen Bates ns = spdk_nvme_ctrlr_get_ns(ctrlr, get_nsid(trid)); 1721f9da54eSStephen Bates if (ns == NULL) { 1731f9da54eSStephen Bates fprintf(stderr, "Could not locate namespace %d on controller %s.\n", 1741f9da54eSStephen Bates get_nsid(trid), trid->traddr); 1751f9da54eSStephen Bates exit(-1); 1761f9da54eSStephen Bates } 1771f9da54eSStephen Bates if (get_rw(trid) == CMB_COPY_READ) { 1781f9da54eSStephen Bates g_config.read.ctrlr = ctrlr; 1791f9da54eSStephen Bates g_config.read.ns = ns; 1801f9da54eSStephen Bates g_config.read.lba_size = spdk_nvme_ns_get_sector_size(ns); 1811f9da54eSStephen Bates } else { 1821f9da54eSStephen Bates g_config.write.ctrlr = ctrlr; 1831f9da54eSStephen Bates g_config.write.ns = ns; 1841f9da54eSStephen Bates g_config.write.lba_size = spdk_nvme_ns_get_sector_size(ns); 1851f9da54eSStephen Bates } 1861f9da54eSStephen Bates printf("%s - attached %s!\n", __func__, trid->traddr); 1871f9da54eSStephen Bates 1881f9da54eSStephen Bates return; 1891f9da54eSStephen Bates } 1901f9da54eSStephen Bates 1911f9da54eSStephen Bates static void 1921f9da54eSStephen Bates usage(char *program_name) 1931f9da54eSStephen Bates { 1941f9da54eSStephen Bates printf("%s options (all mandatory)", program_name); 1951f9da54eSStephen Bates printf("\n"); 1961f9da54eSStephen Bates printf("\t[-r NVMe read parameters]\n"); 1971f9da54eSStephen Bates printf("\t[-w NVMe write parameters]\n"); 1981f9da54eSStephen Bates printf("\t[-c CMB to use for data buffers]\n"); 1991f9da54eSStephen Bates printf("\n"); 2001f9da54eSStephen Bates printf("Read/Write params:\n"); 2011f9da54eSStephen Bates printf(" <pci id>-<namespace>-<start LBA>-<number of LBAs>\n"); 2021f9da54eSStephen Bates } 2031f9da54eSStephen Bates 2041f9da54eSStephen Bates static void 2051f9da54eSStephen Bates parse(char *in, struct nvme_io *io) 2061f9da54eSStephen Bates { 2071f9da54eSStephen Bates char *tok = NULL; 208*b37db069SXuQi char *sp = NULL; 2090f9dc2afSShuhei Matsumoto long int val; 2101f9da54eSStephen Bates 211*b37db069SXuQi tok = strtok_r(in, CMB_COPY_DELIM, &sp); 2121f9da54eSStephen Bates if (tok == NULL) { 2131f9da54eSStephen Bates goto err; 2141f9da54eSStephen Bates } 2151f9da54eSStephen Bates snprintf(&io->trid.traddr[0], SPDK_NVMF_TRADDR_MAX_LEN + 1, 2161f9da54eSStephen Bates "%s", tok); 2171f9da54eSStephen Bates 218*b37db069SXuQi tok = strtok_r(NULL, CMB_COPY_DELIM, &sp); 2191f9da54eSStephen Bates if (tok == NULL) { 2201f9da54eSStephen Bates goto err; 2211f9da54eSStephen Bates } 2220f9dc2afSShuhei Matsumoto val = spdk_strtol(tok, 10); 2230f9dc2afSShuhei Matsumoto if (val < 0) { 2240f9dc2afSShuhei Matsumoto goto err; 2250f9dc2afSShuhei Matsumoto } 2260f9dc2afSShuhei Matsumoto io->nsid = (unsigned)val; 2271f9da54eSStephen Bates 228*b37db069SXuQi tok = strtok_r(NULL, CMB_COPY_DELIM, &sp); 2291f9da54eSStephen Bates if (tok == NULL) { 2301f9da54eSStephen Bates goto err; 2311f9da54eSStephen Bates } 2320f9dc2afSShuhei Matsumoto val = spdk_strtol(tok, 10); 2330f9dc2afSShuhei Matsumoto if (val < 0) { 2340f9dc2afSShuhei Matsumoto goto err; 2350f9dc2afSShuhei Matsumoto } 2360f9dc2afSShuhei Matsumoto io->slba = (unsigned)val; 2371f9da54eSStephen Bates 238*b37db069SXuQi tok = strtok_r(NULL, CMB_COPY_DELIM, &sp); 2391f9da54eSStephen Bates if (tok == NULL) { 2401f9da54eSStephen Bates goto err; 2411f9da54eSStephen Bates } 2420f9dc2afSShuhei Matsumoto val = spdk_strtol(tok, 10); 2430f9dc2afSShuhei Matsumoto if (val < 0) { 2440f9dc2afSShuhei Matsumoto goto err; 2450f9dc2afSShuhei Matsumoto } 2460f9dc2afSShuhei Matsumoto io->nlbas = (unsigned)val; 2471f9da54eSStephen Bates 248*b37db069SXuQi tok = strtok_r(NULL, CMB_COPY_DELIM, &sp); 2491f9da54eSStephen Bates if (tok != NULL) { 2501f9da54eSStephen Bates goto err; 2511f9da54eSStephen Bates } 2521f9da54eSStephen Bates return; 2531f9da54eSStephen Bates 2541f9da54eSStephen Bates err: 2551f9da54eSStephen Bates fprintf(stderr, "%s: error parsing %s\n", __func__, in); 2561f9da54eSStephen Bates exit(-1); 2571f9da54eSStephen Bates 2581f9da54eSStephen Bates } 2591f9da54eSStephen Bates 2601f9da54eSStephen Bates static int 2611f9da54eSStephen Bates parse_args(int argc, char **argv) 2621f9da54eSStephen Bates { 2631f9da54eSStephen Bates int op; 2641f9da54eSStephen Bates unsigned read = 0, write = 0, cmb = 0; 2651f9da54eSStephen Bates 2661f9da54eSStephen Bates while ((op = getopt(argc, argv, "r:w:c:")) != -1) { 2671f9da54eSStephen Bates switch (op) { 2681f9da54eSStephen Bates case 'r': 2691f9da54eSStephen Bates parse(optarg, &g_config.read); 2701f9da54eSStephen Bates read = 1; 2711f9da54eSStephen Bates break; 2721f9da54eSStephen Bates case 'w': 2731f9da54eSStephen Bates parse(optarg, &g_config.write); 2741f9da54eSStephen Bates write = 1; 2751f9da54eSStephen Bates break; 2761f9da54eSStephen Bates case 'c': 2771f9da54eSStephen Bates snprintf(g_config.cmb.trid.traddr, SPDK_NVMF_TRADDR_MAX_LEN + 1, 2781f9da54eSStephen Bates "%s", optarg); 2791f9da54eSStephen Bates cmb = 1; 2801f9da54eSStephen Bates break; 2811f9da54eSStephen Bates default: 2821f9da54eSStephen Bates usage(argv[0]); 2831f9da54eSStephen Bates return 1; 2841f9da54eSStephen Bates } 2851f9da54eSStephen Bates } 2861f9da54eSStephen Bates 2871f9da54eSStephen Bates if ((!read || !write || !cmb)) { 2881f9da54eSStephen Bates usage(argv[0]); 2891f9da54eSStephen Bates return 1; 2901f9da54eSStephen Bates } 2911f9da54eSStephen Bates 2921f9da54eSStephen Bates return 0; 2931f9da54eSStephen Bates } 2941f9da54eSStephen Bates 2958dd1cd21SBen Walker int 2968dd1cd21SBen Walker main(int argc, char **argv) 2971f9da54eSStephen Bates { 2981f9da54eSStephen Bates int rc = 0; 2991f9da54eSStephen Bates struct spdk_env_opts opts; 3001f9da54eSStephen Bates 3011f9da54eSStephen Bates /* 3021f9da54eSStephen Bates * Parse the input arguments. For now we use the following 3031f9da54eSStephen Bates * format list: 3041f9da54eSStephen Bates * 3051f9da54eSStephen Bates * <pci id>-<namespace>-<start LBA>-<number of LBAs> 3061f9da54eSStephen Bates * 3071f9da54eSStephen Bates */ 3081f9da54eSStephen Bates rc = parse_args(argc, argv); 3091f9da54eSStephen Bates if (rc) { 3101f9da54eSStephen Bates fprintf(stderr, "Error in parse_args(): %d\n", 3111f9da54eSStephen Bates rc); 3121f9da54eSStephen Bates return -1; 3131f9da54eSStephen Bates } 3141f9da54eSStephen Bates 3151f9da54eSStephen Bates /* 3161f9da54eSStephen Bates * SPDK relies on an abstraction around the local environment 3171f9da54eSStephen Bates * named env that handles memory allocation and PCI device operations. 3181f9da54eSStephen Bates * This library must be initialized first. 3191f9da54eSStephen Bates * 3201f9da54eSStephen Bates */ 32157fd99b9SJim Harris opts.opts_size = sizeof(opts); 3221f9da54eSStephen Bates spdk_env_opts_init(&opts); 3231f9da54eSStephen Bates opts.name = "cmb_copy"; 3241f9da54eSStephen Bates opts.shm_id = 0; 3251f9da54eSStephen Bates if (spdk_env_init(&opts) < 0) { 3261f9da54eSStephen Bates fprintf(stderr, "Unable to initialize SPDK env\n"); 3271f9da54eSStephen Bates return 1; 3281f9da54eSStephen Bates } 3291f9da54eSStephen Bates 3301f9da54eSStephen Bates /* 3311f9da54eSStephen Bates * CMBs only apply to PCIe attached NVMe controllers so we 3321f9da54eSStephen Bates * only probe the PCIe bus. This is the default when we pass 3331f9da54eSStephen Bates * in NULL for the first argument. 3341f9da54eSStephen Bates */ 3351f9da54eSStephen Bates 3361f9da54eSStephen Bates rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL); 3371f9da54eSStephen Bates if (rc) { 3381f9da54eSStephen Bates fprintf(stderr, "Error in spdk_nvme_probe(): %d\n", 3391f9da54eSStephen Bates rc); 3401f9da54eSStephen Bates return -1; 3411f9da54eSStephen Bates } 3421f9da54eSStephen Bates 3431f9da54eSStephen Bates /* 3441f9da54eSStephen Bates * For now enforce that the read and write controller are not 3451f9da54eSStephen Bates * the same. This avoids an internal only DMA. 3461f9da54eSStephen Bates */ 3471f9da54eSStephen Bates if (!strcmp(g_config.write.trid.traddr, g_config.read.trid.traddr)) { 3481f9da54eSStephen Bates fprintf(stderr, "Read and Write controllers must differ!\n"); 3491f9da54eSStephen Bates return -1; 3501f9da54eSStephen Bates } 3511f9da54eSStephen Bates 3521f9da54eSStephen Bates /* 3531f9da54eSStephen Bates * Perform a few sanity checks and set the buffer size for the 3541f9da54eSStephen Bates * CMB. 3551f9da54eSStephen Bates */ 3561f9da54eSStephen Bates if (g_config.read.nlbas * g_config.read.lba_size != 3571f9da54eSStephen Bates g_config.write.nlbas * g_config.write.lba_size) { 3581f9da54eSStephen Bates fprintf(stderr, "Read and write sizes do not match!\n"); 3591f9da54eSStephen Bates return -1; 3601f9da54eSStephen Bates } 3611f9da54eSStephen Bates g_config.copy_size = g_config.read.nlbas * g_config.read.lba_size; 3621f9da54eSStephen Bates 3631f9da54eSStephen Bates /* 3641f9da54eSStephen Bates * Get the ctrlr pointer for the CMB. For now we assume this 3651f9da54eSStephen Bates * is either the read or write NVMe controller though in 3661f9da54eSStephen Bates * theory that is not a necessary condition. 3671f9da54eSStephen Bates */ 3681f9da54eSStephen Bates 3691f9da54eSStephen Bates if (!strcmp(g_config.cmb.trid.traddr, g_config.read.trid.traddr)) { 3701f9da54eSStephen Bates g_config.cmb.ctrlr = g_config.read.ctrlr; 3711f9da54eSStephen Bates } 3721f9da54eSStephen Bates if (!strcmp(g_config.cmb.trid.traddr, g_config.write.trid.traddr)) { 3731f9da54eSStephen Bates g_config.cmb.ctrlr = g_config.write.ctrlr; 3741f9da54eSStephen Bates } 3751f9da54eSStephen Bates 3769f7cb6aaSChangpeng Liu if (!g_config.read.ctrlr || !g_config.write.ctrlr) { 3779f7cb6aaSChangpeng Liu fprintf(stderr, "No NVMe controller that support CMB was found!\n"); 3789f7cb6aaSChangpeng Liu return -1; 3799f7cb6aaSChangpeng Liu } 3809f7cb6aaSChangpeng Liu 3811f9da54eSStephen Bates /* 3821f9da54eSStephen Bates * Call the cmb_copy() function which performs the CMB 3831f9da54eSStephen Bates * based copy or returns an error code if it fails. 3841f9da54eSStephen Bates */ 3851f9da54eSStephen Bates rc = cmb_copy(); 3861f9da54eSStephen Bates if (rc) { 3871f9da54eSStephen Bates fprintf(stderr, "Error in spdk_cmb_copy(): %d\n", 3881f9da54eSStephen Bates rc); 3891f9da54eSStephen Bates return -1; 3901f9da54eSStephen Bates } 3911f9da54eSStephen Bates 3921f9da54eSStephen Bates return rc; 3931f9da54eSStephen Bates } 394