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/log.h"
9 : : #include "spdk/nvme.h"
10 : : #include "spdk/env.h"
11 : : #include "spdk/string.h"
12 : : #include "spdk/nvme_intel.h"
13 : :
14 : : struct ctrlr_entry {
15 : : struct spdk_nvme_ctrlr *ctrlr;
16 : : struct spdk_nvme_intel_rw_latency_page latency_page;
17 : : TAILQ_ENTRY(ctrlr_entry) link;
18 : : char name[1024];
19 : : };
20 : :
21 : : struct ns_entry {
22 : : struct {
23 : : struct spdk_nvme_ctrlr *ctrlr;
24 : : struct spdk_nvme_ns *ns;
25 : : } nvme;
26 : :
27 : : TAILQ_ENTRY(ns_entry) link;
28 : : uint32_t io_size_blocks;
29 : : uint64_t size_in_ios;
30 : : char name[1024];
31 : : };
32 : :
33 : : struct ns_worker_ctx {
34 : : struct ns_entry *entry;
35 : : uint64_t io_completed;
36 : : uint64_t current_queue_depth;
37 : : uint64_t offset_in_ios;
38 : : bool is_draining;
39 : : struct spdk_nvme_qpair *qpair;
40 : : TAILQ_ENTRY(ns_worker_ctx) link;
41 : : };
42 : :
43 : : struct arb_task {
44 : : struct ns_worker_ctx *ns_ctx;
45 : : void *buf;
46 : : };
47 : :
48 : : struct worker_thread {
49 : : TAILQ_HEAD(, ns_worker_ctx) ns_ctx;
50 : : TAILQ_ENTRY(worker_thread) link;
51 : : unsigned lcore;
52 : : enum spdk_nvme_qprio qprio;
53 : : };
54 : :
55 : : struct arb_context {
56 : : int shm_id;
57 : : int outstanding_commands;
58 : : int num_namespaces;
59 : : int num_workers;
60 : : int rw_percentage;
61 : : int is_random;
62 : : int queue_depth;
63 : : int time_in_sec;
64 : : int io_count;
65 : : uint8_t latency_tracking_enable;
66 : : uint8_t arbitration_mechanism;
67 : : uint8_t arbitration_config;
68 : : uint32_t io_size_bytes;
69 : : uint32_t max_completions;
70 : : uint64_t tsc_rate;
71 : : const char *core_mask;
72 : : const char *workload_type;
73 : : };
74 : :
75 : : struct feature {
76 : : uint32_t result;
77 : : bool valid;
78 : : };
79 : :
80 : : static struct spdk_mempool *task_pool = NULL;
81 : :
82 : : static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers);
83 : : static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces);
84 : : static TAILQ_HEAD(, worker_thread) g_workers = TAILQ_HEAD_INITIALIZER(g_workers);
85 : :
86 : : static struct feature features[SPDK_NVME_FEAT_ARBITRATION + 1] = {};
87 : : static struct spdk_nvme_transport_id g_trid = {};
88 : :
89 : : static struct arb_context g_arbitration = {
90 : : .shm_id = -1,
91 : : .outstanding_commands = 0,
92 : : .num_workers = 0,
93 : : .num_namespaces = 0,
94 : : .rw_percentage = 50,
95 : : .queue_depth = 64,
96 : : .time_in_sec = 60,
97 : : .io_count = 100000,
98 : : .latency_tracking_enable = 0,
99 : : .arbitration_mechanism = SPDK_NVME_CC_AMS_RR,
100 : : .arbitration_config = 0,
101 : : .io_size_bytes = 131072,
102 : : .max_completions = 0,
103 : : /* Default 4 cores for urgent/high/medium/low */
104 : : .core_mask = "0xf",
105 : : .workload_type = "randrw",
106 : : };
107 : :
108 : : static int g_dpdk_mem = 0;
109 : : static bool g_dpdk_mem_single_seg = false;
110 : :
111 : : /*
112 : : * For weighted round robin arbitration mechanism, the smaller value between
113 : : * weight and burst will be picked to execute the commands in one queue.
114 : : */
115 : : #define USER_SPECIFIED_HIGH_PRIORITY_WEIGHT 32
116 : : #define USER_SPECIFIED_MEDIUM_PRIORITY_WEIGHT 16
117 : : #define USER_SPECIFIED_LOW_PRIORITY_WEIGHT 8
118 : :
119 : : static void task_complete(struct arb_task *task);
120 : :
121 : : static void io_complete(void *ctx, const struct spdk_nvme_cpl *completion);
122 : :
123 : : static void get_arb_feature(struct spdk_nvme_ctrlr *ctrlr);
124 : :
125 : : static int set_arb_feature(struct spdk_nvme_ctrlr *ctrlr);
126 : :
127 : : static const char *print_qprio(enum spdk_nvme_qprio);
128 : :
129 : :
130 : : static void
131 : 14 : register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
132 : : {
133 : : struct ns_entry *entry;
134 : : const struct spdk_nvme_ctrlr_data *cdata;
135 : :
136 : 14 : cdata = spdk_nvme_ctrlr_get_data(ctrlr);
137 : :
138 [ + - ]: 14 : if (spdk_nvme_ns_get_size(ns) < g_arbitration.io_size_bytes ||
139 [ + - ]: 14 : spdk_nvme_ns_get_extended_sector_size(ns) > g_arbitration.io_size_bytes ||
140 [ - + - + ]: 14 : g_arbitration.io_size_bytes % spdk_nvme_ns_get_extended_sector_size(ns)) {
141 : 0 : printf("WARNING: controller %-20.20s (%-20.20s) ns %u has invalid "
142 : : "ns size %" PRIu64 " / block size %u for I/O size %u\n",
143 [ # # ]: 0 : cdata->mn, cdata->sn, spdk_nvme_ns_get_id(ns),
144 : : spdk_nvme_ns_get_size(ns), spdk_nvme_ns_get_extended_sector_size(ns),
145 : : g_arbitration.io_size_bytes);
146 : 0 : return;
147 : : }
148 : :
149 : 14 : entry = malloc(sizeof(struct ns_entry));
150 [ - + ]: 14 : if (entry == NULL) {
151 : 0 : perror("ns_entry malloc");
152 : 0 : exit(1);
153 : : }
154 : :
155 : 14 : entry->nvme.ctrlr = ctrlr;
156 : 14 : entry->nvme.ns = ns;
157 : :
158 [ - + ]: 14 : entry->size_in_ios = spdk_nvme_ns_get_size(ns) / g_arbitration.io_size_bytes;
159 [ - + ]: 14 : entry->io_size_blocks = g_arbitration.io_size_bytes / spdk_nvme_ns_get_sector_size(ns);
160 : :
161 [ - + ]: 14 : snprintf(entry->name, 44, "%-20.20s (%-20.20s)", cdata->mn, cdata->sn);
162 : :
163 : 14 : g_arbitration.num_namespaces++;
164 : 14 : TAILQ_INSERT_TAIL(&g_namespaces, entry, link);
165 : : }
166 : :
167 : : static void
168 : 0 : enable_latency_tracking_complete(void *cb_arg, const struct spdk_nvme_cpl *cpl)
169 : : {
170 [ # # # # ]: 0 : if (spdk_nvme_cpl_is_error(cpl)) {
171 [ # # ]: 0 : printf("enable_latency_tracking_complete failed\n");
172 : : }
173 : 0 : g_arbitration.outstanding_commands--;
174 : 0 : }
175 : :
176 : : static void
177 : 0 : set_latency_tracking_feature(struct spdk_nvme_ctrlr *ctrlr, bool enable)
178 : : {
179 : : int res;
180 : : union spdk_nvme_intel_feat_latency_tracking latency_tracking;
181 : :
182 [ # # ]: 0 : if (enable) {
183 : 0 : latency_tracking.bits.enable = 0x01;
184 : : } else {
185 : 0 : latency_tracking.bits.enable = 0x00;
186 : : }
187 : :
188 : 0 : res = spdk_nvme_ctrlr_cmd_set_feature(ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING,
189 : : latency_tracking.raw, 0, NULL, 0, enable_latency_tracking_complete, NULL);
190 [ # # ]: 0 : if (res) {
191 [ # # ]: 0 : printf("fail to allocate nvme request.\n");
192 : 0 : return;
193 : : }
194 : 0 : g_arbitration.outstanding_commands++;
195 : :
196 [ # # ]: 0 : while (g_arbitration.outstanding_commands) {
197 : 0 : spdk_nvme_ctrlr_process_admin_completions(ctrlr);
198 : : }
199 : : }
200 : :
201 : : static void
202 : 12 : register_ctrlr(struct spdk_nvme_ctrlr *ctrlr)
203 : : {
204 : : uint32_t nsid;
205 : : struct spdk_nvme_ns *ns;
206 : 12 : struct ctrlr_entry *entry = calloc(1, sizeof(struct ctrlr_entry));
207 : 12 : union spdk_nvme_cap_register cap = spdk_nvme_ctrlr_get_regs_cap(ctrlr);
208 : 12 : const struct spdk_nvme_ctrlr_data *cdata = spdk_nvme_ctrlr_get_data(ctrlr);
209 : :
210 [ - + ]: 12 : if (entry == NULL) {
211 : 0 : perror("ctrlr_entry malloc");
212 : 0 : exit(1);
213 : : }
214 : :
215 [ - + ]: 12 : snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn);
216 : :
217 : 12 : entry->ctrlr = ctrlr;
218 : 12 : TAILQ_INSERT_TAIL(&g_controllers, entry, link);
219 : :
220 [ - + - - ]: 12 : if ((g_arbitration.latency_tracking_enable != 0) &&
221 : 0 : spdk_nvme_ctrlr_is_feature_supported(ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING)) {
222 : 0 : set_latency_tracking_feature(ctrlr, true);
223 : : }
224 : :
225 [ + + ]: 26 : for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr); nsid != 0;
226 : 14 : nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) {
227 : 14 : ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
228 [ - + ]: 14 : if (ns == NULL) {
229 : 0 : continue;
230 : : }
231 : 14 : register_ns(ctrlr, ns);
232 : : }
233 : :
234 [ - + ]: 12 : if (g_arbitration.arbitration_mechanism == SPDK_NVME_CAP_AMS_WRR &&
235 [ # # ]: 0 : (cap.bits.ams & SPDK_NVME_CAP_AMS_WRR)) {
236 : 0 : get_arb_feature(ctrlr);
237 : :
238 [ # # ]: 0 : if (g_arbitration.arbitration_config != 0) {
239 : 0 : set_arb_feature(ctrlr);
240 : 0 : get_arb_feature(ctrlr);
241 : : }
242 : : }
243 : 12 : }
244 : :
245 : : static __thread unsigned int seed = 0;
246 : :
247 : : static void
248 : 483971 : submit_single_io(struct ns_worker_ctx *ns_ctx)
249 : : {
250 : 483971 : struct arb_task *task = NULL;
251 : : uint64_t offset_in_ios;
252 : : int rc;
253 : 483971 : struct ns_entry *entry = ns_ctx->entry;
254 : :
255 : 483971 : task = spdk_mempool_get(task_pool);
256 [ - + ]: 483971 : if (!task) {
257 [ # # # # ]: 0 : fprintf(stderr, "Failed to get task from task_pool\n");
258 : 0 : exit(1);
259 : : }
260 : :
261 : 483971 : task->buf = spdk_dma_zmalloc(g_arbitration.io_size_bytes, 0x200, NULL);
262 [ - + ]: 483971 : if (!task->buf) {
263 : 0 : spdk_mempool_put(task_pool, task);
264 [ # # # # ]: 0 : fprintf(stderr, "task->buf spdk_dma_zmalloc failed\n");
265 : 0 : exit(1);
266 : : }
267 : :
268 : 483971 : task->ns_ctx = ns_ctx;
269 : :
270 [ + - ]: 483971 : if (g_arbitration.is_random) {
271 [ - + ]: 483971 : offset_in_ios = rand_r(&seed) % entry->size_in_ios;
272 : : } else {
273 : 0 : offset_in_ios = ns_ctx->offset_in_ios++;
274 [ # # ]: 0 : if (ns_ctx->offset_in_ios == entry->size_in_ios) {
275 : 0 : ns_ctx->offset_in_ios = 0;
276 : : }
277 : : }
278 : :
279 [ + - ]: 483971 : if ((g_arbitration.rw_percentage == 100) ||
280 [ + - ]: 483971 : (g_arbitration.rw_percentage != 0 &&
281 [ + + ]: 483971 : ((rand_r(&seed) % 100) < g_arbitration.rw_percentage))) {
282 : 420797 : rc = spdk_nvme_ns_cmd_read(entry->nvme.ns, ns_ctx->qpair, task->buf,
283 : 243380 : offset_in_ios * entry->io_size_blocks,
284 : : entry->io_size_blocks, io_complete, task, 0);
285 : : } else {
286 : 415892 : rc = spdk_nvme_ns_cmd_write(entry->nvme.ns, ns_ctx->qpair, task->buf,
287 : 240591 : offset_in_ios * entry->io_size_blocks,
288 : : entry->io_size_blocks, io_complete, task, 0);
289 : : }
290 : :
291 [ - + ]: 483971 : if (rc != 0) {
292 [ # # # # ]: 0 : fprintf(stderr, "starting I/O failed\n");
293 : : } else {
294 : 483971 : ns_ctx->current_queue_depth++;
295 : : }
296 : 483971 : }
297 : :
298 : : static void
299 : 483971 : task_complete(struct arb_task *task)
300 : : {
301 : : struct ns_worker_ctx *ns_ctx;
302 : :
303 : 483971 : ns_ctx = task->ns_ctx;
304 : 483971 : ns_ctx->current_queue_depth--;
305 : 483971 : ns_ctx->io_completed++;
306 : :
307 : 483971 : spdk_dma_free(task->buf);
308 : 483971 : spdk_mempool_put(task_pool, task);
309 : :
310 : : /*
311 : : * is_draining indicates when time has expired for the test run
312 : : * and we are just waiting for the previously submitted I/O
313 : : * to complete. In this case, do not submit a new I/O to replace
314 : : * the one just completed.
315 : : */
316 [ + + + + ]: 483971 : if (!ns_ctx->is_draining) {
317 : 481795 : submit_single_io(ns_ctx);
318 : : }
319 : 483971 : }
320 : :
321 : : static void
322 : 483971 : io_complete(void *ctx, const struct spdk_nvme_cpl *completion)
323 : : {
324 : 483971 : task_complete((struct arb_task *)ctx);
325 : 483971 : }
326 : :
327 : : static void
328 : 5677053 : check_io(struct ns_worker_ctx *ns_ctx)
329 : : {
330 : 5677053 : spdk_nvme_qpair_process_completions(ns_ctx->qpair, g_arbitration.max_completions);
331 : 5677053 : }
332 : :
333 : : static void
334 : 34 : submit_io(struct ns_worker_ctx *ns_ctx, int queue_depth)
335 : : {
336 [ + + ]: 2210 : while (queue_depth-- > 0) {
337 : 2176 : submit_single_io(ns_ctx);
338 : : }
339 : 34 : }
340 : :
341 : : static void
342 : 34 : drain_io(struct ns_worker_ctx *ns_ctx)
343 : : {
344 : 34 : ns_ctx->is_draining = true;
345 [ + + ]: 39889 : while (ns_ctx->current_queue_depth > 0) {
346 : 39855 : check_io(ns_ctx);
347 : : }
348 : 34 : }
349 : :
350 : : static int
351 : 34 : init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx, enum spdk_nvme_qprio qprio)
352 : : {
353 : 34 : struct spdk_nvme_ctrlr *ctrlr = ns_ctx->entry->nvme.ctrlr;
354 : 14 : struct spdk_nvme_io_qpair_opts opts;
355 : :
356 : 34 : spdk_nvme_ctrlr_get_default_io_qpair_opts(ctrlr, &opts, sizeof(opts));
357 : 34 : opts.qprio = qprio;
358 : :
359 : 34 : ns_ctx->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, &opts, sizeof(opts));
360 [ - + ]: 34 : if (!ns_ctx->qpair) {
361 [ # # ]: 0 : printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair failed\n");
362 : 0 : return 1;
363 : : }
364 : :
365 : 34 : return 0;
366 : : }
367 : :
368 : : static void
369 : 34 : cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
370 : : {
371 : 34 : spdk_nvme_ctrlr_free_io_qpair(ns_ctx->qpair);
372 : 34 : }
373 : :
374 : : static void
375 : 8 : cleanup(uint32_t task_count)
376 : : {
377 : : struct ns_entry *entry, *tmp_entry;
378 : : struct worker_thread *worker, *tmp_worker;
379 : : struct ns_worker_ctx *ns_ctx, *tmp_ns_ctx;
380 : :
381 [ + + ]: 22 : TAILQ_FOREACH_SAFE(entry, &g_namespaces, link, tmp_entry) {
382 [ + + ]: 14 : TAILQ_REMOVE(&g_namespaces, entry, link);
383 : 14 : free(entry);
384 : : };
385 : :
386 [ + + ]: 40 : TAILQ_FOREACH_SAFE(worker, &g_workers, link, tmp_worker) {
387 [ + + ]: 32 : TAILQ_REMOVE(&g_workers, worker, link);
388 : :
389 : : /* ns_worker_ctx is a list in the worker */
390 [ + + ]: 66 : TAILQ_FOREACH_SAFE(ns_ctx, &worker->ns_ctx, link, tmp_ns_ctx) {
391 [ + + ]: 34 : TAILQ_REMOVE(&worker->ns_ctx, ns_ctx, link);
392 : 34 : free(ns_ctx);
393 : : }
394 : :
395 : 32 : free(worker);
396 : : };
397 : :
398 [ - + ]: 8 : if (spdk_mempool_count(task_pool) != (size_t)task_count) {
399 [ # # # # ]: 0 : fprintf(stderr, "task_pool count is %zu but should be %u\n",
400 : : spdk_mempool_count(task_pool), task_count);
401 : : }
402 : 8 : spdk_mempool_free(task_pool);
403 : 8 : }
404 : :
405 : : static int
406 : 32 : work_fn(void *arg)
407 : : {
408 : : uint64_t tsc_end;
409 : 32 : struct worker_thread *worker = (struct worker_thread *)arg;
410 : : struct ns_worker_ctx *ns_ctx;
411 : :
412 [ - + ]: 32 : printf("Starting thread on core %u with %s\n", worker->lcore, print_qprio(worker->qprio));
413 : :
414 : : /* Allocate a queue pair for each namespace. */
415 [ + + ]: 66 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
416 [ - + ]: 34 : if (init_ns_worker_ctx(ns_ctx, worker->qprio) != 0) {
417 [ # # ]: 0 : printf("ERROR: init_ns_worker_ctx() failed\n");
418 : 0 : return 1;
419 : : }
420 : : }
421 : :
422 : 32 : tsc_end = spdk_get_ticks() + g_arbitration.time_in_sec * g_arbitration.tsc_rate;
423 : :
424 : : /* Submit initial I/O for each namespace. */
425 [ + + ]: 66 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
426 : 34 : submit_io(ns_ctx, g_arbitration.queue_depth);
427 : : }
428 : :
429 : : while (1) {
430 : : /*
431 : : * Check for completed I/O for each controller. A new
432 : : * I/O will be submitted in the io_complete callback
433 : : * to replace each I/O that is completed.
434 : : */
435 [ + + ]: 11274354 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
436 : 5637198 : check_io(ns_ctx);
437 : : }
438 : :
439 [ + + ]: 5637156 : if (spdk_get_ticks() > tsc_end) {
440 : 32 : break;
441 : : }
442 : : }
443 : :
444 [ + + ]: 66 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
445 : 34 : drain_io(ns_ctx);
446 : 34 : cleanup_ns_worker_ctx(ns_ctx);
447 : : }
448 : :
449 : 32 : return 0;
450 : : }
451 : :
452 : : static void
453 : 0 : usage(char *program_name)
454 : : {
455 [ # # ]: 0 : printf("%s options", program_name);
456 [ # # ]: 0 : printf("\t\n");
457 [ # # ]: 0 : printf("\t[-d DPDK huge memory size in MB]\n");
458 [ # # ]: 0 : printf("\t[-q io depth]\n");
459 [ # # ]: 0 : printf("\t[-o io size in bytes]\n");
460 [ # # ]: 0 : printf("\t[-w io pattern type, must be one of\n");
461 [ # # ]: 0 : printf("\t\t(read, write, randread, randwrite, rw, randrw)]\n");
462 [ # # ]: 0 : printf("\t[-M rwmixread (100 for reads, 0 for writes)]\n");
463 : : #ifdef DEBUG
464 [ # # ]: 0 : printf("\t[-L enable debug logging]\n");
465 : : #else
466 : : printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n");
467 : : #endif
468 : 0 : spdk_log_usage(stdout, "\t\t-L");
469 [ # # ]: 0 : printf("\t[-l enable latency tracking, default: disabled]\n");
470 [ # # ]: 0 : printf("\t\t(0 - disabled; 1 - enabled)\n");
471 [ # # ]: 0 : printf("\t[-t time in seconds]\n");
472 [ # # ]: 0 : printf("\t[-c core mask for I/O submission/completion.]\n");
473 [ # # ]: 0 : printf("\t\t(default: 0xf - 4 cores)]\n");
474 [ # # ]: 0 : printf("\t[-m max completions per poll]\n");
475 [ # # ]: 0 : printf("\t\t(default: 0 - unlimited)\n");
476 [ # # ]: 0 : printf("\t[-a arbitration mechanism, must be one of below]\n");
477 [ # # ]: 0 : printf("\t\t(0, 1, 2)]\n");
478 [ # # ]: 0 : printf("\t\t(0: default round robin mechanism)]\n");
479 [ # # ]: 0 : printf("\t\t(1: weighted round robin mechanism)]\n");
480 [ # # ]: 0 : printf("\t\t(2: vendor specific mechanism)]\n");
481 [ # # ]: 0 : printf("\t[-b enable arbitration user configuration, default: disabled]\n");
482 [ # # ]: 0 : printf("\t\t(0 - disabled; 1 - enabled)\n");
483 [ # # ]: 0 : printf("\t[-n subjected IOs for performance comparison]\n");
484 [ # # ]: 0 : printf("\t[-i shared memory group ID]\n");
485 [ # # ]: 0 : printf("\t[-r remote NVMe over Fabrics target address]\n");
486 [ # # ]: 0 : printf("\t[-g use single file descriptor for DPDK memory segments]\n");
487 : 0 : }
488 : :
489 : : static const char *
490 : 32 : print_qprio(enum spdk_nvme_qprio qprio)
491 : : {
492 [ + - - - : 32 : switch (qprio) {
- ]
493 : 32 : case SPDK_NVME_QPRIO_URGENT:
494 : 32 : return "urgent priority queue";
495 : 0 : case SPDK_NVME_QPRIO_HIGH:
496 : 0 : return "high priority queue";
497 : 0 : case SPDK_NVME_QPRIO_MEDIUM:
498 : 0 : return "medium priority queue";
499 : 0 : case SPDK_NVME_QPRIO_LOW:
500 : 0 : return "low priority queue";
501 : 0 : default:
502 : 0 : return "invalid priority queue";
503 : : }
504 : : }
505 : :
506 : :
507 : : static void
508 : 8 : print_configuration(char *program_name)
509 : : {
510 [ - + ]: 8 : printf("%s run with configuration:\n", program_name);
511 : 8 : printf("%s -q %d -s %d -w %s -M %d -l %d -t %d -c %s -m %d -a %d -b %d -n %d -i %d\n",
512 : : program_name,
513 : : g_arbitration.queue_depth,
514 : : g_arbitration.io_size_bytes,
515 : : g_arbitration.workload_type,
516 : : g_arbitration.rw_percentage,
517 : 8 : g_arbitration.latency_tracking_enable,
518 : : g_arbitration.time_in_sec,
519 : : g_arbitration.core_mask,
520 : : g_arbitration.max_completions,
521 : 8 : g_arbitration.arbitration_mechanism,
522 [ - + ]: 8 : g_arbitration.arbitration_config,
523 : : g_arbitration.io_count,
524 : : g_arbitration.shm_id);
525 : 8 : }
526 : :
527 : :
528 : : static void
529 : 8 : print_performance(void)
530 : : {
531 : : float io_per_second, sent_all_io_in_secs;
532 : : struct worker_thread *worker;
533 : : struct ns_worker_ctx *ns_ctx;
534 : :
535 [ + + ]: 40 : TAILQ_FOREACH(worker, &g_workers, link) {
536 [ + + ]: 66 : TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
537 : 34 : io_per_second = (float)ns_ctx->io_completed / g_arbitration.time_in_sec;
538 : 34 : sent_all_io_in_secs = g_arbitration.io_count / io_per_second;
539 : 60 : printf("%-43.43s core %u: %8.2f IO/s %8.2f secs/%d ios\n",
540 [ - + ]: 34 : ns_ctx->entry->name, worker->lcore,
541 : : io_per_second, sent_all_io_in_secs, g_arbitration.io_count);
542 : : }
543 : : }
544 [ - + ]: 8 : printf("========================================================\n");
545 : :
546 : 8 : printf("\n");
547 : 8 : }
548 : :
549 : : static void
550 : 0 : print_latency_page(struct ctrlr_entry *entry)
551 : : {
552 : : int i;
553 : :
554 : 0 : printf("\n");
555 [ # # ]: 0 : printf("%s\n", entry->name);
556 [ # # ]: 0 : printf("--------------------------------------------------------\n");
557 : :
558 [ # # ]: 0 : for (i = 0; i < 32; i++) {
559 [ # # ]: 0 : if (entry->latency_page.buckets_32us[i])
560 [ # # ]: 0 : printf("Bucket %dus - %dus: %d\n", i * 32, (i + 1) * 32,
561 : 0 : entry->latency_page.buckets_32us[i]);
562 : : }
563 [ # # ]: 0 : for (i = 0; i < 31; i++) {
564 [ # # ]: 0 : if (entry->latency_page.buckets_1ms[i])
565 [ # # ]: 0 : printf("Bucket %dms - %dms: %d\n", i + 1, i + 2,
566 : 0 : entry->latency_page.buckets_1ms[i]);
567 : : }
568 [ # # ]: 0 : for (i = 0; i < 31; i++) {
569 [ # # ]: 0 : if (entry->latency_page.buckets_32ms[i])
570 [ # # ]: 0 : printf("Bucket %dms - %dms: %d\n", (i + 1) * 32, (i + 2) * 32,
571 : 0 : entry->latency_page.buckets_32ms[i]);
572 : : }
573 : 0 : }
574 : :
575 : : static void
576 : 0 : print_latency_statistics(const char *op_name, enum spdk_nvme_intel_log_page log_page)
577 : : {
578 : : struct ctrlr_entry *ctrlr;
579 : :
580 [ # # ]: 0 : printf("%s Latency Statistics:\n", op_name);
581 [ # # ]: 0 : printf("========================================================\n");
582 [ # # ]: 0 : TAILQ_FOREACH(ctrlr, &g_controllers, link) {
583 [ # # ]: 0 : if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr->ctrlr, log_page)) {
584 [ # # ]: 0 : if (spdk_nvme_ctrlr_cmd_get_log_page(
585 : : ctrlr->ctrlr, log_page,
586 : : SPDK_NVME_GLOBAL_NS_TAG,
587 : 0 : &ctrlr->latency_page,
588 : : sizeof(struct spdk_nvme_intel_rw_latency_page),
589 : : 0,
590 : : enable_latency_tracking_complete,
591 : : NULL)) {
592 [ # # ]: 0 : printf("nvme_ctrlr_cmd_get_log_page() failed\n");
593 : 0 : exit(1);
594 : : }
595 : :
596 : 0 : g_arbitration.outstanding_commands++;
597 : : } else {
598 : 0 : printf("Controller %s: %s latency statistics not supported\n",
599 [ # # ]: 0 : ctrlr->name, op_name);
600 : : }
601 : : }
602 : :
603 [ # # ]: 0 : while (g_arbitration.outstanding_commands) {
604 [ # # ]: 0 : TAILQ_FOREACH(ctrlr, &g_controllers, link) {
605 : 0 : spdk_nvme_ctrlr_process_admin_completions(ctrlr->ctrlr);
606 : : }
607 : : }
608 : :
609 [ # # ]: 0 : TAILQ_FOREACH(ctrlr, &g_controllers, link) {
610 [ # # ]: 0 : if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr->ctrlr, log_page)) {
611 : 0 : print_latency_page(ctrlr);
612 : : }
613 : : }
614 : 0 : printf("\n");
615 : 0 : }
616 : :
617 : : static void
618 : 8 : print_stats(void)
619 : : {
620 : 8 : print_performance();
621 [ - + ]: 8 : if (g_arbitration.latency_tracking_enable) {
622 [ # # ]: 0 : if (g_arbitration.rw_percentage != 0) {
623 : 0 : print_latency_statistics("Read", SPDK_NVME_INTEL_LOG_READ_CMD_LATENCY);
624 : : }
625 [ # # ]: 0 : if (g_arbitration.rw_percentage != 100) {
626 : 0 : print_latency_statistics("Write", SPDK_NVME_INTEL_LOG_WRITE_CMD_LATENCY);
627 : : }
628 : : }
629 : 8 : }
630 : :
631 : : static int
632 : 8 : parse_args(int argc, char **argv)
633 : : {
634 : 8 : const char *workload_type = NULL;
635 : 8 : int op = 0;
636 : 8 : bool mix_specified = false;
637 : : int rc;
638 : : long int val;
639 : :
640 : 8 : spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE);
641 : 8 : snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN);
642 : :
643 [ + + + + ]: 28 : while ((op = getopt(argc, argv, "a:b:c:d:ghi:l:m:n:o:q:r:t:w:M:L:")) != -1) {
644 [ - + - + : 20 : switch (op) {
+ - - + ]
645 : 0 : case 'c':
646 : 0 : g_arbitration.core_mask = optarg;
647 : 0 : break;
648 : 2 : case 'd':
649 : 2 : g_dpdk_mem = spdk_strtol(optarg, 10);
650 [ - + ]: 2 : if (g_dpdk_mem < 0) {
651 [ # # ]: 0 : fprintf(stderr, "Invalid DPDK memory size\n");
652 : 0 : return g_dpdk_mem;
653 : : }
654 : 2 : break;
655 : 0 : case 'w':
656 : 0 : g_arbitration.workload_type = optarg;
657 : 0 : break;
658 : 2 : case 'r':
659 [ - + ]: 2 : if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) {
660 [ # # ]: 0 : fprintf(stderr, "Error parsing transport address\n");
661 : 0 : return 1;
662 : : }
663 : 2 : break;
664 : 2 : case 'g':
665 : 2 : g_dpdk_mem_single_seg = true;
666 : 2 : break;
667 : 0 : case 'h':
668 : : case '?':
669 : 0 : usage(argv[0]);
670 : 0 : return 1;
671 : 0 : case 'L':
672 : 0 : rc = spdk_log_set_flag(optarg);
673 [ # # ]: 0 : if (rc < 0) {
674 [ # # ]: 0 : fprintf(stderr, "unknown flag\n");
675 : 0 : usage(argv[0]);
676 : 0 : exit(EXIT_FAILURE);
677 : : }
678 : : #ifdef DEBUG
679 : 0 : spdk_log_set_print_level(SPDK_LOG_DEBUG);
680 : : #endif
681 : 0 : break;
682 : 14 : default:
683 : 14 : val = spdk_strtol(optarg, 10);
684 [ - + ]: 14 : if (val < 0) {
685 [ # # ]: 0 : fprintf(stderr, "Converting a string to integer failed\n");
686 : 0 : return val;
687 : : }
688 : : switch (op) {
689 : 6 : case 'i':
690 : 6 : g_arbitration.shm_id = val;
691 : 6 : break;
692 : 0 : case 'l':
693 : 0 : g_arbitration.latency_tracking_enable = val;
694 : 0 : break;
695 : 0 : case 'm':
696 : 0 : g_arbitration.max_completions = val;
697 : 0 : break;
698 : 0 : case 'q':
699 : 0 : g_arbitration.queue_depth = val;
700 : 0 : break;
701 : 0 : case 'o':
702 : 0 : g_arbitration.io_size_bytes = val;
703 : 0 : break;
704 : 8 : case 't':
705 : 8 : g_arbitration.time_in_sec = val;
706 : 8 : break;
707 : 0 : case 'M':
708 : 0 : g_arbitration.rw_percentage = val;
709 : 0 : mix_specified = true;
710 : 0 : break;
711 : 0 : case 'a':
712 : 0 : g_arbitration.arbitration_mechanism = val;
713 : 0 : break;
714 : 0 : case 'b':
715 : 0 : g_arbitration.arbitration_config = val;
716 : 0 : break;
717 : 0 : case 'n':
718 : 0 : g_arbitration.io_count = val;
719 : 0 : break;
720 : 0 : default:
721 : 0 : usage(argv[0]);
722 : 0 : return -EINVAL;
723 : : }
724 : : }
725 : : }
726 : :
727 : 8 : workload_type = g_arbitration.workload_type;
728 : :
729 [ + + + - ]: 8 : if (strcmp(workload_type, "read") &&
730 [ + + + - ]: 8 : strcmp(workload_type, "write") &&
731 [ + + + - ]: 8 : strcmp(workload_type, "randread") &&
732 [ + + + - ]: 8 : strcmp(workload_type, "randwrite") &&
733 [ + + + - ]: 8 : strcmp(workload_type, "rw") &&
734 [ - + - + ]: 8 : strcmp(workload_type, "randrw")) {
735 [ # # ]: 0 : fprintf(stderr,
736 : : "io pattern type must be one of\n"
737 : : "(read, write, randread, randwrite, rw, randrw)\n");
738 : 0 : return 1;
739 : : }
740 : :
741 [ + + + - ]: 8 : if (!strcmp(workload_type, "read") ||
742 [ - + - + ]: 8 : !strcmp(workload_type, "randread")) {
743 : 0 : g_arbitration.rw_percentage = 100;
744 : : }
745 : :
746 [ + + + - ]: 8 : if (!strcmp(workload_type, "write") ||
747 [ - + - + ]: 8 : !strcmp(workload_type, "randwrite")) {
748 : 0 : g_arbitration.rw_percentage = 0;
749 : : }
750 : :
751 [ + + + - ]: 8 : if (!strcmp(workload_type, "read") ||
752 [ + + + - ]: 8 : !strcmp(workload_type, "randread") ||
753 [ + + + - ]: 8 : !strcmp(workload_type, "write") ||
754 [ - + - + ]: 8 : !strcmp(workload_type, "randwrite")) {
755 [ # # ]: 0 : if (mix_specified) {
756 [ # # ]: 0 : fprintf(stderr, "Ignoring -M option... Please use -M option"
757 : : " only when using rw or randrw.\n");
758 : : }
759 : : }
760 : :
761 [ + + + - ]: 8 : if (!strcmp(workload_type, "rw") ||
762 [ + + + - ]: 8 : !strcmp(workload_type, "randrw")) {
763 [ + - - + ]: 8 : if (g_arbitration.rw_percentage < 0 || g_arbitration.rw_percentage > 100) {
764 [ # # ]: 0 : fprintf(stderr,
765 : : "-M must be specified to value from 0 to 100 "
766 : : "for rw or randrw.\n");
767 : 0 : return 1;
768 : : }
769 : : }
770 : :
771 [ + + + - ]: 8 : if (!strcmp(workload_type, "read") ||
772 [ + + + - ]: 8 : !strcmp(workload_type, "write") ||
773 [ - + - + ]: 8 : !strcmp(workload_type, "rw")) {
774 : 0 : g_arbitration.is_random = 0;
775 : : } else {
776 : 8 : g_arbitration.is_random = 1;
777 : : }
778 : :
779 [ - + ]: 8 : if (g_arbitration.latency_tracking_enable != 0 &&
780 [ # # ]: 0 : g_arbitration.latency_tracking_enable != 1) {
781 [ # # ]: 0 : fprintf(stderr,
782 : : "-l must be specified to value 0 or 1.\n");
783 : 0 : return 1;
784 : : }
785 : :
786 [ + - ]: 8 : switch (g_arbitration.arbitration_mechanism) {
787 : 8 : case SPDK_NVME_CC_AMS_RR:
788 : : case SPDK_NVME_CC_AMS_WRR:
789 : : case SPDK_NVME_CC_AMS_VS:
790 : 8 : break;
791 : 0 : default:
792 [ # # ]: 0 : fprintf(stderr,
793 : : "-a must be specified to value 0, 1, or 7.\n");
794 : 0 : return 1;
795 : : }
796 : :
797 [ - + ]: 8 : if (g_arbitration.arbitration_config != 0 &&
798 [ # # ]: 0 : g_arbitration.arbitration_config != 1) {
799 [ # # ]: 0 : fprintf(stderr,
800 : : "-b must be specified to value 0 or 1.\n");
801 : 0 : return 1;
802 [ - + ]: 8 : } else if (g_arbitration.arbitration_config == 1 &&
803 [ # # ]: 0 : g_arbitration.arbitration_mechanism != SPDK_NVME_CC_AMS_WRR) {
804 [ # # ]: 0 : fprintf(stderr,
805 : : "-a must be specified to 1 (WRR) together.\n");
806 : 0 : return 1;
807 : : }
808 : :
809 : 8 : return 0;
810 : : }
811 : :
812 : : static int
813 : 8 : register_workers(void)
814 : : {
815 : : uint32_t i;
816 : : struct worker_thread *worker;
817 : 8 : enum spdk_nvme_qprio qprio = SPDK_NVME_QPRIO_URGENT;
818 : :
819 [ + + ]: 40 : SPDK_ENV_FOREACH_CORE(i) {
820 : 32 : worker = calloc(1, sizeof(*worker));
821 [ - + ]: 32 : if (worker == NULL) {
822 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate worker\n");
823 : 0 : return -1;
824 : : }
825 : :
826 : 32 : TAILQ_INIT(&worker->ns_ctx);
827 : 32 : worker->lcore = i;
828 : 32 : TAILQ_INSERT_TAIL(&g_workers, worker, link);
829 : 32 : g_arbitration.num_workers++;
830 : :
831 [ - + ]: 32 : if (g_arbitration.arbitration_mechanism == SPDK_NVME_CAP_AMS_WRR) {
832 : 0 : qprio++;
833 : : }
834 : :
835 : 32 : worker->qprio = qprio & SPDK_NVME_CREATE_IO_SQ_QPRIO_MASK;
836 : : }
837 : :
838 : 8 : return 0;
839 : : }
840 : :
841 : : static bool
842 : 2 : probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
843 : : struct spdk_nvme_ctrlr_opts *opts)
844 : : {
845 : : /* Update with user specified arbitration configuration */
846 : 2 : opts->arb_mechanism = g_arbitration.arbitration_mechanism;
847 : :
848 [ - + ]: 2 : printf("Attaching to %s\n", trid->traddr);
849 : :
850 : 2 : return true;
851 : : }
852 : :
853 : : static void
854 : 12 : attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
855 : : struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
856 : : {
857 [ - + ]: 12 : printf("Attached to %s\n", trid->traddr);
858 : :
859 : : /* Update with actual arbitration configuration in use */
860 : 12 : g_arbitration.arbitration_mechanism = opts->arb_mechanism;
861 : :
862 : 12 : register_ctrlr(ctrlr);
863 : 12 : }
864 : :
865 : : static int
866 : 8 : register_controllers(void)
867 : : {
868 [ - + ]: 8 : printf("Initializing NVMe Controllers\n");
869 : :
870 [ - + ]: 8 : if (spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL) != 0) {
871 [ # # # # ]: 0 : fprintf(stderr, "spdk_nvme_probe() failed\n");
872 : 0 : return 1;
873 : : }
874 : :
875 [ - + ]: 8 : if (g_arbitration.num_namespaces == 0) {
876 [ # # # # ]: 0 : fprintf(stderr, "No valid namespaces to continue IO testing\n");
877 : 0 : return 1;
878 : : }
879 : :
880 : 8 : return 0;
881 : : }
882 : :
883 : : static void
884 : 8 : unregister_controllers(void)
885 : : {
886 : : struct ctrlr_entry *entry, *tmp;
887 : 8 : struct spdk_nvme_detach_ctx *detach_ctx = NULL;
888 : :
889 [ + + ]: 20 : TAILQ_FOREACH_SAFE(entry, &g_controllers, link, tmp) {
890 [ + + ]: 12 : TAILQ_REMOVE(&g_controllers, entry, link);
891 [ - + - - ]: 12 : if (g_arbitration.latency_tracking_enable &&
892 : 0 : spdk_nvme_ctrlr_is_feature_supported(entry->ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING)) {
893 : 0 : set_latency_tracking_feature(entry->ctrlr, false);
894 : : }
895 : 12 : spdk_nvme_detach_async(entry->ctrlr, &detach_ctx);
896 : 12 : free(entry);
897 : : }
898 : :
899 [ + + + + ]: 16 : while (detach_ctx && spdk_nvme_detach_poll_async(detach_ctx) == -EAGAIN) {
900 : : ;
901 : : }
902 : 8 : }
903 : :
904 : : static int
905 : 8 : associate_workers_with_ns(void)
906 : : {
907 : 8 : struct ns_entry *entry = TAILQ_FIRST(&g_namespaces);
908 : 8 : struct worker_thread *worker = TAILQ_FIRST(&g_workers);
909 : : struct ns_worker_ctx *ns_ctx;
910 : : int i, count;
911 : :
912 : 8 : count = g_arbitration.num_namespaces > g_arbitration.num_workers ?
913 : : g_arbitration.num_namespaces : g_arbitration.num_workers;
914 : :
915 [ + + ]: 42 : for (i = 0; i < count; i++) {
916 [ - + ]: 34 : if (entry == NULL) {
917 : 0 : break;
918 : : }
919 : :
920 : 34 : ns_ctx = malloc(sizeof(struct ns_worker_ctx));
921 [ - + ]: 34 : if (!ns_ctx) {
922 : 0 : return 1;
923 : : }
924 [ - + ]: 34 : memset(ns_ctx, 0, sizeof(*ns_ctx));
925 : :
926 [ - + ]: 34 : printf("Associating %s with lcore %d\n", entry->name, worker->lcore);
927 : 34 : ns_ctx->entry = entry;
928 : 34 : TAILQ_INSERT_TAIL(&worker->ns_ctx, ns_ctx, link);
929 : :
930 : 34 : worker = TAILQ_NEXT(worker, link);
931 [ + + ]: 34 : if (worker == NULL) {
932 : 8 : worker = TAILQ_FIRST(&g_workers);
933 : : }
934 : :
935 : 34 : entry = TAILQ_NEXT(entry, link);
936 [ + + ]: 34 : if (entry == NULL) {
937 : 27 : entry = TAILQ_FIRST(&g_namespaces);
938 : : }
939 : :
940 : : }
941 : :
942 : 8 : return 0;
943 : : }
944 : :
945 : : static void
946 : 0 : get_feature_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
947 : : {
948 : 0 : struct feature *feature = cb_arg;
949 : 0 : int fid = feature - features;
950 : :
951 [ # # # # ]: 0 : if (spdk_nvme_cpl_is_error(cpl)) {
952 [ # # ]: 0 : printf("get_feature(0x%02X) failed\n", fid);
953 : : } else {
954 : 0 : feature->result = cpl->cdw0;
955 : 0 : feature->valid = true;
956 : : }
957 : :
958 : 0 : g_arbitration.outstanding_commands--;
959 : 0 : }
960 : :
961 : : static int
962 : 0 : get_feature(struct spdk_nvme_ctrlr *ctrlr, uint8_t fid)
963 : : {
964 : 0 : struct spdk_nvme_cmd cmd = {};
965 : 0 : struct feature *feature = &features[fid];
966 : :
967 : 0 : feature->valid = false;
968 : :
969 : 0 : cmd.opc = SPDK_NVME_OPC_GET_FEATURES;
970 : 0 : cmd.cdw10_bits.get_features.fid = fid;
971 : :
972 : 0 : return spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, NULL, 0, get_feature_completion, feature);
973 : : }
974 : :
975 : : static void
976 : 0 : get_arb_feature(struct spdk_nvme_ctrlr *ctrlr)
977 : : {
978 : 0 : get_feature(ctrlr, SPDK_NVME_FEAT_ARBITRATION);
979 : :
980 : 0 : g_arbitration.outstanding_commands++;
981 : :
982 [ # # ]: 0 : while (g_arbitration.outstanding_commands) {
983 : 0 : spdk_nvme_ctrlr_process_admin_completions(ctrlr);
984 : : }
985 : :
986 [ # # # # ]: 0 : if (features[SPDK_NVME_FEAT_ARBITRATION].valid) {
987 : : union spdk_nvme_cmd_cdw11 arb;
988 : 0 : arb.feat_arbitration.raw = features[SPDK_NVME_FEAT_ARBITRATION].result;
989 : :
990 [ # # ]: 0 : printf("Current Arbitration Configuration\n");
991 [ # # ]: 0 : printf("===========\n");
992 [ # # ]: 0 : printf("Arbitration Burst: ");
993 [ # # ]: 0 : if (arb.feat_arbitration.bits.ab == SPDK_NVME_ARBITRATION_BURST_UNLIMITED) {
994 [ # # ]: 0 : printf("no limit\n");
995 : : } else {
996 [ # # # # ]: 0 : printf("%u\n", 1u << arb.feat_arbitration.bits.ab);
997 : : }
998 : :
999 [ # # ]: 0 : printf("Low Priority Weight: %u\n", arb.feat_arbitration.bits.lpw + 1);
1000 [ # # ]: 0 : printf("Medium Priority Weight: %u\n", arb.feat_arbitration.bits.mpw + 1);
1001 [ # # ]: 0 : printf("High Priority Weight: %u\n", arb.feat_arbitration.bits.hpw + 1);
1002 : 0 : printf("\n");
1003 : : }
1004 : 0 : }
1005 : :
1006 : : static void
1007 : 0 : set_feature_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
1008 : : {
1009 : 0 : struct feature *feature = cb_arg;
1010 : 0 : int fid = feature - features;
1011 : :
1012 [ # # # # ]: 0 : if (spdk_nvme_cpl_is_error(cpl)) {
1013 [ # # ]: 0 : printf("set_feature(0x%02X) failed\n", fid);
1014 : 0 : feature->valid = false;
1015 : : } else {
1016 [ # # ]: 0 : printf("Set Arbitration Feature Successfully\n");
1017 : : }
1018 : :
1019 : 0 : g_arbitration.outstanding_commands--;
1020 : 0 : }
1021 : :
1022 : : static int
1023 : 0 : set_arb_feature(struct spdk_nvme_ctrlr *ctrlr)
1024 : : {
1025 : : int ret;
1026 : 0 : struct spdk_nvme_cmd cmd = {};
1027 : :
1028 : 0 : cmd.opc = SPDK_NVME_OPC_SET_FEATURES;
1029 : 0 : cmd.cdw10_bits.set_features.fid = SPDK_NVME_FEAT_ARBITRATION;
1030 : :
1031 : 0 : g_arbitration.outstanding_commands = 0;
1032 : :
1033 [ # # # # ]: 0 : if (features[SPDK_NVME_FEAT_ARBITRATION].valid) {
1034 : 0 : cmd.cdw11_bits.feat_arbitration.bits.ab = SPDK_NVME_ARBITRATION_BURST_UNLIMITED;
1035 : 0 : cmd.cdw11_bits.feat_arbitration.bits.lpw = USER_SPECIFIED_LOW_PRIORITY_WEIGHT;
1036 : 0 : cmd.cdw11_bits.feat_arbitration.bits.mpw = USER_SPECIFIED_MEDIUM_PRIORITY_WEIGHT;
1037 : 0 : cmd.cdw11_bits.feat_arbitration.bits.hpw = USER_SPECIFIED_HIGH_PRIORITY_WEIGHT;
1038 : : }
1039 : :
1040 : 0 : ret = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, NULL, 0,
1041 : : set_feature_completion, &features[SPDK_NVME_FEAT_ARBITRATION]);
1042 [ # # ]: 0 : if (ret) {
1043 [ # # ]: 0 : printf("Set Arbitration Feature: Failed 0x%x\n", ret);
1044 : 0 : return 1;
1045 : : }
1046 : :
1047 : 0 : g_arbitration.outstanding_commands++;
1048 : :
1049 [ # # ]: 0 : while (g_arbitration.outstanding_commands) {
1050 : 0 : spdk_nvme_ctrlr_process_admin_completions(ctrlr);
1051 : : }
1052 : :
1053 [ # # # # ]: 0 : if (!features[SPDK_NVME_FEAT_ARBITRATION].valid) {
1054 [ # # ]: 0 : printf("Set Arbitration Feature failed and use default configuration\n");
1055 : : }
1056 : :
1057 : 0 : return 0;
1058 : : }
1059 : :
1060 : : int
1061 : 8 : main(int argc, char **argv)
1062 : : {
1063 : : int rc;
1064 : : struct worker_thread *worker, *main_worker;
1065 : : unsigned main_core;
1066 : 3 : char task_pool_name[30];
1067 : 8 : uint32_t task_count = 0;
1068 : 3 : struct spdk_env_opts opts;
1069 : :
1070 : 8 : rc = parse_args(argc, argv);
1071 [ - + ]: 8 : if (rc != 0) {
1072 : 0 : return rc;
1073 : : }
1074 : :
1075 : 8 : spdk_env_opts_init(&opts);
1076 : 8 : opts.name = "arb";
1077 : 8 : opts.mem_size = g_dpdk_mem;
1078 [ - + ]: 8 : opts.hugepage_single_segments = g_dpdk_mem_single_seg;
1079 : 8 : opts.core_mask = g_arbitration.core_mask;
1080 : 8 : opts.shm_id = g_arbitration.shm_id;
1081 [ - + ]: 8 : if (spdk_env_init(&opts) < 0) {
1082 : 0 : return 1;
1083 : : }
1084 : :
1085 : 8 : g_arbitration.tsc_rate = spdk_get_ticks_hz();
1086 : :
1087 [ - + ]: 8 : if (register_workers() != 0) {
1088 : 0 : rc = 1;
1089 : 0 : goto exit;
1090 : : }
1091 : :
1092 [ - + ]: 8 : if (register_controllers() != 0) {
1093 : 0 : rc = 1;
1094 : 0 : goto exit;
1095 : : }
1096 : :
1097 [ - + ]: 8 : if (associate_workers_with_ns() != 0) {
1098 : 0 : rc = 1;
1099 : 0 : goto exit;
1100 : : }
1101 : :
1102 [ - + ]: 8 : snprintf(task_pool_name, sizeof(task_pool_name), "task_pool_%d", getpid());
1103 : :
1104 : : /*
1105 : : * The task_count will be dynamically calculated based on the
1106 : : * number of attached active namespaces, queue depth and number
1107 : : * of cores (workers) involved in the IO perations.
1108 : : */
1109 : 8 : task_count = g_arbitration.num_namespaces > g_arbitration.num_workers ?
1110 : 8 : g_arbitration.num_namespaces : g_arbitration.num_workers;
1111 : 8 : task_count *= g_arbitration.queue_depth;
1112 : :
1113 : 8 : task_pool = spdk_mempool_create(task_pool_name, task_count,
1114 : : sizeof(struct arb_task), 0, SPDK_ENV_SOCKET_ID_ANY);
1115 [ - + ]: 8 : if (task_pool == NULL) {
1116 [ # # # # ]: 0 : fprintf(stderr, "could not initialize task pool\n");
1117 : 0 : rc = 1;
1118 : 0 : goto exit;
1119 : : }
1120 : :
1121 : 8 : print_configuration(argv[0]);
1122 : :
1123 [ - + ]: 8 : printf("Initialization complete. Launching workers.\n");
1124 : :
1125 : : /* Launch all of the secondary workers */
1126 : 8 : main_core = spdk_env_get_current_core();
1127 : 8 : main_worker = NULL;
1128 [ + + ]: 40 : TAILQ_FOREACH(worker, &g_workers, link) {
1129 [ + + ]: 32 : if (worker->lcore != main_core) {
1130 : 24 : spdk_env_thread_launch_pinned(worker->lcore, work_fn, worker);
1131 : : } else {
1132 [ - + ]: 8 : assert(main_worker == NULL);
1133 : 8 : main_worker = worker;
1134 : : }
1135 : : }
1136 : :
1137 [ - + ]: 8 : assert(main_worker != NULL);
1138 : 8 : rc = work_fn(main_worker);
1139 : :
1140 : 8 : spdk_env_thread_wait_all();
1141 : :
1142 : 8 : print_stats();
1143 : :
1144 : 8 : exit:
1145 : 8 : unregister_controllers();
1146 : 8 : cleanup(task_count);
1147 : :
1148 : 8 : spdk_env_fini();
1149 : :
1150 [ - + ]: 8 : if (rc != 0) {
1151 [ # # # # ]: 0 : fprintf(stderr, "%s: errors occurred\n", argv[0]);
1152 : : }
1153 : :
1154 : 8 : return rc;
1155 : : }
|