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