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