Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2020 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : :
8 : : #include "spdk/env.h"
9 : : #include "spdk/log.h"
10 : : #include "spdk/nvme.h"
11 : : #include "spdk/queue.h"
12 : : #include "spdk/string.h"
13 : : #include "spdk/util.h"
14 : : #include "spdk/likely.h"
15 : :
16 : : #define ABORT_GETOPT_STRING "a:c:i:l:o:q:r:s:t:w:GM:T:"
17 : : struct ctrlr_entry {
18 : : struct spdk_nvme_ctrlr *ctrlr;
19 : : enum spdk_nvme_transport_type trtype;
20 : :
21 : : TAILQ_ENTRY(ctrlr_entry) link;
22 : : char name[1024];
23 : : };
24 : :
25 : : struct ns_entry {
26 : : struct spdk_nvme_ctrlr *ctrlr;
27 : : struct spdk_nvme_ns *ns;
28 : :
29 : : TAILQ_ENTRY(ns_entry) link;
30 : : uint32_t io_size_blocks;
31 : : uint32_t num_io_requests;
32 : : uint64_t size_in_ios;
33 : : uint32_t block_size;
34 : : char name[1024];
35 : : };
36 : :
37 : : struct ctrlr_worker_ctx {
38 : : pthread_mutex_t mutex;
39 : : struct ctrlr_entry *entry;
40 : : uint64_t abort_submitted;
41 : : uint64_t abort_submit_failed;
42 : : uint64_t successful_abort;
43 : : uint64_t unsuccessful_abort;
44 : : uint64_t abort_failed;
45 : : uint64_t current_queue_depth;
46 : : struct spdk_nvme_ctrlr *ctrlr;
47 : : TAILQ_ENTRY(ctrlr_worker_ctx) link;
48 : : };
49 : :
50 : : struct ns_worker_ctx {
51 : : struct ns_entry *entry;
52 : : uint64_t io_submitted;
53 : : uint64_t io_completed;
54 : : uint64_t io_aborted;
55 : : uint64_t io_failed;
56 : : uint64_t current_queue_depth;
57 : : uint64_t offset_in_ios;
58 : : bool is_draining;
59 : : struct spdk_nvme_qpair *qpair;
60 : : struct ctrlr_worker_ctx *ctrlr_ctx;
61 : : TAILQ_ENTRY(ns_worker_ctx) link;
62 : : };
63 : :
64 : : struct perf_task {
65 : : struct ns_worker_ctx *ns_ctx;
66 : : void *buf;
67 : : };
68 : :
69 : : struct worker_thread {
70 : : TAILQ_HEAD(, ns_worker_ctx) ns_ctx;
71 : : TAILQ_HEAD(, ctrlr_worker_ctx) ctrlr_ctx;
72 : : TAILQ_ENTRY(worker_thread) link;
73 : : unsigned lcore;
74 : : int status;
75 : : };
76 : :
77 : : static const char *g_workload_type = "read";
78 : : static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers);
79 : : static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces);
80 : : static int g_num_namespaces;
81 : : static TAILQ_HEAD(, worker_thread) g_workers = TAILQ_HEAD_INITIALIZER(g_workers);
82 : : static int g_num_workers = 0;
83 : : static uint32_t g_main_core;
84 : :
85 : : static int g_abort_interval = 1;
86 : :
87 : : static uint64_t g_tsc_rate;
88 : :
89 : : static uint32_t g_io_size_bytes = 131072;
90 : : static uint32_t g_max_io_size_blocks;
91 : : static int g_rw_percentage = -1;
92 : : static int g_is_random;
93 : : static int g_queue_depth = 128;
94 : : static int g_time_in_sec = 3;
95 : : static int g_dpdk_mem;
96 : : static int g_shm_id = -1;
97 : : static bool g_no_pci;
98 : : static bool g_warn;
99 : : static bool g_mix_specified;
100 : : static bool g_no_hugepages;
101 : :
102 : : static const char *g_core_mask;
103 : :
104 : : static const struct option g_abort_cmdline_opts[] = {
105 : : #define ABORT_NO_HUGE 257
106 : : {"no-huge", no_argument, NULL, ABORT_NO_HUGE},
107 : : {0, 0, 0, 0}
108 : : };
109 : :
110 : : struct trid_entry {
111 : : struct spdk_nvme_transport_id trid;
112 : : uint16_t nsid;
113 : : TAILQ_ENTRY(trid_entry) tailq;
114 : : };
115 : :
116 : : static TAILQ_HEAD(, trid_entry) g_trid_list = TAILQ_HEAD_INITIALIZER(g_trid_list);
117 : :
118 : : static void io_complete(void *ctx, const struct spdk_nvme_cpl *cpl);
119 : :
120 : : static int
121 : 48 : build_nvme_name(char *name, size_t length, struct spdk_nvme_ctrlr *ctrlr)
122 : : {
123 : : const struct spdk_nvme_transport_id *trid;
124 : 48 : int res = 0;
125 : :
126 : 48 : trid = spdk_nvme_ctrlr_get_transport_id(ctrlr);
127 : :
128 [ - + + - : 48 : switch (trid->trtype) {
- ]
129 : 0 : case SPDK_NVME_TRANSPORT_PCIE:
130 [ # # ]: 0 : res = snprintf(name, length, "PCIE (%s)", trid->traddr);
131 : 0 : break;
132 : 2 : case SPDK_NVME_TRANSPORT_RDMA:
133 [ - + ]: 2 : res = snprintf(name, length, "RDMA (addr:%s subnqn:%s)", trid->traddr, trid->subnqn);
134 : 2 : break;
135 : 46 : case SPDK_NVME_TRANSPORT_TCP:
136 [ - + ]: 46 : res = snprintf(name, length, "TCP (addr:%s subnqn:%s)", trid->traddr, trid->subnqn);
137 : 46 : break;
138 : 0 : case SPDK_NVME_TRANSPORT_CUSTOM:
139 [ # # ]: 0 : res = snprintf(name, length, "CUSTOM (%s)", trid->traddr);
140 : 0 : break;
141 : :
142 : 0 : default:
143 [ # # # # ]: 0 : fprintf(stderr, "Unknown transport type %d\n", trid->trtype);
144 : 0 : break;
145 : : }
146 : 48 : return res;
147 : : }
148 : :
149 : : static void
150 : 24 : build_nvme_ns_name(char *name, size_t length, struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid)
151 : : {
152 : 24 : int res = 0;
153 : :
154 : 24 : res = build_nvme_name(name, length, ctrlr);
155 [ + - ]: 24 : if (res > 0) {
156 [ - + ]: 24 : snprintf(name + res, length - res, " NSID %u", nsid);
157 : : }
158 : :
159 : 24 : }
160 : :
161 : : static void
162 : 24 : register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
163 : : {
164 : : struct ns_entry *entry;
165 : : const struct spdk_nvme_ctrlr_data *cdata;
166 : : uint32_t max_xfer_size, entries, sector_size;
167 : : uint64_t ns_size;
168 : 0 : struct spdk_nvme_io_qpair_opts opts;
169 : :
170 : 24 : cdata = spdk_nvme_ctrlr_get_data(ctrlr);
171 : :
172 [ - + ]: 24 : if (!spdk_nvme_ns_is_active(ns)) {
173 : 0 : printf("Controller %-20.20s (%-20.20s): Skipping inactive NS %u\n",
174 [ # # ]: 0 : cdata->mn, cdata->sn,
175 : : spdk_nvme_ns_get_id(ns));
176 : 0 : g_warn = true;
177 : 0 : return;
178 : : }
179 : :
180 : 24 : ns_size = spdk_nvme_ns_get_size(ns);
181 : 24 : sector_size = spdk_nvme_ns_get_sector_size(ns);
182 : :
183 [ + - - + ]: 24 : if (ns_size < g_io_size_bytes || sector_size > g_io_size_bytes) {
184 : 0 : printf("WARNING: controller %-20.20s (%-20.20s) ns %u has invalid "
185 : : "ns size %" PRIu64 " / block size %u for I/O size %u\n",
186 [ # # ]: 0 : cdata->mn, cdata->sn, spdk_nvme_ns_get_id(ns),
187 : : ns_size, spdk_nvme_ns_get_sector_size(ns), g_io_size_bytes);
188 : 0 : g_warn = true;
189 : 0 : return;
190 : : }
191 : :
192 : 24 : max_xfer_size = spdk_nvme_ns_get_max_io_xfer_size(ns);
193 : 24 : spdk_nvme_ctrlr_get_default_io_qpair_opts(ctrlr, &opts, sizeof(opts));
194 : : /* NVMe driver may add additional entries based on
195 : : * stripe size and maximum transfer size, we assume
196 : : * 1 more entry be used for stripe.
197 : : */
198 [ - + ]: 24 : entries = (g_io_size_bytes - 1) / max_xfer_size + 2;
199 [ + + ]: 24 : if ((g_queue_depth * entries) > opts.io_queue_size) {
200 [ - + ]: 3 : printf("controller IO queue size %u less than required\n",
201 : : opts.io_queue_size);
202 [ - + ]: 3 : printf("Consider using lower queue depth or small IO size because "
203 : : "IO requests may be queued at the NVMe driver.\n");
204 : : }
205 : : /* For requests which have children requests, parent request itself
206 : : * will also occupy 1 entry.
207 : : */
208 : 24 : entries += 1;
209 : :
210 : 24 : entry = calloc(1, sizeof(struct ns_entry));
211 [ - + ]: 24 : if (entry == NULL) {
212 : 0 : perror("ns_entry malloc");
213 : 0 : exit(1);
214 : : }
215 : :
216 : 24 : entry->ctrlr = ctrlr;
217 : 24 : entry->ns = ns;
218 : 24 : entry->num_io_requests = g_queue_depth * entries;
219 : :
220 [ - + ]: 24 : entry->size_in_ios = ns_size / g_io_size_bytes;
221 [ - + ]: 24 : entry->io_size_blocks = g_io_size_bytes / sector_size;
222 : :
223 : 24 : entry->block_size = spdk_nvme_ns_get_sector_size(ns);
224 : :
225 [ + - ]: 24 : if (g_max_io_size_blocks < entry->io_size_blocks) {
226 : 24 : g_max_io_size_blocks = entry->io_size_blocks;
227 : : }
228 : :
229 : 24 : build_nvme_ns_name(entry->name, sizeof(entry->name), ctrlr, spdk_nvme_ns_get_id(ns));
230 : :
231 : 24 : g_num_namespaces++;
232 : 24 : TAILQ_INSERT_TAIL(&g_namespaces, entry, link);
233 : : }
234 : :
235 : : static void
236 : 24 : unregister_namespaces(void)
237 : : {
238 : : struct ns_entry *entry, *tmp;
239 : :
240 [ + + ]: 48 : TAILQ_FOREACH_SAFE(entry, &g_namespaces, link, tmp) {
241 [ - + ]: 24 : TAILQ_REMOVE(&g_namespaces, entry, link);
242 : 24 : free(entry);
243 : : }
244 : 24 : }
245 : :
246 : : static void
247 : 24 : register_ctrlr(struct spdk_nvme_ctrlr *ctrlr, struct trid_entry *trid_entry)
248 : : {
249 : : struct spdk_nvme_ns *ns;
250 : 24 : struct ctrlr_entry *entry = malloc(sizeof(struct ctrlr_entry));
251 : : uint32_t nsid;
252 : :
253 [ - + ]: 24 : if (entry == NULL) {
254 : 0 : perror("ctrlr_entry malloc");
255 : 0 : exit(1);
256 : : }
257 : :
258 : 24 : build_nvme_name(entry->name, sizeof(entry->name), ctrlr);
259 : :
260 : 24 : entry->ctrlr = ctrlr;
261 : 24 : entry->trtype = trid_entry->trid.trtype;
262 : 24 : TAILQ_INSERT_TAIL(&g_controllers, entry, link);
263 : :
264 [ + + ]: 24 : if (trid_entry->nsid == 0) {
265 [ # # ]: 21 : for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr);
266 [ + + ]: 42 : nsid != 0; nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) {
267 : 21 : ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
268 [ - + ]: 21 : if (ns == NULL) {
269 : 0 : continue;
270 : : }
271 : 21 : register_ns(ctrlr, ns);
272 : : }
273 : : } else {
274 : 3 : ns = spdk_nvme_ctrlr_get_ns(ctrlr, trid_entry->nsid);
275 [ - + ]: 3 : if (!ns) {
276 : 0 : perror("Namespace does not exist.");
277 : 0 : exit(1);
278 : : }
279 : :
280 : 3 : register_ns(ctrlr, ns);
281 : : }
282 : 24 : }
283 : :
284 : : static void
285 : 484013 : abort_complete(void *ctx, const struct spdk_nvme_cpl *cpl)
286 : : {
287 : 484013 : struct ctrlr_worker_ctx *ctrlr_ctx = ctx;
288 : :
289 : 484013 : ctrlr_ctx->current_queue_depth--;
290 [ + - - + ]: 484013 : if (spdk_unlikely(spdk_nvme_cpl_is_error(cpl))) {
291 : 0 : ctrlr_ctx->abort_failed++;
292 [ + + ]: 484013 : } else if ((cpl->cdw0 & 0x1) == 0) {
293 : 158307 : ctrlr_ctx->successful_abort++;
294 : : } else {
295 : 325706 : ctrlr_ctx->unsuccessful_abort++;
296 : : }
297 : 484013 : }
298 : :
299 : : static void
300 : 991217 : abort_task(struct perf_task *task)
301 : : {
302 : 991217 : struct ns_worker_ctx *ns_ctx = task->ns_ctx;
303 : 991217 : struct ctrlr_worker_ctx *ctrlr_ctx = ns_ctx->ctrlr_ctx;
304 : : int rc;
305 : :
306 : : /* Hold mutex to guard ctrlr_ctx->current_queue_depth. */
307 [ - + ]: 991217 : pthread_mutex_lock(&ctrlr_ctx->mutex);
308 : :
309 : 991217 : rc = spdk_nvme_ctrlr_cmd_abort_ext(ctrlr_ctx->ctrlr, ns_ctx->qpair, task, abort_complete,
310 : : ctrlr_ctx);
311 : :
312 [ + + ]: 991217 : if (spdk_unlikely(rc != 0)) {
313 : 507204 : ctrlr_ctx->abort_submit_failed++;
314 : : } else {
315 : 484013 : ctrlr_ctx->current_queue_depth++;
316 : 484013 : ctrlr_ctx->abort_submitted++;
317 : : }
318 : :
319 [ - + ]: 991217 : pthread_mutex_unlock(&ctrlr_ctx->mutex);
320 : 991217 : }
321 : :
322 : : static __thread unsigned int seed = 0;
323 : :
324 : : static inline void
325 : 991217 : submit_single_io(struct perf_task *task)
326 : : {
327 : : uint64_t offset_in_ios, lba;
328 : : int rc;
329 : 991217 : struct ns_worker_ctx *ns_ctx = task->ns_ctx;
330 : 991217 : struct ns_entry *entry = ns_ctx->entry;
331 : :
332 [ + + ]: 991217 : if (g_is_random) {
333 [ - + ]: 22074 : offset_in_ios = rand_r(&seed) % entry->size_in_ios;
334 : : } else {
335 : 969143 : offset_in_ios = ns_ctx->offset_in_ios++;
336 [ + + ]: 969143 : if (ns_ctx->offset_in_ios == entry->size_in_ios) {
337 : 258 : ns_ctx->offset_in_ios = 0;
338 : : }
339 : : }
340 : :
341 : 991217 : lba = offset_in_ios * entry->io_size_blocks;
342 : :
343 [ + + ]: 991217 : if ((g_rw_percentage == 100) ||
344 [ + - + + ]: 858591 : (g_rw_percentage != 0 && (rand_r(&seed) % 100) < g_rw_percentage)) {
345 : 562202 : rc = spdk_nvme_ns_cmd_read(entry->ns, ns_ctx->qpair, task->buf,
346 : : lba, entry->io_size_blocks, io_complete, task, 0);
347 : : } else {
348 : 429015 : rc = spdk_nvme_ns_cmd_write(entry->ns, ns_ctx->qpair, task->buf,
349 : : lba, entry->io_size_blocks, io_complete, task, 0);
350 : : }
351 : :
352 [ - + ]: 991217 : if (spdk_unlikely(rc != 0)) {
353 [ # # ]: 0 : fprintf(stderr, "I/O submission failed\n");
354 : : } else {
355 : 991217 : ns_ctx->current_queue_depth++;
356 : 991217 : ns_ctx->io_submitted++;
357 : :
358 [ - + + - ]: 991217 : if ((ns_ctx->io_submitted % g_abort_interval) == 0) {
359 : 991217 : abort_task(task);
360 : : }
361 : : }
362 : :
363 : 991217 : }
364 : :
365 : : static void
366 : 991217 : io_complete(void *ctx, const struct spdk_nvme_cpl *cpl)
367 : : {
368 : 991217 : struct perf_task *task = ctx;
369 : 991217 : struct ns_worker_ctx *ns_ctx = task->ns_ctx;
370 : :
371 : 991217 : ns_ctx->current_queue_depth--;
372 [ + + - + ]: 991217 : if (spdk_unlikely(spdk_nvme_cpl_is_error(cpl))) {
373 : 153443 : ns_ctx->io_failed++;
374 : : } else {
375 : 837774 : ns_ctx->io_completed++;
376 : : }
377 : :
378 : : /* is_draining indicates when time has expired for the test run and we are
379 : : * just waiting for the previously submitted I/O to complete. In this case,
380 : : * do not submit a new I/O to replace the one just completed.
381 : : */
382 [ - + + + ]: 991217 : if (spdk_unlikely(ns_ctx->is_draining)) {
383 : 1128 : spdk_dma_free(task->buf);
384 : 1128 : free(task);
385 : : } else {
386 : 990089 : submit_single_io(task);
387 : : }
388 : 991217 : }
389 : :
390 : : static struct perf_task *
391 : 1128 : allocate_task(struct ns_worker_ctx *ns_ctx)
392 : : {
393 : : struct perf_task *task;
394 : :
395 : 1128 : task = calloc(1, sizeof(*task));
396 [ - + ]: 1128 : if (task == NULL) {
397 [ # # # # ]: 0 : fprintf(stderr, "Failed to allocate task\n");
398 : 0 : exit(1);
399 : : }
400 : :
401 : 1128 : task->buf = spdk_dma_zmalloc(g_io_size_bytes, 0x200, NULL);
402 [ - + ]: 1128 : if (task->buf == NULL) {
403 : 0 : free(task);
404 [ # # # # ]: 0 : fprintf(stderr, "Failed to allocate task->buf\n");
405 : 0 : exit(1);
406 : : }
407 : :
408 : 1128 : task->ns_ctx = ns_ctx;
409 : :
410 : 1128 : return task;
411 : : }
412 : :
413 : : static void
414 : 24 : submit_io(struct ns_worker_ctx *ns_ctx, int queue_depth)
415 : : {
416 : : struct perf_task *task;
417 : :
418 [ + + ]: 1152 : while (queue_depth-- > 0) {
419 : 1128 : task = allocate_task(ns_ctx);
420 : 1128 : submit_single_io(task);
421 : : }
422 : 24 : }
423 : :
424 : : static int
425 : 24 : work_fn(void *arg)
426 : : {
427 : 24 : struct worker_thread *worker = (struct worker_thread *)arg;
428 : : struct ns_worker_ctx *ns_ctx;
429 : : struct ctrlr_worker_ctx *ctrlr_ctx;
430 : : struct ns_entry *ns_entry;
431 : 0 : struct spdk_nvme_io_qpair_opts opts;
432 : : uint64_t tsc_end;
433 : : uint32_t unfinished_ctx;
434 : 24 : int rc = 0;
435 : :
436 : : /* Allocate queue pair for each namespace. */
437 [ + + ]: 48 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
438 : 24 : ns_entry = ns_ctx->entry;
439 : :
440 : 24 : spdk_nvme_ctrlr_get_default_io_qpair_opts(ns_entry->ctrlr, &opts, sizeof(opts));
441 [ - + ]: 24 : if (opts.io_queue_requests < ns_entry->num_io_requests) {
442 : 0 : opts.io_queue_requests = ns_entry->num_io_requests;
443 : : }
444 : :
445 : 24 : ns_ctx->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ns_entry->ctrlr, &opts, sizeof(opts));
446 [ - + ]: 24 : if (ns_ctx->qpair == NULL) {
447 [ # # # # ]: 0 : fprintf(stderr, "spdk_nvme_ctrlr_alloc_io_qpair failed\n");
448 : 0 : worker->status = -ENOMEM;
449 : 0 : goto out;
450 : : }
451 : : }
452 : :
453 : 24 : tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate;
454 : :
455 : : /* Submit initial I/O for each namespace. */
456 [ + + ]: 48 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
457 : 24 : submit_io(ns_ctx, g_queue_depth);
458 : : }
459 : :
460 : : while (1) {
461 [ + + ]: 10393670 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
462 : 5196835 : rc = spdk_nvme_qpair_process_completions(ns_ctx->qpair, 0);
463 [ - + ]: 5196835 : if (rc < 0) {
464 [ # # # # ]: 0 : fprintf(stderr, "spdk_nvme_qpair_process_completions returned "
465 : : "%d\n", rc);
466 : 0 : worker->status = rc;
467 : 0 : goto out;
468 : : }
469 : : }
470 : :
471 [ + - ]: 5196835 : if (worker->lcore == g_main_core) {
472 [ + + ]: 10393670 : TAILQ_FOREACH(ctrlr_ctx, &worker->ctrlr_ctx, link) {
473 : : /* Hold mutex to guard ctrlr_ctx->current_queue_depth. */
474 [ - + ]: 5196835 : pthread_mutex_lock(&ctrlr_ctx->mutex);
475 : 5196835 : rc = spdk_nvme_ctrlr_process_admin_completions(ctrlr_ctx->ctrlr);
476 [ - + ]: 5196835 : pthread_mutex_unlock(&ctrlr_ctx->mutex);
477 [ - + ]: 5196835 : if (rc < 0) {
478 [ # # # # ]: 0 : fprintf(stderr, "spdk_nvme_ctrlr_process_admin_completions "
479 : : "returned %d\n", rc);
480 : 0 : worker->status = rc;
481 : 0 : goto out;
482 : : }
483 : : }
484 : : }
485 : :
486 [ + + ]: 5196835 : if (spdk_get_ticks() > tsc_end) {
487 : 24 : break;
488 : : }
489 : : }
490 : :
491 : : do {
492 : 3062441 : unfinished_ctx = 0;
493 : :
494 [ + + ]: 6124882 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
495 [ - + + + ]: 3062441 : if (!ns_ctx->is_draining) {
496 : 24 : ns_ctx->is_draining = true;
497 : : }
498 [ + + ]: 3062441 : if (ns_ctx->current_queue_depth > 0) {
499 : 3062417 : rc = spdk_nvme_qpair_process_completions(ns_ctx->qpair, 0);
500 [ - + ]: 3062417 : if (rc < 0) {
501 [ # # # # ]: 0 : fprintf(stderr, "spdk_nvme_qpair_process_completions "
502 : : "returned %d\n", rc);
503 : 0 : worker->status = rc;
504 : 0 : goto out;
505 : : }
506 : 3062417 : unfinished_ctx++;
507 : : }
508 : : }
509 [ + + ]: 3062441 : } while (unfinished_ctx > 0);
510 : :
511 [ + - ]: 24 : if (worker->lcore == g_main_core) {
512 : : do {
513 : 22905 : unfinished_ctx = 0;
514 : :
515 [ + + ]: 45810 : TAILQ_FOREACH(ctrlr_ctx, &worker->ctrlr_ctx, link) {
516 [ - + ]: 22905 : pthread_mutex_lock(&ctrlr_ctx->mutex);
517 [ + + ]: 22905 : if (ctrlr_ctx->current_queue_depth > 0) {
518 : 22881 : rc = spdk_nvme_ctrlr_process_admin_completions(ctrlr_ctx->ctrlr);
519 : 22881 : unfinished_ctx++;
520 : : }
521 [ - + ]: 22905 : pthread_mutex_unlock(&ctrlr_ctx->mutex);
522 [ - + ]: 22905 : if (rc < 0) {
523 [ # # # # ]: 0 : fprintf(stderr, "spdk_nvme_ctrlr_process_admin_completions "
524 : : "returned %d\n", rc);
525 : 0 : worker->status = rc;
526 : 0 : goto out;
527 : : }
528 : : }
529 [ + + ]: 22905 : } while (unfinished_ctx > 0);
530 : : }
531 : 24 : out:
532 [ + + ]: 48 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
533 : : /* Make sure we don't submit any IOs at this point */
534 : 24 : ns_ctx->is_draining = true;
535 : 24 : spdk_nvme_ctrlr_free_io_qpair(ns_ctx->qpair);
536 : : }
537 : :
538 : 24 : return worker->status != 0;
539 : : }
540 : :
541 : : static void
542 : 0 : usage(char *program_name)
543 : : {
544 [ # # ]: 0 : printf("%s options", program_name);
545 : :
546 : 0 : printf("\n");
547 [ # # ]: 0 : printf("\t[-q io depth]\n");
548 [ # # ]: 0 : printf("\t[-o io size in bytes]\n");
549 [ # # ]: 0 : printf("\t[-w io pattern type, must be one of\n");
550 [ # # ]: 0 : printf("\t\t(read, write, randread, randwrite, rw, randrw)]\n");
551 [ # # ]: 0 : printf("\t[-M rwmixread (100 for reads, 0 for writes)]\n");
552 [ # # ]: 0 : printf("\t[-t time in seconds]\n");
553 [ # # ]: 0 : printf("\t[-c core mask for I/O submission/completion.]\n");
554 [ # # ]: 0 : printf("\t\t(default: 1)\n");
555 [ # # ]: 0 : printf("\t[-r Transport ID for local PCIe NVMe or NVMeoF]\n");
556 [ # # ]: 0 : printf("\t Format: 'key:value [key:value] ...'\n");
557 [ # # ]: 0 : printf("\t Keys:\n");
558 [ # # ]: 0 : printf("\t trtype Transport type (e.g. PCIe, RDMA)\n");
559 [ # # ]: 0 : printf("\t adrfam Address family (e.g. IPv4, IPv6)\n");
560 [ # # ]: 0 : printf("\t traddr Transport address (e.g. 0000:04:00.0 for PCIe or 192.168.100.8 for RDMA)\n");
561 [ # # ]: 0 : printf("\t trsvcid Transport service identifier (e.g. 4420)\n");
562 [ # # ]: 0 : printf("\t subnqn Subsystem NQN (default: %s)\n", SPDK_NVMF_DISCOVERY_NQN);
563 [ # # ]: 0 : printf("\t Example: -r 'trtype:PCIe traddr:0000:04:00.0' for PCIe or\n");
564 [ # # ]: 0 : printf("\t -r 'trtype:RDMA adrfam:IPv4 traddr:192.168.100.8 trsvcid:4420' for NVMeoF\n");
565 [ # # ]: 0 : printf("\t[-s DPDK huge memory size in MB.]\n");
566 [ # # ]: 0 : printf("\t[-i shared memory group ID]\n");
567 [ # # ]: 0 : printf("\t[-a abort interval.]\n");
568 [ # # ]: 0 : printf("\t[--no-huge SPDK is run without hugepages\n");
569 : 0 : printf("\t");
570 : 0 : spdk_log_usage(stdout, "-T");
571 : : #ifdef DEBUG
572 [ # # ]: 0 : printf("\t[-G enable debug logging]\n");
573 : : #else
574 : : printf("\t[-G enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n");
575 : : #endif
576 [ # # ]: 0 : printf("\t[-l log level]\n");
577 [ # # ]: 0 : printf("\t Available log levels:\n");
578 [ # # ]: 0 : printf("\t disabled, error, warning, notice, info, debug\n");
579 : 0 : }
580 : :
581 : : static void
582 : 24 : unregister_trids(void)
583 : : {
584 : : struct trid_entry *trid_entry, *tmp;
585 : :
586 [ + + ]: 48 : TAILQ_FOREACH_SAFE(trid_entry, &g_trid_list, tailq, tmp) {
587 [ - + ]: 24 : TAILQ_REMOVE(&g_trid_list, trid_entry, tailq);
588 : 24 : free(trid_entry);
589 : : }
590 : 24 : }
591 : :
592 : : static int
593 : 24 : add_trid(const char *trid_str)
594 : : {
595 : : struct trid_entry *trid_entry;
596 : : struct spdk_nvme_transport_id *trid;
597 : : char *ns;
598 : :
599 : 24 : trid_entry = calloc(1, sizeof(*trid_entry));
600 [ - + ]: 24 : if (trid_entry == NULL) {
601 : 0 : return -1;
602 : : }
603 : :
604 : 24 : trid = &trid_entry->trid;
605 : 24 : trid->trtype = SPDK_NVME_TRANSPORT_PCIE;
606 : 24 : snprintf(trid->subnqn, sizeof(trid->subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN);
607 : :
608 [ - + ]: 24 : if (spdk_nvme_transport_id_parse(trid, trid_str) != 0) {
609 [ # # ]: 0 : fprintf(stderr, "Invalid transport ID format '%s'\n", trid_str);
610 : 0 : free(trid_entry);
611 : 0 : return 1;
612 : : }
613 : :
614 : 24 : spdk_nvme_transport_id_populate_trstring(trid,
615 : : spdk_nvme_transport_id_trtype_str(trid->trtype));
616 : :
617 [ - + ]: 24 : ns = strcasestr(trid_str, "ns:");
618 [ + + ]: 24 : if (ns) {
619 : 0 : char nsid_str[6]; /* 5 digits maximum in an nsid */
620 : : int len;
621 : : int nsid;
622 : :
623 : 3 : ns += 3;
624 : :
625 [ - + ]: 3 : len = strcspn(ns, " \t\n");
626 [ - + ]: 3 : if (len > 5) {
627 [ # # ]: 0 : fprintf(stderr, "NVMe namespace IDs must be 5 digits or less\n");
628 : 0 : free(trid_entry);
629 : 0 : return 1;
630 : : }
631 : :
632 [ - + ]: 3 : memcpy(nsid_str, ns, len);
633 : 3 : nsid_str[len] = '\0';
634 : :
635 : 3 : nsid = spdk_strtol(nsid_str, 10);
636 [ + - - + ]: 3 : if (nsid <= 0 || nsid > 65535) {
637 [ # # ]: 0 : fprintf(stderr, "NVMe namespace IDs must be less than 65536 and greater than 0\n");
638 : 0 : free(trid_entry);
639 : 0 : return 1;
640 : : }
641 : :
642 : 3 : trid_entry->nsid = (uint16_t)nsid;
643 : : }
644 : :
645 : 24 : TAILQ_INSERT_TAIL(&g_trid_list, trid_entry, tailq);
646 : 24 : return 0;
647 : : }
648 : :
649 : : static int
650 : 24 : parse_args(int argc, char **argv)
651 : : {
652 : 0 : int op, opt_index;
653 : : long int val;
654 : : int rc;
655 : :
656 [ - + # # ]: 150 : while ((op = getopt_long(argc, argv, ABORT_GETOPT_STRING, g_abort_cmdline_opts,
657 [ + + ]: 150 : &opt_index)) != -1) {
658 [ + + + + : 126 : switch (op) {
- - + -
- ]
659 : 69 : case 'a':
660 : : case 'i':
661 : : case 'o':
662 : : case 'q':
663 : : case 's':
664 : : case 't':
665 : : case 'M':
666 : 69 : val = spdk_strtol(optarg, 10);
667 [ - + ]: 69 : if (val < 0) {
668 [ # # ]: 0 : fprintf(stderr, "Converting a string to integer failed\n");
669 : 0 : return val;
670 : : }
671 : : switch (op) {
672 : 0 : case 'a':
673 : 0 : g_abort_interval = val;
674 : 0 : break;
675 : 0 : case 'i':
676 : 0 : g_shm_id = val;
677 : 0 : break;
678 : 18 : case 'o':
679 : 18 : g_io_size_bytes = val;
680 : 18 : break;
681 : 24 : case 'q':
682 : 24 : g_queue_depth = val;
683 : 24 : break;
684 : 0 : case 's':
685 : 0 : g_dpdk_mem = val;
686 : 0 : break;
687 : 6 : case 't':
688 : 6 : g_time_in_sec = val;
689 : 6 : break;
690 : 21 : case 'M':
691 : 21 : g_rw_percentage = val;
692 : 21 : g_mix_specified = true;
693 : 21 : break;
694 : : }
695 : 69 : break;
696 : 6 : case 'c':
697 : 6 : g_core_mask = optarg;
698 : 6 : break;
699 : 24 : case 'r':
700 [ - + ]: 24 : if (add_trid(optarg)) {
701 : 0 : usage(argv[0]);
702 : 0 : return 1;
703 : : }
704 : 24 : break;
705 : 21 : case 'w':
706 : 21 : g_workload_type = optarg;
707 : 21 : break;
708 : 0 : case 'G':
709 : : #ifndef DEBUG
710 : : fprintf(stderr, "%s must be configured with --enable-debug for -G flag\n",
711 : : argv[0]);
712 : : usage(argv[0]);
713 : : return 1;
714 : : #else
715 : 0 : spdk_log_set_flag("nvme");
716 : 0 : spdk_log_set_print_level(SPDK_LOG_DEBUG);
717 : 0 : break;
718 : : #endif
719 : 0 : case 'T':
720 : 0 : rc = spdk_log_set_flag(optarg);
721 [ # # ]: 0 : if (rc < 0) {
722 [ # # ]: 0 : fprintf(stderr, "unknown flag\n");
723 : 0 : usage(argv[0]);
724 : 0 : exit(EXIT_FAILURE);
725 : : }
726 : : #ifdef DEBUG
727 : 0 : spdk_log_set_print_level(SPDK_LOG_DEBUG);
728 : : #endif
729 : 0 : break;
730 : 6 : case 'l':
731 [ - + - + ]: 6 : if (!strcmp(optarg, "disabled")) {
732 : 0 : spdk_log_set_print_level(SPDK_LOG_DISABLED);
733 [ - + - + ]: 6 : } else if (!strcmp(optarg, "error")) {
734 : 0 : spdk_log_set_print_level(SPDK_LOG_ERROR);
735 [ - + + - ]: 6 : } else if (!strcmp(optarg, "warning")) {
736 : 6 : spdk_log_set_print_level(SPDK_LOG_WARN);
737 [ # # # # ]: 0 : } else if (!strcmp(optarg, "notice")) {
738 : 0 : spdk_log_set_print_level(SPDK_LOG_NOTICE);
739 [ # # # # ]: 0 : } else if (!strcmp(optarg, "info")) {
740 : 0 : spdk_log_set_print_level(SPDK_LOG_INFO);
741 [ # # # # ]: 0 : } else if (!strcmp(optarg, "debug")) {
742 : 0 : spdk_log_set_print_level(SPDK_LOG_DEBUG);
743 : : } else {
744 [ # # ]: 0 : fprintf(stderr, "Unrecognized log level: %s\n", optarg);
745 : 0 : return 1;
746 : : }
747 : 6 : break;
748 : 0 : case ABORT_NO_HUGE:
749 : 0 : g_no_hugepages = true;
750 : 0 : break;
751 : 0 : default:
752 : 0 : usage(argv[0]);
753 : 0 : return 1;
754 : : }
755 : : }
756 : :
757 [ - + ]: 24 : if (!g_queue_depth) {
758 [ # # ]: 0 : fprintf(stderr, "missing -q (queue size) operand\n");
759 : 0 : usage(argv[0]);
760 : 0 : return 1;
761 : : }
762 [ - + ]: 24 : if (!g_io_size_bytes) {
763 [ # # ]: 0 : fprintf(stderr, "missing -o (block size) operand\n");
764 : 0 : usage(argv[0]);
765 : 0 : return 1;
766 : : }
767 [ - + ]: 24 : if (!g_workload_type) {
768 [ # # ]: 0 : fprintf(stderr, "missing -t (test time in seconds) operand\n");
769 : 0 : usage(argv[0]);
770 : 0 : return 1;
771 : : }
772 : :
773 [ - + ]: 24 : if (!g_time_in_sec) {
774 : 0 : usage(argv[0]);
775 : 0 : return 1;
776 : : }
777 : :
778 [ - + + + ]: 24 : if (strncmp(g_workload_type, "rand", 4) == 0) {
779 : 3 : g_is_random = 1;
780 : 3 : g_workload_type = &g_workload_type[4];
781 : : }
782 : :
783 [ - + + + : 24 : if (strcmp(g_workload_type, "read") == 0 || strcmp(g_workload_type, "write") == 0) {
- + - + ]
784 [ - + + - ]: 3 : g_rw_percentage = strcmp(g_workload_type, "read") == 0 ? 100 : 0;
785 [ - + - + ]: 3 : if (g_mix_specified) {
786 [ # # ]: 0 : fprintf(stderr, "Ignoring -M option... Please use -M option"
787 : : " only when using rw or randrw.\n");
788 : : }
789 [ - + + - ]: 21 : } else if (strcmp(g_workload_type, "rw") == 0) {
790 [ + - - + ]: 21 : if (g_rw_percentage < 0 || g_rw_percentage > 100) {
791 [ # # ]: 0 : fprintf(stderr,
792 : : "-M must be specified to value from 0 to 100 "
793 : : "for rw or randrw.\n");
794 : 0 : return 1;
795 : : }
796 : : } else {
797 [ # # ]: 0 : fprintf(stderr,
798 : : "io pattern type must be one of\n"
799 : : "(read, write, randread, randwrite, rw, randrw)\n");
800 : 0 : return 1;
801 : : }
802 : :
803 [ - + ]: 24 : if (TAILQ_EMPTY(&g_trid_list)) {
804 : : /* If no transport IDs specified, default to enumerating all local PCIe devices */
805 : 0 : add_trid("trtype:PCIe");
806 : : } else {
807 : : struct trid_entry *trid_entry, *trid_entry_tmp;
808 : :
809 : 24 : g_no_pci = true;
810 : : /* check whether there is local PCIe type */
811 [ + + ]: 48 : TAILQ_FOREACH_SAFE(trid_entry, &g_trid_list, tailq, trid_entry_tmp) {
812 [ - + ]: 24 : if (trid_entry->trid.trtype == SPDK_NVME_TRANSPORT_PCIE) {
813 : 0 : g_no_pci = false;
814 : 0 : break;
815 : : }
816 : : }
817 : : }
818 : :
819 : 24 : return 0;
820 : : }
821 : :
822 : : static int
823 : 24 : register_workers(void)
824 : : {
825 : : uint32_t i;
826 : : struct worker_thread *worker;
827 : :
828 [ + + ]: 48 : SPDK_ENV_FOREACH_CORE(i) {
829 : 24 : worker = calloc(1, sizeof(*worker));
830 [ - + ]: 24 : if (worker == NULL) {
831 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate worker\n");
832 : 0 : return -1;
833 : : }
834 : :
835 : 24 : TAILQ_INIT(&worker->ns_ctx);
836 : 24 : TAILQ_INIT(&worker->ctrlr_ctx);
837 : 24 : worker->lcore = i;
838 : 24 : TAILQ_INSERT_TAIL(&g_workers, worker, link);
839 : 24 : g_num_workers++;
840 : : }
841 : :
842 : 24 : return 0;
843 : : }
844 : :
845 : : static void
846 : 24 : unregister_workers(void)
847 : : {
848 : : struct worker_thread *worker, *tmp_worker;
849 : : struct ns_worker_ctx *ns_ctx, *tmp_ns_ctx;
850 : : struct ctrlr_worker_ctx *ctrlr_ctx, *tmp_ctrlr_ctx;
851 : :
852 : : /* Free namespace context and worker thread */
853 [ + + ]: 48 : TAILQ_FOREACH_SAFE(worker, &g_workers, link, tmp_worker) {
854 [ - + ]: 24 : TAILQ_REMOVE(&g_workers, worker, link);
855 : :
856 [ + + ]: 48 : TAILQ_FOREACH_SAFE(ns_ctx, &worker->ns_ctx, link, tmp_ns_ctx) {
857 [ - + ]: 24 : TAILQ_REMOVE(&worker->ns_ctx, ns_ctx, link);
858 : 48 : printf("NS: %s I/O completed: %" PRIu64 ", failed: %" PRIu64 "\n",
859 [ - + ]: 24 : ns_ctx->entry->name, ns_ctx->io_completed, ns_ctx->io_failed);
860 : 24 : free(ns_ctx);
861 : : }
862 : :
863 [ + + ]: 48 : TAILQ_FOREACH_SAFE(ctrlr_ctx, &worker->ctrlr_ctx, link, tmp_ctrlr_ctx) {
864 [ - + ]: 24 : TAILQ_REMOVE(&worker->ctrlr_ctx, ctrlr_ctx, link);
865 : 48 : printf("CTRLR: %s abort submitted %" PRIu64 ", failed to submit %" PRIu64 "\n",
866 [ - + ]: 24 : ctrlr_ctx->entry->name, ctrlr_ctx->abort_submitted,
867 : : ctrlr_ctx->abort_submit_failed);
868 [ - + ]: 24 : printf("\t success %" PRIu64 ", unsuccess %" PRIu64 ", failed %" PRIu64 "\n",
869 : : ctrlr_ctx->successful_abort, ctrlr_ctx->unsuccessful_abort,
870 : : ctrlr_ctx->abort_failed);
871 : 24 : free(ctrlr_ctx);
872 : : }
873 : :
874 : 24 : free(worker);
875 : : }
876 : 24 : }
877 : :
878 : : static bool
879 : 24 : probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
880 : : struct spdk_nvme_ctrlr_opts *opts)
881 : : {
882 : : uint16_t min_aq_size;
883 : :
884 : : /* We need to make sure the admin queue is big enough to handle all of the aborts that
885 : : * will be sent by this test app. We add a few extra entries to account for any admin
886 : : * commands other than the aborts. */
887 : 24 : min_aq_size = spdk_divide_round_up(g_queue_depth, g_abort_interval) + 8;
888 : 24 : opts->admin_queue_size = spdk_max(opts->admin_queue_size, min_aq_size);
889 : :
890 : : /* Avoid possible nvme_qpair_abort_queued_reqs_with_cbarg ERROR when IO queue size is 128. */
891 : 24 : opts->disable_error_logging = true;
892 : :
893 : 24 : return true;
894 : : }
895 : :
896 : : static void
897 : 24 : attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
898 : : struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
899 : : {
900 : 24 : struct trid_entry *trid_entry = cb_ctx;
901 : 0 : struct spdk_pci_addr pci_addr;
902 : : struct spdk_pci_device *pci_dev;
903 : : struct spdk_pci_id pci_id;
904 : :
905 [ + - ]: 24 : if (trid->trtype != SPDK_NVME_TRANSPORT_PCIE) {
906 : 24 : printf("Attached to NVMe over Fabrics controller at %s:%s: %s\n",
907 [ - + ]: 24 : trid->traddr, trid->trsvcid,
908 : 24 : trid->subnqn);
909 : : } else {
910 [ # # ]: 0 : if (spdk_pci_addr_parse(&pci_addr, trid->traddr)) {
911 : 0 : return;
912 : : }
913 : :
914 : 0 : pci_dev = spdk_nvme_ctrlr_get_pci_device(ctrlr);
915 [ # # ]: 0 : if (!pci_dev) {
916 : 0 : return;
917 : : }
918 : :
919 : 0 : pci_id = spdk_pci_device_get_id(pci_dev);
920 : :
921 : 0 : printf("Attached to NVMe Controller at %s [%04x:%04x]\n",
922 [ # # ]: 0 : trid->traddr,
923 : 0 : pci_id.vendor_id, pci_id.device_id);
924 : : }
925 : :
926 : 24 : register_ctrlr(ctrlr, trid_entry);
927 : : }
928 : :
929 : : static int
930 : 24 : register_controllers(void)
931 : : {
932 : : struct trid_entry *trid_entry;
933 : :
934 [ - + ]: 24 : printf("Initializing NVMe Controllers\n");
935 : :
936 [ + + ]: 48 : TAILQ_FOREACH(trid_entry, &g_trid_list, tailq) {
937 [ - + ]: 24 : if (spdk_nvme_probe(&trid_entry->trid, trid_entry, probe_cb, attach_cb, NULL) != 0) {
938 [ # # ]: 0 : fprintf(stderr, "spdk_nvme_probe() failed for transport address '%s'\n",
939 [ # # ]: 0 : trid_entry->trid.traddr);
940 : 0 : return -1;
941 : : }
942 : : }
943 : :
944 : 24 : return 0;
945 : : }
946 : :
947 : : static void
948 : 24 : unregister_controllers(void)
949 : : {
950 : : struct ctrlr_entry *entry, *tmp;
951 : 24 : struct spdk_nvme_detach_ctx *detach_ctx = NULL;
952 : :
953 [ + + ]: 48 : TAILQ_FOREACH_SAFE(entry, &g_controllers, link, tmp) {
954 [ - + ]: 24 : TAILQ_REMOVE(&g_controllers, entry, link);
955 : 24 : spdk_nvme_detach_async(entry->ctrlr, &detach_ctx);
956 : 24 : free(entry);
957 : : }
958 : :
959 [ + - ]: 24 : if (detach_ctx) {
960 : 24 : spdk_nvme_detach_poll(detach_ctx);
961 : : }
962 : 24 : }
963 : :
964 : : static int
965 : 24 : associate_main_worker_with_ctrlr(void)
966 : : {
967 : : struct ctrlr_entry *entry;
968 : : struct worker_thread *worker;
969 : : struct ctrlr_worker_ctx *ctrlr_ctx;
970 : :
971 [ + - ]: 24 : TAILQ_FOREACH(worker, &g_workers, link) {
972 [ + - ]: 24 : if (worker->lcore == g_main_core) {
973 : 24 : break;
974 : : }
975 : : }
976 : :
977 [ - + ]: 24 : if (!worker) {
978 : 0 : return -1;
979 : : }
980 : :
981 [ + + ]: 48 : TAILQ_FOREACH(entry, &g_controllers, link) {
982 : 24 : ctrlr_ctx = calloc(1, sizeof(struct ctrlr_worker_ctx));
983 [ - + ]: 24 : if (!ctrlr_ctx) {
984 : 0 : return -1;
985 : : }
986 : :
987 [ - + ]: 24 : pthread_mutex_init(&ctrlr_ctx->mutex, NULL);
988 : 24 : ctrlr_ctx->entry = entry;
989 : 24 : ctrlr_ctx->ctrlr = entry->ctrlr;
990 : :
991 : 24 : TAILQ_INSERT_TAIL(&worker->ctrlr_ctx, ctrlr_ctx, link);
992 : : }
993 : :
994 : 24 : return 0;
995 : : }
996 : :
997 : : static struct ctrlr_worker_ctx *
998 : 24 : get_ctrlr_worker_ctx(struct spdk_nvme_ctrlr *ctrlr)
999 : : {
1000 : : struct worker_thread *worker;
1001 : : struct ctrlr_worker_ctx *ctrlr_ctx;
1002 : :
1003 [ + - ]: 24 : TAILQ_FOREACH(worker, &g_workers, link) {
1004 [ + - ]: 24 : if (worker->lcore == g_main_core) {
1005 : 24 : break;
1006 : : }
1007 : : }
1008 : :
1009 [ - + ]: 24 : if (!worker) {
1010 : 0 : return NULL;
1011 : : }
1012 : :
1013 [ + - ]: 24 : TAILQ_FOREACH(ctrlr_ctx, &worker->ctrlr_ctx, link) {
1014 [ + - ]: 24 : if (ctrlr_ctx->ctrlr == ctrlr) {
1015 : 24 : return ctrlr_ctx;
1016 : : }
1017 : : }
1018 : :
1019 : 0 : return NULL;
1020 : : }
1021 : :
1022 : : static int
1023 : 24 : associate_workers_with_ns(void)
1024 : : {
1025 : 24 : struct ns_entry *entry = TAILQ_FIRST(&g_namespaces);
1026 : 24 : struct worker_thread *worker = TAILQ_FIRST(&g_workers);
1027 : : struct ns_worker_ctx *ns_ctx;
1028 : : int i, count;
1029 : :
1030 : 24 : count = g_num_namespaces > g_num_workers ? g_num_namespaces : g_num_workers;
1031 : :
1032 [ + + ]: 48 : for (i = 0; i < count; i++) {
1033 [ - + ]: 24 : if (entry == NULL) {
1034 : 0 : break;
1035 : : }
1036 : :
1037 : 24 : ns_ctx = calloc(1, sizeof(struct ns_worker_ctx));
1038 [ - + ]: 24 : if (!ns_ctx) {
1039 : 0 : return -1;
1040 : : }
1041 : :
1042 [ - + ]: 24 : printf("Associating %s with lcore %d\n", entry->name, worker->lcore);
1043 : 24 : ns_ctx->entry = entry;
1044 : 24 : ns_ctx->ctrlr_ctx = get_ctrlr_worker_ctx(entry->ctrlr);
1045 [ - + ]: 24 : if (!ns_ctx->ctrlr_ctx) {
1046 : 0 : free(ns_ctx);
1047 : 0 : return -1;
1048 : : }
1049 : :
1050 : 24 : TAILQ_INSERT_TAIL(&worker->ns_ctx, ns_ctx, link);
1051 : :
1052 : 24 : worker = TAILQ_NEXT(worker, link);
1053 [ + - ]: 24 : if (worker == NULL) {
1054 : 24 : worker = TAILQ_FIRST(&g_workers);
1055 : : }
1056 : :
1057 : 24 : entry = TAILQ_NEXT(entry, link);
1058 [ + - ]: 24 : if (entry == NULL) {
1059 : 24 : entry = TAILQ_FIRST(&g_namespaces);
1060 : : }
1061 : : }
1062 : :
1063 : 24 : return 0;
1064 : : }
1065 : :
1066 : : int
1067 : 24 : main(int argc, char **argv)
1068 : : {
1069 : : int rc;
1070 : : struct worker_thread *worker, *main_worker;
1071 : 0 : struct spdk_env_opts opts;
1072 : :
1073 : 24 : rc = parse_args(argc, argv);
1074 [ - + ]: 24 : if (rc != 0) {
1075 : 0 : return rc;
1076 : : }
1077 : :
1078 : 24 : spdk_env_opts_init(&opts);
1079 : 24 : opts.name = "abort";
1080 : 24 : opts.shm_id = g_shm_id;
1081 [ + + ]: 24 : if (g_core_mask) {
1082 : 6 : opts.core_mask = g_core_mask;
1083 : : }
1084 : :
1085 [ - + ]: 24 : if (g_dpdk_mem) {
1086 : 0 : opts.mem_size = g_dpdk_mem;
1087 : : }
1088 [ - + + - ]: 24 : if (g_no_pci) {
1089 [ - + ]: 24 : opts.no_pci = g_no_pci;
1090 : : }
1091 [ - + - + ]: 24 : if (g_no_hugepages) {
1092 : 0 : opts.no_huge = true;
1093 : : }
1094 [ - + ]: 24 : if (spdk_env_init(&opts) < 0) {
1095 [ # # # # ]: 0 : fprintf(stderr, "Unable to initialize SPDK env\n");
1096 : 0 : unregister_trids();
1097 : 0 : return -1;
1098 : : }
1099 : :
1100 : 24 : g_tsc_rate = spdk_get_ticks_hz();
1101 : :
1102 [ - + ]: 24 : if (register_workers() != 0) {
1103 : 0 : rc = -1;
1104 : 0 : goto cleanup;
1105 : : }
1106 : :
1107 [ - + ]: 24 : if (register_controllers() != 0) {
1108 : 0 : rc = -1;
1109 : 0 : goto cleanup;
1110 : : }
1111 : :
1112 [ - + - + ]: 24 : if (g_warn) {
1113 [ # # ]: 0 : printf("WARNING: Some requested NVMe devices were skipped\n");
1114 : : }
1115 : :
1116 [ - + ]: 24 : if (g_num_namespaces == 0) {
1117 [ # # # # ]: 0 : fprintf(stderr, "No valid NVMe controllers found\n");
1118 : 0 : rc = -1;
1119 : 0 : goto cleanup;
1120 : : }
1121 : :
1122 [ - + ]: 24 : if (associate_main_worker_with_ctrlr() != 0) {
1123 : 0 : rc = -1;
1124 : 0 : goto cleanup;
1125 : : }
1126 : :
1127 [ - + ]: 24 : if (associate_workers_with_ns() != 0) {
1128 : 0 : rc = -1;
1129 : 0 : goto cleanup;
1130 : : }
1131 : :
1132 [ - + ]: 24 : printf("Initialization complete. Launching workers.\n");
1133 : :
1134 : : /* Launch all of the secondary workers */
1135 : 24 : g_main_core = spdk_env_get_current_core();
1136 : 24 : main_worker = NULL;
1137 [ + + ]: 48 : TAILQ_FOREACH(worker, &g_workers, link) {
1138 [ - + ]: 24 : if (worker->lcore != g_main_core) {
1139 : 0 : spdk_env_thread_launch_pinned(worker->lcore, work_fn, worker);
1140 : : } else {
1141 [ - + ]: 24 : assert(main_worker == NULL);
1142 : 24 : main_worker = worker;
1143 : : }
1144 : : }
1145 : :
1146 [ - + ]: 24 : assert(main_worker != NULL);
1147 : 24 : rc = work_fn(main_worker);
1148 : :
1149 : 24 : spdk_env_thread_wait_all();
1150 : :
1151 [ + + ]: 48 : TAILQ_FOREACH(worker, &g_workers, link) {
1152 [ - + ]: 24 : if (worker->status != 0) {
1153 : 0 : rc = 1;
1154 : 0 : break;
1155 : : }
1156 : : }
1157 : :
1158 : 24 : cleanup:
1159 : 24 : unregister_trids();
1160 : 24 : unregister_workers();
1161 : 24 : unregister_namespaces();
1162 : 24 : unregister_controllers();
1163 : :
1164 : 24 : spdk_env_fini();
1165 : :
1166 [ - + ]: 24 : if (rc != 0) {
1167 [ # # # # ]: 0 : fprintf(stderr, "%s: errors occurred\n", argv[0]);
1168 : : }
1169 : :
1170 : 24 : return rc;
1171 : : }
|