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