xref: /spdk/examples/accel/perf/accel_perf.c (revision 9b18966796030d6bfe57e4d57fb7fe1285cf6d7f)
19f51cf32Spaul luse /*-
29f51cf32Spaul luse  *   BSD LICENSE
39f51cf32Spaul luse  *
49f51cf32Spaul luse  *   Copyright (c) Intel Corporation.
59f51cf32Spaul luse  *   All rights reserved.
69f51cf32Spaul luse  *
79f51cf32Spaul luse  *   Redistribution and use in source and binary forms, with or without
89f51cf32Spaul luse  *   modification, are permitted provided that the following conditions
99f51cf32Spaul luse  *   are met:
109f51cf32Spaul luse  *
119f51cf32Spaul luse  *     * Redistributions of source code must retain the above copyright
129f51cf32Spaul luse  *       notice, this list of conditions and the following disclaimer.
139f51cf32Spaul luse  *     * Redistributions in binary form must reproduce the above copyright
149f51cf32Spaul luse  *       notice, this list of conditions and the following disclaimer in
159f51cf32Spaul luse  *       the documentation and/or other materials provided with the
169f51cf32Spaul luse  *       distribution.
179f51cf32Spaul luse  *     * Neither the name of Intel Corporation nor the names of its
189f51cf32Spaul luse  *       contributors may be used to endorse or promote products derived
199f51cf32Spaul luse  *       from this software without specific prior written permission.
209f51cf32Spaul luse  *
219f51cf32Spaul luse  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
229f51cf32Spaul luse  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
239f51cf32Spaul luse  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
249f51cf32Spaul luse  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
259f51cf32Spaul luse  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
269f51cf32Spaul luse  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
279f51cf32Spaul luse  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
289f51cf32Spaul luse  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
299f51cf32Spaul luse  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
309f51cf32Spaul luse  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
319f51cf32Spaul luse  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
329f51cf32Spaul luse  */
339f51cf32Spaul luse 
349f51cf32Spaul luse #include "spdk/stdinc.h"
359f51cf32Spaul luse #include "spdk/thread.h"
369f51cf32Spaul luse #include "spdk/env.h"
379f51cf32Spaul luse #include "spdk/event.h"
389f51cf32Spaul luse #include "spdk/log.h"
399f51cf32Spaul luse #include "spdk/string.h"
409f51cf32Spaul luse #include "spdk/accel_engine.h"
41e69375bfSpaul luse #include "spdk/crc32.h"
420cecfcb1Spaul luse #include "spdk/util.h"
439f51cf32Spaul luse 
44b9218b7aSpaul luse #define DATA_PATTERN 0x5a
450ef079c6Spaul luse #define ALIGN_4K 0x1000
46b9218b7aSpaul luse 
479f51cf32Spaul luse static uint64_t	g_tsc_rate;
489f51cf32Spaul luse static uint64_t g_tsc_us_rate;
499f51cf32Spaul luse static uint64_t g_tsc_end;
50*9b189667Spaul luse static int g_rc;
519f51cf32Spaul luse static int g_xfer_size_bytes = 4096;
529f51cf32Spaul luse static int g_queue_depth = 32;
53f17e6705Spaul luse static int g_ops_per_batch = 0;
54445fe74eSpaul luse static int g_threads_per_core = 1;
559f51cf32Spaul luse static int g_time_in_sec = 5;
56e69375bfSpaul luse static uint32_t g_crc32c_seed = 0;
57b9218b7aSpaul luse static int g_fail_percent_goal = 0;
5889495464Spaul luse static uint8_t g_fill_pattern = 255;
599f51cf32Spaul luse static bool g_verify = false;
602a0c66d0Spaul luse static const char *g_workload_type = NULL;
61514be889Spaul luse static enum accel_capability g_workload_selection;
629f51cf32Spaul luse static struct worker_thread *g_workers = NULL;
639f51cf32Spaul luse static int g_num_workers = 0;
649f51cf32Spaul luse static pthread_mutex_t g_workers_lock = PTHREAD_MUTEX_INITIALIZER;
65a34fc12bSpaul luse uint64_t g_capabilites;
66cdefd3d3Spaul luse 
67cdefd3d3Spaul luse struct worker_thread;
68cdefd3d3Spaul luse static void accel_done(void *ref, int status);
69cdefd3d3Spaul luse 
70445fe74eSpaul luse struct display_info {
71445fe74eSpaul luse 	int core;
72445fe74eSpaul luse 	int thread;
73445fe74eSpaul luse };
74445fe74eSpaul luse 
75cdefd3d3Spaul luse struct ap_task {
76cdefd3d3Spaul luse 	void			*src;
77cdefd3d3Spaul luse 	void			*dst;
78cdefd3d3Spaul luse 	void			*dst2;
79cdefd3d3Spaul luse 	struct worker_thread	*worker;
80cdefd3d3Spaul luse 	int			status;
81cdefd3d3Spaul luse 	int			expected_status; /* used for the compare operation */
82cdefd3d3Spaul luse 	TAILQ_ENTRY(ap_task)	link;
83cdefd3d3Spaul luse };
849f51cf32Spaul luse 
85f17e6705Spaul luse struct accel_batch {
86f17e6705Spaul luse 	int				status;
87f17e6705Spaul luse 	int				cmd_count;
88f17e6705Spaul luse 	struct spdk_accel_batch		*batch;
89f17e6705Spaul luse 	struct worker_thread		*worker;
90f17e6705Spaul luse 	TAILQ_ENTRY(accel_batch)	link;
91f17e6705Spaul luse };
92f17e6705Spaul luse 
939f51cf32Spaul luse struct worker_thread {
949f51cf32Spaul luse 	struct spdk_io_channel		*ch;
959f51cf32Spaul luse 	uint64_t			xfer_completed;
969f51cf32Spaul luse 	uint64_t			xfer_failed;
97b9218b7aSpaul luse 	uint64_t			injected_miscompares;
989f51cf32Spaul luse 	uint64_t			current_queue_depth;
99ac9a1a83Spaul luse 	TAILQ_HEAD(, ap_task)		tasks_pool;
1009f51cf32Spaul luse 	struct worker_thread		*next;
1019f51cf32Spaul luse 	unsigned			core;
1029f51cf32Spaul luse 	struct spdk_thread		*thread;
1039f51cf32Spaul luse 	bool				is_draining;
1049f51cf32Spaul luse 	struct spdk_poller		*is_draining_poller;
1059f51cf32Spaul luse 	struct spdk_poller		*stop_poller;
106ac9a1a83Spaul luse 	void				*task_base;
107f17e6705Spaul luse 	struct accel_batch		*batch_base;
108445fe74eSpaul luse 	struct display_info		display;
109f17e6705Spaul luse 	TAILQ_HEAD(, accel_batch)	in_prep_batches;
110f17e6705Spaul luse 	TAILQ_HEAD(, accel_batch)	in_use_batches;
111f17e6705Spaul luse 	TAILQ_HEAD(, accel_batch)	to_submit_batches;
1129f51cf32Spaul luse };
1139f51cf32Spaul luse 
1149f51cf32Spaul luse static void
1159f51cf32Spaul luse dump_user_config(struct spdk_app_opts *opts)
1169f51cf32Spaul luse {
1179f51cf32Spaul luse 	printf("SPDK Configuration:\n");
1189f51cf32Spaul luse 	printf("Core mask:      %s\n\n", opts->reactor_mask);
1199f51cf32Spaul luse 	printf("Accel Perf Configuration:\n");
1202a0c66d0Spaul luse 	printf("Workload Type:  %s\n", g_workload_type);
121b9218b7aSpaul luse 	if (g_workload_selection == ACCEL_CRC32C) {
122b9218b7aSpaul luse 		printf("CRC-32C seed:   %u\n", g_crc32c_seed);
12389495464Spaul luse 	} else if (g_workload_selection == ACCEL_FILL) {
12489495464Spaul luse 		printf("Fill pattern:   0x%x\n", g_fill_pattern);
125b9218b7aSpaul luse 	} else if ((g_workload_selection == ACCEL_COMPARE) && g_fail_percent_goal > 0) {
12689495464Spaul luse 		printf("Failure inject: %u percent\n", g_fail_percent_goal);
127e69375bfSpaul luse 	}
1289f51cf32Spaul luse 	printf("Transfer size:  %u bytes\n", g_xfer_size_bytes);
1299f51cf32Spaul luse 	printf("Queue depth:    %u\n", g_queue_depth);
130445fe74eSpaul luse 	printf("# threads/core: %u\n", g_threads_per_core);
1319f51cf32Spaul luse 	printf("Run time:       %u seconds\n", g_time_in_sec);
132f17e6705Spaul luse 	if (g_ops_per_batch > 0) {
133f17e6705Spaul luse 		printf("Batching:       %u operations\n", g_ops_per_batch);
134f17e6705Spaul luse 	} else {
135f17e6705Spaul luse 		printf("Batching:       Disabled\n");
136f17e6705Spaul luse 	}
1379f51cf32Spaul luse 	printf("Verify:         %s\n\n", g_verify ? "Yes" : "No");
1389f51cf32Spaul luse }
1399f51cf32Spaul luse 
1409f51cf32Spaul luse static void
1419f51cf32Spaul luse usage(void)
1429f51cf32Spaul luse {
1439f51cf32Spaul luse 	printf("accel_perf options:\n");
1449f51cf32Spaul luse 	printf("\t[-h help message]\n");
145f17e6705Spaul luse 	printf("\t[-q queue depth per core]\n");
146445fe74eSpaul luse 	printf("\t[-T number of threads per core\n");
1479f51cf32Spaul luse 	printf("\t[-o transfer size in bytes]\n");
1489f51cf32Spaul luse 	printf("\t[-t time in seconds]\n");
1490ef079c6Spaul luse 	printf("\t[-w workload type must be one of these: copy, fill, crc32c, compare, dualcast\n");
150e69375bfSpaul luse 	printf("\t[-s for crc32c workload, use this seed value (default 0)\n");
151b9218b7aSpaul luse 	printf("\t[-P for compare workload, percentage of operations that should miscompare (percent, default 0)\n");
15289495464Spaul luse 	printf("\t[-f for fill workload, use this BYTE value (default 255)\n");
1532a0c66d0Spaul luse 	printf("\t[-y verify result if this switch is on]\n");
154f17e6705Spaul luse 	printf("\t[-b batch this number of operations at a time (default 0 = disabled)]\n");
1559f51cf32Spaul luse }
1569f51cf32Spaul luse 
1579f51cf32Spaul luse static int
1589f51cf32Spaul luse parse_args(int argc, char *argv)
1599f51cf32Spaul luse {
1609f51cf32Spaul luse 	switch (argc) {
161f17e6705Spaul luse 	case 'b':
162f17e6705Spaul luse 		g_ops_per_batch = spdk_strtol(optarg, 10);
163f17e6705Spaul luse 		break;
16489495464Spaul luse 	case 'f':
16589495464Spaul luse 		g_fill_pattern = (uint8_t)spdk_strtol(optarg, 10);
16689495464Spaul luse 		break;
167445fe74eSpaul luse 	case 'T':
168445fe74eSpaul luse 		g_threads_per_core = spdk_strtol(optarg, 10);
169445fe74eSpaul luse 		break;
1709f51cf32Spaul luse 	case 'o':
1719f51cf32Spaul luse 		g_xfer_size_bytes = spdk_strtol(optarg, 10);
1729f51cf32Spaul luse 		break;
173b9218b7aSpaul luse 	case 'P':
174b9218b7aSpaul luse 		g_fail_percent_goal = spdk_strtol(optarg, 10);
175b9218b7aSpaul luse 		break;
1769f51cf32Spaul luse 	case 'q':
1779f51cf32Spaul luse 		g_queue_depth = spdk_strtol(optarg, 10);
1789f51cf32Spaul luse 		break;
179e69375bfSpaul luse 	case 's':
180e69375bfSpaul luse 		g_crc32c_seed = spdk_strtol(optarg, 10);
181e69375bfSpaul luse 		break;
1829f51cf32Spaul luse 	case 't':
1839f51cf32Spaul luse 		g_time_in_sec = spdk_strtol(optarg, 10);
1849f51cf32Spaul luse 		break;
1859f51cf32Spaul luse 	case 'y':
1869f51cf32Spaul luse 		g_verify = true;
1879f51cf32Spaul luse 		break;
1882a0c66d0Spaul luse 	case 'w':
1892a0c66d0Spaul luse 		g_workload_type = optarg;
190514be889Spaul luse 		if (!strcmp(g_workload_type, "copy")) {
191514be889Spaul luse 			g_workload_selection = ACCEL_COPY;
192514be889Spaul luse 		} else if (!strcmp(g_workload_type, "fill")) {
193514be889Spaul luse 			g_workload_selection = ACCEL_FILL;
194e69375bfSpaul luse 		} else if (!strcmp(g_workload_type, "crc32c")) {
195e69375bfSpaul luse 			g_workload_selection = ACCEL_CRC32C;
196b9218b7aSpaul luse 		} else if (!strcmp(g_workload_type, "compare")) {
197b9218b7aSpaul luse 			g_workload_selection = ACCEL_COMPARE;
1980ef079c6Spaul luse 		} else if (!strcmp(g_workload_type, "dualcast")) {
1990ef079c6Spaul luse 			g_workload_selection = ACCEL_DUALCAST;
200514be889Spaul luse 		}
2012a0c66d0Spaul luse 		break;
2029f51cf32Spaul luse 	default:
2039f51cf32Spaul luse 		usage();
2049f51cf32Spaul luse 		return 1;
2059f51cf32Spaul luse 	}
2069f51cf32Spaul luse 	return 0;
2079f51cf32Spaul luse }
2089f51cf32Spaul luse 
209eea826a2Spaul luse static int dump_result(void);
2109f51cf32Spaul luse static void
2119f51cf32Spaul luse unregister_worker(void *arg1)
2129f51cf32Spaul luse {
2139f51cf32Spaul luse 	struct worker_thread *worker = arg1;
2149f51cf32Spaul luse 
215ac9a1a83Spaul luse 	free(worker->task_base);
216f17e6705Spaul luse 	free(worker->batch_base);
2179f51cf32Spaul luse 	spdk_put_io_channel(worker->ch);
2189f51cf32Spaul luse 	pthread_mutex_lock(&g_workers_lock);
2199f51cf32Spaul luse 	assert(g_num_workers >= 1);
2209f51cf32Spaul luse 	if (--g_num_workers == 0) {
2219f51cf32Spaul luse 		pthread_mutex_unlock(&g_workers_lock);
222*9b189667Spaul luse 		g_rc = dump_result();
2239f51cf32Spaul luse 		spdk_app_stop(0);
2249f51cf32Spaul luse 	}
2259f51cf32Spaul luse 	pthread_mutex_unlock(&g_workers_lock);
2269f51cf32Spaul luse }
2279f51cf32Spaul luse 
2288da995c4Spaul luse static int
2298da995c4Spaul luse _get_task_data_bufs(struct ap_task *task)
2308da995c4Spaul luse {
2318da995c4Spaul luse 	uint32_t align = 0;
2328da995c4Spaul luse 
2338da995c4Spaul luse 	/* For dualcast, the DSA HW requires 4K alignment on destination addresses but
2348da995c4Spaul luse 	 * we do this for all engines to keep it simple.
2358da995c4Spaul luse 	 */
2368da995c4Spaul luse 	if (g_workload_selection == ACCEL_DUALCAST) {
2378da995c4Spaul luse 		align = ALIGN_4K;
2388da995c4Spaul luse 	}
2398da995c4Spaul luse 
2408da995c4Spaul luse 	task->src = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL);
2418da995c4Spaul luse 	if (task->src == NULL) {
2428da995c4Spaul luse 		fprintf(stderr, "Unable to alloc src buffer\n");
2438da995c4Spaul luse 		return -ENOMEM;
2448da995c4Spaul luse 	}
2458da995c4Spaul luse 	memset(task->src, DATA_PATTERN, g_xfer_size_bytes);
2468da995c4Spaul luse 
2478da995c4Spaul luse 	task->dst = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL);
2488da995c4Spaul luse 	if (task->dst == NULL) {
2498da995c4Spaul luse 		fprintf(stderr, "Unable to alloc dst buffer\n");
2508da995c4Spaul luse 		return -ENOMEM;
2518da995c4Spaul luse 	}
2528da995c4Spaul luse 
2538da995c4Spaul luse 	/* For compare we want the buffers to match, otherwise not. */
2548da995c4Spaul luse 	if (g_workload_selection == ACCEL_COMPARE) {
2558da995c4Spaul luse 		memset(task->dst, DATA_PATTERN, g_xfer_size_bytes);
2568da995c4Spaul luse 	} else {
2578da995c4Spaul luse 		memset(task->dst, ~DATA_PATTERN, g_xfer_size_bytes);
2588da995c4Spaul luse 	}
2598da995c4Spaul luse 
2608da995c4Spaul luse 	/* For fill, set the entire src buffer so we can check if verify is enabled. */
2618da995c4Spaul luse 	if (g_workload_selection == ACCEL_FILL) {
2628da995c4Spaul luse 		memset(task->src, g_fill_pattern, g_xfer_size_bytes);
2638da995c4Spaul luse 	}
2648da995c4Spaul luse 
2658da995c4Spaul luse 	if (g_workload_selection == ACCEL_DUALCAST) {
2668da995c4Spaul luse 		task->dst2 = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL);
2678da995c4Spaul luse 		if (task->dst2 == NULL) {
2688da995c4Spaul luse 			fprintf(stderr, "Unable to alloc dst buffer\n");
2698da995c4Spaul luse 			return -ENOMEM;
2708da995c4Spaul luse 		}
2718da995c4Spaul luse 		memset(task->dst2, ~DATA_PATTERN, g_xfer_size_bytes);
2728da995c4Spaul luse 	}
2738da995c4Spaul luse 
2748da995c4Spaul luse 	return 0;
2758da995c4Spaul luse }
2768da995c4Spaul luse 
277ac9a1a83Spaul luse inline static struct ap_task *
278ac9a1a83Spaul luse _get_task(struct worker_thread *worker)
279ac9a1a83Spaul luse {
280ac9a1a83Spaul luse 	struct ap_task *task;
281ac9a1a83Spaul luse 
282ac9a1a83Spaul luse 	if (!TAILQ_EMPTY(&worker->tasks_pool)) {
283ac9a1a83Spaul luse 		task = TAILQ_FIRST(&worker->tasks_pool);
284ac9a1a83Spaul luse 		TAILQ_REMOVE(&worker->tasks_pool, task, link);
285ac9a1a83Spaul luse 	} else {
286ac9a1a83Spaul luse 		fprintf(stderr, "Unable to get ap_task\n");
287ac9a1a83Spaul luse 		return NULL;
288ac9a1a83Spaul luse 	}
289ac9a1a83Spaul luse 
290ac9a1a83Spaul luse 	task->worker = worker;
291ac9a1a83Spaul luse 	task->worker->current_queue_depth++;
292ac9a1a83Spaul luse 	return task;
293ac9a1a83Spaul luse }
294ac9a1a83Spaul luse 
295f17e6705Spaul luse /* Submit one operation using the same ap task that just completed. */
2969f51cf32Spaul luse static void
297ac9a1a83Spaul luse _submit_single(struct worker_thread *worker, struct ap_task *task)
2989f51cf32Spaul luse {
299b9218b7aSpaul luse 	int random_num;
30040ec8e97Spaul luse 	int rc = 0;
3019f51cf32Spaul luse 
3029f51cf32Spaul luse 	assert(worker);
3039f51cf32Spaul luse 
304e69375bfSpaul luse 	switch (g_workload_selection) {
305e69375bfSpaul luse 	case ACCEL_COPY:
306e8463f87Spaul luse 		rc = spdk_accel_submit_copy(worker->ch, task->dst, task->src,
307e8463f87Spaul luse 					    g_xfer_size_bytes, accel_done, task);
308e69375bfSpaul luse 		break;
309e69375bfSpaul luse 	case ACCEL_FILL:
3102a0c66d0Spaul luse 		/* For fill use the first byte of the task->dst buffer */
311ee7e31f9Spaul luse 		rc = spdk_accel_submit_fill(worker->ch, task->dst, *(uint8_t *)task->src,
312e8463f87Spaul luse 					    g_xfer_size_bytes, accel_done, task);
313e69375bfSpaul luse 		break;
314e69375bfSpaul luse 	case ACCEL_CRC32C:
315e8463f87Spaul luse 		rc = spdk_accel_submit_crc32c(worker->ch, (uint32_t *)task->dst,
316e8463f87Spaul luse 					      task->src, g_crc32c_seed,
317e8463f87Spaul luse 					      g_xfer_size_bytes, accel_done, task);
318e69375bfSpaul luse 		break;
319b9218b7aSpaul luse 	case ACCEL_COMPARE:
320b9218b7aSpaul luse 		random_num = rand() % 100;
321b9218b7aSpaul luse 		if (random_num < g_fail_percent_goal) {
322b9218b7aSpaul luse 			task->expected_status = -EILSEQ;
323b9218b7aSpaul luse 			*(uint8_t *)task->dst = ~DATA_PATTERN;
324b9218b7aSpaul luse 		} else {
325b9218b7aSpaul luse 			task->expected_status = 0;
326b9218b7aSpaul luse 			*(uint8_t *)task->dst = DATA_PATTERN;
327b9218b7aSpaul luse 		}
328ee7e31f9Spaul luse 		rc = spdk_accel_submit_compare(worker->ch, task->dst, task->src,
329e8463f87Spaul luse 					       g_xfer_size_bytes, accel_done, task);
330b9218b7aSpaul luse 		break;
3310ef079c6Spaul luse 	case ACCEL_DUALCAST:
332ee7e31f9Spaul luse 		rc = spdk_accel_submit_dualcast(worker->ch, task->dst, task->dst2,
333e8463f87Spaul luse 						task->src, g_xfer_size_bytes, accel_done, task);
3340ef079c6Spaul luse 		break;
335e69375bfSpaul luse 	default:
3362a0c66d0Spaul luse 		assert(false);
337e69375bfSpaul luse 		break;
338e69375bfSpaul luse 
3392a0c66d0Spaul luse 	}
34040ec8e97Spaul luse 
34140ec8e97Spaul luse 	if (rc) {
342e8463f87Spaul luse 		accel_done(task, rc);
34340ec8e97Spaul luse 	}
3449f51cf32Spaul luse }
3459f51cf32Spaul luse 
346fab40895Spaul luse static int
347f17e6705Spaul luse _batch_prep_cmd(struct worker_thread *worker, struct ap_task *task,
348f17e6705Spaul luse 		struct accel_batch *worker_batch)
349fab40895Spaul luse {
350f17e6705Spaul luse 	struct spdk_accel_batch *batch = worker_batch->batch;
351fab40895Spaul luse 	int rc = 0;
352fab40895Spaul luse 
353f17e6705Spaul luse 	worker_batch->cmd_count++;
354f17e6705Spaul luse 	assert(worker_batch->cmd_count <= g_ops_per_batch);
355f17e6705Spaul luse 
356fab40895Spaul luse 	switch (g_workload_selection) {
357fab40895Spaul luse 	case ACCEL_COPY:
358fab40895Spaul luse 		rc = spdk_accel_batch_prep_copy(worker->ch, batch, task->dst,
359fab40895Spaul luse 						task->src, g_xfer_size_bytes, accel_done, task);
360fab40895Spaul luse 		break;
361fab40895Spaul luse 	case ACCEL_DUALCAST:
362fab40895Spaul luse 		rc = spdk_accel_batch_prep_dualcast(worker->ch, batch, task->dst, task->dst2,
363fab40895Spaul luse 						    task->src, g_xfer_size_bytes, accel_done, task);
364fab40895Spaul luse 		break;
365fab40895Spaul luse 	case ACCEL_COMPARE:
366fab40895Spaul luse 		rc = spdk_accel_batch_prep_compare(worker->ch, batch, task->dst, task->src,
367fab40895Spaul luse 						   g_xfer_size_bytes, accel_done, task);
368fab40895Spaul luse 		break;
369fab40895Spaul luse 	case ACCEL_FILL:
370fab40895Spaul luse 		rc = spdk_accel_batch_prep_fill(worker->ch, batch, task->dst,
371fab40895Spaul luse 						*(uint8_t *)task->src,
372fab40895Spaul luse 						g_xfer_size_bytes, accel_done, task);
373fab40895Spaul luse 		break;
374fab40895Spaul luse 	case ACCEL_CRC32C:
375fab40895Spaul luse 		rc = spdk_accel_batch_prep_crc32c(worker->ch, batch, (uint32_t *)task->dst,
376fab40895Spaul luse 						  task->src, g_crc32c_seed, g_xfer_size_bytes, accel_done, task);
377fab40895Spaul luse 		break;
378fab40895Spaul luse 	default:
379fab40895Spaul luse 		assert(false);
380fab40895Spaul luse 		break;
381fab40895Spaul luse 	}
382fab40895Spaul luse 
383fab40895Spaul luse 	return rc;
384fab40895Spaul luse }
385fab40895Spaul luse 
3869f51cf32Spaul luse static void
387e150f6b8SZiye Yang _free_task_buffers(struct ap_task *task)
388ac9a1a83Spaul luse {
389ac9a1a83Spaul luse 	spdk_dma_free(task->src);
390ac9a1a83Spaul luse 	spdk_dma_free(task->dst);
391ac9a1a83Spaul luse 	if (g_workload_selection == ACCEL_DUALCAST) {
392ac9a1a83Spaul luse 		spdk_dma_free(task->dst2);
393ac9a1a83Spaul luse 	}
394ac9a1a83Spaul luse }
395ac9a1a83Spaul luse 
396f17e6705Spaul luse static void _batch_done(void *cb_arg);
397f17e6705Spaul luse static void
398f17e6705Spaul luse _build_batch(struct worker_thread *worker, struct ap_task *task)
399f17e6705Spaul luse {
400f17e6705Spaul luse 	struct accel_batch *worker_batch = NULL;
401f17e6705Spaul luse 	int rc;
402f17e6705Spaul luse 
403f17e6705Spaul luse 	assert(!TAILQ_EMPTY(&worker->in_prep_batches));
404f17e6705Spaul luse 
405f17e6705Spaul luse 	worker_batch = TAILQ_FIRST(&worker->in_prep_batches);
406f17e6705Spaul luse 
407f17e6705Spaul luse 	/* If an accel batch hasn't been created yet do so now. */
408f17e6705Spaul luse 	if (worker_batch->batch == NULL) {
409f17e6705Spaul luse 		worker_batch->batch = spdk_accel_batch_create(worker->ch);
410f17e6705Spaul luse 		if (worker_batch->batch == NULL) {
411f17e6705Spaul luse 			fprintf(stderr, "error unable to create new batch\n");
412f17e6705Spaul luse 			return;
413f17e6705Spaul luse 		}
414f17e6705Spaul luse 	}
415f17e6705Spaul luse 
416f17e6705Spaul luse 	/* Prep the command re-using the last completed command's task */
417f17e6705Spaul luse 	rc = _batch_prep_cmd(worker, task, worker_batch);
418f17e6705Spaul luse 	if (rc) {
419f17e6705Spaul luse 		fprintf(stderr, "error preping command for batch\n");
420f17e6705Spaul luse 		goto error;
421f17e6705Spaul luse 	}
422f17e6705Spaul luse 
423f17e6705Spaul luse 	/* If this batch is full move it to the to_submit list so it gets
424f17e6705Spaul luse 	 * submitted as batches complete.
425f17e6705Spaul luse 	 */
426f17e6705Spaul luse 	if (worker_batch->cmd_count == g_ops_per_batch) {
427f17e6705Spaul luse 		TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link);
428f17e6705Spaul luse 		TAILQ_INSERT_TAIL(&worker->to_submit_batches, worker_batch, link);
429f17e6705Spaul luse 	}
430f17e6705Spaul luse 
431f17e6705Spaul luse 	return;
432f17e6705Spaul luse error:
433f17e6705Spaul luse 	spdk_accel_batch_cancel(worker->ch, worker_batch->batch);
434f17e6705Spaul luse 
435f17e6705Spaul luse }
436f17e6705Spaul luse 
437f17e6705Spaul luse static void batch_done(void *cb_arg, int status);
438f17e6705Spaul luse static void
439f17e6705Spaul luse _drain_batch(struct worker_thread *worker)
440f17e6705Spaul luse {
441f17e6705Spaul luse 	struct accel_batch *worker_batch, *tmp;
442f17e6705Spaul luse 	int rc;
443f17e6705Spaul luse 
444f17e6705Spaul luse 	/* submit any batches that were being built up. */
445f17e6705Spaul luse 	TAILQ_FOREACH_SAFE(worker_batch, &worker->in_prep_batches, link, tmp) {
446f17e6705Spaul luse 		if (worker_batch->cmd_count == 0) {
447f17e6705Spaul luse 			continue;
448f17e6705Spaul luse 		}
449f17e6705Spaul luse 		worker->current_queue_depth += worker_batch->cmd_count + 1;
450f17e6705Spaul luse 
451f17e6705Spaul luse 		TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link);
452f17e6705Spaul luse 		TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link);
453f17e6705Spaul luse 		rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch);
454f17e6705Spaul luse 		if (rc == 0) {
455f17e6705Spaul luse 			worker_batch->cmd_count = 0;
456f17e6705Spaul luse 		} else {
457f17e6705Spaul luse 			fprintf(stderr, "error sending final batch\n");
458f17e6705Spaul luse 			worker->current_queue_depth -= worker_batch->cmd_count + 1;
459f17e6705Spaul luse 			break;
460f17e6705Spaul luse 		}
461f17e6705Spaul luse 	}
462f17e6705Spaul luse }
463f17e6705Spaul luse 
464f17e6705Spaul luse static void
465f17e6705Spaul luse _batch_done(void *cb_arg)
466f17e6705Spaul luse {
467f17e6705Spaul luse 	struct accel_batch *worker_batch = (struct accel_batch *)cb_arg;
468f17e6705Spaul luse 	struct worker_thread *worker = worker_batch->worker;
469f17e6705Spaul luse 	int rc;
470f17e6705Spaul luse 
471f17e6705Spaul luse 	assert(TAILQ_EMPTY(&worker->in_use_batches) == 0);
472f17e6705Spaul luse 
473f17e6705Spaul luse 	if (worker_batch->status) {
474f17e6705Spaul luse 		SPDK_ERRLOG("error %d\n", worker_batch->status);
475f17e6705Spaul luse 	}
476f17e6705Spaul luse 
477f17e6705Spaul luse 	worker->current_queue_depth--;
478f17e6705Spaul luse 	TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link);
479f17e6705Spaul luse 	TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link);
480f17e6705Spaul luse 	worker_batch->batch = NULL;
481f17e6705Spaul luse 	worker_batch->cmd_count = 0;
482f17e6705Spaul luse 
483f17e6705Spaul luse 	if (!worker->is_draining) {
484f17e6705Spaul luse 		worker_batch = TAILQ_FIRST(&worker->to_submit_batches);
485f17e6705Spaul luse 		if (worker_batch != NULL) {
486f17e6705Spaul luse 
487f17e6705Spaul luse 			assert(worker_batch->cmd_count == g_ops_per_batch);
488f17e6705Spaul luse 
489f17e6705Spaul luse 			/* Add one for the batch command itself. */
490f17e6705Spaul luse 			worker->current_queue_depth += g_ops_per_batch + 1;
491f17e6705Spaul luse 			TAILQ_REMOVE(&worker->to_submit_batches, worker_batch, link);
492f17e6705Spaul luse 			TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link);
493f17e6705Spaul luse 
494f17e6705Spaul luse 			rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch);
495f17e6705Spaul luse 			if (rc) {
496f17e6705Spaul luse 				fprintf(stderr, "error ending batch\n");
497f17e6705Spaul luse 				worker->current_queue_depth -= g_ops_per_batch + 1;
498f17e6705Spaul luse 				return;
499f17e6705Spaul luse 			}
500f17e6705Spaul luse 		}
501f17e6705Spaul luse 	} else {
502f17e6705Spaul luse 		_drain_batch(worker);
503f17e6705Spaul luse 	}
504f17e6705Spaul luse }
505f17e6705Spaul luse 
506ac9a1a83Spaul luse static void
507fab40895Spaul luse batch_done(void *cb_arg, int status)
508fab40895Spaul luse {
509f17e6705Spaul luse 	struct accel_batch *worker_batch = (struct accel_batch *)cb_arg;
510fab40895Spaul luse 
511f17e6705Spaul luse 	assert(worker_batch->worker);
512f17e6705Spaul luse 
513f17e6705Spaul luse 	worker_batch->status = status;
514f17e6705Spaul luse 	spdk_thread_send_msg(worker_batch->worker->thread, _batch_done, worker_batch);
515fab40895Spaul luse }
516fab40895Spaul luse 
517fab40895Spaul luse static void
5189f51cf32Spaul luse _accel_done(void *arg1)
5199f51cf32Spaul luse {
5209f51cf32Spaul luse 	struct ap_task *task = arg1;
5219f51cf32Spaul luse 	struct worker_thread *worker = task->worker;
522e69375bfSpaul luse 	uint32_t sw_crc32c;
5239f51cf32Spaul luse 
5249f51cf32Spaul luse 	assert(worker);
5259f51cf32Spaul luse 	assert(worker->current_queue_depth > 0);
5269f51cf32Spaul luse 
527b9218b7aSpaul luse 	if (g_verify && task->status == 0) {
528b9218b7aSpaul luse 		switch (g_workload_selection) {
529b9218b7aSpaul luse 		case ACCEL_CRC32C:
530e69375bfSpaul luse 			/* calculate sw CRC-32C and compare to sw aceel result. */
531e69375bfSpaul luse 			sw_crc32c = spdk_crc32c_update(task->src, g_xfer_size_bytes, ~g_crc32c_seed);
532e69375bfSpaul luse 			if (*(uint32_t *)task->dst != sw_crc32c) {
533e69375bfSpaul luse 				SPDK_NOTICELOG("CRC-32C miscompare\n");
534e69375bfSpaul luse 				worker->xfer_failed++;
535e69375bfSpaul luse 			}
536b9218b7aSpaul luse 			break;
537b9218b7aSpaul luse 		case ACCEL_COPY:
538b9218b7aSpaul luse 			if (memcmp(task->src, task->dst, g_xfer_size_bytes)) {
5399f51cf32Spaul luse 				SPDK_NOTICELOG("Data miscompare\n");
5409f51cf32Spaul luse 				worker->xfer_failed++;
541b9218b7aSpaul luse 			}
542b9218b7aSpaul luse 			break;
5430ef079c6Spaul luse 		case ACCEL_DUALCAST:
5440ef079c6Spaul luse 			if (memcmp(task->src, task->dst, g_xfer_size_bytes)) {
5450ef079c6Spaul luse 				SPDK_NOTICELOG("Data miscompare, first destination\n");
5460ef079c6Spaul luse 				worker->xfer_failed++;
5470ef079c6Spaul luse 			}
5480ef079c6Spaul luse 			if (memcmp(task->src, task->dst2, g_xfer_size_bytes)) {
5490ef079c6Spaul luse 				SPDK_NOTICELOG("Data miscompare, second destination\n");
5500ef079c6Spaul luse 				worker->xfer_failed++;
5510ef079c6Spaul luse 			}
5520ef079c6Spaul luse 			break;
553d207237fSpaul luse 		case ACCEL_FILL:
554d207237fSpaul luse 			if (memcmp(task->dst, task->src, g_xfer_size_bytes)) {
555d207237fSpaul luse 				SPDK_NOTICELOG("Data miscompare\n");
556d207237fSpaul luse 				worker->xfer_failed++;
557d207237fSpaul luse 			}
558d207237fSpaul luse 			break;
5598cee297cSpaul luse 		case ACCEL_COMPARE:
5608cee297cSpaul luse 			break;
561b9218b7aSpaul luse 		default:
562b9218b7aSpaul luse 			assert(false);
563b9218b7aSpaul luse 			break;
5649f51cf32Spaul luse 		}
5659f51cf32Spaul luse 	}
566b9218b7aSpaul luse 
567b9218b7aSpaul luse 	if (task->expected_status == -EILSEQ) {
568b9218b7aSpaul luse 		assert(task->status != 0);
569b9218b7aSpaul luse 		worker->injected_miscompares++;
570b9218b7aSpaul luse 	} else if (task->status) {
571f17e6705Spaul luse 		/* Expected to pass but the accel engine reported an error (ex: COMPARE operation). */
572b9218b7aSpaul luse 		worker->xfer_failed++;
573b9218b7aSpaul luse 	}
574b9218b7aSpaul luse 
5759f51cf32Spaul luse 	worker->xfer_completed++;
5769f51cf32Spaul luse 	worker->current_queue_depth--;
5779f51cf32Spaul luse 
57840ec8e97Spaul luse 	if (!worker->is_draining) {
579f17e6705Spaul luse 		if (g_ops_per_batch == 0) {
5809f51cf32Spaul luse 			_submit_single(worker, task);
581ac9a1a83Spaul luse 			worker->current_queue_depth++;
582f17e6705Spaul luse 		} else {
583f17e6705Spaul luse 			_build_batch(worker, task);
5849f51cf32Spaul luse 		}
585f17e6705Spaul luse 	} else if (g_ops_per_batch > 0) {
586f17e6705Spaul luse 		_drain_batch(worker);
587b34883e0SZiye Yang 	} else {
588b34883e0SZiye Yang 		TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link);
589f17e6705Spaul luse 	}
5909f51cf32Spaul luse }
5919f51cf32Spaul luse 
5929f51cf32Spaul luse static int
5939f51cf32Spaul luse dump_result(void)
5949f51cf32Spaul luse {
5959f51cf32Spaul luse 	uint64_t total_completed = 0;
5969f51cf32Spaul luse 	uint64_t total_failed = 0;
597b9218b7aSpaul luse 	uint64_t total_miscompared = 0;
5989f51cf32Spaul luse 	uint64_t total_xfer_per_sec, total_bw_in_MiBps;
5999f51cf32Spaul luse 	struct worker_thread *worker = g_workers;
6009f51cf32Spaul luse 
601445fe74eSpaul luse 	printf("\nCore,Thread   Transfers     Bandwidth     Failed     Miscompares\n");
602445fe74eSpaul luse 	printf("------------------------------------------------------------------------\n");
6039f51cf32Spaul luse 	while (worker != NULL) {
6049f51cf32Spaul luse 
6059f51cf32Spaul luse 		uint64_t xfer_per_sec = worker->xfer_completed / g_time_in_sec;
6069f51cf32Spaul luse 		uint64_t bw_in_MiBps = (worker->xfer_completed * g_xfer_size_bytes) /
6079f51cf32Spaul luse 				       (g_time_in_sec * 1024 * 1024);
6089f51cf32Spaul luse 
6099f51cf32Spaul luse 		total_completed += worker->xfer_completed;
6109f51cf32Spaul luse 		total_failed += worker->xfer_failed;
611b9218b7aSpaul luse 		total_miscompared += worker->injected_miscompares;
6129f51cf32Spaul luse 
6139f51cf32Spaul luse 		if (xfer_per_sec) {
614445fe74eSpaul luse 			printf("%u,%u%17" PRIu64 "/s%9" PRIu64 " MiB/s%7" PRIu64 " %11" PRIu64 "\n",
615445fe74eSpaul luse 			       worker->display.core, worker->display.thread, xfer_per_sec,
616b9218b7aSpaul luse 			       bw_in_MiBps, worker->xfer_failed, worker->injected_miscompares);
6179f51cf32Spaul luse 		}
6189f51cf32Spaul luse 
6199f51cf32Spaul luse 		worker = worker->next;
6209f51cf32Spaul luse 	}
6219f51cf32Spaul luse 
6229f51cf32Spaul luse 	total_xfer_per_sec = total_completed / g_time_in_sec;
6239f51cf32Spaul luse 	total_bw_in_MiBps = (total_completed * g_xfer_size_bytes) /
6249f51cf32Spaul luse 			    (g_time_in_sec * 1024 * 1024);
6259f51cf32Spaul luse 
626445fe74eSpaul luse 	printf("=========================================================================\n");
627445fe74eSpaul luse 	printf("Total:%15" PRIu64 "/s%9" PRIu64 " MiB/s%6" PRIu64 " %11" PRIu64"\n\n",
628b9218b7aSpaul luse 	       total_xfer_per_sec, total_bw_in_MiBps, total_failed, total_miscompared);
6299f51cf32Spaul luse 
6309f51cf32Spaul luse 	return total_failed ? 1 : 0;
6319f51cf32Spaul luse }
6329f51cf32Spaul luse 
633e150f6b8SZiye Yang static inline void
634e150f6b8SZiye Yang _free_task_buffers_in_pool(struct worker_thread *worker)
635e150f6b8SZiye Yang {
636e150f6b8SZiye Yang 	struct ap_task *task;
637e150f6b8SZiye Yang 
638e150f6b8SZiye Yang 	assert(worker);
639e150f6b8SZiye Yang 	while ((task = TAILQ_FIRST(&worker->tasks_pool))) {
640e150f6b8SZiye Yang 		TAILQ_REMOVE(&worker->tasks_pool, task, link);
641e150f6b8SZiye Yang 		_free_task_buffers(task);
642e150f6b8SZiye Yang 	}
643e150f6b8SZiye Yang }
644e150f6b8SZiye Yang 
6459f51cf32Spaul luse static int
6469f51cf32Spaul luse _check_draining(void *arg)
6479f51cf32Spaul luse {
6489f51cf32Spaul luse 	struct worker_thread *worker = arg;
6499f51cf32Spaul luse 
6509f51cf32Spaul luse 	assert(worker);
6519f51cf32Spaul luse 
6529f51cf32Spaul luse 	if (worker->current_queue_depth == 0) {
653e150f6b8SZiye Yang 		_free_task_buffers_in_pool(worker);
6549f51cf32Spaul luse 		spdk_poller_unregister(&worker->is_draining_poller);
6559f51cf32Spaul luse 		unregister_worker(worker);
6569f51cf32Spaul luse 	}
6579f51cf32Spaul luse 
6589f51cf32Spaul luse 	return -1;
6599f51cf32Spaul luse }
6609f51cf32Spaul luse 
6619f51cf32Spaul luse static int
6629f51cf32Spaul luse _worker_stop(void *arg)
6639f51cf32Spaul luse {
6649f51cf32Spaul luse 	struct worker_thread *worker = arg;
6659f51cf32Spaul luse 
6669f51cf32Spaul luse 	assert(worker);
6679f51cf32Spaul luse 
6689f51cf32Spaul luse 	spdk_poller_unregister(&worker->stop_poller);
6699f51cf32Spaul luse 
6709f51cf32Spaul luse 	/* now let the worker drain and check it's outstanding IO with a poller */
6719f51cf32Spaul luse 	worker->is_draining = true;
672ab0bc5c2SShuhei Matsumoto 	worker->is_draining_poller = SPDK_POLLER_REGISTER(_check_draining, worker, 0);
6739f51cf32Spaul luse 
6749f51cf32Spaul luse 	return 0;
6759f51cf32Spaul luse }
6769f51cf32Spaul luse 
6779f51cf32Spaul luse static void
678a34fc12bSpaul luse _init_thread(void *arg1)
679a34fc12bSpaul luse {
680a34fc12bSpaul luse 	struct worker_thread *worker;
681a34fc12bSpaul luse 	struct ap_task *task;
682f17e6705Spaul luse 	int i, rc, num_batches;
683f17e6705Spaul luse 	int max_per_batch;
684a34fc12bSpaul luse 	int remaining = g_queue_depth;
685f17e6705Spaul luse 	int num_tasks = g_queue_depth;
686f17e6705Spaul luse 	struct accel_batch *tmp;
687f17e6705Spaul luse 	struct accel_batch *worker_batch = NULL;
688445fe74eSpaul luse 	struct display_info *display = arg1;
689a34fc12bSpaul luse 
690a34fc12bSpaul luse 	worker = calloc(1, sizeof(*worker));
691a34fc12bSpaul luse 	if (worker == NULL) {
692a34fc12bSpaul luse 		fprintf(stderr, "Unable to allocate worker\n");
693445fe74eSpaul luse 		free(display);
694a34fc12bSpaul luse 		return;
695a34fc12bSpaul luse 	}
696a34fc12bSpaul luse 
697445fe74eSpaul luse 	worker->display.core = display->core;
698445fe74eSpaul luse 	worker->display.thread = display->thread;
699445fe74eSpaul luse 	free(display);
7009f51cf32Spaul luse 	worker->core = spdk_env_get_current_core();
7019f51cf32Spaul luse 	worker->thread = spdk_get_thread();
702eea826a2Spaul luse 	pthread_mutex_lock(&g_workers_lock);
703eea826a2Spaul luse 	g_num_workers++;
7049f51cf32Spaul luse 	worker->next = g_workers;
705eea826a2Spaul luse 	g_workers = worker;
706eea826a2Spaul luse 	pthread_mutex_unlock(&g_workers_lock);
7079f51cf32Spaul luse 	worker->ch = spdk_accel_engine_get_io_channel();
708b9218b7aSpaul luse 
709f17e6705Spaul luse 	TAILQ_INIT(&worker->tasks_pool);
710f17e6705Spaul luse 
711f17e6705Spaul luse 	if (g_ops_per_batch > 0) {
712f17e6705Spaul luse 
7130cecfcb1Spaul luse 		max_per_batch = spdk_accel_batch_get_max(worker->ch);
7140cecfcb1Spaul luse 		assert(max_per_batch > 0);
7150cecfcb1Spaul luse 
716f17e6705Spaul luse 		if (g_ops_per_batch > max_per_batch) {
717f17e6705Spaul luse 			fprintf(stderr, "Reducing requested batch amount to max supported of %d\n", max_per_batch);
718f17e6705Spaul luse 			g_ops_per_batch = max_per_batch;
719f17e6705Spaul luse 		}
720f17e6705Spaul luse 
721f17e6705Spaul luse 		if (g_ops_per_batch > g_queue_depth) {
722f17e6705Spaul luse 			fprintf(stderr, "Batch amount > queue depth, resetting to %d\n", g_queue_depth);
723f17e6705Spaul luse 			g_ops_per_batch = g_queue_depth;
724f17e6705Spaul luse 		}
725f17e6705Spaul luse 
726f17e6705Spaul luse 		TAILQ_INIT(&worker->in_prep_batches);
727f17e6705Spaul luse 		TAILQ_INIT(&worker->to_submit_batches);
728f17e6705Spaul luse 		TAILQ_INIT(&worker->in_use_batches);
729f17e6705Spaul luse 
730f17e6705Spaul luse 		/* A worker_batch will live on one of 3 lists:
731f17e6705Spaul luse 		 * IN_PREP: as individual IOs complete new ones are built on on a
732f17e6705Spaul luse 		 *          worker_batch on this list until it reaches g_ops_per_batch.
733f17e6705Spaul luse 		 * TO_SUBMIT: as batches are built up on IO completion they are moved
734f17e6705Spaul luse 		 *	      to this list once they are full.  This list is used in
735f17e6705Spaul luse 		 *	      batch completion to start new batches.
736f17e6705Spaul luse 		 * IN_USE: the worker_batch is outstanding and will be moved to in prep
737f17e6705Spaul luse 		 *         list when the batch is completed.
738f17e6705Spaul luse 		 *
739f17e6705Spaul luse 		 * So we need enough to cover Q depth loading and then one to replace
740f17e6705Spaul luse 		 * each one of those and for when everything is outstanding there needs
741f17e6705Spaul luse 		 * to be one extra batch to build up while the last batch is completing
742f17e6705Spaul luse 		 * IO but before it's completed the batch command.
743f17e6705Spaul luse 		 */
744f17e6705Spaul luse 		num_batches = (g_queue_depth / g_ops_per_batch * 2) + 1;
745f17e6705Spaul luse 		worker->batch_base = calloc(num_batches, sizeof(struct accel_batch));
746f17e6705Spaul luse 		worker_batch = worker->batch_base;
747f17e6705Spaul luse 		for (i = 0; i < num_batches; i++) {
748f17e6705Spaul luse 			worker_batch->worker = worker;
749f17e6705Spaul luse 			TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link);
750f17e6705Spaul luse 			worker_batch++;
751f17e6705Spaul luse 		}
752f17e6705Spaul luse 	}
753f17e6705Spaul luse 
754ac9a1a83Spaul luse 	worker->task_base = calloc(num_tasks, sizeof(struct ap_task));
755ac9a1a83Spaul luse 	if (worker->task_base == NULL) {
756ac9a1a83Spaul luse 		fprintf(stderr, "Could not allocate task base.\n");
757ac9a1a83Spaul luse 		goto error;
7580cecfcb1Spaul luse 	}
759ac9a1a83Spaul luse 
760ac9a1a83Spaul luse 	task = worker->task_base;
761ac9a1a83Spaul luse 	for (i = 0; i < num_tasks; i++) {
762ac9a1a83Spaul luse 		TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link);
763ac9a1a83Spaul luse 		if (_get_task_data_bufs(task)) {
764ac9a1a83Spaul luse 			fprintf(stderr, "Unable to get data bufs\n");
765ac9a1a83Spaul luse 			goto error;
766ac9a1a83Spaul luse 		}
767ac9a1a83Spaul luse 		task++;
7689f51cf32Spaul luse 	}
7699f51cf32Spaul luse 
7709f51cf32Spaul luse 	/* Register a poller that will stop the worker at time elapsed */
771ab0bc5c2SShuhei Matsumoto 	worker->stop_poller = SPDK_POLLER_REGISTER(_worker_stop, worker,
7729f51cf32Spaul luse 			      g_time_in_sec * 1000000ULL);
7739f51cf32Spaul luse 
774f17e6705Spaul luse 	/* If batching is enabled load up to the full Q depth before
775f17e6705Spaul luse 	 * processing any completions, then ping pong between two batches,
776f17e6705Spaul luse 	 * one processing and one being built up for when the other completes.
777a34fc12bSpaul luse 	 */
778f17e6705Spaul luse 	if (g_ops_per_batch > 0) {
779a34fc12bSpaul luse 		do {
780f17e6705Spaul luse 			worker_batch = TAILQ_FIRST(&worker->in_prep_batches);
781f17e6705Spaul luse 			if (worker_batch == NULL) {
782f17e6705Spaul luse 				goto error;
783f17e6705Spaul luse 			}
784f17e6705Spaul luse 
785f17e6705Spaul luse 			worker_batch->batch = spdk_accel_batch_create(worker->ch);
786f17e6705Spaul luse 			if (worker_batch->batch == NULL) {
787f17e6705Spaul luse 				raise(SIGINT);
788a34fc12bSpaul luse 				break;
789a34fc12bSpaul luse 			}
790a34fc12bSpaul luse 
791f17e6705Spaul luse 			for (i = 0; i < g_ops_per_batch; i++) {
792ac9a1a83Spaul luse 				task = _get_task(worker);
793ac9a1a83Spaul luse 				if (task == NULL) {
794a34fc12bSpaul luse 					goto error;
7959f51cf32Spaul luse 				}
796b9218b7aSpaul luse 
797f17e6705Spaul luse 				rc = _batch_prep_cmd(worker, task, worker_batch);
798a34fc12bSpaul luse 				if (rc) {
799a34fc12bSpaul luse 					fprintf(stderr, "error preping command\n");
800a34fc12bSpaul luse 					goto error;
801a34fc12bSpaul luse 				}
802a34fc12bSpaul luse 			}
803a34fc12bSpaul luse 
804f17e6705Spaul luse 			/* for the batch operation itself. */
805f17e6705Spaul luse 			task->worker->current_queue_depth++;
806f17e6705Spaul luse 			TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link);
807f17e6705Spaul luse 			TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link);
808f17e6705Spaul luse 
809f17e6705Spaul luse 			rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch);
810a34fc12bSpaul luse 			if (rc) {
811f17e6705Spaul luse 				fprintf(stderr, "error ending batch\n");
812a34fc12bSpaul luse 				goto error;
813a34fc12bSpaul luse 			}
814f17e6705Spaul luse 			assert(remaining >= g_ops_per_batch);
815f17e6705Spaul luse 			remaining -= g_ops_per_batch;
816f17e6705Spaul luse 		} while (remaining > 0);
817b9218b7aSpaul luse 	}
8180ef079c6Spaul luse 
819f17e6705Spaul luse 	/* Submit as singles when no batching is enabled or we ran out of batches. */
820a34fc12bSpaul luse 	for (i = 0; i < remaining; i++) {
821ac9a1a83Spaul luse 		task = _get_task(worker);
822ac9a1a83Spaul luse 		if (task == NULL) {
823a34fc12bSpaul luse 			goto error;
824b9218b7aSpaul luse 		}
825b9218b7aSpaul luse 
8269f51cf32Spaul luse 		_submit_single(worker, task);
8279f51cf32Spaul luse 	}
828a34fc12bSpaul luse 	return;
829a34fc12bSpaul luse error:
830f17e6705Spaul luse 	if (worker_batch && worker_batch->batch) {
831f17e6705Spaul luse 		TAILQ_FOREACH_SAFE(worker_batch, &worker->in_use_batches, link, tmp) {
832f17e6705Spaul luse 			spdk_accel_batch_cancel(worker->ch, worker_batch->batch);
833f17e6705Spaul luse 			TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link);
834f17e6705Spaul luse 		}
835f17e6705Spaul luse 	}
836e150f6b8SZiye Yang 
837e150f6b8SZiye Yang 	_free_task_buffers_in_pool(worker);
838f17e6705Spaul luse 	free(worker->batch_base);
839ac9a1a83Spaul luse 	free(worker->task_base);
840a34fc12bSpaul luse 	free(worker);
841a34fc12bSpaul luse 	spdk_app_stop(-1);
8429f51cf32Spaul luse }
8439f51cf32Spaul luse 
8449f51cf32Spaul luse static void
845e8463f87Spaul luse accel_done(void *cb_arg, int status)
8469f51cf32Spaul luse {
847e8463f87Spaul luse 	struct ap_task *task = (struct ap_task *)cb_arg;
8489f51cf32Spaul luse 	struct worker_thread *worker = task->worker;
8499f51cf32Spaul luse 
8509f51cf32Spaul luse 	assert(worker);
8519f51cf32Spaul luse 
852b9218b7aSpaul luse 	task->status = status;
8539f51cf32Spaul luse 	spdk_thread_send_msg(worker->thread, _accel_done, task);
8549f51cf32Spaul luse }
8559f51cf32Spaul luse 
8569f51cf32Spaul luse static void
8579f51cf32Spaul luse accel_perf_start(void *arg1)
8589f51cf32Spaul luse {
859514be889Spaul luse 	struct spdk_io_channel *accel_ch;
860eea826a2Spaul luse 	struct spdk_cpuset tmp_cpumask = {};
861eea826a2Spaul luse 	char thread_name[32];
862eea826a2Spaul luse 	uint32_t i;
863445fe74eSpaul luse 	int j;
864eea826a2Spaul luse 	struct spdk_thread *thread;
865445fe74eSpaul luse 	struct display_info *display;
866514be889Spaul luse 
867514be889Spaul luse 	accel_ch = spdk_accel_engine_get_io_channel();
868a34fc12bSpaul luse 	g_capabilites = spdk_accel_get_capabilities(accel_ch);
869514be889Spaul luse 	spdk_put_io_channel(accel_ch);
870514be889Spaul luse 
871a34fc12bSpaul luse 	if ((g_capabilites & g_workload_selection) != g_workload_selection) {
872a7dfca5bSpaul luse 		SPDK_WARNLOG("The selected workload is not natively supported by the current engine\n");
873a7dfca5bSpaul luse 		SPDK_WARNLOG("The software engine will be used instead.\n\n");
874514be889Spaul luse 	}
875514be889Spaul luse 
8769f51cf32Spaul luse 	g_tsc_rate = spdk_get_ticks_hz();
8779f51cf32Spaul luse 	g_tsc_us_rate = g_tsc_rate / (1000 * 1000);
8789f51cf32Spaul luse 	g_tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate;
8799f51cf32Spaul luse 
8809f51cf32Spaul luse 	printf("Running for %d seconds...\n", g_time_in_sec);
8819f51cf32Spaul luse 	fflush(stdout);
8829f51cf32Spaul luse 
883eea826a2Spaul luse 	/* Create worker threads for each core that was specified. */
884eea826a2Spaul luse 	SPDK_ENV_FOREACH_CORE(i) {
885445fe74eSpaul luse 		for (j = 0; j < g_threads_per_core; j++) {
886445fe74eSpaul luse 			snprintf(thread_name, sizeof(thread_name), "ap_worker_%u_%u", i, j);
887eea826a2Spaul luse 			spdk_cpuset_zero(&tmp_cpumask);
888eea826a2Spaul luse 			spdk_cpuset_set_cpu(&tmp_cpumask, i, true);
889eea826a2Spaul luse 			thread = spdk_thread_create(thread_name, &tmp_cpumask);
890445fe74eSpaul luse 			display = calloc(1, sizeof(*display));
891445fe74eSpaul luse 			if (display == NULL) {
892445fe74eSpaul luse 				fprintf(stderr, "Unable to allocate memory\n");
893445fe74eSpaul luse 				spdk_app_stop(-1);
894445fe74eSpaul luse 				return;
895445fe74eSpaul luse 			}
896445fe74eSpaul luse 			display->core = i;
897445fe74eSpaul luse 			display->thread = j;
898445fe74eSpaul luse 			spdk_thread_send_msg(thread, _init_thread, display);
899445fe74eSpaul luse 		}
900eea826a2Spaul luse 	}
9019f51cf32Spaul luse }
9029f51cf32Spaul luse 
9039f51cf32Spaul luse int
9049f51cf32Spaul luse main(int argc, char **argv)
9059f51cf32Spaul luse {
9069f51cf32Spaul luse 	struct spdk_app_opts opts = {};
9079f51cf32Spaul luse 	struct worker_thread *worker, *tmp;
9089f51cf32Spaul luse 
9099f51cf32Spaul luse 	pthread_mutex_init(&g_workers_lock, NULL);
91048701bd9SZiye Yang 	spdk_app_opts_init(&opts, sizeof(opts));
9119f51cf32Spaul luse 	opts.reactor_mask = "0x1";
912445fe74eSpaul luse 	if (spdk_app_parse_args(argc, argv, &opts, "o:q:t:yw:P:f:b:T:", NULL, parse_args,
9131e2b38baSyidong0635 				usage) != SPDK_APP_PARSE_ARGS_SUCCESS) {
914*9b189667Spaul luse 		g_rc = -1;
9159f51cf32Spaul luse 		goto cleanup;
9169f51cf32Spaul luse 	}
9179f51cf32Spaul luse 
918b9218b7aSpaul luse 	if ((g_workload_selection != ACCEL_COPY) &&
919b9218b7aSpaul luse 	    (g_workload_selection != ACCEL_FILL) &&
920b9218b7aSpaul luse 	    (g_workload_selection != ACCEL_CRC32C) &&
9210ef079c6Spaul luse 	    (g_workload_selection != ACCEL_COMPARE) &&
9220ef079c6Spaul luse 	    (g_workload_selection != ACCEL_DUALCAST)) {
9232a0c66d0Spaul luse 		usage();
924*9b189667Spaul luse 		g_rc = -1;
9252a0c66d0Spaul luse 		goto cleanup;
9262a0c66d0Spaul luse 	}
9272a0c66d0Spaul luse 
928f17e6705Spaul luse 	if (g_ops_per_batch > 0 && (g_queue_depth % g_ops_per_batch > 0)) {
929f17e6705Spaul luse 		fprintf(stdout, "batch size must be a multiple of queue depth\n");
930f17e6705Spaul luse 		usage();
931*9b189667Spaul luse 		g_rc = -1;
932f17e6705Spaul luse 		goto cleanup;
933f17e6705Spaul luse 	}
934f17e6705Spaul luse 
9359f51cf32Spaul luse 	dump_user_config(&opts);
936*9b189667Spaul luse 	g_rc = spdk_app_start(&opts, accel_perf_start, NULL);
937*9b189667Spaul luse 	if (g_rc) {
9389f51cf32Spaul luse 		SPDK_ERRLOG("ERROR starting application\n");
9399f51cf32Spaul luse 	}
9409f51cf32Spaul luse 
9419f51cf32Spaul luse 	pthread_mutex_destroy(&g_workers_lock);
9429f51cf32Spaul luse 
9439f51cf32Spaul luse 	worker = g_workers;
9449f51cf32Spaul luse 	while (worker) {
9459f51cf32Spaul luse 		tmp = worker->next;
9469f51cf32Spaul luse 		free(worker);
9479f51cf32Spaul luse 		worker = tmp;
9489f51cf32Spaul luse 	}
9499f51cf32Spaul luse cleanup:
9509f51cf32Spaul luse 	spdk_app_fini();
951*9b189667Spaul luse 	return g_rc;
9529f51cf32Spaul luse }
953