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