LCOV - code coverage report
Current view: top level - spdk/test/nvme/fused_ordering - fused_ordering.c (source / functions) Hit Total Coverage
Test: Combined Lines: 113 194 58.2 %
Date: 2024-07-15 12:30:10 Functions: 8 9 88.9 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 47 134 35.1 %

           Branch data     Line data    Source code
       1                 :            : /*   SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  *   Copyright (C) 2022 Intel Corporation.
       3                 :            :  *   All rights reserved.
       4                 :            :  */
       5                 :            : 
       6                 :            : #include "spdk/stdinc.h"
       7                 :            : 
       8                 :            : #include "spdk/nvme.h"
       9                 :            : #include "spdk/env.h"
      10                 :            : #include "spdk/string.h"
      11                 :            : #include "spdk/log.h"
      12                 :            : #include "spdk/util.h"
      13                 :            : #include "spdk/likely.h"
      14                 :            : 
      15                 :            : #define WRITE_BLOCKS 128
      16                 :            : #define FUSED_BLOCKS 1
      17                 :            : #define NO_WRITE_CMDS 8
      18                 :            : 
      19                 :            : struct worker_thread {
      20                 :            :         TAILQ_ENTRY(worker_thread) link;
      21                 :            :         unsigned lcore;
      22                 :            :         void *cw_buf;
      23                 :            :         void *large_buf;
      24                 :            :         struct spdk_nvme_qpair *qpair;
      25                 :            :         uint32_t poll_count;
      26                 :            :         uint32_t outstanding;
      27                 :            :         int status;
      28                 :            : };
      29                 :            : 
      30                 :            : static struct spdk_nvme_ctrlr *g_ctrlr;
      31                 :            : static struct spdk_nvme_ns *g_ns;
      32                 :            : static struct spdk_nvme_transport_id g_trid = {};
      33                 :            : static uint32_t g_num_workers = 0;
      34                 :            : static TAILQ_HEAD(, worker_thread) g_workers = TAILQ_HEAD_INITIALIZER(g_workers);
      35                 :            : 
      36                 :            : 
      37                 :            : static void
      38                 :      30720 : io_complete(void *arg, const struct spdk_nvme_cpl *cpl)
      39                 :            : {
      40                 :      30720 :         struct worker_thread *worker = arg;
      41                 :            : 
      42   [ +  -  -  + ]:      30720 :         if (spdk_nvme_cpl_is_error(cpl)) {
      43                 :          0 :                 spdk_nvme_print_completion(spdk_nvme_qpair_get_id(worker->qpair),
      44                 :            :                                            (struct spdk_nvme_cpl *)cpl);
      45                 :          0 :                 exit(1);
      46                 :            :         }
      47                 :            : 
      48                 :      30720 :         worker->outstanding--;
      49                 :      30720 : }
      50                 :            : 
      51                 :            : static int
      52                 :          3 : register_workers(void)
      53                 :            : {
      54                 :            :         uint32_t i;
      55                 :            :         struct worker_thread *worker;
      56                 :            : 
      57         [ +  + ]:          6 :         SPDK_ENV_FOREACH_CORE(i) {
      58                 :          3 :                 worker = calloc(1, sizeof(*worker));
      59         [ -  + ]:          3 :                 if (worker == NULL) {
      60   [ #  #  #  # ]:          0 :                         fprintf(stderr, "Unable to allocate worker\n");
      61                 :          0 :                         return -1;
      62                 :            :                 }
      63                 :            : 
      64                 :          3 :                 worker->lcore = i;
      65                 :          3 :                 TAILQ_INSERT_TAIL(&g_workers, worker, link);
      66                 :          3 :                 g_num_workers++;
      67                 :            :         }
      68                 :            : 
      69                 :          3 :         return 0;
      70                 :            : }
      71                 :            : 
      72                 :            : static void
      73                 :          3 : unregister_workers(void)
      74                 :            : {
      75                 :            :         struct worker_thread *worker, *tmp_worker;
      76                 :            : 
      77         [ +  + ]:          6 :         TAILQ_FOREACH_SAFE(worker, &g_workers, link, tmp_worker) {
      78         [ -  + ]:          3 :                 TAILQ_REMOVE(&g_workers, worker, link);
      79                 :          3 :                 free(worker);
      80                 :            :         }
      81                 :          3 : }
      82                 :            : 
      83                 :            : static unsigned
      84                 :          3 : init_workers(void)
      85                 :            : {
      86                 :          3 :         void *cw_buf = NULL, *large_buf = NULL;
      87                 :            :         struct worker_thread *worker;
      88                 :          3 :         int rc = 0;
      89                 :            : 
      90         [ -  + ]:          3 :         assert(g_num_workers);
      91                 :            : 
      92                 :          3 :         cw_buf = spdk_zmalloc(FUSED_BLOCKS * 4096, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY,
      93                 :            :                               SPDK_MALLOC_DMA);
      94         [ -  + ]:          3 :         if (cw_buf == NULL) {
      95         [ #  # ]:          0 :                 printf("ERROR: buffer allocation failed.\n");
      96                 :          0 :                 rc = -1;
      97                 :          0 :                 goto error;
      98                 :            :         }
      99                 :            : 
     100                 :          3 :         large_buf = spdk_zmalloc(WRITE_BLOCKS * 4096, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY,
     101                 :            :                                  SPDK_MALLOC_DMA);
     102         [ -  + ]:          3 :         if (large_buf == NULL) {
     103         [ #  # ]:          0 :                 printf("ERROR: buffer allocation failed.\n");
     104                 :          0 :                 rc = -1;
     105                 :          0 :                 goto error;
     106                 :            :         }
     107                 :            : 
     108         [ +  + ]:          6 :         TAILQ_FOREACH(worker, &g_workers, link) {
     109                 :          3 :                 worker->qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_ctrlr, NULL, 0);
     110         [ -  + ]:          3 :                 if (worker->qpair == NULL) {
     111         [ #  # ]:          0 :                         printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed.\n");
     112                 :          0 :                         rc = -1;
     113                 :          0 :                         goto error;
     114                 :            :                 }
     115                 :          3 :                 worker->cw_buf = cw_buf;
     116                 :          3 :                 worker->large_buf = large_buf;
     117                 :            :         }
     118                 :          3 :         goto exit;
     119                 :            : 
     120                 :          0 : error:
     121         [ #  # ]:          0 :         TAILQ_FOREACH(worker, &g_workers, link) {
     122                 :          0 :                 spdk_nvme_ctrlr_free_io_qpair(worker->qpair);
     123                 :            :         }
     124                 :          0 :         spdk_free(large_buf);
     125                 :          0 :         spdk_free(cw_buf);
     126                 :          3 : exit:
     127                 :          3 :         return rc;
     128                 :            : }
     129                 :            : 
     130                 :            : static void
     131                 :          3 : fini_workers(void)
     132                 :            : {
     133                 :          3 :         void *cw_buf = NULL, *large_buf = NULL;
     134                 :            :         struct worker_thread *worker;
     135                 :            : 
     136         [ +  + ]:          6 :         TAILQ_FOREACH(worker, &g_workers, link) {
     137                 :          3 :                 spdk_nvme_ctrlr_free_io_qpair(worker->qpair);
     138                 :          3 :                 cw_buf = worker->cw_buf;
     139                 :          3 :                 large_buf = worker->large_buf;
     140                 :            :         }
     141                 :            : 
     142                 :          3 :         spdk_free(large_buf);
     143                 :          3 :         spdk_free(cw_buf);
     144                 :          3 : }
     145                 :            : 
     146                 :            : static int
     147                 :       3072 : fused_ordering(void *arg)
     148                 :            : {
     149                 :       3072 :         struct worker_thread *worker = (struct worker_thread *)arg;
     150                 :            :         uint32_t i;
     151                 :       3072 :         uint32_t rc = 0;
     152                 :            : 
     153                 :            :         /* Issue relatively large writes - big enough that the data will not fit
     154                 :            :          * in-capsule - followed by the compare command. Then poll the completion queue a number of
     155                 :            :          * times matching the poll_count variable. This adds a variable amount of delay between
     156                 :            :          * the compare and the subsequent fused write submission.
     157                 :            :          *
     158                 :            :          * GitHub issue #2428 showed a problem where once the non-in-capsule data had been fetched from
     159                 :            :          * the host, that request could get sent to the target layer between the two fused commands. This
     160                 :            :          * variable delay would eventually induce this condition before the fix.
     161                 :            :          */
     162                 :            :         /* Submit 8 write commands per queue */
     163         [ +  + ]:      27648 :         for (i = 0; i < NO_WRITE_CMDS; i++) {
     164                 :      24576 :                 rc = spdk_nvme_ns_cmd_write(g_ns, worker->qpair, worker->large_buf,
     165                 :            :                                             0,
     166                 :            :                                             WRITE_BLOCKS, io_complete,
     167                 :            :                                             worker,
     168                 :            :                                             0);
     169         [ -  + ]:      24576 :                 if (rc != 0) {
     170   [ #  #  #  # ]:          0 :                         fprintf(stderr, "starting write I/O failed\n");
     171                 :          0 :                         goto out;
     172                 :            :                 }
     173                 :            : 
     174                 :      24576 :                 worker->outstanding++;
     175                 :            :         }
     176                 :            : 
     177                 :            :         /* Submit first fuse command, per queue */
     178                 :       3072 :         rc = spdk_nvme_ns_cmd_compare(g_ns, worker->qpair, worker->cw_buf,
     179                 :            :                                       0,
     180                 :            :                                       FUSED_BLOCKS, io_complete,
     181                 :            :                                       worker,
     182                 :            :                                       SPDK_NVME_IO_FLAGS_FUSE_FIRST);
     183         [ -  + ]:       3072 :         if (rc != 0) {
     184   [ #  #  #  # ]:          0 :                 fprintf(stderr, "starting compare I/O failed\n");
     185                 :          0 :                 goto out;
     186                 :            :         }
     187                 :            : 
     188                 :       3072 :         worker->outstanding++;
     189                 :            : 
     190                 :            :         /* Process completions */
     191         [ +  + ]:    1574400 :         while (worker->poll_count-- > 0) {
     192                 :    1571328 :                 spdk_nvme_qpair_process_completions(worker->qpair, 0);
     193                 :            :         }
     194                 :            : 
     195                 :            :         /* Submit second fuse command, one per queue */
     196                 :       3072 :         rc = spdk_nvme_ns_cmd_write(g_ns, worker->qpair, worker->cw_buf, 0,
     197                 :            :                                     FUSED_BLOCKS, io_complete,
     198                 :            :                                     worker,
     199                 :            :                                     SPDK_NVME_IO_FLAGS_FUSE_SECOND);
     200         [ -  + ]:       3072 :         if (rc != 0) {
     201   [ #  #  #  # ]:          0 :                 fprintf(stderr, "starting write I/O failed\n");
     202                 :          0 :                 goto out;
     203                 :            :         }
     204                 :            : 
     205                 :       3072 :         worker->outstanding++;
     206                 :            : 
     207                 :            :         /* Process completions */
     208         [ +  + ]:     353566 :         while (worker->outstanding > 0) {
     209                 :     350494 :                 spdk_nvme_qpair_process_completions(worker->qpair, 0);
     210                 :            :         }
     211                 :            : 
     212                 :       3072 : out:
     213                 :       3072 :         worker->status = rc;
     214                 :       3072 :         return rc;
     215                 :            : }
     216                 :            : 
     217                 :            : static void
     218                 :          0 : usage(const char *program_name)
     219                 :            : {
     220         [ #  # ]:          0 :         printf("%s [options]", program_name);
     221         [ #  # ]:          0 :         printf("\t\n");
     222         [ #  # ]:          0 :         printf("options:\n");
     223         [ #  # ]:          0 :         printf("\t[-r remote NVMe over Fabrics target address]\n");
     224                 :            : #ifdef DEBUG
     225         [ #  # ]:          0 :         printf("\t[-L enable debug logging]\n");
     226                 :            : #else
     227                 :            :         printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n");
     228                 :            :         printf("\t[-c core mask]\n");
     229                 :            : #endif
     230         [ #  # ]:          0 :         printf("\t[-s memory size in MB for DPDK (default: 0MB)]\n");
     231         [ #  # ]:          0 :         printf("\t[--no-huge SPDK is run without hugepages]\n");
     232                 :          0 : }
     233                 :            : 
     234                 :            : #define FUSED_GETOPT_STRING "r:L:q:c:s:"
     235                 :            : static const struct option g_fused_cmdline_opts[] = {
     236                 :            : #define FUSED_NO_HUGE        257
     237                 :            :         {"no-huge", no_argument, NULL, FUSED_NO_HUGE},
     238                 :            :         {0, 0, 0, 0}
     239                 :            : };
     240                 :            : 
     241                 :            : static int
     242                 :          3 : parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
     243                 :            : {
     244                 :          0 :         int op, rc, opt_index;
     245                 :            :         long int value;
     246                 :            : 
     247   [ -  +  -  + ]:          6 :         while ((op = getopt_long(argc, argv, FUSED_GETOPT_STRING, g_fused_cmdline_opts,
     248         [ +  + ]:          6 :                                  &opt_index)) != -1) {
     249   [ +  -  -  -  :          3 :                 switch (op) {
                   -  - ]
     250                 :          3 :                 case 'r':
     251         [ -  + ]:          3 :                         if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) {
     252   [ #  #  #  # ]:          0 :                                 fprintf(stderr, "Error parsing transport address\n");
     253                 :          0 :                                 return 1;
     254                 :            :                         }
     255                 :          3 :                         break;
     256                 :          0 :                 case 'L':
     257                 :          0 :                         rc = spdk_log_set_flag(optarg);
     258         [ #  # ]:          0 :                         if (rc < 0) {
     259   [ #  #  #  # ]:          0 :                                 fprintf(stderr, "unknown flag\n");
     260                 :          0 :                                 usage(argv[0]);
     261                 :          0 :                                 exit(EXIT_FAILURE);
     262                 :            :                         }
     263                 :            : #ifdef DEBUG
     264                 :          0 :                         spdk_log_set_print_level(SPDK_LOG_DEBUG);
     265                 :            : #endif
     266                 :          0 :                         break;
     267                 :          0 :                 case 'c':
     268                 :          0 :                         env_opts->core_mask = optarg;
     269                 :          0 :                         break;
     270                 :          0 :                 case 's':
     271                 :          0 :                         value = spdk_strtol(optarg, 10);
     272         [ #  # ]:          0 :                         if (value < 0) {
     273   [ #  #  #  # ]:          0 :                                 fprintf(stderr, "converting a string to integer failed\n");
     274                 :          0 :                                 return -EINVAL;
     275                 :            :                         }
     276                 :          0 :                         env_opts->mem_size = value;
     277                 :          0 :                         break;
     278                 :          0 :                 case FUSED_NO_HUGE:
     279                 :          0 :                         env_opts->no_huge = true;
     280                 :          0 :                         break;
     281                 :          0 :                 default:
     282                 :          0 :                         usage(argv[0]);
     283                 :          0 :                         return 1;
     284                 :            :                 }
     285                 :            :         }
     286                 :            : 
     287                 :          3 :         return 0;
     288                 :            : }
     289                 :            : 
     290                 :            : int
     291                 :          3 : main(int argc, char **argv)
     292                 :            : {
     293                 :            :         int rc, i;
     294                 :          0 :         struct spdk_env_opts opts;
     295                 :          0 :         struct spdk_nvme_ctrlr_opts ctrlr_opts;
     296                 :            :         int nsid;
     297                 :            :         const struct spdk_nvme_ctrlr_opts *ctrlr_opts_actual;
     298                 :            :         uint32_t ctrlr_io_queues;
     299                 :            :         uint32_t main_core;
     300                 :          3 :         struct worker_thread *main_worker = NULL, *worker = NULL;
     301                 :            : 
     302                 :          3 :         spdk_env_opts_init(&opts);
     303                 :          3 :         spdk_log_set_print_level(SPDK_LOG_NOTICE);
     304                 :          3 :         rc = parse_args(argc, argv, &opts);
     305         [ -  + ]:          3 :         if (rc != 0) {
     306                 :          0 :                 return rc;
     307                 :            :         }
     308                 :            : 
     309                 :          3 :         opts.name = "fused_ordering";
     310         [ -  + ]:          3 :         if (spdk_env_init(&opts) < 0) {
     311         [ #  # ]:          0 :                 fprintf(stderr, "Unable to initialize SPDK env\n");
     312                 :          0 :                 return 1;
     313                 :            :         }
     314                 :            : 
     315         [ -  + ]:          3 :         if (register_workers() != 0) {
     316                 :          0 :                 rc = -1;
     317                 :          0 :                 goto exit;
     318                 :            :         }
     319                 :            : 
     320                 :          3 :         spdk_nvme_ctrlr_get_default_ctrlr_opts(&ctrlr_opts, sizeof(ctrlr_opts));
     321                 :          3 :         ctrlr_opts.keep_alive_timeout_ms = 60 * 1000;
     322                 :          3 :         g_ctrlr = spdk_nvme_connect(&g_trid, &ctrlr_opts, sizeof(ctrlr_opts));
     323         [ -  + ]:          3 :         if (g_ctrlr == NULL) {
     324         [ #  # ]:          0 :                 fprintf(stderr, "spdk_nvme_connect() failed\n");
     325                 :          0 :                 rc = 1;
     326                 :          0 :                 goto exit;
     327                 :            :         }
     328                 :            : 
     329                 :          3 :         printf("Attached to %s\n", g_trid.subnqn);
     330                 :            : 
     331                 :          3 :         nsid = spdk_nvme_ctrlr_get_first_active_ns(g_ctrlr);
     332         [ -  + ]:          3 :         if (nsid == 0) {
     333                 :          0 :                 perror("No active namespaces");
     334                 :          0 :                 exit(1);
     335                 :            :         }
     336                 :          3 :         g_ns = spdk_nvme_ctrlr_get_ns(g_ctrlr, nsid);
     337                 :            : 
     338                 :          3 :         printf("  Namespace ID: %d size: %juGB\n", spdk_nvme_ns_get_id(g_ns),
     339                 :          3 :                spdk_nvme_ns_get_size(g_ns) / 1000000000);
     340                 :            : 
     341                 :          3 :         ctrlr_opts_actual = spdk_nvme_ctrlr_get_opts(g_ctrlr);
     342                 :          3 :         ctrlr_io_queues = ctrlr_opts_actual->num_io_queues;
     343                 :            : 
     344                 :            :         /* One qpair per core */
     345         [ -  + ]:          3 :         if (g_num_workers > ctrlr_io_queues) {
     346                 :          0 :                 printf("ERROR: Number of IO queues requested %d more then ctrlr caps %d.\n", g_num_workers,
     347                 :            :                        ctrlr_io_queues);
     348                 :          0 :                 rc = -1;
     349                 :          0 :                 goto exit;
     350                 :            :         }
     351                 :            : 
     352                 :          3 :         rc = init_workers();
     353         [ -  + ]:          3 :         if (rc) {
     354                 :          0 :                 printf("ERROR: Workers initialization failed.\n");
     355                 :          0 :                 goto exit;
     356                 :            :         }
     357                 :            : 
     358         [ +  + ]:       3075 :         for (i = 0; i < 1024; i++) {
     359                 :       3072 :                 printf("fused_ordering(%d)\n", i);
     360                 :       3072 :                 main_core = spdk_env_get_current_core();
     361         [ +  + ]:       6144 :                 TAILQ_FOREACH(worker, &g_workers, link) {
     362                 :       3072 :                         worker->poll_count = i;
     363         [ -  + ]:       3072 :                         if (worker->lcore != main_core) {
     364                 :          0 :                                 spdk_env_thread_launch_pinned(worker->lcore, fused_ordering, worker);
     365                 :            :                         } else {
     366                 :       3072 :                                 main_worker = worker;
     367                 :            :                         }
     368                 :            :                 }
     369                 :            : 
     370         [ +  - ]:       3072 :                 if (main_worker != NULL) {
     371                 :       3072 :                         fused_ordering(main_worker);
     372                 :            :                 }
     373                 :            : 
     374                 :       3072 :                 spdk_env_thread_wait_all();
     375                 :            : 
     376         [ +  + ]:       6144 :                 TAILQ_FOREACH(worker, &g_workers, link) {
     377         [ -  + ]:       3072 :                         if (spdk_unlikely(worker->status != 0)) {
     378                 :          0 :                                 SPDK_ERRLOG("Iteration of fused ordering(%d) failed.\n", i - 1);
     379                 :          0 :                                 rc = -1;
     380                 :          0 :                                 goto exit;
     381                 :            :                         }
     382                 :            :                 }
     383                 :            :         }
     384                 :            : 
     385                 :          3 : exit:
     386                 :          3 :         fini_workers();
     387                 :          3 :         unregister_workers();
     388                 :          3 :         spdk_nvme_detach(g_ctrlr);
     389                 :          3 :         spdk_env_fini();
     390                 :          3 :         return rc;
     391                 :            : }

Generated by: LCOV version 1.14