xref: /spdk/examples/nvme/arbitration/arbitration.c (revision 186b109dd3a723612e3df79bb3d97699173d39e3)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2016 Intel Corporation.
37991eb19SZiye Yang  *   All rights reserved.
47991eb19SZiye Yang  */
57991eb19SZiye Yang 
6b961d9ccSBen Walker #include "spdk/stdinc.h"
77991eb19SZiye Yang 
84518e4c3SChangpeng Liu #include "spdk/log.h"
97991eb19SZiye Yang #include "spdk/nvme.h"
100dd80395SBen Walker #include "spdk/env.h"
117991eb19SZiye Yang #include "spdk/string.h"
127991eb19SZiye Yang #include "spdk/nvme_intel.h"
137991eb19SZiye Yang 
147991eb19SZiye Yang struct ctrlr_entry {
157991eb19SZiye Yang 	struct spdk_nvme_ctrlr			*ctrlr;
16df56ab77SDaniel Verkamp 	struct spdk_nvme_intel_rw_latency_page	latency_page;
174c3fd228SShuhei Matsumoto 	TAILQ_ENTRY(ctrlr_entry)		link;
187991eb19SZiye Yang 	char					name[1024];
197991eb19SZiye Yang };
207991eb19SZiye Yang 
217991eb19SZiye Yang struct ns_entry {
227991eb19SZiye Yang 	struct {
237991eb19SZiye Yang 		struct spdk_nvme_ctrlr		*ctrlr;
247991eb19SZiye Yang 		struct spdk_nvme_ns		*ns;
257991eb19SZiye Yang 	} nvme;
267991eb19SZiye Yang 
274c3fd228SShuhei Matsumoto 	TAILQ_ENTRY(ns_entry)			link;
287991eb19SZiye Yang 	uint32_t				io_size_blocks;
297991eb19SZiye Yang 	uint64_t				size_in_ios;
307991eb19SZiye Yang 	char					name[1024];
317991eb19SZiye Yang };
327991eb19SZiye Yang 
337991eb19SZiye Yang struct ns_worker_ctx {
347991eb19SZiye Yang 	struct ns_entry				*entry;
357991eb19SZiye Yang 	uint64_t				io_completed;
367991eb19SZiye Yang 	uint64_t				current_queue_depth;
377991eb19SZiye Yang 	uint64_t				offset_in_ios;
387991eb19SZiye Yang 	bool					is_draining;
397991eb19SZiye Yang 	struct spdk_nvme_qpair			*qpair;
404c3fd228SShuhei Matsumoto 	TAILQ_ENTRY(ns_worker_ctx)		link;
417991eb19SZiye Yang };
427991eb19SZiye Yang 
437991eb19SZiye Yang struct arb_task {
447991eb19SZiye Yang 	struct ns_worker_ctx			*ns_ctx;
457991eb19SZiye Yang 	void					*buf;
467991eb19SZiye Yang };
477991eb19SZiye Yang 
487991eb19SZiye Yang struct worker_thread {
494c3fd228SShuhei Matsumoto 	TAILQ_HEAD(, ns_worker_ctx)		ns_ctx;
504c3fd228SShuhei Matsumoto 	TAILQ_ENTRY(worker_thread)		link;
517991eb19SZiye Yang 	unsigned				lcore;
527991eb19SZiye Yang 	enum spdk_nvme_qprio			qprio;
537991eb19SZiye Yang };
547991eb19SZiye Yang 
557991eb19SZiye Yang struct arb_context {
5625270f1dSBen Walker 	int					shm_id;
577991eb19SZiye Yang 	int					outstanding_commands;
587991eb19SZiye Yang 	int					num_namespaces;
597991eb19SZiye Yang 	int					num_workers;
607991eb19SZiye Yang 	int					rw_percentage;
617991eb19SZiye Yang 	int					is_random;
627991eb19SZiye Yang 	int					queue_depth;
637991eb19SZiye Yang 	int					time_in_sec;
647991eb19SZiye Yang 	int					io_count;
657991eb19SZiye Yang 	uint8_t					latency_tracking_enable;
667991eb19SZiye Yang 	uint8_t					arbitration_mechanism;
677991eb19SZiye Yang 	uint8_t					arbitration_config;
687991eb19SZiye Yang 	uint32_t				io_size_bytes;
697991eb19SZiye Yang 	uint32_t				max_completions;
707991eb19SZiye Yang 	uint64_t				tsc_rate;
717991eb19SZiye Yang 	const char				*core_mask;
727991eb19SZiye Yang 	const char				*workload_type;
737991eb19SZiye Yang };
747991eb19SZiye Yang 
757991eb19SZiye Yang struct feature {
767991eb19SZiye Yang 	uint32_t				result;
777991eb19SZiye Yang 	bool					valid;
787991eb19SZiye Yang };
797991eb19SZiye Yang 
8063c1c9d5SZiye Yang static struct spdk_mempool *task_pool		= NULL;
817991eb19SZiye Yang 
824c3fd228SShuhei Matsumoto static TAILQ_HEAD(, ctrlr_entry) g_controllers	= TAILQ_HEAD_INITIALIZER(g_controllers);
834c3fd228SShuhei Matsumoto static TAILQ_HEAD(, ns_entry) g_namespaces	= TAILQ_HEAD_INITIALIZER(g_namespaces);
844c3fd228SShuhei Matsumoto static TAILQ_HEAD(, worker_thread) g_workers	= TAILQ_HEAD_INITIALIZER(g_workers);
857991eb19SZiye Yang 
863630f477SChangpeng Liu static struct feature features[SPDK_NVME_FEAT_ARBITRATION + 1] = {};
8774dcf4aaSMao Jiang static struct spdk_nvme_transport_id g_trid = {};
887991eb19SZiye Yang 
897991eb19SZiye Yang static struct arb_context g_arbitration = {
9025270f1dSBen Walker 	.shm_id					= -1,
917991eb19SZiye Yang 	.outstanding_commands			= 0,
927991eb19SZiye Yang 	.num_workers				= 0,
937991eb19SZiye Yang 	.num_namespaces				= 0,
947991eb19SZiye Yang 	.rw_percentage				= 50,
957991eb19SZiye Yang 	.queue_depth				= 64,
967991eb19SZiye Yang 	.time_in_sec				= 60,
977991eb19SZiye Yang 	.io_count				= 100000,
987991eb19SZiye Yang 	.latency_tracking_enable		= 0,
997991eb19SZiye Yang 	.arbitration_mechanism			= SPDK_NVME_CC_AMS_RR,
1007991eb19SZiye Yang 	.arbitration_config			= 0,
1017991eb19SZiye Yang 	.io_size_bytes				= 131072,
1027991eb19SZiye Yang 	.max_completions			= 0,
10318d26e42SBen Walker 	/* Default 4 cores for urgent/high/medium/low */
1047991eb19SZiye Yang 	.core_mask				= "0xf",
1057991eb19SZiye Yang 	.workload_type				= "randrw",
1067991eb19SZiye Yang };
1077991eb19SZiye Yang 
1084518e4c3SChangpeng Liu static int g_dpdk_mem = 0;
1094518e4c3SChangpeng Liu static bool g_dpdk_mem_single_seg = false;
1104518e4c3SChangpeng Liu 
1117991eb19SZiye Yang /*
1127991eb19SZiye Yang  * For weighted round robin arbitration mechanism, the smaller value between
1137991eb19SZiye Yang  * weight and burst will be picked to execute the commands in one queue.
1147991eb19SZiye Yang  */
1157991eb19SZiye Yang #define USER_SPECIFIED_HIGH_PRIORITY_WEIGHT	32
1167991eb19SZiye Yang #define USER_SPECIFIED_MEDIUM_PRIORITY_WEIGHT	16
1177991eb19SZiye Yang #define USER_SPECIFIED_LOW_PRIORITY_WEIGHT	8
1187991eb19SZiye Yang 
1197991eb19SZiye Yang static void task_complete(struct arb_task *task);
1207991eb19SZiye Yang 
1217991eb19SZiye Yang static void io_complete(void *ctx, const struct spdk_nvme_cpl *completion);
1227991eb19SZiye Yang 
1237991eb19SZiye Yang static void get_arb_feature(struct spdk_nvme_ctrlr *ctrlr);
1247991eb19SZiye Yang 
1257991eb19SZiye Yang static int set_arb_feature(struct spdk_nvme_ctrlr *ctrlr);
1267991eb19SZiye Yang 
1277991eb19SZiye Yang static const char *print_qprio(enum spdk_nvme_qprio);
1287991eb19SZiye Yang 
1297991eb19SZiye Yang 
1307991eb19SZiye Yang static void
1317991eb19SZiye Yang register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
1327991eb19SZiye Yang {
1337991eb19SZiye Yang 	struct ns_entry *entry;
1347991eb19SZiye Yang 	const struct spdk_nvme_ctrlr_data *cdata;
1357991eb19SZiye Yang 
1367991eb19SZiye Yang 	cdata = spdk_nvme_ctrlr_get_data(ctrlr);
1377991eb19SZiye Yang 
1387991eb19SZiye Yang 	if (spdk_nvme_ns_get_size(ns) < g_arbitration.io_size_bytes ||
1396366e361SChangpeng Liu 	    spdk_nvme_ns_get_extended_sector_size(ns) > g_arbitration.io_size_bytes ||
1406366e361SChangpeng Liu 	    g_arbitration.io_size_bytes % spdk_nvme_ns_get_extended_sector_size(ns)) {
1417991eb19SZiye Yang 		printf("WARNING: controller %-20.20s (%-20.20s) ns %u has invalid "
1427991eb19SZiye Yang 		       "ns size %" PRIu64 " / block size %u for I/O size %u\n",
1437991eb19SZiye Yang 		       cdata->mn, cdata->sn, spdk_nvme_ns_get_id(ns),
1446366e361SChangpeng Liu 		       spdk_nvme_ns_get_size(ns), spdk_nvme_ns_get_extended_sector_size(ns),
1457991eb19SZiye Yang 		       g_arbitration.io_size_bytes);
1467991eb19SZiye Yang 		return;
1477991eb19SZiye Yang 	}
1487991eb19SZiye Yang 
1497991eb19SZiye Yang 	entry = malloc(sizeof(struct ns_entry));
1507991eb19SZiye Yang 	if (entry == NULL) {
1517991eb19SZiye Yang 		perror("ns_entry malloc");
1527991eb19SZiye Yang 		exit(1);
1537991eb19SZiye Yang 	}
1547991eb19SZiye Yang 
1557991eb19SZiye Yang 	entry->nvme.ctrlr = ctrlr;
1567991eb19SZiye Yang 	entry->nvme.ns = ns;
1577991eb19SZiye Yang 
1587991eb19SZiye Yang 	entry->size_in_ios = spdk_nvme_ns_get_size(ns) / g_arbitration.io_size_bytes;
1597991eb19SZiye Yang 	entry->io_size_blocks = g_arbitration.io_size_bytes / spdk_nvme_ns_get_sector_size(ns);
1607991eb19SZiye Yang 
1617991eb19SZiye Yang 	snprintf(entry->name, 44, "%-20.20s (%-20.20s)", cdata->mn, cdata->sn);
1627991eb19SZiye Yang 
1637991eb19SZiye Yang 	g_arbitration.num_namespaces++;
1644c3fd228SShuhei Matsumoto 	TAILQ_INSERT_TAIL(&g_namespaces, entry, link);
1657991eb19SZiye Yang }
1667991eb19SZiye Yang 
1677991eb19SZiye Yang static void
1687991eb19SZiye Yang enable_latency_tracking_complete(void *cb_arg, const struct spdk_nvme_cpl *cpl)
1697991eb19SZiye Yang {
1707991eb19SZiye Yang 	if (spdk_nvme_cpl_is_error(cpl)) {
1717991eb19SZiye Yang 		printf("enable_latency_tracking_complete failed\n");
1727991eb19SZiye Yang 	}
1737991eb19SZiye Yang 	g_arbitration.outstanding_commands--;
1747991eb19SZiye Yang }
1757991eb19SZiye Yang 
1767991eb19SZiye Yang static void
1777991eb19SZiye Yang set_latency_tracking_feature(struct spdk_nvme_ctrlr *ctrlr, bool enable)
1787991eb19SZiye Yang {
1797991eb19SZiye Yang 	int res;
1807991eb19SZiye Yang 	union spdk_nvme_intel_feat_latency_tracking latency_tracking;
1817991eb19SZiye Yang 
1827991eb19SZiye Yang 	if (enable) {
1837991eb19SZiye Yang 		latency_tracking.bits.enable = 0x01;
1847991eb19SZiye Yang 	} else {
1857991eb19SZiye Yang 		latency_tracking.bits.enable = 0x00;
1867991eb19SZiye Yang 	}
1877991eb19SZiye Yang 
1887991eb19SZiye Yang 	res = spdk_nvme_ctrlr_cmd_set_feature(ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING,
1897991eb19SZiye Yang 					      latency_tracking.raw, 0, NULL, 0, enable_latency_tracking_complete, NULL);
1907991eb19SZiye Yang 	if (res) {
1917991eb19SZiye Yang 		printf("fail to allocate nvme request.\n");
1927991eb19SZiye Yang 		return;
1937991eb19SZiye Yang 	}
1947991eb19SZiye Yang 	g_arbitration.outstanding_commands++;
1957991eb19SZiye Yang 
1967991eb19SZiye Yang 	while (g_arbitration.outstanding_commands) {
1977991eb19SZiye Yang 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
1987991eb19SZiye Yang 	}
1997991eb19SZiye Yang }
2007991eb19SZiye Yang 
2017991eb19SZiye Yang static void
2027991eb19SZiye Yang register_ctrlr(struct spdk_nvme_ctrlr *ctrlr)
2037991eb19SZiye Yang {
204bd7689bdSChangpeng Liu 	uint32_t nsid;
20534369a12SChangpeng Liu 	struct spdk_nvme_ns *ns;
206df56ab77SDaniel Verkamp 	struct ctrlr_entry *entry = calloc(1, sizeof(struct ctrlr_entry));
2075a8033a5SChangpeng Liu 	union spdk_nvme_cap_register cap = spdk_nvme_ctrlr_get_regs_cap(ctrlr);
2087991eb19SZiye Yang 	const struct spdk_nvme_ctrlr_data *cdata = spdk_nvme_ctrlr_get_data(ctrlr);
2097991eb19SZiye Yang 
2107991eb19SZiye Yang 	if (entry == NULL) {
2117991eb19SZiye Yang 		perror("ctrlr_entry malloc");
2127991eb19SZiye Yang 		exit(1);
2137991eb19SZiye Yang 	}
2147991eb19SZiye Yang 
2157991eb19SZiye Yang 	snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn);
2167991eb19SZiye Yang 
2177991eb19SZiye Yang 	entry->ctrlr = ctrlr;
2184c3fd228SShuhei Matsumoto 	TAILQ_INSERT_TAIL(&g_controllers, entry, link);
2197991eb19SZiye Yang 
2207991eb19SZiye Yang 	if ((g_arbitration.latency_tracking_enable != 0) &&
22159970a89SDaniel Verkamp 	    spdk_nvme_ctrlr_is_feature_supported(ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING)) {
2227991eb19SZiye Yang 		set_latency_tracking_feature(ctrlr, true);
22359970a89SDaniel Verkamp 	}
2247991eb19SZiye Yang 
225bd7689bdSChangpeng Liu 	for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr); nsid != 0;
226bd7689bdSChangpeng Liu 	     nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) {
22734369a12SChangpeng Liu 		ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
22834369a12SChangpeng Liu 		if (ns == NULL) {
22934369a12SChangpeng Liu 			continue;
23034369a12SChangpeng Liu 		}
23134369a12SChangpeng Liu 		register_ns(ctrlr, ns);
2327991eb19SZiye Yang 	}
2337991eb19SZiye Yang 
2345a8033a5SChangpeng Liu 	if (g_arbitration.arbitration_mechanism == SPDK_NVME_CAP_AMS_WRR &&
2355a8033a5SChangpeng Liu 	    (cap.bits.ams & SPDK_NVME_CAP_AMS_WRR)) {
2367991eb19SZiye Yang 		get_arb_feature(ctrlr);
2377991eb19SZiye Yang 
2387991eb19SZiye Yang 		if (g_arbitration.arbitration_config != 0) {
2397991eb19SZiye Yang 			set_arb_feature(ctrlr);
2407991eb19SZiye Yang 			get_arb_feature(ctrlr);
2417991eb19SZiye Yang 		}
2427991eb19SZiye Yang 	}
2437991eb19SZiye Yang }
2447991eb19SZiye Yang 
2457991eb19SZiye Yang static __thread unsigned int seed = 0;
2467991eb19SZiye Yang 
2477991eb19SZiye Yang static void
2487991eb19SZiye Yang submit_single_io(struct ns_worker_ctx *ns_ctx)
2497991eb19SZiye Yang {
2507991eb19SZiye Yang 	struct arb_task		*task = NULL;
2517991eb19SZiye Yang 	uint64_t		offset_in_ios;
2527991eb19SZiye Yang 	int			rc;
2537991eb19SZiye Yang 	struct ns_entry		*entry = ns_ctx->entry;
2547991eb19SZiye Yang 
25563c1c9d5SZiye Yang 	task = spdk_mempool_get(task_pool);
25663c1c9d5SZiye Yang 	if (!task) {
25763c1c9d5SZiye Yang 		fprintf(stderr, "Failed to get task from task_pool\n");
25863c1c9d5SZiye Yang 		exit(1);
25963c1c9d5SZiye Yang 	}
26063c1c9d5SZiye Yang 
26163c1c9d5SZiye Yang 	task->buf = spdk_dma_zmalloc(g_arbitration.io_size_bytes, 0x200, NULL);
26263c1c9d5SZiye Yang 	if (!task->buf) {
26363c1c9d5SZiye Yang 		spdk_mempool_put(task_pool, task);
26463c1c9d5SZiye Yang 		fprintf(stderr, "task->buf spdk_dma_zmalloc failed\n");
2657991eb19SZiye Yang 		exit(1);
2667991eb19SZiye Yang 	}
2677991eb19SZiye Yang 
2687991eb19SZiye Yang 	task->ns_ctx = ns_ctx;
2697991eb19SZiye Yang 
2707991eb19SZiye Yang 	if (g_arbitration.is_random) {
2717991eb19SZiye Yang 		offset_in_ios = rand_r(&seed) % entry->size_in_ios;
2727991eb19SZiye Yang 	} else {
2737991eb19SZiye Yang 		offset_in_ios = ns_ctx->offset_in_ios++;
2747991eb19SZiye Yang 		if (ns_ctx->offset_in_ios == entry->size_in_ios) {
2757991eb19SZiye Yang 			ns_ctx->offset_in_ios = 0;
2767991eb19SZiye Yang 		}
2777991eb19SZiye Yang 	}
2787991eb19SZiye Yang 
2797991eb19SZiye Yang 	if ((g_arbitration.rw_percentage == 100) ||
2807991eb19SZiye Yang 	    (g_arbitration.rw_percentage != 0 &&
2817991eb19SZiye Yang 	     ((rand_r(&seed) % 100) < g_arbitration.rw_percentage))) {
2827991eb19SZiye Yang 		rc = spdk_nvme_ns_cmd_read(entry->nvme.ns, ns_ctx->qpair, task->buf,
2837991eb19SZiye Yang 					   offset_in_ios * entry->io_size_blocks,
2847991eb19SZiye Yang 					   entry->io_size_blocks, io_complete, task, 0);
2857991eb19SZiye Yang 	} else {
2867991eb19SZiye Yang 		rc = spdk_nvme_ns_cmd_write(entry->nvme.ns, ns_ctx->qpair, task->buf,
2877991eb19SZiye Yang 					    offset_in_ios * entry->io_size_blocks,
2887991eb19SZiye Yang 					    entry->io_size_blocks, io_complete, task, 0);
2897991eb19SZiye Yang 	}
2907991eb19SZiye Yang 
2917991eb19SZiye Yang 	if (rc != 0) {
2927991eb19SZiye Yang 		fprintf(stderr, "starting I/O failed\n");
2933d857e36SShuhei Matsumoto 	} else {
2947991eb19SZiye Yang 		ns_ctx->current_queue_depth++;
2957991eb19SZiye Yang 	}
2963d857e36SShuhei Matsumoto }
2977991eb19SZiye Yang 
2987991eb19SZiye Yang static void
2997991eb19SZiye Yang task_complete(struct arb_task *task)
3007991eb19SZiye Yang {
3017991eb19SZiye Yang 	struct ns_worker_ctx	*ns_ctx;
3027991eb19SZiye Yang 
3037991eb19SZiye Yang 	ns_ctx = task->ns_ctx;
3047991eb19SZiye Yang 	ns_ctx->current_queue_depth--;
3057991eb19SZiye Yang 	ns_ctx->io_completed++;
3067991eb19SZiye Yang 
30763c1c9d5SZiye Yang 	spdk_dma_free(task->buf);
30863c1c9d5SZiye Yang 	spdk_mempool_put(task_pool, task);
3097991eb19SZiye Yang 
3107991eb19SZiye Yang 	/*
3117991eb19SZiye Yang 	 * is_draining indicates when time has expired for the test run
3127991eb19SZiye Yang 	 * and we are just waiting for the previously submitted I/O
3137991eb19SZiye Yang 	 * to complete.  In this case, do not submit a new I/O to replace
3147991eb19SZiye Yang 	 * the one just completed.
3157991eb19SZiye Yang 	 */
3167991eb19SZiye Yang 	if (!ns_ctx->is_draining) {
3177991eb19SZiye Yang 		submit_single_io(ns_ctx);
3187991eb19SZiye Yang 	}
3197991eb19SZiye Yang }
3207991eb19SZiye Yang 
3217991eb19SZiye Yang static void
3227991eb19SZiye Yang io_complete(void *ctx, const struct spdk_nvme_cpl *completion)
3237991eb19SZiye Yang {
3247991eb19SZiye Yang 	task_complete((struct arb_task *)ctx);
3257991eb19SZiye Yang }
3267991eb19SZiye Yang 
3277991eb19SZiye Yang static void
3287991eb19SZiye Yang check_io(struct ns_worker_ctx *ns_ctx)
3297991eb19SZiye Yang {
3307991eb19SZiye Yang 	spdk_nvme_qpair_process_completions(ns_ctx->qpair, g_arbitration.max_completions);
3317991eb19SZiye Yang }
3327991eb19SZiye Yang 
3337991eb19SZiye Yang static void
3347991eb19SZiye Yang submit_io(struct ns_worker_ctx *ns_ctx, int queue_depth)
3357991eb19SZiye Yang {
3367991eb19SZiye Yang 	while (queue_depth-- > 0) {
3377991eb19SZiye Yang 		submit_single_io(ns_ctx);
3387991eb19SZiye Yang 	}
3397991eb19SZiye Yang }
3407991eb19SZiye Yang 
3417991eb19SZiye Yang static void
3427991eb19SZiye Yang drain_io(struct ns_worker_ctx *ns_ctx)
3437991eb19SZiye Yang {
3447991eb19SZiye Yang 	ns_ctx->is_draining = true;
3457991eb19SZiye Yang 	while (ns_ctx->current_queue_depth > 0) {
3467991eb19SZiye Yang 		check_io(ns_ctx);
3477991eb19SZiye Yang 	}
3487991eb19SZiye Yang }
3497991eb19SZiye Yang 
3507991eb19SZiye Yang static int
3517991eb19SZiye Yang init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx, enum spdk_nvme_qprio qprio)
3527991eb19SZiye Yang {
353ce4fcbceSDaniel Verkamp 	struct spdk_nvme_ctrlr *ctrlr = ns_ctx->entry->nvme.ctrlr;
354ce4fcbceSDaniel Verkamp 	struct spdk_nvme_io_qpair_opts opts;
355ce4fcbceSDaniel Verkamp 
356ce4fcbceSDaniel Verkamp 	spdk_nvme_ctrlr_get_default_io_qpair_opts(ctrlr, &opts, sizeof(opts));
357ce4fcbceSDaniel Verkamp 	opts.qprio = qprio;
358ce4fcbceSDaniel Verkamp 
359ce4fcbceSDaniel Verkamp 	ns_ctx->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, &opts, sizeof(opts));
3607991eb19SZiye Yang 	if (!ns_ctx->qpair) {
3617991eb19SZiye Yang 		printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair failed\n");
3627991eb19SZiye Yang 		return 1;
3637991eb19SZiye Yang 	}
3647991eb19SZiye Yang 
3657991eb19SZiye Yang 	return 0;
3667991eb19SZiye Yang }
3677991eb19SZiye Yang 
3687991eb19SZiye Yang static void
3697991eb19SZiye Yang cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
3707991eb19SZiye Yang {
3717991eb19SZiye Yang 	spdk_nvme_ctrlr_free_io_qpair(ns_ctx->qpair);
3727991eb19SZiye Yang }
3737991eb19SZiye Yang 
3747991eb19SZiye Yang static void
37563c1c9d5SZiye Yang cleanup(uint32_t task_count)
3767991eb19SZiye Yang {
3774c3fd228SShuhei Matsumoto 	struct ns_entry *entry, *tmp_entry;
3784c3fd228SShuhei Matsumoto 	struct worker_thread *worker, *tmp_worker;
3794c3fd228SShuhei Matsumoto 	struct ns_worker_ctx *ns_ctx, *tmp_ns_ctx;
3807991eb19SZiye Yang 
3814c3fd228SShuhei Matsumoto 	TAILQ_FOREACH_SAFE(entry, &g_namespaces, link, tmp_entry) {
3824c3fd228SShuhei Matsumoto 		TAILQ_REMOVE(&g_namespaces, entry, link);
3837991eb19SZiye Yang 		free(entry);
38484230409SBen Walker 	};
3857991eb19SZiye Yang 
3864c3fd228SShuhei Matsumoto 	TAILQ_FOREACH_SAFE(worker, &g_workers, link, tmp_worker) {
3874c3fd228SShuhei Matsumoto 		TAILQ_REMOVE(&g_workers, worker, link);
3885446c541SJinYu 
3895446c541SJinYu 		/* ns_worker_ctx is a list in the worker */
3904c3fd228SShuhei Matsumoto 		TAILQ_FOREACH_SAFE(ns_ctx, &worker->ns_ctx, link, tmp_ns_ctx) {
3914c3fd228SShuhei Matsumoto 			TAILQ_REMOVE(&worker->ns_ctx, ns_ctx, link);
3925446c541SJinYu 			free(ns_ctx);
3935446c541SJinYu 		}
3945446c541SJinYu 
3957991eb19SZiye Yang 		free(worker);
39684230409SBen Walker 	};
3977991eb19SZiye Yang 
39863c1c9d5SZiye Yang 	if (spdk_mempool_count(task_pool) != (size_t)task_count) {
39963c1c9d5SZiye Yang 		fprintf(stderr, "task_pool count is %zu but should be %u\n",
40063c1c9d5SZiye Yang 			spdk_mempool_count(task_pool), task_count);
4017991eb19SZiye Yang 	}
40263c1c9d5SZiye Yang 	spdk_mempool_free(task_pool);
4037991eb19SZiye Yang }
4047991eb19SZiye Yang 
4057991eb19SZiye Yang static int
4067991eb19SZiye Yang work_fn(void *arg)
4077991eb19SZiye Yang {
4087991eb19SZiye Yang 	uint64_t tsc_end;
4097991eb19SZiye Yang 	struct worker_thread *worker = (struct worker_thread *)arg;
4104c3fd228SShuhei Matsumoto 	struct ns_worker_ctx *ns_ctx;
4117991eb19SZiye Yang 
4127991eb19SZiye Yang 	printf("Starting thread on core %u with %s\n", worker->lcore, print_qprio(worker->qprio));
4137991eb19SZiye Yang 
4147991eb19SZiye Yang 	/* Allocate a queue pair for each namespace. */
4154c3fd228SShuhei Matsumoto 	TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
4167991eb19SZiye Yang 		if (init_ns_worker_ctx(ns_ctx, worker->qprio) != 0) {
4177991eb19SZiye Yang 			printf("ERROR: init_ns_worker_ctx() failed\n");
4187991eb19SZiye Yang 			return 1;
4197991eb19SZiye Yang 		}
4207991eb19SZiye Yang 	}
4217991eb19SZiye Yang 
4220aa29864SBen Walker 	tsc_end = spdk_get_ticks() + g_arbitration.time_in_sec * g_arbitration.tsc_rate;
4237991eb19SZiye Yang 
4247991eb19SZiye Yang 	/* Submit initial I/O for each namespace. */
4254c3fd228SShuhei Matsumoto 	TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
4267991eb19SZiye Yang 		submit_io(ns_ctx, g_arbitration.queue_depth);
4277991eb19SZiye Yang 	}
4287991eb19SZiye Yang 
4297991eb19SZiye Yang 	while (1) {
4307991eb19SZiye Yang 		/*
4317991eb19SZiye Yang 		 * Check for completed I/O for each controller. A new
4327991eb19SZiye Yang 		 * I/O will be submitted in the io_complete callback
4337991eb19SZiye Yang 		 * to replace each I/O that is completed.
4347991eb19SZiye Yang 		 */
4354c3fd228SShuhei Matsumoto 		TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
4367991eb19SZiye Yang 			check_io(ns_ctx);
4377991eb19SZiye Yang 		}
4387991eb19SZiye Yang 
4390aa29864SBen Walker 		if (spdk_get_ticks() > tsc_end) {
4407991eb19SZiye Yang 			break;
4417991eb19SZiye Yang 		}
4427991eb19SZiye Yang 	}
4437991eb19SZiye Yang 
4444c3fd228SShuhei Matsumoto 	TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
4457991eb19SZiye Yang 		drain_io(ns_ctx);
4467991eb19SZiye Yang 		cleanup_ns_worker_ctx(ns_ctx);
4477991eb19SZiye Yang 	}
4487991eb19SZiye Yang 
4497991eb19SZiye Yang 	return 0;
4507991eb19SZiye Yang }
4517991eb19SZiye Yang 
4527991eb19SZiye Yang static void
4537991eb19SZiye Yang usage(char *program_name)
4547991eb19SZiye Yang {
4557991eb19SZiye Yang 	printf("%s options", program_name);
4564518e4c3SChangpeng Liu 	printf("\t\n");
4574518e4c3SChangpeng Liu 	printf("\t[-d DPDK huge memory size in MB]\n");
4587991eb19SZiye Yang 	printf("\t[-q io depth]\n");
45969b4e8b1Swanghailiangx 	printf("\t[-o io size in bytes]\n");
4607991eb19SZiye Yang 	printf("\t[-w io pattern type, must be one of\n");
4617991eb19SZiye Yang 	printf("\t\t(read, write, randread, randwrite, rw, randrw)]\n");
4627991eb19SZiye Yang 	printf("\t[-M rwmixread (100 for reads, 0 for writes)]\n");
4634518e4c3SChangpeng Liu #ifdef DEBUG
4644518e4c3SChangpeng Liu 	printf("\t[-L enable debug logging]\n");
4654518e4c3SChangpeng Liu #else
466809ae055Swanghailiangx 	printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n");
4674518e4c3SChangpeng Liu #endif
4684518e4c3SChangpeng Liu 	spdk_log_usage(stdout, "\t\t-L");
4697991eb19SZiye Yang 	printf("\t[-l enable latency tracking, default: disabled]\n");
4707991eb19SZiye Yang 	printf("\t\t(0 - disabled; 1 - enabled)\n");
4717991eb19SZiye Yang 	printf("\t[-t time in seconds]\n");
4727991eb19SZiye Yang 	printf("\t[-c core mask for I/O submission/completion.]\n");
4737991eb19SZiye Yang 	printf("\t\t(default: 0xf - 4 cores)]\n");
4747991eb19SZiye Yang 	printf("\t[-m max completions per poll]\n");
4757991eb19SZiye Yang 	printf("\t\t(default: 0 - unlimited)\n");
4767991eb19SZiye Yang 	printf("\t[-a arbitration mechanism, must be one of below]\n");
4777991eb19SZiye Yang 	printf("\t\t(0, 1, 2)]\n");
4787991eb19SZiye Yang 	printf("\t\t(0: default round robin mechanism)]\n");
4797991eb19SZiye Yang 	printf("\t\t(1: weighted round robin mechanism)]\n");
4807991eb19SZiye Yang 	printf("\t\t(2: vendor specific mechanism)]\n");
4817991eb19SZiye Yang 	printf("\t[-b enable arbitration user configuration, default: disabled]\n");
4827991eb19SZiye Yang 	printf("\t\t(0 - disabled; 1 - enabled)\n");
48325270f1dSBen Walker 	printf("\t[-n subjected IOs for performance comparison]\n");
48425270f1dSBen Walker 	printf("\t[-i shared memory group ID]\n");
48574dcf4aaSMao Jiang 	printf("\t[-r remote NVMe over Fabrics target address]\n");
48674dcf4aaSMao Jiang 	printf("\t[-g use single file descriptor for DPDK memory segments]\n");
4877991eb19SZiye Yang }
4887991eb19SZiye Yang 
4897991eb19SZiye Yang static const char *
4907991eb19SZiye Yang print_qprio(enum spdk_nvme_qprio qprio)
4917991eb19SZiye Yang {
4927991eb19SZiye Yang 	switch (qprio) {
4937991eb19SZiye Yang 	case SPDK_NVME_QPRIO_URGENT:
4947991eb19SZiye Yang 		return "urgent priority queue";
4957991eb19SZiye Yang 	case SPDK_NVME_QPRIO_HIGH:
4967991eb19SZiye Yang 		return "high priority queue";
4977991eb19SZiye Yang 	case SPDK_NVME_QPRIO_MEDIUM:
4987991eb19SZiye Yang 		return "medium priority queue";
4997991eb19SZiye Yang 	case SPDK_NVME_QPRIO_LOW:
5007991eb19SZiye Yang 		return "low priority queue";
5017991eb19SZiye Yang 	default:
5027991eb19SZiye Yang 		return "invalid priority queue";
5037991eb19SZiye Yang 	}
5047991eb19SZiye Yang }
5057991eb19SZiye Yang 
5067991eb19SZiye Yang 
5077991eb19SZiye Yang static void
5087991eb19SZiye Yang print_configuration(char *program_name)
5097991eb19SZiye Yang {
5107991eb19SZiye Yang 	printf("%s run with configuration:\n", program_name);
5119b847c15SGangCao 	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",
5127991eb19SZiye Yang 	       program_name,
5137991eb19SZiye Yang 	       g_arbitration.queue_depth,
5147991eb19SZiye Yang 	       g_arbitration.io_size_bytes,
5157991eb19SZiye Yang 	       g_arbitration.workload_type,
5167991eb19SZiye Yang 	       g_arbitration.rw_percentage,
5177991eb19SZiye Yang 	       g_arbitration.latency_tracking_enable,
5187991eb19SZiye Yang 	       g_arbitration.time_in_sec,
5197991eb19SZiye Yang 	       g_arbitration.core_mask,
5207991eb19SZiye Yang 	       g_arbitration.max_completions,
5217991eb19SZiye Yang 	       g_arbitration.arbitration_mechanism,
5227991eb19SZiye Yang 	       g_arbitration.arbitration_config,
5239b847c15SGangCao 	       g_arbitration.io_count,
5249b847c15SGangCao 	       g_arbitration.shm_id);
5257991eb19SZiye Yang }
5267991eb19SZiye Yang 
5277991eb19SZiye Yang 
5287991eb19SZiye Yang static void
5297991eb19SZiye Yang print_performance(void)
5307991eb19SZiye Yang {
5317991eb19SZiye Yang 	float io_per_second, sent_all_io_in_secs;
5327991eb19SZiye Yang 	struct worker_thread	*worker;
5337991eb19SZiye Yang 	struct ns_worker_ctx	*ns_ctx;
5347991eb19SZiye Yang 
5354c3fd228SShuhei Matsumoto 	TAILQ_FOREACH(worker, &g_workers, link) {
5364c3fd228SShuhei Matsumoto 		TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
5377991eb19SZiye Yang 			io_per_second = (float)ns_ctx->io_completed / g_arbitration.time_in_sec;
5387991eb19SZiye Yang 			sent_all_io_in_secs = g_arbitration.io_count / io_per_second;
5397991eb19SZiye Yang 			printf("%-43.43s core %u: %8.2f IO/s %8.2f secs/%d ios\n",
5407991eb19SZiye Yang 			       ns_ctx->entry->name, worker->lcore,
5417991eb19SZiye Yang 			       io_per_second, sent_all_io_in_secs, g_arbitration.io_count);
5427991eb19SZiye Yang 		}
5437991eb19SZiye Yang 	}
5447991eb19SZiye Yang 	printf("========================================================\n");
5457991eb19SZiye Yang 
5467991eb19SZiye Yang 	printf("\n");
5477991eb19SZiye Yang }
5487991eb19SZiye Yang 
5497991eb19SZiye Yang static void
5507991eb19SZiye Yang print_latency_page(struct ctrlr_entry *entry)
5517991eb19SZiye Yang {
5527991eb19SZiye Yang 	int i;
5537991eb19SZiye Yang 
5547991eb19SZiye Yang 	printf("\n");
5557991eb19SZiye Yang 	printf("%s\n", entry->name);
5567991eb19SZiye Yang 	printf("--------------------------------------------------------\n");
5577991eb19SZiye Yang 
5587991eb19SZiye Yang 	for (i = 0; i < 32; i++) {
559df56ab77SDaniel Verkamp 		if (entry->latency_page.buckets_32us[i])
5607991eb19SZiye Yang 			printf("Bucket %dus - %dus: %d\n", i * 32, (i + 1) * 32,
561df56ab77SDaniel Verkamp 			       entry->latency_page.buckets_32us[i]);
5627991eb19SZiye Yang 	}
5637991eb19SZiye Yang 	for (i = 0; i < 31; i++) {
564df56ab77SDaniel Verkamp 		if (entry->latency_page.buckets_1ms[i])
5657991eb19SZiye Yang 			printf("Bucket %dms - %dms: %d\n", i + 1, i + 2,
566df56ab77SDaniel Verkamp 			       entry->latency_page.buckets_1ms[i]);
5677991eb19SZiye Yang 	}
5687991eb19SZiye Yang 	for (i = 0; i < 31; i++) {
569df56ab77SDaniel Verkamp 		if (entry->latency_page.buckets_32ms[i])
5707991eb19SZiye Yang 			printf("Bucket %dms - %dms: %d\n", (i + 1) * 32, (i + 2) * 32,
571df56ab77SDaniel Verkamp 			       entry->latency_page.buckets_32ms[i]);
5727991eb19SZiye Yang 	}
5737991eb19SZiye Yang }
5747991eb19SZiye Yang 
5757991eb19SZiye Yang static void
5767991eb19SZiye Yang print_latency_statistics(const char *op_name, enum spdk_nvme_intel_log_page log_page)
5777991eb19SZiye Yang {
5787991eb19SZiye Yang 	struct ctrlr_entry	*ctrlr;
5797991eb19SZiye Yang 
5807991eb19SZiye Yang 	printf("%s Latency Statistics:\n", op_name);
5817991eb19SZiye Yang 	printf("========================================================\n");
5824c3fd228SShuhei Matsumoto 	TAILQ_FOREACH(ctrlr, &g_controllers, link) {
5837991eb19SZiye Yang 		if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr->ctrlr, log_page)) {
5847991eb19SZiye Yang 			if (spdk_nvme_ctrlr_cmd_get_log_page(
5857991eb19SZiye Yang 				    ctrlr->ctrlr, log_page,
5867991eb19SZiye Yang 				    SPDK_NVME_GLOBAL_NS_TAG,
587df56ab77SDaniel Verkamp 				    &ctrlr->latency_page,
5887991eb19SZiye Yang 				    sizeof(struct spdk_nvme_intel_rw_latency_page),
589d24ea900SDaniel Verkamp 				    0,
5907991eb19SZiye Yang 				    enable_latency_tracking_complete,
5917991eb19SZiye Yang 				    NULL)) {
5927991eb19SZiye Yang 				printf("nvme_ctrlr_cmd_get_log_page() failed\n");
5937991eb19SZiye Yang 				exit(1);
5947991eb19SZiye Yang 			}
5957991eb19SZiye Yang 
5967991eb19SZiye Yang 			g_arbitration.outstanding_commands++;
5977991eb19SZiye Yang 		} else {
5987991eb19SZiye Yang 			printf("Controller %s: %s latency statistics not supported\n",
5997991eb19SZiye Yang 			       ctrlr->name, op_name);
6007991eb19SZiye Yang 		}
6017991eb19SZiye Yang 	}
6027991eb19SZiye Yang 
6037991eb19SZiye Yang 	while (g_arbitration.outstanding_commands) {
6044c3fd228SShuhei Matsumoto 		TAILQ_FOREACH(ctrlr, &g_controllers, link) {
6057991eb19SZiye Yang 			spdk_nvme_ctrlr_process_admin_completions(ctrlr->ctrlr);
6067991eb19SZiye Yang 		}
6077991eb19SZiye Yang 	}
6087991eb19SZiye Yang 
6094c3fd228SShuhei Matsumoto 	TAILQ_FOREACH(ctrlr, &g_controllers, link) {
6107991eb19SZiye Yang 		if (spdk_nvme_ctrlr_is_log_page_supported(ctrlr->ctrlr, log_page)) {
6117991eb19SZiye Yang 			print_latency_page(ctrlr);
6127991eb19SZiye Yang 		}
6137991eb19SZiye Yang 	}
6147991eb19SZiye Yang 	printf("\n");
6157991eb19SZiye Yang }
6167991eb19SZiye Yang 
6177991eb19SZiye Yang static void
6187991eb19SZiye Yang print_stats(void)
6197991eb19SZiye Yang {
6207991eb19SZiye Yang 	print_performance();
6217991eb19SZiye Yang 	if (g_arbitration.latency_tracking_enable) {
6227991eb19SZiye Yang 		if (g_arbitration.rw_percentage != 0) {
6237991eb19SZiye Yang 			print_latency_statistics("Read", SPDK_NVME_INTEL_LOG_READ_CMD_LATENCY);
6247991eb19SZiye Yang 		}
6257991eb19SZiye Yang 		if (g_arbitration.rw_percentage != 100) {
6267991eb19SZiye Yang 			print_latency_statistics("Write", SPDK_NVME_INTEL_LOG_WRITE_CMD_LATENCY);
6277991eb19SZiye Yang 		}
6287991eb19SZiye Yang 	}
6297991eb19SZiye Yang }
6307991eb19SZiye Yang 
6317991eb19SZiye Yang static int
6327991eb19SZiye Yang parse_args(int argc, char **argv)
6337991eb19SZiye Yang {
6347991eb19SZiye Yang 	const char *workload_type	= NULL;
6357991eb19SZiye Yang 	int op				= 0;
6367991eb19SZiye Yang 	bool mix_specified		= false;
6374518e4c3SChangpeng Liu 	int				rc;
6380f9dc2afSShuhei Matsumoto 	long int val;
6397991eb19SZiye Yang 
64074dcf4aaSMao Jiang 	spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE);
64174dcf4aaSMao Jiang 	snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN);
64274dcf4aaSMao Jiang 
64369b4e8b1Swanghailiangx 	while ((op = getopt(argc, argv, "a:b:c:d:ghi:l:m:n:o:q:r:t:w:M:L:")) != -1) {
6447991eb19SZiye Yang 		switch (op) {
6457991eb19SZiye Yang 		case 'c':
6467991eb19SZiye Yang 			g_arbitration.core_mask = optarg;
6477991eb19SZiye Yang 			break;
6484518e4c3SChangpeng Liu 		case 'd':
6494518e4c3SChangpeng Liu 			g_dpdk_mem = spdk_strtol(optarg, 10);
6504518e4c3SChangpeng Liu 			if (g_dpdk_mem < 0) {
6514518e4c3SChangpeng Liu 				fprintf(stderr, "Invalid DPDK memory size\n");
6524518e4c3SChangpeng Liu 				return g_dpdk_mem;
6534518e4c3SChangpeng Liu 			}
6544518e4c3SChangpeng Liu 			break;
6557991eb19SZiye Yang 		case 'w':
6567991eb19SZiye Yang 			g_arbitration.workload_type = optarg;
6577991eb19SZiye Yang 			break;
65874dcf4aaSMao Jiang 		case 'r':
65974dcf4aaSMao Jiang 			if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) {
66074dcf4aaSMao Jiang 				fprintf(stderr, "Error parsing transport address\n");
66174dcf4aaSMao Jiang 				return 1;
66274dcf4aaSMao Jiang 			}
66374dcf4aaSMao Jiang 			break;
66474dcf4aaSMao Jiang 		case 'g':
6654518e4c3SChangpeng Liu 			g_dpdk_mem_single_seg = true;
66674dcf4aaSMao Jiang 			break;
6670f9dc2afSShuhei Matsumoto 		case 'h':
6680f9dc2afSShuhei Matsumoto 		case '?':
6690f9dc2afSShuhei Matsumoto 			usage(argv[0]);
6700f9dc2afSShuhei Matsumoto 			return 1;
6714518e4c3SChangpeng Liu 		case 'L':
6724518e4c3SChangpeng Liu 			rc = spdk_log_set_flag(optarg);
6734518e4c3SChangpeng Liu 			if (rc < 0) {
6744518e4c3SChangpeng Liu 				fprintf(stderr, "unknown flag\n");
6754518e4c3SChangpeng Liu 				usage(argv[0]);
6764518e4c3SChangpeng Liu 				exit(EXIT_FAILURE);
6774518e4c3SChangpeng Liu 			}
6784518e4c3SChangpeng Liu #ifdef DEBUG
6794518e4c3SChangpeng Liu 			spdk_log_set_print_level(SPDK_LOG_DEBUG);
6804518e4c3SChangpeng Liu #endif
6814518e4c3SChangpeng Liu 			break;
6820f9dc2afSShuhei Matsumoto 		default:
6830f9dc2afSShuhei Matsumoto 			val = spdk_strtol(optarg, 10);
6840f9dc2afSShuhei Matsumoto 			if (val < 0) {
6850f9dc2afSShuhei Matsumoto 				fprintf(stderr, "Converting a string to integer failed\n");
6860f9dc2afSShuhei Matsumoto 				return val;
6870f9dc2afSShuhei Matsumoto 			}
6880f9dc2afSShuhei Matsumoto 			switch (op) {
6890f9dc2afSShuhei Matsumoto 			case 'i':
6900f9dc2afSShuhei Matsumoto 				g_arbitration.shm_id = val;
6910f9dc2afSShuhei Matsumoto 				break;
6920f9dc2afSShuhei Matsumoto 			case 'l':
6930f9dc2afSShuhei Matsumoto 				g_arbitration.latency_tracking_enable = val;
6940f9dc2afSShuhei Matsumoto 				break;
6950f9dc2afSShuhei Matsumoto 			case 'm':
6960f9dc2afSShuhei Matsumoto 				g_arbitration.max_completions = val;
6970f9dc2afSShuhei Matsumoto 				break;
6980f9dc2afSShuhei Matsumoto 			case 'q':
6990f9dc2afSShuhei Matsumoto 				g_arbitration.queue_depth = val;
7000f9dc2afSShuhei Matsumoto 				break;
70169b4e8b1Swanghailiangx 			case 'o':
7020f9dc2afSShuhei Matsumoto 				g_arbitration.io_size_bytes = val;
7030f9dc2afSShuhei Matsumoto 				break;
7040f9dc2afSShuhei Matsumoto 			case 't':
7050f9dc2afSShuhei Matsumoto 				g_arbitration.time_in_sec = val;
7060f9dc2afSShuhei Matsumoto 				break;
7077991eb19SZiye Yang 			case 'M':
7080f9dc2afSShuhei Matsumoto 				g_arbitration.rw_percentage = val;
7097991eb19SZiye Yang 				mix_specified = true;
7107991eb19SZiye Yang 				break;
7117991eb19SZiye Yang 			case 'a':
7120f9dc2afSShuhei Matsumoto 				g_arbitration.arbitration_mechanism = val;
7137991eb19SZiye Yang 				break;
7147991eb19SZiye Yang 			case 'b':
7150f9dc2afSShuhei Matsumoto 				g_arbitration.arbitration_config = val;
7167991eb19SZiye Yang 				break;
71725270f1dSBen Walker 			case 'n':
7180f9dc2afSShuhei Matsumoto 				g_arbitration.io_count = val;
7197991eb19SZiye Yang 				break;
7207991eb19SZiye Yang 			default:
7217991eb19SZiye Yang 				usage(argv[0]);
7220f9dc2afSShuhei Matsumoto 				return -EINVAL;
7230f9dc2afSShuhei Matsumoto 			}
7247991eb19SZiye Yang 		}
7257991eb19SZiye Yang 	}
7267991eb19SZiye Yang 
7277991eb19SZiye Yang 	workload_type = g_arbitration.workload_type;
7287991eb19SZiye Yang 
7297991eb19SZiye Yang 	if (strcmp(workload_type, "read") &&
7307991eb19SZiye Yang 	    strcmp(workload_type, "write") &&
7317991eb19SZiye Yang 	    strcmp(workload_type, "randread") &&
7327991eb19SZiye Yang 	    strcmp(workload_type, "randwrite") &&
7337991eb19SZiye Yang 	    strcmp(workload_type, "rw") &&
7347991eb19SZiye Yang 	    strcmp(workload_type, "randrw")) {
7357991eb19SZiye Yang 		fprintf(stderr,
7367991eb19SZiye Yang 			"io pattern type must be one of\n"
7377991eb19SZiye Yang 			"(read, write, randread, randwrite, rw, randrw)\n");
7387991eb19SZiye Yang 		return 1;
7397991eb19SZiye Yang 	}
7407991eb19SZiye Yang 
7417991eb19SZiye Yang 	if (!strcmp(workload_type, "read") ||
7427991eb19SZiye Yang 	    !strcmp(workload_type, "randread")) {
7437991eb19SZiye Yang 		g_arbitration.rw_percentage = 100;
7447991eb19SZiye Yang 	}
7457991eb19SZiye Yang 
7467991eb19SZiye Yang 	if (!strcmp(workload_type, "write") ||
7477991eb19SZiye Yang 	    !strcmp(workload_type, "randwrite")) {
7487991eb19SZiye Yang 		g_arbitration.rw_percentage = 0;
7497991eb19SZiye Yang 	}
7507991eb19SZiye Yang 
7517991eb19SZiye Yang 	if (!strcmp(workload_type, "read") ||
7527991eb19SZiye Yang 	    !strcmp(workload_type, "randread") ||
7537991eb19SZiye Yang 	    !strcmp(workload_type, "write") ||
7547991eb19SZiye Yang 	    !strcmp(workload_type, "randwrite")) {
7557991eb19SZiye Yang 		if (mix_specified) {
7567991eb19SZiye Yang 			fprintf(stderr, "Ignoring -M option... Please use -M option"
7577991eb19SZiye Yang 				" only when using rw or randrw.\n");
7587991eb19SZiye Yang 		}
7597991eb19SZiye Yang 	}
7607991eb19SZiye Yang 
7617991eb19SZiye Yang 	if (!strcmp(workload_type, "rw") ||
7627991eb19SZiye Yang 	    !strcmp(workload_type, "randrw")) {
7637991eb19SZiye Yang 		if (g_arbitration.rw_percentage < 0 || g_arbitration.rw_percentage > 100) {
7647991eb19SZiye Yang 			fprintf(stderr,
7657991eb19SZiye Yang 				"-M must be specified to value from 0 to 100 "
7667991eb19SZiye Yang 				"for rw or randrw.\n");
7677991eb19SZiye Yang 			return 1;
7687991eb19SZiye Yang 		}
7697991eb19SZiye Yang 	}
7707991eb19SZiye Yang 
7717991eb19SZiye Yang 	if (!strcmp(workload_type, "read") ||
7727991eb19SZiye Yang 	    !strcmp(workload_type, "write") ||
7737991eb19SZiye Yang 	    !strcmp(workload_type, "rw")) {
7747991eb19SZiye Yang 		g_arbitration.is_random = 0;
7757991eb19SZiye Yang 	} else {
7767991eb19SZiye Yang 		g_arbitration.is_random = 1;
7777991eb19SZiye Yang 	}
7787991eb19SZiye Yang 
7797991eb19SZiye Yang 	if (g_arbitration.latency_tracking_enable != 0 &&
7807991eb19SZiye Yang 	    g_arbitration.latency_tracking_enable != 1) {
7817991eb19SZiye Yang 		fprintf(stderr,
7827991eb19SZiye Yang 			"-l must be specified to value 0 or 1.\n");
7837991eb19SZiye Yang 		return 1;
7847991eb19SZiye Yang 	}
7857991eb19SZiye Yang 
7867991eb19SZiye Yang 	switch (g_arbitration.arbitration_mechanism) {
7877991eb19SZiye Yang 	case SPDK_NVME_CC_AMS_RR:
7887991eb19SZiye Yang 	case SPDK_NVME_CC_AMS_WRR:
7897991eb19SZiye Yang 	case SPDK_NVME_CC_AMS_VS:
7907991eb19SZiye Yang 		break;
7917991eb19SZiye Yang 	default:
7927991eb19SZiye Yang 		fprintf(stderr,
7937991eb19SZiye Yang 			"-a must be specified to value 0, 1, or 7.\n");
7947991eb19SZiye Yang 		return 1;
7957991eb19SZiye Yang 	}
7967991eb19SZiye Yang 
7977991eb19SZiye Yang 	if (g_arbitration.arbitration_config != 0 &&
7987991eb19SZiye Yang 	    g_arbitration.arbitration_config != 1) {
7997991eb19SZiye Yang 		fprintf(stderr,
8007991eb19SZiye Yang 			"-b must be specified to value 0 or 1.\n");
8017991eb19SZiye Yang 		return 1;
8027991eb19SZiye Yang 	} else if (g_arbitration.arbitration_config == 1 &&
8037991eb19SZiye Yang 		   g_arbitration.arbitration_mechanism != SPDK_NVME_CC_AMS_WRR) {
8047991eb19SZiye Yang 		fprintf(stderr,
8057991eb19SZiye Yang 			"-a must be specified to 1 (WRR) together.\n");
8067991eb19SZiye Yang 		return 1;
8077991eb19SZiye Yang 	}
8087991eb19SZiye Yang 
8097991eb19SZiye Yang 	return 0;
8107991eb19SZiye Yang }
8117991eb19SZiye Yang 
8127991eb19SZiye Yang static int
8137991eb19SZiye Yang register_workers(void)
8147991eb19SZiye Yang {
8157f7c03a9SBen Walker 	uint32_t i;
8167991eb19SZiye Yang 	struct worker_thread *worker;
8177991eb19SZiye Yang 	enum spdk_nvme_qprio qprio = SPDK_NVME_QPRIO_URGENT;
8187991eb19SZiye Yang 
8197f7c03a9SBen Walker 	SPDK_ENV_FOREACH_CORE(i) {
82084230409SBen Walker 		worker = calloc(1, sizeof(*worker));
8217991eb19SZiye Yang 		if (worker == NULL) {
82284230409SBen Walker 			fprintf(stderr, "Unable to allocate worker\n");
82384230409SBen Walker 			return -1;
8247991eb19SZiye Yang 		}
8257991eb19SZiye Yang 
8264c3fd228SShuhei Matsumoto 		TAILQ_INIT(&worker->ns_ctx);
8277f7c03a9SBen Walker 		worker->lcore = i;
8284c3fd228SShuhei Matsumoto 		TAILQ_INSERT_TAIL(&g_workers, worker, link);
8297991eb19SZiye Yang 		g_arbitration.num_workers++;
8307991eb19SZiye Yang 
8317991eb19SZiye Yang 		if (g_arbitration.arbitration_mechanism == SPDK_NVME_CAP_AMS_WRR) {
8327991eb19SZiye Yang 			qprio++;
8337991eb19SZiye Yang 		}
83484230409SBen Walker 
8353f2660d7SChangpeng Liu 		worker->qprio = qprio & SPDK_NVME_CREATE_IO_SQ_QPRIO_MASK;
8367991eb19SZiye Yang 	}
8377991eb19SZiye Yang 
8387991eb19SZiye Yang 	return 0;
8397991eb19SZiye Yang }
8407991eb19SZiye Yang 
8417991eb19SZiye Yang static bool
84232e838afSBen Walker probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
843fcb00f37SDaniel Verkamp 	 struct spdk_nvme_ctrlr_opts *opts)
8447991eb19SZiye Yang {
8457991eb19SZiye Yang 	/* Update with user specified arbitration configuration */
8467991eb19SZiye Yang 	opts->arb_mechanism = g_arbitration.arbitration_mechanism;
8477991eb19SZiye Yang 
84832e838afSBen Walker 	printf("Attaching to %s\n", trid->traddr);
8497991eb19SZiye Yang 
8507991eb19SZiye Yang 	return true;
8517991eb19SZiye Yang }
8527991eb19SZiye Yang 
8537991eb19SZiye Yang static void
85432e838afSBen Walker attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
855fcb00f37SDaniel Verkamp 	  struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
8567991eb19SZiye Yang {
85732e838afSBen Walker 	printf("Attached to %s\n", trid->traddr);
8587991eb19SZiye Yang 
8597991eb19SZiye Yang 	/* Update with actual arbitration configuration in use */
8607991eb19SZiye Yang 	g_arbitration.arbitration_mechanism = opts->arb_mechanism;
8617991eb19SZiye Yang 
8627991eb19SZiye Yang 	register_ctrlr(ctrlr);
8637991eb19SZiye Yang }
8647991eb19SZiye Yang 
8657991eb19SZiye Yang static int
8667991eb19SZiye Yang register_controllers(void)
8677991eb19SZiye Yang {
8687991eb19SZiye Yang 	printf("Initializing NVMe Controllers\n");
8697991eb19SZiye Yang 
87074dcf4aaSMao Jiang 	if (spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL) != 0) {
8717991eb19SZiye Yang 		fprintf(stderr, "spdk_nvme_probe() failed\n");
8727991eb19SZiye Yang 		return 1;
8737991eb19SZiye Yang 	}
8747991eb19SZiye Yang 
8757991eb19SZiye Yang 	if (g_arbitration.num_namespaces == 0) {
8767991eb19SZiye Yang 		fprintf(stderr, "No valid namespaces to continue IO testing\n");
8777991eb19SZiye Yang 		return 1;
8787991eb19SZiye Yang 	}
8797991eb19SZiye Yang 
8807991eb19SZiye Yang 	return 0;
8817991eb19SZiye Yang }
8827991eb19SZiye Yang 
8837991eb19SZiye Yang static void
8847991eb19SZiye Yang unregister_controllers(void)
8857991eb19SZiye Yang {
8864c3fd228SShuhei Matsumoto 	struct ctrlr_entry *entry, *tmp;
8870a903c91SShuhei Matsumoto 	struct spdk_nvme_detach_ctx *detach_ctx = NULL;
8887991eb19SZiye Yang 
8894c3fd228SShuhei Matsumoto 	TAILQ_FOREACH_SAFE(entry, &g_controllers, link, tmp) {
8904c3fd228SShuhei Matsumoto 		TAILQ_REMOVE(&g_controllers, entry, link);
8917991eb19SZiye Yang 		if (g_arbitration.latency_tracking_enable &&
89259970a89SDaniel Verkamp 		    spdk_nvme_ctrlr_is_feature_supported(entry->ctrlr, SPDK_NVME_INTEL_FEAT_LATENCY_TRACKING)) {
8937991eb19SZiye Yang 			set_latency_tracking_feature(entry->ctrlr, false);
89459970a89SDaniel Verkamp 		}
8950a903c91SShuhei Matsumoto 		spdk_nvme_detach_async(entry->ctrlr, &detach_ctx);
8967991eb19SZiye Yang 		free(entry);
8977991eb19SZiye Yang 	}
8980a903c91SShuhei Matsumoto 
8990a903c91SShuhei Matsumoto 	while (detach_ctx && spdk_nvme_detach_poll_async(detach_ctx) == -EAGAIN) {
9000a903c91SShuhei Matsumoto 		;
9010a903c91SShuhei Matsumoto 	}
9027991eb19SZiye Yang }
9037991eb19SZiye Yang 
9047991eb19SZiye Yang static int
9057991eb19SZiye Yang associate_workers_with_ns(void)
9067991eb19SZiye Yang {
9074c3fd228SShuhei Matsumoto 	struct ns_entry		*entry = TAILQ_FIRST(&g_namespaces);
9084c3fd228SShuhei Matsumoto 	struct worker_thread	*worker = TAILQ_FIRST(&g_workers);
9097991eb19SZiye Yang 	struct ns_worker_ctx	*ns_ctx;
9107991eb19SZiye Yang 	int			i, count;
9117991eb19SZiye Yang 
9127991eb19SZiye Yang 	count = g_arbitration.num_namespaces > g_arbitration.num_workers ?
9137991eb19SZiye Yang 		g_arbitration.num_namespaces : g_arbitration.num_workers;
9147991eb19SZiye Yang 
9157991eb19SZiye Yang 	for (i = 0; i < count; i++) {
9167991eb19SZiye Yang 		if (entry == NULL) {
9177991eb19SZiye Yang 			break;
9187991eb19SZiye Yang 		}
9197991eb19SZiye Yang 
9207991eb19SZiye Yang 		ns_ctx = malloc(sizeof(struct ns_worker_ctx));
9217991eb19SZiye Yang 		if (!ns_ctx) {
9227991eb19SZiye Yang 			return 1;
9237991eb19SZiye Yang 		}
9247991eb19SZiye Yang 		memset(ns_ctx, 0, sizeof(*ns_ctx));
9257991eb19SZiye Yang 
9267991eb19SZiye Yang 		printf("Associating %s with lcore %d\n", entry->name, worker->lcore);
9277991eb19SZiye Yang 		ns_ctx->entry = entry;
9284c3fd228SShuhei Matsumoto 		TAILQ_INSERT_TAIL(&worker->ns_ctx, ns_ctx, link);
9297991eb19SZiye Yang 
9304c3fd228SShuhei Matsumoto 		worker = TAILQ_NEXT(worker, link);
9317991eb19SZiye Yang 		if (worker == NULL) {
9324c3fd228SShuhei Matsumoto 			worker = TAILQ_FIRST(&g_workers);
9337991eb19SZiye Yang 		}
9347991eb19SZiye Yang 
9354c3fd228SShuhei Matsumoto 		entry = TAILQ_NEXT(entry, link);
9367991eb19SZiye Yang 		if (entry == NULL) {
9374c3fd228SShuhei Matsumoto 			entry = TAILQ_FIRST(&g_namespaces);
9387991eb19SZiye Yang 		}
9397991eb19SZiye Yang 
9407991eb19SZiye Yang 	}
9417991eb19SZiye Yang 
9427991eb19SZiye Yang 	return 0;
9437991eb19SZiye Yang }
9447991eb19SZiye Yang 
9457991eb19SZiye Yang static void
9467991eb19SZiye Yang get_feature_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
9477991eb19SZiye Yang {
9487991eb19SZiye Yang 	struct feature *feature = cb_arg;
9497991eb19SZiye Yang 	int fid = feature - features;
9507991eb19SZiye Yang 
9517991eb19SZiye Yang 	if (spdk_nvme_cpl_is_error(cpl)) {
9527991eb19SZiye Yang 		printf("get_feature(0x%02X) failed\n", fid);
9537991eb19SZiye Yang 	} else {
9547991eb19SZiye Yang 		feature->result = cpl->cdw0;
9557991eb19SZiye Yang 		feature->valid = true;
9567991eb19SZiye Yang 	}
9577991eb19SZiye Yang 
9587991eb19SZiye Yang 	g_arbitration.outstanding_commands--;
9597991eb19SZiye Yang }
9607991eb19SZiye Yang 
9617991eb19SZiye Yang static int
9627991eb19SZiye Yang get_feature(struct spdk_nvme_ctrlr *ctrlr, uint8_t fid)
9637991eb19SZiye Yang {
9647991eb19SZiye Yang 	struct spdk_nvme_cmd cmd = {};
965b97aa6daSChangpeng Liu 	struct feature *feature = &features[fid];
966b97aa6daSChangpeng Liu 
967b97aa6daSChangpeng Liu 	feature->valid = false;
9687991eb19SZiye Yang 
9697991eb19SZiye Yang 	cmd.opc = SPDK_NVME_OPC_GET_FEATURES;
9701fea1fccSChangpeng Liu 	cmd.cdw10_bits.get_features.fid = fid;
9717991eb19SZiye Yang 
972b97aa6daSChangpeng Liu 	return spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, NULL, 0, get_feature_completion, feature);
9737991eb19SZiye Yang }
9747991eb19SZiye Yang 
9757991eb19SZiye Yang static void
9767991eb19SZiye Yang get_arb_feature(struct spdk_nvme_ctrlr *ctrlr)
9777991eb19SZiye Yang {
9787991eb19SZiye Yang 	get_feature(ctrlr, SPDK_NVME_FEAT_ARBITRATION);
9797991eb19SZiye Yang 
9807991eb19SZiye Yang 	g_arbitration.outstanding_commands++;
9817991eb19SZiye Yang 
9827991eb19SZiye Yang 	while (g_arbitration.outstanding_commands) {
9837991eb19SZiye Yang 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
9847991eb19SZiye Yang 	}
9857991eb19SZiye Yang 
9867991eb19SZiye Yang 	if (features[SPDK_NVME_FEAT_ARBITRATION].valid) {
987d7b9a14cSChangpeng Liu 		union spdk_nvme_cmd_cdw11 arb;
988d7b9a14cSChangpeng Liu 		arb.feat_arbitration.raw = features[SPDK_NVME_FEAT_ARBITRATION].result;
9897991eb19SZiye Yang 
9907991eb19SZiye Yang 		printf("Current Arbitration Configuration\n");
9917991eb19SZiye Yang 		printf("===========\n");
9927991eb19SZiye Yang 		printf("Arbitration Burst:           ");
9933f2660d7SChangpeng Liu 		if (arb.feat_arbitration.bits.ab == SPDK_NVME_ARBITRATION_BURST_UNLIMITED) {
9947991eb19SZiye Yang 			printf("no limit\n");
9957991eb19SZiye Yang 		} else {
996d7b9a14cSChangpeng Liu 			printf("%u\n", 1u << arb.feat_arbitration.bits.ab);
9977991eb19SZiye Yang 		}
9987991eb19SZiye Yang 
999d7b9a14cSChangpeng Liu 		printf("Low Priority Weight:         %u\n", arb.feat_arbitration.bits.lpw + 1);
1000d7b9a14cSChangpeng Liu 		printf("Medium Priority Weight:      %u\n", arb.feat_arbitration.bits.mpw + 1);
1001d7b9a14cSChangpeng Liu 		printf("High Priority Weight:        %u\n", arb.feat_arbitration.bits.hpw + 1);
10027991eb19SZiye Yang 		printf("\n");
10037991eb19SZiye Yang 	}
10047991eb19SZiye Yang }
10057991eb19SZiye Yang 
10067991eb19SZiye Yang static void
10077991eb19SZiye Yang set_feature_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
10087991eb19SZiye Yang {
10097991eb19SZiye Yang 	struct feature *feature = cb_arg;
10107991eb19SZiye Yang 	int fid = feature - features;
10117991eb19SZiye Yang 
10127991eb19SZiye Yang 	if (spdk_nvme_cpl_is_error(cpl)) {
10137991eb19SZiye Yang 		printf("set_feature(0x%02X) failed\n", fid);
10147991eb19SZiye Yang 		feature->valid = false;
10157991eb19SZiye Yang 	} else {
10167991eb19SZiye Yang 		printf("Set Arbitration Feature Successfully\n");
10177991eb19SZiye Yang 	}
10187991eb19SZiye Yang 
10197991eb19SZiye Yang 	g_arbitration.outstanding_commands--;
10207991eb19SZiye Yang }
10217991eb19SZiye Yang 
10227991eb19SZiye Yang static int
10237991eb19SZiye Yang set_arb_feature(struct spdk_nvme_ctrlr *ctrlr)
10247991eb19SZiye Yang {
10257991eb19SZiye Yang 	int ret;
10267991eb19SZiye Yang 	struct spdk_nvme_cmd cmd = {};
10277991eb19SZiye Yang 
10287991eb19SZiye Yang 	cmd.opc = SPDK_NVME_OPC_SET_FEATURES;
10291fea1fccSChangpeng Liu 	cmd.cdw10_bits.set_features.fid = SPDK_NVME_FEAT_ARBITRATION;
10307991eb19SZiye Yang 
10317991eb19SZiye Yang 	g_arbitration.outstanding_commands = 0;
10327991eb19SZiye Yang 
10337991eb19SZiye Yang 	if (features[SPDK_NVME_FEAT_ARBITRATION].valid) {
10343f2660d7SChangpeng Liu 		cmd.cdw11_bits.feat_arbitration.bits.ab = SPDK_NVME_ARBITRATION_BURST_UNLIMITED;
10350c9057f0SChangpeng Liu 		cmd.cdw11_bits.feat_arbitration.bits.lpw = USER_SPECIFIED_LOW_PRIORITY_WEIGHT;
10360c9057f0SChangpeng Liu 		cmd.cdw11_bits.feat_arbitration.bits.mpw = USER_SPECIFIED_MEDIUM_PRIORITY_WEIGHT;
10370c9057f0SChangpeng Liu 		cmd.cdw11_bits.feat_arbitration.bits.hpw = USER_SPECIFIED_HIGH_PRIORITY_WEIGHT;
10387991eb19SZiye Yang 	}
10397991eb19SZiye Yang 
10407991eb19SZiye Yang 	ret = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, NULL, 0,
10417991eb19SZiye Yang 					    set_feature_completion, &features[SPDK_NVME_FEAT_ARBITRATION]);
10427991eb19SZiye Yang 	if (ret) {
10437991eb19SZiye Yang 		printf("Set Arbitration Feature: Failed 0x%x\n", ret);
10447991eb19SZiye Yang 		return 1;
10457991eb19SZiye Yang 	}
10467991eb19SZiye Yang 
10477991eb19SZiye Yang 	g_arbitration.outstanding_commands++;
10487991eb19SZiye Yang 
10497991eb19SZiye Yang 	while (g_arbitration.outstanding_commands) {
10507991eb19SZiye Yang 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
10517991eb19SZiye Yang 	}
10527991eb19SZiye Yang 
10537991eb19SZiye Yang 	if (!features[SPDK_NVME_FEAT_ARBITRATION].valid) {
10547991eb19SZiye Yang 		printf("Set Arbitration Feature failed and use default configuration\n");
10557991eb19SZiye Yang 	}
10567991eb19SZiye Yang 
10577991eb19SZiye Yang 	return 0;
10587991eb19SZiye Yang }
10597991eb19SZiye Yang 
10607991eb19SZiye Yang int
10617991eb19SZiye Yang main(int argc, char **argv)
10627991eb19SZiye Yang {
10637991eb19SZiye Yang 	int rc;
106487b21afdSJim Harris 	struct worker_thread *worker, *main_worker;
106587b21afdSJim Harris 	unsigned main_core;
1066987ba616SGangCao 	char task_pool_name[30];
10679ec9c8b3SChangpeng Liu 	uint32_t task_count = 0;
106818d26e42SBen Walker 	struct spdk_env_opts opts;
10697991eb19SZiye Yang 
10707991eb19SZiye Yang 	rc = parse_args(argc, argv);
10717991eb19SZiye Yang 	if (rc != 0) {
10727991eb19SZiye Yang 		return rc;
10737991eb19SZiye Yang 	}
10747991eb19SZiye Yang 
107557fd99b9SJim Harris 	opts.opts_size = sizeof(opts);
107618d26e42SBen Walker 	spdk_env_opts_init(&opts);
107718d26e42SBen Walker 	opts.name = "arb";
10784518e4c3SChangpeng Liu 	opts.mem_size = g_dpdk_mem;
10794518e4c3SChangpeng Liu 	opts.hugepage_single_segments = g_dpdk_mem_single_seg;
108018d26e42SBen Walker 	opts.core_mask = g_arbitration.core_mask;
108118d26e42SBen Walker 	opts.shm_id = g_arbitration.shm_id;
1082095f4254SLance Hartmann 	if (spdk_env_init(&opts) < 0) {
1083095f4254SLance Hartmann 		return 1;
1084095f4254SLance Hartmann 	}
10857991eb19SZiye Yang 
10860aa29864SBen Walker 	g_arbitration.tsc_rate = spdk_get_ticks_hz();
10877991eb19SZiye Yang 
10887991eb19SZiye Yang 	if (register_workers() != 0) {
10899ec9c8b3SChangpeng Liu 		rc = 1;
10909ec9c8b3SChangpeng Liu 		goto exit;
10917991eb19SZiye Yang 	}
10927991eb19SZiye Yang 
10937991eb19SZiye Yang 	if (register_controllers() != 0) {
10949ec9c8b3SChangpeng Liu 		rc = 1;
10959ec9c8b3SChangpeng Liu 		goto exit;
10967991eb19SZiye Yang 	}
10977991eb19SZiye Yang 
10987991eb19SZiye Yang 	if (associate_workers_with_ns() != 0) {
10999ec9c8b3SChangpeng Liu 		rc = 1;
11009ec9c8b3SChangpeng Liu 		goto exit;
11017991eb19SZiye Yang 	}
11027991eb19SZiye Yang 
110311f43621SGangCao 	snprintf(task_pool_name, sizeof(task_pool_name), "task_pool_%d", getpid());
110411f43621SGangCao 
110511f43621SGangCao 	/*
110611f43621SGangCao 	 * The task_count will be dynamically calculated based on the
110711f43621SGangCao 	 * number of attached active namespaces, queue depth and number
110811f43621SGangCao 	 * of cores (workers) involved in the IO perations.
110911f43621SGangCao 	 */
111011f43621SGangCao 	task_count = g_arbitration.num_namespaces > g_arbitration.num_workers ?
111111f43621SGangCao 		     g_arbitration.num_namespaces : g_arbitration.num_workers;
111211f43621SGangCao 	task_count *= g_arbitration.queue_depth;
111311f43621SGangCao 
111463c1c9d5SZiye Yang 	task_pool = spdk_mempool_create(task_pool_name, task_count,
1115*186b109dSJim Harris 					sizeof(struct arb_task), 0, SPDK_ENV_NUMA_ID_ANY);
111611f43621SGangCao 	if (task_pool == NULL) {
111711f43621SGangCao 		fprintf(stderr, "could not initialize task pool\n");
11189ec9c8b3SChangpeng Liu 		rc = 1;
11199ec9c8b3SChangpeng Liu 		goto exit;
112011f43621SGangCao 	}
112111f43621SGangCao 
11227991eb19SZiye Yang 	print_configuration(argv[0]);
11237991eb19SZiye Yang 
11247991eb19SZiye Yang 	printf("Initialization complete. Launching workers.\n");
11257991eb19SZiye Yang 
112687b21afdSJim Harris 	/* Launch all of the secondary workers */
112787b21afdSJim Harris 	main_core = spdk_env_get_current_core();
112887b21afdSJim Harris 	main_worker = NULL;
11294c3fd228SShuhei Matsumoto 	TAILQ_FOREACH(worker, &g_workers, link) {
113087b21afdSJim Harris 		if (worker->lcore != main_core) {
113163c1c9d5SZiye Yang 			spdk_env_thread_launch_pinned(worker->lcore, work_fn, worker);
113284230409SBen Walker 		} else {
113387b21afdSJim Harris 			assert(main_worker == NULL);
113487b21afdSJim Harris 			main_worker = worker;
11357991eb19SZiye Yang 		}
11367991eb19SZiye Yang 	}
11377991eb19SZiye Yang 
113887b21afdSJim Harris 	assert(main_worker != NULL);
113987b21afdSJim Harris 	rc = work_fn(main_worker);
114084230409SBen Walker 
114163c1c9d5SZiye Yang 	spdk_env_thread_wait_all();
114284230409SBen Walker 
11437991eb19SZiye Yang 	print_stats();
11447991eb19SZiye Yang 
11459ec9c8b3SChangpeng Liu exit:
11467991eb19SZiye Yang 	unregister_controllers();
114763c1c9d5SZiye Yang 	cleanup(task_count);
11487991eb19SZiye Yang 
11499ec9c8b3SChangpeng Liu 	spdk_env_fini();
11509ec9c8b3SChangpeng Liu 
11517991eb19SZiye Yang 	if (rc != 0) {
1152b7876f9aSJosh Soref 		fprintf(stderr, "%s: errors occurred\n", argv[0]);
11537991eb19SZiye Yang 	}
11547991eb19SZiye Yang 
11557991eb19SZiye Yang 	return rc;
11567991eb19SZiye Yang }
1157