xref: /spdk/examples/accel/perf/accel_perf.c (revision b34883e07baa28e52ccaaa08679a6e077051307c)
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;
509f51cf32Spaul luse static int g_xfer_size_bytes = 4096;
519f51cf32Spaul luse static int g_queue_depth = 32;
52f17e6705Spaul luse static int g_ops_per_batch = 0;
539f51cf32Spaul luse static int g_time_in_sec = 5;
54e69375bfSpaul luse static uint32_t g_crc32c_seed = 0;
55b9218b7aSpaul luse static int g_fail_percent_goal = 0;
5689495464Spaul luse static uint8_t g_fill_pattern = 255;
579f51cf32Spaul luse static bool g_verify = false;
582a0c66d0Spaul luse static const char *g_workload_type = NULL;
59514be889Spaul luse static enum accel_capability g_workload_selection;
609f51cf32Spaul luse static struct worker_thread *g_workers = NULL;
619f51cf32Spaul luse static int g_num_workers = 0;
629f51cf32Spaul luse static pthread_mutex_t g_workers_lock = PTHREAD_MUTEX_INITIALIZER;
63a34fc12bSpaul luse uint64_t g_capabilites;
64cdefd3d3Spaul luse 
65cdefd3d3Spaul luse struct worker_thread;
66cdefd3d3Spaul luse static void accel_done(void *ref, int status);
67cdefd3d3Spaul luse 
68cdefd3d3Spaul luse struct ap_task {
69cdefd3d3Spaul luse 	void			*src;
70cdefd3d3Spaul luse 	void			*dst;
71cdefd3d3Spaul luse 	void			*dst2;
72cdefd3d3Spaul luse 	struct worker_thread	*worker;
73cdefd3d3Spaul luse 	int			status;
74cdefd3d3Spaul luse 	int			expected_status; /* used for the compare operation */
75cdefd3d3Spaul luse 	TAILQ_ENTRY(ap_task)	link;
76cdefd3d3Spaul luse };
779f51cf32Spaul luse 
78f17e6705Spaul luse struct accel_batch {
79f17e6705Spaul luse 	int				status;
80f17e6705Spaul luse 	int				cmd_count;
81f17e6705Spaul luse 	struct spdk_accel_batch		*batch;
82f17e6705Spaul luse 	struct worker_thread		*worker;
83f17e6705Spaul luse 	TAILQ_ENTRY(accel_batch)	link;
84f17e6705Spaul luse };
85f17e6705Spaul luse 
869f51cf32Spaul luse struct worker_thread {
879f51cf32Spaul luse 	struct spdk_io_channel		*ch;
889f51cf32Spaul luse 	uint64_t			xfer_completed;
899f51cf32Spaul luse 	uint64_t			xfer_failed;
90b9218b7aSpaul luse 	uint64_t			injected_miscompares;
919f51cf32Spaul luse 	uint64_t			current_queue_depth;
92ac9a1a83Spaul luse 	TAILQ_HEAD(, ap_task)		tasks_pool;
939f51cf32Spaul luse 	struct worker_thread		*next;
949f51cf32Spaul luse 	unsigned			core;
959f51cf32Spaul luse 	struct spdk_thread		*thread;
969f51cf32Spaul luse 	bool				is_draining;
979f51cf32Spaul luse 	struct spdk_poller		*is_draining_poller;
989f51cf32Spaul luse 	struct spdk_poller		*stop_poller;
99ac9a1a83Spaul luse 	void				*task_base;
100f17e6705Spaul luse 	struct accel_batch		*batch_base;
101f17e6705Spaul luse 	TAILQ_HEAD(, accel_batch)	in_prep_batches;
102f17e6705Spaul luse 	TAILQ_HEAD(, accel_batch)	in_use_batches;
103f17e6705Spaul luse 	TAILQ_HEAD(, accel_batch)	to_submit_batches;
1049f51cf32Spaul luse };
1059f51cf32Spaul luse 
1069f51cf32Spaul luse static void
1079f51cf32Spaul luse dump_user_config(struct spdk_app_opts *opts)
1089f51cf32Spaul luse {
1099f51cf32Spaul luse 	printf("SPDK Configuration:\n");
1109f51cf32Spaul luse 	printf("Core mask:      %s\n\n", opts->reactor_mask);
1119f51cf32Spaul luse 	printf("Accel Perf Configuration:\n");
1122a0c66d0Spaul luse 	printf("Workload Type:  %s\n", g_workload_type);
113b9218b7aSpaul luse 	if (g_workload_selection == ACCEL_CRC32C) {
114b9218b7aSpaul luse 		printf("CRC-32C seed:   %u\n", g_crc32c_seed);
11589495464Spaul luse 	} else if (g_workload_selection == ACCEL_FILL) {
11689495464Spaul luse 		printf("Fill pattern:   0x%x\n", g_fill_pattern);
117b9218b7aSpaul luse 	} else if ((g_workload_selection == ACCEL_COMPARE) && g_fail_percent_goal > 0) {
11889495464Spaul luse 		printf("Failure inject: %u percent\n", g_fail_percent_goal);
119e69375bfSpaul luse 	}
1209f51cf32Spaul luse 	printf("Transfer size:  %u bytes\n", g_xfer_size_bytes);
1219f51cf32Spaul luse 	printf("Queue depth:    %u\n", g_queue_depth);
1229f51cf32Spaul luse 	printf("Run time:       %u seconds\n", g_time_in_sec);
123f17e6705Spaul luse 	if (g_ops_per_batch > 0) {
124f17e6705Spaul luse 		printf("Batching:       %u operations\n", g_ops_per_batch);
125f17e6705Spaul luse 	} else {
126f17e6705Spaul luse 		printf("Batching:       Disabled\n");
127f17e6705Spaul luse 	}
1289f51cf32Spaul luse 	printf("Verify:         %s\n\n", g_verify ? "Yes" : "No");
1299f51cf32Spaul luse }
1309f51cf32Spaul luse 
1319f51cf32Spaul luse static void
1329f51cf32Spaul luse usage(void)
1339f51cf32Spaul luse {
1349f51cf32Spaul luse 	printf("accel_perf options:\n");
1359f51cf32Spaul luse 	printf("\t[-h help message]\n");
136f17e6705Spaul luse 	printf("\t[-q queue depth per core]\n");
1379f51cf32Spaul luse 	printf("\t[-n number of channels]\n");
1389f51cf32Spaul luse 	printf("\t[-o transfer size in bytes]\n");
1399f51cf32Spaul luse 	printf("\t[-t time in seconds]\n");
1400ef079c6Spaul luse 	printf("\t[-w workload type must be one of these: copy, fill, crc32c, compare, dualcast\n");
141e69375bfSpaul luse 	printf("\t[-s for crc32c workload, use this seed value (default 0)\n");
142b9218b7aSpaul luse 	printf("\t[-P for compare workload, percentage of operations that should miscompare (percent, default 0)\n");
14389495464Spaul luse 	printf("\t[-f for fill workload, use this BYTE value (default 255)\n");
1442a0c66d0Spaul luse 	printf("\t[-y verify result if this switch is on]\n");
145f17e6705Spaul luse 	printf("\t[-b batch this number of operations at a time (default 0 = disabled)]\n");
1469f51cf32Spaul luse }
1479f51cf32Spaul luse 
1489f51cf32Spaul luse static int
1499f51cf32Spaul luse parse_args(int argc, char *argv)
1509f51cf32Spaul luse {
1519f51cf32Spaul luse 	switch (argc) {
152f17e6705Spaul luse 	case 'b':
153f17e6705Spaul luse 		g_ops_per_batch = spdk_strtol(optarg, 10);
154f17e6705Spaul luse 		break;
15589495464Spaul luse 	case 'f':
15689495464Spaul luse 		g_fill_pattern = (uint8_t)spdk_strtol(optarg, 10);
15789495464Spaul luse 		break;
1589f51cf32Spaul luse 	case 'o':
1599f51cf32Spaul luse 		g_xfer_size_bytes = spdk_strtol(optarg, 10);
1609f51cf32Spaul luse 		break;
161b9218b7aSpaul luse 	case 'P':
162b9218b7aSpaul luse 		g_fail_percent_goal = spdk_strtol(optarg, 10);
163b9218b7aSpaul luse 		break;
1649f51cf32Spaul luse 	case 'q':
1659f51cf32Spaul luse 		g_queue_depth = spdk_strtol(optarg, 10);
1669f51cf32Spaul luse 		break;
167e69375bfSpaul luse 	case 's':
168e69375bfSpaul luse 		g_crc32c_seed = spdk_strtol(optarg, 10);
169e69375bfSpaul luse 		break;
1709f51cf32Spaul luse 	case 't':
1719f51cf32Spaul luse 		g_time_in_sec = spdk_strtol(optarg, 10);
1729f51cf32Spaul luse 		break;
1739f51cf32Spaul luse 	case 'y':
1749f51cf32Spaul luse 		g_verify = true;
1759f51cf32Spaul luse 		break;
1762a0c66d0Spaul luse 	case 'w':
1772a0c66d0Spaul luse 		g_workload_type = optarg;
178514be889Spaul luse 		if (!strcmp(g_workload_type, "copy")) {
179514be889Spaul luse 			g_workload_selection = ACCEL_COPY;
180514be889Spaul luse 		} else if (!strcmp(g_workload_type, "fill")) {
181514be889Spaul luse 			g_workload_selection = ACCEL_FILL;
182e69375bfSpaul luse 		} else if (!strcmp(g_workload_type, "crc32c")) {
183e69375bfSpaul luse 			g_workload_selection = ACCEL_CRC32C;
184b9218b7aSpaul luse 		} else if (!strcmp(g_workload_type, "compare")) {
185b9218b7aSpaul luse 			g_workload_selection = ACCEL_COMPARE;
1860ef079c6Spaul luse 		} else if (!strcmp(g_workload_type, "dualcast")) {
1870ef079c6Spaul luse 			g_workload_selection = ACCEL_DUALCAST;
188514be889Spaul luse 		}
1892a0c66d0Spaul luse 		break;
1909f51cf32Spaul luse 	default:
1919f51cf32Spaul luse 		usage();
1929f51cf32Spaul luse 		return 1;
1939f51cf32Spaul luse 	}
1949f51cf32Spaul luse 	return 0;
1959f51cf32Spaul luse }
1969f51cf32Spaul luse 
1979f51cf32Spaul luse static void
1989f51cf32Spaul luse unregister_worker(void *arg1)
1999f51cf32Spaul luse {
2009f51cf32Spaul luse 	struct worker_thread *worker = arg1;
2019f51cf32Spaul luse 
202ac9a1a83Spaul luse 	free(worker->task_base);
203f17e6705Spaul luse 	free(worker->batch_base);
2049f51cf32Spaul luse 	spdk_put_io_channel(worker->ch);
2059f51cf32Spaul luse 	pthread_mutex_lock(&g_workers_lock);
2069f51cf32Spaul luse 	assert(g_num_workers >= 1);
2079f51cf32Spaul luse 	if (--g_num_workers == 0) {
2089f51cf32Spaul luse 		pthread_mutex_unlock(&g_workers_lock);
2099f51cf32Spaul luse 		spdk_app_stop(0);
2109f51cf32Spaul luse 	}
2119f51cf32Spaul luse 	pthread_mutex_unlock(&g_workers_lock);
2129f51cf32Spaul luse }
2139f51cf32Spaul luse 
2148da995c4Spaul luse static int
2158da995c4Spaul luse _get_task_data_bufs(struct ap_task *task)
2168da995c4Spaul luse {
2178da995c4Spaul luse 	uint32_t align = 0;
2188da995c4Spaul luse 
2198da995c4Spaul luse 	/* For dualcast, the DSA HW requires 4K alignment on destination addresses but
2208da995c4Spaul luse 	 * we do this for all engines to keep it simple.
2218da995c4Spaul luse 	 */
2228da995c4Spaul luse 	if (g_workload_selection == ACCEL_DUALCAST) {
2238da995c4Spaul luse 		align = ALIGN_4K;
2248da995c4Spaul luse 	}
2258da995c4Spaul luse 
2268da995c4Spaul luse 	task->src = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL);
2278da995c4Spaul luse 	if (task->src == NULL) {
2288da995c4Spaul luse 		fprintf(stderr, "Unable to alloc src buffer\n");
2298da995c4Spaul luse 		return -ENOMEM;
2308da995c4Spaul luse 	}
2318da995c4Spaul luse 	memset(task->src, DATA_PATTERN, g_xfer_size_bytes);
2328da995c4Spaul luse 
2338da995c4Spaul luse 	task->dst = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL);
2348da995c4Spaul luse 	if (task->dst == NULL) {
2358da995c4Spaul luse 		fprintf(stderr, "Unable to alloc dst buffer\n");
2368da995c4Spaul luse 		return -ENOMEM;
2378da995c4Spaul luse 	}
2388da995c4Spaul luse 
2398da995c4Spaul luse 	/* For compare we want the buffers to match, otherwise not. */
2408da995c4Spaul luse 	if (g_workload_selection == ACCEL_COMPARE) {
2418da995c4Spaul luse 		memset(task->dst, DATA_PATTERN, g_xfer_size_bytes);
2428da995c4Spaul luse 	} else {
2438da995c4Spaul luse 		memset(task->dst, ~DATA_PATTERN, g_xfer_size_bytes);
2448da995c4Spaul luse 	}
2458da995c4Spaul luse 
2468da995c4Spaul luse 	/* For fill, set the entire src buffer so we can check if verify is enabled. */
2478da995c4Spaul luse 	if (g_workload_selection == ACCEL_FILL) {
2488da995c4Spaul luse 		memset(task->src, g_fill_pattern, g_xfer_size_bytes);
2498da995c4Spaul luse 	}
2508da995c4Spaul luse 
2518da995c4Spaul luse 	if (g_workload_selection == ACCEL_DUALCAST) {
2528da995c4Spaul luse 		task->dst2 = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL);
2538da995c4Spaul luse 		if (task->dst2 == NULL) {
2548da995c4Spaul luse 			fprintf(stderr, "Unable to alloc dst buffer\n");
2558da995c4Spaul luse 			return -ENOMEM;
2568da995c4Spaul luse 		}
2578da995c4Spaul luse 		memset(task->dst2, ~DATA_PATTERN, g_xfer_size_bytes);
2588da995c4Spaul luse 	}
2598da995c4Spaul luse 
2608da995c4Spaul luse 	return 0;
2618da995c4Spaul luse }
2628da995c4Spaul luse 
263ac9a1a83Spaul luse inline static struct ap_task *
264ac9a1a83Spaul luse _get_task(struct worker_thread *worker)
265ac9a1a83Spaul luse {
266ac9a1a83Spaul luse 	struct ap_task *task;
267ac9a1a83Spaul luse 
268ac9a1a83Spaul luse 	if (!TAILQ_EMPTY(&worker->tasks_pool)) {
269ac9a1a83Spaul luse 		task = TAILQ_FIRST(&worker->tasks_pool);
270ac9a1a83Spaul luse 		TAILQ_REMOVE(&worker->tasks_pool, task, link);
271ac9a1a83Spaul luse 	} else {
272ac9a1a83Spaul luse 		fprintf(stderr, "Unable to get ap_task\n");
273ac9a1a83Spaul luse 		return NULL;
274ac9a1a83Spaul luse 	}
275ac9a1a83Spaul luse 
276ac9a1a83Spaul luse 	task->worker = worker;
277ac9a1a83Spaul luse 	task->worker->current_queue_depth++;
278ac9a1a83Spaul luse 	return task;
279ac9a1a83Spaul luse }
280ac9a1a83Spaul luse 
281f17e6705Spaul luse /* Submit one operation using the same ap task that just completed. */
2829f51cf32Spaul luse static void
283ac9a1a83Spaul luse _submit_single(struct worker_thread *worker, struct ap_task *task)
2849f51cf32Spaul luse {
285b9218b7aSpaul luse 	int random_num;
28640ec8e97Spaul luse 	int rc = 0;
2879f51cf32Spaul luse 
2889f51cf32Spaul luse 	assert(worker);
2899f51cf32Spaul luse 
290e69375bfSpaul luse 	switch (g_workload_selection) {
291e69375bfSpaul luse 	case ACCEL_COPY:
292e8463f87Spaul luse 		rc = spdk_accel_submit_copy(worker->ch, task->dst, task->src,
293e8463f87Spaul luse 					    g_xfer_size_bytes, accel_done, task);
294e69375bfSpaul luse 		break;
295e69375bfSpaul luse 	case ACCEL_FILL:
2962a0c66d0Spaul luse 		/* For fill use the first byte of the task->dst buffer */
297ee7e31f9Spaul luse 		rc = spdk_accel_submit_fill(worker->ch, task->dst, *(uint8_t *)task->src,
298e8463f87Spaul luse 					    g_xfer_size_bytes, accel_done, task);
299e69375bfSpaul luse 		break;
300e69375bfSpaul luse 	case ACCEL_CRC32C:
301e8463f87Spaul luse 		rc = spdk_accel_submit_crc32c(worker->ch, (uint32_t *)task->dst,
302e8463f87Spaul luse 					      task->src, g_crc32c_seed,
303e8463f87Spaul luse 					      g_xfer_size_bytes, accel_done, task);
304e69375bfSpaul luse 		break;
305b9218b7aSpaul luse 	case ACCEL_COMPARE:
306b9218b7aSpaul luse 		random_num = rand() % 100;
307b9218b7aSpaul luse 		if (random_num < g_fail_percent_goal) {
308b9218b7aSpaul luse 			task->expected_status = -EILSEQ;
309b9218b7aSpaul luse 			*(uint8_t *)task->dst = ~DATA_PATTERN;
310b9218b7aSpaul luse 		} else {
311b9218b7aSpaul luse 			task->expected_status = 0;
312b9218b7aSpaul luse 			*(uint8_t *)task->dst = DATA_PATTERN;
313b9218b7aSpaul luse 		}
314ee7e31f9Spaul luse 		rc = spdk_accel_submit_compare(worker->ch, task->dst, task->src,
315e8463f87Spaul luse 					       g_xfer_size_bytes, accel_done, task);
316b9218b7aSpaul luse 		break;
3170ef079c6Spaul luse 	case ACCEL_DUALCAST:
318ee7e31f9Spaul luse 		rc = spdk_accel_submit_dualcast(worker->ch, task->dst, task->dst2,
319e8463f87Spaul luse 						task->src, g_xfer_size_bytes, accel_done, task);
3200ef079c6Spaul luse 		break;
321e69375bfSpaul luse 	default:
3222a0c66d0Spaul luse 		assert(false);
323e69375bfSpaul luse 		break;
324e69375bfSpaul luse 
3252a0c66d0Spaul luse 	}
32640ec8e97Spaul luse 
32740ec8e97Spaul luse 	if (rc) {
328e8463f87Spaul luse 		accel_done(task, rc);
32940ec8e97Spaul luse 	}
3309f51cf32Spaul luse }
3319f51cf32Spaul luse 
332fab40895Spaul luse static int
333f17e6705Spaul luse _batch_prep_cmd(struct worker_thread *worker, struct ap_task *task,
334f17e6705Spaul luse 		struct accel_batch *worker_batch)
335fab40895Spaul luse {
336f17e6705Spaul luse 	struct spdk_accel_batch *batch = worker_batch->batch;
337fab40895Spaul luse 	int rc = 0;
338fab40895Spaul luse 
339f17e6705Spaul luse 	worker_batch->cmd_count++;
340f17e6705Spaul luse 	assert(worker_batch->cmd_count <= g_ops_per_batch);
341f17e6705Spaul luse 
342fab40895Spaul luse 	switch (g_workload_selection) {
343fab40895Spaul luse 	case ACCEL_COPY:
344fab40895Spaul luse 		rc = spdk_accel_batch_prep_copy(worker->ch, batch, task->dst,
345fab40895Spaul luse 						task->src, g_xfer_size_bytes, accel_done, task);
346fab40895Spaul luse 		break;
347fab40895Spaul luse 	case ACCEL_DUALCAST:
348fab40895Spaul luse 		rc = spdk_accel_batch_prep_dualcast(worker->ch, batch, task->dst, task->dst2,
349fab40895Spaul luse 						    task->src, g_xfer_size_bytes, accel_done, task);
350fab40895Spaul luse 		break;
351fab40895Spaul luse 	case ACCEL_COMPARE:
352fab40895Spaul luse 		rc = spdk_accel_batch_prep_compare(worker->ch, batch, task->dst, task->src,
353fab40895Spaul luse 						   g_xfer_size_bytes, accel_done, task);
354fab40895Spaul luse 		break;
355fab40895Spaul luse 	case ACCEL_FILL:
356fab40895Spaul luse 		rc = spdk_accel_batch_prep_fill(worker->ch, batch, task->dst,
357fab40895Spaul luse 						*(uint8_t *)task->src,
358fab40895Spaul luse 						g_xfer_size_bytes, accel_done, task);
359fab40895Spaul luse 		break;
360fab40895Spaul luse 	case ACCEL_CRC32C:
361fab40895Spaul luse 		rc = spdk_accel_batch_prep_crc32c(worker->ch, batch, (uint32_t *)task->dst,
362fab40895Spaul luse 						  task->src, g_crc32c_seed, g_xfer_size_bytes, accel_done, task);
363fab40895Spaul luse 		break;
364fab40895Spaul luse 	default:
365fab40895Spaul luse 		assert(false);
366fab40895Spaul luse 		break;
367fab40895Spaul luse 	}
368fab40895Spaul luse 
369fab40895Spaul luse 	return rc;
370fab40895Spaul luse }
371fab40895Spaul luse 
3729f51cf32Spaul luse static void
373ac9a1a83Spaul luse _free_task(struct ap_task *task)
374ac9a1a83Spaul luse {
375ac9a1a83Spaul luse 	spdk_dma_free(task->src);
376ac9a1a83Spaul luse 	spdk_dma_free(task->dst);
377ac9a1a83Spaul luse 	if (g_workload_selection == ACCEL_DUALCAST) {
378ac9a1a83Spaul luse 		spdk_dma_free(task->dst2);
379ac9a1a83Spaul luse 	}
380ac9a1a83Spaul luse }
381ac9a1a83Spaul luse 
382f17e6705Spaul luse static void _batch_done(void *cb_arg);
383f17e6705Spaul luse static void
384f17e6705Spaul luse _build_batch(struct worker_thread *worker, struct ap_task *task)
385f17e6705Spaul luse {
386f17e6705Spaul luse 	struct accel_batch *worker_batch = NULL;
387f17e6705Spaul luse 	int rc;
388f17e6705Spaul luse 
389f17e6705Spaul luse 	assert(!TAILQ_EMPTY(&worker->in_prep_batches));
390f17e6705Spaul luse 
391f17e6705Spaul luse 	worker_batch = TAILQ_FIRST(&worker->in_prep_batches);
392f17e6705Spaul luse 
393f17e6705Spaul luse 	/* If an accel batch hasn't been created yet do so now. */
394f17e6705Spaul luse 	if (worker_batch->batch == NULL) {
395f17e6705Spaul luse 		worker_batch->batch = spdk_accel_batch_create(worker->ch);
396f17e6705Spaul luse 		if (worker_batch->batch == NULL) {
397f17e6705Spaul luse 			fprintf(stderr, "error unable to create new batch\n");
398f17e6705Spaul luse 			return;
399f17e6705Spaul luse 		}
400f17e6705Spaul luse 	}
401f17e6705Spaul luse 
402f17e6705Spaul luse 	/* Prep the command re-using the last completed command's task */
403f17e6705Spaul luse 	rc = _batch_prep_cmd(worker, task, worker_batch);
404f17e6705Spaul luse 	if (rc) {
405f17e6705Spaul luse 		fprintf(stderr, "error preping command for batch\n");
406f17e6705Spaul luse 		goto error;
407f17e6705Spaul luse 	}
408f17e6705Spaul luse 
409f17e6705Spaul luse 	/* If this batch is full move it to the to_submit list so it gets
410f17e6705Spaul luse 	 * submitted as batches complete.
411f17e6705Spaul luse 	 */
412f17e6705Spaul luse 	if (worker_batch->cmd_count == g_ops_per_batch) {
413f17e6705Spaul luse 		TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link);
414f17e6705Spaul luse 		TAILQ_INSERT_TAIL(&worker->to_submit_batches, worker_batch, link);
415f17e6705Spaul luse 	}
416f17e6705Spaul luse 
417f17e6705Spaul luse 	return;
418f17e6705Spaul luse error:
419f17e6705Spaul luse 	spdk_accel_batch_cancel(worker->ch, worker_batch->batch);
420f17e6705Spaul luse 
421f17e6705Spaul luse }
422f17e6705Spaul luse 
423f17e6705Spaul luse static void batch_done(void *cb_arg, int status);
424f17e6705Spaul luse static void
425f17e6705Spaul luse _drain_batch(struct worker_thread *worker)
426f17e6705Spaul luse {
427f17e6705Spaul luse 	struct accel_batch *worker_batch, *tmp;
428f17e6705Spaul luse 	int rc;
429f17e6705Spaul luse 
430f17e6705Spaul luse 	/* submit any batches that were being built up. */
431f17e6705Spaul luse 	TAILQ_FOREACH_SAFE(worker_batch, &worker->in_prep_batches, link, tmp) {
432f17e6705Spaul luse 		if (worker_batch->cmd_count == 0) {
433f17e6705Spaul luse 			continue;
434f17e6705Spaul luse 		}
435f17e6705Spaul luse 		worker->current_queue_depth += worker_batch->cmd_count + 1;
436f17e6705Spaul luse 
437f17e6705Spaul luse 		TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link);
438f17e6705Spaul luse 		TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link);
439f17e6705Spaul luse 		rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch);
440f17e6705Spaul luse 		if (rc == 0) {
441f17e6705Spaul luse 			worker_batch->cmd_count = 0;
442f17e6705Spaul luse 		} else {
443f17e6705Spaul luse 			fprintf(stderr, "error sending final batch\n");
444f17e6705Spaul luse 			worker->current_queue_depth -= worker_batch->cmd_count + 1;
445f17e6705Spaul luse 			break;
446f17e6705Spaul luse 		}
447f17e6705Spaul luse 	}
448f17e6705Spaul luse }
449f17e6705Spaul luse 
450f17e6705Spaul luse static void
451f17e6705Spaul luse _batch_done(void *cb_arg)
452f17e6705Spaul luse {
453f17e6705Spaul luse 	struct accel_batch *worker_batch = (struct accel_batch *)cb_arg;
454f17e6705Spaul luse 	struct worker_thread *worker = worker_batch->worker;
455f17e6705Spaul luse 	int rc;
456f17e6705Spaul luse 
457f17e6705Spaul luse 	assert(TAILQ_EMPTY(&worker->in_use_batches) == 0);
458f17e6705Spaul luse 
459f17e6705Spaul luse 	if (worker_batch->status) {
460f17e6705Spaul luse 		SPDK_ERRLOG("error %d\n", worker_batch->status);
461f17e6705Spaul luse 	}
462f17e6705Spaul luse 
463f17e6705Spaul luse 	worker->current_queue_depth--;
464f17e6705Spaul luse 	TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link);
465f17e6705Spaul luse 	TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link);
466f17e6705Spaul luse 	worker_batch->batch = NULL;
467f17e6705Spaul luse 	worker_batch->cmd_count = 0;
468f17e6705Spaul luse 
469f17e6705Spaul luse 	if (!worker->is_draining) {
470f17e6705Spaul luse 		worker_batch = TAILQ_FIRST(&worker->to_submit_batches);
471f17e6705Spaul luse 		if (worker_batch != NULL) {
472f17e6705Spaul luse 
473f17e6705Spaul luse 			assert(worker_batch->cmd_count == g_ops_per_batch);
474f17e6705Spaul luse 
475f17e6705Spaul luse 			/* Add one for the batch command itself. */
476f17e6705Spaul luse 			worker->current_queue_depth += g_ops_per_batch + 1;
477f17e6705Spaul luse 			TAILQ_REMOVE(&worker->to_submit_batches, worker_batch, link);
478f17e6705Spaul luse 			TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link);
479f17e6705Spaul luse 
480f17e6705Spaul luse 			rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch);
481f17e6705Spaul luse 			if (rc) {
482f17e6705Spaul luse 				fprintf(stderr, "error ending batch\n");
483f17e6705Spaul luse 				worker->current_queue_depth -= g_ops_per_batch + 1;
484f17e6705Spaul luse 				return;
485f17e6705Spaul luse 			}
486f17e6705Spaul luse 		}
487f17e6705Spaul luse 	} else {
488f17e6705Spaul luse 		_drain_batch(worker);
489f17e6705Spaul luse 	}
490f17e6705Spaul luse }
491f17e6705Spaul luse 
492ac9a1a83Spaul luse static void
493fab40895Spaul luse batch_done(void *cb_arg, int status)
494fab40895Spaul luse {
495f17e6705Spaul luse 	struct accel_batch *worker_batch = (struct accel_batch *)cb_arg;
496fab40895Spaul luse 
497f17e6705Spaul luse 	assert(worker_batch->worker);
498f17e6705Spaul luse 
499f17e6705Spaul luse 	worker_batch->status = status;
500f17e6705Spaul luse 	spdk_thread_send_msg(worker_batch->worker->thread, _batch_done, worker_batch);
501fab40895Spaul luse }
502fab40895Spaul luse 
503fab40895Spaul luse static void
5049f51cf32Spaul luse _accel_done(void *arg1)
5059f51cf32Spaul luse {
5069f51cf32Spaul luse 	struct ap_task *task = arg1;
5079f51cf32Spaul luse 	struct worker_thread *worker = task->worker;
508e69375bfSpaul luse 	uint32_t sw_crc32c;
5099f51cf32Spaul luse 
5109f51cf32Spaul luse 	assert(worker);
5119f51cf32Spaul luse 	assert(worker->current_queue_depth > 0);
5129f51cf32Spaul luse 
513b9218b7aSpaul luse 	if (g_verify && task->status == 0) {
514b9218b7aSpaul luse 		switch (g_workload_selection) {
515b9218b7aSpaul luse 		case ACCEL_CRC32C:
516e69375bfSpaul luse 			/* calculate sw CRC-32C and compare to sw aceel result. */
517e69375bfSpaul luse 			sw_crc32c = spdk_crc32c_update(task->src, g_xfer_size_bytes, ~g_crc32c_seed);
518e69375bfSpaul luse 			if (*(uint32_t *)task->dst != sw_crc32c) {
519e69375bfSpaul luse 				SPDK_NOTICELOG("CRC-32C miscompare\n");
520e69375bfSpaul luse 				worker->xfer_failed++;
521e69375bfSpaul luse 			}
522b9218b7aSpaul luse 			break;
523b9218b7aSpaul luse 		case ACCEL_COPY:
524b9218b7aSpaul luse 			if (memcmp(task->src, task->dst, g_xfer_size_bytes)) {
5259f51cf32Spaul luse 				SPDK_NOTICELOG("Data miscompare\n");
5269f51cf32Spaul luse 				worker->xfer_failed++;
527b9218b7aSpaul luse 			}
528b9218b7aSpaul luse 			break;
5290ef079c6Spaul luse 		case ACCEL_DUALCAST:
5300ef079c6Spaul luse 			if (memcmp(task->src, task->dst, g_xfer_size_bytes)) {
5310ef079c6Spaul luse 				SPDK_NOTICELOG("Data miscompare, first destination\n");
5320ef079c6Spaul luse 				worker->xfer_failed++;
5330ef079c6Spaul luse 			}
5340ef079c6Spaul luse 			if (memcmp(task->src, task->dst2, g_xfer_size_bytes)) {
5350ef079c6Spaul luse 				SPDK_NOTICELOG("Data miscompare, second destination\n");
5360ef079c6Spaul luse 				worker->xfer_failed++;
5370ef079c6Spaul luse 			}
5380ef079c6Spaul luse 			break;
539d207237fSpaul luse 		case ACCEL_FILL:
540d207237fSpaul luse 			if (memcmp(task->dst, task->src, g_xfer_size_bytes)) {
541d207237fSpaul luse 				SPDK_NOTICELOG("Data miscompare\n");
542d207237fSpaul luse 				worker->xfer_failed++;
543d207237fSpaul luse 			}
544d207237fSpaul luse 			break;
5458cee297cSpaul luse 		case ACCEL_COMPARE:
5468cee297cSpaul luse 			break;
547b9218b7aSpaul luse 		default:
548b9218b7aSpaul luse 			assert(false);
549b9218b7aSpaul luse 			break;
5509f51cf32Spaul luse 		}
5519f51cf32Spaul luse 	}
552b9218b7aSpaul luse 
553b9218b7aSpaul luse 	if (task->expected_status == -EILSEQ) {
554b9218b7aSpaul luse 		assert(task->status != 0);
555b9218b7aSpaul luse 		worker->injected_miscompares++;
556b9218b7aSpaul luse 	} else if (task->status) {
557f17e6705Spaul luse 		/* Expected to pass but the accel engine reported an error (ex: COMPARE operation). */
558b9218b7aSpaul luse 		worker->xfer_failed++;
559b9218b7aSpaul luse 	}
560b9218b7aSpaul luse 
5619f51cf32Spaul luse 	worker->xfer_completed++;
5629f51cf32Spaul luse 	worker->current_queue_depth--;
5639f51cf32Spaul luse 
56440ec8e97Spaul luse 	if (!worker->is_draining) {
565f17e6705Spaul luse 		if (g_ops_per_batch == 0) {
5669f51cf32Spaul luse 			_submit_single(worker, task);
567ac9a1a83Spaul luse 			worker->current_queue_depth++;
568f17e6705Spaul luse 		} else {
569f17e6705Spaul luse 			_build_batch(worker, task);
5709f51cf32Spaul luse 		}
571f17e6705Spaul luse 	} else if (g_ops_per_batch > 0) {
572f17e6705Spaul luse 		_drain_batch(worker);
573*b34883e0SZiye Yang 	} else {
574*b34883e0SZiye Yang 		TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link);
575f17e6705Spaul luse 	}
5769f51cf32Spaul luse }
5779f51cf32Spaul luse 
5789f51cf32Spaul luse static int
5799f51cf32Spaul luse dump_result(void)
5809f51cf32Spaul luse {
5819f51cf32Spaul luse 	uint64_t total_completed = 0;
5829f51cf32Spaul luse 	uint64_t total_failed = 0;
583b9218b7aSpaul luse 	uint64_t total_miscompared = 0;
5849f51cf32Spaul luse 	uint64_t total_xfer_per_sec, total_bw_in_MiBps;
5859f51cf32Spaul luse 	struct worker_thread *worker = g_workers;
5869f51cf32Spaul luse 
587b9218b7aSpaul luse 	printf("\nCore           Transfers     Bandwidth     Failed     Miscompares\n");
588b9218b7aSpaul luse 	printf("-----------------------------------------------------------------\n");
5899f51cf32Spaul luse 	while (worker != NULL) {
5909f51cf32Spaul luse 
5919f51cf32Spaul luse 		uint64_t xfer_per_sec = worker->xfer_completed / g_time_in_sec;
5929f51cf32Spaul luse 		uint64_t bw_in_MiBps = (worker->xfer_completed * g_xfer_size_bytes) /
5939f51cf32Spaul luse 				       (g_time_in_sec * 1024 * 1024);
5949f51cf32Spaul luse 
5959f51cf32Spaul luse 		total_completed += worker->xfer_completed;
5969f51cf32Spaul luse 		total_failed += worker->xfer_failed;
597b9218b7aSpaul luse 		total_miscompared += worker->injected_miscompares;
5989f51cf32Spaul luse 
5999f51cf32Spaul luse 		if (xfer_per_sec) {
600b9218b7aSpaul luse 			printf("%10d%12" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 " %11" PRIu64 "\n",
6019f51cf32Spaul luse 			       worker->core, xfer_per_sec,
602b9218b7aSpaul luse 			       bw_in_MiBps, worker->xfer_failed, worker->injected_miscompares);
6039f51cf32Spaul luse 		}
6049f51cf32Spaul luse 
6059f51cf32Spaul luse 		worker = worker->next;
6069f51cf32Spaul luse 	}
6079f51cf32Spaul luse 
6089f51cf32Spaul luse 	total_xfer_per_sec = total_completed / g_time_in_sec;
6099f51cf32Spaul luse 	total_bw_in_MiBps = (total_completed * g_xfer_size_bytes) /
6109f51cf32Spaul luse 			    (g_time_in_sec * 1024 * 1024);
6119f51cf32Spaul luse 
612b9218b7aSpaul luse 	printf("==================================================================\n");
613b9218b7aSpaul luse 	printf("Total:%16" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 " %11" PRIu64"\n\n",
614b9218b7aSpaul luse 	       total_xfer_per_sec, total_bw_in_MiBps, total_failed, total_miscompared);
6159f51cf32Spaul luse 
6169f51cf32Spaul luse 	return total_failed ? 1 : 0;
6179f51cf32Spaul luse }
6189f51cf32Spaul luse 
6199f51cf32Spaul luse static int
6209f51cf32Spaul luse _check_draining(void *arg)
6219f51cf32Spaul luse {
6229f51cf32Spaul luse 	struct worker_thread *worker = arg;
623ac9a1a83Spaul luse 	struct ap_task *task;
6249f51cf32Spaul luse 
6259f51cf32Spaul luse 	assert(worker);
6269f51cf32Spaul luse 
6279f51cf32Spaul luse 	if (worker->current_queue_depth == 0) {
628ac9a1a83Spaul luse 		while ((task = TAILQ_FIRST(&worker->tasks_pool))) {
629ac9a1a83Spaul luse 			TAILQ_REMOVE(&worker->tasks_pool, task, link);
630ac9a1a83Spaul luse 			_free_task(task);
631ac9a1a83Spaul luse 		}
6329f51cf32Spaul luse 		spdk_poller_unregister(&worker->is_draining_poller);
6339f51cf32Spaul luse 		unregister_worker(worker);
6349f51cf32Spaul luse 	}
6359f51cf32Spaul luse 
6369f51cf32Spaul luse 	return -1;
6379f51cf32Spaul luse }
6389f51cf32Spaul luse 
6399f51cf32Spaul luse static int
6409f51cf32Spaul luse _worker_stop(void *arg)
6419f51cf32Spaul luse {
6429f51cf32Spaul luse 	struct worker_thread *worker = arg;
6439f51cf32Spaul luse 
6449f51cf32Spaul luse 	assert(worker);
6459f51cf32Spaul luse 
6469f51cf32Spaul luse 	spdk_poller_unregister(&worker->stop_poller);
6479f51cf32Spaul luse 
6489f51cf32Spaul luse 	/* now let the worker drain and check it's outstanding IO with a poller */
6499f51cf32Spaul luse 	worker->is_draining = true;
650ab0bc5c2SShuhei Matsumoto 	worker->is_draining_poller = SPDK_POLLER_REGISTER(_check_draining, worker, 0);
6519f51cf32Spaul luse 
6529f51cf32Spaul luse 	return 0;
6539f51cf32Spaul luse }
6549f51cf32Spaul luse 
6559f51cf32Spaul luse static void
6569f51cf32Spaul luse _init_thread_done(void *ctx)
6579f51cf32Spaul luse {
6589f51cf32Spaul luse }
6599f51cf32Spaul luse 
660a34fc12bSpaul luse static void
661a34fc12bSpaul luse _init_thread(void *arg1)
662a34fc12bSpaul luse {
663a34fc12bSpaul luse 	struct worker_thread *worker;
664a34fc12bSpaul luse 	struct ap_task *task;
665f17e6705Spaul luse 	int i, rc, num_batches;
666f17e6705Spaul luse 	int max_per_batch;
667a34fc12bSpaul luse 	int remaining = g_queue_depth;
668f17e6705Spaul luse 	int num_tasks = g_queue_depth;
669f17e6705Spaul luse 	struct accel_batch *tmp;
670f17e6705Spaul luse 	struct accel_batch *worker_batch = NULL;
671a34fc12bSpaul luse 
672a34fc12bSpaul luse 	worker = calloc(1, sizeof(*worker));
673a34fc12bSpaul luse 	if (worker == NULL) {
674a34fc12bSpaul luse 		fprintf(stderr, "Unable to allocate worker\n");
675a34fc12bSpaul luse 		return;
676a34fc12bSpaul luse 	}
677a34fc12bSpaul luse 
6789f51cf32Spaul luse 	worker->core = spdk_env_get_current_core();
6799f51cf32Spaul luse 	worker->thread = spdk_get_thread();
6809f51cf32Spaul luse 	worker->next = g_workers;
6819f51cf32Spaul luse 	worker->ch = spdk_accel_engine_get_io_channel();
682b9218b7aSpaul luse 
683f17e6705Spaul luse 	TAILQ_INIT(&worker->tasks_pool);
684f17e6705Spaul luse 
685f17e6705Spaul luse 	if (g_ops_per_batch > 0) {
686f17e6705Spaul luse 
6870cecfcb1Spaul luse 		max_per_batch = spdk_accel_batch_get_max(worker->ch);
6880cecfcb1Spaul luse 		assert(max_per_batch > 0);
6890cecfcb1Spaul luse 
690f17e6705Spaul luse 		if (g_ops_per_batch > max_per_batch) {
691f17e6705Spaul luse 			fprintf(stderr, "Reducing requested batch amount to max supported of %d\n", max_per_batch);
692f17e6705Spaul luse 			g_ops_per_batch = max_per_batch;
693f17e6705Spaul luse 		}
694f17e6705Spaul luse 
695f17e6705Spaul luse 		if (g_ops_per_batch > g_queue_depth) {
696f17e6705Spaul luse 			fprintf(stderr, "Batch amount > queue depth, resetting to %d\n", g_queue_depth);
697f17e6705Spaul luse 			g_ops_per_batch = g_queue_depth;
698f17e6705Spaul luse 		}
699f17e6705Spaul luse 
700f17e6705Spaul luse 		TAILQ_INIT(&worker->in_prep_batches);
701f17e6705Spaul luse 		TAILQ_INIT(&worker->to_submit_batches);
702f17e6705Spaul luse 		TAILQ_INIT(&worker->in_use_batches);
703f17e6705Spaul luse 
704f17e6705Spaul luse 		/* A worker_batch will live on one of 3 lists:
705f17e6705Spaul luse 		 * IN_PREP: as individual IOs complete new ones are built on on a
706f17e6705Spaul luse 		 *          worker_batch on this list until it reaches g_ops_per_batch.
707f17e6705Spaul luse 		 * TO_SUBMIT: as batches are built up on IO completion they are moved
708f17e6705Spaul luse 		 *	      to this list once they are full.  This list is used in
709f17e6705Spaul luse 		 *	      batch completion to start new batches.
710f17e6705Spaul luse 		 * IN_USE: the worker_batch is outstanding and will be moved to in prep
711f17e6705Spaul luse 		 *         list when the batch is completed.
712f17e6705Spaul luse 		 *
713f17e6705Spaul luse 		 * So we need enough to cover Q depth loading and then one to replace
714f17e6705Spaul luse 		 * each one of those and for when everything is outstanding there needs
715f17e6705Spaul luse 		 * to be one extra batch to build up while the last batch is completing
716f17e6705Spaul luse 		 * IO but before it's completed the batch command.
717f17e6705Spaul luse 		 */
718f17e6705Spaul luse 		num_batches = (g_queue_depth / g_ops_per_batch * 2) + 1;
719f17e6705Spaul luse 		worker->batch_base = calloc(num_batches, sizeof(struct accel_batch));
720f17e6705Spaul luse 		worker_batch = worker->batch_base;
721f17e6705Spaul luse 		for (i = 0; i < num_batches; i++) {
722f17e6705Spaul luse 			worker_batch->worker = worker;
723f17e6705Spaul luse 			TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link);
724f17e6705Spaul luse 			worker_batch++;
725f17e6705Spaul luse 		}
726f17e6705Spaul luse 	}
727f17e6705Spaul luse 
728ac9a1a83Spaul luse 	worker->task_base = calloc(num_tasks, sizeof(struct ap_task));
729ac9a1a83Spaul luse 	if (worker->task_base == NULL) {
730ac9a1a83Spaul luse 		fprintf(stderr, "Could not allocate task base.\n");
731ac9a1a83Spaul luse 		goto error;
7320cecfcb1Spaul luse 	}
733ac9a1a83Spaul luse 
734ac9a1a83Spaul luse 	task = worker->task_base;
735ac9a1a83Spaul luse 	for (i = 0; i < num_tasks; i++) {
736ac9a1a83Spaul luse 		TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link);
737ac9a1a83Spaul luse 		if (_get_task_data_bufs(task)) {
738ac9a1a83Spaul luse 			fprintf(stderr, "Unable to get data bufs\n");
739ac9a1a83Spaul luse 			goto error;
740ac9a1a83Spaul luse 		}
741ac9a1a83Spaul luse 		task++;
7429f51cf32Spaul luse 	}
7439f51cf32Spaul luse 
7449f51cf32Spaul luse 	/* Register a poller that will stop the worker at time elapsed */
745ab0bc5c2SShuhei Matsumoto 	worker->stop_poller = SPDK_POLLER_REGISTER(_worker_stop, worker,
7469f51cf32Spaul luse 			      g_time_in_sec * 1000000ULL);
7479f51cf32Spaul luse 
7489f51cf32Spaul luse 	g_workers = worker;
7499f51cf32Spaul luse 	pthread_mutex_lock(&g_workers_lock);
7509f51cf32Spaul luse 	g_num_workers++;
7519f51cf32Spaul luse 	pthread_mutex_unlock(&g_workers_lock);
7529f51cf32Spaul luse 
753f17e6705Spaul luse 	/* If batching is enabled load up to the full Q depth before
754f17e6705Spaul luse 	 * processing any completions, then ping pong between two batches,
755f17e6705Spaul luse 	 * one processing and one being built up for when the other completes.
756a34fc12bSpaul luse 	 */
757f17e6705Spaul luse 	if (g_ops_per_batch > 0) {
758a34fc12bSpaul luse 		do {
759f17e6705Spaul luse 			worker_batch = TAILQ_FIRST(&worker->in_prep_batches);
760f17e6705Spaul luse 			if (worker_batch == NULL) {
761f17e6705Spaul luse 				goto error;
762f17e6705Spaul luse 			}
763f17e6705Spaul luse 
764f17e6705Spaul luse 			worker_batch->batch = spdk_accel_batch_create(worker->ch);
765f17e6705Spaul luse 			if (worker_batch->batch == NULL) {
766f17e6705Spaul luse 				raise(SIGINT);
767a34fc12bSpaul luse 				break;
768a34fc12bSpaul luse 			}
769a34fc12bSpaul luse 
770f17e6705Spaul luse 			for (i = 0; i < g_ops_per_batch; i++) {
771ac9a1a83Spaul luse 				task = _get_task(worker);
772ac9a1a83Spaul luse 				if (task == NULL) {
773a34fc12bSpaul luse 					goto error;
7749f51cf32Spaul luse 				}
775b9218b7aSpaul luse 
776f17e6705Spaul luse 				rc = _batch_prep_cmd(worker, task, worker_batch);
777a34fc12bSpaul luse 				if (rc) {
778a34fc12bSpaul luse 					fprintf(stderr, "error preping command\n");
779a34fc12bSpaul luse 					goto error;
780a34fc12bSpaul luse 				}
781a34fc12bSpaul luse 			}
782a34fc12bSpaul luse 
783f17e6705Spaul luse 			/* for the batch operation itself. */
784f17e6705Spaul luse 			task->worker->current_queue_depth++;
785f17e6705Spaul luse 			TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link);
786f17e6705Spaul luse 			TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link);
787f17e6705Spaul luse 
788f17e6705Spaul luse 			rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch);
789a34fc12bSpaul luse 			if (rc) {
790f17e6705Spaul luse 				fprintf(stderr, "error ending batch\n");
791a34fc12bSpaul luse 				goto error;
792a34fc12bSpaul luse 			}
793f17e6705Spaul luse 			assert(remaining >= g_ops_per_batch);
794f17e6705Spaul luse 			remaining -= g_ops_per_batch;
795f17e6705Spaul luse 		} while (remaining > 0);
796b9218b7aSpaul luse 	}
7970ef079c6Spaul luse 
798f17e6705Spaul luse 	/* Submit as singles when no batching is enabled or we ran out of batches. */
799a34fc12bSpaul luse 	for (i = 0; i < remaining; i++) {
800ac9a1a83Spaul luse 		task = _get_task(worker);
801ac9a1a83Spaul luse 		if (task == NULL) {
802a34fc12bSpaul luse 			goto error;
803b9218b7aSpaul luse 		}
804b9218b7aSpaul luse 
8059f51cf32Spaul luse 		_submit_single(worker, task);
8069f51cf32Spaul luse 	}
807a34fc12bSpaul luse 	return;
808a34fc12bSpaul luse error:
809f17e6705Spaul luse 	if (worker_batch && worker_batch->batch) {
810f17e6705Spaul luse 		TAILQ_FOREACH_SAFE(worker_batch, &worker->in_use_batches, link, tmp) {
811f17e6705Spaul luse 			spdk_accel_batch_cancel(worker->ch, worker_batch->batch);
812f17e6705Spaul luse 			TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link);
813f17e6705Spaul luse 		}
814f17e6705Spaul luse 	}
815ac9a1a83Spaul luse 	while ((task = TAILQ_FIRST(&worker->tasks_pool))) {
816ac9a1a83Spaul luse 		TAILQ_REMOVE(&worker->tasks_pool, task, link);
817ac9a1a83Spaul luse 		_free_task(task);
8180cecfcb1Spaul luse 	}
819f17e6705Spaul luse 	free(worker->batch_base);
820ac9a1a83Spaul luse 	free(worker->task_base);
821a34fc12bSpaul luse 	free(worker);
822a34fc12bSpaul luse 	spdk_app_stop(-1);
8239f51cf32Spaul luse }
8249f51cf32Spaul luse 
8259f51cf32Spaul luse static void
826e8463f87Spaul luse accel_done(void *cb_arg, int status)
8279f51cf32Spaul luse {
828e8463f87Spaul luse 	struct ap_task *task = (struct ap_task *)cb_arg;
8299f51cf32Spaul luse 	struct worker_thread *worker = task->worker;
8309f51cf32Spaul luse 
8319f51cf32Spaul luse 	assert(worker);
8329f51cf32Spaul luse 
833b9218b7aSpaul luse 	task->status = status;
8349f51cf32Spaul luse 	spdk_thread_send_msg(worker->thread, _accel_done, task);
8359f51cf32Spaul luse }
8369f51cf32Spaul luse 
8379f51cf32Spaul luse static void
8389f51cf32Spaul luse accel_perf_start(void *arg1)
8399f51cf32Spaul luse {
840514be889Spaul luse 	struct spdk_io_channel *accel_ch;
841514be889Spaul luse 
842514be889Spaul luse 	accel_ch = spdk_accel_engine_get_io_channel();
843a34fc12bSpaul luse 	g_capabilites = spdk_accel_get_capabilities(accel_ch);
844514be889Spaul luse 	spdk_put_io_channel(accel_ch);
845514be889Spaul luse 
846a34fc12bSpaul luse 	if ((g_capabilites & g_workload_selection) != g_workload_selection) {
847a7dfca5bSpaul luse 		SPDK_WARNLOG("The selected workload is not natively supported by the current engine\n");
848a7dfca5bSpaul luse 		SPDK_WARNLOG("The software engine will be used instead.\n\n");
849514be889Spaul luse 	}
850514be889Spaul luse 
8519f51cf32Spaul luse 	g_tsc_rate = spdk_get_ticks_hz();
8529f51cf32Spaul luse 	g_tsc_us_rate = g_tsc_rate / (1000 * 1000);
8539f51cf32Spaul luse 	g_tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate;
8549f51cf32Spaul luse 
8559f51cf32Spaul luse 	printf("Running for %d seconds...\n", g_time_in_sec);
8569f51cf32Spaul luse 	fflush(stdout);
8579f51cf32Spaul luse 
8589f51cf32Spaul luse 	spdk_for_each_thread(_init_thread, NULL, _init_thread_done);
8599f51cf32Spaul luse }
8609f51cf32Spaul luse 
8619f51cf32Spaul luse int
8629f51cf32Spaul luse main(int argc, char **argv)
8639f51cf32Spaul luse {
8649f51cf32Spaul luse 	struct spdk_app_opts opts = {};
8659f51cf32Spaul luse 	struct worker_thread *worker, *tmp;
8669f51cf32Spaul luse 	int rc = 0;
8679f51cf32Spaul luse 
8689f51cf32Spaul luse 	pthread_mutex_init(&g_workers_lock, NULL);
86948701bd9SZiye Yang 	spdk_app_opts_init(&opts, sizeof(opts));
8709f51cf32Spaul luse 	opts.reactor_mask = "0x1";
871f17e6705Spaul luse 	if (spdk_app_parse_args(argc, argv, &opts, "o:q:t:yw:P:f:b:", NULL, parse_args,
8721e2b38baSyidong0635 				usage) != SPDK_APP_PARSE_ARGS_SUCCESS) {
8739f51cf32Spaul luse 		rc = -1;
8749f51cf32Spaul luse 		goto cleanup;
8759f51cf32Spaul luse 	}
8769f51cf32Spaul luse 
877b9218b7aSpaul luse 	if ((g_workload_selection != ACCEL_COPY) &&
878b9218b7aSpaul luse 	    (g_workload_selection != ACCEL_FILL) &&
879b9218b7aSpaul luse 	    (g_workload_selection != ACCEL_CRC32C) &&
8800ef079c6Spaul luse 	    (g_workload_selection != ACCEL_COMPARE) &&
8810ef079c6Spaul luse 	    (g_workload_selection != ACCEL_DUALCAST)) {
8822a0c66d0Spaul luse 		usage();
8832a0c66d0Spaul luse 		rc = -1;
8842a0c66d0Spaul luse 		goto cleanup;
8852a0c66d0Spaul luse 	}
8862a0c66d0Spaul luse 
887f17e6705Spaul luse 	if (g_ops_per_batch > 0 && (g_queue_depth % g_ops_per_batch > 0)) {
888f17e6705Spaul luse 		fprintf(stdout, "batch size must be a multiple of queue depth\n");
889f17e6705Spaul luse 		usage();
890f17e6705Spaul luse 		rc = -1;
891f17e6705Spaul luse 		goto cleanup;
892f17e6705Spaul luse 	}
893f17e6705Spaul luse 
8949f51cf32Spaul luse 	dump_user_config(&opts);
8959f51cf32Spaul luse 	rc = spdk_app_start(&opts, accel_perf_start, NULL);
8969f51cf32Spaul luse 	if (rc) {
8979f51cf32Spaul luse 		SPDK_ERRLOG("ERROR starting application\n");
8989f51cf32Spaul luse 	} else {
8999f51cf32Spaul luse 		dump_result();
9009f51cf32Spaul luse 	}
9019f51cf32Spaul luse 
9029f51cf32Spaul luse 	pthread_mutex_destroy(&g_workers_lock);
9039f51cf32Spaul luse 
9049f51cf32Spaul luse 	worker = g_workers;
9059f51cf32Spaul luse 	while (worker) {
9069f51cf32Spaul luse 		tmp = worker->next;
9079f51cf32Spaul luse 		free(worker);
9089f51cf32Spaul luse 		worker = tmp;
9099f51cf32Spaul luse 	}
9109f51cf32Spaul luse cleanup:
9119f51cf32Spaul luse 	spdk_app_fini();
9129f51cf32Spaul luse 	return rc;
9139f51cf32Spaul luse }
914