LCOV - code coverage report
Current view: top level - spdk/app/spdk_nvme_perf - perf.c (source / functions) Hit Total Coverage
Test: Combined Lines: 837 1661 50.4 %
Date: 2024-07-12 22:14:58 Functions: 48 72 66.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 532 1550 34.3 %

           Branch data     Line data    Source code
       1                 :            : /*   SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  *   Copyright (C) 2015 Intel Corporation.
       3                 :            :  *   All rights reserved.
       4                 :            :  *
       5                 :            :  *   Copyright (c) 2019-2021 Mellanox Technologies LTD. All rights reserved.
       6                 :            :  *   Copyright (c) 2021, 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
       7                 :            :  */
       8                 :            : 
       9                 :            : #include "spdk/stdinc.h"
      10                 :            : 
      11                 :            : #include "spdk/config.h"
      12                 :            : #include "spdk/env.h"
      13                 :            : #include "spdk/fd.h"
      14                 :            : #include "spdk/nvme.h"
      15                 :            : #include "spdk/vmd.h"
      16                 :            : #include "spdk/queue.h"
      17                 :            : #include "spdk/string.h"
      18                 :            : #include "spdk/nvme_intel.h"
      19                 :            : #include "spdk/histogram_data.h"
      20                 :            : #include "spdk/endian.h"
      21                 :            : #include "spdk/dif.h"
      22                 :            : #include "spdk/util.h"
      23                 :            : #include "spdk/log.h"
      24                 :            : #include "spdk/likely.h"
      25                 :            : #include "spdk/sock.h"
      26                 :            : #include "spdk/zipf.h"
      27                 :            : #include "spdk/nvmf.h"
      28                 :            : 
      29                 :            : #ifdef SPDK_CONFIG_URING
      30                 :            : #include <liburing.h>
      31                 :            : #endif
      32                 :            : 
      33                 :            : #if HAVE_LIBAIO
      34                 :            : #include <libaio.h>
      35                 :            : #endif
      36                 :            : 
      37                 :            : struct ctrlr_entry {
      38                 :            :         struct spdk_nvme_ctrlr                  *ctrlr;
      39                 :            :         enum spdk_nvme_transport_type           trtype;
      40                 :            :         struct spdk_nvme_intel_rw_latency_page  *latency_page;
      41                 :            : 
      42                 :            :         struct spdk_nvme_qpair                  **unused_qpairs;
      43                 :            : 
      44                 :            :         TAILQ_ENTRY(ctrlr_entry)                link;
      45                 :            :         char                                    name[1024];
      46                 :            : };
      47                 :            : 
      48                 :            : enum entry_type {
      49                 :            :         ENTRY_TYPE_NVME_NS,
      50                 :            :         ENTRY_TYPE_AIO_FILE,
      51                 :            :         ENTRY_TYPE_URING_FILE,
      52                 :            : };
      53                 :            : 
      54                 :            : struct ns_fn_table;
      55                 :            : 
      56                 :            : struct ns_entry {
      57                 :            :         enum entry_type         type;
      58                 :            :         const struct ns_fn_table        *fn_table;
      59                 :            : 
      60                 :            :         union {
      61                 :            :                 struct {
      62                 :            :                         struct spdk_nvme_ctrlr  *ctrlr;
      63                 :            :                         struct spdk_nvme_ns     *ns;
      64                 :            :                 } nvme;
      65                 :            : #ifdef SPDK_CONFIG_URING
      66                 :            :                 struct {
      67                 :            :                         int                     fd;
      68                 :            :                 } uring;
      69                 :            : #endif
      70                 :            : #if HAVE_LIBAIO
      71                 :            :                 struct {
      72                 :            :                         int                     fd;
      73                 :            :                 } aio;
      74                 :            : #endif
      75                 :            :         } u;
      76                 :            : 
      77                 :            :         TAILQ_ENTRY(ns_entry)   link;
      78                 :            :         uint32_t                io_size_blocks;
      79                 :            :         uint32_t                num_io_requests;
      80                 :            :         uint64_t                size_in_ios;
      81                 :            :         uint32_t                block_size;
      82                 :            :         uint32_t                md_size;
      83                 :            :         bool                    md_interleave;
      84                 :            :         unsigned int            seed;
      85                 :            :         struct spdk_zipf        *zipf;
      86                 :            :         bool                    pi_loc;
      87                 :            :         enum spdk_nvme_pi_type  pi_type;
      88                 :            :         uint32_t                io_flags;
      89                 :            :         char                    name[1024];
      90                 :            : };
      91                 :            : 
      92                 :            : static const double g_latency_cutoffs[] = {
      93                 :            :         0.01,
      94                 :            :         0.10,
      95                 :            :         0.25,
      96                 :            :         0.50,
      97                 :            :         0.75,
      98                 :            :         0.90,
      99                 :            :         0.95,
     100                 :            :         0.98,
     101                 :            :         0.99,
     102                 :            :         0.995,
     103                 :            :         0.999,
     104                 :            :         0.9999,
     105                 :            :         0.99999,
     106                 :            :         0.999999,
     107                 :            :         0.9999999,
     108                 :            :         -1,
     109                 :            : };
     110                 :            : 
     111                 :            : struct ns_worker_stats {
     112                 :            :         uint64_t                io_submitted;
     113                 :            :         uint64_t                io_completed;
     114                 :            :         uint64_t                last_io_completed;
     115                 :            :         uint64_t                total_tsc;
     116                 :            :         uint64_t                min_tsc;
     117                 :            :         uint64_t                max_tsc;
     118                 :            :         uint64_t                last_tsc;
     119                 :            :         uint64_t                busy_tsc;
     120                 :            :         uint64_t                idle_tsc;
     121                 :            :         uint64_t                last_busy_tsc;
     122                 :            :         uint64_t                last_idle_tsc;
     123                 :            : };
     124                 :            : 
     125                 :            : struct ns_worker_ctx {
     126                 :            :         struct ns_entry         *entry;
     127                 :            :         struct ns_worker_stats  stats;
     128                 :            :         uint64_t                current_queue_depth;
     129                 :            :         uint64_t                offset_in_ios;
     130                 :            :         bool                    is_draining;
     131                 :            : 
     132                 :            :         union {
     133                 :            :                 struct {
     134                 :            :                         int                             num_active_qpairs;
     135                 :            :                         int                             num_all_qpairs;
     136                 :            :                         struct spdk_nvme_qpair          **qpair;
     137                 :            :                         struct spdk_nvme_poll_group     *group;
     138                 :            :                         int                             last_qpair;
     139                 :            :                 } nvme;
     140                 :            : 
     141                 :            : #ifdef SPDK_CONFIG_URING
     142                 :            :                 struct {
     143                 :            :                         struct io_uring         ring;
     144                 :            :                         uint64_t                io_inflight;
     145                 :            :                         uint64_t                io_pending;
     146                 :            :                         struct io_uring_cqe     **cqes;
     147                 :            : 
     148                 :            :                 } uring;
     149                 :            : #endif
     150                 :            : #if HAVE_LIBAIO
     151                 :            :                 struct {
     152                 :            :                         struct io_event         *events;
     153                 :            :                         io_context_t            ctx;
     154                 :            :                 } aio;
     155                 :            : #endif
     156                 :            :         } u;
     157                 :            : 
     158                 :            :         TAILQ_ENTRY(ns_worker_ctx)      link;
     159                 :            : 
     160                 :            :         TAILQ_HEAD(, perf_task)         queued_tasks;
     161                 :            : 
     162                 :            :         struct spdk_histogram_data      *histogram;
     163                 :            :         int                             status;
     164                 :            : };
     165                 :            : 
     166                 :            : struct perf_task {
     167                 :            :         struct ns_worker_ctx    *ns_ctx;
     168                 :            :         struct iovec            *iovs; /* array of iovecs to transfer. */
     169                 :            :         int                     iovcnt; /* Number of iovecs in iovs array. */
     170                 :            :         int                     iovpos; /* Current iovec position. */
     171                 :            :         uint32_t                iov_offset; /* Offset in current iovec. */
     172                 :            :         struct iovec            md_iov;
     173                 :            :         uint64_t                submit_tsc;
     174                 :            :         bool                    is_read;
     175                 :            :         struct spdk_dif_ctx     dif_ctx;
     176                 :            : #if HAVE_LIBAIO
     177                 :            :         struct iocb             iocb;
     178                 :            : #endif
     179                 :            :         TAILQ_ENTRY(perf_task)  link;
     180                 :            : };
     181                 :            : 
     182                 :            : struct worker_thread {
     183                 :            :         TAILQ_HEAD(, ns_worker_ctx)     ns_ctx;
     184                 :            :         TAILQ_ENTRY(worker_thread)      link;
     185                 :            :         unsigned                        lcore;
     186                 :            : };
     187                 :            : 
     188                 :            : struct ns_fn_table {
     189                 :            :         void    (*setup_payload)(struct perf_task *task, uint8_t pattern);
     190                 :            : 
     191                 :            :         int     (*submit_io)(struct perf_task *task, struct ns_worker_ctx *ns_ctx,
     192                 :            :                              struct ns_entry *entry, uint64_t offset_in_ios);
     193                 :            : 
     194                 :            :         int64_t (*check_io)(struct ns_worker_ctx *ns_ctx);
     195                 :            : 
     196                 :            :         void    (*verify_io)(struct perf_task *task, struct ns_entry *entry);
     197                 :            : 
     198                 :            :         int     (*init_ns_worker_ctx)(struct ns_worker_ctx *ns_ctx);
     199                 :            : 
     200                 :            :         void    (*cleanup_ns_worker_ctx)(struct ns_worker_ctx *ns_ctx);
     201                 :            :         void    (*dump_transport_stats)(uint32_t lcore, struct ns_worker_ctx *ns_ctx);
     202                 :            : };
     203                 :            : 
     204                 :            : static uint32_t g_io_unit_size = (UINT32_MAX & (~0x03));
     205                 :            : 
     206                 :            : static int g_outstanding_commands;
     207                 :            : 
     208                 :            : static bool g_latency_ssd_tracking_enable;
     209                 :            : static int g_latency_sw_tracking_level;
     210                 :            : 
     211                 :            : static bool g_vmd;
     212                 :            : static const char *g_workload_type;
     213                 :            : static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers);
     214                 :            : static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces);
     215                 :            : static uint32_t g_num_namespaces;
     216                 :            : static TAILQ_HEAD(, worker_thread) g_workers = TAILQ_HEAD_INITIALIZER(g_workers);
     217                 :            : static uint32_t g_num_workers = 0;
     218                 :            : static bool g_use_every_core = false;
     219                 :            : static uint32_t g_main_core;
     220                 :            : static pthread_barrier_t g_worker_sync_barrier;
     221                 :            : 
     222                 :            : static uint64_t g_tsc_rate;
     223                 :            : 
     224                 :            : static bool g_monitor_perf_cores = false;
     225                 :            : 
     226                 :            : static uint32_t g_io_align = 0x200;
     227                 :            : static bool g_io_align_specified;
     228                 :            : static uint32_t g_io_size_bytes;
     229                 :            : static uint32_t g_max_io_md_size;
     230                 :            : static uint32_t g_max_io_size_blocks;
     231                 :            : static uint32_t g_metacfg_pract_flag;
     232                 :            : static uint32_t g_metacfg_prchk_flags;
     233                 :            : static int g_rw_percentage = -1;
     234                 :            : static int g_is_random;
     235                 :            : static uint32_t g_queue_depth;
     236                 :            : static int g_nr_io_queues_per_ns = 1;
     237                 :            : static int g_nr_unused_io_queues;
     238                 :            : static int g_time_in_sec;
     239                 :            : static uint64_t g_number_ios;
     240                 :            : static uint64_t g_elapsed_time_in_usec;
     241                 :            : static int g_warmup_time_in_sec;
     242                 :            : static uint32_t g_max_completions;
     243                 :            : static uint32_t g_disable_sq_cmb;
     244                 :            : static bool g_use_uring;
     245                 :            : static bool g_warn;
     246                 :            : static bool g_header_digest;
     247                 :            : static bool g_data_digest;
     248                 :            : static bool g_no_shn_notification;
     249                 :            : static bool g_mix_specified;
     250                 :            : /* The flag is used to exit the program while keep alive fails on the transport */
     251                 :            : static bool g_exit;
     252                 :            : /* Default to 10 seconds for the keep alive value. This value is arbitrary. */
     253                 :            : static uint32_t g_keep_alive_timeout_in_ms = 10000;
     254                 :            : static bool g_continue_on_error = false;
     255                 :            : static uint32_t g_quiet_count = 1;
     256                 :            : static double g_zipf_theta;
     257                 :            : /* Set default io_queue_size to UINT16_MAX, NVMe driver will then reduce this
     258                 :            :  * to MQES to maximize the io_queue_size as much as possible.
     259                 :            :  */
     260                 :            : static uint32_t g_io_queue_size = UINT16_MAX;
     261                 :            : 
     262                 :            : static uint32_t g_sock_zcopy_threshold;
     263                 :            : static char *g_sock_threshold_impl;
     264                 :            : 
     265                 :            : static uint8_t g_transport_tos = 0;
     266                 :            : 
     267                 :            : static uint32_t g_rdma_srq_size;
     268                 :            : uint8_t *g_psk = NULL;
     269                 :            : 
     270                 :            : /* When user specifies -Q, some error messages are rate limited.  When rate
     271                 :            :  * limited, we only print the error message every g_quiet_count times the
     272                 :            :  * error occurs.
     273                 :            :  *
     274                 :            :  * Note: the __count is not thread safe, meaning the rate limiting will not
     275                 :            :  * be exact when running perf with multiple thread with lots of errors.
     276                 :            :  * Thread-local __count would mean rate-limiting per thread which doesn't
     277                 :            :  * seem as useful.
     278                 :            :  */
     279                 :            : #define RATELIMIT_LOG(...) \
     280                 :            :         {                                                               \
     281                 :            :                 static uint64_t __count = 0;                            \
     282                 :            :                 if ((__count % g_quiet_count) == 0) {                   \
     283                 :            :                         if (__count > 0 && g_quiet_count > 1) {           \
     284                 :            :                                 fprintf(stderr, "Message suppressed %" PRIu32 " times: ",   \
     285                 :            :                                         g_quiet_count - 1);             \
     286                 :            :                         }                                               \
     287                 :            :                         fprintf(stderr, __VA_ARGS__);                   \
     288                 :            :                 }                                                       \
     289                 :            :                 __count++;                                              \
     290                 :            :         }
     291                 :            : 
     292                 :            : static bool g_dump_transport_stats;
     293                 :            : static pthread_mutex_t g_stats_mutex;
     294                 :            : 
     295                 :            : #define MAX_ALLOWED_PCI_DEVICE_NUM 128
     296                 :            : static struct spdk_pci_addr g_allowed_pci_addr[MAX_ALLOWED_PCI_DEVICE_NUM];
     297                 :            : 
     298                 :            : struct trid_entry {
     299                 :            :         struct spdk_nvme_transport_id   trid;
     300                 :            :         uint16_t                        nsid;
     301                 :            :         char                            hostnqn[SPDK_NVMF_NQN_MAX_LEN + 1];
     302                 :            :         TAILQ_ENTRY(trid_entry)         tailq;
     303                 :            : };
     304                 :            : 
     305                 :            : static TAILQ_HEAD(, trid_entry) g_trid_list = TAILQ_HEAD_INITIALIZER(g_trid_list);
     306                 :            : 
     307                 :            : static int g_file_optind; /* Index of first filename in argv */
     308                 :            : 
     309                 :            : static inline void task_complete(struct perf_task *task);
     310                 :            : 
     311                 :            : static void
     312                 :          3 : perf_set_sock_opts(const char *impl_name, const char *field, uint32_t val, const char *valstr)
     313                 :            : {
     314                 :          3 :         struct spdk_sock_impl_opts sock_opts = {};
     315                 :          3 :         size_t opts_size = sizeof(sock_opts);
     316                 :            :         int rc;
     317                 :            : 
     318                 :          3 :         rc = spdk_sock_impl_get_opts(impl_name, &sock_opts, &opts_size);
     319         [ -  + ]:          3 :         if (rc != 0) {
     320         [ #  # ]:          0 :                 if (errno == EINVAL) {
     321         [ #  # ]:          0 :                         fprintf(stderr, "Unknown sock impl %s\n", impl_name);
     322                 :            :                 } else {
     323         [ #  # ]:          0 :                         fprintf(stderr, "Failed to get opts for sock impl %s: error %d (%s)\n", impl_name, errno,
     324                 :          0 :                                 strerror(errno));
     325                 :            :                 }
     326                 :          0 :                 return;
     327                 :            :         }
     328                 :            : 
     329         [ -  + ]:          3 :         if (opts_size != sizeof(sock_opts)) {
     330         [ #  # ]:          0 :                 fprintf(stderr, "Warning: sock_opts size mismatch. Expected %zu, received %zu\n",
     331                 :            :                         sizeof(sock_opts), opts_size);
     332                 :          0 :                 opts_size = sizeof(sock_opts);
     333                 :            :         }
     334                 :            : 
     335         [ -  + ]:          3 :         if (!field) {
     336         [ #  # ]:          0 :                 fprintf(stderr, "Warning: no socket opts field specified\n");
     337                 :          0 :                 return;
     338   [ -  +  -  + ]:          3 :         } else if (strcmp(field, "enable_zerocopy_send_client") == 0) {
     339                 :          0 :                 sock_opts.enable_zerocopy_send_client = val;
     340   [ -  +  -  + ]:          3 :         } else if (strcmp(field, "tls_version") == 0) {
     341                 :          0 :                 sock_opts.tls_version = val;
     342   [ -  +  -  + ]:          3 :         } else if (strcmp(field, "ktls") == 0) {
     343                 :          0 :                 sock_opts.enable_ktls = val;
     344   [ -  +  +  - ]:          3 :         } else if (strcmp(field, "psk_path") == 0) {
     345         [ -  + ]:          3 :                 if (!valstr) {
     346         [ #  # ]:          0 :                         fprintf(stderr, "No socket opts value specified\n");
     347                 :          0 :                         return;
     348                 :            :                 }
     349                 :          3 :                 g_psk = calloc(1, SPDK_TLS_PSK_MAX_LEN + 1);
     350         [ -  + ]:          3 :                 if (g_psk == NULL) {
     351         [ #  # ]:          0 :                         fprintf(stderr, "Failed to allocate memory for psk\n");
     352                 :          0 :                         return;
     353                 :            :                 }
     354                 :          3 :                 FILE *psk_file = fopen(valstr, "r");
     355         [ -  + ]:          3 :                 if (psk_file == NULL) {
     356         [ #  # ]:          0 :                         fprintf(stderr, "Could not open PSK file\n");
     357                 :          0 :                         return;
     358                 :            :                 }
     359         [ -  + ]:          3 :                 if (fscanf(psk_file, "%" SPDK_STRINGIFY(SPDK_TLS_PSK_MAX_LEN) "s", g_psk) != 1) {
     360         [ #  # ]:          0 :                         fprintf(stderr, "Could not retrieve PSK from file\n");
     361                 :          0 :                         fclose(psk_file);
     362                 :          0 :                         return;
     363                 :            :                 }
     364         [ -  + ]:          3 :                 if (fclose(psk_file)) {
     365         [ #  # ]:          0 :                         fprintf(stderr, "Failed to close PSK file\n");
     366                 :          0 :                         return;
     367                 :            :                 }
     368   [ #  #  #  # ]:          0 :         } else if (strcmp(field, "zerocopy_threshold") == 0) {
     369                 :          0 :                 sock_opts.zerocopy_threshold = val;
     370                 :            :         } else {
     371         [ #  # ]:          0 :                 fprintf(stderr, "Warning: invalid or unprocessed socket opts field: %s\n", field);
     372                 :          0 :                 return;
     373                 :            :         }
     374                 :            : 
     375         [ -  + ]:          3 :         if (spdk_sock_impl_set_opts(impl_name, &sock_opts, opts_size)) {
     376         [ #  # ]:          0 :                 fprintf(stderr, "Failed to set %s: %d for sock impl %s : error %d (%s)\n", field, val, impl_name,
     377                 :          0 :                         errno, strerror(errno));
     378                 :            :         }
     379                 :            : }
     380                 :            : 
     381                 :            : static void
     382                 :     121944 : nvme_perf_reset_sgl(void *ref, uint32_t sgl_offset)
     383                 :            : {
     384                 :            :         struct iovec *iov;
     385                 :     121944 :         struct perf_task *task = (struct perf_task *)ref;
     386                 :            : 
     387                 :     121944 :         task->iov_offset = sgl_offset;
     388         [ +  - ]:     609720 :         for (task->iovpos = 0; task->iovpos < task->iovcnt; task->iovpos++) {
     389                 :     609720 :                 iov = &task->iovs[task->iovpos];
     390         [ +  + ]:     609720 :                 if (task->iov_offset < iov->iov_len) {
     391                 :     121944 :                         break;
     392                 :            :                 }
     393                 :            : 
     394                 :     487776 :                 task->iov_offset -= iov->iov_len;
     395                 :            :         }
     396                 :     121944 : }
     397                 :            : 
     398                 :            : static int
     399                 :     975552 : nvme_perf_next_sge(void *ref, void **address, uint32_t *length)
     400                 :            : {
     401                 :            :         struct iovec *iov;
     402                 :     975552 :         struct perf_task *task = (struct perf_task *)ref;
     403                 :            : 
     404         [ -  + ]:     975552 :         assert(task->iovpos < task->iovcnt);
     405                 :            : 
     406                 :     975552 :         iov = &task->iovs[task->iovpos];
     407         [ -  + ]:     975552 :         assert(task->iov_offset <= iov->iov_len);
     408                 :            : 
     409                 :     975552 :         *address = iov->iov_base + task->iov_offset;
     410                 :     975552 :         *length = iov->iov_len - task->iov_offset;
     411                 :     975552 :         task->iovpos++;
     412                 :     975552 :         task->iov_offset = 0;
     413                 :            : 
     414                 :     975552 :         return 0;
     415                 :            : }
     416                 :            : 
     417                 :            : static int
     418                 :      12638 : nvme_perf_allocate_iovs(struct perf_task *task, void *buf, uint32_t length)
     419                 :            : {
     420                 :      12638 :         int iovpos = 0;
     421                 :            :         struct iovec *iov;
     422                 :      12638 :         uint32_t offset = 0;
     423                 :            : 
     424         [ -  + ]:      12638 :         task->iovcnt = SPDK_CEIL_DIV(length, (uint64_t)g_io_unit_size);
     425                 :      12638 :         task->iovs = calloc(task->iovcnt, sizeof(struct iovec));
     426         [ -  + ]:      12638 :         if (!task->iovs) {
     427                 :          0 :                 return -1;
     428                 :            :         }
     429                 :            : 
     430         [ +  + ]:      40636 :         while (length > 0) {
     431                 :      27998 :                 iov = &task->iovs[iovpos];
     432                 :      27998 :                 iov->iov_len = spdk_min(length, g_io_unit_size);
     433                 :      27998 :                 iov->iov_base = buf + offset;
     434                 :      27998 :                 length -= iov->iov_len;
     435                 :      27998 :                 offset += iov->iov_len;
     436                 :      27998 :                 iovpos++;
     437                 :            :         }
     438                 :            : 
     439                 :      12638 :         return 0;
     440                 :            : }
     441                 :            : 
     442                 :            : #ifdef SPDK_CONFIG_URING
     443                 :            : 
     444                 :            : static void
     445                 :          0 : uring_setup_payload(struct perf_task *task, uint8_t pattern)
     446                 :            : {
     447                 :            :         struct iovec *iov;
     448                 :            : 
     449                 :          0 :         task->iovs = calloc(1, sizeof(struct iovec));
     450         [ #  # ]:          0 :         if (!task->iovs) {
     451   [ #  #  #  # ]:          0 :                 fprintf(stderr, "perf task failed to allocate iovs\n");
     452                 :          0 :                 exit(1);
     453                 :            :         }
     454                 :          0 :         task->iovcnt = 1;
     455                 :            : 
     456                 :          0 :         iov = &task->iovs[0];
     457                 :          0 :         iov->iov_base = spdk_dma_zmalloc(g_io_size_bytes, g_io_align, NULL);
     458                 :          0 :         iov->iov_len = g_io_size_bytes;
     459         [ #  # ]:          0 :         if (iov->iov_base == NULL) {
     460   [ #  #  #  # ]:          0 :                 fprintf(stderr, "spdk_dma_zmalloc() for task->iovs[0].iov_base failed\n");
     461                 :          0 :                 free(task->iovs);
     462                 :          0 :                 exit(1);
     463                 :            :         }
     464         [ #  # ]:          0 :         memset(iov->iov_base, pattern, iov->iov_len);
     465                 :          0 : }
     466                 :            : 
     467                 :            : static int
     468                 :          0 : uring_submit_io(struct perf_task *task, struct ns_worker_ctx *ns_ctx,
     469                 :            :                 struct ns_entry *entry, uint64_t offset_in_ios)
     470                 :            : {
     471                 :            :         struct io_uring_sqe *sqe;
     472                 :            : 
     473                 :          0 :         sqe = io_uring_get_sqe(&ns_ctx->u.uring.ring);
     474         [ #  # ]:          0 :         if (!sqe) {
     475   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Cannot get sqe\n");
     476                 :          0 :                 return -1;
     477                 :            :         }
     478                 :            : 
     479   [ #  #  #  # ]:          0 :         if (task->is_read) {
     480                 :          0 :                 io_uring_prep_readv(sqe, entry->u.uring.fd, task->iovs, 1, offset_in_ios * task->iovs[0].iov_len);
     481                 :            :         } else {
     482                 :          0 :                 io_uring_prep_writev(sqe, entry->u.uring.fd, task->iovs, 1, offset_in_ios * task->iovs[0].iov_len);
     483                 :            :         }
     484                 :            : 
     485                 :          0 :         io_uring_sqe_set_data(sqe, task);
     486                 :          0 :         ns_ctx->u.uring.io_pending++;
     487                 :            : 
     488                 :          0 :         return 0;
     489                 :            : }
     490                 :            : 
     491                 :            : static int64_t
     492                 :          0 : uring_check_io(struct ns_worker_ctx *ns_ctx)
     493                 :            : {
     494                 :          0 :         int i, to_complete, to_submit, count = 0, ret = 0;
     495                 :            :         struct perf_task *task;
     496                 :            : 
     497                 :          0 :         to_submit = ns_ctx->u.uring.io_pending;
     498                 :            : 
     499         [ #  # ]:          0 :         if (to_submit > 0) {
     500                 :            :                 /* If there are I/O to submit, use io_uring_submit here.
     501                 :            :                  * It will automatically call spdk_io_uring_enter appropriately. */
     502                 :          0 :                 ret = io_uring_submit(&ns_ctx->u.uring.ring);
     503         [ #  # ]:          0 :                 if (ret < 0) {
     504                 :          0 :                         ns_ctx->status = 1;
     505                 :          0 :                         return -1;
     506                 :            :                 }
     507                 :          0 :                 ns_ctx->u.uring.io_pending = 0;
     508                 :          0 :                 ns_ctx->u.uring.io_inflight += to_submit;
     509                 :            :         }
     510                 :            : 
     511                 :          0 :         to_complete = ns_ctx->u.uring.io_inflight;
     512         [ #  # ]:          0 :         if (to_complete > 0) {
     513                 :          0 :                 count = io_uring_peek_batch_cqe(&ns_ctx->u.uring.ring, ns_ctx->u.uring.cqes, to_complete);
     514                 :          0 :                 ns_ctx->u.uring.io_inflight -= count;
     515         [ #  # ]:          0 :                 for (i = 0; i < count; i++) {
     516                 :            :                         int res;
     517                 :            : 
     518         [ #  # ]:          0 :                         assert(ns_ctx->u.uring.cqes[i] != NULL);
     519                 :          0 :                         task = (struct perf_task *)ns_ctx->u.uring.cqes[i]->user_data;
     520                 :          0 :                         res = ns_ctx->u.uring.cqes[i]->res;
     521         [ #  # ]:          0 :                         if (res != (int)task->iovs[0].iov_len) {
     522         [ #  # ]:          0 :                                 fprintf(stderr, "cqe->status=%d, iov_len=%d\n", res,
     523         [ #  # ]:          0 :                                         (int)task->iovs[0].iov_len);
     524                 :          0 :                                 ns_ctx->status = 1;
     525         [ #  # ]:          0 :                                 if (res == -EIO) {
     526                 :            :                                         /* The block device has been removed.
     527                 :            :                                          * Stop trying to send I/O to it.
     528                 :            :                                          */
     529                 :          0 :                                         ns_ctx->is_draining = true;
     530                 :            :                                 }
     531                 :            :                         }
     532                 :          0 :                         io_uring_cqe_seen(&ns_ctx->u.uring.ring, ns_ctx->u.uring.cqes[i]);
     533                 :          0 :                         task_complete(task);
     534                 :            :                 }
     535                 :            :         }
     536                 :          0 :         return count;
     537                 :            : }
     538                 :            : 
     539                 :            : static void
     540                 :          0 : uring_verify_io(struct perf_task *task, struct ns_entry *entry)
     541                 :            : {
     542                 :          0 : }
     543                 :            : 
     544                 :            : static int
     545                 :          0 : uring_init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
     546                 :            : {
     547         [ #  # ]:          0 :         if (io_uring_queue_init(g_queue_depth, &ns_ctx->u.uring.ring, 0) < 0) {
     548                 :          0 :                 SPDK_ERRLOG("uring I/O context setup failure\n");
     549                 :          0 :                 return -1;
     550                 :            :         }
     551                 :            : 
     552                 :          0 :         ns_ctx->u.uring.cqes = calloc(g_queue_depth, sizeof(struct io_uring_cqe *));
     553         [ #  # ]:          0 :         if (!ns_ctx->u.uring.cqes) {
     554                 :          0 :                 io_uring_queue_exit(&ns_ctx->u.uring.ring);
     555                 :          0 :                 return -1;
     556                 :            :         }
     557                 :            : 
     558                 :          0 :         return 0;
     559                 :            : }
     560                 :            : 
     561                 :            : static void
     562                 :          0 : uring_cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
     563                 :            : {
     564                 :          0 :         io_uring_queue_exit(&ns_ctx->u.uring.ring);
     565                 :          0 :         free(ns_ctx->u.uring.cqes);
     566                 :          0 : }
     567                 :            : 
     568                 :            : static const struct ns_fn_table uring_fn_table = {
     569                 :            :         .setup_payload          = uring_setup_payload,
     570                 :            :         .submit_io              = uring_submit_io,
     571                 :            :         .check_io               = uring_check_io,
     572                 :            :         .verify_io              = uring_verify_io,
     573                 :            :         .init_ns_worker_ctx     = uring_init_ns_worker_ctx,
     574                 :            :         .cleanup_ns_worker_ctx  = uring_cleanup_ns_worker_ctx,
     575                 :            : };
     576                 :            : 
     577                 :            : #endif
     578                 :            : 
     579                 :            : #ifdef HAVE_LIBAIO
     580                 :            : static void
     581                 :          0 : aio_setup_payload(struct perf_task *task, uint8_t pattern)
     582                 :            : {
     583                 :            :         struct iovec *iov;
     584                 :            : 
     585                 :          0 :         task->iovs = calloc(1, sizeof(struct iovec));
     586         [ #  # ]:          0 :         if (!task->iovs) {
     587   [ #  #  #  # ]:          0 :                 fprintf(stderr, "perf task failed to allocate iovs\n");
     588                 :          0 :                 exit(1);
     589                 :            :         }
     590                 :          0 :         task->iovcnt = 1;
     591                 :            : 
     592                 :          0 :         iov = &task->iovs[0];
     593                 :          0 :         iov->iov_base = spdk_dma_zmalloc(g_io_size_bytes, g_io_align, NULL);
     594                 :          0 :         iov->iov_len = g_io_size_bytes;
     595         [ #  # ]:          0 :         if (iov->iov_base == NULL) {
     596   [ #  #  #  # ]:          0 :                 fprintf(stderr, "spdk_dma_zmalloc() for task->iovs[0].iov_base failed\n");
     597                 :          0 :                 free(task->iovs);
     598                 :          0 :                 exit(1);
     599                 :            :         }
     600         [ #  # ]:          0 :         memset(iov->iov_base, pattern, iov->iov_len);
     601                 :          0 : }
     602                 :            : 
     603                 :            : static int
     604                 :          0 : aio_submit(io_context_t aio_ctx, struct iocb *iocb, int fd, enum io_iocb_cmd cmd,
     605                 :            :            struct iovec *iov, uint64_t offset, void *cb_ctx)
     606                 :            : {
     607                 :          0 :         iocb->aio_fildes = fd;
     608                 :          0 :         iocb->aio_reqprio = 0;
     609                 :          0 :         iocb->aio_lio_opcode = cmd;
     610                 :          0 :         iocb->u.c.buf = iov->iov_base;
     611                 :          0 :         iocb->u.c.nbytes = iov->iov_len;
     612                 :          0 :         iocb->u.c.offset = offset * iov->iov_len;
     613                 :          0 :         iocb->data = cb_ctx;
     614                 :            : 
     615         [ #  # ]:          0 :         if (io_submit(aio_ctx, 1, &iocb) < 0) {
     616         [ #  # ]:          0 :                 printf("io_submit");
     617                 :          0 :                 return -1;
     618                 :            :         }
     619                 :            : 
     620                 :          0 :         return 0;
     621                 :            : }
     622                 :            : 
     623                 :            : static int
     624                 :          0 : aio_submit_io(struct perf_task *task, struct ns_worker_ctx *ns_ctx,
     625                 :            :               struct ns_entry *entry, uint64_t offset_in_ios)
     626                 :            : {
     627   [ #  #  #  # ]:          0 :         if (task->is_read) {
     628                 :          0 :                 return aio_submit(ns_ctx->u.aio.ctx, &task->iocb, entry->u.aio.fd, IO_CMD_PREAD,
     629                 :            :                                   task->iovs, offset_in_ios, task);
     630                 :            :         } else {
     631                 :          0 :                 return aio_submit(ns_ctx->u.aio.ctx, &task->iocb, entry->u.aio.fd, IO_CMD_PWRITE,
     632                 :            :                                   task->iovs, offset_in_ios, task);
     633                 :            :         }
     634                 :            : }
     635                 :            : 
     636                 :            : static int64_t
     637                 :          0 : aio_check_io(struct ns_worker_ctx *ns_ctx)
     638                 :            : {
     639                 :            :         int count, i;
     640                 :          0 :         struct timespec timeout;
     641                 :            :         struct perf_task *task;
     642                 :            : 
     643                 :          0 :         timeout.tv_sec = 0;
     644                 :          0 :         timeout.tv_nsec = 0;
     645                 :            : 
     646                 :          0 :         count = io_getevents(ns_ctx->u.aio.ctx, 1, g_queue_depth, ns_ctx->u.aio.events, &timeout);
     647         [ #  # ]:          0 :         if (count < 0) {
     648   [ #  #  #  # ]:          0 :                 fprintf(stderr, "io_getevents error\n");
     649                 :          0 :                 ns_ctx->status = 1;
     650                 :          0 :                 return -1;
     651                 :            :         }
     652                 :            : 
     653         [ #  # ]:          0 :         for (i = 0; i < count; i++) {
     654                 :            :                 unsigned long res;
     655                 :            : 
     656                 :          0 :                 task = (struct perf_task *)ns_ctx->u.aio.events[i].data;
     657                 :          0 :                 res = ns_ctx->u.aio.events[i].res;
     658         [ #  # ]:          0 :                 if (res != (uint64_t)task->iovs[0].iov_len) {
     659         [ #  # ]:          0 :                         fprintf(stderr, "event->res=%ld, iov_len=%lu\n", (long)res,
     660         [ #  # ]:          0 :                                 (uint64_t)task->iovs[0].iov_len);
     661                 :          0 :                         ns_ctx->status = 1;
     662         [ #  # ]:          0 :                         if ((long)res == -EIO) {
     663                 :            :                                 /* The block device has been removed.  Stop trying to send I/O to it. */
     664                 :          0 :                                 ns_ctx->is_draining = true;
     665                 :            :                         }
     666                 :            :                 }
     667                 :          0 :                 task_complete(ns_ctx->u.aio.events[i].data);
     668                 :            :         }
     669                 :          0 :         return count;
     670                 :            : }
     671                 :            : 
     672                 :            : static void
     673                 :          0 : aio_verify_io(struct perf_task *task, struct ns_entry *entry)
     674                 :            : {
     675                 :          0 : }
     676                 :            : 
     677                 :            : static int
     678                 :          0 : aio_init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
     679                 :            : {
     680                 :          0 :         ns_ctx->u.aio.events = calloc(g_queue_depth, sizeof(struct io_event));
     681         [ #  # ]:          0 :         if (!ns_ctx->u.aio.events) {
     682                 :          0 :                 return -1;
     683                 :            :         }
     684                 :          0 :         ns_ctx->u.aio.ctx = 0;
     685         [ #  # ]:          0 :         if (io_setup(g_queue_depth, &ns_ctx->u.aio.ctx) < 0) {
     686                 :          0 :                 free(ns_ctx->u.aio.events);
     687                 :          0 :                 perror("io_setup");
     688                 :          0 :                 return -1;
     689                 :            :         }
     690                 :          0 :         return 0;
     691                 :            : }
     692                 :            : 
     693                 :            : static void
     694                 :          0 : aio_cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
     695                 :            : {
     696                 :          0 :         io_destroy(ns_ctx->u.aio.ctx);
     697                 :          0 :         free(ns_ctx->u.aio.events);
     698                 :          0 : }
     699                 :            : 
     700                 :            : static const struct ns_fn_table aio_fn_table = {
     701                 :            :         .setup_payload          = aio_setup_payload,
     702                 :            :         .submit_io              = aio_submit_io,
     703                 :            :         .check_io               = aio_check_io,
     704                 :            :         .verify_io              = aio_verify_io,
     705                 :            :         .init_ns_worker_ctx     = aio_init_ns_worker_ctx,
     706                 :            :         .cleanup_ns_worker_ctx  = aio_cleanup_ns_worker_ctx,
     707                 :            : };
     708                 :            : 
     709                 :            : #endif /* HAVE_LIBAIO */
     710                 :            : 
     711                 :            : #if defined(HAVE_LIBAIO) || defined(SPDK_CONFIG_URING)
     712                 :            : 
     713                 :            : static int
     714                 :          0 : register_file(const char *path)
     715                 :            : {
     716                 :            :         struct ns_entry *entry;
     717                 :            : 
     718                 :            :         int flags, fd;
     719                 :            :         uint64_t size;
     720                 :            :         uint32_t blklen;
     721                 :            : 
     722         [ #  # ]:          0 :         if (g_rw_percentage == 100) {
     723                 :          0 :                 flags = O_RDONLY;
     724         [ #  # ]:          0 :         } else if (g_rw_percentage == 0) {
     725                 :          0 :                 flags = O_WRONLY;
     726                 :            :         } else {
     727                 :          0 :                 flags = O_RDWR;
     728                 :            :         }
     729                 :            : 
     730                 :          0 :         flags |= O_DIRECT;
     731                 :            : 
     732         [ #  # ]:          0 :         fd = open(path, flags);
     733         [ #  # ]:          0 :         if (fd < 0) {
     734         [ #  # ]:          0 :                 fprintf(stderr, "Could not open device %s: %s\n", path, strerror(errno));
     735                 :          0 :                 return -1;
     736                 :            :         }
     737                 :            : 
     738                 :          0 :         size = spdk_fd_get_size(fd);
     739         [ #  # ]:          0 :         if (size == 0) {
     740         [ #  # ]:          0 :                 fprintf(stderr, "Could not determine size of device %s\n", path);
     741                 :          0 :                 close(fd);
     742                 :          0 :                 return -1;
     743                 :            :         }
     744                 :            : 
     745                 :          0 :         blklen = spdk_fd_get_blocklen(fd);
     746         [ #  # ]:          0 :         if (blklen == 0) {
     747         [ #  # ]:          0 :                 fprintf(stderr, "Could not determine block size of device %s\n", path);
     748                 :          0 :                 close(fd);
     749                 :          0 :                 return -1;
     750                 :            :         }
     751                 :            : 
     752                 :            :         /*
     753                 :            :          * TODO: This should really calculate the LCM of the current g_io_align and blklen.
     754                 :            :          * For now, it's fairly safe to just assume all block sizes are powers of 2.
     755                 :            :          */
     756         [ #  # ]:          0 :         if (g_io_align < blklen) {
     757   [ #  #  #  # ]:          0 :                 if (g_io_align_specified) {
     758         [ #  # ]:          0 :                         fprintf(stderr, "Wrong IO alignment (%u). aio requires block-sized alignment (%u)\n", g_io_align,
     759                 :            :                                 blklen);
     760                 :          0 :                         close(fd);
     761                 :          0 :                         return -1;
     762                 :            :                 }
     763                 :            : 
     764                 :          0 :                 g_io_align = blklen;
     765                 :            :         }
     766                 :            : 
     767                 :          0 :         entry = calloc(1, sizeof(struct ns_entry));
     768         [ #  # ]:          0 :         if (entry == NULL) {
     769                 :          0 :                 close(fd);
     770                 :          0 :                 perror("ns_entry malloc");
     771                 :          0 :                 return -1;
     772                 :            :         }
     773                 :            : 
     774   [ #  #  #  # ]:          0 :         if (g_use_uring) {
     775                 :            : #ifdef SPDK_CONFIG_URING
     776                 :          0 :                 entry->type = ENTRY_TYPE_URING_FILE;
     777                 :          0 :                 entry->fn_table = &uring_fn_table;
     778                 :          0 :                 entry->u.uring.fd = fd;
     779                 :            : #endif
     780                 :            :         } else {
     781                 :            : #if HAVE_LIBAIO
     782                 :          0 :                 entry->type = ENTRY_TYPE_AIO_FILE;
     783                 :          0 :                 entry->fn_table = &aio_fn_table;
     784                 :          0 :                 entry->u.aio.fd = fd;
     785                 :            : #endif
     786                 :            :         }
     787         [ #  # ]:          0 :         entry->size_in_ios = size / g_io_size_bytes;
     788         [ #  # ]:          0 :         entry->io_size_blocks = g_io_size_bytes / blklen;
     789                 :            : 
     790         [ #  # ]:          0 :         if (g_is_random) {
     791                 :          0 :                 entry->seed = rand();
     792         [ #  # ]:          0 :                 if (g_zipf_theta > 0) {
     793                 :          0 :                         entry->zipf = spdk_zipf_create(entry->size_in_ios, g_zipf_theta, 0);
     794                 :            :                 }
     795                 :            :         }
     796                 :            : 
     797                 :          0 :         snprintf(entry->name, sizeof(entry->name), "%s", path);
     798                 :            : 
     799                 :          0 :         g_num_namespaces++;
     800                 :          0 :         TAILQ_INSERT_TAIL(&g_namespaces, entry, link);
     801                 :            : 
     802                 :          0 :         return 0;
     803                 :            : }
     804                 :            : 
     805                 :            : static int
     806                 :        124 : register_files(int argc, char **argv)
     807                 :            : {
     808                 :            :         int i;
     809                 :            : 
     810                 :            :         /* Treat everything after the options as files for AIO/URING */
     811         [ -  + ]:        124 :         for (i = g_file_optind; i < argc; i++) {
     812         [ #  # ]:          0 :                 if (register_file(argv[i]) != 0) {
     813                 :          0 :                         return 1;
     814                 :            :                 }
     815                 :            :         }
     816                 :            : 
     817                 :        124 :         return 0;
     818                 :            : }
     819                 :            : #endif
     820                 :            : 
     821                 :            : static void io_complete(void *ctx, const struct spdk_nvme_cpl *cpl);
     822                 :            : 
     823                 :            : static void
     824                 :      12638 : nvme_setup_payload(struct perf_task *task, uint8_t pattern)
     825                 :            : {
     826                 :            :         uint32_t max_io_size_bytes, max_io_md_size;
     827                 :            :         void *buf;
     828                 :            :         int rc;
     829                 :            : 
     830                 :            :         /* maximum extended lba format size from all active namespace,
     831                 :            :          * it's same with g_io_size_bytes for namespace without metadata.
     832                 :            :          */
     833                 :      12638 :         max_io_size_bytes = g_io_size_bytes + g_max_io_md_size * g_max_io_size_blocks;
     834                 :      12638 :         buf = spdk_dma_zmalloc(max_io_size_bytes, g_io_align, NULL);
     835         [ -  + ]:      12638 :         if (buf == NULL) {
     836   [ #  #  #  # ]:          0 :                 fprintf(stderr, "task->buf spdk_dma_zmalloc failed\n");
     837                 :          0 :                 exit(1);
     838                 :            :         }
     839         [ -  + ]:      12638 :         memset(buf, pattern, max_io_size_bytes);
     840                 :            : 
     841                 :      12638 :         rc = nvme_perf_allocate_iovs(task, buf, max_io_size_bytes);
     842         [ -  + ]:      12638 :         if (rc < 0) {
     843   [ #  #  #  # ]:          0 :                 fprintf(stderr, "perf task failed to allocate iovs\n");
     844                 :          0 :                 spdk_dma_free(buf);
     845                 :          0 :                 exit(1);
     846                 :            :         }
     847                 :            : 
     848                 :      12638 :         max_io_md_size = g_max_io_md_size * g_max_io_size_blocks;
     849         [ +  + ]:      12638 :         if (max_io_md_size != 0) {
     850                 :       2112 :                 task->md_iov.iov_base = spdk_dma_zmalloc(max_io_md_size, g_io_align, NULL);
     851                 :       2112 :                 task->md_iov.iov_len = max_io_md_size;
     852         [ -  + ]:       2112 :                 if (task->md_iov.iov_base == NULL) {
     853   [ #  #  #  # ]:          0 :                         fprintf(stderr, "task->md_buf spdk_dma_zmalloc failed\n");
     854                 :          0 :                         spdk_dma_free(task->iovs[0].iov_base);
     855                 :          0 :                         free(task->iovs);
     856                 :          0 :                         exit(1);
     857                 :            :                 }
     858                 :            :         }
     859                 :      12638 : }
     860                 :            : 
     861                 :            : static int
     862                 :   13076836 : nvme_submit_io(struct perf_task *task, struct ns_worker_ctx *ns_ctx,
     863                 :            :                struct ns_entry *entry, uint64_t offset_in_ios)
     864                 :            : {
     865                 :            :         uint64_t lba;
     866                 :            :         int rc;
     867                 :            :         int qp_num;
     868                 :    2381475 :         struct spdk_dif_ctx_init_ext_opts dif_opts;
     869                 :            : 
     870                 :            :         enum dif_mode {
     871                 :            :                 DIF_MODE_NONE = 0,
     872                 :            :                 DIF_MODE_DIF = 1,
     873                 :            :                 DIF_MODE_DIX = 2,
     874                 :   13076836 :         }  mode = DIF_MODE_NONE;
     875                 :            : 
     876                 :   13076836 :         lba = offset_in_ios * entry->io_size_blocks;
     877                 :            : 
     878   [ +  +  +  - ]:   13076836 :         if (entry->md_size != 0 && !(entry->io_flags & SPDK_NVME_IO_FLAGS_PRACT)) {
     879   [ -  +  -  + ]:     135504 :                 if (entry->md_interleave) {
     880                 :          0 :                         mode = DIF_MODE_DIF;
     881                 :            :                 } else {
     882                 :     135504 :                         mode = DIF_MODE_DIX;
     883                 :            :                 }
     884                 :            :         }
     885                 :            : 
     886                 :   13076836 :         qp_num = ns_ctx->u.nvme.last_qpair;
     887                 :   13076836 :         ns_ctx->u.nvme.last_qpair++;
     888         [ +  + ]:   13076836 :         if (ns_ctx->u.nvme.last_qpair == ns_ctx->u.nvme.num_active_qpairs) {
     889                 :   13073454 :                 ns_ctx->u.nvme.last_qpair = 0;
     890                 :            :         }
     891                 :            : 
     892         [ +  + ]:   13076836 :         if (mode != DIF_MODE_NONE) {
     893                 :     135504 :                 dif_opts.size = SPDK_SIZEOF(&dif_opts, dif_pi_format);
     894                 :     135504 :                 dif_opts.dif_pi_format = SPDK_DIF_PI_FORMAT_16;
     895                 :     271008 :                 rc = spdk_dif_ctx_init(&task->dif_ctx, entry->block_size, entry->md_size,
     896   [ -  +  -  + ]:     135504 :                                        entry->md_interleave, entry->pi_loc,
     897                 :     135504 :                                        (enum spdk_dif_type)entry->pi_type, entry->io_flags,
     898                 :     135504 :                                        lba, 0xFFFF, (uint16_t)entry->io_size_blocks, 0, 0, &dif_opts);
     899         [ -  + ]:     135504 :                 if (rc != 0) {
     900   [ #  #  #  # ]:          0 :                         fprintf(stderr, "Initialization of DIF context failed\n");
     901                 :          0 :                         exit(1);
     902                 :            :                 }
     903                 :            :         }
     904                 :            : 
     905   [ +  +  +  + ]:   13076836 :         if (task->is_read) {
     906         [ +  + ]:    9843125 :                 if (task->iovcnt == 1) {
     907                 :   18036596 :                         return spdk_nvme_ns_cmd_read_with_md(entry->u.nvme.ns, ns_ctx->u.nvme.qpair[qp_num],
     908                 :    9827869 :                                                              task->iovs[0].iov_base, task->md_iov.iov_base,
     909                 :            :                                                              lba,
     910                 :            :                                                              entry->io_size_blocks, io_complete,
     911                 :            :                                                              task, entry->io_flags,
     912                 :    9827869 :                                                              task->dif_ctx.apptag_mask, task->dif_ctx.app_tag);
     913                 :            :                 } else {
     914                 :      15256 :                         return spdk_nvme_ns_cmd_readv_with_md(entry->u.nvme.ns, ns_ctx->u.nvme.qpair[qp_num],
     915                 :            :                                                               lba, entry->io_size_blocks,
     916                 :            :                                                               io_complete, task, entry->io_flags,
     917                 :            :                                                               nvme_perf_reset_sgl, nvme_perf_next_sge,
     918                 :            :                                                               task->md_iov.iov_base,
     919                 :      15256 :                                                               task->dif_ctx.apptag_mask, task->dif_ctx.app_tag);
     920                 :            :                 }
     921                 :            :         } else {
     922      [ -  +  + ]:    3233711 :                 switch (mode) {
     923                 :          0 :                 case DIF_MODE_DIF:
     924                 :          0 :                         rc = spdk_dif_generate(task->iovs, task->iovcnt, entry->io_size_blocks, &task->dif_ctx);
     925         [ #  # ]:          0 :                         if (rc != 0) {
     926   [ #  #  #  # ]:          0 :                                 fprintf(stderr, "Generation of DIF failed\n");
     927                 :          0 :                                 return rc;
     928                 :            :                         }
     929                 :          0 :                         break;
     930                 :      11008 :                 case DIF_MODE_DIX:
     931                 :      11008 :                         rc = spdk_dix_generate(task->iovs, task->iovcnt, &task->md_iov, entry->io_size_blocks,
     932                 :      11008 :                                                &task->dif_ctx);
     933         [ -  + ]:      11008 :                         if (rc != 0) {
     934   [ #  #  #  # ]:          0 :                                 fprintf(stderr, "Generation of DIX failed\n");
     935                 :          0 :                                 return rc;
     936                 :            :                         }
     937                 :      11008 :                         break;
     938                 :    3222703 :                 default:
     939                 :    3222703 :                         break;
     940                 :            :                 }
     941                 :            : 
     942         [ +  + ]:    3233711 :                 if (task->iovcnt == 1) {
     943                 :    6266660 :                         return spdk_nvme_ns_cmd_write_with_md(entry->u.nvme.ns, ns_ctx->u.nvme.qpair[qp_num],
     944                 :    3218481 :                                                               task->iovs[0].iov_base, task->md_iov.iov_base,
     945                 :            :                                                               lba,
     946                 :            :                                                               entry->io_size_blocks, io_complete,
     947                 :            :                                                               task, entry->io_flags,
     948                 :    3218481 :                                                               task->dif_ctx.apptag_mask, task->dif_ctx.app_tag);
     949                 :            :                 } else {
     950                 :      15230 :                         return spdk_nvme_ns_cmd_writev_with_md(entry->u.nvme.ns, ns_ctx->u.nvme.qpair[qp_num],
     951                 :            :                                                                lba, entry->io_size_blocks,
     952                 :            :                                                                io_complete, task, entry->io_flags,
     953                 :            :                                                                nvme_perf_reset_sgl, nvme_perf_next_sge,
     954                 :            :                                                                task->md_iov.iov_base,
     955                 :      15230 :                                                                task->dif_ctx.apptag_mask, task->dif_ctx.app_tag);
     956                 :            :                 }
     957                 :            :         }
     958                 :            : }
     959                 :            : 
     960                 :            : static void
     961                 :    2020449 : perf_disconnect_cb(struct spdk_nvme_qpair *qpair, void *ctx)
     962                 :            : {
     963                 :    2020449 :         struct ns_worker_ctx *ns_ctx = ctx;
     964                 :            : 
     965                 :    2020449 :         ns_ctx->is_draining = true;
     966                 :    2020449 :         ns_ctx->status = 1;
     967                 :    2020449 : }
     968                 :            : 
     969                 :            : static int64_t
     970                 :  220393548 : nvme_check_io(struct ns_worker_ctx *ns_ctx)
     971                 :            : {
     972                 :            :         int64_t rc;
     973                 :            : 
     974                 :  220393548 :         rc = spdk_nvme_poll_group_process_completions(ns_ctx->u.nvme.group, g_max_completions,
     975                 :            :                         perf_disconnect_cb);
     976         [ +  + ]:  220393548 :         if (rc < 0) {
     977   [ -  +  -  + ]:          6 :                 fprintf(stderr, "NVMe io qpair process completion error\n");
     978                 :          6 :                 ns_ctx->status = 1;
     979                 :          6 :                 return -1;
     980                 :            :         }
     981                 :  220393542 :         return rc;
     982                 :            : }
     983                 :            : 
     984                 :            : static void
     985                 :     135504 : nvme_verify_io(struct perf_task *task, struct ns_entry *entry)
     986                 :            : {
     987                 :     135504 :         struct spdk_dif_error err_blk = {};
     988                 :            :         int rc;
     989                 :            : 
     990   [ -  +  +  +  :     135504 :         if (!task->is_read || (entry->io_flags & SPDK_NVME_IO_FLAGS_PRACT)) {
                   -  + ]
     991                 :      11008 :                 return;
     992                 :            :         }
     993                 :            : 
     994   [ -  +  -  + ]:     124496 :         if (entry->md_interleave) {
     995                 :          0 :                 rc = spdk_dif_verify(task->iovs, task->iovcnt, entry->io_size_blocks, &task->dif_ctx,
     996                 :            :                                      &err_blk);
     997         [ #  # ]:          0 :                 if (rc != 0) {
     998         [ #  # ]:          0 :                         fprintf(stderr, "DIF error detected. type=%d, offset=%" PRIu32 "\n",
     999         [ #  # ]:          0 :                                 err_blk.err_type, err_blk.err_offset);
    1000                 :            :                 }
    1001                 :            :         } else {
    1002                 :     124496 :                 rc = spdk_dix_verify(task->iovs, task->iovcnt, &task->md_iov, entry->io_size_blocks,
    1003                 :     124496 :                                      &task->dif_ctx, &err_blk);
    1004         [ -  + ]:     124496 :                 if (rc != 0) {
    1005         [ #  # ]:          0 :                         fprintf(stderr, "DIX error detected. type=%d, offset=%" PRIu32 "\n",
    1006         [ #  # ]:          0 :                                 err_blk.err_type, err_blk.err_offset);
    1007                 :            :                 }
    1008                 :            :         }
    1009                 :            : }
    1010                 :            : 
    1011                 :            : /*
    1012                 :            :  * TODO: If a controller has multiple namespaces, they could all use the same queue.
    1013                 :            :  *  For now, give each namespace/thread combination its own queue.
    1014                 :            :  */
    1015                 :            : static int
    1016                 :        199 : nvme_init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
    1017                 :            : {
    1018                 :            :         const struct spdk_nvme_ctrlr_opts *ctrlr_opts;
    1019                 :         64 :         struct spdk_nvme_io_qpair_opts opts;
    1020                 :        199 :         struct ns_entry *entry = ns_ctx->entry;
    1021                 :            :         struct spdk_nvme_poll_group *group;
    1022                 :            :         struct spdk_nvme_qpair *qpair;
    1023                 :            :         uint64_t poll_timeout_tsc;
    1024                 :            :         int i, rc;
    1025                 :            : 
    1026                 :        199 :         ns_ctx->u.nvme.num_active_qpairs = g_nr_io_queues_per_ns;
    1027                 :        199 :         ns_ctx->u.nvme.num_all_qpairs = g_nr_io_queues_per_ns + g_nr_unused_io_queues;
    1028                 :        199 :         ns_ctx->u.nvme.qpair = calloc(ns_ctx->u.nvme.num_all_qpairs, sizeof(struct spdk_nvme_qpair *));
    1029         [ -  + ]:        199 :         if (!ns_ctx->u.nvme.qpair) {
    1030                 :          0 :                 return -1;
    1031                 :            :         }
    1032                 :            : 
    1033                 :        199 :         spdk_nvme_ctrlr_get_default_io_qpair_opts(entry->u.nvme.ctrlr, &opts, sizeof(opts));
    1034         [ -  + ]:        199 :         if (opts.io_queue_requests < entry->num_io_requests) {
    1035                 :          0 :                 opts.io_queue_requests = entry->num_io_requests;
    1036                 :            :         }
    1037                 :        199 :         opts.delay_cmd_submit = true;
    1038                 :        199 :         opts.create_only = true;
    1039                 :            : 
    1040                 :        199 :         ctrlr_opts = spdk_nvme_ctrlr_get_opts(entry->u.nvme.ctrlr);
    1041         [ +  + ]:        299 :         opts.async_mode = !(spdk_nvme_ctrlr_get_transport_id(entry->u.nvme.ctrlr)->trtype ==
    1042                 :            :                             SPDK_NVME_TRANSPORT_PCIE
    1043         [ +  - ]:        100 :                             && ns_ctx->u.nvme.num_all_qpairs > ctrlr_opts->admin_queue_size);
    1044                 :            : 
    1045                 :        199 :         ns_ctx->u.nvme.group = spdk_nvme_poll_group_create(ns_ctx, NULL);
    1046         [ -  + ]:        199 :         if (ns_ctx->u.nvme.group == NULL) {
    1047                 :          0 :                 goto poll_group_failed;
    1048                 :            :         }
    1049                 :            : 
    1050                 :        199 :         group = ns_ctx->u.nvme.group;
    1051         [ +  + ]:        434 :         for (i = 0; i < ns_ctx->u.nvme.num_all_qpairs; i++) {
    1052                 :        235 :                 ns_ctx->u.nvme.qpair[i] = spdk_nvme_ctrlr_alloc_io_qpair(entry->u.nvme.ctrlr, &opts,
    1053                 :            :                                           sizeof(opts));
    1054                 :        235 :                 qpair = ns_ctx->u.nvme.qpair[i];
    1055         [ -  + ]:        235 :                 if (!qpair) {
    1056         [ #  # ]:          0 :                         printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair failed\n");
    1057                 :          0 :                         goto qpair_failed;
    1058                 :            :                 }
    1059                 :            : 
    1060         [ -  + ]:        235 :                 if (spdk_nvme_poll_group_add(group, qpair)) {
    1061         [ #  # ]:          0 :                         printf("ERROR: unable to add I/O qpair to poll group.\n");
    1062                 :          0 :                         spdk_nvme_ctrlr_free_io_qpair(qpair);
    1063                 :          0 :                         goto qpair_failed;
    1064                 :            :                 }
    1065                 :            : 
    1066         [ -  + ]:        235 :                 if (spdk_nvme_ctrlr_connect_io_qpair(entry->u.nvme.ctrlr, qpair)) {
    1067         [ #  # ]:          0 :                         printf("ERROR: unable to connect I/O qpair.\n");
    1068                 :          0 :                         spdk_nvme_ctrlr_free_io_qpair(qpair);
    1069                 :          0 :                         goto qpair_failed;
    1070                 :            :                 }
    1071                 :            :         }
    1072                 :            : 
    1073                 :            :         /* Busy poll here until all qpairs are connected - this ensures once we start
    1074                 :            :          * I/O we aren't still waiting for some qpairs to connect. Limit the poll to
    1075                 :            :          * 10 seconds though.
    1076                 :            :          */
    1077                 :        199 :         poll_timeout_tsc = spdk_get_ticks() + 10 * spdk_get_ticks_hz();
    1078                 :        199 :         rc = -EAGAIN;
    1079   [ +  -  +  - ]:     370911 :         while (spdk_get_ticks() < poll_timeout_tsc && rc == -EAGAIN) {
    1080                 :     370911 :                 spdk_nvme_poll_group_process_completions(group, 0, perf_disconnect_cb);
    1081                 :     370911 :                 rc = spdk_nvme_poll_group_all_connected(group);
    1082         [ +  + ]:     370911 :                 if (rc == 0) {
    1083                 :        199 :                         return 0;
    1084                 :            :                 }
    1085                 :            :         }
    1086                 :            : 
    1087                 :            :         /* If we reach here, it means we either timed out, or some connection failed. */
    1088   [ #  #  #  # ]:          0 :         assert(spdk_get_ticks() > poll_timeout_tsc || rc == -EIO);
    1089                 :            : 
    1090                 :          0 : qpair_failed:
    1091         [ #  # ]:          0 :         for (; i > 0; --i) {
    1092                 :          0 :                 spdk_nvme_ctrlr_free_io_qpair(ns_ctx->u.nvme.qpair[i - 1]);
    1093                 :            :         }
    1094                 :            : 
    1095                 :          0 :         spdk_nvme_poll_group_destroy(ns_ctx->u.nvme.group);
    1096                 :          0 : poll_group_failed:
    1097                 :          0 :         free(ns_ctx->u.nvme.qpair);
    1098                 :          0 :         return -1;
    1099                 :            : }
    1100                 :            : 
    1101                 :            : static void
    1102                 :        199 : nvme_cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
    1103                 :            : {
    1104                 :            :         int i;
    1105                 :            : 
    1106         [ +  + ]:        434 :         for (i = 0; i < ns_ctx->u.nvme.num_all_qpairs; i++) {
    1107                 :        235 :                 spdk_nvme_ctrlr_free_io_qpair(ns_ctx->u.nvme.qpair[i]);
    1108                 :            :         }
    1109                 :            : 
    1110                 :        199 :         spdk_nvme_poll_group_destroy(ns_ctx->u.nvme.group);
    1111                 :        199 :         free(ns_ctx->u.nvme.qpair);
    1112                 :        199 : }
    1113                 :            : 
    1114                 :            : static void
    1115                 :          2 : nvme_dump_rdma_statistics(struct spdk_nvme_transport_poll_group_stat *stat)
    1116                 :            : {
    1117                 :            :         struct spdk_nvme_rdma_device_stat *device_stats;
    1118                 :            :         uint32_t i;
    1119                 :            : 
    1120         [ -  + ]:          2 :         printf("RDMA transport:\n");
    1121         [ +  + ]:          4 :         for (i = 0; i < stat->rdma.num_devices; i++) {
    1122                 :          2 :                 device_stats = &stat->rdma.device_stats[i];
    1123         [ -  + ]:          2 :                 printf("\tdev name:              %s\n", device_stats->name);
    1124         [ -  + ]:          2 :                 printf("\tpolls:                 %"PRIu64"\n", device_stats->polls);
    1125         [ -  + ]:          2 :                 printf("\tidle_polls:            %"PRIu64"\n", device_stats->idle_polls);
    1126         [ -  + ]:          2 :                 printf("\tcompletions:           %"PRIu64"\n", device_stats->completions);
    1127         [ -  + ]:          2 :                 printf("\tqueued_requests:       %"PRIu64"\n", device_stats->queued_requests);
    1128         [ -  + ]:          2 :                 printf("\ttotal_send_wrs:        %"PRIu64"\n", device_stats->total_send_wrs);
    1129         [ -  + ]:          2 :                 printf("\tsend_doorbell_updates: %"PRIu64"\n", device_stats->send_doorbell_updates);
    1130         [ -  + ]:          2 :                 printf("\ttotal_recv_wrs:        %"PRIu64"\n", device_stats->total_recv_wrs);
    1131         [ -  + ]:          2 :                 printf("\trecv_doorbell_updates: %"PRIu64"\n", device_stats->recv_doorbell_updates);
    1132         [ -  + ]:          2 :                 printf("\t---------------------------------\n");
    1133                 :            :         }
    1134                 :          2 : }
    1135                 :            : 
    1136                 :            : static void
    1137                 :          0 : nvme_dump_pcie_statistics(struct spdk_nvme_transport_poll_group_stat *stat)
    1138                 :            : {
    1139                 :            :         struct spdk_nvme_pcie_stat *pcie_stat;
    1140                 :            : 
    1141                 :          0 :         pcie_stat = &stat->pcie;
    1142                 :            : 
    1143         [ #  # ]:          0 :         printf("PCIE transport:\n");
    1144         [ #  # ]:          0 :         printf("\tpolls:               %"PRIu64"\n", pcie_stat->polls);
    1145         [ #  # ]:          0 :         printf("\tidle_polls:          %"PRIu64"\n", pcie_stat->idle_polls);
    1146         [ #  # ]:          0 :         printf("\tcompletions:         %"PRIu64"\n", pcie_stat->completions);
    1147         [ #  # ]:          0 :         printf("\tcq_mmio_doorbell_updates: %"PRIu64"\n", pcie_stat->cq_mmio_doorbell_updates);
    1148         [ #  # ]:          0 :         printf("\tcq_shadow_doorbell_updates: %"PRIu64"\n", pcie_stat->cq_shadow_doorbell_updates);
    1149         [ #  # ]:          0 :         printf("\tsubmitted_requests:  %"PRIu64"\n", pcie_stat->submitted_requests);
    1150         [ #  # ]:          0 :         printf("\tsq_mmio_doorbell_updates:  %"PRIu64"\n", pcie_stat->sq_mmio_doorbell_updates);
    1151         [ #  # ]:          0 :         printf("\tsq_shadow_doorbell_updates:  %"PRIu64"\n", pcie_stat->sq_shadow_doorbell_updates);
    1152         [ #  # ]:          0 :         printf("\tqueued_requests:     %"PRIu64"\n", pcie_stat->queued_requests);
    1153                 :          0 : }
    1154                 :            : 
    1155                 :            : static void
    1156                 :          6 : nvme_dump_tcp_statistics(struct spdk_nvme_transport_poll_group_stat *stat)
    1157                 :            : {
    1158                 :            :         struct spdk_nvme_tcp_stat *tcp_stat;
    1159                 :            : 
    1160                 :          6 :         tcp_stat = &stat->tcp;
    1161                 :            : 
    1162         [ -  + ]:          6 :         printf("TCP transport:\n");
    1163         [ -  + ]:          6 :         printf("\tpolls:              %"PRIu64"\n", tcp_stat->polls);
    1164         [ -  + ]:          6 :         printf("\tidle_polls:         %"PRIu64"\n", tcp_stat->idle_polls);
    1165         [ -  + ]:          6 :         printf("\tsock_completions:   %"PRIu64"\n", tcp_stat->socket_completions);
    1166         [ -  + ]:          6 :         printf("\tnvme_completions:   %"PRIu64"\n", tcp_stat->nvme_completions);
    1167         [ -  + ]:          6 :         printf("\tsubmitted_requests: %"PRIu64"\n", tcp_stat->submitted_requests);
    1168         [ -  + ]:          6 :         printf("\tqueued_requests:    %"PRIu64"\n", tcp_stat->queued_requests);
    1169                 :          6 : }
    1170                 :            : 
    1171                 :            : static void
    1172                 :          8 : nvme_dump_transport_stats(uint32_t lcore, struct ns_worker_ctx *ns_ctx)
    1173                 :            : {
    1174                 :            :         struct spdk_nvme_poll_group *group;
    1175                 :          8 :         struct spdk_nvme_poll_group_stat *stat = NULL;
    1176                 :            :         uint32_t i;
    1177                 :            :         int rc;
    1178                 :            : 
    1179                 :          8 :         group = ns_ctx->u.nvme.group;
    1180         [ -  + ]:          8 :         if (group == NULL) {
    1181                 :          0 :                 return;
    1182                 :            :         }
    1183                 :            : 
    1184                 :          8 :         rc = spdk_nvme_poll_group_get_stats(group, &stat);
    1185         [ -  + ]:          8 :         if (rc) {
    1186   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Can't get transport stats, error %d\n", rc);
    1187                 :          0 :                 return;
    1188                 :            :         }
    1189                 :            : 
    1190         [ -  + ]:          8 :         printf("\n====================\n");
    1191         [ -  + ]:          8 :         printf("lcore %u, ns %s statistics:\n", lcore, ns_ctx->entry->name);
    1192                 :            : 
    1193         [ +  + ]:         16 :         for (i = 0; i < stat->num_transports; i++) {
    1194   [ +  -  +  - ]:          8 :                 switch (stat->transport_stat[i]->trtype) {
    1195                 :          2 :                 case SPDK_NVME_TRANSPORT_RDMA:
    1196                 :          2 :                         nvme_dump_rdma_statistics(stat->transport_stat[i]);
    1197                 :          2 :                         break;
    1198                 :          0 :                 case SPDK_NVME_TRANSPORT_PCIE:
    1199                 :          0 :                         nvme_dump_pcie_statistics(stat->transport_stat[i]);
    1200                 :          0 :                         break;
    1201                 :          6 :                 case SPDK_NVME_TRANSPORT_TCP:
    1202                 :          6 :                         nvme_dump_tcp_statistics(stat->transport_stat[i]);
    1203                 :          6 :                         break;
    1204                 :          0 :                 default:
    1205   [ #  #  #  # ]:          0 :                         fprintf(stderr, "Unknown transport statistics %d %s\n", stat->transport_stat[i]->trtype,
    1206                 :          0 :                                 spdk_nvme_transport_id_trtype_str(stat->transport_stat[i]->trtype));
    1207                 :            :                 }
    1208                 :            :         }
    1209                 :            : 
    1210                 :          8 :         spdk_nvme_poll_group_free_stats(group, stat);
    1211                 :            : }
    1212                 :            : 
    1213                 :            : static const struct ns_fn_table nvme_fn_table = {
    1214                 :            :         .setup_payload          = nvme_setup_payload,
    1215                 :            :         .submit_io              = nvme_submit_io,
    1216                 :            :         .check_io               = nvme_check_io,
    1217                 :            :         .verify_io              = nvme_verify_io,
    1218                 :            :         .init_ns_worker_ctx     = nvme_init_ns_worker_ctx,
    1219                 :            :         .cleanup_ns_worker_ctx  = nvme_cleanup_ns_worker_ctx,
    1220                 :            :         .dump_transport_stats   = nvme_dump_transport_stats
    1221                 :            : };
    1222                 :            : 
    1223                 :            : static int
    1224                 :        337 : build_nvme_name(char *name, size_t length, struct spdk_nvme_ctrlr *ctrlr)
    1225                 :            : {
    1226                 :            :         const struct spdk_nvme_transport_id *trid;
    1227                 :        337 :         int res = 0;
    1228                 :            : 
    1229                 :        337 :         trid = spdk_nvme_ctrlr_get_transport_id(ctrlr);
    1230                 :            : 
    1231   [ +  +  +  +  :        337 :         switch (trid->trtype) {
                   -  - ]
    1232                 :        184 :         case SPDK_NVME_TRANSPORT_PCIE:
    1233         [ -  + ]:        184 :                 res = snprintf(name, length, "PCIE (%s)", trid->traddr);
    1234                 :        184 :                 break;
    1235                 :         38 :         case SPDK_NVME_TRANSPORT_RDMA:
    1236         [ -  + ]:         38 :                 res = snprintf(name, length, "RDMA (addr:%s subnqn:%s)", trid->traddr, trid->subnqn);
    1237                 :         38 :                 break;
    1238                 :        107 :         case SPDK_NVME_TRANSPORT_TCP:
    1239         [ -  + ]:        107 :                 res = snprintf(name, length, "TCP (addr:%s subnqn:%s)", trid->traddr, trid->subnqn);
    1240                 :        107 :                 break;
    1241                 :          8 :         case SPDK_NVME_TRANSPORT_VFIOUSER:
    1242         [ -  + ]:          8 :                 res = snprintf(name, length, "VFIOUSER (%s)", trid->traddr);
    1243                 :          8 :                 break;
    1244                 :          0 :         case SPDK_NVME_TRANSPORT_CUSTOM:
    1245         [ #  # ]:          0 :                 res = snprintf(name, length, "CUSTOM (%s)", trid->traddr);
    1246                 :          0 :                 break;
    1247                 :            : 
    1248                 :          0 :         default:
    1249   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Unknown transport type %d\n", trid->trtype);
    1250                 :          0 :                 break;
    1251                 :            :         }
    1252                 :        337 :         return res;
    1253                 :            : }
    1254                 :            : 
    1255                 :            : static void
    1256                 :        181 : build_nvme_ns_name(char *name, size_t length, struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid)
    1257                 :            : {
    1258                 :        181 :         int res = 0;
    1259                 :            : 
    1260                 :        181 :         res = build_nvme_name(name, length, ctrlr);
    1261         [ +  - ]:        181 :         if (res > 0) {
    1262         [ -  + ]:        181 :                 snprintf(name + res, length - res, " NSID %u", nsid);
    1263                 :            :         }
    1264                 :            : 
    1265                 :        181 : }
    1266                 :            : 
    1267                 :            : static void
    1268                 :        195 : register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
    1269                 :            : {
    1270                 :            :         struct ns_entry *entry;
    1271                 :            :         const struct spdk_nvme_ctrlr_data *cdata;
    1272                 :            :         uint32_t max_xfer_size, entries, sector_size;
    1273                 :            :         uint64_t ns_size;
    1274                 :         64 :         struct spdk_nvme_io_qpair_opts opts;
    1275                 :            : 
    1276                 :        195 :         cdata = spdk_nvme_ctrlr_get_data(ctrlr);
    1277                 :            : 
    1278         [ -  + ]:        195 :         if (!spdk_nvme_ns_is_active(ns)) {
    1279                 :          0 :                 printf("Controller %-20.20s (%-20.20s): Skipping inactive NS %u\n",
    1280         [ #  # ]:          0 :                        cdata->mn, cdata->sn,
    1281                 :            :                        spdk_nvme_ns_get_id(ns));
    1282                 :          0 :                 g_warn = true;
    1283                 :         14 :                 return;
    1284                 :            :         }
    1285                 :            : 
    1286                 :        195 :         ns_size = spdk_nvme_ns_get_size(ns);
    1287                 :        195 :         sector_size = spdk_nvme_ns_get_sector_size(ns);
    1288                 :            : 
    1289   [ +  -  +  + ]:        195 :         if (ns_size < g_io_size_bytes || sector_size > g_io_size_bytes) {
    1290                 :         12 :                 printf("WARNING: controller %-20.20s (%-20.20s) ns %u has invalid "
    1291                 :            :                        "ns size %" PRIu64 " / block size %u for I/O size %u\n",
    1292         [ -  + ]:          6 :                        cdata->mn, cdata->sn, spdk_nvme_ns_get_id(ns),
    1293                 :            :                        ns_size, spdk_nvme_ns_get_sector_size(ns), g_io_size_bytes);
    1294                 :          6 :                 g_warn = true;
    1295                 :          6 :                 return;
    1296                 :            :         }
    1297                 :            : 
    1298                 :        189 :         max_xfer_size = spdk_nvme_ns_get_max_io_xfer_size(ns);
    1299                 :        189 :         spdk_nvme_ctrlr_get_default_io_qpair_opts(ctrlr, &opts, sizeof(opts));
    1300                 :            :         /* NVMe driver may add additional entries based on
    1301                 :            :          * stripe size and maximum transfer size, we assume
    1302                 :            :          * 1 more entry be used for stripe.
    1303                 :            :          */
    1304         [ -  + ]:        189 :         entries = (g_io_size_bytes - 1) / max_xfer_size + 2;
    1305         [ +  + ]:        189 :         if ((g_queue_depth * entries) > opts.io_queue_size) {
    1306         [ -  + ]:         46 :                 printf("Controller IO queue size %u, less than required.\n",
    1307                 :            :                        opts.io_queue_size);
    1308         [ -  + ]:         46 :                 printf("Consider using lower queue depth or smaller IO size, because "
    1309                 :            :                        "IO requests may be queued at the NVMe driver.\n");
    1310                 :            :         }
    1311                 :            :         /* For requests which have children requests, parent request itself
    1312                 :            :          * will also occupy 1 entry.
    1313                 :            :          */
    1314                 :        189 :         entries += 1;
    1315                 :            : 
    1316                 :        189 :         entry = calloc(1, sizeof(struct ns_entry));
    1317         [ -  + ]:        189 :         if (entry == NULL) {
    1318                 :          0 :                 perror("ns_entry malloc");
    1319                 :          0 :                 exit(1);
    1320                 :            :         }
    1321                 :            : 
    1322                 :        189 :         entry->type = ENTRY_TYPE_NVME_NS;
    1323                 :        189 :         entry->fn_table = &nvme_fn_table;
    1324                 :        189 :         entry->u.nvme.ctrlr = ctrlr;
    1325                 :        189 :         entry->u.nvme.ns = ns;
    1326                 :        189 :         entry->num_io_requests = entries * spdk_divide_round_up(g_queue_depth, g_nr_io_queues_per_ns);
    1327                 :            : 
    1328         [ -  + ]:        189 :         entry->size_in_ios = ns_size / g_io_size_bytes;
    1329         [ -  + ]:        189 :         entry->io_size_blocks = g_io_size_bytes / sector_size;
    1330                 :            : 
    1331         [ +  + ]:        189 :         if (g_is_random) {
    1332                 :         88 :                 entry->seed = rand();
    1333         [ -  + ]:         88 :                 if (g_zipf_theta > 0) {
    1334                 :          0 :                         entry->zipf = spdk_zipf_create(entry->size_in_ios, g_zipf_theta, 0);
    1335                 :            :                 }
    1336                 :            :         }
    1337                 :            : 
    1338                 :        189 :         entry->block_size = spdk_nvme_ns_get_extended_sector_size(ns);
    1339                 :        189 :         entry->md_size = spdk_nvme_ns_get_md_size(ns);
    1340                 :        189 :         entry->md_interleave = spdk_nvme_ns_supports_extended_lba(ns);
    1341                 :        189 :         entry->pi_loc = spdk_nvme_ns_get_data(ns)->dps.md_start;
    1342                 :        189 :         entry->pi_type = spdk_nvme_ns_get_pi_type(ns);
    1343                 :            : 
    1344         [ -  + ]:        189 :         if (spdk_nvme_ns_get_flags(ns) & SPDK_NVME_NS_DPS_PI_SUPPORTED) {
    1345                 :          0 :                 entry->io_flags = g_metacfg_pract_flag | g_metacfg_prchk_flags;
    1346                 :            :         }
    1347                 :            : 
    1348                 :            :         /* If metadata size = 8 bytes, PI is stripped (read) or inserted (write),
    1349                 :            :          *  and so reduce metadata size from block size.  (If metadata size > 8 bytes,
    1350                 :            :          *  PI is passed (read) or replaced (write).  So block size is not necessary
    1351                 :            :          *  to change.)
    1352                 :            :          */
    1353   [ -  +  -  - ]:        189 :         if ((entry->io_flags & SPDK_NVME_IO_FLAGS_PRACT) && (entry->md_size == 8)) {
    1354                 :          0 :                 entry->block_size = spdk_nvme_ns_get_sector_size(ns);
    1355                 :            :         }
    1356                 :            : 
    1357   [ -  +  +  + ]:        189 :         if (g_io_size_bytes % entry->block_size != 0) {
    1358         [ -  + ]:          8 :                 printf("WARNING: IO size %u (-o) is not a multiple of nsid %u sector size %u."
    1359                 :            :                        " Removing this ns from test\n", g_io_size_bytes, spdk_nvme_ns_get_id(ns), entry->block_size);
    1360                 :          8 :                 g_warn = true;
    1361                 :          8 :                 spdk_zipf_free(&entry->zipf);
    1362                 :          8 :                 free(entry);
    1363                 :          8 :                 return;
    1364                 :            :         }
    1365                 :            : 
    1366         [ +  + ]:        181 :         if (g_max_io_md_size < entry->md_size) {
    1367                 :          8 :                 g_max_io_md_size = entry->md_size;
    1368                 :            :         }
    1369                 :            : 
    1370         [ +  + ]:        181 :         if (g_max_io_size_blocks < entry->io_size_blocks) {
    1371                 :        114 :                 g_max_io_size_blocks = entry->io_size_blocks;
    1372                 :            :         }
    1373                 :            : 
    1374                 :        181 :         build_nvme_ns_name(entry->name, sizeof(entry->name), ctrlr, spdk_nvme_ns_get_id(ns));
    1375                 :            : 
    1376                 :        181 :         g_num_namespaces++;
    1377                 :        181 :         TAILQ_INSERT_TAIL(&g_namespaces, entry, link);
    1378                 :            : }
    1379                 :            : 
    1380                 :            : static void
    1381                 :        124 : unregister_namespaces(void)
    1382                 :            : {
    1383                 :            :         struct ns_entry *entry, *tmp;
    1384                 :            : 
    1385         [ +  + ]:        305 :         TAILQ_FOREACH_SAFE(entry, &g_namespaces, link, tmp) {
    1386         [ +  + ]:        181 :                 TAILQ_REMOVE(&g_namespaces, entry, link);
    1387                 :        181 :                 spdk_zipf_free(&entry->zipf);
    1388   [ +  +  +  + ]:        181 :                 if (g_use_uring) {
    1389                 :            : #ifdef SPDK_CONFIG_URING
    1390                 :          0 :                         close(entry->u.uring.fd);
    1391                 :            : #endif
    1392                 :            :                 } else {
    1393                 :            : #if HAVE_LIBAIO
    1394                 :        181 :                         close(entry->u.aio.fd);
    1395                 :            : #endif
    1396                 :            :                 }
    1397                 :        181 :                 free(entry);
    1398                 :            :         }
    1399                 :        124 : }
    1400                 :            : 
    1401                 :            : static void
    1402                 :          0 : enable_latency_tracking_complete(void *cb_arg, const struct spdk_nvme_cpl *cpl)
    1403                 :            : {
    1404   [ #  #  #  # ]:          0 :         if (spdk_nvme_cpl_is_error(cpl)) {
    1405         [ #  # ]:          0 :                 printf("enable_latency_tracking_complete failed\n");
    1406                 :            :         }
    1407                 :          0 :         g_outstanding_commands--;
    1408                 :          0 : }
    1409                 :            : 
    1410                 :            : static void
    1411                 :          0 : set_latency_tracking_feature(struct spdk_nvme_ctrlr *ctrlr, bool enable)
    1412                 :            : {
    1413                 :            :         int res;
    1414                 :            :         union spdk_nvme_intel_feat_latency_tracking latency_tracking;
    1415                 :            : 
    1416         [ #  # ]:          0 :         if (enable) {
    1417                 :          0 :                 latency_tracking.bits.enable = 0x01;
    1418                 :            :         } else {
    1419                 :          0 :                 latency_tracking.bits.enable = 0x00;
    1420                 :            :         }
    1421                 :            : 
    1422                 :          0 :         res = spdk_nvme_ctrlr_cmd_set_feature(ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING,
    1423                 :            :                                               latency_tracking.raw, 0, NULL, 0, enable_latency_tracking_complete, NULL);
    1424         [ #  # ]:          0 :         if (res) {
    1425         [ #  # ]:          0 :                 printf("fail to allocate nvme request.\n");
    1426                 :          0 :                 return;
    1427                 :            :         }
    1428                 :          0 :         g_outstanding_commands++;
    1429                 :            : 
    1430         [ #  # ]:          0 :         while (g_outstanding_commands) {
    1431                 :          0 :                 spdk_nvme_ctrlr_process_admin_completions(ctrlr);
    1432                 :            :         }
    1433                 :            : }
    1434                 :            : 
    1435                 :            : static void
    1436                 :        156 : register_ctrlr(struct spdk_nvme_ctrlr *ctrlr, struct trid_entry *trid_entry)
    1437                 :            : {
    1438                 :            :         struct spdk_nvme_ns *ns;
    1439                 :        156 :         struct ctrlr_entry *entry = malloc(sizeof(struct ctrlr_entry));
    1440                 :            :         uint32_t nsid;
    1441                 :            : 
    1442         [ -  + ]:        156 :         if (entry == NULL) {
    1443                 :          0 :                 perror("ctrlr_entry malloc");
    1444                 :          0 :                 exit(1);
    1445                 :            :         }
    1446                 :            : 
    1447                 :        156 :         entry->latency_page = spdk_dma_zmalloc(sizeof(struct spdk_nvme_intel_rw_latency_page),
    1448                 :            :                                                4096, NULL);
    1449         [ -  + ]:        156 :         if (entry->latency_page == NULL) {
    1450         [ #  # ]:          0 :                 printf("Allocation error (latency page)\n");
    1451                 :          0 :                 exit(1);
    1452                 :            :         }
    1453                 :            : 
    1454                 :        156 :         build_nvme_name(entry->name, sizeof(entry->name), ctrlr);
    1455                 :            : 
    1456                 :        156 :         entry->ctrlr = ctrlr;
    1457                 :        156 :         entry->trtype = trid_entry->trid.trtype;
    1458                 :        156 :         TAILQ_INSERT_TAIL(&g_controllers, entry, link);
    1459                 :            : 
    1460   [ -  +  -  +  :        156 :         if (g_latency_ssd_tracking_enable &&
                   -  - ]
    1461                 :          0 :             spdk_nvme_ctrlr_is_feature_supported(ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING)) {
    1462                 :          0 :                 set_latency_tracking_feature(ctrlr, true);
    1463                 :            :         }
    1464                 :            : 
    1465         [ +  - ]:        156 :         if (trid_entry->nsid == 0) {
    1466         [ +  + ]:        164 :                 for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr);
    1467         [ +  + ]:        343 :                      nsid != 0; nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) {
    1468                 :        195 :                         ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
    1469         [ -  + ]:        195 :                         if (ns == NULL) {
    1470                 :          0 :                                 continue;
    1471                 :            :                         }
    1472                 :        195 :                         register_ns(ctrlr, ns);
    1473                 :            :                 }
    1474                 :            :         } else {
    1475                 :          0 :                 ns = spdk_nvme_ctrlr_get_ns(ctrlr, trid_entry->nsid);
    1476         [ #  # ]:          0 :                 if (!ns) {
    1477                 :          0 :                         perror("Namespace does not exist.");
    1478                 :          0 :                         exit(1);
    1479                 :            :                 }
    1480                 :            : 
    1481                 :          0 :                 register_ns(ctrlr, ns);
    1482                 :            :         }
    1483                 :        156 : }
    1484                 :            : 
    1485                 :            : static inline void
    1486                 :   13076836 : submit_single_io(struct perf_task *task)
    1487                 :            : {
    1488                 :            :         uint64_t                offset_in_ios;
    1489                 :            :         int                     rc;
    1490                 :   13076836 :         struct ns_worker_ctx    *ns_ctx = task->ns_ctx;
    1491                 :   13076836 :         struct ns_entry         *entry = ns_ctx->entry;
    1492                 :            : 
    1493   [ -  +  -  + ]:   13076836 :         assert(!ns_ctx->is_draining);
    1494                 :            : 
    1495         [ -  + ]:   13076836 :         if (entry->zipf) {
    1496                 :          0 :                 offset_in_ios = spdk_zipf_generate(entry->zipf);
    1497         [ +  + ]:   13076836 :         } else if (g_is_random) {
    1498         [ -  + ]:    6552368 :                 offset_in_ios = rand_r(&entry->seed) % entry->size_in_ios;
    1499                 :            :         } else {
    1500                 :    6524468 :                 offset_in_ios = ns_ctx->offset_in_ios++;
    1501         [ +  + ]:    6524468 :                 if (ns_ctx->offset_in_ios == entry->size_in_ios) {
    1502                 :         39 :                         ns_ctx->offset_in_ios = 0;
    1503                 :            :                 }
    1504                 :            :         }
    1505                 :            : 
    1506                 :   13076836 :         task->submit_tsc = spdk_get_ticks();
    1507                 :            : 
    1508         [ +  + ]:   13076836 :         if ((g_rw_percentage == 100) ||
    1509   [ +  +  +  + ]:    4461638 :             (g_rw_percentage != 0 && ((rand_r(&entry->seed) % 100) < g_rw_percentage))) {
    1510                 :    9843125 :                 task->is_read = true;
    1511                 :            :         } else {
    1512                 :    3233711 :                 task->is_read = false;
    1513                 :            :         }
    1514                 :            : 
    1515                 :   13076836 :         rc = entry->fn_table->submit_io(task, ns_ctx, entry, offset_in_ios);
    1516                 :            : 
    1517         [ +  + ]:   13076836 :         if (spdk_unlikely(rc != 0)) {
    1518   [ -  +  +  + ]:      50916 :                 if (g_continue_on_error) {
    1519                 :            :                         /* We can't just resubmit here or we can get in a loop that
    1520                 :            :                          * stack overflows. */
    1521                 :      50744 :                         TAILQ_INSERT_TAIL(&ns_ctx->queued_tasks, task, link);
    1522                 :            :                 } else {
    1523   [ -  +  +  -  :        172 :                         RATELIMIT_LOG("starting I/O failed: %d\n", rc);
          +  +  -  +  -  
                -  -  + ]
    1524                 :        172 :                         spdk_dma_free(task->iovs[0].iov_base);
    1525                 :        172 :                         free(task->iovs);
    1526                 :        172 :                         spdk_dma_free(task->md_iov.iov_base);
    1527                 :        172 :                         task->ns_ctx->status = 1;
    1528                 :        172 :                         free(task);
    1529                 :            :                 }
    1530                 :            :         } else {
    1531                 :   13025920 :                 ns_ctx->current_queue_depth++;
    1532                 :   13025920 :                 ns_ctx->stats.io_submitted++;
    1533                 :            :         }
    1534                 :            : 
    1535   [ -  +  -  - ]:   13076836 :         if (spdk_unlikely(g_number_ios && ns_ctx->stats.io_submitted >= g_number_ios)) {
    1536                 :          0 :                 ns_ctx->is_draining = true;
    1537                 :            :         }
    1538                 :   13076836 : }
    1539                 :            : 
    1540                 :            : static inline void
    1541                 :   13025920 : task_complete(struct perf_task *task)
    1542                 :            : {
    1543                 :            :         struct ns_worker_ctx    *ns_ctx;
    1544                 :            :         uint64_t                tsc_diff;
    1545                 :            :         struct ns_entry         *entry;
    1546                 :            : 
    1547                 :   13025920 :         ns_ctx = task->ns_ctx;
    1548                 :   13025920 :         entry = ns_ctx->entry;
    1549                 :   13025920 :         ns_ctx->current_queue_depth--;
    1550                 :   13025920 :         ns_ctx->stats.io_completed++;
    1551                 :   13025920 :         tsc_diff = spdk_get_ticks() - task->submit_tsc;
    1552                 :   13025920 :         ns_ctx->stats.total_tsc += tsc_diff;
    1553         [ +  + ]:   13025920 :         if (spdk_unlikely(ns_ctx->stats.min_tsc > tsc_diff)) {
    1554                 :       3478 :                 ns_ctx->stats.min_tsc = tsc_diff;
    1555                 :            :         }
    1556         [ +  + ]:   13025920 :         if (spdk_unlikely(ns_ctx->stats.max_tsc < tsc_diff)) {
    1557                 :      13286 :                 ns_ctx->stats.max_tsc = tsc_diff;
    1558                 :            :         }
    1559         [ +  + ]:   13025920 :         if (spdk_unlikely(g_latency_sw_tracking_level > 0)) {
    1560                 :     922067 :                 spdk_histogram_data_tally(ns_ctx->histogram, tsc_diff);
    1561                 :            :         }
    1562                 :            : 
    1563         [ +  + ]:   13025920 :         if (spdk_unlikely(entry->md_size > 0)) {
    1564                 :            :                 /* add application level verification for end-to-end data protection */
    1565                 :     135504 :                 entry->fn_table->verify_io(task, entry);
    1566                 :            :         }
    1567                 :            : 
    1568                 :            :         /*
    1569                 :            :          * is_draining indicates when time has expired or io_submitted exceeded
    1570                 :            :          * g_number_ios for the test run and we are just waiting for the previously
    1571                 :            :          * submitted I/O to complete. In this case, do not submit a new I/O to
    1572                 :            :          * replace the one just completed.
    1573                 :            :          */
    1574   [ +  +  +  + ]:   13025920 :         if (spdk_unlikely(ns_ctx->is_draining)) {
    1575                 :      12466 :                 spdk_dma_free(task->iovs[0].iov_base);
    1576                 :      12466 :                 free(task->iovs);
    1577                 :      12466 :                 spdk_dma_free(task->md_iov.iov_base);
    1578                 :      12466 :                 free(task);
    1579                 :            :         } else {
    1580                 :   13013454 :                 submit_single_io(task);
    1581                 :            :         }
    1582                 :   13025920 : }
    1583                 :            : 
    1584                 :            : static void
    1585                 :   13025920 : io_complete(void *ctx, const struct spdk_nvme_cpl *cpl)
    1586                 :            : {
    1587                 :   13025920 :         struct perf_task *task = ctx;
    1588                 :            : 
    1589   [ +  +  -  + ]:   13025920 :         if (spdk_unlikely(spdk_nvme_cpl_is_error(cpl))) {
    1590   [ -  +  +  + ]:     193176 :                 if (task->is_read) {
    1591   [ -  +  +  +  :     192933 :                         RATELIMIT_LOG("Read completed with error (sct=%d, sc=%d)\n",
          +  +  +  +  -  
                +  -  + ]
    1592                 :            :                                       cpl->status.sct, cpl->status.sc);
    1593                 :            :                 } else {
    1594   [ -  +  +  -  :        243 :                         RATELIMIT_LOG("Write completed with error (sct=%d, sc=%d)\n",
          +  +  -  +  -  
                -  -  + ]
    1595                 :            :                                       cpl->status.sct, cpl->status.sc);
    1596                 :            :                 }
    1597   [ -  +  +  + ]:     193176 :                 if (!g_continue_on_error) {
    1598         [ +  - ]:        890 :                         if (cpl->status.sct == SPDK_NVME_SCT_GENERIC &&
    1599         [ -  + ]:        890 :                             cpl->status.sc == SPDK_NVME_SC_INVALID_NAMESPACE_OR_FORMAT) {
    1600                 :            :                                 /* The namespace was hotplugged.  Stop trying to send I/O to it. */
    1601                 :          0 :                                 task->ns_ctx->is_draining = true;
    1602                 :            :                         }
    1603                 :            : 
    1604                 :        890 :                         task->ns_ctx->status = 1;
    1605                 :            :                 }
    1606                 :            :         }
    1607                 :            : 
    1608                 :   13025920 :         task_complete(task);
    1609                 :   13025920 : }
    1610                 :            : 
    1611                 :            : static struct perf_task *
    1612                 :      12638 : allocate_task(struct ns_worker_ctx *ns_ctx, int queue_depth)
    1613                 :            : {
    1614                 :            :         struct perf_task *task;
    1615                 :            : 
    1616                 :      12638 :         task = calloc(1, sizeof(*task));
    1617         [ -  + ]:      12638 :         if (task == NULL) {
    1618   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Out of memory allocating tasks\n");
    1619                 :          0 :                 exit(1);
    1620                 :            :         }
    1621                 :            : 
    1622                 :      12638 :         ns_ctx->entry->fn_table->setup_payload(task, queue_depth % 8 + 1);
    1623                 :            : 
    1624                 :      12638 :         task->ns_ctx = ns_ctx;
    1625                 :            : 
    1626                 :      12638 :         return task;
    1627                 :            : }
    1628                 :            : 
    1629                 :            : static void
    1630                 :        199 : submit_io(struct ns_worker_ctx *ns_ctx, int queue_depth)
    1631                 :            : {
    1632                 :            :         struct perf_task *task;
    1633                 :            : 
    1634         [ +  + ]:      12837 :         while (queue_depth-- > 0) {
    1635                 :      12638 :                 task = allocate_task(ns_ctx, queue_depth);
    1636                 :      12638 :                 submit_single_io(task);
    1637                 :            :         }
    1638                 :        199 : }
    1639                 :            : 
    1640                 :            : static int
    1641                 :        199 : init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
    1642                 :            : {
    1643                 :        199 :         TAILQ_INIT(&ns_ctx->queued_tasks);
    1644                 :        199 :         return ns_ctx->entry->fn_table->init_ns_worker_ctx(ns_ctx);
    1645                 :            : }
    1646                 :            : 
    1647                 :            : static void
    1648                 :        199 : cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
    1649                 :            : {
    1650                 :            :         struct perf_task *task, *ttask;
    1651                 :            : 
    1652         [ -  + ]:        199 :         TAILQ_FOREACH_SAFE(task, &ns_ctx->queued_tasks, link, ttask) {
    1653         [ #  # ]:          0 :                 TAILQ_REMOVE(&ns_ctx->queued_tasks, task, link);
    1654                 :          0 :                 task_complete(task);
    1655                 :            :         }
    1656                 :        199 :         ns_ctx->entry->fn_table->cleanup_ns_worker_ctx(ns_ctx);
    1657                 :        199 : }
    1658                 :            : 
    1659                 :            : static void
    1660                 :        659 : print_periodic_performance(bool warmup)
    1661                 :            : {
    1662                 :            :         uint64_t io_this_second;
    1663                 :            :         double mb_this_second;
    1664                 :            :         struct worker_thread *worker;
    1665                 :            :         struct ns_worker_ctx *ns_ctx;
    1666                 :            :         uint64_t busy_tsc;
    1667                 :            :         uint64_t idle_tsc;
    1668                 :        659 :         uint64_t core_busy_tsc = 0;
    1669                 :        659 :         uint64_t core_idle_tsc = 0;
    1670                 :        659 :         double core_busy_perc = 0;
    1671                 :            : 
    1672         [ +  - ]:        659 :         if (!isatty(STDOUT_FILENO)) {
    1673                 :            :                 /* Don't print periodic stats if output is not going
    1674                 :            :                  * to a terminal.
    1675                 :            :                  */
    1676                 :        659 :                 return;
    1677                 :            :         }
    1678                 :          0 :         io_this_second = 0;
    1679         [ #  # ]:          0 :         TAILQ_FOREACH(worker, &g_workers, link) {
    1680                 :          0 :                 busy_tsc = 0;
    1681                 :          0 :                 idle_tsc = 0;
    1682         [ #  # ]:          0 :                 TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    1683                 :          0 :                         io_this_second += ns_ctx->stats.io_completed - ns_ctx->stats.last_io_completed;
    1684                 :          0 :                         ns_ctx->stats.last_io_completed = ns_ctx->stats.io_completed;
    1685                 :            : 
    1686   [ #  #  #  # ]:          0 :                         if (g_monitor_perf_cores) {
    1687                 :          0 :                                 busy_tsc += ns_ctx->stats.busy_tsc - ns_ctx->stats.last_busy_tsc;
    1688                 :          0 :                                 idle_tsc += ns_ctx->stats.idle_tsc - ns_ctx->stats.last_idle_tsc;
    1689                 :          0 :                                 ns_ctx->stats.last_busy_tsc = ns_ctx->stats.busy_tsc;
    1690                 :          0 :                                 ns_ctx->stats.last_idle_tsc = ns_ctx->stats.idle_tsc;
    1691                 :            :                         }
    1692                 :            :                 }
    1693   [ #  #  #  # ]:          0 :                 if (g_monitor_perf_cores) {
    1694                 :          0 :                         core_busy_tsc += busy_tsc;
    1695                 :          0 :                         core_idle_tsc += idle_tsc;
    1696                 :            :                 }
    1697                 :            :         }
    1698                 :          0 :         mb_this_second = (double)io_this_second * g_io_size_bytes / (1024 * 1024);
    1699                 :            : 
    1700   [ #  #  #  # ]:          0 :         printf("%s%9ju IOPS, %8.2f MiB/s", warmup ? "[warmup] " : "", io_this_second, mb_this_second);
    1701   [ #  #  #  # ]:          0 :         if (g_monitor_perf_cores) {
    1702                 :          0 :                 core_busy_perc = (double)core_busy_tsc / (core_idle_tsc + core_busy_tsc) * 100;
    1703         [ #  # ]:          0 :                 printf("%3d Core(s): %6.2f%% Busy", g_num_workers, core_busy_perc);
    1704                 :            :         }
    1705                 :          0 :         printf("\r");
    1706                 :          0 :         fflush(stdout);
    1707                 :            : }
    1708                 :            : 
    1709                 :            : static void
    1710                 :          4 : perf_dump_transport_statistics(struct worker_thread *worker)
    1711                 :            : {
    1712                 :            :         struct ns_worker_ctx *ns_ctx;
    1713                 :            : 
    1714         [ +  + ]:         12 :         TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    1715         [ +  - ]:          8 :                 if (ns_ctx->entry->fn_table->dump_transport_stats) {
    1716                 :          8 :                         ns_ctx->entry->fn_table->dump_transport_stats(worker->lcore, ns_ctx);
    1717                 :            :                 }
    1718                 :            :         }
    1719                 :          4 : }
    1720                 :            : 
    1721                 :            : static int
    1722                 :        132 : work_fn(void *arg)
    1723                 :            : {
    1724                 :            :         uint64_t tsc_start, tsc_end, tsc_current, tsc_next_print;
    1725                 :        132 :         struct worker_thread *worker = (struct worker_thread *) arg;
    1726                 :        132 :         struct ns_worker_ctx *ns_ctx = NULL;
    1727                 :            :         uint32_t unfinished_ns_ctx;
    1728                 :        132 :         bool warmup = false;
    1729                 :            :         int rc;
    1730                 :            :         int64_t check_rc;
    1731                 :            :         uint64_t check_now;
    1732                 :         24 :         TAILQ_HEAD(, perf_task) swap;
    1733                 :            :         struct perf_task *task;
    1734                 :            : 
    1735                 :            :         /* Allocate queue pairs for each namespace. */
    1736         [ +  + ]:        331 :         TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    1737         [ -  + ]:        199 :                 if (init_ns_worker_ctx(ns_ctx) != 0) {
    1738                 :          0 :                         printf("ERROR: init_ns_worker_ctx() failed\n");
    1739                 :            :                         /* Wait on barrier to avoid blocking of successful workers */
    1740                 :          0 :                         pthread_barrier_wait(&g_worker_sync_barrier);
    1741                 :          0 :                         ns_ctx->status = 1;
    1742                 :          0 :                         return 1;
    1743                 :            :                 }
    1744                 :            :         }
    1745                 :            : 
    1746                 :        132 :         rc = pthread_barrier_wait(&g_worker_sync_barrier);
    1747   [ +  +  -  + ]:        132 :         if (rc != 0 && rc != PTHREAD_BARRIER_SERIAL_THREAD) {
    1748                 :          0 :                 printf("ERROR: failed to wait on thread sync barrier\n");
    1749                 :          0 :                 ns_ctx->status = 1;
    1750                 :          0 :                 return 1;
    1751                 :            :         }
    1752                 :            : 
    1753                 :        132 :         tsc_start = spdk_get_ticks();
    1754                 :        132 :         tsc_current = tsc_start;
    1755                 :        132 :         tsc_next_print = tsc_current + g_tsc_rate;
    1756                 :            : 
    1757         [ -  + ]:        132 :         if (g_warmup_time_in_sec) {
    1758                 :          0 :                 warmup = true;
    1759                 :          0 :                 tsc_end = tsc_current + g_warmup_time_in_sec * g_tsc_rate;
    1760                 :            :         } else {
    1761                 :        132 :                 tsc_end = tsc_current + g_time_in_sec * g_tsc_rate;
    1762                 :            :         }
    1763                 :            : 
    1764                 :            :         /* Submit initial I/O for each namespace. */
    1765         [ +  + ]:        331 :         TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    1766                 :        199 :                 submit_io(ns_ctx, g_queue_depth);
    1767                 :            :         }
    1768                 :            : 
    1769   [ +  +  +  - ]:  201899925 :         while (spdk_likely(!g_exit)) {
    1770                 :  201899925 :                 bool all_draining = true;
    1771                 :            : 
    1772                 :            :                 /*
    1773                 :            :                  * Check for completed I/O for each controller. A new
    1774                 :            :                  * I/O will be submitted in the io_complete callback
    1775                 :            :                  * to replace each I/O that is completed.
    1776                 :            :                  */
    1777         [ +  + ]:  420380581 :                 TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    1778   [ -  +  +  + ]:  218480696 :                         if (g_continue_on_error) {
    1779                 :            :                                 /* Submit any I/O that is queued up */
    1780                 :   28935954 :                                 TAILQ_INIT(&swap);
    1781   [ +  +  -  + ]:   28935954 :                                 TAILQ_SWAP(&swap, &ns_ctx->queued_tasks, perf_task, link);
    1782         [ +  + ]:   28986698 :                                 while (!TAILQ_EMPTY(&swap)) {
    1783                 :      50744 :                                         task = TAILQ_FIRST(&swap);
    1784         [ +  + ]:      50744 :                                         TAILQ_REMOVE(&swap, task, link);
    1785                 :      50744 :                                         submit_single_io(task);
    1786                 :            :                                 }
    1787                 :            :                         }
    1788                 :            : 
    1789                 :  218480696 :                         check_now = spdk_get_ticks();
    1790                 :  218480696 :                         check_rc = ns_ctx->entry->fn_table->check_io(ns_ctx);
    1791                 :            : 
    1792         [ +  + ]:  218480696 :                         if (check_rc > 0) {
    1793                 :    1944195 :                                 ns_ctx->stats.busy_tsc += check_now - ns_ctx->stats.last_tsc;
    1794                 :            :                         } else {
    1795                 :  216536503 :                                 ns_ctx->stats.idle_tsc += check_now - ns_ctx->stats.last_tsc;
    1796                 :            :                         }
    1797                 :  218480696 :                         ns_ctx->stats.last_tsc = check_now;
    1798                 :            : 
    1799   [ +  +  +  + ]:  218480696 :                         if (!ns_ctx->is_draining) {
    1800                 :  218480690 :                                 all_draining = false;
    1801                 :            :                         }
    1802                 :            :                 }
    1803                 :            : 
    1804         [ +  + ]:  201899925 :                 if (spdk_unlikely(all_draining)) {
    1805                 :          6 :                         break;
    1806                 :            :                 }
    1807                 :            : 
    1808                 :  201899919 :                 tsc_current = spdk_get_ticks();
    1809                 :            : 
    1810   [ +  +  +  + ]:  201899919 :                 if (worker->lcore == g_main_core && tsc_current > tsc_next_print) {
    1811                 :        659 :                         tsc_next_print += g_tsc_rate;
    1812                 :        659 :                         print_periodic_performance(warmup);
    1813                 :            :                 }
    1814                 :            : 
    1815         [ +  + ]:  201899919 :                 if (tsc_current > tsc_end) {
    1816         [ -  + ]:        126 :                         if (warmup) {
    1817                 :            :                                 /* Update test start and end time, clear statistics */
    1818                 :          0 :                                 tsc_start = spdk_get_ticks();
    1819                 :          0 :                                 tsc_end = tsc_start + g_time_in_sec * g_tsc_rate;
    1820                 :            : 
    1821         [ #  # ]:          0 :                                 TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    1822         [ #  # ]:          0 :                                         memset(&ns_ctx->stats, 0, sizeof(ns_ctx->stats));
    1823                 :          0 :                                         ns_ctx->stats.min_tsc = UINT64_MAX;
    1824                 :          0 :                                         spdk_histogram_data_reset(ns_ctx->histogram);
    1825                 :            :                                 }
    1826                 :            : 
    1827   [ #  #  #  # ]:          0 :                                 if (worker->lcore == g_main_core && isatty(STDOUT_FILENO)) {
    1828                 :            :                                         /* warmup stage prints a longer string to stdout, need to erase it */
    1829                 :          0 :                                         printf("%c[2K", 27);
    1830                 :            :                                 }
    1831                 :            : 
    1832                 :          0 :                                 warmup = false;
    1833                 :            :                         } else {
    1834                 :        126 :                                 break;
    1835                 :            :                         }
    1836                 :            :                 }
    1837                 :            :         }
    1838                 :            : 
    1839                 :            :         /* Capture the actual elapsed time when we break out of the main loop. This will account
    1840                 :            :          * for cases where we exit prematurely due to a signal. We only need to capture it on
    1841                 :            :          * one core, so use the main core.
    1842                 :            :          */
    1843         [ +  + ]:        132 :         if (worker->lcore == g_main_core) {
    1844         [ -  + ]:        114 :                 g_elapsed_time_in_usec = (tsc_current - tsc_start) * SPDK_SEC_TO_USEC / g_tsc_rate;
    1845                 :            :         }
    1846                 :            : 
    1847   [ -  +  +  + ]:        132 :         if (g_dump_transport_stats) {
    1848                 :          4 :                 pthread_mutex_lock(&g_stats_mutex);
    1849                 :          4 :                 perf_dump_transport_statistics(worker);
    1850                 :          4 :                 pthread_mutex_unlock(&g_stats_mutex);
    1851                 :            :         }
    1852                 :            : 
    1853                 :            :         /* drain the io of each ns_ctx in round robin to make the fairness */
    1854                 :            :         do {
    1855                 :    1899137 :                 unfinished_ns_ctx = 0;
    1856         [ +  + ]:    4508244 :                 TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    1857                 :            :                         /* first time will enter into this if case */
    1858   [ +  +  +  + ]:    2609107 :                         if (!ns_ctx->is_draining) {
    1859                 :        193 :                                 ns_ctx->is_draining = true;
    1860                 :            :                         }
    1861                 :            : 
    1862         [ +  + ]:    2609107 :                         if (ns_ctx->current_queue_depth > 0) {
    1863                 :    1912853 :                                 ns_ctx->entry->fn_table->check_io(ns_ctx);
    1864         [ +  + ]:    1912853 :                                 if (ns_ctx->current_queue_depth > 0) {
    1865                 :    1912654 :                                         unfinished_ns_ctx++;
    1866                 :            :                                 }
    1867                 :            :                         }
    1868                 :            :                 }
    1869         [ +  + ]:    1899137 :         } while (unfinished_ns_ctx > 0);
    1870                 :            : 
    1871         [ +  + ]:        331 :         TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    1872                 :        199 :                 cleanup_ns_worker_ctx(ns_ctx);
    1873                 :            :         }
    1874                 :            : 
    1875                 :        132 :         return 0;
    1876                 :            : }
    1877                 :            : 
    1878                 :            : static void
    1879                 :          0 : usage(char *program_name)
    1880                 :            : {
    1881         [ #  # ]:          0 :         printf("%s options", program_name);
    1882                 :            : #if defined(SPDK_CONFIG_URING) || defined(HAVE_LIBAIO)
    1883         [ #  # ]:          0 :         printf(" [Kernel device(s)]...");
    1884                 :            : #endif
    1885         [ #  # ]:          0 :         printf("\n\n");
    1886         [ #  # ]:          0 :         printf("==== BASIC OPTIONS ====\n\n");
    1887         [ #  # ]:          0 :         printf("\t-q, --io-depth <val> io depth\n");
    1888         [ #  # ]:          0 :         printf("\t-o, --io-size <val> io size in bytes\n");
    1889         [ #  # ]:          0 :         printf("\t-w, --io-pattern <pattern> io pattern type, must be one of\n");
    1890         [ #  # ]:          0 :         printf("\t\t(read, write, randread, randwrite, rw, randrw)\n");
    1891         [ #  # ]:          0 :         printf("\t-M, --rwmixread <0-100> rwmixread (100 for reads, 0 for writes)\n");
    1892         [ #  # ]:          0 :         printf("\t-t, --time <sec> time in seconds\n");
    1893         [ #  # ]:          0 :         printf("\t-a, --warmup-time <sec> warmup time in seconds\n");
    1894         [ #  # ]:          0 :         printf("\t-c, --core-mask <mask> core mask for I/O submission/completion.\n");
    1895         [ #  # ]:          0 :         printf("\t\t(default: 1)\n");
    1896         [ #  # ]:          0 :         printf("\t-r, --transport <fmt> Transport ID for local PCIe NVMe or NVMeoF\n");
    1897         [ #  # ]:          0 :         printf("\t\t Format: 'key:value [key:value] ...'\n");
    1898         [ #  # ]:          0 :         printf("\t\t Keys:\n");
    1899         [ #  # ]:          0 :         printf("\t\t  trtype      Transport type (e.g. PCIe, RDMA)\n");
    1900         [ #  # ]:          0 :         printf("\t\t  adrfam      Address family (e.g. IPv4, IPv6)\n");
    1901         [ #  # ]:          0 :         printf("\t\t  traddr      Transport address (e.g. 0000:04:00.0 for PCIe or 192.168.100.8 for RDMA)\n");
    1902         [ #  # ]:          0 :         printf("\t\t  trsvcid     Transport service identifier (e.g. 4420)\n");
    1903         [ #  # ]:          0 :         printf("\t\t  subnqn      Subsystem NQN (default: %s)\n", SPDK_NVMF_DISCOVERY_NQN);
    1904         [ #  # ]:          0 :         printf("\t\t  ns          NVMe namespace ID (all active namespaces are used by default)\n");
    1905         [ #  # ]:          0 :         printf("\t\t  hostnqn     Host NQN\n");
    1906         [ #  # ]:          0 :         printf("\t\t Example: -r 'trtype:PCIe traddr:0000:04:00.0' for PCIe or\n");
    1907         [ #  # ]:          0 :         printf("\t\t          -r 'trtype:RDMA adrfam:IPv4 traddr:192.168.100.8 trsvcid:4420' for NVMeoF\n");
    1908         [ #  # ]:          0 :         printf("\t\t Note: can be specified multiple times to test multiple disks/targets.\n");
    1909                 :          0 :         printf("\n");
    1910                 :            : 
    1911         [ #  # ]:          0 :         printf("==== ADVANCED OPTIONS ====\n\n");
    1912         [ #  # ]:          0 :         printf("\t--use-every-core for each namespace, I/Os are submitted from all cores\n");
    1913         [ #  # ]:          0 :         printf("\t--io-queue-size <val> size of NVMe IO queue. Default: maximum allowed by controller\n");
    1914         [ #  # ]:          0 :         printf("\t-O, --io-unit-size io unit size in bytes (4-byte aligned) for SPDK driver. default: same as io size\n");
    1915         [ #  # ]:          0 :         printf("\t-P, --num-qpairs <val> number of io queues per namespace. default: 1\n");
    1916         [ #  # ]:          0 :         printf("\t-U, --num-unused-qpairs <val> number of unused io queues per controller. default: 0\n");
    1917         [ #  # ]:          0 :         printf("\t-A, --buffer-alignment IO buffer alignment. Must be power of 2 and not less than cache line (%u)\n",
    1918                 :            :                SPDK_CACHE_LINE_SIZE);
    1919         [ #  # ]:          0 :         printf("\t-s, --hugemem-size <MB> DPDK huge memory size in MB.\n");
    1920         [ #  # ]:          0 :         printf("\t-g, --mem-single-seg use single file descriptor for DPDK memory segments\n");
    1921         [ #  # ]:          0 :         printf("\t-C, --max-completion-per-poll <val> max completions per poll\n");
    1922         [ #  # ]:          0 :         printf("\t\t(default: 0 - unlimited)\n");
    1923         [ #  # ]:          0 :         printf("\t-i, --shmem-grp-id <id> shared memory group ID\n");
    1924         [ #  # ]:          0 :         printf("\t-d, --number-ios <val> number of I/O to perform per thread on each namespace. Note: this is additional exit criteria.\n");
    1925         [ #  # ]:          0 :         printf("\t\t(default: 0 - unlimited)\n");
    1926         [ #  # ]:          0 :         printf("\t-e, --metadata <fmt> metadata configuration\n");
    1927         [ #  # ]:          0 :         printf("\t\t Keys:\n");
    1928         [ #  # ]:          0 :         printf("\t\t  PRACT      Protection Information Action bit (PRACT=1 or PRACT=0)\n");
    1929         [ #  # ]:          0 :         printf("\t\t  PRCHK      Control of Protection Information Checking (PRCHK=GUARD|REFTAG|APPTAG)\n");
    1930         [ #  # ]:          0 :         printf("\t\t Example: -e 'PRACT=0,PRCHK=GUARD|REFTAG|APPTAG'\n");
    1931         [ #  # ]:          0 :         printf("\t\t          -e 'PRACT=1,PRCHK=GUARD'\n");
    1932         [ #  # ]:          0 :         printf("\t-F, --zipf <theta> use zipf distribution for random I/O\n");
    1933                 :            : #ifdef SPDK_CONFIG_URING
    1934         [ #  # ]:          0 :         printf("\t-R, --enable-uring enable using liburing to drive kernel devices (Default: libaio)\n");
    1935                 :            : #endif
    1936         [ #  # ]:          0 :         printf("\t--iova-mode <mode> specify DPDK IOVA mode: va|pa\n");
    1937         [ #  # ]:          0 :         printf("\t--no-huge, SPDK is run without hugepages\n");
    1938                 :          0 :         printf("\n");
    1939                 :            : 
    1940         [ #  # ]:          0 :         printf("==== PCIe OPTIONS ====\n\n");
    1941         [ #  # ]:          0 :         printf("\t-b, --allowed-pci-addr <addr> allowed local PCIe device address\n");
    1942         [ #  # ]:          0 :         printf("\t\t Example: -b 0000:d8:00.0 -b 0000:d9:00.0\n");
    1943         [ #  # ]:          0 :         printf("\t-V, --enable-vmd enable VMD enumeration\n");
    1944         [ #  # ]:          0 :         printf("\t-D, --disable-sq-cmb disable submission queue in controller memory buffer, default: enabled\n");
    1945                 :          0 :         printf("\n");
    1946                 :            : 
    1947         [ #  # ]:          0 :         printf("==== TCP OPTIONS ====\n\n");
    1948         [ #  # ]:          0 :         printf("\t-S, --default-sock-impl <impl> set the default sock impl, e.g. \"posix\"\n");
    1949         [ #  # ]:          0 :         printf("\t--disable-ktls disable Kernel TLS. Only valid for ssl impl. Default for ssl impl\n");
    1950         [ #  # ]:          0 :         printf("\t--enable-ktls enable Kernel TLS. Only valid for ssl impl\n");
    1951         [ #  # ]:          0 :         printf("\t--tls-version <val> TLS version to use. Only valid for ssl impl. Default: 0 (auto-negotiation)\n");
    1952         [ #  # ]:          0 :         printf("\t--psk-path <val> Path to PSK file (only applies when sock_impl == ssl)\n");
    1953         [ #  # ]:          0 :         printf("\t--psk-identity <val> Default PSK ID, e.g. psk.spdk.io (only applies when sock_impl == ssl)\n");
    1954         [ #  # ]:          0 :         printf("\t--zerocopy-threshold <val> data is sent with MSG_ZEROCOPY if size is greater than this val. Default: 0 to disable it\n");
    1955         [ #  # ]:          0 :         printf("\t--zerocopy-threshold-sock-impl <impl> specify the sock implementation to set zerocopy_threshold\n");
    1956         [ #  # ]:          0 :         printf("\t-z, --disable-zcopy <impl> disable zero copy send for the given sock implementation. Default for posix impl\n");
    1957         [ #  # ]:          0 :         printf("\t-Z, --enable-zcopy <impl> enable zero copy send for the given sock implementation\n");
    1958         [ #  # ]:          0 :         printf("\t-k, --keepalive <ms> keep alive timeout period in millisecond\n");
    1959         [ #  # ]:          0 :         printf("\t-H, --enable-tcp-hdgst enable header digest for TCP transport, default: disabled\n");
    1960         [ #  # ]:          0 :         printf("\t-I, --enable-tcp-ddgst enable data digest for TCP transport, default: disabled\n");
    1961                 :          0 :         printf("\n");
    1962                 :            : 
    1963         [ #  # ]:          0 :         printf("==== RDMA OPTIONS ====\n\n");
    1964         [ #  # ]:          0 :         printf("\t--transport-tos <val> specify the type of service for RDMA transport. Default: 0 (disabled)\n");
    1965         [ #  # ]:          0 :         printf("\t--rdma-srq-size <val> The size of a shared rdma receive queue. Default: 0 (disabled)\n");
    1966         [ #  # ]:          0 :         printf("\t-k, --keepalive <ms> keep alive timeout period in millisecond\n");
    1967                 :          0 :         printf("\n");
    1968                 :            : 
    1969         [ #  # ]:          0 :         printf("==== LOGGING ====\n\n");
    1970         [ #  # ]:          0 :         printf("\t-L, --enable-sw-latency-tracking enable latency tracking via sw, default: disabled\n");
    1971         [ #  # ]:          0 :         printf("\t\t-L for latency summary, -LL for detailed histogram\n");
    1972         [ #  # ]:          0 :         printf("\t-l, --enable-ssd-latency-tracking enable latency tracking via ssd (if supported), default: disabled\n");
    1973         [ #  # ]:          0 :         printf("\t-N, --no-shst-notification no shutdown notification process for controllers, default: disabled\n");
    1974         [ #  # ]:          0 :         printf("\t-Q, --continue-on-error <val> Do not stop on error. Log I/O errors every N times (default: 1)\n");
    1975                 :          0 :         spdk_log_usage(stdout, "\t-T");
    1976         [ #  # ]:          0 :         printf("\t-m, --cpu-usage display real-time overall cpu usage on used cores\n");
    1977                 :            : #ifdef DEBUG
    1978         [ #  # ]:          0 :         printf("\t-G, --enable-debug enable debug logging\n");
    1979                 :            : #else
    1980                 :            :         printf("\t-G, --enable-debug enable debug logging (flag disabled, must reconfigure with --enable-debug)\n");
    1981                 :            : #endif
    1982         [ #  # ]:          0 :         printf("\t--transport-stats dump transport statistics\n");
    1983         [ #  # ]:          0 :         printf("\n\n");
    1984                 :          0 : }
    1985                 :            : 
    1986                 :            : static void
    1987                 :     178176 : check_cutoff(void *ctx, uint64_t start, uint64_t end, uint64_t count,
    1988                 :            :              uint64_t total, uint64_t so_far)
    1989                 :            : {
    1990                 :            :         double so_far_pct;
    1991                 :     178176 :         double **cutoff = ctx;
    1992                 :            : 
    1993         [ +  + ]:     178176 :         if (count == 0) {
    1994                 :     172593 :                 return;
    1995                 :            :         }
    1996                 :            : 
    1997                 :       5583 :         so_far_pct = (double)so_far / total;
    1998   [ +  +  +  + ]:       5943 :         while (so_far_pct >= **cutoff && **cutoff > 0) {
    1999         [ -  + ]:        360 :                 printf("%9.5f%% : %9.3fus\n", **cutoff * 100, (double)end * 1000 * 1000 / g_tsc_rate);
    2000                 :        360 :                 (*cutoff)++;
    2001                 :            :         }
    2002                 :            : }
    2003                 :            : 
    2004                 :            : static void
    2005                 :     178176 : print_bucket(void *ctx, uint64_t start, uint64_t end, uint64_t count,
    2006                 :            :              uint64_t total, uint64_t so_far)
    2007                 :            : {
    2008                 :            :         double so_far_pct;
    2009                 :            : 
    2010         [ +  + ]:     178176 :         if (count == 0) {
    2011                 :     172593 :                 return;
    2012                 :            :         }
    2013                 :            : 
    2014                 :       5583 :         so_far_pct = (double)so_far * 100 / total;
    2015                 :       5583 :         printf("%9.3f - %9.3f: %9.4f%%  (%9ju)\n",
    2016                 :       5583 :                (double)start * 1000 * 1000 / g_tsc_rate,
    2017         [ -  + ]:       5583 :                (double)end * 1000 * 1000 / g_tsc_rate,
    2018                 :            :                so_far_pct, count);
    2019                 :            : }
    2020                 :            : 
    2021                 :            : static void
    2022                 :        114 : print_performance(void)
    2023                 :            : {
    2024                 :            :         uint64_t total_io_completed, total_io_tsc;
    2025                 :            :         double io_per_second, mb_per_second, average_latency, min_latency, max_latency;
    2026                 :            :         double sum_ave_latency, min_latency_so_far, max_latency_so_far;
    2027                 :            :         double total_io_per_second, total_mb_per_second;
    2028                 :            :         int ns_count;
    2029                 :            :         struct worker_thread    *worker;
    2030                 :            :         struct ns_worker_ctx    *ns_ctx;
    2031                 :            :         uint32_t max_strlen;
    2032                 :            : 
    2033                 :        114 :         total_io_per_second = 0;
    2034                 :        114 :         total_mb_per_second = 0;
    2035                 :        114 :         total_io_completed = 0;
    2036                 :        114 :         total_io_tsc = 0;
    2037                 :        114 :         min_latency_so_far = (double)UINT64_MAX;
    2038                 :        114 :         max_latency_so_far = 0;
    2039                 :        114 :         ns_count = 0;
    2040                 :            : 
    2041                 :        114 :         max_strlen = 0;
    2042         [ +  + ]:        246 :         TAILQ_FOREACH(worker, &g_workers, link) {
    2043         [ +  + ]:        331 :                 TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    2044   [ +  +  +  +  :        199 :                         max_strlen = spdk_max(strlen(ns_ctx->entry->name), max_strlen);
                   -  + ]
    2045                 :            :                 }
    2046                 :            :         }
    2047                 :            : 
    2048                 :        114 :         printf("========================================================\n");
    2049                 :        114 :         printf("%*s\n", max_strlen + 60, "Latency(us)");
    2050                 :        114 :         printf("%-*s: %10s %10s %10s %10s %10s\n",
    2051                 :            :                max_strlen + 13, "Device Information", "IOPS", "MiB/s", "Average", "min", "max");
    2052                 :            : 
    2053         [ +  + ]:        246 :         TAILQ_FOREACH(worker, &g_workers, link) {
    2054         [ +  + ]:        331 :                 TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    2055         [ +  - ]:        199 :                         if (ns_ctx->stats.io_completed != 0) {
    2056                 :        199 :                                 io_per_second = (double)ns_ctx->stats.io_completed * 1000 * 1000 / g_elapsed_time_in_usec;
    2057                 :        199 :                                 mb_per_second = io_per_second * g_io_size_bytes / (1024 * 1024);
    2058                 :        199 :                                 average_latency = ((double)ns_ctx->stats.total_tsc / ns_ctx->stats.io_completed) * 1000 * 1000 /
    2059                 :            :                                                   g_tsc_rate;
    2060                 :        199 :                                 min_latency = (double)ns_ctx->stats.min_tsc * 1000 * 1000 / g_tsc_rate;
    2061         [ +  + ]:        199 :                                 if (min_latency < min_latency_so_far) {
    2062                 :        137 :                                         min_latency_so_far = min_latency;
    2063                 :            :                                 }
    2064                 :            : 
    2065                 :        199 :                                 max_latency = (double)ns_ctx->stats.max_tsc * 1000 * 1000 / g_tsc_rate;
    2066         [ +  + ]:        199 :                                 if (max_latency > max_latency_so_far) {
    2067                 :        153 :                                         max_latency_so_far = max_latency;
    2068                 :            :                                 }
    2069                 :            : 
    2070                 :        199 :                                 printf("%-*.*s from core %2u: %10.2f %10.2f %10.2f %10.2f %10.2f\n",
    2071                 :        199 :                                        max_strlen, max_strlen, ns_ctx->entry->name, worker->lcore,
    2072                 :            :                                        io_per_second, mb_per_second,
    2073                 :            :                                        average_latency, min_latency, max_latency);
    2074                 :        199 :                                 total_io_per_second += io_per_second;
    2075                 :        199 :                                 total_mb_per_second += mb_per_second;
    2076                 :        199 :                                 total_io_completed += ns_ctx->stats.io_completed;
    2077                 :        199 :                                 total_io_tsc += ns_ctx->stats.total_tsc;
    2078                 :        199 :                                 ns_count++;
    2079                 :            :                         }
    2080                 :            :                 }
    2081                 :            :         }
    2082                 :            : 
    2083   [ +  -  +  - ]:        114 :         if (ns_count != 0 && total_io_completed) {
    2084                 :        114 :                 sum_ave_latency = ((double)total_io_tsc / total_io_completed) * 1000 * 1000 / g_tsc_rate;
    2085                 :        114 :                 printf("========================================================\n");
    2086                 :        114 :                 printf("%-*s: %10.2f %10.2f %10.2f %10.2f %10.2f\n",
    2087                 :            :                        max_strlen + 13, "Total", total_io_per_second, total_mb_per_second,
    2088                 :            :                        sum_ave_latency, min_latency_so_far, max_latency_so_far);
    2089                 :        114 :                 printf("\n");
    2090                 :            :         }
    2091                 :            : 
    2092   [ +  +  -  + ]:        114 :         if (g_latency_sw_tracking_level == 0 || total_io_completed == 0) {
    2093                 :        102 :                 return;
    2094                 :            :         }
    2095                 :            : 
    2096         [ +  + ]:         24 :         TAILQ_FOREACH(worker, &g_workers, link) {
    2097         [ +  + ]:         36 :                 TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    2098                 :         24 :                         const double *cutoff = g_latency_cutoffs;
    2099                 :            : 
    2100                 :         24 :                         printf("Summary latency data for %-43.43s from core %u:\n", ns_ctx->entry->name, worker->lcore);
    2101                 :         24 :                         printf("=================================================================================\n");
    2102                 :            : 
    2103                 :         24 :                         spdk_histogram_data_iterate(ns_ctx->histogram, check_cutoff, &cutoff);
    2104                 :            : 
    2105                 :         24 :                         printf("\n");
    2106                 :            :                 }
    2107                 :            :         }
    2108                 :            : 
    2109         [ -  + ]:         12 :         if (g_latency_sw_tracking_level == 1) {
    2110                 :          0 :                 return;
    2111                 :            :         }
    2112                 :            : 
    2113         [ +  + ]:         24 :         TAILQ_FOREACH(worker, &g_workers, link) {
    2114         [ +  + ]:         36 :                 TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    2115                 :         24 :                         printf("Latency histogram for %-43.43s from core %u:\n", ns_ctx->entry->name, worker->lcore);
    2116                 :         24 :                         printf("==============================================================================\n");
    2117                 :         24 :                         printf("       Range in us     Cumulative    IO count\n");
    2118                 :            : 
    2119                 :         24 :                         spdk_histogram_data_iterate(ns_ctx->histogram, print_bucket, NULL);
    2120                 :         24 :                         printf("\n");
    2121                 :            :                 }
    2122                 :            :         }
    2123                 :            : 
    2124                 :            : }
    2125                 :            : 
    2126                 :            : static void
    2127                 :          0 : print_latency_page(struct ctrlr_entry *entry)
    2128                 :            : {
    2129                 :            :         int i;
    2130                 :            : 
    2131                 :          0 :         printf("\n");
    2132         [ #  # ]:          0 :         printf("%s\n", entry->name);
    2133         [ #  # ]:          0 :         printf("--------------------------------------------------------\n");
    2134                 :            : 
    2135         [ #  # ]:          0 :         for (i = 0; i < 32; i++) {
    2136         [ #  # ]:          0 :                 if (entry->latency_page->buckets_32us[i]) {
    2137         [ #  # ]:          0 :                         printf("Bucket %dus - %dus: %d\n", i * 32, (i + 1) * 32, entry->latency_page->buckets_32us[i]);
    2138                 :            :                 }
    2139                 :            :         }
    2140         [ #  # ]:          0 :         for (i = 0; i < 31; i++) {
    2141         [ #  # ]:          0 :                 if (entry->latency_page->buckets_1ms[i]) {
    2142         [ #  # ]:          0 :                         printf("Bucket %dms - %dms: %d\n", i + 1, i + 2, entry->latency_page->buckets_1ms[i]);
    2143                 :            :                 }
    2144                 :            :         }
    2145         [ #  # ]:          0 :         for (i = 0; i < 31; i++) {
    2146         [ #  # ]:          0 :                 if (entry->latency_page->buckets_32ms[i])
    2147         [ #  # ]:          0 :                         printf("Bucket %dms - %dms: %d\n", (i + 1) * 32, (i + 2) * 32,
    2148                 :          0 :                                entry->latency_page->buckets_32ms[i]);
    2149                 :            :         }
    2150                 :          0 : }
    2151                 :            : 
    2152                 :            : static void
    2153                 :          0 : print_latency_statistics(const char *op_name, enum spdk_nvme_intel_log_page log_page)
    2154                 :            : {
    2155                 :            :         struct ctrlr_entry      *ctrlr;
    2156                 :            : 
    2157         [ #  # ]:          0 :         printf("%s Latency Statistics:\n", op_name);
    2158         [ #  # ]:          0 :         printf("========================================================\n");
    2159         [ #  # ]:          0 :         TAILQ_FOREACH(ctrlr, &g_controllers, link) {
    2160         [ #  # ]:          0 :                 if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr->ctrlr, log_page)) {
    2161         [ #  # ]:          0 :                         if (spdk_nvme_ctrlr_cmd_get_log_page(ctrlr->ctrlr, log_page, SPDK_NVME_GLOBAL_NS_TAG,
    2162                 :          0 :                                                              ctrlr->latency_page, sizeof(struct spdk_nvme_intel_rw_latency_page), 0,
    2163                 :            :                                                              enable_latency_tracking_complete,
    2164                 :            :                                                              NULL)) {
    2165         [ #  # ]:          0 :                                 printf("nvme_ctrlr_cmd_get_log_page() failed\n");
    2166                 :          0 :                                 exit(1);
    2167                 :            :                         }
    2168                 :            : 
    2169                 :          0 :                         g_outstanding_commands++;
    2170                 :            :                 } else {
    2171         [ #  # ]:          0 :                         printf("Controller %s: %s latency statistics not supported\n", ctrlr->name, op_name);
    2172                 :            :                 }
    2173                 :            :         }
    2174                 :            : 
    2175         [ #  # ]:          0 :         while (g_outstanding_commands) {
    2176         [ #  # ]:          0 :                 TAILQ_FOREACH(ctrlr, &g_controllers, link) {
    2177                 :          0 :                         spdk_nvme_ctrlr_process_admin_completions(ctrlr->ctrlr);
    2178                 :            :                 }
    2179                 :            :         }
    2180                 :            : 
    2181         [ #  # ]:          0 :         TAILQ_FOREACH(ctrlr, &g_controllers, link) {
    2182         [ #  # ]:          0 :                 if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr->ctrlr, log_page)) {
    2183                 :          0 :                         print_latency_page(ctrlr);
    2184                 :            :                 }
    2185                 :            :         }
    2186                 :          0 :         printf("\n");
    2187                 :          0 : }
    2188                 :            : 
    2189                 :            : static void
    2190                 :        114 : print_stats(void)
    2191                 :            : {
    2192                 :        114 :         print_performance();
    2193   [ -  +  -  + ]:        114 :         if (g_latency_ssd_tracking_enable) {
    2194         [ #  # ]:          0 :                 if (g_rw_percentage != 0) {
    2195                 :          0 :                         print_latency_statistics("Read", SPDK_NVME_INTEL_LOG_READ_CMD_LATENCY);
    2196                 :            :                 }
    2197         [ #  # ]:          0 :                 if (g_rw_percentage != 100) {
    2198                 :          0 :                         print_latency_statistics("Write", SPDK_NVME_INTEL_LOG_WRITE_CMD_LATENCY);
    2199                 :            :                 }
    2200                 :            :         }
    2201                 :        114 : }
    2202                 :            : 
    2203                 :            : static void
    2204                 :        124 : unregister_trids(void)
    2205                 :            : {
    2206                 :            :         struct trid_entry *trid_entry, *tmp;
    2207                 :            : 
    2208         [ +  + ]:        248 :         TAILQ_FOREACH_SAFE(trid_entry, &g_trid_list, tailq, tmp) {
    2209         [ -  + ]:        124 :                 TAILQ_REMOVE(&g_trid_list, trid_entry, tailq);
    2210                 :        124 :                 free(trid_entry);
    2211                 :            :         }
    2212                 :        124 : }
    2213                 :            : 
    2214                 :            : static int
    2215                 :        124 : add_trid(const char *trid_str)
    2216                 :            : {
    2217                 :            :         struct trid_entry *trid_entry;
    2218                 :            :         struct spdk_nvme_transport_id *trid;
    2219                 :            :         char *ns;
    2220                 :            :         char *hostnqn;
    2221                 :            : 
    2222                 :        124 :         trid_entry = calloc(1, sizeof(*trid_entry));
    2223         [ -  + ]:        124 :         if (trid_entry == NULL) {
    2224                 :          0 :                 return -1;
    2225                 :            :         }
    2226                 :            : 
    2227                 :        124 :         trid = &trid_entry->trid;
    2228                 :        124 :         trid->trtype = SPDK_NVME_TRANSPORT_PCIE;
    2229                 :        124 :         snprintf(trid->subnqn, sizeof(trid->subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN);
    2230                 :            : 
    2231         [ -  + ]:        124 :         if (spdk_nvme_transport_id_parse(trid, trid_str) != 0) {
    2232         [ #  # ]:          0 :                 fprintf(stderr, "Invalid transport ID format '%s'\n", trid_str);
    2233                 :          0 :                 free(trid_entry);
    2234                 :          0 :                 return 1;
    2235                 :            :         }
    2236                 :            : 
    2237         [ -  + ]:        124 :         ns = strcasestr(trid_str, "ns:");
    2238         [ -  + ]:        124 :         if (ns) {
    2239                 :          0 :                 char nsid_str[6]; /* 5 digits maximum in an nsid */
    2240                 :            :                 int len;
    2241                 :            :                 int nsid;
    2242                 :            : 
    2243                 :          0 :                 ns += 3;
    2244                 :            : 
    2245         [ #  # ]:          0 :                 len = strcspn(ns, " \t\n");
    2246         [ #  # ]:          0 :                 if (len > 5) {
    2247         [ #  # ]:          0 :                         fprintf(stderr, "NVMe namespace IDs must be 5 digits or less\n");
    2248                 :          0 :                         free(trid_entry);
    2249                 :          0 :                         return 1;
    2250                 :            :                 }
    2251                 :            : 
    2252         [ #  # ]:          0 :                 memcpy(nsid_str, ns, len);
    2253                 :          0 :                 nsid_str[len] = '\0';
    2254                 :            : 
    2255                 :          0 :                 nsid = spdk_strtol(nsid_str, 10);
    2256   [ #  #  #  # ]:          0 :                 if (nsid <= 0 || nsid > 65535) {
    2257         [ #  # ]:          0 :                         fprintf(stderr, "NVMe namespace IDs must be less than 65536 and greater than 0\n");
    2258                 :          0 :                         free(trid_entry);
    2259                 :          0 :                         return 1;
    2260                 :            :                 }
    2261                 :            : 
    2262                 :          0 :                 trid_entry->nsid = (uint16_t)nsid;
    2263                 :            :         }
    2264                 :            : 
    2265         [ -  + ]:        124 :         hostnqn = strcasestr(trid_str, "hostnqn:");
    2266         [ +  + ]:        124 :         if (hostnqn) {
    2267                 :            :                 size_t len;
    2268                 :            : 
    2269                 :          3 :                 hostnqn += strlen("hostnqn:");
    2270                 :            : 
    2271         [ -  + ]:          3 :                 len = strcspn(hostnqn, " \t\n");
    2272         [ -  + ]:          3 :                 if (len > (sizeof(trid_entry->hostnqn) - 1)) {
    2273         [ #  # ]:          0 :                         fprintf(stderr, "Host NQN is too long\n");
    2274                 :          0 :                         free(trid_entry);
    2275                 :          0 :                         return 1;
    2276                 :            :                 }
    2277                 :            : 
    2278   [ -  +  -  + ]:          3 :                 memcpy(trid_entry->hostnqn, hostnqn, len);
    2279                 :          3 :                 trid_entry->hostnqn[len] = '\0';
    2280                 :            :         }
    2281                 :            : 
    2282                 :        124 :         TAILQ_INSERT_TAIL(&g_trid_list, trid_entry, tailq);
    2283                 :        124 :         return 0;
    2284                 :            : }
    2285                 :            : 
    2286                 :            : static int
    2287                 :          0 : add_allowed_pci_device(const char *bdf_str, struct spdk_env_opts *env_opts)
    2288                 :            : {
    2289                 :            :         int rc;
    2290                 :            : 
    2291         [ #  # ]:          0 :         if (env_opts->num_pci_addr >= MAX_ALLOWED_PCI_DEVICE_NUM) {
    2292   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Currently we only support allowed PCI device num=%d\n",
    2293                 :            :                         MAX_ALLOWED_PCI_DEVICE_NUM);
    2294                 :          0 :                 return -1;
    2295                 :            :         }
    2296                 :            : 
    2297                 :          0 :         rc = spdk_pci_addr_parse(&env_opts->pci_allowed[env_opts->num_pci_addr], bdf_str);
    2298         [ #  # ]:          0 :         if (rc < 0) {
    2299   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Failed to parse the given bdf_str=%s\n", bdf_str);
    2300                 :          0 :                 return -1;
    2301                 :            :         }
    2302                 :            : 
    2303                 :          0 :         env_opts->num_pci_addr++;
    2304                 :          0 :         return 0;
    2305                 :            : }
    2306                 :            : 
    2307                 :            : static size_t
    2308                 :          0 : parse_next_key(const char **str, char *key, char *val, size_t key_buf_size,
    2309                 :            :                size_t val_buf_size)
    2310                 :            : {
    2311                 :            :         const char *sep;
    2312                 :          0 :         const char *separator = ", \t\n";
    2313                 :            :         size_t key_len, val_len;
    2314                 :            : 
    2315   [ #  #  #  # ]:          0 :         *str += strspn(*str, separator);
    2316                 :            : 
    2317         [ #  # ]:          0 :         sep = strchr(*str, '=');
    2318         [ #  # ]:          0 :         if (!sep) {
    2319         [ #  # ]:          0 :                 fprintf(stderr, "Key without '=' separator\n");
    2320                 :          0 :                 return 0;
    2321                 :            :         }
    2322                 :            : 
    2323                 :          0 :         key_len = sep - *str;
    2324         [ #  # ]:          0 :         if (key_len >= key_buf_size) {
    2325         [ #  # ]:          0 :                 fprintf(stderr, "Key length %zu is greater than maximum allowed %zu\n",
    2326                 :            :                         key_len, key_buf_size - 1);
    2327                 :          0 :                 return 0;
    2328                 :            :         }
    2329                 :            : 
    2330   [ #  #  #  # ]:          0 :         memcpy(key, *str, key_len);
    2331                 :          0 :         key[key_len] = '\0';
    2332                 :            : 
    2333                 :          0 :         *str += key_len + 1;    /* Skip key */
    2334   [ #  #  #  # ]:          0 :         val_len = strcspn(*str, separator);
    2335         [ #  # ]:          0 :         if (val_len == 0) {
    2336         [ #  # ]:          0 :                 fprintf(stderr, "Key without value\n");
    2337                 :          0 :                 return 0;
    2338                 :            :         }
    2339                 :            : 
    2340         [ #  # ]:          0 :         if (val_len >= val_buf_size) {
    2341         [ #  # ]:          0 :                 fprintf(stderr, "Value length %zu is greater than maximum allowed %zu\n",
    2342                 :            :                         val_len, val_buf_size - 1);
    2343                 :          0 :                 return 0;
    2344                 :            :         }
    2345                 :            : 
    2346   [ #  #  #  # ]:          0 :         memcpy(val, *str, val_len);
    2347                 :          0 :         val[val_len] = '\0';
    2348                 :            : 
    2349                 :          0 :         *str += val_len;
    2350                 :            : 
    2351                 :          0 :         return val_len;
    2352                 :            : }
    2353                 :            : 
    2354                 :            : static int
    2355                 :          0 : parse_metadata(const char *metacfg_str)
    2356                 :            : {
    2357                 :          0 :         const char *str;
    2358                 :            :         size_t val_len;
    2359                 :          0 :         char key[32];
    2360                 :          0 :         char val[1024];
    2361                 :            : 
    2362         [ #  # ]:          0 :         if (metacfg_str == NULL) {
    2363                 :          0 :                 return -EINVAL;
    2364                 :            :         }
    2365                 :            : 
    2366                 :          0 :         str = metacfg_str;
    2367                 :            : 
    2368         [ #  # ]:          0 :         while (*str != '\0') {
    2369                 :          0 :                 val_len = parse_next_key(&str, key, val, sizeof(key), sizeof(val));
    2370         [ #  # ]:          0 :                 if (val_len == 0) {
    2371         [ #  # ]:          0 :                         fprintf(stderr, "Failed to parse metadata\n");
    2372                 :          0 :                         return -EINVAL;
    2373                 :            :                 }
    2374                 :            : 
    2375         [ #  # ]:          0 :                 if (strcmp(key, "PRACT") == 0) {
    2376         [ #  # ]:          0 :                         if (*val == '1') {
    2377                 :          0 :                                 g_metacfg_pract_flag = SPDK_NVME_IO_FLAGS_PRACT;
    2378                 :            :                         }
    2379         [ #  # ]:          0 :                 } else if (strcmp(key, "PRCHK") == 0) {
    2380         [ #  # ]:          0 :                         if (strstr(val, "GUARD") != NULL) {
    2381                 :          0 :                                 g_metacfg_prchk_flags |= SPDK_NVME_IO_FLAGS_PRCHK_GUARD;
    2382                 :            :                         }
    2383         [ #  # ]:          0 :                         if (strstr(val, "REFTAG") != NULL) {
    2384                 :          0 :                                 g_metacfg_prchk_flags |= SPDK_NVME_IO_FLAGS_PRCHK_REFTAG;
    2385                 :            :                         }
    2386         [ #  # ]:          0 :                         if (strstr(val, "APPTAG") != NULL) {
    2387                 :          0 :                                 g_metacfg_prchk_flags |= SPDK_NVME_IO_FLAGS_PRCHK_APPTAG;
    2388                 :            :                         }
    2389                 :            :                 } else {
    2390         [ #  # ]:          0 :                         fprintf(stderr, "Unknown key '%s'\n", key);
    2391                 :            :                 }
    2392                 :            :         }
    2393                 :            : 
    2394                 :          0 :         return 0;
    2395                 :            : }
    2396                 :            : 
    2397                 :            : #define PERF_GETOPT_SHORT "a:b:c:d:e:gi:lmo:q:r:k:s:t:w:z:A:C:DF:GHILM:NO:P:Q:RS:T:U:VZ:"
    2398                 :            : 
    2399                 :            : static const struct option g_perf_cmdline_opts[] = {
    2400                 :            : #define PERF_WARMUP_TIME        'a'
    2401                 :            :         {"warmup-time",                       required_argument,      NULL, PERF_WARMUP_TIME},
    2402                 :            : #define PERF_ALLOWED_PCI_ADDR   'b'
    2403                 :            :         {"allowed-pci-addr",                  required_argument,      NULL, PERF_ALLOWED_PCI_ADDR},
    2404                 :            : #define PERF_CORE_MASK  'c'
    2405                 :            :         {"core-mask",                 required_argument,      NULL, PERF_CORE_MASK},
    2406                 :            : #define PERF_METADATA   'e'
    2407                 :            :         {"metadata",                  required_argument,      NULL, PERF_METADATA},
    2408                 :            : #define PERF_MEM_SINGL_SEG      'g'
    2409                 :            :         {"mem-single-seg", no_argument, NULL, PERF_MEM_SINGL_SEG},
    2410                 :            : #define PERF_SHMEM_GROUP_ID     'i'
    2411                 :            :         {"shmem-grp-id",                      required_argument,      NULL, PERF_SHMEM_GROUP_ID},
    2412                 :            : #define PERF_ENABLE_SSD_LATENCY_TRACING 'l'
    2413                 :            :         {"enable-ssd-latency-tracking", no_argument, NULL, PERF_ENABLE_SSD_LATENCY_TRACING},
    2414                 :            : #define PERF_CPU_USAGE  'm'
    2415                 :            :         {"cpu-usage", no_argument, NULL, PERF_CPU_USAGE},
    2416                 :            : #define PERF_IO_SIZE    'o'
    2417                 :            :         {"io-size",                   required_argument,      NULL, PERF_IO_SIZE},
    2418                 :            : #define PERF_IO_DEPTH   'q'
    2419                 :            :         {"io-depth",                  required_argument,      NULL, PERF_IO_DEPTH},
    2420                 :            : #define PERF_TRANSPORT  'r'
    2421                 :            :         {"transport",                 required_argument,      NULL, PERF_TRANSPORT},
    2422                 :            : #define PERF_KEEPALIVE  'k'
    2423                 :            :         {"keepalive",                 required_argument,      NULL, PERF_KEEPALIVE},
    2424                 :            : #define PERF_HUGEMEM_SIZE       's'
    2425                 :            :         {"hugemem-size",                      required_argument,      NULL, PERF_HUGEMEM_SIZE},
    2426                 :            : #define PERF_TIME       't'
    2427                 :            :         {"time",                      required_argument,      NULL, PERF_TIME},
    2428                 :            : #define PERF_NUMBER_IOS 'd'
    2429                 :            :         {"number-ios",                        required_argument,      NULL, PERF_NUMBER_IOS},
    2430                 :            : #define PERF_IO_PATTERN 'w'
    2431                 :            :         {"io-pattern",                        required_argument,      NULL, PERF_IO_PATTERN},
    2432                 :            : #define PERF_DISABLE_ZCOPY      'z'
    2433                 :            :         {"disable-zcopy",                     required_argument,      NULL, PERF_DISABLE_ZCOPY},
    2434                 :            : #define PERF_BUFFER_ALIGNMENT   'A'
    2435                 :            :         {"buffer-alignment",                  required_argument,      NULL, PERF_BUFFER_ALIGNMENT},
    2436                 :            : #define PERF_MAX_COMPLETIONS_PER_POLL   'C'
    2437                 :            :         {"max-completion-per-poll",                   required_argument,      NULL, PERF_MAX_COMPLETIONS_PER_POLL},
    2438                 :            : #define PERF_DISABLE_SQ_CMB     'D'
    2439                 :            :         {"disable-sq-cmb",                    no_argument,    NULL, PERF_DISABLE_SQ_CMB},
    2440                 :            : #define PERF_ZIPF               'F'
    2441                 :            :         {"zipf",                              required_argument,      NULL, PERF_ZIPF},
    2442                 :            : #define PERF_ENABLE_DEBUG       'G'
    2443                 :            :         {"enable-debug",                      no_argument,    NULL, PERF_ENABLE_DEBUG},
    2444                 :            : #define PERF_ENABLE_TCP_HDGST   'H'
    2445                 :            :         {"enable-tcp-hdgst",                  no_argument,    NULL, PERF_ENABLE_TCP_HDGST},
    2446                 :            : #define PERF_ENABLE_TCP_DDGST   'I'
    2447                 :            :         {"enable-tcp-ddgst",                  no_argument,    NULL, PERF_ENABLE_TCP_DDGST},
    2448                 :            : #define PERF_ENABLE_SW_LATENCY_TRACING  'L'
    2449                 :            :         {"enable-sw-latency-tracking", no_argument, NULL, PERF_ENABLE_SW_LATENCY_TRACING},
    2450                 :            : #define PERF_RW_MIXREAD 'M'
    2451                 :            :         {"rwmixread", required_argument, NULL, PERF_RW_MIXREAD},
    2452                 :            : #define PERF_NO_SHST_NOTIFICATION       'N'
    2453                 :            :         {"no-shst-notification", no_argument, NULL, PERF_NO_SHST_NOTIFICATION},
    2454                 :            : #define PERF_IO_UNIT_SIZE       'O'
    2455                 :            :         {"io-unit-size",                      required_argument,      NULL, PERF_IO_UNIT_SIZE},
    2456                 :            : #define PERF_IO_QUEUES_PER_NS   'P'
    2457                 :            :         {"num-qpairs", required_argument, NULL, PERF_IO_QUEUES_PER_NS},
    2458                 :            : #define PERF_CONTINUE_ON_ERROR  'Q'
    2459                 :            :         {"continue-on-error",                 required_argument,      NULL, PERF_CONTINUE_ON_ERROR},
    2460                 :            : #define PERF_ENABLE_URING       'R'
    2461                 :            :         {"enable-uring", no_argument, NULL, PERF_ENABLE_URING},
    2462                 :            : #define PERF_DEFAULT_SOCK_IMPL  'S'
    2463                 :            :         {"default-sock-impl", required_argument, NULL, PERF_DEFAULT_SOCK_IMPL},
    2464                 :            : #define PERF_LOG_FLAG   'T'
    2465                 :            :         {"logflag", required_argument, NULL, PERF_LOG_FLAG},
    2466                 :            : #define PERF_NUM_UNUSED_IO_QPAIRS       'U'
    2467                 :            :         {"num-unused-qpairs", required_argument, NULL, PERF_NUM_UNUSED_IO_QPAIRS},
    2468                 :            : #define PERF_ENABLE_VMD 'V'
    2469                 :            :         {"enable-vmd", no_argument, NULL, PERF_ENABLE_VMD},
    2470                 :            : #define PERF_ENABLE_ZCOPY       'Z'
    2471                 :            :         {"enable-zcopy",                      required_argument,      NULL, PERF_ENABLE_ZCOPY},
    2472                 :            : #define PERF_TRANSPORT_STATISTICS       257
    2473                 :            :         {"transport-stats", no_argument, NULL, PERF_TRANSPORT_STATISTICS},
    2474                 :            : #define PERF_IOVA_MODE          258
    2475                 :            :         {"iova-mode", required_argument, NULL, PERF_IOVA_MODE},
    2476                 :            : #define PERF_IO_QUEUE_SIZE      259
    2477                 :            :         {"io-queue-size", required_argument, NULL, PERF_IO_QUEUE_SIZE},
    2478                 :            : #define PERF_DISABLE_KTLS       260
    2479                 :            :         {"disable-ktls", no_argument, NULL, PERF_DISABLE_KTLS},
    2480                 :            : #define PERF_ENABLE_KTLS        261
    2481                 :            :         {"enable-ktls", no_argument, NULL, PERF_ENABLE_KTLS},
    2482                 :            : #define PERF_TLS_VERSION        262
    2483                 :            :         {"tls-version", required_argument, NULL, PERF_TLS_VERSION},
    2484                 :            : #define PERF_PSK_PATH           263
    2485                 :            :         {"psk-path", required_argument, NULL, PERF_PSK_PATH},
    2486                 :            : #define PERF_PSK_IDENTITY       264
    2487                 :            :         {"psk-identity ", required_argument, NULL, PERF_PSK_IDENTITY},
    2488                 :            : #define PERF_ZEROCOPY_THRESHOLD         265
    2489                 :            :         {"zerocopy-threshold", required_argument, NULL, PERF_ZEROCOPY_THRESHOLD},
    2490                 :            : #define PERF_SOCK_IMPL          266
    2491                 :            :         {"zerocopy-threshold-sock-impl", required_argument, NULL, PERF_SOCK_IMPL},
    2492                 :            : #define PERF_TRANSPORT_TOS              267
    2493                 :            :         {"transport-tos", required_argument, NULL, PERF_TRANSPORT_TOS},
    2494                 :            : #define PERF_RDMA_SRQ_SIZE      268
    2495                 :            :         {"rdma-srq-size", required_argument, NULL, PERF_RDMA_SRQ_SIZE},
    2496                 :            : #define PERF_USE_EVERY_CORE     269
    2497                 :            :         {"use-every-core", no_argument, NULL, PERF_USE_EVERY_CORE},
    2498                 :            : #define PERF_NO_HUGE            270
    2499                 :            :         {"no-huge", no_argument, NULL, PERF_NO_HUGE},
    2500                 :            :         /* Should be the last element */
    2501                 :            :         {0, 0, 0, 0}
    2502                 :            : };
    2503                 :            : 
    2504                 :            : static int
    2505                 :        124 : parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
    2506                 :            : {
    2507                 :         24 :         int op, long_idx;
    2508                 :            :         long int val;
    2509                 :         24 :         uint64_t val_u64;
    2510                 :            :         int rc;
    2511                 :         24 :         char *endptr;
    2512                 :        124 :         bool ssl_used = false;
    2513                 :        124 :         char *sock_impl = "posix";
    2514                 :            : 
    2515   [ +  +  +  + ]:        954 :         while ((op = getopt_long(argc, argv, PERF_GETOPT_SHORT, g_perf_cmdline_opts, &long_idx)) != -1) {
    2516   [ +  +  -  -  :        830 :                 switch (op) {
          +  -  +  -  -  
          +  +  -  -  +  
          +  +  +  -  -  
          -  -  -  -  +  
          -  -  -  -  +  
          +  -  -  -  -  
                      - ]
    2517                 :        375 :                 case PERF_WARMUP_TIME:
    2518                 :            :                 case PERF_SHMEM_GROUP_ID:
    2519                 :            :                 case PERF_MAX_COMPLETIONS_PER_POLL:
    2520                 :            :                 case PERF_IO_QUEUES_PER_NS:
    2521                 :            :                 case PERF_IO_DEPTH:
    2522                 :            :                 case PERF_KEEPALIVE:
    2523                 :            :                 case PERF_TIME:
    2524                 :            :                 case PERF_RW_MIXREAD:
    2525                 :            :                 case PERF_NUM_UNUSED_IO_QPAIRS:
    2526                 :            :                 case PERF_CONTINUE_ON_ERROR:
    2527                 :            :                 case PERF_IO_QUEUE_SIZE:
    2528                 :            :                 case PERF_RDMA_SRQ_SIZE:
    2529                 :        375 :                         val = spdk_strtol(optarg, 10);
    2530         [ -  + ]:        375 :                         if (val < 0) {
    2531         [ #  # ]:          0 :                                 fprintf(stderr, "Converting a string to integer failed\n");
    2532                 :          0 :                                 return val;
    2533                 :            :                         }
    2534                 :            :                         switch (op) {
    2535                 :          0 :                         case PERF_WARMUP_TIME:
    2536                 :          0 :                                 g_warmup_time_in_sec = val;
    2537                 :          0 :                                 break;
    2538                 :         52 :                         case PERF_SHMEM_GROUP_ID:
    2539                 :         52 :                                 env_opts->shm_id = val;
    2540                 :         52 :                                 break;
    2541                 :          0 :                         case PERF_MAX_COMPLETIONS_PER_POLL:
    2542                 :          0 :                                 g_max_completions = val;
    2543                 :          0 :                                 break;
    2544                 :         10 :                         case PERF_IO_QUEUES_PER_NS:
    2545                 :         10 :                                 g_nr_io_queues_per_ns = val;
    2546                 :         10 :                                 break;
    2547                 :        124 :                         case PERF_IO_DEPTH:
    2548                 :        124 :                                 g_queue_depth = val;
    2549                 :        124 :                                 break;
    2550                 :          0 :                         case PERF_KEEPALIVE:
    2551                 :          0 :                                 g_keep_alive_timeout_in_ms = val;
    2552                 :          0 :                                 break;
    2553                 :        124 :                         case PERF_TIME:
    2554                 :        124 :                                 g_time_in_sec = val;
    2555                 :        124 :                                 break;
    2556                 :         62 :                         case PERF_RW_MIXREAD:
    2557                 :         62 :                                 g_rw_percentage = val;
    2558                 :         62 :                                 g_mix_specified = true;
    2559                 :         62 :                                 break;
    2560                 :          3 :                         case PERF_CONTINUE_ON_ERROR:
    2561                 :          3 :                                 g_quiet_count = val;
    2562                 :          3 :                                 g_continue_on_error = true;
    2563                 :          3 :                                 break;
    2564                 :          0 :                         case PERF_NUM_UNUSED_IO_QPAIRS:
    2565                 :          0 :                                 g_nr_unused_io_queues = val;
    2566                 :          0 :                                 break;
    2567                 :          0 :                         case PERF_IO_QUEUE_SIZE:
    2568                 :          0 :                                 g_io_queue_size = val;
    2569                 :          0 :                                 break;
    2570                 :          0 :                         case PERF_RDMA_SRQ_SIZE:
    2571                 :          0 :                                 g_rdma_srq_size = val;
    2572                 :          0 :                                 break;
    2573                 :            :                         }
    2574                 :        375 :                         break;
    2575                 :        142 :                 case PERF_IO_SIZE:
    2576                 :            :                 case PERF_IO_UNIT_SIZE:
    2577                 :            :                 case PERF_ZEROCOPY_THRESHOLD:
    2578                 :            :                 case PERF_BUFFER_ALIGNMENT:
    2579                 :            :                 case PERF_HUGEMEM_SIZE:
    2580                 :            :                 case PERF_NUMBER_IOS:
    2581                 :        142 :                         rc = spdk_parse_capacity(optarg, &val_u64, NULL);
    2582         [ -  + ]:        142 :                         if (rc != 0) {
    2583         [ #  # ]:          0 :                                 fprintf(stderr, "Converting a string to integer failed\n");
    2584                 :          0 :                                 return 1;
    2585                 :            :                         }
    2586                 :            :                         switch (op) {
    2587                 :        124 :                         case PERF_IO_SIZE:
    2588                 :        124 :                                 g_io_size_bytes = (uint32_t)val_u64;
    2589                 :        124 :                                 break;
    2590                 :          8 :                         case PERF_IO_UNIT_SIZE:
    2591                 :          8 :                                 g_io_unit_size = (uint32_t)val_u64;
    2592                 :          8 :                                 break;
    2593                 :          0 :                         case PERF_ZEROCOPY_THRESHOLD:
    2594                 :          0 :                                 g_sock_zcopy_threshold = (uint32_t)val_u64;
    2595                 :          0 :                                 break;
    2596                 :          0 :                         case PERF_BUFFER_ALIGNMENT:
    2597                 :          0 :                                 g_io_align = (uint32_t)val_u64;
    2598   [ #  #  #  # ]:          0 :                                 if (!spdk_u32_is_pow2(g_io_align) || g_io_align < SPDK_CACHE_LINE_SIZE) {
    2599         [ #  # ]:          0 :                                         fprintf(stderr, "Wrong alignment %u. Must be power of 2 and not less than cache lize (%u)\n",
    2600                 :            :                                                 g_io_align, SPDK_CACHE_LINE_SIZE);
    2601                 :          0 :                                         usage(argv[0]);
    2602                 :          0 :                                         return 1;
    2603                 :            :                                 }
    2604                 :          0 :                                 g_io_align_specified = true;
    2605                 :          0 :                                 break;
    2606                 :         10 :                         case PERF_HUGEMEM_SIZE:
    2607                 :         10 :                                 env_opts->mem_size = (int)val_u64;
    2608                 :         10 :                                 break;
    2609                 :          0 :                         case PERF_NUMBER_IOS:
    2610                 :          0 :                                 g_number_ios = val_u64;
    2611                 :          0 :                                 break;
    2612                 :            :                         }
    2613                 :        142 :                         break;
    2614                 :          0 :                 case PERF_ZIPF:
    2615                 :          0 :                         errno = 0;
    2616         [ #  # ]:          0 :                         g_zipf_theta = strtod(optarg, &endptr);
    2617   [ #  #  #  #  :          0 :                         if (errno || optarg == endptr || g_zipf_theta < 0) {
                   #  # ]
    2618         [ #  # ]:          0 :                                 fprintf(stderr, "Illegal zipf theta value %s\n", optarg);
    2619                 :          0 :                                 return 1;
    2620                 :            :                         }
    2621                 :          0 :                         break;
    2622                 :          0 :                 case PERF_ALLOWED_PCI_ADDR:
    2623         [ #  # ]:          0 :                         if (add_allowed_pci_device(optarg, env_opts)) {
    2624                 :          0 :                                 usage(argv[0]);
    2625                 :          0 :                                 return 1;
    2626                 :            :                         }
    2627                 :          0 :                         break;
    2628                 :         61 :                 case PERF_CORE_MASK:
    2629                 :         61 :                         env_opts->core_mask = optarg;
    2630                 :         61 :                         break;
    2631                 :          0 :                 case PERF_METADATA:
    2632         [ #  # ]:          0 :                         if (parse_metadata(optarg)) {
    2633                 :          0 :                                 usage(argv[0]);
    2634                 :          0 :                                 return 1;
    2635                 :            :                         }
    2636                 :          0 :                         break;
    2637                 :          4 :                 case PERF_MEM_SINGL_SEG:
    2638                 :          4 :                         env_opts->hugepage_single_segments = true;
    2639                 :          4 :                         break;
    2640                 :          0 :                 case PERF_ENABLE_SSD_LATENCY_TRACING:
    2641                 :          0 :                         g_latency_ssd_tracking_enable = true;
    2642                 :          0 :                         break;
    2643                 :          0 :                 case PERF_CPU_USAGE:
    2644                 :          0 :                         g_monitor_perf_cores = true;
    2645                 :          0 :                         break;
    2646                 :         76 :                 case PERF_TRANSPORT:
    2647         [ -  + ]:         76 :                         if (add_trid(optarg)) {
    2648                 :          0 :                                 usage(argv[0]);
    2649                 :          0 :                                 return 1;
    2650                 :            :                         }
    2651                 :         76 :                         break;
    2652                 :        124 :                 case PERF_IO_PATTERN:
    2653                 :        124 :                         g_workload_type = optarg;
    2654                 :        124 :                         break;
    2655                 :          0 :                 case PERF_DISABLE_SQ_CMB:
    2656                 :          0 :                         g_disable_sq_cmb = 1;
    2657                 :          0 :                         break;
    2658                 :          0 :                 case PERF_ENABLE_DEBUG:
    2659                 :            : #ifndef DEBUG
    2660                 :            :                         fprintf(stderr, "%s must be configured with --enable-debug for -G flag\n",
    2661                 :            :                                 argv[0]);
    2662                 :            :                         usage(argv[0]);
    2663                 :            :                         return 1;
    2664                 :            : #else
    2665                 :          0 :                         spdk_log_set_flag("nvme");
    2666                 :          0 :                         spdk_log_set_print_level(SPDK_LOG_DEBUG);
    2667                 :          0 :                         break;
    2668                 :            : #endif
    2669                 :          4 :                 case PERF_ENABLE_TCP_HDGST:
    2670                 :          4 :                         g_header_digest = 1;
    2671                 :          4 :                         break;
    2672                 :          4 :                 case PERF_ENABLE_TCP_DDGST:
    2673                 :          4 :                         g_data_digest = 1;
    2674                 :          4 :                         break;
    2675                 :         24 :                 case PERF_ENABLE_SW_LATENCY_TRACING:
    2676                 :         24 :                         g_latency_sw_tracking_level++;
    2677                 :         24 :                         break;
    2678                 :          6 :                 case PERF_NO_SHST_NOTIFICATION:
    2679                 :          6 :                         g_no_shn_notification = true;
    2680                 :          6 :                         break;
    2681                 :          0 :                 case PERF_ENABLE_URING:
    2682                 :            : #ifndef SPDK_CONFIG_URING
    2683         [ #  # ]:          0 :                         fprintf(stderr, "%s must be rebuilt with CONFIG_URING=y for -R flag.\n",
    2684                 :            :                                 argv[0]);
    2685                 :          0 :                         usage(argv[0]);
    2686                 :          0 :                         return 0;
    2687                 :            : #endif
    2688                 :          0 :                         g_use_uring = true;
    2689                 :          0 :                         break;
    2690                 :          0 :                 case PERF_LOG_FLAG:
    2691                 :          0 :                         rc = spdk_log_set_flag(optarg);
    2692         [ #  # ]:          0 :                         if (rc < 0) {
    2693         [ #  # ]:          0 :                                 fprintf(stderr, "unknown flag\n");
    2694                 :          0 :                                 usage(argv[0]);
    2695                 :          0 :                                 exit(EXIT_FAILURE);
    2696                 :            :                         }
    2697                 :            : #ifdef DEBUG
    2698                 :          0 :                         spdk_log_set_print_level(SPDK_LOG_DEBUG);
    2699                 :            : #endif
    2700                 :          0 :                         break;
    2701                 :          0 :                 case PERF_ENABLE_VMD:
    2702                 :          0 :                         g_vmd = true;
    2703                 :          0 :                         break;
    2704                 :          0 :                 case PERF_DISABLE_KTLS:
    2705                 :          0 :                         ssl_used = true;
    2706                 :          0 :                         perf_set_sock_opts("ssl", "ktls", 0, NULL);
    2707                 :          0 :                         break;
    2708                 :          0 :                 case PERF_ENABLE_KTLS:
    2709                 :          0 :                         ssl_used = true;
    2710                 :          0 :                         perf_set_sock_opts("ssl", "ktls", 1, NULL);
    2711                 :          0 :                         break;
    2712                 :          0 :                 case PERF_TLS_VERSION:
    2713                 :          0 :                         ssl_used = true;
    2714                 :          0 :                         val = spdk_strtol(optarg, 10);
    2715         [ #  # ]:          0 :                         if (val < 0) {
    2716         [ #  # ]:          0 :                                 fprintf(stderr, "Illegal tls version value %s\n", optarg);
    2717                 :          0 :                                 return val;
    2718                 :            :                         }
    2719                 :          0 :                         perf_set_sock_opts("ssl", "tls_version", val, NULL);
    2720                 :          0 :                         break;
    2721                 :          3 :                 case PERF_PSK_PATH:
    2722                 :          3 :                         ssl_used = true;
    2723                 :          3 :                         perf_set_sock_opts("ssl", "psk_path", 0, optarg);
    2724                 :          3 :                         break;
    2725                 :          0 :                 case PERF_PSK_IDENTITY:
    2726                 :          0 :                         ssl_used = true;
    2727                 :          0 :                         perf_set_sock_opts("ssl", "psk_identity", 0, optarg);
    2728                 :          0 :                         break;
    2729                 :          0 :                 case PERF_DISABLE_ZCOPY:
    2730                 :          0 :                         perf_set_sock_opts(optarg, "enable_zerocopy_send_client", 0, NULL);
    2731                 :          0 :                         break;
    2732                 :          0 :                 case PERF_ENABLE_ZCOPY:
    2733                 :          0 :                         perf_set_sock_opts(optarg, "enable_zerocopy_send_client", 1, NULL);
    2734                 :          0 :                         break;
    2735                 :          0 :                 case PERF_USE_EVERY_CORE:
    2736                 :          0 :                         g_use_every_core = true;
    2737                 :          0 :                         break;
    2738                 :          3 :                 case PERF_DEFAULT_SOCK_IMPL:
    2739                 :          3 :                         sock_impl = optarg;
    2740                 :          3 :                         rc = spdk_sock_set_default_impl(optarg);
    2741         [ -  + ]:          3 :                         if (rc) {
    2742         [ #  # ]:          0 :                                 fprintf(stderr, "Failed to set sock impl %s, err %d (%s)\n", optarg, errno, strerror(errno));
    2743                 :          0 :                                 return 1;
    2744                 :            :                         }
    2745                 :          3 :                         break;
    2746                 :          4 :                 case PERF_TRANSPORT_STATISTICS:
    2747                 :          4 :                         g_dump_transport_stats = true;
    2748                 :          4 :                         break;
    2749                 :          0 :                 case PERF_IOVA_MODE:
    2750                 :          0 :                         env_opts->iova_mode = optarg;
    2751                 :          0 :                         break;
    2752                 :          0 :                 case PERF_SOCK_IMPL:
    2753                 :          0 :                         g_sock_threshold_impl = optarg;
    2754                 :          0 :                         break;
    2755                 :          0 :                 case PERF_TRANSPORT_TOS:
    2756                 :          0 :                         val = spdk_strtol(optarg, 10);
    2757         [ #  # ]:          0 :                         if (val < 0) {
    2758         [ #  # ]:          0 :                                 fprintf(stderr, "Invalid TOS value\n");
    2759                 :          0 :                                 return 1;
    2760                 :            :                         }
    2761                 :          0 :                         g_transport_tos = val;
    2762                 :          0 :                         break;
    2763                 :          0 :                 case PERF_NO_HUGE:
    2764                 :          0 :                         env_opts->no_huge = true;
    2765                 :          0 :                         break;
    2766                 :          0 :                 default:
    2767                 :          0 :                         usage(argv[0]);
    2768                 :          0 :                         return 1;
    2769                 :            :                 }
    2770                 :            :         }
    2771                 :            : 
    2772         [ -  + ]:        124 :         if (!g_nr_io_queues_per_ns) {
    2773                 :          0 :                 usage(argv[0]);
    2774                 :          0 :                 return 1;
    2775                 :            :         }
    2776                 :            : 
    2777         [ -  + ]:        124 :         if (!g_queue_depth) {
    2778         [ #  # ]:          0 :                 fprintf(stderr, "missing -q (--io-depth) operand\n");
    2779                 :          0 :                 usage(argv[0]);
    2780                 :          0 :                 return 1;
    2781                 :            :         }
    2782         [ -  + ]:        124 :         if (!g_io_size_bytes) {
    2783         [ #  # ]:          0 :                 fprintf(stderr, "missing -o (--io-size) operand\n");
    2784                 :          0 :                 usage(argv[0]);
    2785                 :          0 :                 return 1;
    2786                 :            :         }
    2787   [ +  -  -  + ]:        124 :         if (!g_io_unit_size || g_io_unit_size % 4) {
    2788         [ #  # ]:          0 :                 fprintf(stderr, "io unit size can not be 0 or non 4-byte aligned\n");
    2789                 :          0 :                 return 1;
    2790                 :            :         }
    2791         [ -  + ]:        124 :         if (!g_workload_type) {
    2792         [ #  # ]:          0 :                 fprintf(stderr, "missing -w (--io-pattern) operand\n");
    2793                 :          0 :                 usage(argv[0]);
    2794                 :          0 :                 return 1;
    2795                 :            :         }
    2796         [ -  + ]:        124 :         if (!g_time_in_sec) {
    2797         [ #  # ]:          0 :                 fprintf(stderr, "missing -t (--time) operand\n");
    2798                 :          0 :                 usage(argv[0]);
    2799                 :          0 :                 return 1;
    2800                 :            :         }
    2801         [ -  + ]:        124 :         if (!g_quiet_count) {
    2802         [ #  # ]:          0 :                 fprintf(stderr, "-Q (--continue-on-error) value must be greater than 0\n");
    2803                 :          0 :                 usage(argv[0]);
    2804                 :          0 :                 return 1;
    2805                 :            :         }
    2806                 :            : 
    2807   [ -  +  +  + ]:        124 :         if (strncmp(g_workload_type, "rand", 4) == 0) {
    2808                 :         71 :                 g_is_random = 1;
    2809                 :         71 :                 g_workload_type = &g_workload_type[4];
    2810                 :            :         }
    2811                 :            : 
    2812   [ +  +  -  +  :        124 :         if (ssl_used && strncmp(sock_impl, "ssl", 3) != 0) {
                   -  + ]
    2813         [ #  # ]:          0 :                 fprintf(stderr, "sock impl is not SSL but tried to use one of the SSL only options\n");
    2814                 :          0 :                 usage(argv[0]);
    2815                 :          0 :                 return 1;
    2816                 :            :         }
    2817                 :            : 
    2818                 :            : 
    2819   [ +  +  +  +  :        124 :         if (strcmp(g_workload_type, "read") == 0 || strcmp(g_workload_type, "write") == 0) {
             -  +  +  + ]
    2820   [ +  +  +  + ]:         62 :                 g_rw_percentage = strcmp(g_workload_type, "read") == 0 ? 100 : 0;
    2821   [ -  +  -  + ]:         62 :                 if (g_mix_specified) {
    2822         [ #  # ]:          0 :                         fprintf(stderr, "Ignoring -M (--rwmixread) option... Please use -M option"
    2823                 :            :                                 " only when using rw or randrw.\n");
    2824                 :            :                 }
    2825   [ -  +  +  - ]:         62 :         } else if (strcmp(g_workload_type, "rw") == 0) {
    2826   [ +  -  -  + ]:         62 :                 if (g_rw_percentage < 0 || g_rw_percentage > 100) {
    2827         [ #  # ]:          0 :                         fprintf(stderr,
    2828                 :            :                                 "-M (--rwmixread) must be specified to value from 0 to 100 "
    2829                 :            :                                 "for rw or randrw.\n");
    2830                 :          0 :                         return 1;
    2831                 :            :                 }
    2832                 :            :         } else {
    2833         [ #  # ]:          0 :                 fprintf(stderr,
    2834                 :            :                         "-w (--io-pattern) io pattern type must be one of\n"
    2835                 :            :                         "(read, write, randread, randwrite, rw, randrw)\n");
    2836                 :          0 :                 return 1;
    2837                 :            :         }
    2838                 :            : 
    2839         [ -  + ]:        124 :         if (g_sock_zcopy_threshold > 0) {
    2840         [ #  # ]:          0 :                 if (!g_sock_threshold_impl) {
    2841         [ #  # ]:          0 :                         fprintf(stderr,
    2842                 :            :                                 "--zerocopy-threshold must be set with sock implementation specified(--zerocopy-threshold-sock-impl <impl>)\n");
    2843                 :          0 :                         return 1;
    2844                 :            :                 }
    2845                 :            : 
    2846                 :          0 :                 perf_set_sock_opts(g_sock_threshold_impl, "zerocopy_threshold", g_sock_zcopy_threshold, NULL);
    2847                 :            :         }
    2848                 :            : 
    2849   [ -  +  -  - ]:        124 :         if (g_number_ios && g_warmup_time_in_sec) {
    2850         [ #  # ]:          0 :                 fprintf(stderr, "-d (--number-ios) with -a (--warmup-time) is not supported\n");
    2851                 :          0 :                 return 1;
    2852                 :            :         }
    2853                 :            : 
    2854   [ -  +  -  - ]:        124 :         if (g_number_ios && g_number_ios < g_queue_depth) {
    2855         [ #  # ]:          0 :                 fprintf(stderr, "-d (--number-ios) less than -q (--io-depth) is not supported\n");
    2856                 :          0 :                 return 1;
    2857                 :            :         }
    2858                 :            : 
    2859         [ -  + ]:        124 :         if (g_rdma_srq_size != 0) {
    2860                 :          0 :                 struct spdk_nvme_transport_opts opts;
    2861                 :            : 
    2862                 :          0 :                 spdk_nvme_transport_get_opts(&opts, sizeof(opts));
    2863                 :          0 :                 opts.rdma_srq_size = g_rdma_srq_size;
    2864                 :            : 
    2865                 :          0 :                 rc = spdk_nvme_transport_set_opts(&opts, sizeof(opts));
    2866         [ #  # ]:          0 :                 if (rc != 0) {
    2867         [ #  # ]:          0 :                         fprintf(stderr, "Failed to set NVMe transport options.\n");
    2868                 :          0 :                         return 1;
    2869                 :            :                 }
    2870                 :            :         }
    2871                 :            : 
    2872         [ +  + ]:        124 :         if (TAILQ_EMPTY(&g_trid_list)) {
    2873                 :            :                 /* If no transport IDs specified, default to enumerating all local PCIe devices */
    2874                 :         48 :                 add_trid("trtype:PCIe");
    2875                 :            :         } else {
    2876                 :            :                 struct trid_entry *trid_entry, *trid_entry_tmp;
    2877                 :            : 
    2878                 :         76 :                 env_opts->no_pci = true;
    2879                 :            :                 /* check whether there is local PCIe type */
    2880         [ +  + ]:        148 :                 TAILQ_FOREACH_SAFE(trid_entry, &g_trid_list, tailq, trid_entry_tmp) {
    2881         [ +  + ]:         76 :                         if (trid_entry->trid.trtype == SPDK_NVME_TRANSPORT_PCIE) {
    2882                 :          4 :                                 env_opts->no_pci = false;
    2883                 :          4 :                                 break;
    2884                 :            :                         }
    2885                 :            :                 }
    2886                 :            :         }
    2887                 :            : 
    2888                 :        124 :         g_file_optind = optind;
    2889                 :            : 
    2890                 :        124 :         return 0;
    2891                 :            : }
    2892                 :            : 
    2893                 :            : static int
    2894                 :        124 : register_workers(void)
    2895                 :            : {
    2896                 :            :         uint32_t i;
    2897                 :            :         struct worker_thread *worker;
    2898                 :            : 
    2899         [ +  + ]:        278 :         SPDK_ENV_FOREACH_CORE(i) {
    2900                 :        154 :                 worker = calloc(1, sizeof(*worker));
    2901         [ -  + ]:        154 :                 if (worker == NULL) {
    2902   [ #  #  #  # ]:          0 :                         fprintf(stderr, "Unable to allocate worker\n");
    2903                 :          0 :                         return -1;
    2904                 :            :                 }
    2905                 :            : 
    2906                 :        154 :                 TAILQ_INIT(&worker->ns_ctx);
    2907                 :        154 :                 worker->lcore = i;
    2908                 :        154 :                 TAILQ_INSERT_TAIL(&g_workers, worker, link);
    2909                 :        154 :                 g_num_workers++;
    2910                 :            :         }
    2911                 :            : 
    2912                 :        124 :         return 0;
    2913                 :            : }
    2914                 :            : 
    2915                 :            : static void
    2916                 :        124 : unregister_workers(void)
    2917                 :            : {
    2918                 :            :         struct worker_thread *worker, *tmp_worker;
    2919                 :            :         struct ns_worker_ctx *ns_ctx, *tmp_ns_ctx;
    2920                 :            : 
    2921                 :            :         /* Free namespace context and worker thread */
    2922         [ +  + ]:        278 :         TAILQ_FOREACH_SAFE(worker, &g_workers, link, tmp_worker) {
    2923         [ +  + ]:        154 :                 TAILQ_REMOVE(&g_workers, worker, link);
    2924                 :            : 
    2925         [ +  + ]:        353 :                 TAILQ_FOREACH_SAFE(ns_ctx, &worker->ns_ctx, link, tmp_ns_ctx) {
    2926         [ +  + ]:        199 :                         TAILQ_REMOVE(&worker->ns_ctx, ns_ctx, link);
    2927                 :        199 :                         spdk_histogram_data_free(ns_ctx->histogram);
    2928                 :        199 :                         free(ns_ctx);
    2929                 :            :                 }
    2930                 :            : 
    2931                 :        154 :                 free(worker);
    2932                 :            :         }
    2933                 :        124 : }
    2934                 :            : 
    2935                 :            : static bool
    2936                 :         72 : probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
    2937                 :            :          struct spdk_nvme_ctrlr_opts *opts)
    2938                 :            : {
    2939                 :         72 :         struct trid_entry *trid_entry = cb_ctx;
    2940                 :            : 
    2941         [ -  + ]:         72 :         if (trid->trtype == SPDK_NVME_TRANSPORT_PCIE) {
    2942         [ #  # ]:          0 :                 if (g_disable_sq_cmb) {
    2943                 :          0 :                         opts->use_cmb_sqs = false;
    2944                 :            :                 }
    2945   [ #  #  #  # ]:          0 :                 if (g_no_shn_notification) {
    2946                 :          0 :                         opts->no_shn_notification = true;
    2947                 :            :                 }
    2948                 :            :         }
    2949                 :            : 
    2950         [ -  + ]:         72 :         if (trid->trtype != trid_entry->trid.trtype &&
    2951   [ #  #  #  #  :          0 :             strcasecmp(trid->trstring, trid_entry->trid.trstring)) {
                   #  # ]
    2952                 :          0 :                 return false;
    2953                 :            :         }
    2954                 :            : 
    2955                 :         72 :         opts->io_queue_size = g_io_queue_size;
    2956                 :            : 
    2957                 :            :         /* Set the header and data_digest */
    2958         [ -  + ]:         72 :         opts->header_digest = g_header_digest;
    2959         [ -  + ]:         72 :         opts->data_digest = g_data_digest;
    2960                 :         72 :         opts->keep_alive_timeout_ms = g_keep_alive_timeout_in_ms;
    2961   [ -  +  -  + ]:         72 :         memcpy(opts->hostnqn, trid_entry->hostnqn, sizeof(opts->hostnqn));
    2962                 :            : 
    2963                 :         72 :         opts->transport_tos = g_transport_tos;
    2964         [ -  + ]:         72 :         if (opts->num_io_queues < g_num_workers * g_nr_io_queues_per_ns) {
    2965                 :          0 :                 opts->num_io_queues = g_num_workers * g_nr_io_queues_per_ns;
    2966                 :            :         }
    2967                 :            : 
    2968         [ +  + ]:         72 :         if (g_psk != NULL) {
    2969   [ -  +  -  +  :          3 :                 memcpy(opts->psk, g_psk, strlen(g_psk));
                   -  + ]
    2970                 :            :         }
    2971                 :            : 
    2972                 :         72 :         return true;
    2973                 :            : }
    2974                 :            : 
    2975                 :            : static void
    2976                 :        156 : attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
    2977                 :            :           struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
    2978                 :            : {
    2979                 :        156 :         struct trid_entry       *trid_entry = cb_ctx;
    2980                 :         48 :         struct spdk_pci_addr    pci_addr;
    2981                 :            :         struct spdk_pci_device  *pci_dev;
    2982                 :            :         struct spdk_pci_id      pci_id;
    2983                 :            : 
    2984         [ +  + ]:        156 :         if (trid->trtype != SPDK_NVME_TRANSPORT_PCIE) {
    2985                 :         72 :                 printf("Attached to NVMe over Fabrics controller at %s:%s: %s\n",
    2986         [ -  + ]:         72 :                        trid->traddr, trid->trsvcid,
    2987                 :         72 :                        trid->subnqn);
    2988                 :            :         } else {
    2989         [ -  + ]:         84 :                 if (spdk_pci_addr_parse(&pci_addr, trid->traddr)) {
    2990                 :          0 :                         return;
    2991                 :            :                 }
    2992                 :            : 
    2993                 :         84 :                 pci_dev = spdk_nvme_ctrlr_get_pci_device(ctrlr);
    2994         [ -  + ]:         84 :                 if (!pci_dev) {
    2995                 :          0 :                         return;
    2996                 :            :                 }
    2997                 :            : 
    2998                 :         84 :                 pci_id = spdk_pci_device_get_id(pci_dev);
    2999                 :            : 
    3000                 :         84 :                 printf("Attached to NVMe Controller at %s [%04x:%04x]\n",
    3001         [ -  + ]:         84 :                        trid->traddr,
    3002                 :         84 :                        pci_id.vendor_id, pci_id.device_id);
    3003                 :            :         }
    3004                 :            : 
    3005                 :        156 :         register_ctrlr(ctrlr, trid_entry);
    3006                 :            : }
    3007                 :            : 
    3008                 :            : static int
    3009                 :        124 : register_controllers(void)
    3010                 :            : {
    3011                 :            :         struct trid_entry *trid_entry;
    3012                 :            : 
    3013         [ -  + ]:        124 :         printf("Initializing NVMe Controllers\n");
    3014                 :            : 
    3015   [ -  +  -  +  :        124 :         if (g_vmd && spdk_vmd_init()) {
                   -  - ]
    3016   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Failed to initialize VMD."
    3017                 :            :                         " Some NVMe devices can be unavailable.\n");
    3018                 :            :         }
    3019                 :            : 
    3020         [ +  + ]:        248 :         TAILQ_FOREACH(trid_entry, &g_trid_list, tailq) {
    3021         [ -  + ]:        124 :                 if (spdk_nvme_probe(&trid_entry->trid, trid_entry, probe_cb, attach_cb, NULL) != 0) {
    3022         [ #  # ]:          0 :                         fprintf(stderr, "spdk_nvme_probe() failed for transport address '%s'\n",
    3023         [ #  # ]:          0 :                                 trid_entry->trid.traddr);
    3024                 :          0 :                         return -1;
    3025                 :            :                 }
    3026                 :            :         }
    3027                 :            : 
    3028                 :        124 :         return 0;
    3029                 :            : }
    3030                 :            : 
    3031                 :            : static void
    3032                 :        124 : unregister_controllers(void)
    3033                 :            : {
    3034                 :            :         struct ctrlr_entry *entry, *tmp;
    3035                 :        124 :         struct spdk_nvme_detach_ctx *detach_ctx = NULL;
    3036                 :            : 
    3037         [ +  + ]:        280 :         TAILQ_FOREACH_SAFE(entry, &g_controllers, link, tmp) {
    3038         [ +  + ]:        156 :                 TAILQ_REMOVE(&g_controllers, entry, link);
    3039                 :            : 
    3040                 :        156 :                 spdk_dma_free(entry->latency_page);
    3041   [ -  +  -  +  :        156 :                 if (g_latency_ssd_tracking_enable &&
                   -  - ]
    3042                 :          0 :                     spdk_nvme_ctrlr_is_feature_supported(entry->ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING)) {
    3043                 :          0 :                         set_latency_tracking_feature(entry->ctrlr, false);
    3044                 :            :                 }
    3045                 :            : 
    3046         [ -  + ]:        156 :                 if (g_nr_unused_io_queues) {
    3047                 :            :                         int i;
    3048                 :            : 
    3049         [ #  # ]:          0 :                         for (i = 0; i < g_nr_unused_io_queues; i++) {
    3050                 :          0 :                                 spdk_nvme_ctrlr_free_io_qpair(entry->unused_qpairs[i]);
    3051                 :            :                         }
    3052                 :            : 
    3053                 :          0 :                         free(entry->unused_qpairs);
    3054                 :            :                 }
    3055                 :            : 
    3056                 :        156 :                 spdk_nvme_detach_async(entry->ctrlr, &detach_ctx);
    3057                 :        156 :                 free(entry);
    3058                 :            :         }
    3059                 :            : 
    3060         [ +  + ]:        124 :         if (detach_ctx) {
    3061                 :         72 :                 spdk_nvme_detach_poll(detach_ctx);
    3062                 :            :         }
    3063                 :            : 
    3064   [ -  +  -  + ]:        124 :         if (g_vmd) {
    3065                 :          0 :                 spdk_vmd_fini();
    3066                 :            :         }
    3067                 :        124 : }
    3068                 :            : 
    3069                 :            : static int
    3070                 :        199 : allocate_ns_worker(struct ns_entry *entry, struct worker_thread *worker)
    3071                 :            : {
    3072                 :            :         struct ns_worker_ctx    *ns_ctx;
    3073                 :            : 
    3074                 :        199 :         ns_ctx = calloc(1, sizeof(struct ns_worker_ctx));
    3075         [ -  + ]:        199 :         if (!ns_ctx) {
    3076                 :          0 :                 return -1;
    3077                 :            :         }
    3078                 :            : 
    3079         [ -  + ]:        199 :         printf("Associating %s with lcore %d\n", entry->name, worker->lcore);
    3080                 :        199 :         ns_ctx->stats.min_tsc = UINT64_MAX;
    3081                 :        199 :         ns_ctx->entry = entry;
    3082                 :        199 :         ns_ctx->histogram = spdk_histogram_data_alloc();
    3083                 :        199 :         TAILQ_INSERT_TAIL(&worker->ns_ctx, ns_ctx, link);
    3084                 :            : 
    3085                 :        199 :         return 0;
    3086                 :            : }
    3087                 :            : 
    3088                 :            : static int
    3089                 :        114 : associate_workers_with_ns(void)
    3090                 :            : {
    3091                 :        114 :         struct ns_entry         *entry = TAILQ_FIRST(&g_namespaces);
    3092                 :        114 :         struct worker_thread    *worker = TAILQ_FIRST(&g_workers);
    3093                 :            :         int                     i, count;
    3094                 :            : 
    3095                 :            :         /* Each core contains single worker, and namespaces are associated as follows:
    3096                 :            :          * --use-every-core not specified (default):
    3097                 :            :          * 2) equal workers and namespaces - each worker associated with single namespace
    3098                 :            :          * 3) more workers than namespaces - each namespace is associated with one or more workers
    3099                 :            :          * 4) more namespaces than workers - each worker is associated with one or more namespaces
    3100                 :            :          * --use-every-core option enabled - every worker is associated with all namespaces
    3101                 :            :          */
    3102   [ -  +  -  + ]:        114 :         if (g_use_every_core) {
    3103         [ #  # ]:          0 :                 TAILQ_FOREACH(worker, &g_workers, link) {
    3104         [ #  # ]:          0 :                         TAILQ_FOREACH(entry, &g_namespaces, link) {
    3105         [ #  # ]:          0 :                                 if (allocate_ns_worker(entry, worker) != 0) {
    3106                 :          0 :                                         return -1;
    3107                 :            :                                 }
    3108                 :            :                         }
    3109                 :            :                 }
    3110                 :          0 :                 return 0;
    3111                 :            :         }
    3112                 :            : 
    3113                 :        114 :         count = g_num_namespaces > g_num_workers ? g_num_namespaces : g_num_workers;
    3114                 :            : 
    3115         [ +  + ]:        313 :         for (i = 0; i < count; i++) {
    3116         [ -  + ]:        199 :                 if (entry == NULL) {
    3117                 :          0 :                         break;
    3118                 :            :                 }
    3119                 :            : 
    3120         [ -  + ]:        199 :                 if (allocate_ns_worker(entry, worker) != 0) {
    3121                 :          0 :                         return -1;
    3122                 :            :                 }
    3123                 :            : 
    3124                 :        199 :                 worker = TAILQ_NEXT(worker, link);
    3125         [ +  + ]:        199 :                 if (worker == NULL) {
    3126                 :        181 :                         worker = TAILQ_FIRST(&g_workers);
    3127                 :            :                 }
    3128                 :            : 
    3129                 :        199 :                 entry = TAILQ_NEXT(entry, link);
    3130         [ +  + ]:        199 :                 if (entry == NULL) {
    3131                 :        132 :                         entry = TAILQ_FIRST(&g_namespaces);
    3132                 :            :                 }
    3133                 :            : 
    3134                 :            :         }
    3135                 :            : 
    3136                 :        114 :         return 0;
    3137                 :            : }
    3138                 :            : 
    3139                 :            : static void *
    3140                 :        114 : nvme_poll_ctrlrs(void *arg)
    3141                 :            : {
    3142                 :            :         struct ctrlr_entry *entry;
    3143                 :         24 :         int oldstate;
    3144                 :            :         int rc;
    3145                 :            : 
    3146                 :        114 :         spdk_unaffinitize_thread();
    3147                 :            : 
    3148                 :            :         while (true) {
    3149                 :        790 :                 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
    3150                 :            : 
    3151         [ +  + ]:       1708 :                 TAILQ_FOREACH(entry, &g_controllers, link) {
    3152         [ +  + ]:        918 :                         if (entry->trtype != SPDK_NVME_TRANSPORT_PCIE) {
    3153                 :        594 :                                 rc = spdk_nvme_ctrlr_process_admin_completions(entry->ctrlr);
    3154   [ -  +  -  -  :        594 :                                 if (spdk_unlikely(rc < 0 && !g_exit)) {
                   -  - ]
    3155                 :          0 :                                         g_exit = true;
    3156                 :            :                                 }
    3157                 :            :                         }
    3158                 :            :                 }
    3159                 :            : 
    3160                 :        790 :                 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
    3161                 :            : 
    3162                 :            :                 /* This is a pthread cancellation point and cannot be removed. */
    3163                 :        790 :                 sleep(1);
    3164                 :            :         }
    3165                 :            : 
    3166                 :            :         return NULL;
    3167                 :            : }
    3168                 :            : 
    3169                 :            : static void
    3170                 :          0 : sig_handler(int signo)
    3171                 :            : {
    3172                 :          0 :         g_exit = true;
    3173                 :          0 : }
    3174                 :            : 
    3175                 :            : static int
    3176                 :        124 : setup_sig_handlers(void)
    3177                 :            : {
    3178                 :        124 :         struct sigaction sigact = {};
    3179                 :            :         int rc;
    3180                 :            : 
    3181                 :        124 :         sigemptyset(&sigact.sa_mask);
    3182                 :        124 :         sigact.sa_handler = sig_handler;
    3183                 :        124 :         rc = sigaction(SIGINT, &sigact, NULL);
    3184         [ -  + ]:        124 :         if (rc < 0) {
    3185         [ #  # ]:          0 :                 fprintf(stderr, "sigaction(SIGINT) failed, errno %d (%s)\n", errno, strerror(errno));
    3186                 :          0 :                 return -1;
    3187                 :            :         }
    3188                 :            : 
    3189                 :        124 :         rc = sigaction(SIGTERM, &sigact, NULL);
    3190         [ -  + ]:        124 :         if (rc < 0) {
    3191         [ #  # ]:          0 :                 fprintf(stderr, "sigaction(SIGTERM) failed, errno %d (%s)\n", errno, strerror(errno));
    3192                 :          0 :                 return -1;
    3193                 :            :         }
    3194                 :            : 
    3195                 :        124 :         return 0;
    3196                 :            : }
    3197                 :            : 
    3198                 :            : int
    3199                 :        124 : main(int argc, char **argv)
    3200                 :            : {
    3201                 :            :         int rc;
    3202                 :            :         struct worker_thread *worker, *main_worker;
    3203                 :            :         struct ns_worker_ctx *ns_ctx;
    3204                 :         24 :         struct spdk_env_opts opts;
    3205                 :        124 :         pthread_t thread_id = 0;
    3206                 :            : 
    3207                 :            :         /* Use the runtime PID to set the random seed */
    3208                 :        124 :         srand(getpid());
    3209                 :            : 
    3210                 :        124 :         spdk_env_opts_init(&opts);
    3211                 :        124 :         opts.name = "perf";
    3212                 :        124 :         opts.pci_allowed = g_allowed_pci_addr;
    3213                 :        124 :         rc = parse_args(argc, argv, &opts);
    3214         [ -  + ]:        124 :         if (rc != 0) {
    3215                 :          0 :                 free(g_psk);
    3216                 :          0 :                 return rc;
    3217                 :            :         }
    3218                 :            :         /* Transport statistics are printed from each thread.
    3219                 :            :          * To avoid mess in terminal, init and use mutex */
    3220         [ -  + ]:        124 :         rc = pthread_mutex_init(&g_stats_mutex, NULL);
    3221         [ -  + ]:        124 :         if (rc != 0) {
    3222   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Failed to init mutex\n");
    3223                 :          0 :                 free(g_psk);
    3224                 :          0 :                 return -1;
    3225                 :            :         }
    3226         [ -  + ]:        124 :         if (spdk_env_init(&opts) < 0) {
    3227   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Unable to initialize SPDK env\n");
    3228                 :          0 :                 unregister_trids();
    3229         [ #  # ]:          0 :                 pthread_mutex_destroy(&g_stats_mutex);
    3230                 :          0 :                 free(g_psk);
    3231                 :          0 :                 return -1;
    3232                 :            :         }
    3233                 :            : 
    3234                 :        124 :         rc = setup_sig_handlers();
    3235         [ -  + ]:        124 :         if (rc != 0) {
    3236                 :          0 :                 rc = -1;
    3237                 :          0 :                 goto cleanup;
    3238                 :            :         }
    3239                 :            : 
    3240                 :        124 :         g_tsc_rate = spdk_get_ticks_hz();
    3241                 :            : 
    3242         [ -  + ]:        124 :         if (register_workers() != 0) {
    3243                 :          0 :                 rc = -1;
    3244                 :          0 :                 goto cleanup;
    3245                 :            :         }
    3246                 :            : 
    3247                 :            : #if defined(HAVE_LIBAIO) || defined(SPDK_CONFIG_URING)
    3248         [ -  + ]:        124 :         if (register_files(argc, argv) != 0) {
    3249                 :          0 :                 rc = -1;
    3250                 :          0 :                 goto cleanup;
    3251                 :            :         }
    3252                 :            : #endif
    3253                 :            : 
    3254         [ -  + ]:        124 :         if (register_controllers() != 0) {
    3255                 :          0 :                 rc = -1;
    3256                 :          0 :                 goto cleanup;
    3257                 :            :         }
    3258                 :            : 
    3259   [ -  +  +  + ]:        124 :         if (g_warn) {
    3260         [ -  + ]:         10 :                 printf("WARNING: Some requested NVMe devices were skipped\n");
    3261                 :            :         }
    3262                 :            : 
    3263         [ +  + ]:        124 :         if (g_num_namespaces == 0) {
    3264   [ -  +  -  + ]:         10 :                 fprintf(stderr, "No valid NVMe controllers or AIO or URING devices found\n");
    3265                 :         10 :                 goto cleanup;
    3266                 :            :         }
    3267                 :            : 
    3268   [ +  +  -  + ]:        114 :         if (g_num_workers > 1 && g_quiet_count > 1) {
    3269   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Error message rate-limiting enabled across multiple threads.\n");
    3270   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Error suppression count may not be exact.\n");
    3271                 :            :         }
    3272                 :            : 
    3273   [ -  +  -  + ]:        114 :         rc = pthread_create(&thread_id, NULL, &nvme_poll_ctrlrs, NULL);
    3274         [ -  + ]:        114 :         if (rc != 0) {
    3275   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Unable to spawn a thread to poll admin queues.\n");
    3276                 :          0 :                 goto cleanup;
    3277                 :            :         }
    3278                 :            : 
    3279         [ -  + ]:        114 :         if (associate_workers_with_ns() != 0) {
    3280                 :          0 :                 rc = -1;
    3281                 :          0 :                 goto cleanup;
    3282                 :            :         }
    3283                 :            : 
    3284         [ -  + ]:        114 :         rc = pthread_barrier_init(&g_worker_sync_barrier, NULL, g_num_workers);
    3285         [ -  + ]:        114 :         if (rc != 0) {
    3286   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Unable to initialize thread sync barrier\n");
    3287                 :          0 :                 goto cleanup;
    3288                 :            :         }
    3289                 :            : 
    3290         [ -  + ]:        114 :         printf("Initialization complete. Launching workers.\n");
    3291                 :            : 
    3292                 :            :         /* Launch all of the secondary workers */
    3293                 :        114 :         g_main_core = spdk_env_get_current_core();
    3294                 :        114 :         main_worker = NULL;
    3295         [ +  + ]:        246 :         TAILQ_FOREACH(worker, &g_workers, link) {
    3296         [ +  + ]:        132 :                 if (worker->lcore != g_main_core) {
    3297                 :         18 :                         spdk_env_thread_launch_pinned(worker->lcore, work_fn, worker);
    3298                 :            :                 } else {
    3299         [ -  + ]:        114 :                         assert(main_worker == NULL);
    3300                 :        114 :                         main_worker = worker;
    3301                 :            :                 }
    3302                 :            :         }
    3303                 :            : 
    3304         [ -  + ]:        114 :         assert(main_worker != NULL);
    3305                 :        114 :         work_fn(main_worker);
    3306                 :            : 
    3307                 :        114 :         spdk_env_thread_wait_all();
    3308                 :            : 
    3309                 :        114 :         print_stats();
    3310                 :            : 
    3311         [ -  + ]:        114 :         pthread_barrier_destroy(&g_worker_sync_barrier);
    3312                 :            : 
    3313                 :        124 : cleanup:
    3314   [ +  +  +  - ]:        124 :         if (thread_id && pthread_cancel(thread_id) == 0) {
    3315                 :        114 :                 pthread_join(thread_id, NULL);
    3316                 :            :         }
    3317                 :            : 
    3318                 :            :         /* Collect errors from all workers and namespaces */
    3319         [ +  + ]:        275 :         TAILQ_FOREACH(worker, &g_workers, link) {
    3320         [ +  + ]:        154 :                 if (rc != 0) {
    3321                 :          3 :                         break;
    3322                 :            :                 }
    3323                 :            : 
    3324         [ +  + ]:        344 :                 TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
    3325         [ +  + ]:        196 :                         if (ns_ctx->status != 0) {
    3326                 :          3 :                                 rc = ns_ctx->status;
    3327                 :          3 :                                 break;
    3328                 :            :                         }
    3329                 :            :                 }
    3330                 :            :         }
    3331                 :            : 
    3332                 :        124 :         unregister_trids();
    3333                 :        124 :         unregister_namespaces();
    3334                 :        124 :         unregister_controllers();
    3335                 :        124 :         unregister_workers();
    3336                 :            : 
    3337                 :        124 :         spdk_env_fini();
    3338                 :            : 
    3339                 :        124 :         free(g_psk);
    3340                 :            : 
    3341         [ -  + ]:        124 :         pthread_mutex_destroy(&g_stats_mutex);
    3342                 :            : 
    3343         [ +  + ]:        124 :         if (rc != 0) {
    3344   [ -  +  -  + ]:          3 :                 fprintf(stderr, "%s: errors occurred\n", argv[0]);
    3345                 :            :         }
    3346                 :            : 
    3347                 :        124 :         return rc;
    3348                 :            : }

Generated by: LCOV version 1.14