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