LCOV - code coverage report
Current view: top level - spdk/examples/nvme/arbitration - arbitration.c (source / functions) Hit Total Coverage
Test: Combined Lines: 291 573 50.8 %
Date: 2024-07-13 06:45:54 Functions: 24 34 70.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 181 531 34.1 %

           Branch data     Line data    Source code
       1                 :            : /*   SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  *   Copyright (C) 2016 Intel Corporation.
       3                 :            :  *   All rights reserved.
       4                 :            :  */
       5                 :            : 
       6                 :            : #include "spdk/stdinc.h"
       7                 :            : 
       8                 :            : #include "spdk/log.h"
       9                 :            : #include "spdk/nvme.h"
      10                 :            : #include "spdk/env.h"
      11                 :            : #include "spdk/string.h"
      12                 :            : #include "spdk/nvme_intel.h"
      13                 :            : #include "spdk/string.h"
      14                 :            : 
      15                 :            : struct ctrlr_entry {
      16                 :            :         struct spdk_nvme_ctrlr                  *ctrlr;
      17                 :            :         struct spdk_nvme_intel_rw_latency_page  latency_page;
      18                 :            :         TAILQ_ENTRY(ctrlr_entry)                link;
      19                 :            :         char                                    name[1024];
      20                 :            : };
      21                 :            : 
      22                 :            : struct ns_entry {
      23                 :            :         struct {
      24                 :            :                 struct spdk_nvme_ctrlr          *ctrlr;
      25                 :            :                 struct spdk_nvme_ns             *ns;
      26                 :            :         } nvme;
      27                 :            : 
      28                 :            :         TAILQ_ENTRY(ns_entry)                   link;
      29                 :            :         uint32_t                                io_size_blocks;
      30                 :            :         uint64_t                                size_in_ios;
      31                 :            :         char                                    name[1024];
      32                 :            : };
      33                 :            : 
      34                 :            : struct ns_worker_ctx {
      35                 :            :         struct ns_entry                         *entry;
      36                 :            :         uint64_t                                io_completed;
      37                 :            :         uint64_t                                current_queue_depth;
      38                 :            :         uint64_t                                offset_in_ios;
      39                 :            :         bool                                    is_draining;
      40                 :            :         struct spdk_nvme_qpair                  *qpair;
      41                 :            :         TAILQ_ENTRY(ns_worker_ctx)              link;
      42                 :            : };
      43                 :            : 
      44                 :            : struct arb_task {
      45                 :            :         struct ns_worker_ctx                    *ns_ctx;
      46                 :            :         void                                    *buf;
      47                 :            : };
      48                 :            : 
      49                 :            : struct worker_thread {
      50                 :            :         TAILQ_HEAD(, ns_worker_ctx)             ns_ctx;
      51                 :            :         TAILQ_ENTRY(worker_thread)              link;
      52                 :            :         unsigned                                lcore;
      53                 :            :         enum spdk_nvme_qprio                    qprio;
      54                 :            : };
      55                 :            : 
      56                 :            : struct arb_context {
      57                 :            :         int                                     shm_id;
      58                 :            :         int                                     outstanding_commands;
      59                 :            :         int                                     num_namespaces;
      60                 :            :         int                                     num_workers;
      61                 :            :         int                                     rw_percentage;
      62                 :            :         int                                     is_random;
      63                 :            :         int                                     queue_depth;
      64                 :            :         int                                     time_in_sec;
      65                 :            :         int                                     io_count;
      66                 :            :         uint8_t                                 latency_tracking_enable;
      67                 :            :         uint8_t                                 arbitration_mechanism;
      68                 :            :         uint8_t                                 arbitration_config;
      69                 :            :         uint32_t                                io_size_bytes;
      70                 :            :         uint32_t                                max_completions;
      71                 :            :         uint64_t                                tsc_rate;
      72                 :            :         const char                              *core_mask;
      73                 :            :         const char                              *workload_type;
      74                 :            : };
      75                 :            : 
      76                 :            : struct feature {
      77                 :            :         uint32_t                                result;
      78                 :            :         bool                                    valid;
      79                 :            : };
      80                 :            : 
      81                 :            : static struct spdk_mempool *task_pool           = NULL;
      82                 :            : 
      83                 :            : static TAILQ_HEAD(, ctrlr_entry) g_controllers  = TAILQ_HEAD_INITIALIZER(g_controllers);
      84                 :            : static TAILQ_HEAD(, ns_entry) g_namespaces      = TAILQ_HEAD_INITIALIZER(g_namespaces);
      85                 :            : static TAILQ_HEAD(, worker_thread) g_workers    = TAILQ_HEAD_INITIALIZER(g_workers);
      86                 :            : 
      87                 :            : static struct feature features[SPDK_NVME_FEAT_ARBITRATION + 1] = {};
      88                 :            : static struct spdk_nvme_transport_id g_trid = {};
      89                 :            : 
      90                 :            : static struct arb_context g_arbitration = {
      91                 :            :         .shm_id                                 = -1,
      92                 :            :         .outstanding_commands                   = 0,
      93                 :            :         .num_workers                            = 0,
      94                 :            :         .num_namespaces                         = 0,
      95                 :            :         .rw_percentage                          = 50,
      96                 :            :         .queue_depth                            = 64,
      97                 :            :         .time_in_sec                            = 60,
      98                 :            :         .io_count                               = 100000,
      99                 :            :         .latency_tracking_enable                = 0,
     100                 :            :         .arbitration_mechanism                  = SPDK_NVME_CC_AMS_RR,
     101                 :            :         .arbitration_config                     = 0,
     102                 :            :         .io_size_bytes                          = 131072,
     103                 :            :         .max_completions                        = 0,
     104                 :            :         /* Default 4 cores for urgent/high/medium/low */
     105                 :            :         .core_mask                              = "0xf",
     106                 :            :         .workload_type                          = "randrw",
     107                 :            : };
     108                 :            : 
     109                 :            : static int g_dpdk_mem = 0;
     110                 :            : static bool g_dpdk_mem_single_seg = false;
     111                 :            : 
     112                 :            : /*
     113                 :            :  * For weighted round robin arbitration mechanism, the smaller value between
     114                 :            :  * weight and burst will be picked to execute the commands in one queue.
     115                 :            :  */
     116                 :            : #define USER_SPECIFIED_HIGH_PRIORITY_WEIGHT     32
     117                 :            : #define USER_SPECIFIED_MEDIUM_PRIORITY_WEIGHT   16
     118                 :            : #define USER_SPECIFIED_LOW_PRIORITY_WEIGHT      8
     119                 :            : 
     120                 :            : static void task_complete(struct arb_task *task);
     121                 :            : 
     122                 :            : static void io_complete(void *ctx, const struct spdk_nvme_cpl *completion);
     123                 :            : 
     124                 :            : static void get_arb_feature(struct spdk_nvme_ctrlr *ctrlr);
     125                 :            : 
     126                 :            : static int set_arb_feature(struct spdk_nvme_ctrlr *ctrlr);
     127                 :            : 
     128                 :            : static const char *print_qprio(enum spdk_nvme_qprio);
     129                 :            : 
     130                 :            : 
     131                 :            : static void
     132                 :         14 : register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
     133                 :            : {
     134                 :            :         struct ns_entry *entry;
     135                 :            :         const struct spdk_nvme_ctrlr_data *cdata;
     136                 :            : 
     137                 :         14 :         cdata = spdk_nvme_ctrlr_get_data(ctrlr);
     138                 :            : 
     139         [ +  - ]:         14 :         if (spdk_nvme_ns_get_size(ns) < g_arbitration.io_size_bytes ||
     140         [ +  - ]:         14 :             spdk_nvme_ns_get_extended_sector_size(ns) > g_arbitration.io_size_bytes ||
     141   [ -  +  -  + ]:         14 :             g_arbitration.io_size_bytes % spdk_nvme_ns_get_extended_sector_size(ns)) {
     142                 :          0 :                 printf("WARNING: controller %-20.20s (%-20.20s) ns %u has invalid "
     143                 :            :                        "ns size %" PRIu64 " / block size %u for I/O size %u\n",
     144         [ #  # ]:          0 :                        cdata->mn, cdata->sn, spdk_nvme_ns_get_id(ns),
     145                 :            :                        spdk_nvme_ns_get_size(ns), spdk_nvme_ns_get_extended_sector_size(ns),
     146                 :            :                        g_arbitration.io_size_bytes);
     147                 :          0 :                 return;
     148                 :            :         }
     149                 :            : 
     150                 :         14 :         entry = malloc(sizeof(struct ns_entry));
     151         [ -  + ]:         14 :         if (entry == NULL) {
     152                 :          0 :                 perror("ns_entry malloc");
     153                 :          0 :                 exit(1);
     154                 :            :         }
     155                 :            : 
     156                 :         14 :         entry->nvme.ctrlr = ctrlr;
     157                 :         14 :         entry->nvme.ns = ns;
     158                 :            : 
     159         [ -  + ]:         14 :         entry->size_in_ios = spdk_nvme_ns_get_size(ns) / g_arbitration.io_size_bytes;
     160         [ -  + ]:         14 :         entry->io_size_blocks = g_arbitration.io_size_bytes / spdk_nvme_ns_get_sector_size(ns);
     161                 :            : 
     162         [ -  + ]:         14 :         snprintf(entry->name, 44, "%-20.20s (%-20.20s)", cdata->mn, cdata->sn);
     163                 :            : 
     164                 :         14 :         g_arbitration.num_namespaces++;
     165                 :         14 :         TAILQ_INSERT_TAIL(&g_namespaces, entry, link);
     166                 :            : }
     167                 :            : 
     168                 :            : static void
     169                 :          0 : enable_latency_tracking_complete(void *cb_arg, const struct spdk_nvme_cpl *cpl)
     170                 :            : {
     171   [ #  #  #  # ]:          0 :         if (spdk_nvme_cpl_is_error(cpl)) {
     172         [ #  # ]:          0 :                 printf("enable_latency_tracking_complete failed\n");
     173                 :            :         }
     174                 :          0 :         g_arbitration.outstanding_commands--;
     175                 :          0 : }
     176                 :            : 
     177                 :            : static void
     178                 :          0 : set_latency_tracking_feature(struct spdk_nvme_ctrlr *ctrlr, bool enable)
     179                 :            : {
     180                 :            :         int res;
     181                 :            :         union spdk_nvme_intel_feat_latency_tracking latency_tracking;
     182                 :            : 
     183         [ #  # ]:          0 :         if (enable) {
     184                 :          0 :                 latency_tracking.bits.enable = 0x01;
     185                 :            :         } else {
     186                 :          0 :                 latency_tracking.bits.enable = 0x00;
     187                 :            :         }
     188                 :            : 
     189                 :          0 :         res = spdk_nvme_ctrlr_cmd_set_feature(ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING,
     190                 :            :                                               latency_tracking.raw, 0, NULL, 0, enable_latency_tracking_complete, NULL);
     191         [ #  # ]:          0 :         if (res) {
     192         [ #  # ]:          0 :                 printf("fail to allocate nvme request.\n");
     193                 :          0 :                 return;
     194                 :            :         }
     195                 :          0 :         g_arbitration.outstanding_commands++;
     196                 :            : 
     197         [ #  # ]:          0 :         while (g_arbitration.outstanding_commands) {
     198                 :          0 :                 spdk_nvme_ctrlr_process_admin_completions(ctrlr);
     199                 :            :         }
     200                 :            : }
     201                 :            : 
     202                 :            : static void
     203                 :         12 : register_ctrlr(struct spdk_nvme_ctrlr *ctrlr)
     204                 :            : {
     205                 :            :         uint32_t nsid;
     206                 :            :         struct spdk_nvme_ns *ns;
     207                 :         12 :         struct ctrlr_entry *entry = calloc(1, sizeof(struct ctrlr_entry));
     208                 :         12 :         union spdk_nvme_cap_register cap = spdk_nvme_ctrlr_get_regs_cap(ctrlr);
     209                 :         12 :         const struct spdk_nvme_ctrlr_data *cdata = spdk_nvme_ctrlr_get_data(ctrlr);
     210                 :            : 
     211         [ -  + ]:         12 :         if (entry == NULL) {
     212                 :          0 :                 perror("ctrlr_entry malloc");
     213                 :          0 :                 exit(1);
     214                 :            :         }
     215                 :            : 
     216         [ -  + ]:         12 :         snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn);
     217                 :            : 
     218                 :         12 :         entry->ctrlr = ctrlr;
     219                 :         12 :         TAILQ_INSERT_TAIL(&g_controllers, entry, link);
     220                 :            : 
     221   [ -  +  -  - ]:         12 :         if ((g_arbitration.latency_tracking_enable != 0) &&
     222                 :          0 :             spdk_nvme_ctrlr_is_feature_supported(ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING)) {
     223                 :          0 :                 set_latency_tracking_feature(ctrlr, true);
     224                 :            :         }
     225                 :            : 
     226         [ +  + ]:         26 :         for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr); nsid != 0;
     227                 :         14 :              nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) {
     228                 :         14 :                 ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
     229         [ -  + ]:         14 :                 if (ns == NULL) {
     230                 :          0 :                         continue;
     231                 :            :                 }
     232                 :         14 :                 register_ns(ctrlr, ns);
     233                 :            :         }
     234                 :            : 
     235         [ -  + ]:         12 :         if (g_arbitration.arbitration_mechanism == SPDK_NVME_CAP_AMS_WRR &&
     236         [ #  # ]:          0 :             (cap.bits.ams & SPDK_NVME_CAP_AMS_WRR)) {
     237                 :          0 :                 get_arb_feature(ctrlr);
     238                 :            : 
     239         [ #  # ]:          0 :                 if (g_arbitration.arbitration_config != 0) {
     240                 :          0 :                         set_arb_feature(ctrlr);
     241                 :          0 :                         get_arb_feature(ctrlr);
     242                 :            :                 }
     243                 :            :         }
     244                 :         12 : }
     245                 :            : 
     246                 :            : static __thread unsigned int seed = 0;
     247                 :            : 
     248                 :            : static void
     249                 :     420591 : submit_single_io(struct ns_worker_ctx *ns_ctx)
     250                 :            : {
     251                 :     420591 :         struct arb_task         *task = NULL;
     252                 :            :         uint64_t                offset_in_ios;
     253                 :            :         int                     rc;
     254                 :     420591 :         struct ns_entry         *entry = ns_ctx->entry;
     255                 :            : 
     256                 :     420591 :         task = spdk_mempool_get(task_pool);
     257         [ -  + ]:     420591 :         if (!task) {
     258   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Failed to get task from task_pool\n");
     259                 :          0 :                 exit(1);
     260                 :            :         }
     261                 :            : 
     262                 :     420591 :         task->buf = spdk_dma_zmalloc(g_arbitration.io_size_bytes, 0x200, NULL);
     263         [ -  + ]:     420591 :         if (!task->buf) {
     264                 :          0 :                 spdk_mempool_put(task_pool, task);
     265   [ #  #  #  # ]:          0 :                 fprintf(stderr, "task->buf spdk_dma_zmalloc failed\n");
     266                 :          0 :                 exit(1);
     267                 :            :         }
     268                 :            : 
     269                 :     420591 :         task->ns_ctx = ns_ctx;
     270                 :            : 
     271         [ +  - ]:     420591 :         if (g_arbitration.is_random) {
     272         [ -  + ]:     420591 :                 offset_in_ios = rand_r(&seed) % entry->size_in_ios;
     273                 :            :         } else {
     274                 :          0 :                 offset_in_ios = ns_ctx->offset_in_ios++;
     275         [ #  # ]:          0 :                 if (ns_ctx->offset_in_ios == entry->size_in_ios) {
     276                 :          0 :                         ns_ctx->offset_in_ios = 0;
     277                 :            :                 }
     278                 :            :         }
     279                 :            : 
     280         [ +  - ]:     420591 :         if ((g_arbitration.rw_percentage == 100) ||
     281         [ +  - ]:     420591 :             (g_arbitration.rw_percentage != 0 &&
     282         [ +  + ]:     420591 :              ((rand_r(&seed) % 100) < g_arbitration.rw_percentage))) {
     283                 :     345180 :                 rc = spdk_nvme_ns_cmd_read(entry->nvme.ns, ns_ctx->qpair, task->buf,
     284                 :     211536 :                                            offset_in_ios * entry->io_size_blocks,
     285                 :            :                                            entry->io_size_blocks, io_complete, task, 0);
     286                 :            :         } else {
     287                 :     341024 :                 rc = spdk_nvme_ns_cmd_write(entry->nvme.ns, ns_ctx->qpair, task->buf,
     288                 :     209055 :                                             offset_in_ios * entry->io_size_blocks,
     289                 :            :                                             entry->io_size_blocks, io_complete, task, 0);
     290                 :            :         }
     291                 :            : 
     292         [ -  + ]:     420591 :         if (rc != 0) {
     293   [ #  #  #  # ]:          0 :                 fprintf(stderr, "starting I/O failed\n");
     294                 :            :         } else {
     295                 :     420591 :                 ns_ctx->current_queue_depth++;
     296                 :            :         }
     297                 :     420591 : }
     298                 :            : 
     299                 :            : static void
     300                 :     420591 : task_complete(struct arb_task *task)
     301                 :            : {
     302                 :            :         struct ns_worker_ctx    *ns_ctx;
     303                 :            : 
     304                 :     420591 :         ns_ctx = task->ns_ctx;
     305                 :     420591 :         ns_ctx->current_queue_depth--;
     306                 :     420591 :         ns_ctx->io_completed++;
     307                 :            : 
     308                 :     420591 :         spdk_dma_free(task->buf);
     309                 :     420591 :         spdk_mempool_put(task_pool, task);
     310                 :            : 
     311                 :            :         /*
     312                 :            :          * is_draining indicates when time has expired for the test run
     313                 :            :          * and we are just waiting for the previously submitted I/O
     314                 :            :          * to complete.  In this case, do not submit a new I/O to replace
     315                 :            :          * the one just completed.
     316                 :            :          */
     317   [ +  +  +  + ]:     420591 :         if (!ns_ctx->is_draining) {
     318                 :     418415 :                 submit_single_io(ns_ctx);
     319                 :            :         }
     320                 :     420591 : }
     321                 :            : 
     322                 :            : static void
     323                 :     420591 : io_complete(void *ctx, const struct spdk_nvme_cpl *completion)
     324                 :            : {
     325                 :     420591 :         task_complete((struct arb_task *)ctx);
     326                 :     420591 : }
     327                 :            : 
     328                 :            : static void
     329                 :    4788709 : check_io(struct ns_worker_ctx *ns_ctx)
     330                 :            : {
     331                 :    4788709 :         spdk_nvme_qpair_process_completions(ns_ctx->qpair, g_arbitration.max_completions);
     332                 :    4788709 : }
     333                 :            : 
     334                 :            : static void
     335                 :         34 : submit_io(struct ns_worker_ctx *ns_ctx, int queue_depth)
     336                 :            : {
     337         [ +  + ]:       2210 :         while (queue_depth-- > 0) {
     338                 :       2176 :                 submit_single_io(ns_ctx);
     339                 :            :         }
     340                 :         34 : }
     341                 :            : 
     342                 :            : static void
     343                 :         34 : drain_io(struct ns_worker_ctx *ns_ctx)
     344                 :            : {
     345                 :         34 :         ns_ctx->is_draining = true;
     346         [ +  + ]:      13363 :         while (ns_ctx->current_queue_depth > 0) {
     347                 :      13329 :                 check_io(ns_ctx);
     348                 :            :         }
     349                 :         34 : }
     350                 :            : 
     351                 :            : static int
     352                 :         34 : init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx, enum spdk_nvme_qprio qprio)
     353                 :            : {
     354                 :         34 :         struct spdk_nvme_ctrlr *ctrlr = ns_ctx->entry->nvme.ctrlr;
     355                 :         14 :         struct spdk_nvme_io_qpair_opts opts;
     356                 :            : 
     357                 :         34 :         spdk_nvme_ctrlr_get_default_io_qpair_opts(ctrlr, &opts, sizeof(opts));
     358                 :         34 :         opts.qprio = qprio;
     359                 :            : 
     360                 :         34 :         ns_ctx->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, &opts, sizeof(opts));
     361         [ -  + ]:         34 :         if (!ns_ctx->qpair) {
     362         [ #  # ]:          0 :                 printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair failed\n");
     363                 :          0 :                 return 1;
     364                 :            :         }
     365                 :            : 
     366                 :         34 :         return 0;
     367                 :            : }
     368                 :            : 
     369                 :            : static void
     370                 :         34 : cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
     371                 :            : {
     372                 :         34 :         spdk_nvme_ctrlr_free_io_qpair(ns_ctx->qpair);
     373                 :         34 : }
     374                 :            : 
     375                 :            : static void
     376                 :          8 : cleanup(uint32_t task_count)
     377                 :            : {
     378                 :            :         struct ns_entry *entry, *tmp_entry;
     379                 :            :         struct worker_thread *worker, *tmp_worker;
     380                 :            :         struct ns_worker_ctx *ns_ctx, *tmp_ns_ctx;
     381                 :            : 
     382         [ +  + ]:         22 :         TAILQ_FOREACH_SAFE(entry, &g_namespaces, link, tmp_entry) {
     383         [ +  + ]:         14 :                 TAILQ_REMOVE(&g_namespaces, entry, link);
     384                 :         14 :                 free(entry);
     385                 :            :         };
     386                 :            : 
     387         [ +  + ]:         40 :         TAILQ_FOREACH_SAFE(worker, &g_workers, link, tmp_worker) {
     388         [ +  + ]:         32 :                 TAILQ_REMOVE(&g_workers, worker, link);
     389                 :            : 
     390                 :            :                 /* ns_worker_ctx is a list in the worker */
     391         [ +  + ]:         66 :                 TAILQ_FOREACH_SAFE(ns_ctx, &worker->ns_ctx, link, tmp_ns_ctx) {
     392         [ +  + ]:         34 :                         TAILQ_REMOVE(&worker->ns_ctx, ns_ctx, link);
     393                 :         34 :                         free(ns_ctx);
     394                 :            :                 }
     395                 :            : 
     396                 :         32 :                 free(worker);
     397                 :            :         };
     398                 :            : 
     399         [ -  + ]:          8 :         if (spdk_mempool_count(task_pool) != (size_t)task_count) {
     400   [ #  #  #  # ]:          0 :                 fprintf(stderr, "task_pool count is %zu but should be %u\n",
     401                 :            :                         spdk_mempool_count(task_pool), task_count);
     402                 :            :         }
     403                 :          8 :         spdk_mempool_free(task_pool);
     404                 :          8 : }
     405                 :            : 
     406                 :            : static int
     407                 :         32 : work_fn(void *arg)
     408                 :            : {
     409                 :            :         uint64_t tsc_end;
     410                 :         32 :         struct worker_thread *worker = (struct worker_thread *)arg;
     411                 :            :         struct ns_worker_ctx *ns_ctx;
     412                 :            : 
     413         [ -  + ]:         32 :         printf("Starting thread on core %u with %s\n", worker->lcore, print_qprio(worker->qprio));
     414                 :            : 
     415                 :            :         /* Allocate a queue pair for each namespace. */
     416         [ +  + ]:         66 :         TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
     417         [ -  + ]:         34 :                 if (init_ns_worker_ctx(ns_ctx, worker->qprio) != 0) {
     418         [ #  # ]:          0 :                         printf("ERROR: init_ns_worker_ctx() failed\n");
     419                 :          0 :                         return 1;
     420                 :            :                 }
     421                 :            :         }
     422                 :            : 
     423                 :         32 :         tsc_end = spdk_get_ticks() + g_arbitration.time_in_sec * g_arbitration.tsc_rate;
     424                 :            : 
     425                 :            :         /* Submit initial I/O for each namespace. */
     426         [ +  + ]:         66 :         TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
     427                 :         34 :                 submit_io(ns_ctx, g_arbitration.queue_depth);
     428                 :            :         }
     429                 :            : 
     430                 :            :         while (1) {
     431                 :            :                 /*
     432                 :            :                  * Check for completed I/O for each controller. A new
     433                 :            :                  * I/O will be submitted in the io_complete callback
     434                 :            :                  * to replace each I/O that is completed.
     435                 :            :                  */
     436         [ +  + ]:    9550736 :                 TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
     437                 :    4775380 :                         check_io(ns_ctx);
     438                 :            :                 }
     439                 :            : 
     440         [ +  + ]:    4775356 :                 if (spdk_get_ticks() > tsc_end) {
     441                 :         32 :                         break;
     442                 :            :                 }
     443                 :            :         }
     444                 :            : 
     445         [ +  + ]:         66 :         TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
     446                 :         34 :                 drain_io(ns_ctx);
     447                 :         34 :                 cleanup_ns_worker_ctx(ns_ctx);
     448                 :            :         }
     449                 :            : 
     450                 :         32 :         return 0;
     451                 :            : }
     452                 :            : 
     453                 :            : static void
     454                 :          0 : usage(char *program_name)
     455                 :            : {
     456         [ #  # ]:          0 :         printf("%s options", program_name);
     457         [ #  # ]:          0 :         printf("\t\n");
     458         [ #  # ]:          0 :         printf("\t[-d DPDK huge memory size in MB]\n");
     459         [ #  # ]:          0 :         printf("\t[-q io depth]\n");
     460         [ #  # ]:          0 :         printf("\t[-o io size in bytes]\n");
     461         [ #  # ]:          0 :         printf("\t[-w io pattern type, must be one of\n");
     462         [ #  # ]:          0 :         printf("\t\t(read, write, randread, randwrite, rw, randrw)]\n");
     463         [ #  # ]:          0 :         printf("\t[-M rwmixread (100 for reads, 0 for writes)]\n");
     464                 :            : #ifdef DEBUG
     465         [ #  # ]:          0 :         printf("\t[-L enable debug logging]\n");
     466                 :            : #else
     467                 :            :         printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n");
     468                 :            : #endif
     469                 :          0 :         spdk_log_usage(stdout, "\t\t-L");
     470         [ #  # ]:          0 :         printf("\t[-l enable latency tracking, default: disabled]\n");
     471         [ #  # ]:          0 :         printf("\t\t(0 - disabled; 1 - enabled)\n");
     472         [ #  # ]:          0 :         printf("\t[-t time in seconds]\n");
     473         [ #  # ]:          0 :         printf("\t[-c core mask for I/O submission/completion.]\n");
     474         [ #  # ]:          0 :         printf("\t\t(default: 0xf - 4 cores)]\n");
     475         [ #  # ]:          0 :         printf("\t[-m max completions per poll]\n");
     476         [ #  # ]:          0 :         printf("\t\t(default: 0 - unlimited)\n");
     477         [ #  # ]:          0 :         printf("\t[-a arbitration mechanism, must be one of below]\n");
     478         [ #  # ]:          0 :         printf("\t\t(0, 1, 2)]\n");
     479         [ #  # ]:          0 :         printf("\t\t(0: default round robin mechanism)]\n");
     480         [ #  # ]:          0 :         printf("\t\t(1: weighted round robin mechanism)]\n");
     481         [ #  # ]:          0 :         printf("\t\t(2: vendor specific mechanism)]\n");
     482         [ #  # ]:          0 :         printf("\t[-b enable arbitration user configuration, default: disabled]\n");
     483         [ #  # ]:          0 :         printf("\t\t(0 - disabled; 1 - enabled)\n");
     484         [ #  # ]:          0 :         printf("\t[-n subjected IOs for performance comparison]\n");
     485         [ #  # ]:          0 :         printf("\t[-i shared memory group ID]\n");
     486         [ #  # ]:          0 :         printf("\t[-r remote NVMe over Fabrics target address]\n");
     487         [ #  # ]:          0 :         printf("\t[-g use single file descriptor for DPDK memory segments]\n");
     488                 :          0 : }
     489                 :            : 
     490                 :            : static const char *
     491                 :         32 : print_qprio(enum spdk_nvme_qprio qprio)
     492                 :            : {
     493   [ +  -  -  -  :         32 :         switch (qprio) {
                      - ]
     494                 :         32 :         case SPDK_NVME_QPRIO_URGENT:
     495                 :         32 :                 return "urgent priority queue";
     496                 :          0 :         case SPDK_NVME_QPRIO_HIGH:
     497                 :          0 :                 return "high priority queue";
     498                 :          0 :         case SPDK_NVME_QPRIO_MEDIUM:
     499                 :          0 :                 return "medium priority queue";
     500                 :          0 :         case SPDK_NVME_QPRIO_LOW:
     501                 :          0 :                 return "low priority queue";
     502                 :          0 :         default:
     503                 :          0 :                 return "invalid priority queue";
     504                 :            :         }
     505                 :            : }
     506                 :            : 
     507                 :            : 
     508                 :            : static void
     509                 :          8 : print_configuration(char *program_name)
     510                 :            : {
     511         [ -  + ]:          8 :         printf("%s run with configuration:\n", program_name);
     512                 :          8 :         printf("%s -q %d -s %d -w %s -M %d -l %d -t %d -c %s -m %d -a %d -b %d -n %d -i %d\n",
     513                 :            :                program_name,
     514                 :            :                g_arbitration.queue_depth,
     515                 :            :                g_arbitration.io_size_bytes,
     516                 :            :                g_arbitration.workload_type,
     517                 :            :                g_arbitration.rw_percentage,
     518                 :          8 :                g_arbitration.latency_tracking_enable,
     519                 :            :                g_arbitration.time_in_sec,
     520                 :            :                g_arbitration.core_mask,
     521                 :            :                g_arbitration.max_completions,
     522                 :          8 :                g_arbitration.arbitration_mechanism,
     523         [ -  + ]:          8 :                g_arbitration.arbitration_config,
     524                 :            :                g_arbitration.io_count,
     525                 :            :                g_arbitration.shm_id);
     526                 :          8 : }
     527                 :            : 
     528                 :            : 
     529                 :            : static void
     530                 :          8 : print_performance(void)
     531                 :            : {
     532                 :            :         float io_per_second, sent_all_io_in_secs;
     533                 :            :         struct worker_thread    *worker;
     534                 :            :         struct ns_worker_ctx    *ns_ctx;
     535                 :            : 
     536         [ +  + ]:         40 :         TAILQ_FOREACH(worker, &g_workers, link) {
     537         [ +  + ]:         66 :                 TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
     538                 :         34 :                         io_per_second = (float)ns_ctx->io_completed / g_arbitration.time_in_sec;
     539                 :         34 :                         sent_all_io_in_secs = g_arbitration.io_count / io_per_second;
     540                 :         60 :                         printf("%-43.43s core %u: %8.2f IO/s %8.2f secs/%d ios\n",
     541         [ -  + ]:         34 :                                ns_ctx->entry->name, worker->lcore,
     542                 :            :                                io_per_second, sent_all_io_in_secs, g_arbitration.io_count);
     543                 :            :                 }
     544                 :            :         }
     545         [ -  + ]:          8 :         printf("========================================================\n");
     546                 :            : 
     547                 :          8 :         printf("\n");
     548                 :          8 : }
     549                 :            : 
     550                 :            : static void
     551                 :          0 : print_latency_page(struct ctrlr_entry *entry)
     552                 :            : {
     553                 :            :         int i;
     554                 :            : 
     555                 :          0 :         printf("\n");
     556         [ #  # ]:          0 :         printf("%s\n", entry->name);
     557         [ #  # ]:          0 :         printf("--------------------------------------------------------\n");
     558                 :            : 
     559         [ #  # ]:          0 :         for (i = 0; i < 32; i++) {
     560         [ #  # ]:          0 :                 if (entry->latency_page.buckets_32us[i])
     561         [ #  # ]:          0 :                         printf("Bucket %dus - %dus: %d\n", i * 32, (i + 1) * 32,
     562                 :          0 :                                entry->latency_page.buckets_32us[i]);
     563                 :            :         }
     564         [ #  # ]:          0 :         for (i = 0; i < 31; i++) {
     565         [ #  # ]:          0 :                 if (entry->latency_page.buckets_1ms[i])
     566         [ #  # ]:          0 :                         printf("Bucket %dms - %dms: %d\n", i + 1, i + 2,
     567                 :          0 :                                entry->latency_page.buckets_1ms[i]);
     568                 :            :         }
     569         [ #  # ]:          0 :         for (i = 0; i < 31; i++) {
     570         [ #  # ]:          0 :                 if (entry->latency_page.buckets_32ms[i])
     571         [ #  # ]:          0 :                         printf("Bucket %dms - %dms: %d\n", (i + 1) * 32, (i + 2) * 32,
     572                 :          0 :                                entry->latency_page.buckets_32ms[i]);
     573                 :            :         }
     574                 :          0 : }
     575                 :            : 
     576                 :            : static void
     577                 :          0 : print_latency_statistics(const char *op_name, enum spdk_nvme_intel_log_page log_page)
     578                 :            : {
     579                 :            :         struct ctrlr_entry      *ctrlr;
     580                 :            : 
     581         [ #  # ]:          0 :         printf("%s Latency Statistics:\n", op_name);
     582         [ #  # ]:          0 :         printf("========================================================\n");
     583         [ #  # ]:          0 :         TAILQ_FOREACH(ctrlr, &g_controllers, link) {
     584         [ #  # ]:          0 :                 if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr->ctrlr, log_page)) {
     585         [ #  # ]:          0 :                         if (spdk_nvme_ctrlr_cmd_get_log_page(
     586                 :            :                                     ctrlr->ctrlr, log_page,
     587                 :            :                                     SPDK_NVME_GLOBAL_NS_TAG,
     588                 :          0 :                                     &ctrlr->latency_page,
     589                 :            :                                     sizeof(struct spdk_nvme_intel_rw_latency_page),
     590                 :            :                                     0,
     591                 :            :                                     enable_latency_tracking_complete,
     592                 :            :                                     NULL)) {
     593         [ #  # ]:          0 :                                 printf("nvme_ctrlr_cmd_get_log_page() failed\n");
     594                 :          0 :                                 exit(1);
     595                 :            :                         }
     596                 :            : 
     597                 :          0 :                         g_arbitration.outstanding_commands++;
     598                 :            :                 } else {
     599                 :          0 :                         printf("Controller %s: %s latency statistics not supported\n",
     600         [ #  # ]:          0 :                                ctrlr->name, op_name);
     601                 :            :                 }
     602                 :            :         }
     603                 :            : 
     604         [ #  # ]:          0 :         while (g_arbitration.outstanding_commands) {
     605         [ #  # ]:          0 :                 TAILQ_FOREACH(ctrlr, &g_controllers, link) {
     606                 :          0 :                         spdk_nvme_ctrlr_process_admin_completions(ctrlr->ctrlr);
     607                 :            :                 }
     608                 :            :         }
     609                 :            : 
     610         [ #  # ]:          0 :         TAILQ_FOREACH(ctrlr, &g_controllers, link) {
     611         [ #  # ]:          0 :                 if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr->ctrlr, log_page)) {
     612                 :          0 :                         print_latency_page(ctrlr);
     613                 :            :                 }
     614                 :            :         }
     615                 :          0 :         printf("\n");
     616                 :          0 : }
     617                 :            : 
     618                 :            : static void
     619                 :          8 : print_stats(void)
     620                 :            : {
     621                 :          8 :         print_performance();
     622         [ -  + ]:          8 :         if (g_arbitration.latency_tracking_enable) {
     623         [ #  # ]:          0 :                 if (g_arbitration.rw_percentage != 0) {
     624                 :          0 :                         print_latency_statistics("Read", SPDK_NVME_INTEL_LOG_READ_CMD_LATENCY);
     625                 :            :                 }
     626         [ #  # ]:          0 :                 if (g_arbitration.rw_percentage != 100) {
     627                 :          0 :                         print_latency_statistics("Write", SPDK_NVME_INTEL_LOG_WRITE_CMD_LATENCY);
     628                 :            :                 }
     629                 :            :         }
     630                 :          8 : }
     631                 :            : 
     632                 :            : static int
     633                 :          8 : parse_args(int argc, char **argv)
     634                 :            : {
     635                 :          8 :         const char *workload_type       = NULL;
     636                 :          8 :         int op                          = 0;
     637                 :          8 :         bool mix_specified              = false;
     638                 :            :         int                             rc;
     639                 :            :         long int val;
     640                 :            : 
     641                 :          8 :         spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE);
     642                 :          8 :         snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN);
     643                 :            : 
     644   [ +  +  +  + ]:         28 :         while ((op = getopt(argc, argv, "a:b:c:d:ghi:l:m:n:o:q:r:t:w:M:L:")) != -1) {
     645   [ -  +  -  +  :         20 :                 switch (op) {
             +  -  -  + ]
     646                 :          0 :                 case 'c':
     647                 :          0 :                         g_arbitration.core_mask = optarg;
     648                 :          0 :                         break;
     649                 :          2 :                 case 'd':
     650                 :          2 :                         g_dpdk_mem = spdk_strtol(optarg, 10);
     651         [ -  + ]:          2 :                         if (g_dpdk_mem < 0) {
     652         [ #  # ]:          0 :                                 fprintf(stderr, "Invalid DPDK memory size\n");
     653                 :          0 :                                 return g_dpdk_mem;
     654                 :            :                         }
     655                 :          2 :                         break;
     656                 :          0 :                 case 'w':
     657                 :          0 :                         g_arbitration.workload_type = optarg;
     658                 :          0 :                         break;
     659                 :          2 :                 case 'r':
     660         [ -  + ]:          2 :                         if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) {
     661         [ #  # ]:          0 :                                 fprintf(stderr, "Error parsing transport address\n");
     662                 :          0 :                                 return 1;
     663                 :            :                         }
     664                 :          2 :                         break;
     665                 :          2 :                 case 'g':
     666                 :          2 :                         g_dpdk_mem_single_seg = true;
     667                 :          2 :                         break;
     668                 :          0 :                 case 'h':
     669                 :            :                 case '?':
     670                 :          0 :                         usage(argv[0]);
     671                 :          0 :                         return 1;
     672                 :          0 :                 case 'L':
     673                 :          0 :                         rc = spdk_log_set_flag(optarg);
     674         [ #  # ]:          0 :                         if (rc < 0) {
     675         [ #  # ]:          0 :                                 fprintf(stderr, "unknown flag\n");
     676                 :          0 :                                 usage(argv[0]);
     677                 :          0 :                                 exit(EXIT_FAILURE);
     678                 :            :                         }
     679                 :            : #ifdef DEBUG
     680                 :          0 :                         spdk_log_set_print_level(SPDK_LOG_DEBUG);
     681                 :            : #endif
     682                 :          0 :                         break;
     683                 :         14 :                 default:
     684                 :         14 :                         val = spdk_strtol(optarg, 10);
     685         [ -  + ]:         14 :                         if (val < 0) {
     686         [ #  # ]:          0 :                                 fprintf(stderr, "Converting a string to integer failed\n");
     687                 :          0 :                                 return val;
     688                 :            :                         }
     689                 :            :                         switch (op) {
     690                 :          6 :                         case 'i':
     691                 :          6 :                                 g_arbitration.shm_id = val;
     692                 :          6 :                                 break;
     693                 :          0 :                         case 'l':
     694                 :          0 :                                 g_arbitration.latency_tracking_enable = val;
     695                 :          0 :                                 break;
     696                 :          0 :                         case 'm':
     697                 :          0 :                                 g_arbitration.max_completions = val;
     698                 :          0 :                                 break;
     699                 :          0 :                         case 'q':
     700                 :          0 :                                 g_arbitration.queue_depth = val;
     701                 :          0 :                                 break;
     702                 :          0 :                         case 'o':
     703                 :          0 :                                 g_arbitration.io_size_bytes = val;
     704                 :          0 :                                 break;
     705                 :          8 :                         case 't':
     706                 :          8 :                                 g_arbitration.time_in_sec = val;
     707                 :          8 :                                 break;
     708                 :          0 :                         case 'M':
     709                 :          0 :                                 g_arbitration.rw_percentage = val;
     710                 :          0 :                                 mix_specified = true;
     711                 :          0 :                                 break;
     712                 :          0 :                         case 'a':
     713                 :          0 :                                 g_arbitration.arbitration_mechanism = val;
     714                 :          0 :                                 break;
     715                 :          0 :                         case 'b':
     716                 :          0 :                                 g_arbitration.arbitration_config = val;
     717                 :          0 :                                 break;
     718                 :          0 :                         case 'n':
     719                 :          0 :                                 g_arbitration.io_count = val;
     720                 :          0 :                                 break;
     721                 :          0 :                         default:
     722                 :          0 :                                 usage(argv[0]);
     723                 :          0 :                                 return -EINVAL;
     724                 :            :                         }
     725                 :            :                 }
     726                 :            :         }
     727                 :            : 
     728                 :          8 :         workload_type = g_arbitration.workload_type;
     729                 :            : 
     730   [ +  +  +  - ]:          8 :         if (strcmp(workload_type, "read") &&
     731   [ +  +  +  - ]:          8 :             strcmp(workload_type, "write") &&
     732   [ +  +  +  - ]:          8 :             strcmp(workload_type, "randread") &&
     733   [ +  +  +  - ]:          8 :             strcmp(workload_type, "randwrite") &&
     734   [ +  +  +  - ]:          8 :             strcmp(workload_type, "rw") &&
     735   [ -  +  -  + ]:          8 :             strcmp(workload_type, "randrw")) {
     736         [ #  # ]:          0 :                 fprintf(stderr,
     737                 :            :                         "io pattern type must be one of\n"
     738                 :            :                         "(read, write, randread, randwrite, rw, randrw)\n");
     739                 :          0 :                 return 1;
     740                 :            :         }
     741                 :            : 
     742   [ +  +  +  - ]:          8 :         if (!strcmp(workload_type, "read") ||
     743   [ -  +  -  + ]:          8 :             !strcmp(workload_type, "randread")) {
     744                 :          0 :                 g_arbitration.rw_percentage = 100;
     745                 :            :         }
     746                 :            : 
     747   [ +  +  +  - ]:          8 :         if (!strcmp(workload_type, "write") ||
     748   [ -  +  -  + ]:          8 :             !strcmp(workload_type, "randwrite")) {
     749                 :          0 :                 g_arbitration.rw_percentage = 0;
     750                 :            :         }
     751                 :            : 
     752   [ +  +  +  - ]:          8 :         if (!strcmp(workload_type, "read") ||
     753   [ +  +  +  - ]:          8 :             !strcmp(workload_type, "randread") ||
     754   [ +  +  +  - ]:          8 :             !strcmp(workload_type, "write") ||
     755   [ -  +  -  + ]:          8 :             !strcmp(workload_type, "randwrite")) {
     756         [ #  # ]:          0 :                 if (mix_specified) {
     757         [ #  # ]:          0 :                         fprintf(stderr, "Ignoring -M option... Please use -M option"
     758                 :            :                                 " only when using rw or randrw.\n");
     759                 :            :                 }
     760                 :            :         }
     761                 :            : 
     762   [ +  +  +  - ]:          8 :         if (!strcmp(workload_type, "rw") ||
     763   [ +  +  +  - ]:          8 :             !strcmp(workload_type, "randrw")) {
     764   [ +  -  -  + ]:          8 :                 if (g_arbitration.rw_percentage < 0 || g_arbitration.rw_percentage > 100) {
     765         [ #  # ]:          0 :                         fprintf(stderr,
     766                 :            :                                 "-M must be specified to value from 0 to 100 "
     767                 :            :                                 "for rw or randrw.\n");
     768                 :          0 :                         return 1;
     769                 :            :                 }
     770                 :            :         }
     771                 :            : 
     772   [ +  +  +  - ]:          8 :         if (!strcmp(workload_type, "read") ||
     773   [ +  +  +  - ]:          8 :             !strcmp(workload_type, "write") ||
     774   [ -  +  -  + ]:          8 :             !strcmp(workload_type, "rw")) {
     775                 :          0 :                 g_arbitration.is_random = 0;
     776                 :            :         } else {
     777                 :          8 :                 g_arbitration.is_random = 1;
     778                 :            :         }
     779                 :            : 
     780         [ -  + ]:          8 :         if (g_arbitration.latency_tracking_enable != 0 &&
     781         [ #  # ]:          0 :             g_arbitration.latency_tracking_enable != 1) {
     782         [ #  # ]:          0 :                 fprintf(stderr,
     783                 :            :                         "-l must be specified to value 0 or 1.\n");
     784                 :          0 :                 return 1;
     785                 :            :         }
     786                 :            : 
     787         [ +  - ]:          8 :         switch (g_arbitration.arbitration_mechanism) {
     788                 :          8 :         case SPDK_NVME_CC_AMS_RR:
     789                 :            :         case SPDK_NVME_CC_AMS_WRR:
     790                 :            :         case SPDK_NVME_CC_AMS_VS:
     791                 :          8 :                 break;
     792                 :          0 :         default:
     793         [ #  # ]:          0 :                 fprintf(stderr,
     794                 :            :                         "-a must be specified to value 0, 1, or 7.\n");
     795                 :          0 :                 return 1;
     796                 :            :         }
     797                 :            : 
     798         [ -  + ]:          8 :         if (g_arbitration.arbitration_config != 0 &&
     799         [ #  # ]:          0 :             g_arbitration.arbitration_config != 1) {
     800         [ #  # ]:          0 :                 fprintf(stderr,
     801                 :            :                         "-b must be specified to value 0 or 1.\n");
     802                 :          0 :                 return 1;
     803         [ -  + ]:          8 :         } else if (g_arbitration.arbitration_config == 1 &&
     804         [ #  # ]:          0 :                    g_arbitration.arbitration_mechanism != SPDK_NVME_CC_AMS_WRR) {
     805         [ #  # ]:          0 :                 fprintf(stderr,
     806                 :            :                         "-a must be specified to 1 (WRR) together.\n");
     807                 :          0 :                 return 1;
     808                 :            :         }
     809                 :            : 
     810                 :          8 :         return 0;
     811                 :            : }
     812                 :            : 
     813                 :            : static int
     814                 :          8 : register_workers(void)
     815                 :            : {
     816                 :            :         uint32_t i;
     817                 :            :         struct worker_thread *worker;
     818                 :          8 :         enum spdk_nvme_qprio qprio = SPDK_NVME_QPRIO_URGENT;
     819                 :            : 
     820         [ +  + ]:         40 :         SPDK_ENV_FOREACH_CORE(i) {
     821                 :         32 :                 worker = calloc(1, sizeof(*worker));
     822         [ -  + ]:         32 :                 if (worker == NULL) {
     823   [ #  #  #  # ]:          0 :                         fprintf(stderr, "Unable to allocate worker\n");
     824                 :          0 :                         return -1;
     825                 :            :                 }
     826                 :            : 
     827                 :         32 :                 TAILQ_INIT(&worker->ns_ctx);
     828                 :         32 :                 worker->lcore = i;
     829                 :         32 :                 TAILQ_INSERT_TAIL(&g_workers, worker, link);
     830                 :         32 :                 g_arbitration.num_workers++;
     831                 :            : 
     832         [ -  + ]:         32 :                 if (g_arbitration.arbitration_mechanism == SPDK_NVME_CAP_AMS_WRR) {
     833                 :          0 :                         qprio++;
     834                 :            :                 }
     835                 :            : 
     836                 :         32 :                 worker->qprio = qprio & SPDK_NVME_CREATE_IO_SQ_QPRIO_MASK;
     837                 :            :         }
     838                 :            : 
     839                 :          8 :         return 0;
     840                 :            : }
     841                 :            : 
     842                 :            : static bool
     843                 :          2 : probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
     844                 :            :          struct spdk_nvme_ctrlr_opts *opts)
     845                 :            : {
     846                 :            :         /* Update with user specified arbitration configuration */
     847                 :          2 :         opts->arb_mechanism = g_arbitration.arbitration_mechanism;
     848                 :            : 
     849         [ -  + ]:          2 :         printf("Attaching to %s\n", trid->traddr);
     850                 :            : 
     851                 :          2 :         return true;
     852                 :            : }
     853                 :            : 
     854                 :            : static void
     855                 :         12 : attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
     856                 :            :           struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
     857                 :            : {
     858         [ -  + ]:         12 :         printf("Attached to %s\n", trid->traddr);
     859                 :            : 
     860                 :            :         /* Update with actual arbitration configuration in use */
     861                 :         12 :         g_arbitration.arbitration_mechanism = opts->arb_mechanism;
     862                 :            : 
     863                 :         12 :         register_ctrlr(ctrlr);
     864                 :         12 : }
     865                 :            : 
     866                 :            : static int
     867                 :          8 : register_controllers(void)
     868                 :            : {
     869         [ -  + ]:          8 :         printf("Initializing NVMe Controllers\n");
     870                 :            : 
     871         [ -  + ]:          8 :         if (spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL) != 0) {
     872   [ #  #  #  # ]:          0 :                 fprintf(stderr, "spdk_nvme_probe() failed\n");
     873                 :          0 :                 return 1;
     874                 :            :         }
     875                 :            : 
     876         [ -  + ]:          8 :         if (g_arbitration.num_namespaces == 0) {
     877   [ #  #  #  # ]:          0 :                 fprintf(stderr, "No valid namespaces to continue IO testing\n");
     878                 :          0 :                 return 1;
     879                 :            :         }
     880                 :            : 
     881                 :          8 :         return 0;
     882                 :            : }
     883                 :            : 
     884                 :            : static void
     885                 :          8 : unregister_controllers(void)
     886                 :            : {
     887                 :            :         struct ctrlr_entry *entry, *tmp;
     888                 :          8 :         struct spdk_nvme_detach_ctx *detach_ctx = NULL;
     889                 :            : 
     890         [ +  + ]:         20 :         TAILQ_FOREACH_SAFE(entry, &g_controllers, link, tmp) {
     891         [ +  + ]:         12 :                 TAILQ_REMOVE(&g_controllers, entry, link);
     892   [ -  +  -  - ]:         12 :                 if (g_arbitration.latency_tracking_enable &&
     893                 :          0 :                     spdk_nvme_ctrlr_is_feature_supported(entry->ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING)) {
     894                 :          0 :                         set_latency_tracking_feature(entry->ctrlr, false);
     895                 :            :                 }
     896                 :         12 :                 spdk_nvme_detach_async(entry->ctrlr, &detach_ctx);
     897                 :         12 :                 free(entry);
     898                 :            :         }
     899                 :            : 
     900   [ +  +  +  + ]:         16 :         while (detach_ctx && spdk_nvme_detach_poll_async(detach_ctx) == -EAGAIN) {
     901                 :            :                 ;
     902                 :            :         }
     903                 :          8 : }
     904                 :            : 
     905                 :            : static int
     906                 :          8 : associate_workers_with_ns(void)
     907                 :            : {
     908                 :          8 :         struct ns_entry         *entry = TAILQ_FIRST(&g_namespaces);
     909                 :          8 :         struct worker_thread    *worker = TAILQ_FIRST(&g_workers);
     910                 :            :         struct ns_worker_ctx    *ns_ctx;
     911                 :            :         int                     i, count;
     912                 :            : 
     913                 :          8 :         count = g_arbitration.num_namespaces > g_arbitration.num_workers ?
     914                 :            :                 g_arbitration.num_namespaces : g_arbitration.num_workers;
     915                 :            : 
     916         [ +  + ]:         42 :         for (i = 0; i < count; i++) {
     917         [ -  + ]:         34 :                 if (entry == NULL) {
     918                 :          0 :                         break;
     919                 :            :                 }
     920                 :            : 
     921                 :         34 :                 ns_ctx = malloc(sizeof(struct ns_worker_ctx));
     922         [ -  + ]:         34 :                 if (!ns_ctx) {
     923                 :          0 :                         return 1;
     924                 :            :                 }
     925         [ -  + ]:         34 :                 memset(ns_ctx, 0, sizeof(*ns_ctx));
     926                 :            : 
     927         [ -  + ]:         34 :                 printf("Associating %s with lcore %d\n", entry->name, worker->lcore);
     928                 :         34 :                 ns_ctx->entry = entry;
     929                 :         34 :                 TAILQ_INSERT_TAIL(&worker->ns_ctx, ns_ctx, link);
     930                 :            : 
     931                 :         34 :                 worker = TAILQ_NEXT(worker, link);
     932         [ +  + ]:         34 :                 if (worker == NULL) {
     933                 :          8 :                         worker = TAILQ_FIRST(&g_workers);
     934                 :            :                 }
     935                 :            : 
     936                 :         34 :                 entry = TAILQ_NEXT(entry, link);
     937         [ +  + ]:         34 :                 if (entry == NULL) {
     938                 :         27 :                         entry = TAILQ_FIRST(&g_namespaces);
     939                 :            :                 }
     940                 :            : 
     941                 :            :         }
     942                 :            : 
     943                 :          8 :         return 0;
     944                 :            : }
     945                 :            : 
     946                 :            : static void
     947                 :          0 : get_feature_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
     948                 :            : {
     949                 :          0 :         struct feature *feature = cb_arg;
     950                 :          0 :         int fid = feature - features;
     951                 :            : 
     952   [ #  #  #  # ]:          0 :         if (spdk_nvme_cpl_is_error(cpl)) {
     953         [ #  # ]:          0 :                 printf("get_feature(0x%02X) failed\n", fid);
     954                 :            :         } else {
     955                 :          0 :                 feature->result = cpl->cdw0;
     956                 :          0 :                 feature->valid = true;
     957                 :            :         }
     958                 :            : 
     959                 :          0 :         g_arbitration.outstanding_commands--;
     960                 :          0 : }
     961                 :            : 
     962                 :            : static int
     963                 :          0 : get_feature(struct spdk_nvme_ctrlr *ctrlr, uint8_t fid)
     964                 :            : {
     965                 :          0 :         struct spdk_nvme_cmd cmd = {};
     966                 :          0 :         struct feature *feature = &features[fid];
     967                 :            : 
     968                 :          0 :         feature->valid = false;
     969                 :            : 
     970                 :          0 :         cmd.opc = SPDK_NVME_OPC_GET_FEATURES;
     971                 :          0 :         cmd.cdw10_bits.get_features.fid = fid;
     972                 :            : 
     973                 :          0 :         return spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, NULL, 0, get_feature_completion, feature);
     974                 :            : }
     975                 :            : 
     976                 :            : static void
     977                 :          0 : get_arb_feature(struct spdk_nvme_ctrlr *ctrlr)
     978                 :            : {
     979                 :          0 :         get_feature(ctrlr, SPDK_NVME_FEAT_ARBITRATION);
     980                 :            : 
     981                 :          0 :         g_arbitration.outstanding_commands++;
     982                 :            : 
     983         [ #  # ]:          0 :         while (g_arbitration.outstanding_commands) {
     984                 :          0 :                 spdk_nvme_ctrlr_process_admin_completions(ctrlr);
     985                 :            :         }
     986                 :            : 
     987   [ #  #  #  # ]:          0 :         if (features[SPDK_NVME_FEAT_ARBITRATION].valid) {
     988                 :            :                 union spdk_nvme_cmd_cdw11 arb;
     989                 :          0 :                 arb.feat_arbitration.raw = features[SPDK_NVME_FEAT_ARBITRATION].result;
     990                 :            : 
     991         [ #  # ]:          0 :                 printf("Current Arbitration Configuration\n");
     992         [ #  # ]:          0 :                 printf("===========\n");
     993         [ #  # ]:          0 :                 printf("Arbitration Burst:           ");
     994         [ #  # ]:          0 :                 if (arb.feat_arbitration.bits.ab == SPDK_NVME_ARBITRATION_BURST_UNLIMITED) {
     995         [ #  # ]:          0 :                         printf("no limit\n");
     996                 :            :                 } else {
     997   [ #  #  #  # ]:          0 :                         printf("%u\n", 1u << arb.feat_arbitration.bits.ab);
     998                 :            :                 }
     999                 :            : 
    1000         [ #  # ]:          0 :                 printf("Low Priority Weight:         %u\n", arb.feat_arbitration.bits.lpw + 1);
    1001         [ #  # ]:          0 :                 printf("Medium Priority Weight:      %u\n", arb.feat_arbitration.bits.mpw + 1);
    1002         [ #  # ]:          0 :                 printf("High Priority Weight:        %u\n", arb.feat_arbitration.bits.hpw + 1);
    1003                 :          0 :                 printf("\n");
    1004                 :            :         }
    1005                 :          0 : }
    1006                 :            : 
    1007                 :            : static void
    1008                 :          0 : set_feature_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
    1009                 :            : {
    1010                 :          0 :         struct feature *feature = cb_arg;
    1011                 :          0 :         int fid = feature - features;
    1012                 :            : 
    1013   [ #  #  #  # ]:          0 :         if (spdk_nvme_cpl_is_error(cpl)) {
    1014         [ #  # ]:          0 :                 printf("set_feature(0x%02X) failed\n", fid);
    1015                 :          0 :                 feature->valid = false;
    1016                 :            :         } else {
    1017         [ #  # ]:          0 :                 printf("Set Arbitration Feature Successfully\n");
    1018                 :            :         }
    1019                 :            : 
    1020                 :          0 :         g_arbitration.outstanding_commands--;
    1021                 :          0 : }
    1022                 :            : 
    1023                 :            : static int
    1024                 :          0 : set_arb_feature(struct spdk_nvme_ctrlr *ctrlr)
    1025                 :            : {
    1026                 :            :         int ret;
    1027                 :          0 :         struct spdk_nvme_cmd cmd = {};
    1028                 :            : 
    1029                 :          0 :         cmd.opc = SPDK_NVME_OPC_SET_FEATURES;
    1030                 :          0 :         cmd.cdw10_bits.set_features.fid = SPDK_NVME_FEAT_ARBITRATION;
    1031                 :            : 
    1032                 :          0 :         g_arbitration.outstanding_commands = 0;
    1033                 :            : 
    1034   [ #  #  #  # ]:          0 :         if (features[SPDK_NVME_FEAT_ARBITRATION].valid) {
    1035                 :          0 :                 cmd.cdw11_bits.feat_arbitration.bits.ab = SPDK_NVME_ARBITRATION_BURST_UNLIMITED;
    1036                 :          0 :                 cmd.cdw11_bits.feat_arbitration.bits.lpw = USER_SPECIFIED_LOW_PRIORITY_WEIGHT;
    1037                 :          0 :                 cmd.cdw11_bits.feat_arbitration.bits.mpw = USER_SPECIFIED_MEDIUM_PRIORITY_WEIGHT;
    1038                 :          0 :                 cmd.cdw11_bits.feat_arbitration.bits.hpw = USER_SPECIFIED_HIGH_PRIORITY_WEIGHT;
    1039                 :            :         }
    1040                 :            : 
    1041                 :          0 :         ret = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, NULL, 0,
    1042                 :            :                                             set_feature_completion, &features[SPDK_NVME_FEAT_ARBITRATION]);
    1043         [ #  # ]:          0 :         if (ret) {
    1044         [ #  # ]:          0 :                 printf("Set Arbitration Feature: Failed 0x%x\n", ret);
    1045                 :          0 :                 return 1;
    1046                 :            :         }
    1047                 :            : 
    1048                 :          0 :         g_arbitration.outstanding_commands++;
    1049                 :            : 
    1050         [ #  # ]:          0 :         while (g_arbitration.outstanding_commands) {
    1051                 :          0 :                 spdk_nvme_ctrlr_process_admin_completions(ctrlr);
    1052                 :            :         }
    1053                 :            : 
    1054   [ #  #  #  # ]:          0 :         if (!features[SPDK_NVME_FEAT_ARBITRATION].valid) {
    1055         [ #  # ]:          0 :                 printf("Set Arbitration Feature failed and use default configuration\n");
    1056                 :            :         }
    1057                 :            : 
    1058                 :          0 :         return 0;
    1059                 :            : }
    1060                 :            : 
    1061                 :            : int
    1062                 :          8 : main(int argc, char **argv)
    1063                 :            : {
    1064                 :            :         int rc;
    1065                 :            :         struct worker_thread *worker, *main_worker;
    1066                 :            :         unsigned main_core;
    1067                 :          3 :         char task_pool_name[30];
    1068                 :          8 :         uint32_t task_count = 0;
    1069                 :          3 :         struct spdk_env_opts opts;
    1070                 :            : 
    1071                 :          8 :         rc = parse_args(argc, argv);
    1072         [ -  + ]:          8 :         if (rc != 0) {
    1073                 :          0 :                 return rc;
    1074                 :            :         }
    1075                 :            : 
    1076                 :          8 :         spdk_env_opts_init(&opts);
    1077                 :          8 :         opts.name = "arb";
    1078                 :          8 :         opts.mem_size = g_dpdk_mem;
    1079         [ -  + ]:          8 :         opts.hugepage_single_segments = g_dpdk_mem_single_seg;
    1080                 :          8 :         opts.core_mask = g_arbitration.core_mask;
    1081                 :          8 :         opts.shm_id = g_arbitration.shm_id;
    1082         [ -  + ]:          8 :         if (spdk_env_init(&opts) < 0) {
    1083                 :          0 :                 return 1;
    1084                 :            :         }
    1085                 :            : 
    1086                 :          8 :         g_arbitration.tsc_rate = spdk_get_ticks_hz();
    1087                 :            : 
    1088         [ -  + ]:          8 :         if (register_workers() != 0) {
    1089                 :          0 :                 rc = 1;
    1090                 :          0 :                 goto exit;
    1091                 :            :         }
    1092                 :            : 
    1093         [ -  + ]:          8 :         if (register_controllers() != 0) {
    1094                 :          0 :                 rc = 1;
    1095                 :          0 :                 goto exit;
    1096                 :            :         }
    1097                 :            : 
    1098         [ -  + ]:          8 :         if (associate_workers_with_ns() != 0) {
    1099                 :          0 :                 rc = 1;
    1100                 :          0 :                 goto exit;
    1101                 :            :         }
    1102                 :            : 
    1103         [ -  + ]:          8 :         snprintf(task_pool_name, sizeof(task_pool_name), "task_pool_%d", getpid());
    1104                 :            : 
    1105                 :            :         /*
    1106                 :            :          * The task_count will be dynamically calculated based on the
    1107                 :            :          * number of attached active namespaces, queue depth and number
    1108                 :            :          * of cores (workers) involved in the IO perations.
    1109                 :            :          */
    1110                 :          8 :         task_count = g_arbitration.num_namespaces > g_arbitration.num_workers ?
    1111                 :          8 :                      g_arbitration.num_namespaces : g_arbitration.num_workers;
    1112                 :          8 :         task_count *= g_arbitration.queue_depth;
    1113                 :            : 
    1114                 :          8 :         task_pool = spdk_mempool_create(task_pool_name, task_count,
    1115                 :            :                                         sizeof(struct arb_task), 0, SPDK_ENV_SOCKET_ID_ANY);
    1116         [ -  + ]:          8 :         if (task_pool == NULL) {
    1117   [ #  #  #  # ]:          0 :                 fprintf(stderr, "could not initialize task pool\n");
    1118                 :          0 :                 rc = 1;
    1119                 :          0 :                 goto exit;
    1120                 :            :         }
    1121                 :            : 
    1122                 :          8 :         print_configuration(argv[0]);
    1123                 :            : 
    1124         [ -  + ]:          8 :         printf("Initialization complete. Launching workers.\n");
    1125                 :            : 
    1126                 :            :         /* Launch all of the secondary workers */
    1127                 :          8 :         main_core = spdk_env_get_current_core();
    1128                 :          8 :         main_worker = NULL;
    1129         [ +  + ]:         40 :         TAILQ_FOREACH(worker, &g_workers, link) {
    1130         [ +  + ]:         32 :                 if (worker->lcore != main_core) {
    1131                 :         24 :                         spdk_env_thread_launch_pinned(worker->lcore, work_fn, worker);
    1132                 :            :                 } else {
    1133         [ -  + ]:          8 :                         assert(main_worker == NULL);
    1134                 :          8 :                         main_worker = worker;
    1135                 :            :                 }
    1136                 :            :         }
    1137                 :            : 
    1138         [ -  + ]:          8 :         assert(main_worker != NULL);
    1139                 :          8 :         rc = work_fn(main_worker);
    1140                 :            : 
    1141                 :          8 :         spdk_env_thread_wait_all();
    1142                 :            : 
    1143                 :          8 :         print_stats();
    1144                 :            : 
    1145                 :          8 : exit:
    1146                 :          8 :         unregister_controllers();
    1147                 :          8 :         cleanup(task_count);
    1148                 :            : 
    1149                 :          8 :         spdk_env_fini();
    1150                 :            : 
    1151         [ -  + ]:          8 :         if (rc != 0) {
    1152   [ #  #  #  # ]:          0 :                 fprintf(stderr, "%s: errors occurred\n", argv[0]);
    1153                 :            :         }
    1154                 :            : 
    1155                 :          8 :         return rc;
    1156                 :            : }

Generated by: LCOV version 1.14