xref: /spdk/examples/accel/perf/accel_perf.c (revision 0a6b70983da2708ac6f99794f55bd98df85b92bb)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2020 Intel Corporation.
39f51cf32Spaul luse  *   All rights reserved.
49f51cf32Spaul luse  */
59f51cf32Spaul luse 
69f51cf32Spaul luse #include "spdk/stdinc.h"
79f51cf32Spaul luse #include "spdk/thread.h"
89f51cf32Spaul luse #include "spdk/env.h"
99f51cf32Spaul luse #include "spdk/event.h"
109f51cf32Spaul luse #include "spdk/log.h"
119f51cf32Spaul luse #include "spdk/string.h"
12081f080aSBen Walker #include "spdk/accel.h"
13e69375bfSpaul luse #include "spdk/crc32.h"
140cecfcb1Spaul luse #include "spdk/util.h"
15a5d5ab27SArtur Paszkiewicz #include "spdk/xor.h"
169f51cf32Spaul luse 
17b9218b7aSpaul luse #define DATA_PATTERN 0x5a
180ef079c6Spaul luse #define ALIGN_4K 0x1000
196afbf3dbSpaul luse #define COMP_BUF_PAD_PERCENTAGE 1.1L
20b9218b7aSpaul luse 
219f51cf32Spaul luse static uint64_t	g_tsc_rate;
229f51cf32Spaul luse static uint64_t g_tsc_end;
239b189667Spaul luse static int g_rc;
249f51cf32Spaul luse static int g_xfer_size_bytes = 4096;
259f51cf32Spaul luse static int g_queue_depth = 32;
26e1bf63afSJim Harris /* g_allocate_depth indicates how many tasks we allocate per worker. It will
27e1bf63afSJim Harris  * be at least as much as the queue depth.
28e1bf63afSJim Harris  */
29e1bf63afSJim Harris static int g_allocate_depth = 0;
30445fe74eSpaul luse static int g_threads_per_core = 1;
319f51cf32Spaul luse static int g_time_in_sec = 5;
32e69375bfSpaul luse static uint32_t g_crc32c_seed = 0;
33850cd900Spaul luse static uint32_t g_chained_count = 1;
34b9218b7aSpaul luse static int g_fail_percent_goal = 0;
3589495464Spaul luse static uint8_t g_fill_pattern = 255;
36a5d5ab27SArtur Paszkiewicz static uint32_t g_xor_src_count = 2;
379f51cf32Spaul luse static bool g_verify = false;
382a0c66d0Spaul luse static const char *g_workload_type = NULL;
3984dbdf08STomasz Zawadzki static enum spdk_accel_opcode g_workload_selection = SPDK_ACCEL_OPC_LAST;
40*0a6b7098STomasz Zawadzki static const char *g_module_name = NULL;
419f51cf32Spaul luse static struct worker_thread *g_workers = NULL;
429f51cf32Spaul luse static int g_num_workers = 0;
4327e85f52SBen Walker static char *g_cd_file_in_name = NULL;
449f51cf32Spaul luse static pthread_mutex_t g_workers_lock = PTHREAD_MUTEX_INITIALIZER;
459260fa0cSpaul luse static struct spdk_app_opts g_opts = {};
46cdefd3d3Spaul luse 
4727e85f52SBen Walker struct ap_compress_seg {
4827e85f52SBen Walker 	void		*uncompressed_data;
4927e85f52SBen Walker 	uint32_t	uncompressed_len;
5027e85f52SBen Walker 	struct iovec	*uncompressed_iovs;
5127e85f52SBen Walker 	uint32_t	uncompressed_iovcnt;
5227e85f52SBen Walker 
5327e85f52SBen Walker 	void		*compressed_data;
5427e85f52SBen Walker 	uint32_t	compressed_len;
556afbf3dbSpaul luse 	uint32_t	compressed_len_padded;
5627e85f52SBen Walker 	struct iovec	*compressed_iovs;
5727e85f52SBen Walker 	uint32_t	compressed_iovcnt;
5827e85f52SBen Walker 
5927e85f52SBen Walker 	STAILQ_ENTRY(ap_compress_seg)	link;
6027e85f52SBen Walker };
6127e85f52SBen Walker 
6227e85f52SBen Walker static STAILQ_HEAD(, ap_compress_seg) g_compress_segs = STAILQ_HEAD_INITIALIZER(g_compress_segs);
6327e85f52SBen Walker 
64cdefd3d3Spaul luse struct worker_thread;
65cdefd3d3Spaul luse static void accel_done(void *ref, int status);
66cdefd3d3Spaul luse 
67445fe74eSpaul luse struct display_info {
68445fe74eSpaul luse 	int core;
69445fe74eSpaul luse 	int thread;
70445fe74eSpaul luse };
71445fe74eSpaul luse 
72cdefd3d3Spaul luse struct ap_task {
73cdefd3d3Spaul luse 	void			*src;
7428886ac3Spaul luse 	struct iovec		*src_iovs;
7528886ac3Spaul luse 	uint32_t		src_iovcnt;
76a5d5ab27SArtur Paszkiewicz 	void			**sources;
77850cd900Spaul luse 	struct iovec		*dst_iovs;
78850cd900Spaul luse 	uint32_t		dst_iovcnt;
79cdefd3d3Spaul luse 	void			*dst;
80cdefd3d3Spaul luse 	void			*dst2;
81221eb3f4Spaul luse 	uint32_t		crc_dst;
8227e85f52SBen Walker 	uint32_t		compressed_sz;
8327e85f52SBen Walker 	struct ap_compress_seg *cur_seg;
84cdefd3d3Spaul luse 	struct worker_thread	*worker;
85cdefd3d3Spaul luse 	int			expected_status; /* used for the compare operation */
86cdefd3d3Spaul luse 	TAILQ_ENTRY(ap_task)	link;
87cdefd3d3Spaul luse };
889f51cf32Spaul luse 
899f51cf32Spaul luse struct worker_thread {
909f51cf32Spaul luse 	struct spdk_io_channel		*ch;
91e3babb2bSKonrad Sztyber 	struct spdk_accel_opcode_stats	stats;
929f51cf32Spaul luse 	uint64_t			xfer_failed;
93b9218b7aSpaul luse 	uint64_t			injected_miscompares;
949f51cf32Spaul luse 	uint64_t			current_queue_depth;
95ac9a1a83Spaul luse 	TAILQ_HEAD(, ap_task)		tasks_pool;
969f51cf32Spaul luse 	struct worker_thread		*next;
979f51cf32Spaul luse 	unsigned			core;
989f51cf32Spaul luse 	struct spdk_thread		*thread;
999f51cf32Spaul luse 	bool				is_draining;
1009f51cf32Spaul luse 	struct spdk_poller		*is_draining_poller;
1019f51cf32Spaul luse 	struct spdk_poller		*stop_poller;
102ac9a1a83Spaul luse 	void				*task_base;
103445fe74eSpaul luse 	struct display_info		display;
1045105dc5dSKonrad Sztyber 	enum spdk_accel_opcode		workload;
1059f51cf32Spaul luse };
1069f51cf32Spaul luse 
1079f51cf32Spaul luse static void
1089260fa0cSpaul luse dump_user_config(void)
1099f51cf32Spaul luse {
110712e8cb7SBen Walker 	const char *module_name = NULL;
1119260fa0cSpaul luse 	int rc;
1129260fa0cSpaul luse 
113712e8cb7SBen Walker 	rc = spdk_accel_get_opc_module_name(g_workload_selection, &module_name);
1149260fa0cSpaul luse 	if (rc) {
115712e8cb7SBen Walker 		printf("error getting module name (%d)\n", rc);
1169260fa0cSpaul luse 	}
1179260fa0cSpaul luse 
1189260fa0cSpaul luse 	printf("\nSPDK Configuration:\n");
1199260fa0cSpaul luse 	printf("Core mask:      %s\n\n", g_opts.reactor_mask);
1209f51cf32Spaul luse 	printf("Accel Perf Configuration:\n");
1212a0c66d0Spaul luse 	printf("Workload Type:  %s\n", g_workload_type);
1225105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_CRC32C ||
1235105dc5dSKonrad Sztyber 	    g_workload_selection == SPDK_ACCEL_OPC_COPY_CRC32C) {
124b9218b7aSpaul luse 		printf("CRC-32C seed:   %u\n", g_crc32c_seed);
1255105dc5dSKonrad Sztyber 	} else if (g_workload_selection == SPDK_ACCEL_OPC_FILL) {
12689495464Spaul luse 		printf("Fill pattern:   0x%x\n", g_fill_pattern);
1275105dc5dSKonrad Sztyber 	} else if ((g_workload_selection == SPDK_ACCEL_OPC_COMPARE) && g_fail_percent_goal > 0) {
12889495464Spaul luse 		printf("Failure inject: %u percent\n", g_fail_percent_goal);
1295105dc5dSKonrad Sztyber 	} else if (g_workload_selection == SPDK_ACCEL_OPC_XOR) {
130a5d5ab27SArtur Paszkiewicz 		printf("Source buffers: %u\n", g_xor_src_count);
131e69375bfSpaul luse 	}
1325105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_COPY_CRC32C) {
133221eb3f4Spaul luse 		printf("Vector size:    %u bytes\n", g_xfer_size_bytes);
134850cd900Spaul luse 		printf("Transfer size:  %u bytes\n", g_xfer_size_bytes * g_chained_count);
135221eb3f4Spaul luse 	} else {
1369f51cf32Spaul luse 		printf("Transfer size:  %u bytes\n", g_xfer_size_bytes);
137221eb3f4Spaul luse 	}
138850cd900Spaul luse 	printf("vector count    %u\n", g_chained_count);
139712e8cb7SBen Walker 	printf("Module:         %s\n", module_name);
1405105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_COMPRESS ||
1415105dc5dSKonrad Sztyber 	    g_workload_selection == SPDK_ACCEL_OPC_DECOMPRESS) {
14227e85f52SBen Walker 		printf("File Name:      %s\n", g_cd_file_in_name);
14327e85f52SBen Walker 	}
1449f51cf32Spaul luse 	printf("Queue depth:    %u\n", g_queue_depth);
145e1bf63afSJim Harris 	printf("Allocate depth: %u\n", g_allocate_depth);
146445fe74eSpaul luse 	printf("# threads/core: %u\n", g_threads_per_core);
1479f51cf32Spaul luse 	printf("Run time:       %u seconds\n", g_time_in_sec);
1489f51cf32Spaul luse 	printf("Verify:         %s\n\n", g_verify ? "Yes" : "No");
1499f51cf32Spaul luse }
1509f51cf32Spaul luse 
1519f51cf32Spaul luse static void
1529f51cf32Spaul luse usage(void)
1539f51cf32Spaul luse {
1549f51cf32Spaul luse 	printf("accel_perf options:\n");
1559f51cf32Spaul luse 	printf("\t[-h help message]\n");
156f17e6705Spaul luse 	printf("\t[-q queue depth per core]\n");
157850cd900Spaul luse 	printf("\t[-C for supported workloads, use this value to configure the io vector size to test (default 1)\n");
158445fe74eSpaul luse 	printf("\t[-T number of threads per core\n");
15927e85f52SBen Walker 	printf("\t[-o transfer size in bytes (default: 4KiB. For compress/decompress, 0 means the input file size)]\n");
1609f51cf32Spaul luse 	printf("\t[-t time in seconds]\n");
161a5d5ab27SArtur Paszkiewicz 	printf("\t[-w workload type must be one of these: copy, fill, crc32c, copy_crc32c, compare, compress, decompress, dualcast, xor\n");
162*0a6b7098STomasz Zawadzki 	printf("\t[-M assign module to the operation, not compatible with accel_assign_opc RPC\n");
16327e85f52SBen Walker 	printf("\t[-l for compress/decompress workloads, name of uncompressed input file\n");
164436877a4SKonrad Sztyber 	printf("\t[-S for crc32c workload, use this seed value (default 0)\n");
165b9218b7aSpaul luse 	printf("\t[-P for compare workload, percentage of operations that should miscompare (percent, default 0)\n");
16689495464Spaul luse 	printf("\t[-f for fill workload, use this BYTE value (default 255)\n");
167a5d5ab27SArtur Paszkiewicz 	printf("\t[-x for xor workload, use this number of source buffers (default, minimum: 2)]\n");
1682a0c66d0Spaul luse 	printf("\t[-y verify result if this switch is on]\n");
169e1bf63afSJim Harris 	printf("\t[-a tasks to allocate per core (default: same value as -q)]\n");
170e1bf63afSJim Harris 	printf("\t\tCan be used to spread operations across a wider range of memory.\n");
1719f51cf32Spaul luse }
1729f51cf32Spaul luse 
1739f51cf32Spaul luse static int
174436877a4SKonrad Sztyber parse_args(int ch, char *arg)
1759f51cf32Spaul luse {
176358b84b4SZiye Yang 	int argval = 0;
177c82d5789SJim Harris 
178436877a4SKonrad Sztyber 	switch (ch) {
179e1bf63afSJim Harris 	case 'a':
180c82d5789SJim Harris 	case 'C':
181c82d5789SJim Harris 	case 'f':
182c82d5789SJim Harris 	case 'T':
183c82d5789SJim Harris 	case 'o':
184c82d5789SJim Harris 	case 'P':
185c82d5789SJim Harris 	case 'q':
186436877a4SKonrad Sztyber 	case 'S':
187c82d5789SJim Harris 	case 't':
188a5d5ab27SArtur Paszkiewicz 	case 'x':
189c82d5789SJim Harris 		argval = spdk_strtol(optarg, 10);
190c82d5789SJim Harris 		if (argval < 0) {
191436877a4SKonrad Sztyber 			fprintf(stderr, "-%c option must be non-negative.\n", ch);
192c82d5789SJim Harris 			usage();
193c82d5789SJim Harris 			return 1;
194c82d5789SJim Harris 		}
195c82d5789SJim Harris 		break;
196c82d5789SJim Harris 	default:
197c82d5789SJim Harris 		break;
198c82d5789SJim Harris 	};
199c82d5789SJim Harris 
200436877a4SKonrad Sztyber 	switch (ch) {
201e1bf63afSJim Harris 	case 'a':
202e1bf63afSJim Harris 		g_allocate_depth = argval;
203e1bf63afSJim Harris 		break;
20488754353SZiye Yang 	case 'C':
205850cd900Spaul luse 		g_chained_count = argval;
20688754353SZiye Yang 		break;
20727e85f52SBen Walker 	case 'l':
20827e85f52SBen Walker 		g_cd_file_in_name = optarg;
20927e85f52SBen Walker 		break;
21089495464Spaul luse 	case 'f':
211c82d5789SJim Harris 		g_fill_pattern = (uint8_t)argval;
21289495464Spaul luse 		break;
213445fe74eSpaul luse 	case 'T':
214c82d5789SJim Harris 		g_threads_per_core = argval;
215445fe74eSpaul luse 		break;
2169f51cf32Spaul luse 	case 'o':
217c82d5789SJim Harris 		g_xfer_size_bytes = argval;
2189f51cf32Spaul luse 		break;
219b9218b7aSpaul luse 	case 'P':
220c82d5789SJim Harris 		g_fail_percent_goal = argval;
221b9218b7aSpaul luse 		break;
2229f51cf32Spaul luse 	case 'q':
223c82d5789SJim Harris 		g_queue_depth = argval;
2249f51cf32Spaul luse 		break;
225436877a4SKonrad Sztyber 	case 'S':
226c82d5789SJim Harris 		g_crc32c_seed = argval;
227e69375bfSpaul luse 		break;
2289f51cf32Spaul luse 	case 't':
229c82d5789SJim Harris 		g_time_in_sec = argval;
2309f51cf32Spaul luse 		break;
231a5d5ab27SArtur Paszkiewicz 	case 'x':
232a5d5ab27SArtur Paszkiewicz 		g_xor_src_count = argval;
233a5d5ab27SArtur Paszkiewicz 		break;
2349f51cf32Spaul luse 	case 'y':
2359f51cf32Spaul luse 		g_verify = true;
2369f51cf32Spaul luse 		break;
2372a0c66d0Spaul luse 	case 'w':
2382a0c66d0Spaul luse 		g_workload_type = optarg;
239514be889Spaul luse 		if (!strcmp(g_workload_type, "copy")) {
2405105dc5dSKonrad Sztyber 			g_workload_selection = SPDK_ACCEL_OPC_COPY;
241514be889Spaul luse 		} else if (!strcmp(g_workload_type, "fill")) {
2425105dc5dSKonrad Sztyber 			g_workload_selection = SPDK_ACCEL_OPC_FILL;
243e69375bfSpaul luse 		} else if (!strcmp(g_workload_type, "crc32c")) {
2445105dc5dSKonrad Sztyber 			g_workload_selection = SPDK_ACCEL_OPC_CRC32C;
245221eb3f4Spaul luse 		} else if (!strcmp(g_workload_type, "copy_crc32c")) {
2465105dc5dSKonrad Sztyber 			g_workload_selection = SPDK_ACCEL_OPC_COPY_CRC32C;
247b9218b7aSpaul luse 		} else if (!strcmp(g_workload_type, "compare")) {
2485105dc5dSKonrad Sztyber 			g_workload_selection = SPDK_ACCEL_OPC_COMPARE;
2490ef079c6Spaul luse 		} else if (!strcmp(g_workload_type, "dualcast")) {
2505105dc5dSKonrad Sztyber 			g_workload_selection = SPDK_ACCEL_OPC_DUALCAST;
25127e85f52SBen Walker 		} else if (!strcmp(g_workload_type, "compress")) {
2525105dc5dSKonrad Sztyber 			g_workload_selection = SPDK_ACCEL_OPC_COMPRESS;
25327e85f52SBen Walker 		} else if (!strcmp(g_workload_type, "decompress")) {
2545105dc5dSKonrad Sztyber 			g_workload_selection = SPDK_ACCEL_OPC_DECOMPRESS;
255a5d5ab27SArtur Paszkiewicz 		} else if (!strcmp(g_workload_type, "xor")) {
2565105dc5dSKonrad Sztyber 			g_workload_selection = SPDK_ACCEL_OPC_XOR;
257b21221e1Spaul luse 		} else {
25884dbdf08STomasz Zawadzki 			fprintf(stderr, "Unsupported workload type: %s\n", optarg);
259b21221e1Spaul luse 			usage();
260b21221e1Spaul luse 			return 1;
261514be889Spaul luse 		}
2622a0c66d0Spaul luse 		break;
263*0a6b7098STomasz Zawadzki 	case 'M':
264*0a6b7098STomasz Zawadzki 		g_module_name = optarg;
265*0a6b7098STomasz Zawadzki 		break;
266*0a6b7098STomasz Zawadzki 
2679f51cf32Spaul luse 	default:
2689f51cf32Spaul luse 		usage();
2699f51cf32Spaul luse 		return 1;
2709f51cf32Spaul luse 	}
27188754353SZiye Yang 
2729f51cf32Spaul luse 	return 0;
2739f51cf32Spaul luse }
2749f51cf32Spaul luse 
275eea826a2Spaul luse static int dump_result(void);
2769f51cf32Spaul luse static void
2779f51cf32Spaul luse unregister_worker(void *arg1)
2789f51cf32Spaul luse {
2799f51cf32Spaul luse 	struct worker_thread *worker = arg1;
2809f51cf32Spaul luse 
28120b5c460STomasz Zawadzki 	if (worker->ch) {
282e3babb2bSKonrad Sztyber 		spdk_accel_get_opcode_stats(worker->ch, worker->workload,
283e3babb2bSKonrad Sztyber 					    &worker->stats, sizeof(worker->stats));
2849f51cf32Spaul luse 		spdk_put_io_channel(worker->ch);
28520b5c460STomasz Zawadzki 		worker->ch = NULL;
28620b5c460STomasz Zawadzki 	}
28720b5c460STomasz Zawadzki 	free(worker->task_base);
2880e7821e9SJim Harris 	spdk_thread_exit(spdk_get_thread());
2899f51cf32Spaul luse 	pthread_mutex_lock(&g_workers_lock);
2909f51cf32Spaul luse 	assert(g_num_workers >= 1);
2919f51cf32Spaul luse 	if (--g_num_workers == 0) {
2929f51cf32Spaul luse 		pthread_mutex_unlock(&g_workers_lock);
29320b5c460STomasz Zawadzki 		/* Only dump results on successful runs */
29420b5c460STomasz Zawadzki 		if (g_rc == 0) {
2959b189667Spaul luse 			g_rc = dump_result();
29620b5c460STomasz Zawadzki 		}
29720b5c460STomasz Zawadzki 		spdk_app_stop(g_rc);
298f042d6baSGangCao 	} else {
2999f51cf32Spaul luse 		pthread_mutex_unlock(&g_workers_lock);
3009f51cf32Spaul luse 	}
301f042d6baSGangCao }
3029f51cf32Spaul luse 
30327e85f52SBen Walker static void
30427e85f52SBen Walker accel_perf_construct_iovs(void *buf, uint64_t sz, struct iovec *iovs, uint32_t iovcnt)
30527e85f52SBen Walker {
30627e85f52SBen Walker 	uint64_t ele_size;
30727e85f52SBen Walker 	uint8_t *data;
30827e85f52SBen Walker 	uint32_t i;
30927e85f52SBen Walker 
31027e85f52SBen Walker 	ele_size = spdk_divide_round_up(sz, iovcnt);
31127e85f52SBen Walker 
31227e85f52SBen Walker 	data = buf;
31327e85f52SBen Walker 	for (i = 0; i < iovcnt; i++) {
31427e85f52SBen Walker 		ele_size = spdk_min(ele_size, sz);
31527e85f52SBen Walker 		assert(ele_size > 0);
31627e85f52SBen Walker 
31727e85f52SBen Walker 		iovs[i].iov_base = data;
31827e85f52SBen Walker 		iovs[i].iov_len = ele_size;
31927e85f52SBen Walker 
32027e85f52SBen Walker 		data += ele_size;
32127e85f52SBen Walker 		sz -= ele_size;
32227e85f52SBen Walker 	}
32327e85f52SBen Walker 	assert(sz == 0);
32427e85f52SBen Walker }
32527e85f52SBen Walker 
3268da995c4Spaul luse static int
3278da995c4Spaul luse _get_task_data_bufs(struct ap_task *task)
3288da995c4Spaul luse {
3298da995c4Spaul luse 	uint32_t align = 0;
33088754353SZiye Yang 	uint32_t i = 0;
331221eb3f4Spaul luse 	int dst_buff_len = g_xfer_size_bytes;
3328da995c4Spaul luse 
3338da995c4Spaul luse 	/* For dualcast, the DSA HW requires 4K alignment on destination addresses but
334712e8cb7SBen Walker 	 * we do this for all modules to keep it simple.
3358da995c4Spaul luse 	 */
3365105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_DUALCAST) {
3378da995c4Spaul luse 		align = ALIGN_4K;
3388da995c4Spaul luse 	}
3398da995c4Spaul luse 
3405105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_COMPRESS ||
3415105dc5dSKonrad Sztyber 	    g_workload_selection == SPDK_ACCEL_OPC_DECOMPRESS) {
34227e85f52SBen Walker 		task->cur_seg = STAILQ_FIRST(&g_compress_segs);
3436afbf3dbSpaul luse 
3445105dc5dSKonrad Sztyber 		if (g_workload_selection == SPDK_ACCEL_OPC_COMPRESS) {
3456afbf3dbSpaul luse 			dst_buff_len = task->cur_seg->compressed_len_padded;
3466afbf3dbSpaul luse 		}
3476afbf3dbSpaul luse 
3486afbf3dbSpaul luse 		task->dst = spdk_dma_zmalloc(dst_buff_len, align, NULL);
3496afbf3dbSpaul luse 		if (task->dst == NULL) {
3506afbf3dbSpaul luse 			fprintf(stderr, "Unable to alloc dst buffer\n");
3516afbf3dbSpaul luse 			return -ENOMEM;
3526afbf3dbSpaul luse 		}
3536afbf3dbSpaul luse 
3546afbf3dbSpaul luse 		task->dst_iovs = calloc(g_chained_count, sizeof(struct iovec));
3556afbf3dbSpaul luse 		if (!task->dst_iovs) {
3566afbf3dbSpaul luse 			fprintf(stderr, "cannot allocate task->dst_iovs for task=%p\n", task);
3576afbf3dbSpaul luse 			return -ENOMEM;
3586afbf3dbSpaul luse 		}
3596afbf3dbSpaul luse 		task->dst_iovcnt = g_chained_count;
3606afbf3dbSpaul luse 		accel_perf_construct_iovs(task->dst, dst_buff_len, task->dst_iovs, task->dst_iovcnt);
3616afbf3dbSpaul luse 
3626afbf3dbSpaul luse 		return 0;
3636afbf3dbSpaul luse 	}
3646afbf3dbSpaul luse 
3655105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_CRC32C ||
3665105dc5dSKonrad Sztyber 	    g_workload_selection == SPDK_ACCEL_OPC_COPY_CRC32C) {
367850cd900Spaul luse 		assert(g_chained_count > 0);
368850cd900Spaul luse 		task->src_iovcnt = g_chained_count;
36928886ac3Spaul luse 		task->src_iovs = calloc(task->src_iovcnt, sizeof(struct iovec));
37028886ac3Spaul luse 		if (!task->src_iovs) {
37128886ac3Spaul luse 			fprintf(stderr, "cannot allocated task->src_iovs fot task=%p\n", task);
37288754353SZiye Yang 			return -ENOMEM;
37388754353SZiye Yang 		}
37488754353SZiye Yang 
3755105dc5dSKonrad Sztyber 		if (g_workload_selection == SPDK_ACCEL_OPC_COPY_CRC32C) {
376850cd900Spaul luse 			dst_buff_len = g_xfer_size_bytes * g_chained_count;
377221eb3f4Spaul luse 		}
378221eb3f4Spaul luse 
37928886ac3Spaul luse 		for (i = 0; i < task->src_iovcnt; i++) {
38028886ac3Spaul luse 			task->src_iovs[i].iov_base = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL);
38128886ac3Spaul luse 			if (task->src_iovs[i].iov_base == NULL) {
38288754353SZiye Yang 				return -ENOMEM;
38388754353SZiye Yang 			}
38428886ac3Spaul luse 			memset(task->src_iovs[i].iov_base, DATA_PATTERN, g_xfer_size_bytes);
38528886ac3Spaul luse 			task->src_iovs[i].iov_len = g_xfer_size_bytes;
38688754353SZiye Yang 		}
3875105dc5dSKonrad Sztyber 	} else if (g_workload_selection == SPDK_ACCEL_OPC_XOR) {
388a5d5ab27SArtur Paszkiewicz 		assert(g_xor_src_count > 1);
389a5d5ab27SArtur Paszkiewicz 		task->sources = calloc(g_xor_src_count, sizeof(*task->sources));
390a5d5ab27SArtur Paszkiewicz 		if (!task->sources) {
391a5d5ab27SArtur Paszkiewicz 			return -ENOMEM;
392a5d5ab27SArtur Paszkiewicz 		}
39388754353SZiye Yang 
394a5d5ab27SArtur Paszkiewicz 		for (i = 0; i < g_xor_src_count; i++) {
395a5d5ab27SArtur Paszkiewicz 			task->sources[i] = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL);
396a5d5ab27SArtur Paszkiewicz 			if (!task->sources[i]) {
397a5d5ab27SArtur Paszkiewicz 				return -ENOMEM;
398a5d5ab27SArtur Paszkiewicz 			}
399a5d5ab27SArtur Paszkiewicz 			memset(task->sources[i], DATA_PATTERN, g_xfer_size_bytes);
400a5d5ab27SArtur Paszkiewicz 		}
40188754353SZiye Yang 	} else {
4028da995c4Spaul luse 		task->src = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL);
4038da995c4Spaul luse 		if (task->src == NULL) {
4048da995c4Spaul luse 			fprintf(stderr, "Unable to alloc src buffer\n");
4058da995c4Spaul luse 			return -ENOMEM;
4068da995c4Spaul luse 		}
40788754353SZiye Yang 
40888754353SZiye Yang 		/* For fill, set the entire src buffer so we can check if verify is enabled. */
4095105dc5dSKonrad Sztyber 		if (g_workload_selection == SPDK_ACCEL_OPC_FILL) {
41088754353SZiye Yang 			memset(task->src, g_fill_pattern, g_xfer_size_bytes);
41188754353SZiye Yang 		} else {
4128da995c4Spaul luse 			memset(task->src, DATA_PATTERN, g_xfer_size_bytes);
41388754353SZiye Yang 		}
41488754353SZiye Yang 	}
4158da995c4Spaul luse 
4165105dc5dSKonrad Sztyber 	if (g_workload_selection != SPDK_ACCEL_OPC_CRC32C) {
417221eb3f4Spaul luse 		task->dst = spdk_dma_zmalloc(dst_buff_len, align, NULL);
4188da995c4Spaul luse 		if (task->dst == NULL) {
4198da995c4Spaul luse 			fprintf(stderr, "Unable to alloc dst buffer\n");
4208da995c4Spaul luse 			return -ENOMEM;
4218da995c4Spaul luse 		}
4228da995c4Spaul luse 
4238da995c4Spaul luse 		/* For compare we want the buffers to match, otherwise not. */
4245105dc5dSKonrad Sztyber 		if (g_workload_selection == SPDK_ACCEL_OPC_COMPARE) {
425221eb3f4Spaul luse 			memset(task->dst, DATA_PATTERN, dst_buff_len);
4268da995c4Spaul luse 		} else {
427221eb3f4Spaul luse 			memset(task->dst, ~DATA_PATTERN, dst_buff_len);
428221eb3f4Spaul luse 		}
4298da995c4Spaul luse 	}
4308da995c4Spaul luse 
43184162738Spaul luse 	/* For dualcast 2 buffers are needed for the operation.  */
4325105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_DUALCAST ||
4335105dc5dSKonrad Sztyber 	    (g_workload_selection == SPDK_ACCEL_OPC_XOR && g_verify)) {
4348da995c4Spaul luse 		task->dst2 = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL);
4358da995c4Spaul luse 		if (task->dst2 == NULL) {
4368da995c4Spaul luse 			fprintf(stderr, "Unable to alloc dst buffer\n");
4378da995c4Spaul luse 			return -ENOMEM;
4388da995c4Spaul luse 		}
4398da995c4Spaul luse 		memset(task->dst2, ~DATA_PATTERN, g_xfer_size_bytes);
4408da995c4Spaul luse 	}
4418da995c4Spaul luse 
4428da995c4Spaul luse 	return 0;
4438da995c4Spaul luse }
4448da995c4Spaul luse 
445ac9a1a83Spaul luse inline static struct ap_task *
446ac9a1a83Spaul luse _get_task(struct worker_thread *worker)
447ac9a1a83Spaul luse {
448ac9a1a83Spaul luse 	struct ap_task *task;
449ac9a1a83Spaul luse 
450ac9a1a83Spaul luse 	if (!TAILQ_EMPTY(&worker->tasks_pool)) {
451ac9a1a83Spaul luse 		task = TAILQ_FIRST(&worker->tasks_pool);
452ac9a1a83Spaul luse 		TAILQ_REMOVE(&worker->tasks_pool, task, link);
453ac9a1a83Spaul luse 	} else {
454ac9a1a83Spaul luse 		fprintf(stderr, "Unable to get ap_task\n");
455ac9a1a83Spaul luse 		return NULL;
456ac9a1a83Spaul luse 	}
457ac9a1a83Spaul luse 
458ac9a1a83Spaul luse 	return task;
459ac9a1a83Spaul luse }
460ac9a1a83Spaul luse 
461f17e6705Spaul luse /* Submit one operation using the same ap task that just completed. */
4629f51cf32Spaul luse static void
463ac9a1a83Spaul luse _submit_single(struct worker_thread *worker, struct ap_task *task)
4649f51cf32Spaul luse {
465b9218b7aSpaul luse 	int random_num;
46640ec8e97Spaul luse 	int rc = 0;
46712c40f05Spaul luse 	int flags = 0;
4689f51cf32Spaul luse 
4699f51cf32Spaul luse 	assert(worker);
4709f51cf32Spaul luse 
47113067997Spaul luse 	switch (worker->workload) {
4725105dc5dSKonrad Sztyber 	case SPDK_ACCEL_OPC_COPY:
473e8463f87Spaul luse 		rc = spdk_accel_submit_copy(worker->ch, task->dst, task->src,
47412c40f05Spaul luse 					    g_xfer_size_bytes, flags, accel_done, task);
475e69375bfSpaul luse 		break;
4765105dc5dSKonrad Sztyber 	case SPDK_ACCEL_OPC_FILL:
4772a0c66d0Spaul luse 		/* For fill use the first byte of the task->dst buffer */
478ee7e31f9Spaul luse 		rc = spdk_accel_submit_fill(worker->ch, task->dst, *(uint8_t *)task->src,
47912c40f05Spaul luse 					    g_xfer_size_bytes, flags, accel_done, task);
480e69375bfSpaul luse 		break;
4815105dc5dSKonrad Sztyber 	case SPDK_ACCEL_OPC_CRC32C:
482a738acd5Spaul luse 		rc = spdk_accel_submit_crc32cv(worker->ch, &task->crc_dst,
48328886ac3Spaul luse 					       task->src_iovs, task->src_iovcnt, g_crc32c_seed,
48490c56d96SZiye Yang 					       accel_done, task);
485e69375bfSpaul luse 		break;
4865105dc5dSKonrad Sztyber 	case SPDK_ACCEL_OPC_COPY_CRC32C:
48728886ac3Spaul luse 		rc = spdk_accel_submit_copy_crc32cv(worker->ch, task->dst, task->src_iovs, task->src_iovcnt,
48812c40f05Spaul luse 						    &task->crc_dst, g_crc32c_seed, flags, accel_done, task);
489221eb3f4Spaul luse 		break;
4905105dc5dSKonrad Sztyber 	case SPDK_ACCEL_OPC_COMPARE:
491b9218b7aSpaul luse 		random_num = rand() % 100;
492b9218b7aSpaul luse 		if (random_num < g_fail_percent_goal) {
493b9218b7aSpaul luse 			task->expected_status = -EILSEQ;
494b9218b7aSpaul luse 			*(uint8_t *)task->dst = ~DATA_PATTERN;
495b9218b7aSpaul luse 		} else {
496b9218b7aSpaul luse 			task->expected_status = 0;
497b9218b7aSpaul luse 			*(uint8_t *)task->dst = DATA_PATTERN;
498b9218b7aSpaul luse 		}
499ee7e31f9Spaul luse 		rc = spdk_accel_submit_compare(worker->ch, task->dst, task->src,
500e8463f87Spaul luse 					       g_xfer_size_bytes, accel_done, task);
501b9218b7aSpaul luse 		break;
5025105dc5dSKonrad Sztyber 	case SPDK_ACCEL_OPC_DUALCAST:
503ee7e31f9Spaul luse 		rc = spdk_accel_submit_dualcast(worker->ch, task->dst, task->dst2,
50412c40f05Spaul luse 						task->src, g_xfer_size_bytes, flags, accel_done, task);
5050ef079c6Spaul luse 		break;
5065105dc5dSKonrad Sztyber 	case SPDK_ACCEL_OPC_COMPRESS:
50727e85f52SBen Walker 		task->src_iovs = task->cur_seg->uncompressed_iovs;
50827e85f52SBen Walker 		task->src_iovcnt = task->cur_seg->uncompressed_iovcnt;
5096afbf3dbSpaul luse 		rc = spdk_accel_submit_compress(worker->ch, task->dst, task->cur_seg->compressed_len_padded,
5106afbf3dbSpaul luse 						task->src_iovs,
51127e85f52SBen Walker 						task->src_iovcnt, &task->compressed_sz, flags, accel_done, task);
51227e85f52SBen Walker 		break;
5135105dc5dSKonrad Sztyber 	case SPDK_ACCEL_OPC_DECOMPRESS:
51427e85f52SBen Walker 		task->src_iovs = task->cur_seg->compressed_iovs;
51527e85f52SBen Walker 		task->src_iovcnt = task->cur_seg->compressed_iovcnt;
51627e85f52SBen Walker 		rc = spdk_accel_submit_decompress(worker->ch, task->dst_iovs, task->dst_iovcnt, task->src_iovs,
51791f3063bSpaul luse 						  task->src_iovcnt, NULL, flags, accel_done, task);
51827e85f52SBen Walker 		break;
5195105dc5dSKonrad Sztyber 	case SPDK_ACCEL_OPC_XOR:
520a5d5ab27SArtur Paszkiewicz 		rc = spdk_accel_submit_xor(worker->ch, task->dst, task->sources, g_xor_src_count,
521a5d5ab27SArtur Paszkiewicz 					   g_xfer_size_bytes, accel_done, task);
522a5d5ab27SArtur Paszkiewicz 		break;
523e69375bfSpaul luse 	default:
5242a0c66d0Spaul luse 		assert(false);
525e69375bfSpaul luse 		break;
526e69375bfSpaul luse 
5272a0c66d0Spaul luse 	}
52840ec8e97Spaul luse 
5296799d46aSpaul luse 	worker->current_queue_depth++;
53040ec8e97Spaul luse 	if (rc) {
531e8463f87Spaul luse 		accel_done(task, rc);
53240ec8e97Spaul luse 	}
5339f51cf32Spaul luse }
5349f51cf32Spaul luse 
5359f51cf32Spaul luse static void
536e150f6b8SZiye Yang _free_task_buffers(struct ap_task *task)
537ac9a1a83Spaul luse {
53888754353SZiye Yang 	uint32_t i;
53988754353SZiye Yang 
5405105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_DECOMPRESS ||
5415105dc5dSKonrad Sztyber 	    g_workload_selection == SPDK_ACCEL_OPC_COMPRESS) {
54227e85f52SBen Walker 		free(task->dst_iovs);
5435105dc5dSKonrad Sztyber 	} else if (g_workload_selection == SPDK_ACCEL_OPC_CRC32C ||
5445105dc5dSKonrad Sztyber 		   g_workload_selection == SPDK_ACCEL_OPC_COPY_CRC32C) {
54528886ac3Spaul luse 		if (task->src_iovs) {
54628886ac3Spaul luse 			for (i = 0; i < task->src_iovcnt; i++) {
54728886ac3Spaul luse 				if (task->src_iovs[i].iov_base) {
54828886ac3Spaul luse 					spdk_dma_free(task->src_iovs[i].iov_base);
54988754353SZiye Yang 				}
55088754353SZiye Yang 			}
55128886ac3Spaul luse 			free(task->src_iovs);
55288754353SZiye Yang 		}
5535105dc5dSKonrad Sztyber 	} else if (g_workload_selection == SPDK_ACCEL_OPC_XOR) {
554a5d5ab27SArtur Paszkiewicz 		if (task->sources) {
555a5d5ab27SArtur Paszkiewicz 			for (i = 0; i < g_xor_src_count; i++) {
556a5d5ab27SArtur Paszkiewicz 				spdk_dma_free(task->sources[i]);
557a5d5ab27SArtur Paszkiewicz 			}
558a5d5ab27SArtur Paszkiewicz 			free(task->sources);
559a5d5ab27SArtur Paszkiewicz 		}
56088754353SZiye Yang 	} else {
561ac9a1a83Spaul luse 		spdk_dma_free(task->src);
56288754353SZiye Yang 	}
56388754353SZiye Yang 
564ac9a1a83Spaul luse 	spdk_dma_free(task->dst);
5655105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_DUALCAST || g_workload_selection == SPDK_ACCEL_OPC_XOR) {
566ac9a1a83Spaul luse 		spdk_dma_free(task->dst2);
567ac9a1a83Spaul luse 	}
568ac9a1a83Spaul luse }
569ac9a1a83Spaul luse 
570221eb3f4Spaul luse static int
57128886ac3Spaul luse _vector_memcmp(void *_dst, struct iovec *src_src_iovs, uint32_t iovcnt)
572221eb3f4Spaul luse {
573221eb3f4Spaul luse 	uint32_t i;
574221eb3f4Spaul luse 	uint32_t ttl_len = 0;
575221eb3f4Spaul luse 	uint8_t *dst = (uint8_t *)_dst;
576221eb3f4Spaul luse 
577221eb3f4Spaul luse 	for (i = 0; i < iovcnt; i++) {
57828886ac3Spaul luse 		if (memcmp(dst, src_src_iovs[i].iov_base, src_src_iovs[i].iov_len)) {
579221eb3f4Spaul luse 			return -1;
580221eb3f4Spaul luse 		}
58128886ac3Spaul luse 		dst += src_src_iovs[i].iov_len;
58228886ac3Spaul luse 		ttl_len += src_src_iovs[i].iov_len;
583221eb3f4Spaul luse 	}
584221eb3f4Spaul luse 
585221eb3f4Spaul luse 	if (ttl_len != iovcnt * g_xfer_size_bytes) {
586221eb3f4Spaul luse 		return -1;
587221eb3f4Spaul luse 	}
588221eb3f4Spaul luse 
589221eb3f4Spaul luse 	return 0;
590221eb3f4Spaul luse }
591221eb3f4Spaul luse 
59213067997Spaul luse static int _worker_stop(void *arg);
59313067997Spaul luse 
594fab40895Spaul luse static void
595df42f358Spaul luse accel_done(void *arg1, int status)
5969f51cf32Spaul luse {
5979f51cf32Spaul luse 	struct ap_task *task = arg1;
5989f51cf32Spaul luse 	struct worker_thread *worker = task->worker;
599e69375bfSpaul luse 	uint32_t sw_crc32c;
6009f51cf32Spaul luse 
6019f51cf32Spaul luse 	assert(worker);
6029f51cf32Spaul luse 	assert(worker->current_queue_depth > 0);
6039f51cf32Spaul luse 
604df42f358Spaul luse 	if (g_verify && status == 0) {
60513067997Spaul luse 		switch (worker->workload) {
6065105dc5dSKonrad Sztyber 		case SPDK_ACCEL_OPC_COPY_CRC32C:
60728886ac3Spaul luse 			sw_crc32c = spdk_crc32c_iov_update(task->src_iovs, task->src_iovcnt, ~g_crc32c_seed);
608221eb3f4Spaul luse 			if (task->crc_dst != sw_crc32c) {
609221eb3f4Spaul luse 				SPDK_NOTICELOG("CRC-32C miscompare\n");
610221eb3f4Spaul luse 				worker->xfer_failed++;
611221eb3f4Spaul luse 			}
61228886ac3Spaul luse 			if (_vector_memcmp(task->dst, task->src_iovs, task->src_iovcnt)) {
613221eb3f4Spaul luse 				SPDK_NOTICELOG("Data miscompare\n");
614221eb3f4Spaul luse 				worker->xfer_failed++;
615221eb3f4Spaul luse 			}
616221eb3f4Spaul luse 			break;
6175105dc5dSKonrad Sztyber 		case SPDK_ACCEL_OPC_CRC32C:
61828886ac3Spaul luse 			sw_crc32c = spdk_crc32c_iov_update(task->src_iovs, task->src_iovcnt, ~g_crc32c_seed);
619a738acd5Spaul luse 			if (task->crc_dst != sw_crc32c) {
620e69375bfSpaul luse 				SPDK_NOTICELOG("CRC-32C miscompare\n");
621e69375bfSpaul luse 				worker->xfer_failed++;
622e69375bfSpaul luse 			}
623b9218b7aSpaul luse 			break;
6245105dc5dSKonrad Sztyber 		case SPDK_ACCEL_OPC_COPY:
625b9218b7aSpaul luse 			if (memcmp(task->src, task->dst, g_xfer_size_bytes)) {
6269f51cf32Spaul luse 				SPDK_NOTICELOG("Data miscompare\n");
6279f51cf32Spaul luse 				worker->xfer_failed++;
628b9218b7aSpaul luse 			}
629b9218b7aSpaul luse 			break;
6305105dc5dSKonrad Sztyber 		case SPDK_ACCEL_OPC_DUALCAST:
6310ef079c6Spaul luse 			if (memcmp(task->src, task->dst, g_xfer_size_bytes)) {
6320ef079c6Spaul luse 				SPDK_NOTICELOG("Data miscompare, first destination\n");
6330ef079c6Spaul luse 				worker->xfer_failed++;
6340ef079c6Spaul luse 			}
6350ef079c6Spaul luse 			if (memcmp(task->src, task->dst2, g_xfer_size_bytes)) {
6360ef079c6Spaul luse 				SPDK_NOTICELOG("Data miscompare, second destination\n");
6370ef079c6Spaul luse 				worker->xfer_failed++;
6380ef079c6Spaul luse 			}
6390ef079c6Spaul luse 			break;
6405105dc5dSKonrad Sztyber 		case SPDK_ACCEL_OPC_FILL:
641d207237fSpaul luse 			if (memcmp(task->dst, task->src, g_xfer_size_bytes)) {
642d207237fSpaul luse 				SPDK_NOTICELOG("Data miscompare\n");
643d207237fSpaul luse 				worker->xfer_failed++;
644d207237fSpaul luse 			}
645d207237fSpaul luse 			break;
6465105dc5dSKonrad Sztyber 		case SPDK_ACCEL_OPC_COMPARE:
6478cee297cSpaul luse 			break;
6485105dc5dSKonrad Sztyber 		case SPDK_ACCEL_OPC_COMPRESS:
64927e85f52SBen Walker 			break;
6505105dc5dSKonrad Sztyber 		case SPDK_ACCEL_OPC_DECOMPRESS:
65127e85f52SBen Walker 			if (memcmp(task->dst, task->cur_seg->uncompressed_data, task->cur_seg->uncompressed_len)) {
65227e85f52SBen Walker 				SPDK_NOTICELOG("Data miscompare on decompression\n");
65327e85f52SBen Walker 				worker->xfer_failed++;
65427e85f52SBen Walker 			}
65527e85f52SBen Walker 			break;
6565105dc5dSKonrad Sztyber 		case SPDK_ACCEL_OPC_XOR:
657a5d5ab27SArtur Paszkiewicz 			if (spdk_xor_gen(task->dst2, task->sources, g_xor_src_count,
658a5d5ab27SArtur Paszkiewicz 					 g_xfer_size_bytes) != 0) {
659a5d5ab27SArtur Paszkiewicz 				SPDK_ERRLOG("Failed to generate xor for verification\n");
660a5d5ab27SArtur Paszkiewicz 			} else if (memcmp(task->dst, task->dst2, g_xfer_size_bytes)) {
661a5d5ab27SArtur Paszkiewicz 				SPDK_NOTICELOG("Data miscompare\n");
662a5d5ab27SArtur Paszkiewicz 				worker->xfer_failed++;
663a5d5ab27SArtur Paszkiewicz 			}
664a5d5ab27SArtur Paszkiewicz 			break;
665b9218b7aSpaul luse 		default:
666b9218b7aSpaul luse 			assert(false);
667b9218b7aSpaul luse 			break;
6689f51cf32Spaul luse 		}
6699f51cf32Spaul luse 	}
670b9218b7aSpaul luse 
6715105dc5dSKonrad Sztyber 	if (worker->workload == SPDK_ACCEL_OPC_COMPRESS ||
6725105dc5dSKonrad Sztyber 	    g_workload_selection == SPDK_ACCEL_OPC_DECOMPRESS) {
67327e85f52SBen Walker 		/* Advance the task to the next segment */
67427e85f52SBen Walker 		task->cur_seg = STAILQ_NEXT(task->cur_seg, link);
67527e85f52SBen Walker 		if (task->cur_seg == NULL) {
67627e85f52SBen Walker 			task->cur_seg = STAILQ_FIRST(&g_compress_segs);
67727e85f52SBen Walker 		}
67827e85f52SBen Walker 	}
67927e85f52SBen Walker 
680b9218b7aSpaul luse 	if (task->expected_status == -EILSEQ) {
681df42f358Spaul luse 		assert(status != 0);
682b9218b7aSpaul luse 		worker->injected_miscompares++;
68313067997Spaul luse 		status = 0;
684df42f358Spaul luse 	} else if (status) {
685712e8cb7SBen Walker 		/* Expected to pass but the accel module reported an error (ex: COMPARE operation). */
686b9218b7aSpaul luse 		worker->xfer_failed++;
687b9218b7aSpaul luse 	}
688b9218b7aSpaul luse 
6899f51cf32Spaul luse 	worker->current_queue_depth--;
6909f51cf32Spaul luse 
69113067997Spaul luse 	if (!worker->is_draining && status == 0) {
692451462f6SJim Harris 		TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link);
693451462f6SJim Harris 		task = _get_task(worker);
6949f51cf32Spaul luse 		_submit_single(worker, task);
695f17e6705Spaul luse 	} else {
696b34883e0SZiye Yang 		TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link);
697f17e6705Spaul luse 	}
6989f51cf32Spaul luse }
6999f51cf32Spaul luse 
7009f51cf32Spaul luse static int
7019f51cf32Spaul luse dump_result(void)
7029f51cf32Spaul luse {
7039f51cf32Spaul luse 	uint64_t total_completed = 0;
7049f51cf32Spaul luse 	uint64_t total_failed = 0;
705b9218b7aSpaul luse 	uint64_t total_miscompared = 0;
7069f51cf32Spaul luse 	uint64_t total_xfer_per_sec, total_bw_in_MiBps;
7079f51cf32Spaul luse 	struct worker_thread *worker = g_workers;
7089424e9ebSKonrad Sztyber 	char tmp[64];
7099f51cf32Spaul luse 
7109424e9ebSKonrad Sztyber 	printf("\n%-12s %20s %16s %16s %16s\n",
7119424e9ebSKonrad Sztyber 	       "Core,Thread", "Transfers", "Bandwidth", "Failed", "Miscompares");
7129424e9ebSKonrad Sztyber 	printf("------------------------------------------------------------------------------------\n");
7139f51cf32Spaul luse 	while (worker != NULL) {
7149f51cf32Spaul luse 
715e3babb2bSKonrad Sztyber 		uint64_t xfer_per_sec = worker->stats.executed / g_time_in_sec;
716e3babb2bSKonrad Sztyber 		uint64_t bw_in_MiBps = worker->stats.num_bytes /
7179f51cf32Spaul luse 				       (g_time_in_sec * 1024 * 1024);
7189f51cf32Spaul luse 
719e3babb2bSKonrad Sztyber 		total_completed += worker->stats.executed;
7209f51cf32Spaul luse 		total_failed += worker->xfer_failed;
721b9218b7aSpaul luse 		total_miscompared += worker->injected_miscompares;
7229f51cf32Spaul luse 
7239424e9ebSKonrad Sztyber 		snprintf(tmp, sizeof(tmp), "%u,%u", worker->display.core, worker->display.thread);
7249f51cf32Spaul luse 		if (xfer_per_sec) {
7259424e9ebSKonrad Sztyber 			printf("%-12s %18" PRIu64 "/s %10" PRIu64 " MiB/s %16"PRIu64 " %16" PRIu64 "\n",
7269424e9ebSKonrad Sztyber 			       tmp, xfer_per_sec, bw_in_MiBps, worker->xfer_failed,
7279424e9ebSKonrad Sztyber 			       worker->injected_miscompares);
7289f51cf32Spaul luse 		}
7299f51cf32Spaul luse 
7309f51cf32Spaul luse 		worker = worker->next;
7319f51cf32Spaul luse 	}
7329f51cf32Spaul luse 
7339f51cf32Spaul luse 	total_xfer_per_sec = total_completed / g_time_in_sec;
7349f51cf32Spaul luse 	total_bw_in_MiBps = (total_completed * g_xfer_size_bytes) /
7359f51cf32Spaul luse 			    (g_time_in_sec * 1024 * 1024);
7369f51cf32Spaul luse 
7379424e9ebSKonrad Sztyber 	printf("====================================================================================\n");
7389424e9ebSKonrad Sztyber 	printf("%-12s %18" PRIu64 "/s %10" PRIu64 " MiB/s %16"PRIu64 " %16" PRIu64 "\n",
7399424e9ebSKonrad Sztyber 	       "Total", total_xfer_per_sec, total_bw_in_MiBps, total_failed, total_miscompared);
7409f51cf32Spaul luse 
7419f51cf32Spaul luse 	return total_failed ? 1 : 0;
7429f51cf32Spaul luse }
7439f51cf32Spaul luse 
744e150f6b8SZiye Yang static inline void
745e150f6b8SZiye Yang _free_task_buffers_in_pool(struct worker_thread *worker)
746e150f6b8SZiye Yang {
747e150f6b8SZiye Yang 	struct ap_task *task;
748e150f6b8SZiye Yang 
749e150f6b8SZiye Yang 	assert(worker);
750e150f6b8SZiye Yang 	while ((task = TAILQ_FIRST(&worker->tasks_pool))) {
751e150f6b8SZiye Yang 		TAILQ_REMOVE(&worker->tasks_pool, task, link);
752e150f6b8SZiye Yang 		_free_task_buffers(task);
753e150f6b8SZiye Yang 	}
754e150f6b8SZiye Yang }
755e150f6b8SZiye Yang 
7569f51cf32Spaul luse static int
7579f51cf32Spaul luse _check_draining(void *arg)
7589f51cf32Spaul luse {
7599f51cf32Spaul luse 	struct worker_thread *worker = arg;
7609f51cf32Spaul luse 
7619f51cf32Spaul luse 	assert(worker);
7629f51cf32Spaul luse 
7639f51cf32Spaul luse 	if (worker->current_queue_depth == 0) {
764e150f6b8SZiye Yang 		_free_task_buffers_in_pool(worker);
7659f51cf32Spaul luse 		spdk_poller_unregister(&worker->is_draining_poller);
7669f51cf32Spaul luse 		unregister_worker(worker);
7679f51cf32Spaul luse 	}
7689f51cf32Spaul luse 
769fa9e703fSpaul Luse 	return SPDK_POLLER_BUSY;
7709f51cf32Spaul luse }
7719f51cf32Spaul luse 
7729f51cf32Spaul luse static int
7739f51cf32Spaul luse _worker_stop(void *arg)
7749f51cf32Spaul luse {
7759f51cf32Spaul luse 	struct worker_thread *worker = arg;
7769f51cf32Spaul luse 
7779f51cf32Spaul luse 	assert(worker);
7789f51cf32Spaul luse 
7799f51cf32Spaul luse 	spdk_poller_unregister(&worker->stop_poller);
7809f51cf32Spaul luse 
7819f51cf32Spaul luse 	/* now let the worker drain and check it's outstanding IO with a poller */
7829f51cf32Spaul luse 	worker->is_draining = true;
783ab0bc5c2SShuhei Matsumoto 	worker->is_draining_poller = SPDK_POLLER_REGISTER(_check_draining, worker, 0);
7849f51cf32Spaul luse 
785fa9e703fSpaul Luse 	return SPDK_POLLER_BUSY;
7869f51cf32Spaul luse }
7879f51cf32Spaul luse 
78820b5c460STomasz Zawadzki static void shutdown_cb(void);
78920b5c460STomasz Zawadzki 
7909f51cf32Spaul luse static void
791a34fc12bSpaul luse _init_thread(void *arg1)
792a34fc12bSpaul luse {
793a34fc12bSpaul luse 	struct worker_thread *worker;
794a34fc12bSpaul luse 	struct ap_task *task;
795998b5d66Spaul luse 	int i, num_tasks = g_allocate_depth;
796445fe74eSpaul luse 	struct display_info *display = arg1;
797a34fc12bSpaul luse 
798a34fc12bSpaul luse 	worker = calloc(1, sizeof(*worker));
799a34fc12bSpaul luse 	if (worker == NULL) {
800a34fc12bSpaul luse 		fprintf(stderr, "Unable to allocate worker\n");
801445fe74eSpaul luse 		free(display);
80220b5c460STomasz Zawadzki 		spdk_thread_exit(spdk_get_thread());
80320b5c460STomasz Zawadzki 		goto no_worker;
804a34fc12bSpaul luse 	}
805a34fc12bSpaul luse 
80613067997Spaul luse 	worker->workload = g_workload_selection;
807445fe74eSpaul luse 	worker->display.core = display->core;
808445fe74eSpaul luse 	worker->display.thread = display->thread;
809445fe74eSpaul luse 	free(display);
8109f51cf32Spaul luse 	worker->core = spdk_env_get_current_core();
8119f51cf32Spaul luse 	worker->thread = spdk_get_thread();
812eea826a2Spaul luse 	pthread_mutex_lock(&g_workers_lock);
813eea826a2Spaul luse 	g_num_workers++;
8149f51cf32Spaul luse 	worker->next = g_workers;
815eea826a2Spaul luse 	g_workers = worker;
816eea826a2Spaul luse 	pthread_mutex_unlock(&g_workers_lock);
81734c48f1bSBen Walker 	worker->ch = spdk_accel_get_io_channel();
8182dd64cf9Spaul luse 	if (worker->ch == NULL) {
8192dd64cf9Spaul luse 		fprintf(stderr, "Unable to get an accel channel\n");
8202dd64cf9Spaul luse 		goto error;
8212dd64cf9Spaul luse 	}
822b9218b7aSpaul luse 
823f17e6705Spaul luse 	TAILQ_INIT(&worker->tasks_pool);
824f17e6705Spaul luse 
825ac9a1a83Spaul luse 	worker->task_base = calloc(num_tasks, sizeof(struct ap_task));
826ac9a1a83Spaul luse 	if (worker->task_base == NULL) {
827ac9a1a83Spaul luse 		fprintf(stderr, "Could not allocate task base.\n");
828ac9a1a83Spaul luse 		goto error;
8290cecfcb1Spaul luse 	}
830ac9a1a83Spaul luse 
831ac9a1a83Spaul luse 	task = worker->task_base;
832ac9a1a83Spaul luse 	for (i = 0; i < num_tasks; i++) {
833ac9a1a83Spaul luse 		TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link);
8344cd7ca9bSJim Harris 		task->worker = worker;
835ac9a1a83Spaul luse 		if (_get_task_data_bufs(task)) {
836ac9a1a83Spaul luse 			fprintf(stderr, "Unable to get data bufs\n");
837ac9a1a83Spaul luse 			goto error;
838ac9a1a83Spaul luse 		}
839ac9a1a83Spaul luse 		task++;
8409f51cf32Spaul luse 	}
8419f51cf32Spaul luse 
8429f51cf32Spaul luse 	/* Register a poller that will stop the worker at time elapsed */
843ab0bc5c2SShuhei Matsumoto 	worker->stop_poller = SPDK_POLLER_REGISTER(_worker_stop, worker,
8449f51cf32Spaul luse 			      g_time_in_sec * 1000000ULL);
8459f51cf32Spaul luse 
846998b5d66Spaul luse 	/* Load up queue depth worth of operations. */
847998b5d66Spaul luse 	for (i = 0; i < g_queue_depth; i++) {
848ac9a1a83Spaul luse 		task = _get_task(worker);
849ac9a1a83Spaul luse 		if (task == NULL) {
850a34fc12bSpaul luse 			goto error;
851b9218b7aSpaul luse 		}
852b9218b7aSpaul luse 
8539f51cf32Spaul luse 		_submit_single(worker, task);
8549f51cf32Spaul luse 	}
855a34fc12bSpaul luse 	return;
856a34fc12bSpaul luse error:
857e150f6b8SZiye Yang 
858e150f6b8SZiye Yang 	_free_task_buffers_in_pool(worker);
859ac9a1a83Spaul luse 	free(worker->task_base);
86020b5c460STomasz Zawadzki no_worker:
86120b5c460STomasz Zawadzki 	shutdown_cb();
86220b5c460STomasz Zawadzki 	g_rc = -1;
8639f51cf32Spaul luse }
8649f51cf32Spaul luse 
8659f51cf32Spaul luse static void
8669f51cf32Spaul luse accel_perf_start(void *arg1)
8679f51cf32Spaul luse {
868eea826a2Spaul luse 	struct spdk_cpuset tmp_cpumask = {};
869eea826a2Spaul luse 	char thread_name[32];
870eea826a2Spaul luse 	uint32_t i;
871445fe74eSpaul luse 	int j;
872eea826a2Spaul luse 	struct spdk_thread *thread;
873445fe74eSpaul luse 	struct display_info *display;
874514be889Spaul luse 
8759f51cf32Spaul luse 	g_tsc_rate = spdk_get_ticks_hz();
8769f51cf32Spaul luse 	g_tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate;
8779f51cf32Spaul luse 
8789260fa0cSpaul luse 	dump_user_config();
8799260fa0cSpaul 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 
90327e85f52SBen Walker static void
90427e85f52SBen Walker accel_perf_free_compress_segs(void)
90527e85f52SBen Walker {
90627e85f52SBen Walker 	struct ap_compress_seg *seg, *tmp;
90727e85f52SBen Walker 
90827e85f52SBen Walker 	STAILQ_FOREACH_SAFE(seg, &g_compress_segs, link, tmp) {
90927e85f52SBen Walker 		free(seg->uncompressed_iovs);
91027e85f52SBen Walker 		free(seg->compressed_iovs);
91127e85f52SBen Walker 		spdk_dma_free(seg->compressed_data);
91227e85f52SBen Walker 		spdk_dma_free(seg->uncompressed_data);
91327e85f52SBen Walker 		STAILQ_REMOVE_HEAD(&g_compress_segs, link);
91427e85f52SBen Walker 		free(seg);
91527e85f52SBen Walker 	}
91627e85f52SBen Walker }
91727e85f52SBen Walker 
91827e85f52SBen Walker struct accel_perf_prep_ctx {
91927e85f52SBen Walker 	FILE			*file;
92027e85f52SBen Walker 	long			remaining;
92127e85f52SBen Walker 	struct spdk_io_channel	*ch;
92227e85f52SBen Walker 	struct ap_compress_seg	*cur_seg;
92327e85f52SBen Walker };
92427e85f52SBen Walker 
92527e85f52SBen Walker static void accel_perf_prep_process_seg(struct accel_perf_prep_ctx *ctx);
92627e85f52SBen Walker 
92727e85f52SBen Walker static void
92827e85f52SBen Walker accel_perf_prep_process_seg_cpl(void *ref, int status)
92927e85f52SBen Walker {
93027e85f52SBen Walker 	struct accel_perf_prep_ctx *ctx = ref;
93127e85f52SBen Walker 	struct ap_compress_seg *seg;
93227e85f52SBen Walker 
93327e85f52SBen Walker 	if (status != 0) {
93427e85f52SBen Walker 		fprintf(stderr, "error (%d) on initial compress completion\n", status);
93527e85f52SBen Walker 		spdk_dma_free(ctx->cur_seg->compressed_data);
93627e85f52SBen Walker 		spdk_dma_free(ctx->cur_seg->uncompressed_data);
93727e85f52SBen Walker 		free(ctx->cur_seg);
93827e85f52SBen Walker 		spdk_put_io_channel(ctx->ch);
93927e85f52SBen Walker 		fclose(ctx->file);
94027e85f52SBen Walker 		free(ctx);
94127e85f52SBen Walker 		spdk_app_stop(-status);
94227e85f52SBen Walker 		return;
94327e85f52SBen Walker 	}
94427e85f52SBen Walker 
94527e85f52SBen Walker 	seg = ctx->cur_seg;
94627e85f52SBen Walker 
9475105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_DECOMPRESS) {
94827e85f52SBen Walker 		seg->compressed_iovs = calloc(g_chained_count, sizeof(struct iovec));
94927e85f52SBen Walker 		if (seg->compressed_iovs == NULL) {
95027e85f52SBen Walker 			fprintf(stderr, "unable to allocate iovec\n");
95127e85f52SBen Walker 			spdk_dma_free(seg->compressed_data);
95227e85f52SBen Walker 			spdk_dma_free(seg->uncompressed_data);
95327e85f52SBen Walker 			free(seg);
95427e85f52SBen Walker 			spdk_put_io_channel(ctx->ch);
95527e85f52SBen Walker 			fclose(ctx->file);
95627e85f52SBen Walker 			free(ctx);
95727e85f52SBen Walker 			spdk_app_stop(-ENOMEM);
95827e85f52SBen Walker 			return;
95927e85f52SBen Walker 		}
96027e85f52SBen Walker 		seg->compressed_iovcnt = g_chained_count;
96127e85f52SBen Walker 
96227e85f52SBen Walker 		accel_perf_construct_iovs(seg->compressed_data, seg->compressed_len, seg->compressed_iovs,
96327e85f52SBen Walker 					  seg->compressed_iovcnt);
96427e85f52SBen Walker 	}
96527e85f52SBen Walker 
96627e85f52SBen Walker 	STAILQ_INSERT_TAIL(&g_compress_segs, seg, link);
96727e85f52SBen Walker 	ctx->remaining -= seg->uncompressed_len;
96827e85f52SBen Walker 
96927e85f52SBen Walker 	accel_perf_prep_process_seg(ctx);
97027e85f52SBen Walker }
97127e85f52SBen Walker 
97227e85f52SBen Walker static void
97327e85f52SBen Walker accel_perf_prep_process_seg(struct accel_perf_prep_ctx *ctx)
97427e85f52SBen Walker {
97527e85f52SBen Walker 	struct ap_compress_seg *seg;
9766afbf3dbSpaul luse 	int sz, sz_read, sz_padded;
97727e85f52SBen Walker 	void *ubuf, *cbuf;
97827e85f52SBen Walker 	struct iovec iov[1];
97927e85f52SBen Walker 	int rc;
98027e85f52SBen Walker 
98127e85f52SBen Walker 	if (ctx->remaining == 0) {
98227e85f52SBen Walker 		spdk_put_io_channel(ctx->ch);
98327e85f52SBen Walker 		fclose(ctx->file);
98427e85f52SBen Walker 		free(ctx);
98527e85f52SBen Walker 		accel_perf_start(NULL);
98627e85f52SBen Walker 		return;
98727e85f52SBen Walker 	}
98827e85f52SBen Walker 
98927e85f52SBen Walker 	sz = spdk_min(ctx->remaining, g_xfer_size_bytes);
9906afbf3dbSpaul luse 	/* Add 10% pad to the compress buffer for incompressible data. Note that a real app
9916afbf3dbSpaul luse 	 * would likely either deal with the failure of not having a large enough buffer
9926afbf3dbSpaul luse 	 * by submitting another operation with a larger one.  Or, like the vbdev module
9936afbf3dbSpaul luse 	 * does, just accept the error and use the data uncompressed marking it as such in
9946afbf3dbSpaul luse 	 * its own metadata so that in the future it doesn't try to decompress uncompressed
9956afbf3dbSpaul luse 	 * data, etc.
9966afbf3dbSpaul luse 	 */
9976afbf3dbSpaul luse 	sz_padded = sz * COMP_BUF_PAD_PERCENTAGE;
99827e85f52SBen Walker 
99927e85f52SBen Walker 	ubuf = spdk_dma_zmalloc(sz, ALIGN_4K, NULL);
100027e85f52SBen Walker 	if (!ubuf) {
100127e85f52SBen Walker 		fprintf(stderr, "unable to allocate uncompress buffer\n");
100227e85f52SBen Walker 		rc = -ENOMEM;
100327e85f52SBen Walker 		goto error;
100427e85f52SBen Walker 	}
100527e85f52SBen Walker 
10066afbf3dbSpaul luse 	cbuf = spdk_dma_malloc(sz_padded, ALIGN_4K, NULL);
100727e85f52SBen Walker 	if (!cbuf) {
100827e85f52SBen Walker 		fprintf(stderr, "unable to allocate compress buffer\n");
100927e85f52SBen Walker 		rc = -ENOMEM;
101027e85f52SBen Walker 		spdk_dma_free(ubuf);
101127e85f52SBen Walker 		goto error;
101227e85f52SBen Walker 	}
101327e85f52SBen Walker 
101427e85f52SBen Walker 	seg = calloc(1, sizeof(*seg));
101527e85f52SBen Walker 	if (!seg) {
101627e85f52SBen Walker 		fprintf(stderr, "unable to allocate comp/decomp segment\n");
101727e85f52SBen Walker 		spdk_dma_free(ubuf);
101827e85f52SBen Walker 		spdk_dma_free(cbuf);
101927e85f52SBen Walker 		rc = -ENOMEM;
102027e85f52SBen Walker 		goto error;
102127e85f52SBen Walker 	}
102227e85f52SBen Walker 
102327e85f52SBen Walker 	sz_read = fread(ubuf, sizeof(uint8_t), sz, ctx->file);
102427e85f52SBen Walker 	if (sz_read != sz) {
102527e85f52SBen Walker 		fprintf(stderr, "unable to read input file\n");
102627e85f52SBen Walker 		free(seg);
102727e85f52SBen Walker 		spdk_dma_free(ubuf);
102827e85f52SBen Walker 		spdk_dma_free(cbuf);
102927e85f52SBen Walker 		rc = -errno;
103027e85f52SBen Walker 		goto error;
103127e85f52SBen Walker 	}
103227e85f52SBen Walker 
10335105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_COMPRESS) {
103427e85f52SBen Walker 		seg->uncompressed_iovs = calloc(g_chained_count, sizeof(struct iovec));
103527e85f52SBen Walker 		if (seg->uncompressed_iovs == NULL) {
103627e85f52SBen Walker 			fprintf(stderr, "unable to allocate iovec\n");
103727e85f52SBen Walker 			free(seg);
103827e85f52SBen Walker 			spdk_dma_free(ubuf);
103927e85f52SBen Walker 			spdk_dma_free(cbuf);
104027e85f52SBen Walker 			rc = -ENOMEM;
104127e85f52SBen Walker 			goto error;
104227e85f52SBen Walker 		}
104327e85f52SBen Walker 		seg->uncompressed_iovcnt = g_chained_count;
104427e85f52SBen Walker 		accel_perf_construct_iovs(ubuf, sz, seg->uncompressed_iovs, seg->uncompressed_iovcnt);
104527e85f52SBen Walker 	}
104627e85f52SBen Walker 
104727e85f52SBen Walker 	seg->uncompressed_data = ubuf;
104827e85f52SBen Walker 	seg->uncompressed_len = sz;
104927e85f52SBen Walker 	seg->compressed_data = cbuf;
105027e85f52SBen Walker 	seg->compressed_len = sz;
10516afbf3dbSpaul luse 	seg->compressed_len_padded = sz_padded;
105227e85f52SBen Walker 
105327e85f52SBen Walker 	ctx->cur_seg = seg;
105427e85f52SBen Walker 	iov[0].iov_base = seg->uncompressed_data;
105527e85f52SBen Walker 	iov[0].iov_len = seg->uncompressed_len;
105627e85f52SBen Walker 	/* Note that anytime a call is made to spdk_accel_submit_compress() there's a chance
105727e85f52SBen Walker 	 * it will fail with -ENOMEM in the event that the destination buffer is not large enough
10586afbf3dbSpaul luse 	 * to hold the compressed data.  This example app simply adds 10% buffer for compressed data
10596afbf3dbSpaul luse 	 * but real applications may want to consider a more sophisticated method.
106027e85f52SBen Walker 	 */
10616afbf3dbSpaul luse 	rc = spdk_accel_submit_compress(ctx->ch, seg->compressed_data, seg->compressed_len_padded, iov, 1,
106227e85f52SBen Walker 					&seg->compressed_len, 0, accel_perf_prep_process_seg_cpl, ctx);
106327e85f52SBen Walker 	if (rc < 0) {
106427e85f52SBen Walker 		fprintf(stderr, "error (%d) on initial compress submission\n", rc);
106527e85f52SBen Walker 		goto error;
106627e85f52SBen Walker 	}
106727e85f52SBen Walker 
106827e85f52SBen Walker 	return;
106927e85f52SBen Walker 
107027e85f52SBen Walker error:
107127e85f52SBen Walker 	spdk_put_io_channel(ctx->ch);
107227e85f52SBen Walker 	fclose(ctx->file);
107327e85f52SBen Walker 	free(ctx);
107427e85f52SBen Walker 	spdk_app_stop(rc);
107527e85f52SBen Walker }
107627e85f52SBen Walker 
107727e85f52SBen Walker static void
107827e85f52SBen Walker accel_perf_prep(void *arg1)
107927e85f52SBen Walker {
108027e85f52SBen Walker 	struct accel_perf_prep_ctx *ctx;
1081*0a6b7098STomasz Zawadzki 	const char *module_name = NULL;
108227e85f52SBen Walker 	int rc = 0;
108327e85f52SBen Walker 
1084*0a6b7098STomasz Zawadzki 	if (g_module_name) {
1085*0a6b7098STomasz Zawadzki 		rc = spdk_accel_get_opc_module_name(g_workload_selection, &module_name);
1086*0a6b7098STomasz Zawadzki 		if (rc != 0 || strcmp(g_module_name, module_name) != 0) {
1087*0a6b7098STomasz Zawadzki 			fprintf(stderr, "Module '%s' was assigned via JSON config or RPC, instead of '%s'\n",
1088*0a6b7098STomasz Zawadzki 				module_name, g_module_name);
1089*0a6b7098STomasz Zawadzki 			fprintf(stderr, "-M option is not compatible with accel_assign_opc RPC\n");
1090*0a6b7098STomasz Zawadzki 			rc = -EINVAL;
1091*0a6b7098STomasz Zawadzki 			goto error_end;
1092*0a6b7098STomasz Zawadzki 		}
1093*0a6b7098STomasz Zawadzki 	}
1094*0a6b7098STomasz Zawadzki 
10955105dc5dSKonrad Sztyber 	if (g_workload_selection != SPDK_ACCEL_OPC_COMPRESS &&
10965105dc5dSKonrad Sztyber 	    g_workload_selection != SPDK_ACCEL_OPC_DECOMPRESS) {
109727e85f52SBen Walker 		accel_perf_start(arg1);
109827e85f52SBen Walker 		return;
109927e85f52SBen Walker 	}
110027e85f52SBen Walker 
110127e85f52SBen Walker 	if (g_cd_file_in_name == NULL) {
110227e85f52SBen Walker 		fprintf(stdout, "A filename is required.\n");
110327e85f52SBen Walker 		rc = -EINVAL;
110427e85f52SBen Walker 		goto error_end;
110527e85f52SBen Walker 	}
110627e85f52SBen Walker 
11075105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_COMPRESS && g_verify) {
110827e85f52SBen Walker 		fprintf(stdout, "\nCompression does not support the verify option, aborting.\n");
110927e85f52SBen Walker 		rc = -ENOTSUP;
111027e85f52SBen Walker 		goto error_end;
111127e85f52SBen Walker 	}
111227e85f52SBen Walker 
111327e85f52SBen Walker 	printf("Preparing input file...\n");
111427e85f52SBen Walker 
111527e85f52SBen Walker 	ctx = calloc(1, sizeof(*ctx));
111627e85f52SBen Walker 	if (ctx == NULL) {
111727e85f52SBen Walker 		rc = -ENOMEM;
111827e85f52SBen Walker 		goto error_end;
111927e85f52SBen Walker 	}
112027e85f52SBen Walker 
112127e85f52SBen Walker 	ctx->file = fopen(g_cd_file_in_name, "r");
112227e85f52SBen Walker 	if (ctx->file == NULL) {
112327e85f52SBen Walker 		fprintf(stderr, "Could not open file %s.\n", g_cd_file_in_name);
112427e85f52SBen Walker 		rc = -errno;
112527e85f52SBen Walker 		goto error_ctx;
112627e85f52SBen Walker 	}
112727e85f52SBen Walker 
112827e85f52SBen Walker 	fseek(ctx->file, 0L, SEEK_END);
112927e85f52SBen Walker 	ctx->remaining = ftell(ctx->file);
113027e85f52SBen Walker 	fseek(ctx->file, 0L, SEEK_SET);
113127e85f52SBen Walker 
113227e85f52SBen Walker 	ctx->ch = spdk_accel_get_io_channel();
113327e85f52SBen Walker 	if (ctx->ch == NULL) {
113427e85f52SBen Walker 		rc = -EAGAIN;
113527e85f52SBen Walker 		goto error_file;
113627e85f52SBen Walker 	}
113727e85f52SBen Walker 
113827e85f52SBen Walker 	if (g_xfer_size_bytes == 0) {
113927e85f52SBen Walker 		/* size of 0 means "file at a time" */
114027e85f52SBen Walker 		g_xfer_size_bytes = ctx->remaining;
114127e85f52SBen Walker 	}
114227e85f52SBen Walker 
114327e85f52SBen Walker 	accel_perf_prep_process_seg(ctx);
114427e85f52SBen Walker 	return;
114527e85f52SBen Walker 
114627e85f52SBen Walker error_file:
114727e85f52SBen Walker 	fclose(ctx->file);
114827e85f52SBen Walker error_ctx:
114927e85f52SBen Walker 	free(ctx);
115027e85f52SBen Walker error_end:
115127e85f52SBen Walker 	spdk_app_stop(rc);
115227e85f52SBen Walker }
115327e85f52SBen Walker 
11544c7b5046SKonrad Sztyber static void
11554c7b5046SKonrad Sztyber worker_shutdown(void *ctx)
11564c7b5046SKonrad Sztyber {
11574c7b5046SKonrad Sztyber 	_worker_stop(ctx);
11584c7b5046SKonrad Sztyber }
11594c7b5046SKonrad Sztyber 
11604c7b5046SKonrad Sztyber static void
11614c7b5046SKonrad Sztyber shutdown_cb(void)
11624c7b5046SKonrad Sztyber {
11634c7b5046SKonrad Sztyber 	struct worker_thread *worker;
11644c7b5046SKonrad Sztyber 
11654c7b5046SKonrad Sztyber 	pthread_mutex_lock(&g_workers_lock);
1166da5ec81fSKonrad Sztyber 	if (!g_workers) {
1167da5ec81fSKonrad Sztyber 		spdk_app_stop(1);
1168da5ec81fSKonrad Sztyber 		goto unlock;
1169da5ec81fSKonrad Sztyber 	}
1170da5ec81fSKonrad Sztyber 
11714c7b5046SKonrad Sztyber 	worker = g_workers;
11724c7b5046SKonrad Sztyber 	while (worker) {
11734c7b5046SKonrad Sztyber 		spdk_thread_send_msg(worker->thread, worker_shutdown, worker);
11744c7b5046SKonrad Sztyber 		worker = worker->next;
11754c7b5046SKonrad Sztyber 	}
1176da5ec81fSKonrad Sztyber unlock:
11774c7b5046SKonrad Sztyber 	pthread_mutex_unlock(&g_workers_lock);
11784c7b5046SKonrad Sztyber }
11794c7b5046SKonrad Sztyber 
11809f51cf32Spaul luse int
11819f51cf32Spaul luse main(int argc, char **argv)
11829f51cf32Spaul luse {
11839f51cf32Spaul luse 	struct worker_thread *worker, *tmp;
1184f5b5a9c4SKonrad Sztyber 	int rc;
11859f51cf32Spaul luse 
11869f51cf32Spaul luse 	pthread_mutex_init(&g_workers_lock, NULL);
11879260fa0cSpaul luse 	spdk_app_opts_init(&g_opts, sizeof(g_opts));
11889260fa0cSpaul luse 	g_opts.name = "accel_perf";
11899260fa0cSpaul luse 	g_opts.reactor_mask = "0x1";
11904c7b5046SKonrad Sztyber 	g_opts.shutdown_cb = shutdown_cb;
1191f5b5a9c4SKonrad Sztyber 
1192*0a6b7098STomasz Zawadzki 	rc = spdk_app_parse_args(argc, argv, &g_opts, "a:C:o:q:t:yw:M:P:f:T:l:S:x:", NULL,
1193f5b5a9c4SKonrad Sztyber 				 parse_args, usage);
1194f5b5a9c4SKonrad Sztyber 	if (rc != SPDK_APP_PARSE_ARGS_SUCCESS) {
1195f5b5a9c4SKonrad Sztyber 		return rc == SPDK_APP_PARSE_ARGS_HELP ? 0 : 1;
11969f51cf32Spaul luse 	}
11979f51cf32Spaul luse 
119884dbdf08STomasz Zawadzki 	if (g_workload_selection == SPDK_ACCEL_OPC_LAST) {
119984dbdf08STomasz Zawadzki 		fprintf(stderr, "Must provide a workload type\n");
12002a0c66d0Spaul luse 		usage();
1201724f3006STomasz Zawadzki 		return -1;
12022a0c66d0Spaul luse 	}
12032a0c66d0Spaul luse 
1204e1bf63afSJim Harris 	if (g_allocate_depth > 0 && g_queue_depth > g_allocate_depth) {
1205e1bf63afSJim Harris 		fprintf(stdout, "allocate depth must be at least as big as queue depth\n");
1206e1bf63afSJim Harris 		usage();
1207724f3006STomasz Zawadzki 		return -1;
1208e1bf63afSJim Harris 	}
1209e1bf63afSJim Harris 
1210e1bf63afSJim Harris 	if (g_allocate_depth == 0) {
1211e1bf63afSJim Harris 		g_allocate_depth = g_queue_depth;
1212e1bf63afSJim Harris 	}
1213e1bf63afSJim Harris 
12145105dc5dSKonrad Sztyber 	if ((g_workload_selection == SPDK_ACCEL_OPC_CRC32C ||
12155105dc5dSKonrad Sztyber 	     g_workload_selection == SPDK_ACCEL_OPC_COPY_CRC32C) &&
1216850cd900Spaul luse 	    g_chained_count == 0) {
121788754353SZiye Yang 		usage();
1218724f3006STomasz Zawadzki 		return -1;
121988754353SZiye Yang 	}
122088754353SZiye Yang 
12215105dc5dSKonrad Sztyber 	if (g_workload_selection == SPDK_ACCEL_OPC_XOR && g_xor_src_count < 2) {
1222a5d5ab27SArtur Paszkiewicz 		usage();
1223724f3006STomasz Zawadzki 		return -1;
1224a5d5ab27SArtur Paszkiewicz 	}
1225a5d5ab27SArtur Paszkiewicz 
1226*0a6b7098STomasz Zawadzki 	if (g_module_name && spdk_accel_assign_opc(g_workload_selection, g_module_name)) {
1227*0a6b7098STomasz Zawadzki 		fprintf(stderr, "Was not able to assign '%s' module to the workload\n", g_module_name);
1228*0a6b7098STomasz Zawadzki 		usage();
1229*0a6b7098STomasz Zawadzki 		return -1;
1230*0a6b7098STomasz Zawadzki 	}
1231*0a6b7098STomasz Zawadzki 
123227e85f52SBen Walker 	g_rc = spdk_app_start(&g_opts, accel_perf_prep, NULL);
12339b189667Spaul luse 	if (g_rc) {
12349f51cf32Spaul luse 		SPDK_ERRLOG("ERROR starting application\n");
12359f51cf32Spaul luse 	}
12369f51cf32Spaul luse 
12379f51cf32Spaul luse 	pthread_mutex_destroy(&g_workers_lock);
12389f51cf32Spaul luse 
12399f51cf32Spaul luse 	worker = g_workers;
12409f51cf32Spaul luse 	while (worker) {
12419f51cf32Spaul luse 		tmp = worker->next;
12429f51cf32Spaul luse 		free(worker);
12439f51cf32Spaul luse 		worker = tmp;
12449f51cf32Spaul luse 	}
124527e85f52SBen Walker 	accel_perf_free_compress_segs();
12469f51cf32Spaul luse 	spdk_app_fini();
12479b189667Spaul luse 	return g_rc;
12489f51cf32Spaul luse }
1249