Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2016 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : :
8 : : #include "spdk/barrier.h"
9 : : #include "spdk/fd.h"
10 : : #include "spdk/nvme.h"
11 : : #include "spdk/env.h"
12 : : #include "spdk/string.h"
13 : : #include "spdk/nvme_intel.h"
14 : : #include "spdk/histogram_data.h"
15 : : #include "spdk/log.h"
16 : :
17 : : #if HAVE_LIBAIO
18 : : #include <libaio.h>
19 : : #endif
20 : :
21 : : struct ctrlr_entry {
22 : : struct spdk_nvme_ctrlr *ctrlr;
23 : : TAILQ_ENTRY(ctrlr_entry) link;
24 : : char name[1024];
25 : : };
26 : :
27 : : enum entry_type {
28 : : ENTRY_TYPE_NVME_NS,
29 : : ENTRY_TYPE_AIO_FILE,
30 : : };
31 : :
32 : : struct ns_entry {
33 : : enum entry_type type;
34 : :
35 : : union {
36 : : struct {
37 : : struct spdk_nvme_ctrlr *ctrlr;
38 : : struct spdk_nvme_ns *ns;
39 : : struct spdk_nvme_qpair *qpair;
40 : : } nvme;
41 : : #if HAVE_LIBAIO
42 : : struct {
43 : : int fd;
44 : : struct io_event *events;
45 : : io_context_t ctx;
46 : : } aio;
47 : : #endif
48 : : } u;
49 : :
50 : : uint32_t io_size_blocks;
51 : : uint64_t size_in_ios;
52 : : bool is_draining;
53 : : uint32_t current_queue_depth;
54 : : char name[1024];
55 : : struct ns_entry *next;
56 : :
57 : : struct spdk_histogram_data *submit_histogram;
58 : : struct spdk_histogram_data *complete_histogram;
59 : : };
60 : :
61 : : struct perf_task {
62 : : void *buf;
63 : : uint64_t submit_tsc;
64 : : #if HAVE_LIBAIO
65 : : struct iocb iocb;
66 : : #endif
67 : : };
68 : :
69 : : static bool g_enable_histogram = false;
70 : :
71 : : static TAILQ_HEAD(, ctrlr_entry) g_ctrlr = TAILQ_HEAD_INITIALIZER(g_ctrlr);
72 : : static struct ns_entry *g_ns = NULL;
73 : :
74 : : static uint64_t g_tsc_rate;
75 : :
76 : : static uint32_t g_io_size_bytes;
77 : : static int g_time_in_sec;
78 : :
79 : : static int g_aio_optind; /* Index of first AIO filename in argv */
80 : :
81 : : struct perf_task *g_task;
82 : : uint64_t g_tsc_submit = 0;
83 : : uint64_t g_tsc_submit_min = UINT64_MAX;
84 : : uint64_t g_tsc_submit_max = 0;
85 : : uint64_t g_tsc_complete = 0;
86 : : uint64_t g_tsc_complete_min = UINT64_MAX;
87 : : uint64_t g_tsc_complete_max = 0;
88 : : uint64_t g_io_completed = 0;
89 : :
90 : : static struct spdk_nvme_transport_id g_trid = {};
91 : :
92 : : static void
93 : 12 : register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
94 : : {
95 : : struct ns_entry *entry;
96 : : const struct spdk_nvme_ctrlr_data *cdata;
97 : :
98 : 12 : cdata = spdk_nvme_ctrlr_get_data(ctrlr);
99 : :
100 [ - + ]: 12 : if (!spdk_nvme_ns_is_active(ns)) {
101 : 0 : printf("Controller %-20.20s (%-20.20s): Skipping inactive NS %u\n",
102 [ # # ]: 0 : cdata->mn, cdata->sn,
103 : : spdk_nvme_ns_get_id(ns));
104 : 0 : return;
105 : : }
106 : :
107 [ + - ]: 12 : if (spdk_nvme_ns_get_size(ns) < g_io_size_bytes ||
108 [ - + ]: 12 : spdk_nvme_ns_get_sector_size(ns) > g_io_size_bytes) {
109 : 0 : printf("WARNING: controller %-20.20s (%-20.20s) ns %u has invalid "
110 : : "ns size %" PRIu64 " / block size %u for I/O size %u\n",
111 [ # # ]: 0 : cdata->mn, cdata->sn, spdk_nvme_ns_get_id(ns),
112 : : spdk_nvme_ns_get_size(ns), spdk_nvme_ns_get_sector_size(ns), g_io_size_bytes);
113 : 0 : return;
114 : : }
115 : :
116 : 12 : entry = calloc(1, sizeof(struct ns_entry));
117 [ - + ]: 12 : if (entry == NULL) {
118 : 0 : perror("ns_entry malloc");
119 : 0 : exit(1);
120 : : }
121 : :
122 : 12 : entry->type = ENTRY_TYPE_NVME_NS;
123 : 12 : entry->u.nvme.ctrlr = ctrlr;
124 : 12 : entry->u.nvme.ns = ns;
125 : :
126 [ - + ]: 12 : entry->size_in_ios = spdk_nvme_ns_get_size(ns) /
127 : : g_io_size_bytes;
128 [ - + ]: 12 : entry->io_size_blocks = g_io_size_bytes / spdk_nvme_ns_get_sector_size(ns);
129 : 12 : entry->submit_histogram = spdk_histogram_data_alloc();
130 : 12 : entry->complete_histogram = spdk_histogram_data_alloc();
131 : :
132 [ - + ]: 12 : snprintf(entry->name, 44, "%-20.20s (%-20.20s)", cdata->mn, cdata->sn);
133 : :
134 : 12 : entry->next = g_ns;
135 : 12 : g_ns = entry;
136 : : }
137 : :
138 : : static void
139 : 12 : register_ctrlr(struct spdk_nvme_ctrlr *ctrlr)
140 : : {
141 : : int num_ns;
142 : 12 : struct ctrlr_entry *entry = malloc(sizeof(struct ctrlr_entry));
143 : 12 : const struct spdk_nvme_ctrlr_data *cdata = spdk_nvme_ctrlr_get_data(ctrlr);
144 : :
145 [ - + ]: 12 : if (entry == NULL) {
146 : 0 : perror("ctrlr_entry malloc");
147 : 0 : exit(1);
148 : : }
149 : :
150 [ - + ]: 12 : snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn);
151 : :
152 : 12 : entry->ctrlr = ctrlr;
153 : :
154 : 12 : TAILQ_INSERT_TAIL(&g_ctrlr, entry, link);
155 : :
156 : 12 : num_ns = spdk_nvme_ctrlr_get_num_ns(ctrlr);
157 : : /* Only register the first namespace. */
158 [ - + ]: 12 : if (num_ns < 1) {
159 [ # # # # ]: 0 : fprintf(stderr, "controller found with no namespaces\n");
160 : 0 : return;
161 : : }
162 : :
163 : 12 : register_ns(ctrlr, spdk_nvme_ctrlr_get_ns(ctrlr, 1));
164 : : }
165 : :
166 : : #if HAVE_LIBAIO
167 : : static int
168 : 0 : register_aio_file(const char *path)
169 : : {
170 : : struct ns_entry *entry;
171 : :
172 : : int fd;
173 : : uint64_t size;
174 : : uint32_t blklen;
175 : :
176 [ # # ]: 0 : fd = open(path, O_RDWR | O_DIRECT);
177 [ # # ]: 0 : if (fd < 0) {
178 [ # # ]: 0 : fprintf(stderr, "Could not open AIO device %s: %s\n", path, strerror(errno));
179 : 0 : return -1;
180 : : }
181 : :
182 : 0 : size = spdk_fd_get_size(fd);
183 [ # # ]: 0 : if (size == 0) {
184 [ # # ]: 0 : fprintf(stderr, "Could not determine size of AIO device %s\n", path);
185 : 0 : close(fd);
186 : 0 : return -1;
187 : : }
188 : :
189 : 0 : blklen = spdk_fd_get_blocklen(fd);
190 [ # # ]: 0 : if (blklen == 0) {
191 [ # # ]: 0 : fprintf(stderr, "Could not determine block size of AIO device %s\n", path);
192 : 0 : close(fd);
193 : 0 : return -1;
194 : : }
195 : :
196 : 0 : entry = calloc(1, sizeof(struct ns_entry));
197 [ # # ]: 0 : if (entry == NULL) {
198 : 0 : close(fd);
199 : 0 : perror("aio ns_entry malloc");
200 : 0 : return -1;
201 : : }
202 : :
203 : 0 : entry->type = ENTRY_TYPE_AIO_FILE;
204 : 0 : entry->u.aio.fd = fd;
205 [ # # ]: 0 : entry->size_in_ios = size / g_io_size_bytes;
206 [ # # ]: 0 : entry->io_size_blocks = g_io_size_bytes / blklen;
207 : 0 : entry->submit_histogram = spdk_histogram_data_alloc();
208 : 0 : entry->complete_histogram = spdk_histogram_data_alloc();
209 : :
210 : 0 : snprintf(entry->name, sizeof(entry->name), "%s", path);
211 : :
212 : 0 : g_ns = entry;
213 : :
214 : 0 : return 0;
215 : : }
216 : :
217 : : static int
218 : 0 : aio_submit(io_context_t aio_ctx, struct iocb *iocb, int fd, enum io_iocb_cmd cmd, void *buf,
219 : : unsigned long nbytes, uint64_t offset, void *cb_ctx)
220 : : {
221 : 0 : iocb->aio_fildes = fd;
222 : 0 : iocb->aio_reqprio = 0;
223 : 0 : iocb->aio_lio_opcode = cmd;
224 : 0 : iocb->u.c.buf = buf;
225 : 0 : iocb->u.c.nbytes = nbytes;
226 : 0 : iocb->u.c.offset = offset;
227 : 0 : iocb->data = cb_ctx;
228 : :
229 [ # # ]: 0 : if (io_submit(aio_ctx, 1, &iocb) < 0) {
230 [ # # ]: 0 : printf("io_submit");
231 : 0 : return -1;
232 : : }
233 : :
234 : 0 : return 0;
235 : : }
236 : :
237 : : static void
238 : 0 : aio_check_io(void)
239 : : {
240 : : int count, i;
241 : 0 : struct timespec timeout;
242 : :
243 : 0 : timeout.tv_sec = 0;
244 : 0 : timeout.tv_nsec = 0;
245 : :
246 : 0 : count = io_getevents(g_ns->u.aio.ctx, 1, 1, g_ns->u.aio.events, &timeout);
247 [ # # ]: 0 : if (count < 0) {
248 [ # # # # ]: 0 : fprintf(stderr, "io_getevents error\n");
249 : 0 : exit(1);
250 : : }
251 : :
252 [ # # ]: 0 : for (i = 0; i < count; i++) {
253 : 0 : g_ns->current_queue_depth--;
254 : : }
255 : 0 : }
256 : : #endif /* HAVE_LIBAIO */
257 : :
258 : : static void io_complete(void *ctx, const struct spdk_nvme_cpl *completion);
259 : :
260 : : static __thread unsigned int seed = 0;
261 : :
262 : : static void
263 : 156027 : submit_single_io(void)
264 : : {
265 : : uint64_t offset_in_ios;
266 : : uint64_t start;
267 : : int rc;
268 : 156027 : struct ns_entry *entry = g_ns;
269 : : uint64_t tsc_submit;
270 : :
271 [ - + ]: 156027 : offset_in_ios = rand_r(&seed) % entry->size_in_ios;
272 : :
273 : 156027 : start = spdk_get_ticks();
274 : 156027 : spdk_rmb();
275 : : #if HAVE_LIBAIO
276 [ - + ]: 156027 : if (entry->type == ENTRY_TYPE_AIO_FILE) {
277 : 0 : rc = aio_submit(g_ns->u.aio.ctx, &g_task->iocb, entry->u.aio.fd, IO_CMD_PREAD, g_task->buf,
278 : : g_io_size_bytes, offset_in_ios * g_io_size_bytes, g_task);
279 : : } else
280 : : #endif
281 : : {
282 : 294854 : rc = spdk_nvme_ns_cmd_read(entry->u.nvme.ns, g_ns->u.nvme.qpair, g_task->buf,
283 : 156027 : offset_in_ios * entry->io_size_blocks,
284 : : entry->io_size_blocks, io_complete, g_task, 0);
285 : : }
286 : :
287 : 156027 : spdk_rmb();
288 : 156027 : tsc_submit = spdk_get_ticks() - start;
289 : 156027 : g_tsc_submit += tsc_submit;
290 [ + + ]: 156027 : if (tsc_submit < g_tsc_submit_min) {
291 : 214 : g_tsc_submit_min = tsc_submit;
292 : : }
293 [ + + ]: 156027 : if (tsc_submit > g_tsc_submit_max) {
294 : 40 : g_tsc_submit_max = tsc_submit;
295 : : }
296 [ + + + - ]: 156027 : if (g_enable_histogram) {
297 : 156027 : spdk_histogram_data_tally(entry->submit_histogram, tsc_submit);
298 : : }
299 : :
300 [ - + ]: 156027 : if (rc != 0) {
301 [ # # # # ]: 0 : fprintf(stderr, "starting I/O failed\n");
302 : : } else {
303 : 156027 : g_ns->current_queue_depth++;
304 : : }
305 : 156027 : }
306 : :
307 : : static void
308 : 156027 : io_complete(void *ctx, const struct spdk_nvme_cpl *completion)
309 : : {
310 : 156027 : g_ns->current_queue_depth--;
311 : 156027 : }
312 : :
313 : : uint64_t g_complete_tsc_start;
314 : :
315 : : static uint64_t
316 : 7817009 : check_io(void)
317 : : {
318 : : uint64_t end, tsc_complete;
319 : :
320 : 7817009 : spdk_rmb();
321 : : #if HAVE_LIBAIO
322 [ - + ]: 7817009 : if (g_ns->type == ENTRY_TYPE_AIO_FILE) {
323 : 0 : aio_check_io();
324 : : } else
325 : : #endif
326 : : {
327 : 7817009 : spdk_nvme_qpair_process_completions(g_ns->u.nvme.qpair, 0);
328 : : }
329 : 7817009 : spdk_rmb();
330 : 7817009 : end = spdk_get_ticks();
331 [ + + ]: 7817009 : if (g_ns->current_queue_depth == 1) {
332 : : /*
333 : : * Account for race condition in AIO case where interrupt occurs
334 : : * after checking for queue depth. If the timestamp capture
335 : : * is too big compared to the last capture, assume that an
336 : : * interrupt fired, and do not bump the start tsc forward. This
337 : : * will ensure this extra time is accounted for next time through
338 : : * when we see current_queue_depth drop to 0.
339 : : */
340 [ - + - - ]: 7660982 : if (g_ns->type == ENTRY_TYPE_NVME_NS || (end - g_complete_tsc_start) < 500) {
341 : 7660982 : g_complete_tsc_start = end;
342 : : }
343 : : } else {
344 : 156027 : tsc_complete = end - g_complete_tsc_start;
345 : 156027 : g_tsc_complete += tsc_complete;
346 [ + + ]: 156027 : if (tsc_complete < g_tsc_complete_min) {
347 : 178 : g_tsc_complete_min = tsc_complete;
348 : : }
349 [ + + ]: 156027 : if (tsc_complete > g_tsc_complete_max) {
350 : 55 : g_tsc_complete_max = tsc_complete;
351 : : }
352 [ + + + - ]: 156027 : if (g_enable_histogram) {
353 : 156027 : spdk_histogram_data_tally(g_ns->complete_histogram, tsc_complete);
354 : : }
355 : 156027 : g_io_completed++;
356 [ + + + + ]: 156027 : if (!g_ns->is_draining) {
357 : 156019 : submit_single_io();
358 : : }
359 : 156027 : end = g_complete_tsc_start = spdk_get_ticks();
360 : : }
361 : :
362 : 7817009 : return end;
363 : : }
364 : :
365 : : static void
366 : 8 : drain_io(void)
367 : : {
368 : 8 : g_ns->is_draining = true;
369 [ + + ]: 412 : while (g_ns->current_queue_depth > 0) {
370 : 404 : check_io();
371 : : }
372 : 8 : }
373 : :
374 : : static int
375 : 8 : init_ns_worker_ctx(void)
376 : : {
377 [ - + ]: 8 : if (g_ns->type == ENTRY_TYPE_AIO_FILE) {
378 : : #ifdef HAVE_LIBAIO
379 : 0 : g_ns->u.aio.events = calloc(1, sizeof(struct io_event));
380 [ # # ]: 0 : if (!g_ns->u.aio.events) {
381 : 0 : return -1;
382 : : }
383 : 0 : g_ns->u.aio.ctx = 0;
384 [ # # ]: 0 : if (io_setup(1, &g_ns->u.aio.ctx) < 0) {
385 : 0 : free(g_ns->u.aio.events);
386 : 0 : perror("io_setup");
387 : 0 : return -1;
388 : : }
389 : : #endif
390 : : } else {
391 : : /*
392 : : * TODO: If a controller has multiple namespaces, they could all use the same queue.
393 : : * For now, give each namespace/thread combination its own queue.
394 : : */
395 : 8 : g_ns->u.nvme.qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_ns->u.nvme.ctrlr, NULL, 0);
396 [ - + ]: 8 : if (!g_ns->u.nvme.qpair) {
397 [ # # ]: 0 : printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair failed\n");
398 : 0 : return -1;
399 : : }
400 : : }
401 : :
402 : 8 : return 0;
403 : : }
404 : :
405 : : static void
406 : 8 : cleanup_ns_worker_ctx(void)
407 : : {
408 [ - + ]: 8 : if (g_ns->type == ENTRY_TYPE_AIO_FILE) {
409 : : #ifdef HAVE_LIBAIO
410 : 0 : io_destroy(g_ns->u.aio.ctx);
411 : 0 : free(g_ns->u.aio.events);
412 : : #endif
413 : : } else {
414 : 8 : spdk_nvme_ctrlr_free_io_qpair(g_ns->u.nvme.qpair);
415 : : }
416 : 8 : }
417 : :
418 : : static int
419 : 8 : work_fn(void)
420 : : {
421 : : uint64_t tsc_end, current;
422 : :
423 : : /* Allocate a queue pair for each namespace. */
424 [ - + ]: 8 : if (init_ns_worker_ctx() != 0) {
425 [ # # ]: 0 : printf("ERROR: init_ns_worker_ctx() failed\n");
426 : 0 : return 1;
427 : : }
428 : :
429 : 8 : tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate;
430 : :
431 : : /* Submit initial I/O for each namespace. */
432 : 8 : submit_single_io();
433 : 8 : g_complete_tsc_start = spdk_get_ticks();
434 : :
435 : : while (1) {
436 : : /*
437 : : * Check for completed I/O for each controller. A new
438 : : * I/O will be submitted in the io_complete callback
439 : : * to replace each I/O that is completed.
440 : : */
441 : 7816605 : current = check_io();
442 : :
443 [ + + ]: 7816605 : if (current > tsc_end) {
444 : 8 : break;
445 : : }
446 : : }
447 : :
448 : 8 : drain_io();
449 : 8 : cleanup_ns_worker_ctx();
450 : :
451 : 8 : return 0;
452 : : }
453 : :
454 : : static void
455 : 0 : usage(char *program_name)
456 : : {
457 [ # # ]: 0 : printf("%s options", program_name);
458 : : #if HAVE_LIBAIO
459 [ # # ]: 0 : printf(" [AIO device(s)]...");
460 : : #endif
461 [ # # ]: 0 : printf("\t\n");
462 [ # # ]: 0 : printf("\t[-d DPDK huge memory size in MB]\n");
463 [ # # ]: 0 : printf("\t[-o io size in bytes]\n");
464 [ # # ]: 0 : printf("\t[-t time in seconds]\n");
465 [ # # ]: 0 : printf("\t\t(default: 1)]\n");
466 [ # # ]: 0 : printf("\t[-H enable histograms]\n");
467 [ # # ]: 0 : printf("\t[-g use single file descriptor for DPDK memory segments]\n");
468 [ # # ]: 0 : printf("\t[-i shared memory group ID]\n");
469 [ # # ]: 0 : printf("\t[-r remote NVMe over Fabrics target address]\n");
470 : : #ifdef DEBUG
471 [ # # ]: 0 : printf("\t[-L enable debug logging]\n");
472 : : #else
473 : : printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n");
474 : : #endif
475 : 0 : spdk_log_usage(stdout, "\t\t-L");
476 : 0 : }
477 : :
478 : : static void
479 : 118784 : print_bucket(void *ctx, uint64_t start, uint64_t end, uint64_t count,
480 : : uint64_t total, uint64_t so_far)
481 : : {
482 : : double so_far_pct;
483 : :
484 [ + + ]: 118784 : if (count == 0) {
485 : 115762 : return;
486 : : }
487 : :
488 : 3022 : so_far_pct = (double)so_far * 100 / total;
489 : :
490 : 3022 : printf("%9.3f - %9.3f: %9.4f%% (%9ju)\n",
491 : 3022 : (double)start * 1000 * 1000 / g_tsc_rate,
492 [ - + ]: 3022 : (double)end * 1000 * 1000 / g_tsc_rate,
493 : : so_far_pct, count);
494 : : }
495 : :
496 : : static void
497 : 8 : print_stats(void)
498 : : {
499 : 8 : double divisor = (double)g_tsc_rate / (1000 * 1000 * 1000);
500 : :
501 : 8 : printf("submit (in ns) avg, min, max = %8.1f, %8.1f, %8.1f\n",
502 : 8 : (double)g_tsc_submit / g_io_completed / divisor,
503 : 8 : (double)g_tsc_submit_min / divisor,
504 [ - + ]: 8 : (double)g_tsc_submit_max / divisor);
505 : 8 : printf("complete (in ns) avg, min, max = %8.1f, %8.1f, %8.1f\n",
506 : 8 : (double)g_tsc_complete / g_io_completed / divisor,
507 : 8 : (double)g_tsc_complete_min / divisor,
508 [ - + ]: 8 : (double)g_tsc_complete_max / divisor);
509 : :
510 [ - + - + ]: 8 : if (!g_enable_histogram) {
511 : 0 : return;
512 : : }
513 : :
514 : 8 : printf("\n");
515 [ - + ]: 8 : printf("Submit histogram\n");
516 [ - + ]: 8 : printf("================\n");
517 [ - + ]: 8 : printf(" Range in us Cumulative Count\n");
518 : 8 : spdk_histogram_data_iterate(g_ns->submit_histogram, print_bucket, NULL);
519 : 8 : printf("\n");
520 : :
521 [ - + ]: 8 : printf("Complete histogram\n");
522 [ - + ]: 8 : printf("==================\n");
523 [ - + ]: 8 : printf(" Range in us Cumulative Count\n");
524 : 8 : spdk_histogram_data_iterate(g_ns->complete_histogram, print_bucket, NULL);
525 : 8 : printf("\n");
526 : :
527 : : }
528 : :
529 : : static int
530 : 8 : parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
531 : : {
532 : : int op, rc;
533 : : long int val;
534 : :
535 : : /* default value */
536 : 8 : g_io_size_bytes = 0;
537 : 8 : g_time_in_sec = 0;
538 : :
539 : 8 : spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE);
540 [ - + ]: 8 : snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN);
541 : :
542 [ + + + + : 44 : while ((op = getopt(argc, argv, "d:ghi:o:r:t:HL:")) != -1) {
+ + ]
543 [ - + + + : 36 : switch (op) {
+ + + + -
- ]
544 : 0 : case 'h':
545 : 0 : usage(argv[0]);
546 : 0 : exit(0);
547 : : break;
548 : 8 : case 'o':
549 : 8 : val = spdk_strtol(optarg, 10);
550 [ - + ]: 8 : if (val < 0) {
551 [ # # # # ]: 0 : fprintf(stderr, "Invalid io size\n");
552 : 0 : return val;
553 : : }
554 : 8 : g_io_size_bytes = (uint32_t)val;
555 : 8 : break;
556 : 8 : case 't':
557 : 8 : g_time_in_sec = spdk_strtol(optarg, 10);
558 [ - + ]: 8 : if (g_time_in_sec < 0) {
559 [ # # # # ]: 0 : fprintf(stderr, "Invalid run time\n");
560 : 0 : return g_time_in_sec;
561 : : }
562 : 8 : break;
563 : 8 : case 'H':
564 : 8 : g_enable_histogram = true;
565 : 8 : break;
566 : 6 : case 'i':
567 : 6 : env_opts->shm_id = spdk_strtol(optarg, 10);
568 [ - + ]: 6 : if (env_opts->shm_id < 0) {
569 [ # # # # ]: 0 : fprintf(stderr, "Invalid shared memory ID\n");
570 : 0 : return env_opts->shm_id;
571 : : }
572 : 6 : break;
573 : 2 : case 'g':
574 : 2 : env_opts->hugepage_single_segments = true;
575 : 2 : break;
576 : 2 : case 'r':
577 [ - + ]: 2 : if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) {
578 [ # # # # ]: 0 : fprintf(stderr, "Error parsing transport address\n");
579 : 0 : return 1;
580 : : }
581 : 2 : break;
582 : 2 : case 'd':
583 : 2 : env_opts->mem_size = spdk_strtol(optarg, 10);
584 [ - + ]: 2 : if (env_opts->mem_size < 0) {
585 [ # # # # ]: 0 : fprintf(stderr, "Invalid DPDK memory size\n");
586 : 0 : return env_opts->mem_size;
587 : : }
588 : 2 : break;
589 : 0 : case 'L':
590 : 0 : rc = spdk_log_set_flag(optarg);
591 [ # # ]: 0 : if (rc < 0) {
592 [ # # # # ]: 0 : fprintf(stderr, "unknown flag\n");
593 : 0 : usage(argv[0]);
594 : 0 : exit(EXIT_FAILURE);
595 : : }
596 : : #ifdef DEBUG
597 : 0 : spdk_log_set_print_level(SPDK_LOG_DEBUG);
598 : : #endif
599 : 0 : break;
600 : 0 : default:
601 : 0 : usage(argv[0]);
602 : 0 : return 1;
603 : : }
604 : : }
605 : :
606 [ - + ]: 8 : if (!g_io_size_bytes) {
607 : 0 : usage(argv[0]);
608 : 0 : return 1;
609 : : }
610 [ - + ]: 8 : if (!g_time_in_sec) {
611 : 0 : usage(argv[0]);
612 : 0 : return 1;
613 : : }
614 : :
615 : 8 : g_aio_optind = optind;
616 : :
617 : 8 : return 0;
618 : : }
619 : :
620 : : static bool
621 : 2 : probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
622 : : struct spdk_nvme_ctrlr_opts *opts)
623 : : {
624 : : static uint32_t ctrlr_found = 0;
625 : :
626 [ - + ]: 2 : if (ctrlr_found == 1) {
627 [ # # # # ]: 0 : fprintf(stderr, "only attaching to one controller, so skipping\n");
628 [ # # ]: 0 : fprintf(stderr, " controller at PCI address %s\n",
629 [ # # ]: 0 : trid->traddr);
630 : 0 : return false;
631 : : }
632 : 2 : ctrlr_found = 1;
633 : :
634 [ - + ]: 2 : printf("Attaching to %s\n", trid->traddr);
635 : :
636 : 2 : return true;
637 : : }
638 : :
639 : : static void
640 : 12 : attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
641 : : struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
642 : : {
643 [ - + ]: 12 : printf("Attached to %s\n", trid->traddr);
644 : :
645 : 12 : register_ctrlr(ctrlr);
646 : 12 : }
647 : :
648 : : static int
649 : 8 : register_controllers(void)
650 : : {
651 [ - + ]: 8 : printf("Initializing NVMe Controllers\n");
652 : :
653 [ - + ]: 8 : if (spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL) != 0) {
654 [ # # # # ]: 0 : fprintf(stderr, "spdk_nvme_probe() failed\n");
655 : 0 : return 1;
656 : : }
657 : :
658 [ - + ]: 8 : if (g_ns == NULL) {
659 [ # # # # ]: 0 : fprintf(stderr, "no NVMe controller found - check that device is bound to uio/vfio\n");
660 : 0 : return 1;
661 : : }
662 : :
663 : 8 : return 0;
664 : : }
665 : :
666 : : static void
667 : 8 : cleanup(void)
668 : : {
669 : 8 : struct ns_entry *ns_entry = g_ns;
670 : : struct ctrlr_entry *ctrlr_entry, *tmp_ctrlr_entry;
671 : 8 : struct spdk_nvme_detach_ctx *detach_ctx = NULL;
672 : :
673 [ + + ]: 20 : while (ns_entry) {
674 : 12 : struct ns_entry *next = ns_entry->next;
675 : :
676 : 12 : spdk_histogram_data_free(ns_entry->submit_histogram);
677 : 12 : spdk_histogram_data_free(ns_entry->complete_histogram);
678 : 12 : free(ns_entry);
679 : 12 : ns_entry = next;
680 : : }
681 : :
682 [ + + ]: 20 : TAILQ_FOREACH_SAFE(ctrlr_entry, &g_ctrlr, link, tmp_ctrlr_entry) {
683 [ + + ]: 12 : TAILQ_REMOVE(&g_ctrlr, ctrlr_entry, link);
684 : 12 : spdk_nvme_detach_async(ctrlr_entry->ctrlr, &detach_ctx);
685 : 12 : free(ctrlr_entry);
686 : : }
687 : :
688 [ + + ]: 8 : if (detach_ctx) {
689 : 2 : spdk_nvme_detach_poll(detach_ctx);
690 : : }
691 : 8 : }
692 : :
693 : : int
694 : 8 : main(int argc, char **argv)
695 : : {
696 : : int rc;
697 : 3 : struct spdk_env_opts opts;
698 : :
699 : 8 : spdk_env_opts_init(&opts);
700 : 8 : rc = parse_args(argc, argv, &opts);
701 [ - + ]: 8 : if (rc != 0) {
702 : 0 : return rc;
703 : : }
704 : :
705 : 8 : opts.name = "overhead";
706 : 8 : opts.core_mask = "0x1";
707 [ - + ]: 8 : if (spdk_env_init(&opts) < 0) {
708 [ # # # # ]: 0 : fprintf(stderr, "Unable to initialize SPDK env\n");
709 : 0 : return 1;
710 : : }
711 : :
712 : 8 : g_task = spdk_zmalloc(sizeof(struct perf_task), 0, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
713 [ - + ]: 8 : if (g_task == NULL) {
714 [ # # # # ]: 0 : fprintf(stderr, "g_task alloc failed\n");
715 : 0 : exit(1);
716 : : }
717 : :
718 : 8 : g_task->buf = spdk_zmalloc(g_io_size_bytes, 0x1000, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
719 [ - + ]: 8 : if (g_task->buf == NULL) {
720 [ # # # # ]: 0 : fprintf(stderr, "g_task->buf spdk_zmalloc failed\n");
721 : 0 : exit(1);
722 : : }
723 : :
724 : 8 : g_tsc_rate = spdk_get_ticks_hz();
725 : :
726 : : #if HAVE_LIBAIO
727 [ - + ]: 8 : if (g_aio_optind < argc) {
728 [ # # ]: 0 : printf("Measuring overhead for AIO device %s.\n", argv[g_aio_optind]);
729 [ # # ]: 0 : if (register_aio_file(argv[g_aio_optind]) != 0) {
730 : 0 : cleanup();
731 : 0 : return -1;
732 : : }
733 : : } else
734 : : #endif
735 : : {
736 [ - + ]: 8 : if (register_controllers() != 0) {
737 : 0 : cleanup();
738 : 0 : return -1;
739 : : }
740 : : }
741 : :
742 [ - + ]: 8 : printf("Initialization complete. Launching workers.\n");
743 : :
744 : 8 : rc = work_fn();
745 : :
746 : 8 : print_stats();
747 : :
748 : 8 : cleanup();
749 : :
750 [ - + ]: 8 : if (rc != 0) {
751 [ # # # # ]: 0 : fprintf(stderr, "%s: errors occurred\n", argv[0]);
752 : : }
753 : :
754 : 8 : return rc;
755 : : }
|