LCOV - code coverage report
Current view: top level - spdk/examples/nvme/cmb_copy - cmb_copy.c (source / functions) Hit Total Coverage
Test: Combined Lines: 125 182 68.7 %
Date: 2024-07-13 22:49:35 Functions: 9 10 90.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 66 162 40.7 %

           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         [ +  + ]:        413 :         while (!g_config.read.done) {
     118                 :        411 :                 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         [ +  + ]:        530 :         while (!g_config.write.done) {
     131                 :        528 :                 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                 :            : }

Generated by: LCOV version 1.14