xref: /spdk/examples/accel/perf/accel_perf.c (revision cdefd3d3f8f7effdd2bb78f5e893226d875d1464)
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;
529f51cf32Spaul luse static int g_time_in_sec = 5;
53e69375bfSpaul luse static uint32_t g_crc32c_seed = 0;
54b9218b7aSpaul luse static int g_fail_percent_goal = 0;
5589495464Spaul luse static uint8_t g_fill_pattern = 255;
569f51cf32Spaul luse static bool g_verify = false;
572a0c66d0Spaul luse static const char *g_workload_type = NULL;
58514be889Spaul luse static enum accel_capability g_workload_selection;
599f51cf32Spaul luse static struct worker_thread *g_workers = NULL;
609f51cf32Spaul luse static int g_num_workers = 0;
619f51cf32Spaul luse static pthread_mutex_t g_workers_lock = PTHREAD_MUTEX_INITIALIZER;
62a34fc12bSpaul luse uint64_t g_capabilites;
63*cdefd3d3Spaul luse 
64*cdefd3d3Spaul luse struct worker_thread;
65*cdefd3d3Spaul luse static void accel_done(void *ref, int status);
66*cdefd3d3Spaul luse 
67*cdefd3d3Spaul luse struct ap_task {
68*cdefd3d3Spaul luse 	void			*src;
69*cdefd3d3Spaul luse 	void			*dst;
70*cdefd3d3Spaul luse 	void			*dst2;
71*cdefd3d3Spaul luse 	struct worker_thread	*worker;
72*cdefd3d3Spaul luse 	int			status;
73*cdefd3d3Spaul luse 	int			expected_status; /* used for the compare operation */
74*cdefd3d3Spaul luse 	TAILQ_ENTRY(ap_task)	link;
75*cdefd3d3Spaul luse };
769f51cf32Spaul luse 
779f51cf32Spaul luse struct worker_thread {
789f51cf32Spaul luse 	struct spdk_io_channel		*ch;
799f51cf32Spaul luse 	uint64_t			xfer_completed;
809f51cf32Spaul luse 	uint64_t			xfer_failed;
81b9218b7aSpaul luse 	uint64_t			injected_miscompares;
829f51cf32Spaul luse 	uint64_t			current_queue_depth;
830cecfcb1Spaul luse 	TAILQ_HEAD(, ap_task)		tasks;
849f51cf32Spaul luse 	struct worker_thread		*next;
859f51cf32Spaul luse 	unsigned			core;
869f51cf32Spaul luse 	struct spdk_thread		*thread;
879f51cf32Spaul luse 	bool				is_draining;
889f51cf32Spaul luse 	struct spdk_poller		*is_draining_poller;
899f51cf32Spaul luse 	struct spdk_poller		*stop_poller;
909f51cf32Spaul luse };
919f51cf32Spaul luse 
929f51cf32Spaul luse static void
939f51cf32Spaul luse dump_user_config(struct spdk_app_opts *opts)
949f51cf32Spaul luse {
959f51cf32Spaul luse 	printf("SPDK Configuration:\n");
969f51cf32Spaul luse 	printf("Core mask:      %s\n\n", opts->reactor_mask);
979f51cf32Spaul luse 	printf("Accel Perf Configuration:\n");
982a0c66d0Spaul luse 	printf("Workload Type:  %s\n", g_workload_type);
99b9218b7aSpaul luse 	if (g_workload_selection == ACCEL_CRC32C) {
100b9218b7aSpaul luse 		printf("CRC-32C seed:   %u\n", g_crc32c_seed);
10189495464Spaul luse 	} else if (g_workload_selection == ACCEL_FILL) {
10289495464Spaul luse 		printf("Fill pattern:   0x%x\n", g_fill_pattern);
103b9218b7aSpaul luse 	} else if ((g_workload_selection == ACCEL_COMPARE) && g_fail_percent_goal > 0) {
10489495464Spaul luse 		printf("Failure inject: %u percent\n", g_fail_percent_goal);
105e69375bfSpaul luse 	}
1069f51cf32Spaul luse 	printf("Transfer size:  %u bytes\n", g_xfer_size_bytes);
1079f51cf32Spaul luse 	printf("Queue depth:    %u\n", g_queue_depth);
1089f51cf32Spaul luse 	printf("Run time:       %u seconds\n", g_time_in_sec);
1099f51cf32Spaul luse 	printf("Verify:         %s\n\n", g_verify ? "Yes" : "No");
1109f51cf32Spaul luse }
1119f51cf32Spaul luse 
1129f51cf32Spaul luse static void
1139f51cf32Spaul luse usage(void)
1149f51cf32Spaul luse {
1159f51cf32Spaul luse 	printf("accel_perf options:\n");
1169f51cf32Spaul luse 	printf("\t[-h help message]\n");
1179f51cf32Spaul luse 	printf("\t[-q queue depth]\n");
1189f51cf32Spaul luse 	printf("\t[-n number of channels]\n");
1199f51cf32Spaul luse 	printf("\t[-o transfer size in bytes]\n");
1209f51cf32Spaul luse 	printf("\t[-t time in seconds]\n");
1210ef079c6Spaul luse 	printf("\t[-w workload type must be one of these: copy, fill, crc32c, compare, dualcast\n");
122e69375bfSpaul luse 	printf("\t[-s for crc32c workload, use this seed value (default 0)\n");
123b9218b7aSpaul luse 	printf("\t[-P for compare workload, percentage of operations that should miscompare (percent, default 0)\n");
12489495464Spaul luse 	printf("\t[-f for fill workload, use this BYTE value (default 255)\n");
1252a0c66d0Spaul luse 	printf("\t[-y verify result if this switch is on]\n");
1269f51cf32Spaul luse }
1279f51cf32Spaul luse 
1289f51cf32Spaul luse static int
1299f51cf32Spaul luse parse_args(int argc, char *argv)
1309f51cf32Spaul luse {
1319f51cf32Spaul luse 	switch (argc) {
13289495464Spaul luse 	case 'f':
13389495464Spaul luse 		g_fill_pattern = (uint8_t)spdk_strtol(optarg, 10);
13489495464Spaul luse 		break;
1359f51cf32Spaul luse 	case 'o':
1369f51cf32Spaul luse 		g_xfer_size_bytes = spdk_strtol(optarg, 10);
1379f51cf32Spaul luse 		break;
138b9218b7aSpaul luse 	case 'P':
139b9218b7aSpaul luse 		g_fail_percent_goal = spdk_strtol(optarg, 10);
140b9218b7aSpaul luse 		break;
1419f51cf32Spaul luse 	case 'q':
1429f51cf32Spaul luse 		g_queue_depth = spdk_strtol(optarg, 10);
1439f51cf32Spaul luse 		break;
144e69375bfSpaul luse 	case 's':
145e69375bfSpaul luse 		g_crc32c_seed = spdk_strtol(optarg, 10);
146e69375bfSpaul luse 		break;
1479f51cf32Spaul luse 	case 't':
1489f51cf32Spaul luse 		g_time_in_sec = spdk_strtol(optarg, 10);
1499f51cf32Spaul luse 		break;
1509f51cf32Spaul luse 	case 'y':
1519f51cf32Spaul luse 		g_verify = true;
1529f51cf32Spaul luse 		break;
1532a0c66d0Spaul luse 	case 'w':
1542a0c66d0Spaul luse 		g_workload_type = optarg;
155514be889Spaul luse 		if (!strcmp(g_workload_type, "copy")) {
156514be889Spaul luse 			g_workload_selection = ACCEL_COPY;
157514be889Spaul luse 		} else if (!strcmp(g_workload_type, "fill")) {
158514be889Spaul luse 			g_workload_selection = ACCEL_FILL;
159e69375bfSpaul luse 		} else if (!strcmp(g_workload_type, "crc32c")) {
160e69375bfSpaul luse 			g_workload_selection = ACCEL_CRC32C;
161b9218b7aSpaul luse 		} else if (!strcmp(g_workload_type, "compare")) {
162b9218b7aSpaul luse 			g_workload_selection = ACCEL_COMPARE;
1630ef079c6Spaul luse 		} else if (!strcmp(g_workload_type, "dualcast")) {
1640ef079c6Spaul luse 			g_workload_selection = ACCEL_DUALCAST;
165514be889Spaul luse 		}
1662a0c66d0Spaul luse 		break;
1679f51cf32Spaul luse 	default:
1689f51cf32Spaul luse 		usage();
1699f51cf32Spaul luse 		return 1;
1709f51cf32Spaul luse 	}
1719f51cf32Spaul luse 	return 0;
1729f51cf32Spaul luse }
1739f51cf32Spaul luse 
1749f51cf32Spaul luse static void
1759f51cf32Spaul luse unregister_worker(void *arg1)
1769f51cf32Spaul luse {
1779f51cf32Spaul luse 	struct worker_thread *worker = arg1;
1780cecfcb1Spaul luse 	struct ap_task *task;
1799f51cf32Spaul luse 
1800cecfcb1Spaul luse 	while (!TAILQ_EMPTY(&worker->tasks)) {
1810cecfcb1Spaul luse 		task = TAILQ_FIRST(&worker->tasks);
1820cecfcb1Spaul luse 		TAILQ_REMOVE(&worker->tasks, task, link);
1830cecfcb1Spaul luse 		free(task);
1840cecfcb1Spaul luse 	}
1859f51cf32Spaul luse 	spdk_put_io_channel(worker->ch);
1869f51cf32Spaul luse 	pthread_mutex_lock(&g_workers_lock);
1879f51cf32Spaul luse 	assert(g_num_workers >= 1);
1889f51cf32Spaul luse 	if (--g_num_workers == 0) {
1899f51cf32Spaul luse 		pthread_mutex_unlock(&g_workers_lock);
1909f51cf32Spaul luse 		spdk_app_stop(0);
1919f51cf32Spaul luse 	}
1929f51cf32Spaul luse 	pthread_mutex_unlock(&g_workers_lock);
1939f51cf32Spaul luse }
1949f51cf32Spaul luse 
1959f51cf32Spaul luse static void accel_done(void *ref, int status);
1969f51cf32Spaul luse 
1979f51cf32Spaul luse static void
1989f51cf32Spaul luse _submit_single(void *arg1, void *arg2)
1999f51cf32Spaul luse {
2009f51cf32Spaul luse 	struct worker_thread *worker = arg1;
2019f51cf32Spaul luse 	struct ap_task *task = arg2;
202b9218b7aSpaul luse 	int random_num;
20340ec8e97Spaul luse 	int rc = 0;
2049f51cf32Spaul luse 
2059f51cf32Spaul luse 	assert(worker);
2069f51cf32Spaul luse 
2079f51cf32Spaul luse 	task->worker = worker;
2089f51cf32Spaul luse 	task->worker->current_queue_depth++;
209e69375bfSpaul luse 	switch (g_workload_selection) {
210e69375bfSpaul luse 	case ACCEL_COPY:
211e8463f87Spaul luse 		rc = spdk_accel_submit_copy(worker->ch, task->dst, task->src,
212e8463f87Spaul luse 					    g_xfer_size_bytes, accel_done, task);
213e69375bfSpaul luse 		break;
214e69375bfSpaul luse 	case ACCEL_FILL:
2152a0c66d0Spaul luse 		/* For fill use the first byte of the task->dst buffer */
216ee7e31f9Spaul luse 		rc = spdk_accel_submit_fill(worker->ch, task->dst, *(uint8_t *)task->src,
217e8463f87Spaul luse 					    g_xfer_size_bytes, accel_done, task);
218e69375bfSpaul luse 		break;
219e69375bfSpaul luse 	case ACCEL_CRC32C:
220e8463f87Spaul luse 		rc = spdk_accel_submit_crc32c(worker->ch, (uint32_t *)task->dst,
221e8463f87Spaul luse 					      task->src, g_crc32c_seed,
222e8463f87Spaul luse 					      g_xfer_size_bytes, accel_done, task);
223e69375bfSpaul luse 		break;
224b9218b7aSpaul luse 	case ACCEL_COMPARE:
225b9218b7aSpaul luse 		random_num = rand() % 100;
226b9218b7aSpaul luse 		if (random_num < g_fail_percent_goal) {
227b9218b7aSpaul luse 			task->expected_status = -EILSEQ;
228b9218b7aSpaul luse 			*(uint8_t *)task->dst = ~DATA_PATTERN;
229b9218b7aSpaul luse 		} else {
230b9218b7aSpaul luse 			task->expected_status = 0;
231b9218b7aSpaul luse 			*(uint8_t *)task->dst = DATA_PATTERN;
232b9218b7aSpaul luse 		}
233ee7e31f9Spaul luse 		rc = spdk_accel_submit_compare(worker->ch, task->dst, task->src,
234e8463f87Spaul luse 					       g_xfer_size_bytes, accel_done, task);
235b9218b7aSpaul luse 		break;
2360ef079c6Spaul luse 	case ACCEL_DUALCAST:
237ee7e31f9Spaul luse 		rc = spdk_accel_submit_dualcast(worker->ch, task->dst, task->dst2,
238e8463f87Spaul luse 						task->src, g_xfer_size_bytes, accel_done, task);
2390ef079c6Spaul luse 		break;
240e69375bfSpaul luse 	default:
2412a0c66d0Spaul luse 		assert(false);
242e69375bfSpaul luse 		break;
243e69375bfSpaul luse 
2442a0c66d0Spaul luse 	}
24540ec8e97Spaul luse 
24640ec8e97Spaul luse 	if (rc) {
247e8463f87Spaul luse 		accel_done(task, rc);
24840ec8e97Spaul luse 	}
2499f51cf32Spaul luse }
2509f51cf32Spaul luse 
2519f51cf32Spaul luse static void
2529f51cf32Spaul luse _accel_done(void *arg1)
2539f51cf32Spaul luse {
2549f51cf32Spaul luse 	struct ap_task *task = arg1;
2559f51cf32Spaul luse 	struct worker_thread *worker = task->worker;
256e69375bfSpaul luse 	uint32_t sw_crc32c;
2579f51cf32Spaul luse 
2589f51cf32Spaul luse 	assert(worker);
2599f51cf32Spaul luse 	assert(worker->current_queue_depth > 0);
2609f51cf32Spaul luse 
261b9218b7aSpaul luse 	if (g_verify && task->status == 0) {
262b9218b7aSpaul luse 		switch (g_workload_selection) {
263b9218b7aSpaul luse 		case ACCEL_CRC32C:
264e69375bfSpaul luse 			/* calculate sw CRC-32C and compare to sw aceel result. */
265e69375bfSpaul luse 			sw_crc32c = spdk_crc32c_update(task->src, g_xfer_size_bytes, ~g_crc32c_seed);
266e69375bfSpaul luse 			if (*(uint32_t *)task->dst != sw_crc32c) {
267e69375bfSpaul luse 				SPDK_NOTICELOG("CRC-32C miscompare\n");
268e69375bfSpaul luse 				worker->xfer_failed++;
269e69375bfSpaul luse 			}
270b9218b7aSpaul luse 			break;
271b9218b7aSpaul luse 		case ACCEL_COPY:
272b9218b7aSpaul luse 			if (memcmp(task->src, task->dst, g_xfer_size_bytes)) {
2739f51cf32Spaul luse 				SPDK_NOTICELOG("Data miscompare\n");
2749f51cf32Spaul luse 				worker->xfer_failed++;
275b9218b7aSpaul luse 			}
276b9218b7aSpaul luse 			break;
2770ef079c6Spaul luse 		case ACCEL_DUALCAST:
2780ef079c6Spaul luse 			if (memcmp(task->src, task->dst, g_xfer_size_bytes)) {
2790ef079c6Spaul luse 				SPDK_NOTICELOG("Data miscompare, first destination\n");
2800ef079c6Spaul luse 				worker->xfer_failed++;
2810ef079c6Spaul luse 			}
2820ef079c6Spaul luse 			if (memcmp(task->src, task->dst2, g_xfer_size_bytes)) {
2830ef079c6Spaul luse 				SPDK_NOTICELOG("Data miscompare, second destination\n");
2840ef079c6Spaul luse 				worker->xfer_failed++;
2850ef079c6Spaul luse 			}
2860ef079c6Spaul luse 			break;
287d207237fSpaul luse 		case ACCEL_FILL:
288d207237fSpaul luse 			if (memcmp(task->dst, task->src, g_xfer_size_bytes)) {
289d207237fSpaul luse 				SPDK_NOTICELOG("Data miscompare\n");
290d207237fSpaul luse 				worker->xfer_failed++;
291d207237fSpaul luse 			}
292d207237fSpaul luse 			break;
2938cee297cSpaul luse 		case ACCEL_COMPARE:
2948cee297cSpaul luse 			break;
295b9218b7aSpaul luse 		default:
296b9218b7aSpaul luse 			assert(false);
297b9218b7aSpaul luse 			break;
2989f51cf32Spaul luse 		}
2999f51cf32Spaul luse 	}
300b9218b7aSpaul luse 
301b9218b7aSpaul luse 	if (task->expected_status == -EILSEQ) {
302b9218b7aSpaul luse 		assert(task->status != 0);
303b9218b7aSpaul luse 		worker->injected_miscompares++;
304b9218b7aSpaul luse 	} else if (task->status) {
305b9218b7aSpaul luse 		/* Expected to pass but API reported error. */
306b9218b7aSpaul luse 		worker->xfer_failed++;
307b9218b7aSpaul luse 	}
308b9218b7aSpaul luse 
3099f51cf32Spaul luse 	worker->xfer_completed++;
3109f51cf32Spaul luse 	worker->current_queue_depth--;
3119f51cf32Spaul luse 
31240ec8e97Spaul luse 	if (!worker->is_draining) {
3139f51cf32Spaul luse 		_submit_single(worker, task);
3149f51cf32Spaul luse 	} else {
315b9218b7aSpaul luse 		spdk_free(task->src);
316b9218b7aSpaul luse 		spdk_free(task->dst);
3170ef079c6Spaul luse 		if (g_workload_selection == ACCEL_DUALCAST) {
3180ef079c6Spaul luse 			spdk_free(task->dst2);
3190ef079c6Spaul luse 		}
3200cecfcb1Spaul luse 		TAILQ_INSERT_TAIL(&worker->tasks, task, link);
3219f51cf32Spaul luse 	}
3229f51cf32Spaul luse }
3239f51cf32Spaul luse 
324a34fc12bSpaul luse static void
325e8463f87Spaul luse batch_done(void *cb_arg, int status)
326a34fc12bSpaul luse {
327e8463f87Spaul luse 	struct ap_task *task = (struct ap_task *)cb_arg;
328a34fc12bSpaul luse 	struct worker_thread *worker = task->worker;
329a34fc12bSpaul luse 
330a34fc12bSpaul luse 	worker->current_queue_depth--;
3310cecfcb1Spaul luse 	TAILQ_INSERT_TAIL(&worker->tasks, task, link);
332a34fc12bSpaul luse }
333a34fc12bSpaul luse 
3349f51cf32Spaul luse static int
3359f51cf32Spaul luse dump_result(void)
3369f51cf32Spaul luse {
3379f51cf32Spaul luse 	uint64_t total_completed = 0;
3389f51cf32Spaul luse 	uint64_t total_failed = 0;
339b9218b7aSpaul luse 	uint64_t total_miscompared = 0;
3409f51cf32Spaul luse 	uint64_t total_xfer_per_sec, total_bw_in_MiBps;
3419f51cf32Spaul luse 	struct worker_thread *worker = g_workers;
3429f51cf32Spaul luse 
343b9218b7aSpaul luse 	printf("\nCore           Transfers     Bandwidth     Failed     Miscompares\n");
344b9218b7aSpaul luse 	printf("-----------------------------------------------------------------\n");
3459f51cf32Spaul luse 	while (worker != NULL) {
3469f51cf32Spaul luse 
3479f51cf32Spaul luse 		uint64_t xfer_per_sec = worker->xfer_completed / g_time_in_sec;
3489f51cf32Spaul luse 		uint64_t bw_in_MiBps = (worker->xfer_completed * g_xfer_size_bytes) /
3499f51cf32Spaul luse 				       (g_time_in_sec * 1024 * 1024);
3509f51cf32Spaul luse 
3519f51cf32Spaul luse 		total_completed += worker->xfer_completed;
3529f51cf32Spaul luse 		total_failed += worker->xfer_failed;
353b9218b7aSpaul luse 		total_miscompared += worker->injected_miscompares;
3549f51cf32Spaul luse 
3559f51cf32Spaul luse 		if (xfer_per_sec) {
356b9218b7aSpaul luse 			printf("%10d%12" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 " %11" PRIu64 "\n",
3579f51cf32Spaul luse 			       worker->core, xfer_per_sec,
358b9218b7aSpaul luse 			       bw_in_MiBps, worker->xfer_failed, worker->injected_miscompares);
3599f51cf32Spaul luse 		}
3609f51cf32Spaul luse 
3619f51cf32Spaul luse 		worker = worker->next;
3629f51cf32Spaul luse 	}
3639f51cf32Spaul luse 
3649f51cf32Spaul luse 	total_xfer_per_sec = total_completed / g_time_in_sec;
3659f51cf32Spaul luse 	total_bw_in_MiBps = (total_completed * g_xfer_size_bytes) /
3669f51cf32Spaul luse 			    (g_time_in_sec * 1024 * 1024);
3679f51cf32Spaul luse 
368b9218b7aSpaul luse 	printf("==================================================================\n");
369b9218b7aSpaul luse 	printf("Total:%16" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 " %11" PRIu64"\n\n",
370b9218b7aSpaul luse 	       total_xfer_per_sec, total_bw_in_MiBps, total_failed, total_miscompared);
3719f51cf32Spaul luse 
3729f51cf32Spaul luse 	return total_failed ? 1 : 0;
3739f51cf32Spaul luse }
3749f51cf32Spaul luse 
3759f51cf32Spaul luse static int
3769f51cf32Spaul luse _check_draining(void *arg)
3779f51cf32Spaul luse {
3789f51cf32Spaul luse 	struct worker_thread *worker = arg;
3799f51cf32Spaul luse 
3809f51cf32Spaul luse 	assert(worker);
3819f51cf32Spaul luse 
3829f51cf32Spaul luse 	if (worker->current_queue_depth == 0) {
3839f51cf32Spaul luse 		spdk_poller_unregister(&worker->is_draining_poller);
3849f51cf32Spaul luse 		unregister_worker(worker);
3859f51cf32Spaul luse 	}
3869f51cf32Spaul luse 
3879f51cf32Spaul luse 	return -1;
3889f51cf32Spaul luse }
3899f51cf32Spaul luse 
3909f51cf32Spaul luse static int
3919f51cf32Spaul luse _worker_stop(void *arg)
3929f51cf32Spaul luse {
3939f51cf32Spaul luse 	struct worker_thread *worker = arg;
3949f51cf32Spaul luse 
3959f51cf32Spaul luse 	assert(worker);
3969f51cf32Spaul luse 
3979f51cf32Spaul luse 	spdk_poller_unregister(&worker->stop_poller);
3989f51cf32Spaul luse 
3999f51cf32Spaul luse 	/* now let the worker drain and check it's outstanding IO with a poller */
4009f51cf32Spaul luse 	worker->is_draining = true;
401ab0bc5c2SShuhei Matsumoto 	worker->is_draining_poller = SPDK_POLLER_REGISTER(_check_draining, worker, 0);
4029f51cf32Spaul luse 
4039f51cf32Spaul luse 	return 0;
4049f51cf32Spaul luse }
4059f51cf32Spaul luse 
4069f51cf32Spaul luse static void
4079f51cf32Spaul luse _init_thread_done(void *ctx)
4089f51cf32Spaul luse {
4099f51cf32Spaul luse }
4109f51cf32Spaul luse 
411a34fc12bSpaul luse static int
412a34fc12bSpaul luse _get_task_data_bufs(struct ap_task *task)
4139f51cf32Spaul luse {
4140ef079c6Spaul luse 	uint32_t align = 0;
4159f51cf32Spaul luse 
4160ef079c6Spaul luse 	/* For dualcast, the DSA HW requires 4K alignment on destination addresses but
4170ef079c6Spaul luse 	 * we do this for all engines to keep it simple.
4180ef079c6Spaul luse 	 */
4190ef079c6Spaul luse 	if (g_workload_selection == ACCEL_DUALCAST) {
4200ef079c6Spaul luse 		align = ALIGN_4K;
4210ef079c6Spaul luse 	}
4220ef079c6Spaul luse 
423a34fc12bSpaul luse 	task->src = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL);
424a34fc12bSpaul luse 	if (task->src == NULL) {
425a34fc12bSpaul luse 		fprintf(stderr, "Unable to alloc src buffer\n");
426a34fc12bSpaul luse 		return -ENOMEM;
427a34fc12bSpaul luse 	}
428a34fc12bSpaul luse 	memset(task->src, DATA_PATTERN, g_xfer_size_bytes);
429a34fc12bSpaul luse 
430a34fc12bSpaul luse 	task->dst = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL);
431a34fc12bSpaul luse 	if (task->dst == NULL) {
432a34fc12bSpaul luse 		fprintf(stderr, "Unable to alloc dst buffer\n");
433a34fc12bSpaul luse 		return -ENOMEM;
434a34fc12bSpaul luse 	}
435a34fc12bSpaul luse 
436a34fc12bSpaul luse 	/* For compare we want the buffers to match, otherwise not. */
437a34fc12bSpaul luse 	if (g_workload_selection == ACCEL_COMPARE) {
438a34fc12bSpaul luse 		memset(task->dst, DATA_PATTERN, g_xfer_size_bytes);
439a34fc12bSpaul luse 	} else {
440a34fc12bSpaul luse 		memset(task->dst, ~DATA_PATTERN, g_xfer_size_bytes);
441a34fc12bSpaul luse 	}
442a34fc12bSpaul luse 
443d207237fSpaul luse 	/* For fill, set the entire src buffer so we can check if verify is enabled. */
444d207237fSpaul luse 	if (g_workload_selection == ACCEL_FILL) {
445d207237fSpaul luse 		memset(task->src, g_fill_pattern, g_xfer_size_bytes);
446d207237fSpaul luse 	}
447d207237fSpaul luse 
448a34fc12bSpaul luse 	if (g_workload_selection == ACCEL_DUALCAST) {
449a34fc12bSpaul luse 		task->dst2 = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL);
450a34fc12bSpaul luse 		if (task->dst2 == NULL) {
451a34fc12bSpaul luse 			fprintf(stderr, "Unable to alloc dst buffer\n");
452a34fc12bSpaul luse 			return -ENOMEM;
453a34fc12bSpaul luse 		}
454a34fc12bSpaul luse 		memset(task->dst2, ~DATA_PATTERN, g_xfer_size_bytes);
455a34fc12bSpaul luse 	}
456a34fc12bSpaul luse 
457a34fc12bSpaul luse 	return 0;
458a34fc12bSpaul luse }
459a34fc12bSpaul luse 
460a34fc12bSpaul luse static int
461a34fc12bSpaul luse _batch_prep_cmd(struct worker_thread *worker, struct ap_task *task, struct spdk_accel_batch *batch)
462a34fc12bSpaul luse {
463a34fc12bSpaul luse 	int rc = 0;
464a34fc12bSpaul luse 
465a34fc12bSpaul luse 	switch (g_workload_selection) {
466a34fc12bSpaul luse 	case ACCEL_COPY:
467ee7e31f9Spaul luse 		rc = spdk_accel_batch_prep_copy(worker->ch, batch, task->dst,
468e8463f87Spaul luse 						task->src, g_xfer_size_bytes, accel_done, task);
469a34fc12bSpaul luse 		break;
470ec086e6fSpaul luse 	case ACCEL_DUALCAST:
471ee7e31f9Spaul luse 		rc = spdk_accel_batch_prep_dualcast(worker->ch, batch, task->dst, task->dst2,
472e8463f87Spaul luse 						    task->src, g_xfer_size_bytes, accel_done, task);
473ec086e6fSpaul luse 		break;
474d137ba30Spaul luse 	case ACCEL_COMPARE:
475ee7e31f9Spaul luse 		rc = spdk_accel_batch_prep_compare(worker->ch, batch, task->dst, task->src,
476e8463f87Spaul luse 						   g_xfer_size_bytes, accel_done, task);
477d137ba30Spaul luse 		break;
478d207237fSpaul luse 	case ACCEL_FILL:
479e8463f87Spaul luse 		rc = spdk_accel_batch_prep_fill(worker->ch, batch, task->dst,
480e8463f87Spaul luse 						*(uint8_t *)task->src,
481e8463f87Spaul luse 						g_xfer_size_bytes, accel_done, task);
482d207237fSpaul luse 		break;
483e54f14a5Spaul luse 	case ACCEL_CRC32C:
484e8463f87Spaul luse 		rc = spdk_accel_batch_prep_crc32c(worker->ch, batch, (uint32_t *)task->dst,
485e8463f87Spaul luse 						  task->src, g_crc32c_seed, g_xfer_size_bytes, accel_done, task);
486e54f14a5Spaul luse 		break;
487a34fc12bSpaul luse 	default:
488a34fc12bSpaul luse 		assert(false);
489a34fc12bSpaul luse 		break;
490a34fc12bSpaul luse 	}
491a34fc12bSpaul luse 
492a34fc12bSpaul luse 	return rc;
493a34fc12bSpaul luse }
494a34fc12bSpaul luse 
495a34fc12bSpaul luse static void
496a34fc12bSpaul luse _init_thread(void *arg1)
497a34fc12bSpaul luse {
498a34fc12bSpaul luse 	struct worker_thread *worker;
499a34fc12bSpaul luse 	struct ap_task *task;
5000cecfcb1Spaul luse 	int i, rc, max_per_batch, batch_count, num_tasks;
501a34fc12bSpaul luse 	int remaining = g_queue_depth;
502a34fc12bSpaul luse 	struct spdk_accel_batch *batch, *new_batch;
503a34fc12bSpaul luse 
504a34fc12bSpaul luse 	worker = calloc(1, sizeof(*worker));
505a34fc12bSpaul luse 	if (worker == NULL) {
506a34fc12bSpaul luse 		fprintf(stderr, "Unable to allocate worker\n");
507a34fc12bSpaul luse 		return;
508a34fc12bSpaul luse 	}
509a34fc12bSpaul luse 
5109f51cf32Spaul luse 	worker->core = spdk_env_get_current_core();
5119f51cf32Spaul luse 	worker->thread = spdk_get_thread();
5129f51cf32Spaul luse 	worker->next = g_workers;
5139f51cf32Spaul luse 	worker->ch = spdk_accel_engine_get_io_channel();
514b9218b7aSpaul luse 
5150cecfcb1Spaul luse 	max_per_batch = spdk_accel_batch_get_max(worker->ch);
5160cecfcb1Spaul luse 	assert(max_per_batch > 0);
5170cecfcb1Spaul luse 	num_tasks = g_queue_depth + spdk_divide_round_up(g_queue_depth, max_per_batch);
5180cecfcb1Spaul luse 
5190cecfcb1Spaul luse 	TAILQ_INIT(&worker->tasks);
5200cecfcb1Spaul luse 	for (i = 0; i < num_tasks; i++) {
5210cecfcb1Spaul luse 		task = calloc(1, sizeof(struct ap_task));
5220cecfcb1Spaul luse 		if (task == NULL) {
5230cecfcb1Spaul luse 			fprintf(stderr, "Could not allocate task.\n");
5249f51cf32Spaul luse 			return;
5250cecfcb1Spaul luse 			/* TODO cleanup */
5260cecfcb1Spaul luse 		}
5270cecfcb1Spaul luse 		TAILQ_INSERT_TAIL(&worker->tasks, task, link);
5289f51cf32Spaul luse 	}
5299f51cf32Spaul luse 
5309f51cf32Spaul luse 	/* Register a poller that will stop the worker at time elapsed */
531ab0bc5c2SShuhei Matsumoto 	worker->stop_poller = SPDK_POLLER_REGISTER(_worker_stop, worker,
5329f51cf32Spaul luse 			      g_time_in_sec * 1000000ULL);
5339f51cf32Spaul luse 
5349f51cf32Spaul luse 	g_workers = worker;
5359f51cf32Spaul luse 	pthread_mutex_lock(&g_workers_lock);
5369f51cf32Spaul luse 	g_num_workers++;
5379f51cf32Spaul luse 	pthread_mutex_unlock(&g_workers_lock);
5389f51cf32Spaul luse 
539f295f5b3Spaul luse 	/* Batching is only possible if there is at least 2 operations. */
540f295f5b3Spaul luse 	if (g_queue_depth > 1) {
541a34fc12bSpaul luse 
542a34fc12bSpaul luse 		/* Outter loop sets up each batch command, inner loop populates the
543a34fc12bSpaul luse 		 * batch descriptors.
544a34fc12bSpaul luse 		 */
545a34fc12bSpaul luse 		do {
546a34fc12bSpaul luse 			new_batch = spdk_accel_batch_create(worker->ch);
547a34fc12bSpaul luse 			if (new_batch == NULL) {
548a34fc12bSpaul luse 				break;
549a34fc12bSpaul luse 			}
550a34fc12bSpaul luse 
551a34fc12bSpaul luse 			batch = new_batch;
552a34fc12bSpaul luse 			batch_count = 0;
553a34fc12bSpaul luse 
554a34fc12bSpaul luse 			do {
5550cecfcb1Spaul luse 				if (!TAILQ_EMPTY(&worker->tasks)) {
5560cecfcb1Spaul luse 					task = TAILQ_FIRST(&worker->tasks);
5570cecfcb1Spaul luse 					TAILQ_REMOVE(&worker->tasks, task, link);
5580cecfcb1Spaul luse 				} else {
5599f51cf32Spaul luse 					fprintf(stderr, "Unable to get accel_task\n");
560a34fc12bSpaul luse 					goto error;
561a34fc12bSpaul luse 				}
562a34fc12bSpaul luse 				task->worker = worker;
563a34fc12bSpaul luse 				task->worker->current_queue_depth++;
564a34fc12bSpaul luse 
565a34fc12bSpaul luse 				if (_get_task_data_bufs(task)) {
566a34fc12bSpaul luse 					fprintf(stderr, "Unable to get data bufs\n");
567a34fc12bSpaul luse 					goto error;
5689f51cf32Spaul luse 				}
569b9218b7aSpaul luse 
570a34fc12bSpaul luse 				rc = _batch_prep_cmd(worker, task, batch);
571a34fc12bSpaul luse 				if (rc) {
572a34fc12bSpaul luse 					fprintf(stderr, "error preping command\n");
573a34fc12bSpaul luse 					goto error;
574a34fc12bSpaul luse 				}
575a34fc12bSpaul luse 				remaining--;
576a34fc12bSpaul luse 				batch_count++;
577a34fc12bSpaul luse 			} while (batch_count < max_per_batch && remaining > 0);
578a34fc12bSpaul luse 
579a34fc12bSpaul luse 			/* Now send the batch command. */
5800cecfcb1Spaul luse 			if (!TAILQ_EMPTY(&worker->tasks)) {
5810cecfcb1Spaul luse 				task = TAILQ_FIRST(&worker->tasks);
5820cecfcb1Spaul luse 				TAILQ_REMOVE(&worker->tasks, task, link);
5830cecfcb1Spaul luse 			} else {
584a34fc12bSpaul luse 				fprintf(stderr, "Unable to get accel_task\n");
585a34fc12bSpaul luse 				goto error;
586a34fc12bSpaul luse 			}
587a34fc12bSpaul luse 			task->worker = worker;
588a34fc12bSpaul luse 			task->worker->current_queue_depth++;
589a34fc12bSpaul luse 
590e8463f87Spaul luse 			rc = spdk_accel_batch_submit(worker->ch, batch, batch_done, task);
591a34fc12bSpaul luse 			if (rc) {
592a34fc12bSpaul luse 				fprintf(stderr, "error ending batch %d\n", rc);
593a34fc12bSpaul luse 				goto error;
594a34fc12bSpaul luse 			}
595a34fc12bSpaul luse 			/* We can't build a batch unless it has 2 descriptors (per spec). */
596a34fc12bSpaul luse 		} while (remaining > 1);
597a34fc12bSpaul luse 
598a34fc12bSpaul luse 		/* If there are no more left, we're done. */
599a34fc12bSpaul luse 		if (remaining == 0) {
600b9218b7aSpaul luse 			return;
601b9218b7aSpaul luse 		}
602b9218b7aSpaul luse 	}
6030ef079c6Spaul luse 
604a34fc12bSpaul luse 	/* For engines that don't support batch or for the odd event that
605a34fc12bSpaul luse 	 * a batch ends with only one descriptor left.
606a34fc12bSpaul luse 	 */
607a34fc12bSpaul luse 	for (i = 0; i < remaining; i++) {
608a34fc12bSpaul luse 
6090cecfcb1Spaul luse 		if (!TAILQ_EMPTY(&worker->tasks)) {
6100cecfcb1Spaul luse 			task = TAILQ_FIRST(&worker->tasks);
6110cecfcb1Spaul luse 			TAILQ_REMOVE(&worker->tasks, task, link);
6120cecfcb1Spaul luse 		} else {
613a34fc12bSpaul luse 			fprintf(stderr, "Unable to get accel_task\n");
614a34fc12bSpaul luse 			goto error;
6150ef079c6Spaul luse 		}
6160ef079c6Spaul luse 
617a34fc12bSpaul luse 		if (_get_task_data_bufs(task)) {
618a34fc12bSpaul luse 			fprintf(stderr, "Unable to get data bufs\n");
619a34fc12bSpaul luse 			goto error;
620b9218b7aSpaul luse 		}
621b9218b7aSpaul luse 
6229f51cf32Spaul luse 		_submit_single(worker, task);
6239f51cf32Spaul luse 	}
624a34fc12bSpaul luse 	return;
625a34fc12bSpaul luse error:
626a34fc12bSpaul luse 	/* TODO clean exit */
627a34fc12bSpaul luse 	raise(SIGINT);
6280cecfcb1Spaul luse 	while (!TAILQ_EMPTY(&worker->tasks)) {
6290cecfcb1Spaul luse 		task = TAILQ_FIRST(&worker->tasks);
6300cecfcb1Spaul luse 		TAILQ_REMOVE(&worker->tasks, task, link);
6310cecfcb1Spaul luse 		free(task);
6320cecfcb1Spaul luse 	}
633a34fc12bSpaul luse 	free(worker);
634a34fc12bSpaul luse 	spdk_app_stop(-1);
6359f51cf32Spaul luse }
6369f51cf32Spaul luse 
6379f51cf32Spaul luse static void
638e8463f87Spaul luse accel_done(void *cb_arg, int status)
6399f51cf32Spaul luse {
640e8463f87Spaul luse 	struct ap_task *task = (struct ap_task *)cb_arg;
6419f51cf32Spaul luse 	struct worker_thread *worker = task->worker;
6429f51cf32Spaul luse 
6439f51cf32Spaul luse 	assert(worker);
6449f51cf32Spaul luse 
645b9218b7aSpaul luse 	task->status = status;
6469f51cf32Spaul luse 	spdk_thread_send_msg(worker->thread, _accel_done, task);
6479f51cf32Spaul luse }
6489f51cf32Spaul luse 
6499f51cf32Spaul luse static void
6509f51cf32Spaul luse accel_perf_start(void *arg1)
6519f51cf32Spaul luse {
652514be889Spaul luse 	struct spdk_io_channel *accel_ch;
653514be889Spaul luse 
654514be889Spaul luse 	accel_ch = spdk_accel_engine_get_io_channel();
655a34fc12bSpaul luse 	g_capabilites = spdk_accel_get_capabilities(accel_ch);
656514be889Spaul luse 	spdk_put_io_channel(accel_ch);
657514be889Spaul luse 
658a34fc12bSpaul luse 	if ((g_capabilites & g_workload_selection) != g_workload_selection) {
659a7dfca5bSpaul luse 		SPDK_WARNLOG("The selected workload is not natively supported by the current engine\n");
660a7dfca5bSpaul luse 		SPDK_WARNLOG("The software engine will be used instead.\n\n");
661514be889Spaul luse 	}
662514be889Spaul luse 
6639f51cf32Spaul luse 	g_tsc_rate = spdk_get_ticks_hz();
6649f51cf32Spaul luse 	g_tsc_us_rate = g_tsc_rate / (1000 * 1000);
6659f51cf32Spaul luse 	g_tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate;
6669f51cf32Spaul luse 
6679f51cf32Spaul luse 	printf("Running for %d seconds...\n", g_time_in_sec);
6689f51cf32Spaul luse 	fflush(stdout);
6699f51cf32Spaul luse 
6709f51cf32Spaul luse 	spdk_for_each_thread(_init_thread, NULL, _init_thread_done);
6719f51cf32Spaul luse }
6729f51cf32Spaul luse 
6739f51cf32Spaul luse int
6749f51cf32Spaul luse main(int argc, char **argv)
6759f51cf32Spaul luse {
6769f51cf32Spaul luse 	struct spdk_app_opts opts = {};
6779f51cf32Spaul luse 	struct worker_thread *worker, *tmp;
6789f51cf32Spaul luse 	int rc = 0;
6799f51cf32Spaul luse 
6809f51cf32Spaul luse 	pthread_mutex_init(&g_workers_lock, NULL);
68148701bd9SZiye Yang 	spdk_app_opts_init(&opts, sizeof(opts));
6829f51cf32Spaul luse 	opts.reactor_mask = "0x1";
6831e2b38baSyidong0635 	if (spdk_app_parse_args(argc, argv, &opts, "o:q:t:yw:P:f:", NULL, parse_args,
6841e2b38baSyidong0635 				usage) != SPDK_APP_PARSE_ARGS_SUCCESS) {
6859f51cf32Spaul luse 		rc = -1;
6869f51cf32Spaul luse 		goto cleanup;
6879f51cf32Spaul luse 	}
6889f51cf32Spaul luse 
689b9218b7aSpaul luse 	if ((g_workload_selection != ACCEL_COPY) &&
690b9218b7aSpaul luse 	    (g_workload_selection != ACCEL_FILL) &&
691b9218b7aSpaul luse 	    (g_workload_selection != ACCEL_CRC32C) &&
6920ef079c6Spaul luse 	    (g_workload_selection != ACCEL_COMPARE) &&
6930ef079c6Spaul luse 	    (g_workload_selection != ACCEL_DUALCAST)) {
6942a0c66d0Spaul luse 		usage();
6952a0c66d0Spaul luse 		rc = -1;
6962a0c66d0Spaul luse 		goto cleanup;
6972a0c66d0Spaul luse 	}
6982a0c66d0Spaul luse 
6999f51cf32Spaul luse 	dump_user_config(&opts);
7009f51cf32Spaul luse 	rc = spdk_app_start(&opts, accel_perf_start, NULL);
7019f51cf32Spaul luse 	if (rc) {
7029f51cf32Spaul luse 		SPDK_ERRLOG("ERROR starting application\n");
7039f51cf32Spaul luse 	} else {
7049f51cf32Spaul luse 		dump_result();
7059f51cf32Spaul luse 	}
7069f51cf32Spaul luse 
7079f51cf32Spaul luse 	pthread_mutex_destroy(&g_workers_lock);
7089f51cf32Spaul luse 
7099f51cf32Spaul luse 	worker = g_workers;
7109f51cf32Spaul luse 	while (worker) {
7119f51cf32Spaul luse 		tmp = worker->next;
7129f51cf32Spaul luse 		free(worker);
7139f51cf32Spaul luse 		worker = tmp;
7149f51cf32Spaul luse 	}
7159f51cf32Spaul luse cleanup:
7169f51cf32Spaul luse 	spdk_app_fini();
7179f51cf32Spaul luse 	return rc;
7189f51cf32Spaul luse }
719