xref: /spdk/examples/accel/perf/accel_perf.c (revision e69375bff34ec748bf57dafd81cec3297d60040f)
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"
41*e69375bfSpaul luse #include "spdk/crc32.h"
429f51cf32Spaul luse 
439f51cf32Spaul luse static uint64_t	g_tsc_rate;
449f51cf32Spaul luse static uint64_t g_tsc_us_rate;
459f51cf32Spaul luse static uint64_t g_tsc_end;
469f51cf32Spaul luse static int g_xfer_size_bytes = 4096;
479f51cf32Spaul luse static int g_queue_depth = 32;
489f51cf32Spaul luse static int g_time_in_sec = 5;
49*e69375bfSpaul luse static uint32_t g_crc32c_seed = 0;
509f51cf32Spaul luse static bool g_verify = false;
512a0c66d0Spaul luse static const char *g_workload_type = NULL;
52514be889Spaul luse static enum accel_capability g_workload_selection;
539f51cf32Spaul luse static struct worker_thread *g_workers = NULL;
549f51cf32Spaul luse static int g_num_workers = 0;
559f51cf32Spaul luse static pthread_mutex_t g_workers_lock = PTHREAD_MUTEX_INITIALIZER;
569f51cf32Spaul luse 
579f51cf32Spaul luse struct worker_thread {
589f51cf32Spaul luse 	struct spdk_io_channel		*ch;
599f51cf32Spaul luse 	uint64_t			xfer_completed;
609f51cf32Spaul luse 	uint64_t			xfer_failed;
619f51cf32Spaul luse 	uint64_t			current_queue_depth;
629f51cf32Spaul luse 	struct spdk_mempool		*data_pool;
639f51cf32Spaul luse 	struct spdk_mempool		*task_pool;
649f51cf32Spaul luse 	struct worker_thread		*next;
659f51cf32Spaul luse 	unsigned			core;
669f51cf32Spaul luse 	struct spdk_thread		*thread;
679f51cf32Spaul luse 	bool				is_draining;
689f51cf32Spaul luse 	struct spdk_poller		*is_draining_poller;
699f51cf32Spaul luse 	struct spdk_poller		*stop_poller;
709f51cf32Spaul luse };
719f51cf32Spaul luse 
729f51cf32Spaul luse struct ap_task {
739f51cf32Spaul luse 	void			*src;
749f51cf32Spaul luse 	void			*dst;
759f51cf32Spaul luse 	struct worker_thread	*worker;
769f51cf32Spaul luse };
779f51cf32Spaul luse 
789f51cf32Spaul luse inline static struct ap_task *
799f51cf32Spaul luse __ap_task_from_accel_task(struct spdk_accel_task *at)
809f51cf32Spaul luse {
819f51cf32Spaul luse 	return (struct ap_task *)((uintptr_t)at - sizeof(struct ap_task));
829f51cf32Spaul luse }
839f51cf32Spaul luse 
849f51cf32Spaul luse inline static struct spdk_accel_task *
859f51cf32Spaul luse __accel_task_from_ap_task(struct ap_task *ap)
869f51cf32Spaul luse {
879f51cf32Spaul luse 	return (struct spdk_accel_task *)((uintptr_t)ap + sizeof(struct ap_task));
889f51cf32Spaul luse }
899f51cf32Spaul luse 
909f51cf32Spaul luse static void
919f51cf32Spaul luse dump_user_config(struct spdk_app_opts *opts)
929f51cf32Spaul luse {
939f51cf32Spaul luse 	printf("SPDK Configuration:\n");
949f51cf32Spaul luse 	printf("Core mask:      %s\n\n", opts->reactor_mask);
959f51cf32Spaul luse 	printf("Accel Perf Configuration:\n");
962a0c66d0Spaul luse 	printf("Workload Type:  %s\n", g_workload_type);
97*e69375bfSpaul luse 	if (!strcmp(g_workload_type, "crc32c")) {
98*e69375bfSpaul luse 		printf("CRC-32C seed:   %u", g_crc32c_seed);
99*e69375bfSpaul luse 	}
1009f51cf32Spaul luse 	printf("Transfer size:  %u bytes\n", g_xfer_size_bytes);
1019f51cf32Spaul luse 	printf("Queue depth:    %u\n", g_queue_depth);
1029f51cf32Spaul luse 	printf("Run time:       %u seconds\n", g_time_in_sec);
1039f51cf32Spaul luse 	printf("Verify:         %s\n\n", g_verify ? "Yes" : "No");
1049f51cf32Spaul luse }
1059f51cf32Spaul luse 
1069f51cf32Spaul luse static void
1079f51cf32Spaul luse usage(void)
1089f51cf32Spaul luse {
1099f51cf32Spaul luse 	printf("accel_perf options:\n");
1109f51cf32Spaul luse 	printf("\t[-h help message]\n");
1119f51cf32Spaul luse 	printf("\t[-q queue depth]\n");
1129f51cf32Spaul luse 	printf("\t[-n number of channels]\n");
1139f51cf32Spaul luse 	printf("\t[-o transfer size in bytes]\n");
1149f51cf32Spaul luse 	printf("\t[-t time in seconds]\n");
115*e69375bfSpaul luse 	printf("\t[-w workload type must be one of these: copy, fill, crc32c\n");
116*e69375bfSpaul luse 	printf("\t[-s for crc32c workload, use this seed value (default 0)\n");
1172a0c66d0Spaul luse 	printf("\t[-y verify result if this switch is on]\n");
1189f51cf32Spaul luse }
1199f51cf32Spaul luse 
1209f51cf32Spaul luse static int
1219f51cf32Spaul luse parse_args(int argc, char *argv)
1229f51cf32Spaul luse {
1239f51cf32Spaul luse 	switch (argc) {
1249f51cf32Spaul luse 	case 'o':
1259f51cf32Spaul luse 		g_xfer_size_bytes = spdk_strtol(optarg, 10);
1269f51cf32Spaul luse 		break;
1279f51cf32Spaul luse 	case 'q':
1289f51cf32Spaul luse 		g_queue_depth = spdk_strtol(optarg, 10);
1299f51cf32Spaul luse 		break;
130*e69375bfSpaul luse 	case 's':
131*e69375bfSpaul luse 		g_crc32c_seed = spdk_strtol(optarg, 10);
132*e69375bfSpaul luse 		break;
1339f51cf32Spaul luse 	case 't':
1349f51cf32Spaul luse 		g_time_in_sec = spdk_strtol(optarg, 10);
1359f51cf32Spaul luse 		break;
1369f51cf32Spaul luse 	case 'y':
1379f51cf32Spaul luse 		g_verify = true;
1389f51cf32Spaul luse 		break;
1392a0c66d0Spaul luse 	case 'w':
1402a0c66d0Spaul luse 		g_workload_type = optarg;
141514be889Spaul luse 		if (!strcmp(g_workload_type, "copy")) {
142514be889Spaul luse 			g_workload_selection = ACCEL_COPY;
143514be889Spaul luse 		} else if (!strcmp(g_workload_type, "fill")) {
144514be889Spaul luse 			g_workload_selection = ACCEL_FILL;
145*e69375bfSpaul luse 		} else if (!strcmp(g_workload_type, "crc32c")) {
146*e69375bfSpaul luse 			g_workload_selection = ACCEL_CRC32C;
147514be889Spaul luse 		}
1482a0c66d0Spaul luse 		break;
1499f51cf32Spaul luse 	default:
1509f51cf32Spaul luse 		usage();
1519f51cf32Spaul luse 		return 1;
1529f51cf32Spaul luse 	}
1539f51cf32Spaul luse 	return 0;
1549f51cf32Spaul luse }
1559f51cf32Spaul luse 
1569f51cf32Spaul luse static void
1579f51cf32Spaul luse unregister_worker(void *arg1)
1589f51cf32Spaul luse {
1599f51cf32Spaul luse 	struct worker_thread *worker = arg1;
1609f51cf32Spaul luse 
1619f51cf32Spaul luse 	spdk_mempool_free(worker->data_pool);
1629f51cf32Spaul luse 	spdk_mempool_free(worker->task_pool);
1639f51cf32Spaul luse 	spdk_put_io_channel(worker->ch);
1649f51cf32Spaul luse 	pthread_mutex_lock(&g_workers_lock);
1659f51cf32Spaul luse 	assert(g_num_workers >= 1);
1669f51cf32Spaul luse 	if (--g_num_workers == 0) {
1679f51cf32Spaul luse 		pthread_mutex_unlock(&g_workers_lock);
1689f51cf32Spaul luse 		spdk_app_stop(0);
1699f51cf32Spaul luse 	}
1709f51cf32Spaul luse 	pthread_mutex_unlock(&g_workers_lock);
1719f51cf32Spaul luse }
1729f51cf32Spaul luse 
1739f51cf32Spaul luse static void accel_done(void *ref, int status);
1749f51cf32Spaul luse 
1759f51cf32Spaul luse static void
1769f51cf32Spaul luse _submit_single(void *arg1, void *arg2)
1779f51cf32Spaul luse {
1789f51cf32Spaul luse 	struct worker_thread *worker = arg1;
1799f51cf32Spaul luse 	struct ap_task *task = arg2;
1809f51cf32Spaul luse 
1819f51cf32Spaul luse 	assert(worker);
1829f51cf32Spaul luse 
1839f51cf32Spaul luse 	if (g_verify) {
1849f51cf32Spaul luse 		memset(task->src, 0x5a, g_xfer_size_bytes);
1852a0c66d0Spaul luse 		memset(task->dst, 0xa5, g_xfer_size_bytes);
1869f51cf32Spaul luse 	}
1879f51cf32Spaul luse 	task->worker = worker;
1889f51cf32Spaul luse 	task->worker->current_queue_depth++;
189*e69375bfSpaul luse 	switch (g_workload_selection) {
190*e69375bfSpaul luse 	case ACCEL_COPY:
1919f51cf32Spaul luse 		spdk_accel_submit_copy(__accel_task_from_ap_task(task),
1929f51cf32Spaul luse 				       worker->ch, task->dst,
1939f51cf32Spaul luse 				       task->src, g_xfer_size_bytes, accel_done);
194*e69375bfSpaul luse 		break;
195*e69375bfSpaul luse 	case ACCEL_FILL:
1962a0c66d0Spaul luse 		/* For fill use the first byte of the task->dst buffer */
1972a0c66d0Spaul luse 		spdk_accel_submit_fill(__accel_task_from_ap_task(task),
1982a0c66d0Spaul luse 				       worker->ch, task->dst, *(uint8_t *)task->src,
1992a0c66d0Spaul luse 				       g_xfer_size_bytes, accel_done);
200*e69375bfSpaul luse 		break;
201*e69375bfSpaul luse 	case ACCEL_CRC32C:
202*e69375bfSpaul luse 		spdk_accel_submit_crc32c(__accel_task_from_ap_task(task),
203*e69375bfSpaul luse 					 worker->ch, (uint32_t *)task->dst, task->src, g_crc32c_seed,
204*e69375bfSpaul luse 					 g_xfer_size_bytes, accel_done);
205*e69375bfSpaul luse 		break;
206*e69375bfSpaul luse 	default:
2072a0c66d0Spaul luse 		assert(false);
208*e69375bfSpaul luse 		break;
209*e69375bfSpaul luse 
2102a0c66d0Spaul luse 	}
2119f51cf32Spaul luse }
2129f51cf32Spaul luse 
2139f51cf32Spaul luse static void
2149f51cf32Spaul luse _accel_done(void *arg1)
2159f51cf32Spaul luse {
2169f51cf32Spaul luse 	struct ap_task *task = arg1;
2179f51cf32Spaul luse 	struct worker_thread *worker = task->worker;
218*e69375bfSpaul luse 	uint32_t sw_crc32c;
2199f51cf32Spaul luse 
2209f51cf32Spaul luse 	assert(worker);
2219f51cf32Spaul luse 	assert(worker->current_queue_depth > 0);
2229f51cf32Spaul luse 
2239f51cf32Spaul luse 	if (g_verify) {
224*e69375bfSpaul luse 		if (!strcmp(g_workload_type, "crc32c")) {
225*e69375bfSpaul luse 			/* calculate sw CRC-32C and compare to sw aceel result. */
226*e69375bfSpaul luse 			sw_crc32c = spdk_crc32c_update(task->src, g_xfer_size_bytes, ~g_crc32c_seed);
227*e69375bfSpaul luse 			if (*(uint32_t *)task->dst != sw_crc32c) {
228*e69375bfSpaul luse 				SPDK_NOTICELOG("CRC-32C miscompare\n");
229*e69375bfSpaul luse 				worker->xfer_failed++;
230*e69375bfSpaul luse 				/* TODO: cleanup */
231*e69375bfSpaul luse 				exit(-1);
232*e69375bfSpaul luse 			}
233*e69375bfSpaul luse 		} else if (memcmp(task->src, task->dst, g_xfer_size_bytes)) {
2349f51cf32Spaul luse 			SPDK_NOTICELOG("Data miscompare\n");
2359f51cf32Spaul luse 			worker->xfer_failed++;
2369f51cf32Spaul luse 			/* TODO: cleanup */
2379f51cf32Spaul luse 			exit(-1);
2389f51cf32Spaul luse 		}
2399f51cf32Spaul luse 	}
2409f51cf32Spaul luse 	worker->xfer_completed++;
2419f51cf32Spaul luse 	worker->current_queue_depth--;
2429f51cf32Spaul luse 
2439f51cf32Spaul luse 	if (!worker->is_draining) {
2449f51cf32Spaul luse 		_submit_single(worker, task);
2459f51cf32Spaul luse 	} else {
2469f51cf32Spaul luse 		spdk_mempool_put(worker->data_pool, task->src);
2479f51cf32Spaul luse 		spdk_mempool_put(worker->data_pool, task->dst);
2489f51cf32Spaul luse 		spdk_mempool_put(worker->task_pool, task);
2499f51cf32Spaul luse 	}
2509f51cf32Spaul luse }
2519f51cf32Spaul luse 
2529f51cf32Spaul luse static int
2539f51cf32Spaul luse dump_result(void)
2549f51cf32Spaul luse {
2559f51cf32Spaul luse 	uint64_t total_completed = 0;
2569f51cf32Spaul luse 	uint64_t total_failed = 0;
2579f51cf32Spaul luse 	uint64_t total_xfer_per_sec, total_bw_in_MiBps;
2589f51cf32Spaul luse 	struct worker_thread *worker = g_workers;
2599f51cf32Spaul luse 
2609f51cf32Spaul luse 	printf("\nCore           Transfers     Bandwidth     Failed\n");
2619f51cf32Spaul luse 	printf("-------------------------------------------------\n");
2629f51cf32Spaul luse 	while (worker != NULL) {
2639f51cf32Spaul luse 
2649f51cf32Spaul luse 		uint64_t xfer_per_sec = worker->xfer_completed / g_time_in_sec;
2659f51cf32Spaul luse 		uint64_t bw_in_MiBps = (worker->xfer_completed * g_xfer_size_bytes) /
2669f51cf32Spaul luse 				       (g_time_in_sec * 1024 * 1024);
2679f51cf32Spaul luse 
2689f51cf32Spaul luse 		total_completed += worker->xfer_completed;
2699f51cf32Spaul luse 		total_failed += worker->xfer_failed;
2709f51cf32Spaul luse 
2719f51cf32Spaul luse 		if (xfer_per_sec) {
2729f51cf32Spaul luse 			printf("%10d%12" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 "\n",
2739f51cf32Spaul luse 			       worker->core, xfer_per_sec,
2749f51cf32Spaul luse 			       bw_in_MiBps, worker->xfer_failed);
2759f51cf32Spaul luse 		}
2769f51cf32Spaul luse 
2779f51cf32Spaul luse 		worker = worker->next;
2789f51cf32Spaul luse 	}
2799f51cf32Spaul luse 
2809f51cf32Spaul luse 	total_xfer_per_sec = total_completed / g_time_in_sec;
2819f51cf32Spaul luse 	total_bw_in_MiBps = (total_completed * g_xfer_size_bytes) /
2829f51cf32Spaul luse 			    (g_time_in_sec * 1024 * 1024);
2839f51cf32Spaul luse 
2849f51cf32Spaul luse 	printf("=================================================\n");
2859f51cf32Spaul luse 	printf("Total:%16" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 "\n\n",
2869f51cf32Spaul luse 	       total_xfer_per_sec, total_bw_in_MiBps, total_failed);
2879f51cf32Spaul luse 
2889f51cf32Spaul luse 	return total_failed ? 1 : 0;
2899f51cf32Spaul luse }
2909f51cf32Spaul luse 
2919f51cf32Spaul luse static int
2929f51cf32Spaul luse _check_draining(void *arg)
2939f51cf32Spaul luse {
2949f51cf32Spaul luse 	struct worker_thread *worker = arg;
2959f51cf32Spaul luse 
2969f51cf32Spaul luse 	assert(worker);
2979f51cf32Spaul luse 
2989f51cf32Spaul luse 	if (worker->current_queue_depth == 0) {
2999f51cf32Spaul luse 		spdk_poller_unregister(&worker->is_draining_poller);
3009f51cf32Spaul luse 		unregister_worker(worker);
3019f51cf32Spaul luse 	}
3029f51cf32Spaul luse 
3039f51cf32Spaul luse 	return -1;
3049f51cf32Spaul luse }
3059f51cf32Spaul luse 
3069f51cf32Spaul luse static int
3079f51cf32Spaul luse _worker_stop(void *arg)
3089f51cf32Spaul luse {
3099f51cf32Spaul luse 	struct worker_thread *worker = arg;
3109f51cf32Spaul luse 
3119f51cf32Spaul luse 	assert(worker);
3129f51cf32Spaul luse 
3139f51cf32Spaul luse 	spdk_poller_unregister(&worker->stop_poller);
3149f51cf32Spaul luse 
3159f51cf32Spaul luse 	/* now let the worker drain and check it's outstanding IO with a poller */
3169f51cf32Spaul luse 	worker->is_draining = true;
317ab0bc5c2SShuhei Matsumoto 	worker->is_draining_poller = SPDK_POLLER_REGISTER(_check_draining, worker, 0);
3189f51cf32Spaul luse 
3199f51cf32Spaul luse 	return 0;
3209f51cf32Spaul luse }
3219f51cf32Spaul luse 
3229f51cf32Spaul luse static void
3239f51cf32Spaul luse _init_thread_done(void *ctx)
3249f51cf32Spaul luse {
3259f51cf32Spaul luse }
3269f51cf32Spaul luse 
3279f51cf32Spaul luse static void
3289f51cf32Spaul luse _init_thread(void *arg1)
3299f51cf32Spaul luse {
3309f51cf32Spaul luse 	struct worker_thread *worker;
3319f51cf32Spaul luse 	char buf_pool_name[30], task_pool_name[30];
3329f51cf32Spaul luse 	struct ap_task *task;
3339f51cf32Spaul luse 	int i;
3349f51cf32Spaul luse 
3359f51cf32Spaul luse 	worker = calloc(1, sizeof(*worker));
3369f51cf32Spaul luse 	if (worker == NULL) {
3379f51cf32Spaul luse 		fprintf(stderr, "Unable to allocate worker\n");
3389f51cf32Spaul luse 		return;
3399f51cf32Spaul luse 	}
3409f51cf32Spaul luse 
3419f51cf32Spaul luse 	worker->core = spdk_env_get_current_core();
3429f51cf32Spaul luse 	worker->thread = spdk_get_thread();
3439f51cf32Spaul luse 	worker->next = g_workers;
3449f51cf32Spaul luse 	worker->ch = spdk_accel_engine_get_io_channel();
3459f51cf32Spaul luse 	snprintf(buf_pool_name, sizeof(buf_pool_name), "buf_pool_%d", g_num_workers);
3469f51cf32Spaul luse 	snprintf(task_pool_name, sizeof(task_pool_name), "task_pool_%d", g_num_workers);
3479f51cf32Spaul luse 	worker->data_pool = spdk_mempool_create(buf_pool_name,
3489f51cf32Spaul luse 						g_queue_depth * 2, /* src + dst */
3499f51cf32Spaul luse 						g_xfer_size_bytes,
3509f51cf32Spaul luse 						SPDK_MEMPOOL_DEFAULT_CACHE_SIZE,
3519f51cf32Spaul luse 						SPDK_ENV_SOCKET_ID_ANY);
3529f51cf32Spaul luse 	worker->task_pool = spdk_mempool_create(task_pool_name,
3539f51cf32Spaul luse 						g_queue_depth,
3549f51cf32Spaul luse 						spdk_accel_task_size() + sizeof(struct ap_task),
3559f51cf32Spaul luse 						SPDK_MEMPOOL_DEFAULT_CACHE_SIZE,
3569f51cf32Spaul luse 						SPDK_ENV_SOCKET_ID_ANY);
3579f51cf32Spaul luse 	if (!worker->data_pool || !worker->task_pool) {
3589f51cf32Spaul luse 		fprintf(stderr, "Could not allocate buffer pool.\n");
3599f51cf32Spaul luse 		spdk_mempool_free(worker->data_pool);
3609f51cf32Spaul luse 		spdk_mempool_free(worker->task_pool);
3619f51cf32Spaul luse 		free(worker);
3629f51cf32Spaul luse 		return;
3639f51cf32Spaul luse 	}
3649f51cf32Spaul luse 
3659f51cf32Spaul luse 	/* Register a poller that will stop the worker at time elapsed */
366ab0bc5c2SShuhei Matsumoto 	worker->stop_poller = SPDK_POLLER_REGISTER(_worker_stop, worker,
3679f51cf32Spaul luse 			      g_time_in_sec * 1000000ULL);
3689f51cf32Spaul luse 
3699f51cf32Spaul luse 	g_workers = worker;
3709f51cf32Spaul luse 	pthread_mutex_lock(&g_workers_lock);
3719f51cf32Spaul luse 	g_num_workers++;
3729f51cf32Spaul luse 	pthread_mutex_unlock(&g_workers_lock);
3739f51cf32Spaul luse 
3749f51cf32Spaul luse 	for (i = 0; i < g_queue_depth; i++) {
3759f51cf32Spaul luse 		task = spdk_mempool_get(worker->task_pool);
3769f51cf32Spaul luse 		if (!task) {
3779f51cf32Spaul luse 			fprintf(stderr, "Unable to get accel_task\n");
3789f51cf32Spaul luse 			return;
3799f51cf32Spaul luse 		}
3809f51cf32Spaul luse 		task->src = spdk_mempool_get(worker->data_pool);
3819f51cf32Spaul luse 		task->dst = spdk_mempool_get(worker->data_pool);
3829f51cf32Spaul luse 		_submit_single(worker, task);
3839f51cf32Spaul luse 	}
3849f51cf32Spaul luse }
3859f51cf32Spaul luse 
3869f51cf32Spaul luse static void
3879f51cf32Spaul luse accel_done(void *ref, int status)
3889f51cf32Spaul luse {
3899f51cf32Spaul luse 	struct ap_task *task = __ap_task_from_accel_task(ref);
3909f51cf32Spaul luse 	struct worker_thread *worker = task->worker;
3919f51cf32Spaul luse 
3929f51cf32Spaul luse 	assert(worker);
3939f51cf32Spaul luse 
3949f51cf32Spaul luse 	spdk_thread_send_msg(worker->thread, _accel_done, task);
3959f51cf32Spaul luse }
3969f51cf32Spaul luse 
3979f51cf32Spaul luse static void
3989f51cf32Spaul luse accel_perf_start(void *arg1)
3999f51cf32Spaul luse {
400514be889Spaul luse 	uint64_t capabilites;
401514be889Spaul luse 	struct spdk_io_channel *accel_ch;
402514be889Spaul luse 
403514be889Spaul luse 	accel_ch = spdk_accel_engine_get_io_channel();
404514be889Spaul luse 	capabilites = spdk_accel_get_capabilities(accel_ch);
405514be889Spaul luse 	spdk_put_io_channel(accel_ch);
406514be889Spaul luse 
407514be889Spaul luse 	if ((capabilites & g_workload_selection) != g_workload_selection) {
408514be889Spaul luse 		SPDK_ERRLOG("Selected workload is not supported by the current engine\n");
409514be889Spaul luse 		SPDK_NOTICELOG("Software engine is selected by default, enable a HW engine via RPC\n\n");
410514be889Spaul luse 		spdk_app_stop(-1);
411514be889Spaul luse 		return;
412514be889Spaul luse 	}
413514be889Spaul luse 
4149f51cf32Spaul luse 	g_tsc_rate = spdk_get_ticks_hz();
4159f51cf32Spaul luse 	g_tsc_us_rate = g_tsc_rate / (1000 * 1000);
4169f51cf32Spaul luse 	g_tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate;
4179f51cf32Spaul luse 
4189f51cf32Spaul luse 	printf("Running for %d seconds...\n", g_time_in_sec);
4199f51cf32Spaul luse 	fflush(stdout);
4209f51cf32Spaul luse 
4219f51cf32Spaul luse 	spdk_for_each_thread(_init_thread, NULL, _init_thread_done);
4229f51cf32Spaul luse }
4239f51cf32Spaul luse 
4249f51cf32Spaul luse int
4259f51cf32Spaul luse main(int argc, char **argv)
4269f51cf32Spaul luse {
4279f51cf32Spaul luse 	struct spdk_app_opts opts = {};
4289f51cf32Spaul luse 	struct worker_thread *worker, *tmp;
4299f51cf32Spaul luse 	int rc = 0;
4309f51cf32Spaul luse 
4319f51cf32Spaul luse 	pthread_mutex_init(&g_workers_lock, NULL);
4329f51cf32Spaul luse 	spdk_app_opts_init(&opts);
4339f51cf32Spaul luse 	opts.reactor_mask = "0x1";
4342a0c66d0Spaul luse 	if ((rc = spdk_app_parse_args(argc, argv, &opts, "o:q:t:yw:", NULL, parse_args,
4359f51cf32Spaul luse 				      usage)) != SPDK_APP_PARSE_ARGS_SUCCESS) {
4369f51cf32Spaul luse 		rc = -1;
4379f51cf32Spaul luse 		goto cleanup;
4389f51cf32Spaul luse 	}
4399f51cf32Spaul luse 
44036676037Spaul luse 	if (g_workload_type == NULL ||
44136676037Spaul luse 	    (strcmp(g_workload_type, "copy") &&
442*e69375bfSpaul luse 	     strcmp(g_workload_type, "fill") &&
443*e69375bfSpaul luse 	     strcmp(g_workload_type, "crc32c"))) {
4442a0c66d0Spaul luse 		usage();
4452a0c66d0Spaul luse 		rc = -1;
4462a0c66d0Spaul luse 		goto cleanup;
4472a0c66d0Spaul luse 	}
4482a0c66d0Spaul luse 
4499f51cf32Spaul luse 	dump_user_config(&opts);
4509f51cf32Spaul luse 	rc = spdk_app_start(&opts, accel_perf_start, NULL);
4519f51cf32Spaul luse 	if (rc) {
4529f51cf32Spaul luse 		SPDK_ERRLOG("ERROR starting application\n");
4539f51cf32Spaul luse 	} else {
4549f51cf32Spaul luse 		dump_result();
4559f51cf32Spaul luse 	}
4569f51cf32Spaul luse 
4579f51cf32Spaul luse 	pthread_mutex_destroy(&g_workers_lock);
4589f51cf32Spaul luse 
4599f51cf32Spaul luse 	worker = g_workers;
4609f51cf32Spaul luse 	while (worker) {
4619f51cf32Spaul luse 		tmp = worker->next;
4629f51cf32Spaul luse 		free(worker);
4639f51cf32Spaul luse 		worker = tmp;
4649f51cf32Spaul luse 	}
4659f51cf32Spaul luse cleanup:
4669f51cf32Spaul luse 	spdk_app_fini();
4679f51cf32Spaul luse 	return rc;
4689f51cf32Spaul luse }
469