4 * Copyright (c) Intel Corporation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "spdk/stdinc.h"
36 #include "spdk/nvme.h"
38 #include "spdk/string.h"
39 #include "spdk/nvme_intel.h"
42 struct spdk_nvme_ctrlr
*ctrlr
;
43 struct spdk_nvme_intel_rw_latency_page latency_page
;
44 struct ctrlr_entry
*next
;
50 struct spdk_nvme_ctrlr
*ctrlr
;
51 struct spdk_nvme_ns
*ns
;
54 struct ns_entry
*next
;
55 uint32_t io_size_blocks
;
60 struct ns_worker_ctx
{
61 struct ns_entry
*entry
;
62 uint64_t io_completed
;
63 uint64_t current_queue_depth
;
64 uint64_t offset_in_ios
;
66 struct spdk_nvme_qpair
*qpair
;
67 struct ns_worker_ctx
*next
;
71 struct ns_worker_ctx
*ns_ctx
;
75 struct worker_thread
{
76 struct ns_worker_ctx
*ns_ctx
;
77 struct worker_thread
*next
;
79 enum spdk_nvme_qprio qprio
;
84 int outstanding_commands
;
92 uint8_t latency_tracking_enable
;
93 uint8_t arbitration_mechanism
;
94 uint8_t arbitration_config
;
95 uint32_t io_size_bytes
;
96 uint32_t max_completions
;
98 const char *core_mask
;
99 const char *workload_type
;
107 static struct spdk_mempool
*task_pool
= NULL
;
109 static struct ctrlr_entry
*g_controllers
= NULL
;
110 static struct ns_entry
*g_namespaces
= NULL
;
111 static struct worker_thread
*g_workers
= NULL
;
113 static struct feature features
[256];
115 static struct arb_context g_arbitration
= {
117 .outstanding_commands
= 0,
124 .latency_tracking_enable
= 0,
125 .arbitration_mechanism
= SPDK_NVME_CC_AMS_RR
,
126 .arbitration_config
= 0,
127 .io_size_bytes
= 131072,
128 .max_completions
= 0,
129 /* Default 4 cores for urgent/high/medium/low */
131 .workload_type
= "randrw",
135 * For weighted round robin arbitration mechanism, the smaller value between
136 * weight and burst will be picked to execute the commands in one queue.
138 #define USER_SPECIFIED_HIGH_PRIORITY_WEIGHT 32
139 #define USER_SPECIFIED_MEDIUM_PRIORITY_WEIGHT 16
140 #define USER_SPECIFIED_LOW_PRIORITY_WEIGHT 8
141 #define USER_SPECIFIED_ARBITRATION_BURST 7 /* No limit */
144 * Description of dword for priority weight and arbitration burst
145 * ------------------------------------------------------------------------------
146 * 31 : 24 | 23 : 16 | 15 : 08 | 07 : 03 | 02 : 00
147 * ------------------------------------------------------------------------------
148 * High Prio Weight | Medium Prio Weight | Low Prio Weight | Reserved | Arb Burst
149 * ------------------------------------------------------------------------------
151 * The priority weights are zero based value.
153 #define SPDK_NVME_HIGH_PRIO_WEIGHT_SHIFT 24
154 #define SPDK_NVME_MED_PRIO_WEIGHT_SHIFT 16
155 #define SPDK_NVME_LOW_PRIO_WEIGHT_SHIFT 8
156 #define SPDK_NVME_PRIO_WEIGHT_MASK 0xFF
157 #define SPDK_NVME_ARB_BURST_MASK 0x7
159 #define SPDK_NVME_QPRIO_MAX (SPDK_NVME_QPRIO_LOW + 1)
161 static void task_complete(struct arb_task
*task
);
163 static void io_complete(void *ctx
, const struct spdk_nvme_cpl
*completion
);
165 static void get_arb_feature(struct spdk_nvme_ctrlr
*ctrlr
);
167 static int set_arb_feature(struct spdk_nvme_ctrlr
*ctrlr
);
169 static const char *print_qprio(enum spdk_nvme_qprio
);
173 register_ns(struct spdk_nvme_ctrlr
*ctrlr
, struct spdk_nvme_ns
*ns
)
175 struct ns_entry
*entry
;
176 const struct spdk_nvme_ctrlr_data
*cdata
;
178 cdata
= spdk_nvme_ctrlr_get_data(ctrlr
);
180 if (!spdk_nvme_ns_is_active(ns
)) {
181 printf("Controller %-20.20s (%-20.20s): Skipping inactive NS %u\n",
182 cdata
->mn
, cdata
->sn
,
183 spdk_nvme_ns_get_id(ns
));
187 if (spdk_nvme_ns_get_size(ns
) < g_arbitration
.io_size_bytes
||
188 spdk_nvme_ns_get_sector_size(ns
) > g_arbitration
.io_size_bytes
) {
189 printf("WARNING: controller %-20.20s (%-20.20s) ns %u has invalid "
190 "ns size %" PRIu64
" / block size %u for I/O size %u\n",
191 cdata
->mn
, cdata
->sn
, spdk_nvme_ns_get_id(ns
),
192 spdk_nvme_ns_get_size(ns
), spdk_nvme_ns_get_sector_size(ns
),
193 g_arbitration
.io_size_bytes
);
197 entry
= malloc(sizeof(struct ns_entry
));
199 perror("ns_entry malloc");
203 entry
->nvme
.ctrlr
= ctrlr
;
206 entry
->size_in_ios
= spdk_nvme_ns_get_size(ns
) / g_arbitration
.io_size_bytes
;
207 entry
->io_size_blocks
= g_arbitration
.io_size_bytes
/ spdk_nvme_ns_get_sector_size(ns
);
209 snprintf(entry
->name
, 44, "%-20.20s (%-20.20s)", cdata
->mn
, cdata
->sn
);
211 g_arbitration
.num_namespaces
++;
212 entry
->next
= g_namespaces
;
213 g_namespaces
= entry
;
217 enable_latency_tracking_complete(void *cb_arg
, const struct spdk_nvme_cpl
*cpl
)
219 if (spdk_nvme_cpl_is_error(cpl
)) {
220 printf("enable_latency_tracking_complete failed\n");
222 g_arbitration
.outstanding_commands
--;
226 set_latency_tracking_feature(struct spdk_nvme_ctrlr
*ctrlr
, bool enable
)
229 union spdk_nvme_intel_feat_latency_tracking latency_tracking
;
232 latency_tracking
.bits
.enable
= 0x01;
234 latency_tracking
.bits
.enable
= 0x00;
237 res
= spdk_nvme_ctrlr_cmd_set_feature(ctrlr
, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING
,
238 latency_tracking
.raw
, 0, NULL
, 0, enable_latency_tracking_complete
, NULL
);
240 printf("fail to allocate nvme request.\n");
243 g_arbitration
.outstanding_commands
++;
245 while (g_arbitration
.outstanding_commands
) {
246 spdk_nvme_ctrlr_process_admin_completions(ctrlr
);
251 register_ctrlr(struct spdk_nvme_ctrlr
*ctrlr
)
254 struct spdk_nvme_ns
*ns
;
255 struct ctrlr_entry
*entry
= calloc(1, sizeof(struct ctrlr_entry
));
256 const struct spdk_nvme_ctrlr_data
*cdata
= spdk_nvme_ctrlr_get_data(ctrlr
);
259 perror("ctrlr_entry malloc");
263 snprintf(entry
->name
, sizeof(entry
->name
), "%-20.20s (%-20.20s)", cdata
->mn
, cdata
->sn
);
265 entry
->ctrlr
= ctrlr
;
266 entry
->next
= g_controllers
;
267 g_controllers
= entry
;
269 if ((g_arbitration
.latency_tracking_enable
!= 0) &&
270 spdk_nvme_ctrlr_is_feature_supported(ctrlr
, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING
)) {
271 set_latency_tracking_feature(ctrlr
, true);
274 num_ns
= spdk_nvme_ctrlr_get_num_ns(ctrlr
);
275 for (nsid
= 1; nsid
<= num_ns
; nsid
++) {
276 ns
= spdk_nvme_ctrlr_get_ns(ctrlr
, nsid
);
280 register_ns(ctrlr
, ns
);
283 if (g_arbitration
.arbitration_mechanism
== SPDK_NVME_CAP_AMS_WRR
) {
284 get_arb_feature(ctrlr
);
286 if (g_arbitration
.arbitration_config
!= 0) {
287 set_arb_feature(ctrlr
);
288 get_arb_feature(ctrlr
);
293 static __thread
unsigned int seed
= 0;
296 submit_single_io(struct ns_worker_ctx
*ns_ctx
)
298 struct arb_task
*task
= NULL
;
299 uint64_t offset_in_ios
;
301 struct ns_entry
*entry
= ns_ctx
->entry
;
303 task
= spdk_mempool_get(task_pool
);
305 fprintf(stderr
, "Failed to get task from task_pool\n");
309 task
->buf
= spdk_dma_zmalloc(g_arbitration
.io_size_bytes
, 0x200, NULL
);
311 spdk_mempool_put(task_pool
, task
);
312 fprintf(stderr
, "task->buf spdk_dma_zmalloc failed\n");
316 task
->ns_ctx
= ns_ctx
;
318 if (g_arbitration
.is_random
) {
319 offset_in_ios
= rand_r(&seed
) % entry
->size_in_ios
;
321 offset_in_ios
= ns_ctx
->offset_in_ios
++;
322 if (ns_ctx
->offset_in_ios
== entry
->size_in_ios
) {
323 ns_ctx
->offset_in_ios
= 0;
327 if ((g_arbitration
.rw_percentage
== 100) ||
328 (g_arbitration
.rw_percentage
!= 0 &&
329 ((rand_r(&seed
) % 100) < g_arbitration
.rw_percentage
))) {
330 rc
= spdk_nvme_ns_cmd_read(entry
->nvme
.ns
, ns_ctx
->qpair
, task
->buf
,
331 offset_in_ios
* entry
->io_size_blocks
,
332 entry
->io_size_blocks
, io_complete
, task
, 0);
334 rc
= spdk_nvme_ns_cmd_write(entry
->nvme
.ns
, ns_ctx
->qpair
, task
->buf
,
335 offset_in_ios
* entry
->io_size_blocks
,
336 entry
->io_size_blocks
, io_complete
, task
, 0);
340 fprintf(stderr
, "starting I/O failed\n");
343 ns_ctx
->current_queue_depth
++;
347 task_complete(struct arb_task
*task
)
349 struct ns_worker_ctx
*ns_ctx
;
351 ns_ctx
= task
->ns_ctx
;
352 ns_ctx
->current_queue_depth
--;
353 ns_ctx
->io_completed
++;
355 spdk_dma_free(task
->buf
);
356 spdk_mempool_put(task_pool
, task
);
359 * is_draining indicates when time has expired for the test run
360 * and we are just waiting for the previously submitted I/O
361 * to complete. In this case, do not submit a new I/O to replace
362 * the one just completed.
364 if (!ns_ctx
->is_draining
) {
365 submit_single_io(ns_ctx
);
370 io_complete(void *ctx
, const struct spdk_nvme_cpl
*completion
)
372 task_complete((struct arb_task
*)ctx
);
376 check_io(struct ns_worker_ctx
*ns_ctx
)
378 spdk_nvme_qpair_process_completions(ns_ctx
->qpair
, g_arbitration
.max_completions
);
382 submit_io(struct ns_worker_ctx
*ns_ctx
, int queue_depth
)
384 while (queue_depth
-- > 0) {
385 submit_single_io(ns_ctx
);
390 drain_io(struct ns_worker_ctx
*ns_ctx
)
392 ns_ctx
->is_draining
= true;
393 while (ns_ctx
->current_queue_depth
> 0) {
399 init_ns_worker_ctx(struct ns_worker_ctx
*ns_ctx
, enum spdk_nvme_qprio qprio
)
401 struct spdk_nvme_ctrlr
*ctrlr
= ns_ctx
->entry
->nvme
.ctrlr
;
402 struct spdk_nvme_io_qpair_opts opts
;
404 spdk_nvme_ctrlr_get_default_io_qpair_opts(ctrlr
, &opts
, sizeof(opts
));
407 ns_ctx
->qpair
= spdk_nvme_ctrlr_alloc_io_qpair(ctrlr
, &opts
, sizeof(opts
));
408 if (!ns_ctx
->qpair
) {
409 printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair failed\n");
417 cleanup_ns_worker_ctx(struct ns_worker_ctx
*ns_ctx
)
419 spdk_nvme_ctrlr_free_io_qpair(ns_ctx
->qpair
);
423 cleanup(uint32_t task_count
)
425 struct ns_entry
*entry
= g_namespaces
;
426 struct ns_entry
*next_entry
= NULL
;
427 struct worker_thread
*worker
= g_workers
;
428 struct worker_thread
*next_worker
= NULL
;
431 next_entry
= entry
->next
;
437 next_worker
= worker
->next
;
438 free(worker
->ns_ctx
);
440 worker
= next_worker
;
443 if (spdk_mempool_count(task_pool
) != (size_t)task_count
) {
444 fprintf(stderr
, "task_pool count is %zu but should be %u\n",
445 spdk_mempool_count(task_pool
), task_count
);
447 spdk_mempool_free(task_pool
);
454 struct worker_thread
*worker
= (struct worker_thread
*)arg
;
455 struct ns_worker_ctx
*ns_ctx
= NULL
;
457 printf("Starting thread on core %u with %s\n", worker
->lcore
, print_qprio(worker
->qprio
));
459 /* Allocate a queue pair for each namespace. */
460 ns_ctx
= worker
->ns_ctx
;
461 while (ns_ctx
!= NULL
) {
462 if (init_ns_worker_ctx(ns_ctx
, worker
->qprio
) != 0) {
463 printf("ERROR: init_ns_worker_ctx() failed\n");
466 ns_ctx
= ns_ctx
->next
;
469 tsc_end
= spdk_get_ticks() + g_arbitration
.time_in_sec
* g_arbitration
.tsc_rate
;
471 /* Submit initial I/O for each namespace. */
472 ns_ctx
= worker
->ns_ctx
;
474 while (ns_ctx
!= NULL
) {
475 submit_io(ns_ctx
, g_arbitration
.queue_depth
);
476 ns_ctx
= ns_ctx
->next
;
481 * Check for completed I/O for each controller. A new
482 * I/O will be submitted in the io_complete callback
483 * to replace each I/O that is completed.
485 ns_ctx
= worker
->ns_ctx
;
486 while (ns_ctx
!= NULL
) {
488 ns_ctx
= ns_ctx
->next
;
491 if (spdk_get_ticks() > tsc_end
) {
496 ns_ctx
= worker
->ns_ctx
;
497 while (ns_ctx
!= NULL
) {
499 cleanup_ns_worker_ctx(ns_ctx
);
500 ns_ctx
= ns_ctx
->next
;
507 usage(char *program_name
)
509 printf("%s options", program_name
);
511 printf("\t[-q io depth]\n");
512 printf("\t[-s io size in bytes]\n");
513 printf("\t[-w io pattern type, must be one of\n");
514 printf("\t\t(read, write, randread, randwrite, rw, randrw)]\n");
515 printf("\t[-M rwmixread (100 for reads, 0 for writes)]\n");
516 printf("\t[-l enable latency tracking, default: disabled]\n");
517 printf("\t\t(0 - disabled; 1 - enabled)\n");
518 printf("\t[-t time in seconds]\n");
519 printf("\t[-c core mask for I/O submission/completion.]\n");
520 printf("\t\t(default: 0xf - 4 cores)]\n");
521 printf("\t[-m max completions per poll]\n");
522 printf("\t\t(default: 0 - unlimited)\n");
523 printf("\t[-a arbitration mechanism, must be one of below]\n");
524 printf("\t\t(0, 1, 2)]\n");
525 printf("\t\t(0: default round robin mechanism)]\n");
526 printf("\t\t(1: weighted round robin mechanism)]\n");
527 printf("\t\t(2: vendor specific mechanism)]\n");
528 printf("\t[-b enable arbitration user configuration, default: disabled]\n");
529 printf("\t\t(0 - disabled; 1 - enabled)\n");
530 printf("\t[-n subjected IOs for performance comparison]\n");
531 printf("\t[-i shared memory group ID]\n");
535 print_qprio(enum spdk_nvme_qprio qprio
)
538 case SPDK_NVME_QPRIO_URGENT
:
539 return "urgent priority queue";
540 case SPDK_NVME_QPRIO_HIGH
:
541 return "high priority queue";
542 case SPDK_NVME_QPRIO_MEDIUM
:
543 return "medium priority queue";
544 case SPDK_NVME_QPRIO_LOW
:
545 return "low priority queue";
547 return "invalid priority queue";
553 print_configuration(char *program_name
)
555 printf("%s run with configuration:\n", program_name
);
556 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",
558 g_arbitration
.queue_depth
,
559 g_arbitration
.io_size_bytes
,
560 g_arbitration
.workload_type
,
561 g_arbitration
.rw_percentage
,
562 g_arbitration
.latency_tracking_enable
,
563 g_arbitration
.time_in_sec
,
564 g_arbitration
.core_mask
,
565 g_arbitration
.max_completions
,
566 g_arbitration
.arbitration_mechanism
,
567 g_arbitration
.arbitration_config
,
568 g_arbitration
.io_count
,
569 g_arbitration
.shm_id
);
574 print_performance(void)
576 float io_per_second
, sent_all_io_in_secs
;
577 struct worker_thread
*worker
;
578 struct ns_worker_ctx
*ns_ctx
;
582 ns_ctx
= worker
->ns_ctx
;
584 io_per_second
= (float)ns_ctx
->io_completed
/ g_arbitration
.time_in_sec
;
585 sent_all_io_in_secs
= g_arbitration
.io_count
/ io_per_second
;
586 printf("%-43.43s core %u: %8.2f IO/s %8.2f secs/%d ios\n",
587 ns_ctx
->entry
->name
, worker
->lcore
,
588 io_per_second
, sent_all_io_in_secs
, g_arbitration
.io_count
);
589 ns_ctx
= ns_ctx
->next
;
591 worker
= worker
->next
;
593 printf("========================================================\n");
599 print_latency_page(struct ctrlr_entry
*entry
)
604 printf("%s\n", entry
->name
);
605 printf("--------------------------------------------------------\n");
607 for (i
= 0; i
< 32; i
++) {
608 if (entry
->latency_page
.buckets_32us
[i
])
609 printf("Bucket %dus - %dus: %d\n", i
* 32, (i
+ 1) * 32,
610 entry
->latency_page
.buckets_32us
[i
]);
612 for (i
= 0; i
< 31; i
++) {
613 if (entry
->latency_page
.buckets_1ms
[i
])
614 printf("Bucket %dms - %dms: %d\n", i
+ 1, i
+ 2,
615 entry
->latency_page
.buckets_1ms
[i
]);
617 for (i
= 0; i
< 31; i
++) {
618 if (entry
->latency_page
.buckets_32ms
[i
])
619 printf("Bucket %dms - %dms: %d\n", (i
+ 1) * 32, (i
+ 2) * 32,
620 entry
->latency_page
.buckets_32ms
[i
]);
625 print_latency_statistics(const char *op_name
, enum spdk_nvme_intel_log_page log_page
)
627 struct ctrlr_entry
*ctrlr
;
629 printf("%s Latency Statistics:\n", op_name
);
630 printf("========================================================\n");
631 ctrlr
= g_controllers
;
633 if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr
->ctrlr
, log_page
)) {
634 if (spdk_nvme_ctrlr_cmd_get_log_page(
635 ctrlr
->ctrlr
, log_page
,
636 SPDK_NVME_GLOBAL_NS_TAG
,
637 &ctrlr
->latency_page
,
638 sizeof(struct spdk_nvme_intel_rw_latency_page
),
640 enable_latency_tracking_complete
,
642 printf("nvme_ctrlr_cmd_get_log_page() failed\n");
646 g_arbitration
.outstanding_commands
++;
648 printf("Controller %s: %s latency statistics not supported\n",
649 ctrlr
->name
, op_name
);
654 while (g_arbitration
.outstanding_commands
) {
655 ctrlr
= g_controllers
;
657 spdk_nvme_ctrlr_process_admin_completions(ctrlr
->ctrlr
);
662 ctrlr
= g_controllers
;
664 if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr
->ctrlr
, log_page
)) {
665 print_latency_page(ctrlr
);
676 if (g_arbitration
.latency_tracking_enable
) {
677 if (g_arbitration
.rw_percentage
!= 0) {
678 print_latency_statistics("Read", SPDK_NVME_INTEL_LOG_READ_CMD_LATENCY
);
680 if (g_arbitration
.rw_percentage
!= 100) {
681 print_latency_statistics("Write", SPDK_NVME_INTEL_LOG_WRITE_CMD_LATENCY
);
687 parse_args(int argc
, char **argv
)
689 const char *workload_type
= NULL
;
691 bool mix_specified
= false;
693 while ((op
= getopt(argc
, argv
, "c:l:i:m:q:s:t:w:M:a:b:n:h")) != -1) {
696 g_arbitration
.core_mask
= optarg
;
699 g_arbitration
.shm_id
= atoi(optarg
);
702 g_arbitration
.latency_tracking_enable
= atoi(optarg
);
705 g_arbitration
.max_completions
= atoi(optarg
);
708 g_arbitration
.queue_depth
= atoi(optarg
);
711 g_arbitration
.io_size_bytes
= atoi(optarg
);
714 g_arbitration
.time_in_sec
= atoi(optarg
);
717 g_arbitration
.workload_type
= optarg
;
720 g_arbitration
.rw_percentage
= atoi(optarg
);
721 mix_specified
= true;
724 g_arbitration
.arbitration_mechanism
= atoi(optarg
);
727 g_arbitration
.arbitration_config
= atoi(optarg
);
730 g_arbitration
.io_count
= atoi(optarg
);
739 workload_type
= g_arbitration
.workload_type
;
741 if (strcmp(workload_type
, "read") &&
742 strcmp(workload_type
, "write") &&
743 strcmp(workload_type
, "randread") &&
744 strcmp(workload_type
, "randwrite") &&
745 strcmp(workload_type
, "rw") &&
746 strcmp(workload_type
, "randrw")) {
748 "io pattern type must be one of\n"
749 "(read, write, randread, randwrite, rw, randrw)\n");
753 if (!strcmp(workload_type
, "read") ||
754 !strcmp(workload_type
, "randread")) {
755 g_arbitration
.rw_percentage
= 100;
758 if (!strcmp(workload_type
, "write") ||
759 !strcmp(workload_type
, "randwrite")) {
760 g_arbitration
.rw_percentage
= 0;
763 if (!strcmp(workload_type
, "read") ||
764 !strcmp(workload_type
, "randread") ||
765 !strcmp(workload_type
, "write") ||
766 !strcmp(workload_type
, "randwrite")) {
768 fprintf(stderr
, "Ignoring -M option... Please use -M option"
769 " only when using rw or randrw.\n");
773 if (!strcmp(workload_type
, "rw") ||
774 !strcmp(workload_type
, "randrw")) {
775 if (g_arbitration
.rw_percentage
< 0 || g_arbitration
.rw_percentage
> 100) {
777 "-M must be specified to value from 0 to 100 "
778 "for rw or randrw.\n");
783 if (!strcmp(workload_type
, "read") ||
784 !strcmp(workload_type
, "write") ||
785 !strcmp(workload_type
, "rw")) {
786 g_arbitration
.is_random
= 0;
788 g_arbitration
.is_random
= 1;
791 if (g_arbitration
.latency_tracking_enable
!= 0 &&
792 g_arbitration
.latency_tracking_enable
!= 1) {
794 "-l must be specified to value 0 or 1.\n");
798 switch (g_arbitration
.arbitration_mechanism
) {
799 case SPDK_NVME_CC_AMS_RR
:
800 case SPDK_NVME_CC_AMS_WRR
:
801 case SPDK_NVME_CC_AMS_VS
:
805 "-a must be specified to value 0, 1, or 7.\n");
809 if (g_arbitration
.arbitration_config
!= 0 &&
810 g_arbitration
.arbitration_config
!= 1) {
812 "-b must be specified to value 0 or 1.\n");
814 } else if (g_arbitration
.arbitration_config
== 1 &&
815 g_arbitration
.arbitration_mechanism
!= SPDK_NVME_CC_AMS_WRR
) {
817 "-a must be specified to 1 (WRR) together.\n");
825 register_workers(void)
828 struct worker_thread
*worker
;
829 enum spdk_nvme_qprio qprio
= SPDK_NVME_QPRIO_URGENT
;
832 g_arbitration
.num_workers
= 0;
834 SPDK_ENV_FOREACH_CORE(i
) {
835 worker
= calloc(1, sizeof(*worker
));
836 if (worker
== NULL
) {
837 fprintf(stderr
, "Unable to allocate worker\n");
842 worker
->next
= g_workers
;
844 g_arbitration
.num_workers
++;
846 if (g_arbitration
.arbitration_mechanism
== SPDK_NVME_CAP_AMS_WRR
) {
850 worker
->qprio
= qprio
% SPDK_NVME_QPRIO_MAX
;
857 probe_cb(void *cb_ctx
, const struct spdk_nvme_transport_id
*trid
,
858 struct spdk_nvme_ctrlr_opts
*opts
)
860 /* Update with user specified arbitration configuration */
861 opts
->arb_mechanism
= g_arbitration
.arbitration_mechanism
;
863 printf("Attaching to %s\n", trid
->traddr
);
869 attach_cb(void *cb_ctx
, const struct spdk_nvme_transport_id
*trid
,
870 struct spdk_nvme_ctrlr
*ctrlr
, const struct spdk_nvme_ctrlr_opts
*opts
)
872 printf("Attached to %s\n", trid
->traddr
);
874 /* Update with actual arbitration configuration in use */
875 g_arbitration
.arbitration_mechanism
= opts
->arb_mechanism
;
877 register_ctrlr(ctrlr
);
881 register_controllers(void)
883 printf("Initializing NVMe Controllers\n");
885 if (spdk_nvme_probe(NULL
, NULL
, probe_cb
, attach_cb
, NULL
) != 0) {
886 fprintf(stderr
, "spdk_nvme_probe() failed\n");
890 if (g_arbitration
.num_namespaces
== 0) {
891 fprintf(stderr
, "No valid namespaces to continue IO testing\n");
899 unregister_controllers(void)
901 struct ctrlr_entry
*entry
= g_controllers
;
904 struct ctrlr_entry
*next
= entry
->next
;
905 if (g_arbitration
.latency_tracking_enable
&&
906 spdk_nvme_ctrlr_is_feature_supported(entry
->ctrlr
, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING
)) {
907 set_latency_tracking_feature(entry
->ctrlr
, false);
909 spdk_nvme_detach(entry
->ctrlr
);
916 associate_workers_with_ns(void)
918 struct ns_entry
*entry
= g_namespaces
;
919 struct worker_thread
*worker
= g_workers
;
920 struct ns_worker_ctx
*ns_ctx
;
923 count
= g_arbitration
.num_namespaces
> g_arbitration
.num_workers
?
924 g_arbitration
.num_namespaces
: g_arbitration
.num_workers
;
926 for (i
= 0; i
< count
; i
++) {
931 ns_ctx
= malloc(sizeof(struct ns_worker_ctx
));
935 memset(ns_ctx
, 0, sizeof(*ns_ctx
));
937 printf("Associating %s with lcore %d\n", entry
->name
, worker
->lcore
);
938 ns_ctx
->entry
= entry
;
939 ns_ctx
->next
= worker
->ns_ctx
;
940 worker
->ns_ctx
= ns_ctx
;
942 worker
= worker
->next
;
943 if (worker
== NULL
) {
949 entry
= g_namespaces
;
958 get_feature_completion(void *cb_arg
, const struct spdk_nvme_cpl
*cpl
)
960 struct feature
*feature
= cb_arg
;
961 int fid
= feature
- features
;
963 if (spdk_nvme_cpl_is_error(cpl
)) {
964 printf("get_feature(0x%02X) failed\n", fid
);
966 feature
->result
= cpl
->cdw0
;
967 feature
->valid
= true;
970 g_arbitration
.outstanding_commands
--;
974 get_feature(struct spdk_nvme_ctrlr
*ctrlr
, uint8_t fid
)
976 struct spdk_nvme_cmd cmd
= {};
978 cmd
.opc
= SPDK_NVME_OPC_GET_FEATURES
;
981 return spdk_nvme_ctrlr_cmd_admin_raw(ctrlr
, &cmd
, NULL
, 0, get_feature_completion
, &features
[fid
]);
985 get_arb_feature(struct spdk_nvme_ctrlr
*ctrlr
)
987 get_feature(ctrlr
, SPDK_NVME_FEAT_ARBITRATION
);
989 g_arbitration
.outstanding_commands
++;
991 while (g_arbitration
.outstanding_commands
) {
992 spdk_nvme_ctrlr_process_admin_completions(ctrlr
);
995 if (features
[SPDK_NVME_FEAT_ARBITRATION
].valid
) {
996 uint32_t arb
= features
[SPDK_NVME_FEAT_ARBITRATION
].result
;
997 unsigned ab
, lpw
, mpw
, hpw
;
999 ab
= arb
& SPDK_NVME_ARB_BURST_MASK
;
1000 lpw
= ((arb
>> SPDK_NVME_LOW_PRIO_WEIGHT_SHIFT
) & SPDK_NVME_PRIO_WEIGHT_MASK
) + 1;
1001 mpw
= ((arb
>> SPDK_NVME_MED_PRIO_WEIGHT_SHIFT
) & SPDK_NVME_PRIO_WEIGHT_MASK
) + 1;
1002 hpw
= ((arb
>> SPDK_NVME_HIGH_PRIO_WEIGHT_SHIFT
) & SPDK_NVME_PRIO_WEIGHT_MASK
) + 1;
1004 printf("Current Arbitration Configuration\n");
1005 printf("===========\n");
1006 printf("Arbitration Burst: ");
1007 if (ab
== SPDK_NVME_ARB_BURST_MASK
) {
1008 printf("no limit\n");
1010 printf("%u\n", 1u << ab
);
1013 printf("Low Priority Weight: %u\n", lpw
);
1014 printf("Medium Priority Weight: %u\n", mpw
);
1015 printf("High Priority Weight: %u\n", hpw
);
1021 set_feature_completion(void *cb_arg
, const struct spdk_nvme_cpl
*cpl
)
1023 struct feature
*feature
= cb_arg
;
1024 int fid
= feature
- features
;
1026 if (spdk_nvme_cpl_is_error(cpl
)) {
1027 printf("set_feature(0x%02X) failed\n", fid
);
1028 feature
->valid
= false;
1030 printf("Set Arbitration Feature Successfully\n");
1033 g_arbitration
.outstanding_commands
--;
1037 set_arb_feature(struct spdk_nvme_ctrlr
*ctrlr
)
1040 struct spdk_nvme_cmd cmd
= {};
1042 unsigned ab
, lpw
, mpw
, hpw
;
1044 cmd
.opc
= SPDK_NVME_OPC_SET_FEATURES
;
1045 cmd
.cdw10
= SPDK_NVME_FEAT_ARBITRATION
;
1047 g_arbitration
.outstanding_commands
= 0;
1049 if (features
[SPDK_NVME_FEAT_ARBITRATION
].valid
) {
1050 ab
= USER_SPECIFIED_ARBITRATION_BURST
& SPDK_NVME_ARB_BURST_MASK
;
1051 hpw
= USER_SPECIFIED_HIGH_PRIORITY_WEIGHT
<< SPDK_NVME_HIGH_PRIO_WEIGHT_SHIFT
;
1052 mpw
= USER_SPECIFIED_MEDIUM_PRIORITY_WEIGHT
<< SPDK_NVME_MED_PRIO_WEIGHT_SHIFT
;
1053 lpw
= USER_SPECIFIED_LOW_PRIORITY_WEIGHT
<< SPDK_NVME_LOW_PRIO_WEIGHT_SHIFT
;
1054 arb
= hpw
| mpw
| lpw
| ab
;
1058 ret
= spdk_nvme_ctrlr_cmd_admin_raw(ctrlr
, &cmd
, NULL
, 0,
1059 set_feature_completion
, &features
[SPDK_NVME_FEAT_ARBITRATION
]);
1061 printf("Set Arbitration Feature: Failed 0x%x\n", ret
);
1065 g_arbitration
.outstanding_commands
++;
1067 while (g_arbitration
.outstanding_commands
) {
1068 spdk_nvme_ctrlr_process_admin_completions(ctrlr
);
1071 if (!features
[SPDK_NVME_FEAT_ARBITRATION
].valid
) {
1072 printf("Set Arbitration Feature failed and use default configuration\n");
1079 main(int argc
, char **argv
)
1082 struct worker_thread
*worker
, *master_worker
;
1083 unsigned master_core
;
1084 char task_pool_name
[30];
1085 uint32_t task_count
;
1086 struct spdk_env_opts opts
;
1088 rc
= parse_args(argc
, argv
);
1093 spdk_env_opts_init(&opts
);
1095 opts
.core_mask
= g_arbitration
.core_mask
;
1096 opts
.shm_id
= g_arbitration
.shm_id
;
1097 if (spdk_env_init(&opts
) < 0) {
1101 g_arbitration
.tsc_rate
= spdk_get_ticks_hz();
1103 if (register_workers() != 0) {
1107 if (register_controllers() != 0) {
1111 if (associate_workers_with_ns() != 0) {
1115 snprintf(task_pool_name
, sizeof(task_pool_name
), "task_pool_%d", getpid());
1118 * The task_count will be dynamically calculated based on the
1119 * number of attached active namespaces, queue depth and number
1120 * of cores (workers) involved in the IO perations.
1122 task_count
= g_arbitration
.num_namespaces
> g_arbitration
.num_workers
?
1123 g_arbitration
.num_namespaces
: g_arbitration
.num_workers
;
1124 task_count
*= g_arbitration
.queue_depth
;
1126 task_pool
= spdk_mempool_create(task_pool_name
, task_count
,
1127 sizeof(struct arb_task
), 0, SPDK_ENV_SOCKET_ID_ANY
);
1128 if (task_pool
== NULL
) {
1129 fprintf(stderr
, "could not initialize task pool\n");
1133 print_configuration(argv
[0]);
1135 printf("Initialization complete. Launching workers.\n");
1137 /* Launch all of the slave workers */
1138 master_core
= spdk_env_get_current_core();
1139 master_worker
= NULL
;
1141 while (worker
!= NULL
) {
1142 if (worker
->lcore
!= master_core
) {
1143 spdk_env_thread_launch_pinned(worker
->lcore
, work_fn
, worker
);
1145 assert(master_worker
== NULL
);
1146 master_worker
= worker
;
1148 worker
= worker
->next
;
1151 assert(master_worker
!= NULL
);
1152 rc
= work_fn(master_worker
);
1154 spdk_env_thread_wait_all();
1158 unregister_controllers();
1160 cleanup(task_count
);
1163 fprintf(stderr
, "%s: errors occured\n", argv
[0]);