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