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 473e2e2a57Spaul luse static bool g_using_sw_engine = false; 489f51cf32Spaul luse static uint64_t g_tsc_rate; 499f51cf32Spaul luse static uint64_t g_tsc_end; 509b189667Spaul luse static int g_rc; 519f51cf32Spaul luse static int g_xfer_size_bytes = 4096; 529f51cf32Spaul luse static int g_queue_depth = 32; 53f17e6705Spaul luse static int g_ops_per_batch = 0; 54445fe74eSpaul luse static int g_threads_per_core = 1; 559f51cf32Spaul luse static int g_time_in_sec = 5; 56e69375bfSpaul luse static uint32_t g_crc32c_seed = 0; 5788754353SZiye Yang static uint32_t g_crc32c_chained_count = 1; 58b9218b7aSpaul luse static int g_fail_percent_goal = 0; 5989495464Spaul luse static uint8_t g_fill_pattern = 255; 609f51cf32Spaul luse static bool g_verify = false; 612a0c66d0Spaul luse static const char *g_workload_type = NULL; 62514be889Spaul luse static enum accel_capability g_workload_selection; 639f51cf32Spaul luse static struct worker_thread *g_workers = NULL; 649f51cf32Spaul luse static int g_num_workers = 0; 659f51cf32Spaul luse static pthread_mutex_t g_workers_lock = PTHREAD_MUTEX_INITIALIZER; 66cdefd3d3Spaul luse 67cdefd3d3Spaul luse struct worker_thread; 68cdefd3d3Spaul luse static void accel_done(void *ref, int status); 69cdefd3d3Spaul luse 70445fe74eSpaul luse struct display_info { 71445fe74eSpaul luse int core; 72445fe74eSpaul luse int thread; 73445fe74eSpaul luse }; 74445fe74eSpaul luse 75cdefd3d3Spaul luse struct ap_task { 76cdefd3d3Spaul luse void *src; 7788754353SZiye Yang struct iovec *iovs; 7888754353SZiye Yang uint32_t iov_cnt; 79cdefd3d3Spaul luse void *dst; 80cdefd3d3Spaul luse void *dst2; 81cdefd3d3Spaul luse struct worker_thread *worker; 82cdefd3d3Spaul luse int status; 83cdefd3d3Spaul luse int expected_status; /* used for the compare operation */ 84cdefd3d3Spaul luse TAILQ_ENTRY(ap_task) link; 85cdefd3d3Spaul luse }; 869f51cf32Spaul luse 87f17e6705Spaul luse struct accel_batch { 88f17e6705Spaul luse int status; 89f17e6705Spaul luse int cmd_count; 90f17e6705Spaul luse struct spdk_accel_batch *batch; 91f17e6705Spaul luse struct worker_thread *worker; 92f17e6705Spaul luse TAILQ_ENTRY(accel_batch) link; 93f17e6705Spaul luse }; 94f17e6705Spaul luse 959f51cf32Spaul luse struct worker_thread { 969f51cf32Spaul luse struct spdk_io_channel *ch; 979f51cf32Spaul luse uint64_t xfer_completed; 989f51cf32Spaul luse uint64_t xfer_failed; 99b9218b7aSpaul luse uint64_t injected_miscompares; 1009f51cf32Spaul luse uint64_t current_queue_depth; 101ac9a1a83Spaul luse TAILQ_HEAD(, ap_task) tasks_pool; 1029f51cf32Spaul luse struct worker_thread *next; 1039f51cf32Spaul luse unsigned core; 1049f51cf32Spaul luse struct spdk_thread *thread; 1059f51cf32Spaul luse bool is_draining; 1069f51cf32Spaul luse struct spdk_poller *is_draining_poller; 1079f51cf32Spaul luse struct spdk_poller *stop_poller; 108ac9a1a83Spaul luse void *task_base; 109f17e6705Spaul luse struct accel_batch *batch_base; 110445fe74eSpaul luse struct display_info display; 111f17e6705Spaul luse TAILQ_HEAD(, accel_batch) in_prep_batches; 112f17e6705Spaul luse TAILQ_HEAD(, accel_batch) in_use_batches; 113f17e6705Spaul luse TAILQ_HEAD(, accel_batch) to_submit_batches; 1149f51cf32Spaul luse }; 1159f51cf32Spaul luse 1169f51cf32Spaul luse static void 1179f51cf32Spaul luse dump_user_config(struct spdk_app_opts *opts) 1189f51cf32Spaul luse { 1199f51cf32Spaul luse printf("SPDK Configuration:\n"); 1209f51cf32Spaul luse printf("Core mask: %s\n\n", opts->reactor_mask); 1219f51cf32Spaul luse printf("Accel Perf Configuration:\n"); 1222a0c66d0Spaul luse printf("Workload Type: %s\n", g_workload_type); 123b9218b7aSpaul luse if (g_workload_selection == ACCEL_CRC32C) { 124b9218b7aSpaul luse printf("CRC-32C seed: %u\n", g_crc32c_seed); 12588754353SZiye Yang printf("vector size: %u\n", g_crc32c_chained_count); 12689495464Spaul luse } else if (g_workload_selection == ACCEL_FILL) { 12789495464Spaul luse printf("Fill pattern: 0x%x\n", g_fill_pattern); 128b9218b7aSpaul luse } else if ((g_workload_selection == ACCEL_COMPARE) && g_fail_percent_goal > 0) { 12989495464Spaul luse printf("Failure inject: %u percent\n", g_fail_percent_goal); 130e69375bfSpaul luse } 1319f51cf32Spaul luse printf("Transfer size: %u bytes\n", g_xfer_size_bytes); 1329f51cf32Spaul luse printf("Queue depth: %u\n", g_queue_depth); 133445fe74eSpaul luse printf("# threads/core: %u\n", g_threads_per_core); 1349f51cf32Spaul luse printf("Run time: %u seconds\n", g_time_in_sec); 135f17e6705Spaul luse if (g_ops_per_batch > 0) { 136f17e6705Spaul luse printf("Batching: %u operations\n", g_ops_per_batch); 137f17e6705Spaul luse } else { 138f17e6705Spaul luse printf("Batching: Disabled\n"); 139f17e6705Spaul luse } 1409f51cf32Spaul luse printf("Verify: %s\n\n", g_verify ? "Yes" : "No"); 1419f51cf32Spaul luse } 1429f51cf32Spaul luse 1439f51cf32Spaul luse static void 1449f51cf32Spaul luse usage(void) 1459f51cf32Spaul luse { 1469f51cf32Spaul luse printf("accel_perf options:\n"); 1479f51cf32Spaul luse printf("\t[-h help message]\n"); 148f17e6705Spaul luse printf("\t[-q queue depth per core]\n"); 14988754353SZiye Yang printf("\t[-C for crc32c workload, use this value to configre the io vector size to test (default 1)\n"); 150445fe74eSpaul luse printf("\t[-T number of threads per core\n"); 15188754353SZiye Yang printf("\t[-n number of channels]\n"); 1529f51cf32Spaul luse printf("\t[-o transfer size in bytes]\n"); 1539f51cf32Spaul luse printf("\t[-t time in seconds]\n"); 1540ef079c6Spaul luse printf("\t[-w workload type must be one of these: copy, fill, crc32c, compare, dualcast\n"); 155e69375bfSpaul luse printf("\t[-s for crc32c workload, use this seed value (default 0)\n"); 156b9218b7aSpaul luse printf("\t[-P for compare workload, percentage of operations that should miscompare (percent, default 0)\n"); 15789495464Spaul luse printf("\t[-f for fill workload, use this BYTE value (default 255)\n"); 1582a0c66d0Spaul luse printf("\t[-y verify result if this switch is on]\n"); 159f17e6705Spaul luse printf("\t[-b batch this number of operations at a time (default 0 = disabled)]\n"); 1609f51cf32Spaul luse } 1619f51cf32Spaul luse 1629f51cf32Spaul luse static int 1639f51cf32Spaul luse parse_args(int argc, char *argv) 1649f51cf32Spaul luse { 1659f51cf32Spaul luse switch (argc) { 166f17e6705Spaul luse case 'b': 167f17e6705Spaul luse g_ops_per_batch = spdk_strtol(optarg, 10); 168f17e6705Spaul luse break; 16988754353SZiye Yang case 'C': 17088754353SZiye Yang g_crc32c_chained_count = spdk_strtol(optarg, 10); 17188754353SZiye Yang break; 17289495464Spaul luse case 'f': 17389495464Spaul luse g_fill_pattern = (uint8_t)spdk_strtol(optarg, 10); 17489495464Spaul luse break; 175445fe74eSpaul luse case 'T': 176445fe74eSpaul luse g_threads_per_core = spdk_strtol(optarg, 10); 177445fe74eSpaul luse break; 1789f51cf32Spaul luse case 'o': 1799f51cf32Spaul luse g_xfer_size_bytes = spdk_strtol(optarg, 10); 1809f51cf32Spaul luse break; 181b9218b7aSpaul luse case 'P': 182b9218b7aSpaul luse g_fail_percent_goal = spdk_strtol(optarg, 10); 183b9218b7aSpaul luse break; 1849f51cf32Spaul luse case 'q': 1859f51cf32Spaul luse g_queue_depth = spdk_strtol(optarg, 10); 1869f51cf32Spaul luse break; 187e69375bfSpaul luse case 's': 188e69375bfSpaul luse g_crc32c_seed = spdk_strtol(optarg, 10); 189e69375bfSpaul luse break; 1909f51cf32Spaul luse case 't': 1919f51cf32Spaul luse g_time_in_sec = spdk_strtol(optarg, 10); 1929f51cf32Spaul luse break; 1939f51cf32Spaul luse case 'y': 1949f51cf32Spaul luse g_verify = true; 1959f51cf32Spaul luse break; 1962a0c66d0Spaul luse case 'w': 1972a0c66d0Spaul luse g_workload_type = optarg; 198514be889Spaul luse if (!strcmp(g_workload_type, "copy")) { 199514be889Spaul luse g_workload_selection = ACCEL_COPY; 200514be889Spaul luse } else if (!strcmp(g_workload_type, "fill")) { 201514be889Spaul luse g_workload_selection = ACCEL_FILL; 202e69375bfSpaul luse } else if (!strcmp(g_workload_type, "crc32c")) { 203e69375bfSpaul luse g_workload_selection = ACCEL_CRC32C; 204b9218b7aSpaul luse } else if (!strcmp(g_workload_type, "compare")) { 205b9218b7aSpaul luse g_workload_selection = ACCEL_COMPARE; 2060ef079c6Spaul luse } else if (!strcmp(g_workload_type, "dualcast")) { 2070ef079c6Spaul luse g_workload_selection = ACCEL_DUALCAST; 208514be889Spaul luse } 2092a0c66d0Spaul luse break; 2109f51cf32Spaul luse default: 2119f51cf32Spaul luse usage(); 2129f51cf32Spaul luse return 1; 2139f51cf32Spaul luse } 21488754353SZiye Yang 2159f51cf32Spaul luse return 0; 2169f51cf32Spaul luse } 2179f51cf32Spaul luse 218eea826a2Spaul luse static int dump_result(void); 2199f51cf32Spaul luse static void 2209f51cf32Spaul luse unregister_worker(void *arg1) 2219f51cf32Spaul luse { 2229f51cf32Spaul luse struct worker_thread *worker = arg1; 2239f51cf32Spaul luse 224ac9a1a83Spaul luse free(worker->task_base); 225f17e6705Spaul luse free(worker->batch_base); 2269f51cf32Spaul luse spdk_put_io_channel(worker->ch); 2279f51cf32Spaul luse pthread_mutex_lock(&g_workers_lock); 2289f51cf32Spaul luse assert(g_num_workers >= 1); 2299f51cf32Spaul luse if (--g_num_workers == 0) { 2309f51cf32Spaul luse pthread_mutex_unlock(&g_workers_lock); 2319b189667Spaul luse g_rc = dump_result(); 2329f51cf32Spaul luse spdk_app_stop(0); 2339f51cf32Spaul luse } 2349f51cf32Spaul luse pthread_mutex_unlock(&g_workers_lock); 2359f51cf32Spaul luse } 2369f51cf32Spaul luse 2378da995c4Spaul luse static int 2388da995c4Spaul luse _get_task_data_bufs(struct ap_task *task) 2398da995c4Spaul luse { 2408da995c4Spaul luse uint32_t align = 0; 24188754353SZiye Yang uint32_t i = 0; 2428da995c4Spaul luse 2438da995c4Spaul luse /* For dualcast, the DSA HW requires 4K alignment on destination addresses but 2448da995c4Spaul luse * we do this for all engines to keep it simple. 2458da995c4Spaul luse */ 2468da995c4Spaul luse if (g_workload_selection == ACCEL_DUALCAST) { 2478da995c4Spaul luse align = ALIGN_4K; 2488da995c4Spaul luse } 2498da995c4Spaul luse 25088754353SZiye Yang if (g_workload_selection == ACCEL_CRC32C) { 25188754353SZiye Yang assert(g_crc32c_chained_count > 0); 25288754353SZiye Yang task->iov_cnt = g_crc32c_chained_count; 25388754353SZiye Yang task->iovs = calloc(task->iov_cnt, sizeof(struct iovec)); 25488754353SZiye Yang if (!task->iovs) { 25588754353SZiye Yang fprintf(stderr, "cannot allocated task->iovs fot task=%p\n", task); 25688754353SZiye Yang return -ENOMEM; 25788754353SZiye Yang } 25888754353SZiye Yang 25988754353SZiye Yang for (i = 0; i < task->iov_cnt; i++) { 26088754353SZiye Yang task->iovs[i].iov_base = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL); 26188754353SZiye Yang if (task->iovs[i].iov_base == NULL) { 26288754353SZiye Yang return -ENOMEM; 26388754353SZiye Yang } 26488754353SZiye Yang memset(task->iovs[i].iov_base, DATA_PATTERN, g_xfer_size_bytes); 26588754353SZiye Yang task->iovs[i].iov_len = g_xfer_size_bytes; 26688754353SZiye Yang } 26788754353SZiye Yang 26888754353SZiye Yang } else { 2698da995c4Spaul luse task->src = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL); 2708da995c4Spaul luse if (task->src == NULL) { 2718da995c4Spaul luse fprintf(stderr, "Unable to alloc src buffer\n"); 2728da995c4Spaul luse return -ENOMEM; 2738da995c4Spaul luse } 27488754353SZiye Yang 27588754353SZiye Yang /* For fill, set the entire src buffer so we can check if verify is enabled. */ 27688754353SZiye Yang if (g_workload_selection == ACCEL_FILL) { 27788754353SZiye Yang memset(task->src, g_fill_pattern, g_xfer_size_bytes); 27888754353SZiye Yang } else { 2798da995c4Spaul luse memset(task->src, DATA_PATTERN, g_xfer_size_bytes); 28088754353SZiye Yang } 28188754353SZiye Yang } 2828da995c4Spaul luse 2838da995c4Spaul luse task->dst = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL); 2848da995c4Spaul luse if (task->dst == NULL) { 2858da995c4Spaul luse fprintf(stderr, "Unable to alloc dst buffer\n"); 2868da995c4Spaul luse return -ENOMEM; 2878da995c4Spaul luse } 2888da995c4Spaul luse 2898da995c4Spaul luse /* For compare we want the buffers to match, otherwise not. */ 2908da995c4Spaul luse if (g_workload_selection == ACCEL_COMPARE) { 2918da995c4Spaul luse memset(task->dst, DATA_PATTERN, g_xfer_size_bytes); 2928da995c4Spaul luse } else { 2938da995c4Spaul luse memset(task->dst, ~DATA_PATTERN, g_xfer_size_bytes); 2948da995c4Spaul luse } 2958da995c4Spaul luse 2968da995c4Spaul luse if (g_workload_selection == ACCEL_DUALCAST) { 2978da995c4Spaul luse task->dst2 = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL); 2988da995c4Spaul luse if (task->dst2 == NULL) { 2998da995c4Spaul luse fprintf(stderr, "Unable to alloc dst buffer\n"); 3008da995c4Spaul luse return -ENOMEM; 3018da995c4Spaul luse } 3028da995c4Spaul luse memset(task->dst2, ~DATA_PATTERN, g_xfer_size_bytes); 3038da995c4Spaul luse } 3048da995c4Spaul luse 3058da995c4Spaul luse return 0; 3068da995c4Spaul luse } 3078da995c4Spaul luse 308ac9a1a83Spaul luse inline static struct ap_task * 309ac9a1a83Spaul luse _get_task(struct worker_thread *worker) 310ac9a1a83Spaul luse { 311ac9a1a83Spaul luse struct ap_task *task; 312ac9a1a83Spaul luse 313ac9a1a83Spaul luse if (!TAILQ_EMPTY(&worker->tasks_pool)) { 314ac9a1a83Spaul luse task = TAILQ_FIRST(&worker->tasks_pool); 315ac9a1a83Spaul luse TAILQ_REMOVE(&worker->tasks_pool, task, link); 316ac9a1a83Spaul luse } else { 317ac9a1a83Spaul luse fprintf(stderr, "Unable to get ap_task\n"); 318ac9a1a83Spaul luse return NULL; 319ac9a1a83Spaul luse } 320ac9a1a83Spaul luse 321ac9a1a83Spaul luse return task; 322ac9a1a83Spaul luse } 323ac9a1a83Spaul luse 324f17e6705Spaul luse /* Submit one operation using the same ap task that just completed. */ 3259f51cf32Spaul luse static void 326ac9a1a83Spaul luse _submit_single(struct worker_thread *worker, struct ap_task *task) 3279f51cf32Spaul luse { 328b9218b7aSpaul luse int random_num; 32940ec8e97Spaul luse int rc = 0; 3309f51cf32Spaul luse 3319f51cf32Spaul luse assert(worker); 3329f51cf32Spaul luse 333e69375bfSpaul luse switch (g_workload_selection) { 334e69375bfSpaul luse case ACCEL_COPY: 335e8463f87Spaul luse rc = spdk_accel_submit_copy(worker->ch, task->dst, task->src, 336e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 337e69375bfSpaul luse break; 338e69375bfSpaul luse case ACCEL_FILL: 3392a0c66d0Spaul luse /* For fill use the first byte of the task->dst buffer */ 340ee7e31f9Spaul luse rc = spdk_accel_submit_fill(worker->ch, task->dst, *(uint8_t *)task->src, 341e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 342e69375bfSpaul luse break; 343e69375bfSpaul luse case ACCEL_CRC32C: 34490c56d96SZiye Yang rc = spdk_accel_submit_crc32cv(worker->ch, (uint32_t *)task->dst, 34588754353SZiye Yang task->iovs, task->iov_cnt, g_crc32c_seed, 34690c56d96SZiye Yang accel_done, task); 347e69375bfSpaul luse break; 348b9218b7aSpaul luse case ACCEL_COMPARE: 349b9218b7aSpaul luse random_num = rand() % 100; 350b9218b7aSpaul luse if (random_num < g_fail_percent_goal) { 351b9218b7aSpaul luse task->expected_status = -EILSEQ; 352b9218b7aSpaul luse *(uint8_t *)task->dst = ~DATA_PATTERN; 353b9218b7aSpaul luse } else { 354b9218b7aSpaul luse task->expected_status = 0; 355b9218b7aSpaul luse *(uint8_t *)task->dst = DATA_PATTERN; 356b9218b7aSpaul luse } 357ee7e31f9Spaul luse rc = spdk_accel_submit_compare(worker->ch, task->dst, task->src, 358e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 359b9218b7aSpaul luse break; 3600ef079c6Spaul luse case ACCEL_DUALCAST: 361ee7e31f9Spaul luse rc = spdk_accel_submit_dualcast(worker->ch, task->dst, task->dst2, 362e8463f87Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 3630ef079c6Spaul luse break; 364e69375bfSpaul luse default: 3652a0c66d0Spaul luse assert(false); 366e69375bfSpaul luse break; 367e69375bfSpaul luse 3682a0c66d0Spaul luse } 36940ec8e97Spaul luse 37040ec8e97Spaul luse if (rc) { 371e8463f87Spaul luse accel_done(task, rc); 37240ec8e97Spaul luse } 3739f51cf32Spaul luse } 3749f51cf32Spaul luse 375fab40895Spaul luse static int 376f17e6705Spaul luse _batch_prep_cmd(struct worker_thread *worker, struct ap_task *task, 377f17e6705Spaul luse struct accel_batch *worker_batch) 378fab40895Spaul luse { 379f17e6705Spaul luse struct spdk_accel_batch *batch = worker_batch->batch; 380fab40895Spaul luse int rc = 0; 381fab40895Spaul luse 382f17e6705Spaul luse worker_batch->cmd_count++; 383f17e6705Spaul luse assert(worker_batch->cmd_count <= g_ops_per_batch); 384f17e6705Spaul luse 385fab40895Spaul luse switch (g_workload_selection) { 386fab40895Spaul luse case ACCEL_COPY: 387fab40895Spaul luse rc = spdk_accel_batch_prep_copy(worker->ch, batch, task->dst, 388fab40895Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 389fab40895Spaul luse break; 390fab40895Spaul luse case ACCEL_DUALCAST: 391fab40895Spaul luse rc = spdk_accel_batch_prep_dualcast(worker->ch, batch, task->dst, task->dst2, 392fab40895Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 393fab40895Spaul luse break; 394fab40895Spaul luse case ACCEL_COMPARE: 395fab40895Spaul luse rc = spdk_accel_batch_prep_compare(worker->ch, batch, task->dst, task->src, 396fab40895Spaul luse g_xfer_size_bytes, accel_done, task); 397fab40895Spaul luse break; 398fab40895Spaul luse case ACCEL_FILL: 399fab40895Spaul luse rc = spdk_accel_batch_prep_fill(worker->ch, batch, task->dst, 400fab40895Spaul luse *(uint8_t *)task->src, 401fab40895Spaul luse g_xfer_size_bytes, accel_done, task); 402fab40895Spaul luse break; 403fab40895Spaul luse case ACCEL_CRC32C: 40490c56d96SZiye Yang rc = spdk_accel_batch_prep_crc32cv(worker->ch, batch, (uint32_t *)task->dst, 40588754353SZiye Yang task->iovs, task->iov_cnt, g_crc32c_seed, accel_done, task); 406fab40895Spaul luse break; 407fab40895Spaul luse default: 408fab40895Spaul luse assert(false); 409fab40895Spaul luse break; 410fab40895Spaul luse } 411fab40895Spaul luse 412fab40895Spaul luse return rc; 413fab40895Spaul luse } 414fab40895Spaul luse 4159f51cf32Spaul luse static void 416e150f6b8SZiye Yang _free_task_buffers(struct ap_task *task) 417ac9a1a83Spaul luse { 41888754353SZiye Yang uint32_t i; 41988754353SZiye Yang 42088754353SZiye Yang if (g_workload_selection == ACCEL_CRC32C) { 42188754353SZiye Yang if (task->iovs) { 42288754353SZiye Yang for (i = 0; i < task->iov_cnt; i++) { 42388754353SZiye Yang if (task->iovs[i].iov_base) { 42488754353SZiye Yang spdk_dma_free(task->iovs[i].iov_base); 42588754353SZiye Yang } 42688754353SZiye Yang } 42788754353SZiye Yang free(task->iovs); 42888754353SZiye Yang } 42988754353SZiye Yang } else { 430ac9a1a83Spaul luse spdk_dma_free(task->src); 43188754353SZiye Yang } 43288754353SZiye Yang 433ac9a1a83Spaul luse spdk_dma_free(task->dst); 434ac9a1a83Spaul luse if (g_workload_selection == ACCEL_DUALCAST) { 435ac9a1a83Spaul luse spdk_dma_free(task->dst2); 436ac9a1a83Spaul luse } 437ac9a1a83Spaul luse } 438ac9a1a83Spaul luse 439f17e6705Spaul luse static void _batch_done(void *cb_arg); 440f17e6705Spaul luse static void 441f17e6705Spaul luse _build_batch(struct worker_thread *worker, struct ap_task *task) 442f17e6705Spaul luse { 443f17e6705Spaul luse struct accel_batch *worker_batch = NULL; 444f17e6705Spaul luse int rc; 445f17e6705Spaul luse 446f17e6705Spaul luse assert(!TAILQ_EMPTY(&worker->in_prep_batches)); 447f17e6705Spaul luse 448f17e6705Spaul luse worker_batch = TAILQ_FIRST(&worker->in_prep_batches); 449f17e6705Spaul luse 450f17e6705Spaul luse /* If an accel batch hasn't been created yet do so now. */ 451f17e6705Spaul luse if (worker_batch->batch == NULL) { 452f17e6705Spaul luse worker_batch->batch = spdk_accel_batch_create(worker->ch); 453f17e6705Spaul luse if (worker_batch->batch == NULL) { 454f17e6705Spaul luse fprintf(stderr, "error unable to create new batch\n"); 455f17e6705Spaul luse return; 456f17e6705Spaul luse } 457f17e6705Spaul luse } 458f17e6705Spaul luse 459f17e6705Spaul luse /* Prep the command re-using the last completed command's task */ 460f17e6705Spaul luse rc = _batch_prep_cmd(worker, task, worker_batch); 461f17e6705Spaul luse if (rc) { 462f17e6705Spaul luse fprintf(stderr, "error preping command for batch\n"); 463f17e6705Spaul luse goto error; 464f17e6705Spaul luse } 465f17e6705Spaul luse 466f17e6705Spaul luse /* If this batch is full move it to the to_submit list so it gets 467f17e6705Spaul luse * submitted as batches complete. 468f17e6705Spaul luse */ 469f17e6705Spaul luse if (worker_batch->cmd_count == g_ops_per_batch) { 470f17e6705Spaul luse TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link); 471f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->to_submit_batches, worker_batch, link); 472f17e6705Spaul luse } 473f17e6705Spaul luse 474f17e6705Spaul luse return; 475f17e6705Spaul luse error: 476f17e6705Spaul luse spdk_accel_batch_cancel(worker->ch, worker_batch->batch); 477f17e6705Spaul luse 478f17e6705Spaul luse } 479f17e6705Spaul luse 480f17e6705Spaul luse static void batch_done(void *cb_arg, int status); 481f17e6705Spaul luse static void 482f17e6705Spaul luse _drain_batch(struct worker_thread *worker) 483f17e6705Spaul luse { 484f17e6705Spaul luse struct accel_batch *worker_batch, *tmp; 485f17e6705Spaul luse int rc; 486f17e6705Spaul luse 487f17e6705Spaul luse /* submit any batches that were being built up. */ 488f17e6705Spaul luse TAILQ_FOREACH_SAFE(worker_batch, &worker->in_prep_batches, link, tmp) { 489f17e6705Spaul luse if (worker_batch->cmd_count == 0) { 490f17e6705Spaul luse continue; 491f17e6705Spaul luse } 492f17e6705Spaul luse worker->current_queue_depth += worker_batch->cmd_count + 1; 493f17e6705Spaul luse 494f17e6705Spaul luse TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link); 495f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link); 496f17e6705Spaul luse rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch); 497f17e6705Spaul luse if (rc == 0) { 498f17e6705Spaul luse worker_batch->cmd_count = 0; 499f17e6705Spaul luse } else { 500f17e6705Spaul luse fprintf(stderr, "error sending final batch\n"); 501f17e6705Spaul luse worker->current_queue_depth -= worker_batch->cmd_count + 1; 502f17e6705Spaul luse break; 503f17e6705Spaul luse } 504f17e6705Spaul luse } 505f17e6705Spaul luse } 506f17e6705Spaul luse 507f17e6705Spaul luse static void 508f17e6705Spaul luse _batch_done(void *cb_arg) 509f17e6705Spaul luse { 510f17e6705Spaul luse struct accel_batch *worker_batch = (struct accel_batch *)cb_arg; 511f17e6705Spaul luse struct worker_thread *worker = worker_batch->worker; 512f17e6705Spaul luse int rc; 513f17e6705Spaul luse 514f17e6705Spaul luse assert(TAILQ_EMPTY(&worker->in_use_batches) == 0); 515f17e6705Spaul luse 516f17e6705Spaul luse if (worker_batch->status) { 517f17e6705Spaul luse SPDK_ERRLOG("error %d\n", worker_batch->status); 518f17e6705Spaul luse } 519f17e6705Spaul luse 520f17e6705Spaul luse worker->current_queue_depth--; 521f17e6705Spaul luse TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link); 522f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link); 523f17e6705Spaul luse worker_batch->batch = NULL; 524f17e6705Spaul luse worker_batch->cmd_count = 0; 525f17e6705Spaul luse 526f17e6705Spaul luse if (!worker->is_draining) { 527f17e6705Spaul luse worker_batch = TAILQ_FIRST(&worker->to_submit_batches); 528f17e6705Spaul luse if (worker_batch != NULL) { 529f17e6705Spaul luse 530f17e6705Spaul luse assert(worker_batch->cmd_count == g_ops_per_batch); 531f17e6705Spaul luse 532f17e6705Spaul luse /* Add one for the batch command itself. */ 533f17e6705Spaul luse worker->current_queue_depth += g_ops_per_batch + 1; 534f17e6705Spaul luse TAILQ_REMOVE(&worker->to_submit_batches, worker_batch, link); 535f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link); 536f17e6705Spaul luse 537f17e6705Spaul luse rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch); 538f17e6705Spaul luse if (rc) { 539f17e6705Spaul luse fprintf(stderr, "error ending batch\n"); 540f17e6705Spaul luse worker->current_queue_depth -= g_ops_per_batch + 1; 541f17e6705Spaul luse return; 542f17e6705Spaul luse } 543f17e6705Spaul luse } 544f17e6705Spaul luse } else { 545f17e6705Spaul luse _drain_batch(worker); 546f17e6705Spaul luse } 547f17e6705Spaul luse } 548f17e6705Spaul luse 549ac9a1a83Spaul luse static void 550fab40895Spaul luse batch_done(void *cb_arg, int status) 551fab40895Spaul luse { 552f17e6705Spaul luse struct accel_batch *worker_batch = (struct accel_batch *)cb_arg; 553fab40895Spaul luse 554f17e6705Spaul luse assert(worker_batch->worker); 555f17e6705Spaul luse 556f17e6705Spaul luse worker_batch->status = status; 557f17e6705Spaul luse spdk_thread_send_msg(worker_batch->worker->thread, _batch_done, worker_batch); 558fab40895Spaul luse } 559fab40895Spaul luse 560fab40895Spaul luse static void 5619f51cf32Spaul luse _accel_done(void *arg1) 5629f51cf32Spaul luse { 5639f51cf32Spaul luse struct ap_task *task = arg1; 5649f51cf32Spaul luse struct worker_thread *worker = task->worker; 565e69375bfSpaul luse uint32_t sw_crc32c; 5669f51cf32Spaul luse 5679f51cf32Spaul luse assert(worker); 5689f51cf32Spaul luse assert(worker->current_queue_depth > 0); 5699f51cf32Spaul luse 570b9218b7aSpaul luse if (g_verify && task->status == 0) { 571b9218b7aSpaul luse switch (g_workload_selection) { 572b9218b7aSpaul luse case ACCEL_CRC32C: 573b85127ccSZiye Yang sw_crc32c = spdk_crc32c_iov_update(task->iovs, task->iov_cnt, ~g_crc32c_seed); 574e69375bfSpaul luse if (*(uint32_t *)task->dst != sw_crc32c) { 575e69375bfSpaul luse SPDK_NOTICELOG("CRC-32C miscompare\n"); 576e69375bfSpaul luse worker->xfer_failed++; 577e69375bfSpaul luse } 578b9218b7aSpaul luse break; 579b9218b7aSpaul luse case ACCEL_COPY: 580b9218b7aSpaul luse if (memcmp(task->src, task->dst, g_xfer_size_bytes)) { 5819f51cf32Spaul luse SPDK_NOTICELOG("Data miscompare\n"); 5829f51cf32Spaul luse worker->xfer_failed++; 583b9218b7aSpaul luse } 584b9218b7aSpaul luse break; 5850ef079c6Spaul luse case ACCEL_DUALCAST: 5860ef079c6Spaul luse if (memcmp(task->src, task->dst, g_xfer_size_bytes)) { 5870ef079c6Spaul luse SPDK_NOTICELOG("Data miscompare, first destination\n"); 5880ef079c6Spaul luse worker->xfer_failed++; 5890ef079c6Spaul luse } 5900ef079c6Spaul luse if (memcmp(task->src, task->dst2, g_xfer_size_bytes)) { 5910ef079c6Spaul luse SPDK_NOTICELOG("Data miscompare, second destination\n"); 5920ef079c6Spaul luse worker->xfer_failed++; 5930ef079c6Spaul luse } 5940ef079c6Spaul luse break; 595d207237fSpaul luse case ACCEL_FILL: 596d207237fSpaul luse if (memcmp(task->dst, task->src, g_xfer_size_bytes)) { 597d207237fSpaul luse SPDK_NOTICELOG("Data miscompare\n"); 598d207237fSpaul luse worker->xfer_failed++; 599d207237fSpaul luse } 600d207237fSpaul luse break; 6018cee297cSpaul luse case ACCEL_COMPARE: 6028cee297cSpaul luse break; 603b9218b7aSpaul luse default: 604b9218b7aSpaul luse assert(false); 605b9218b7aSpaul luse break; 6069f51cf32Spaul luse } 6079f51cf32Spaul luse } 608b9218b7aSpaul luse 609b9218b7aSpaul luse if (task->expected_status == -EILSEQ) { 610b9218b7aSpaul luse assert(task->status != 0); 611b9218b7aSpaul luse worker->injected_miscompares++; 612b9218b7aSpaul luse } else if (task->status) { 613f17e6705Spaul luse /* Expected to pass but the accel engine reported an error (ex: COMPARE operation). */ 614b9218b7aSpaul luse worker->xfer_failed++; 615b9218b7aSpaul luse } 616b9218b7aSpaul luse 6179f51cf32Spaul luse worker->xfer_completed++; 6189f51cf32Spaul luse worker->current_queue_depth--; 6199f51cf32Spaul luse 62040ec8e97Spaul luse if (!worker->is_draining) { 621*451462f6SJim Harris TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link); 622*451462f6SJim Harris task = _get_task(worker); 623f17e6705Spaul luse if (g_ops_per_batch == 0) { 6249f51cf32Spaul luse _submit_single(worker, task); 625ac9a1a83Spaul luse worker->current_queue_depth++; 626f17e6705Spaul luse } else { 627f17e6705Spaul luse _build_batch(worker, task); 6289f51cf32Spaul luse } 629f17e6705Spaul luse } else if (g_ops_per_batch > 0) { 630f17e6705Spaul luse _drain_batch(worker); 631b34883e0SZiye Yang } else { 632b34883e0SZiye Yang TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link); 633f17e6705Spaul luse } 6349f51cf32Spaul luse } 6359f51cf32Spaul luse 6369f51cf32Spaul luse static int 6379f51cf32Spaul luse dump_result(void) 6389f51cf32Spaul luse { 6399f51cf32Spaul luse uint64_t total_completed = 0; 6409f51cf32Spaul luse uint64_t total_failed = 0; 641b9218b7aSpaul luse uint64_t total_miscompared = 0; 6429f51cf32Spaul luse uint64_t total_xfer_per_sec, total_bw_in_MiBps; 6439f51cf32Spaul luse struct worker_thread *worker = g_workers; 6449f51cf32Spaul luse 645445fe74eSpaul luse printf("\nCore,Thread Transfers Bandwidth Failed Miscompares\n"); 646445fe74eSpaul luse printf("------------------------------------------------------------------------\n"); 6479f51cf32Spaul luse while (worker != NULL) { 6489f51cf32Spaul luse 6499f51cf32Spaul luse uint64_t xfer_per_sec = worker->xfer_completed / g_time_in_sec; 6509f51cf32Spaul luse uint64_t bw_in_MiBps = (worker->xfer_completed * g_xfer_size_bytes) / 6519f51cf32Spaul luse (g_time_in_sec * 1024 * 1024); 6529f51cf32Spaul luse 6539f51cf32Spaul luse total_completed += worker->xfer_completed; 6549f51cf32Spaul luse total_failed += worker->xfer_failed; 655b9218b7aSpaul luse total_miscompared += worker->injected_miscompares; 6569f51cf32Spaul luse 6579f51cf32Spaul luse if (xfer_per_sec) { 658445fe74eSpaul luse printf("%u,%u%17" PRIu64 "/s%9" PRIu64 " MiB/s%7" PRIu64 " %11" PRIu64 "\n", 659445fe74eSpaul luse worker->display.core, worker->display.thread, xfer_per_sec, 660b9218b7aSpaul luse bw_in_MiBps, worker->xfer_failed, worker->injected_miscompares); 6619f51cf32Spaul luse } 6629f51cf32Spaul luse 6639f51cf32Spaul luse worker = worker->next; 6649f51cf32Spaul luse } 6659f51cf32Spaul luse 6669f51cf32Spaul luse total_xfer_per_sec = total_completed / g_time_in_sec; 6679f51cf32Spaul luse total_bw_in_MiBps = (total_completed * g_xfer_size_bytes) / 6689f51cf32Spaul luse (g_time_in_sec * 1024 * 1024); 6699f51cf32Spaul luse 670445fe74eSpaul luse printf("=========================================================================\n"); 671445fe74eSpaul luse printf("Total:%15" PRIu64 "/s%9" PRIu64 " MiB/s%6" PRIu64 " %11" PRIu64"\n\n", 672b9218b7aSpaul luse total_xfer_per_sec, total_bw_in_MiBps, total_failed, total_miscompared); 6739f51cf32Spaul luse 6749f51cf32Spaul luse return total_failed ? 1 : 0; 6759f51cf32Spaul luse } 6769f51cf32Spaul luse 677e150f6b8SZiye Yang static inline void 678e150f6b8SZiye Yang _free_task_buffers_in_pool(struct worker_thread *worker) 679e150f6b8SZiye Yang { 680e150f6b8SZiye Yang struct ap_task *task; 681e150f6b8SZiye Yang 682e150f6b8SZiye Yang assert(worker); 683e150f6b8SZiye Yang while ((task = TAILQ_FIRST(&worker->tasks_pool))) { 684e150f6b8SZiye Yang TAILQ_REMOVE(&worker->tasks_pool, task, link); 685e150f6b8SZiye Yang _free_task_buffers(task); 686e150f6b8SZiye Yang } 687e150f6b8SZiye Yang } 688e150f6b8SZiye Yang 6899f51cf32Spaul luse static int 6909f51cf32Spaul luse _check_draining(void *arg) 6919f51cf32Spaul luse { 6929f51cf32Spaul luse struct worker_thread *worker = arg; 6939f51cf32Spaul luse 6949f51cf32Spaul luse assert(worker); 6959f51cf32Spaul luse 6969f51cf32Spaul luse if (worker->current_queue_depth == 0) { 697e150f6b8SZiye Yang _free_task_buffers_in_pool(worker); 6989f51cf32Spaul luse spdk_poller_unregister(&worker->is_draining_poller); 6999f51cf32Spaul luse unregister_worker(worker); 7009f51cf32Spaul luse } 7019f51cf32Spaul luse 7029f51cf32Spaul luse return -1; 7039f51cf32Spaul luse } 7049f51cf32Spaul luse 7059f51cf32Spaul luse static int 7069f51cf32Spaul luse _worker_stop(void *arg) 7079f51cf32Spaul luse { 7089f51cf32Spaul luse struct worker_thread *worker = arg; 7099f51cf32Spaul luse 7109f51cf32Spaul luse assert(worker); 7119f51cf32Spaul luse 7129f51cf32Spaul luse spdk_poller_unregister(&worker->stop_poller); 7139f51cf32Spaul luse 7149f51cf32Spaul luse /* now let the worker drain and check it's outstanding IO with a poller */ 7159f51cf32Spaul luse worker->is_draining = true; 716ab0bc5c2SShuhei Matsumoto worker->is_draining_poller = SPDK_POLLER_REGISTER(_check_draining, worker, 0); 7179f51cf32Spaul luse 7189f51cf32Spaul luse return 0; 7199f51cf32Spaul luse } 7209f51cf32Spaul luse 7219f51cf32Spaul luse static void 722a34fc12bSpaul luse _init_thread(void *arg1) 723a34fc12bSpaul luse { 724a34fc12bSpaul luse struct worker_thread *worker; 725a34fc12bSpaul luse struct ap_task *task; 726f17e6705Spaul luse int i, rc, num_batches; 727f17e6705Spaul luse int max_per_batch; 728a34fc12bSpaul luse int remaining = g_queue_depth; 729f17e6705Spaul luse int num_tasks = g_queue_depth; 730f17e6705Spaul luse struct accel_batch *tmp; 731f17e6705Spaul luse struct accel_batch *worker_batch = NULL; 732445fe74eSpaul luse struct display_info *display = arg1; 733475fadf3Spaul luse uint64_t capabilities; 734a34fc12bSpaul luse 735a34fc12bSpaul luse worker = calloc(1, sizeof(*worker)); 736a34fc12bSpaul luse if (worker == NULL) { 737a34fc12bSpaul luse fprintf(stderr, "Unable to allocate worker\n"); 738445fe74eSpaul luse free(display); 739a34fc12bSpaul luse return; 740a34fc12bSpaul luse } 741a34fc12bSpaul luse 742445fe74eSpaul luse worker->display.core = display->core; 743445fe74eSpaul luse worker->display.thread = display->thread; 744445fe74eSpaul luse free(display); 7459f51cf32Spaul luse worker->core = spdk_env_get_current_core(); 7469f51cf32Spaul luse worker->thread = spdk_get_thread(); 747eea826a2Spaul luse pthread_mutex_lock(&g_workers_lock); 748eea826a2Spaul luse g_num_workers++; 7499f51cf32Spaul luse worker->next = g_workers; 750eea826a2Spaul luse g_workers = worker; 751eea826a2Spaul luse pthread_mutex_unlock(&g_workers_lock); 7529f51cf32Spaul luse worker->ch = spdk_accel_engine_get_io_channel(); 753b9218b7aSpaul luse 754475fadf3Spaul luse if (g_num_workers == 1) { 755475fadf3Spaul luse capabilities = spdk_accel_get_capabilities(worker->ch); 756475fadf3Spaul luse if ((capabilities & g_workload_selection) != g_workload_selection) { 757475fadf3Spaul luse g_using_sw_engine = true; 758475fadf3Spaul luse SPDK_WARNLOG("The selected workload is not natively supported by the current engine\n"); 759475fadf3Spaul luse SPDK_WARNLOG("The software engine will be used instead.\n\n"); 760475fadf3Spaul luse } 761475fadf3Spaul luse } 762475fadf3Spaul luse 763f17e6705Spaul luse TAILQ_INIT(&worker->tasks_pool); 764f17e6705Spaul luse 765f17e6705Spaul luse if (g_ops_per_batch > 0) { 766f17e6705Spaul luse 7670cecfcb1Spaul luse max_per_batch = spdk_accel_batch_get_max(worker->ch); 7680cecfcb1Spaul luse assert(max_per_batch > 0); 7690cecfcb1Spaul luse 770f17e6705Spaul luse if (g_ops_per_batch > max_per_batch) { 771f17e6705Spaul luse fprintf(stderr, "Reducing requested batch amount to max supported of %d\n", max_per_batch); 772f17e6705Spaul luse g_ops_per_batch = max_per_batch; 773f17e6705Spaul luse } 774f17e6705Spaul luse 775f17e6705Spaul luse if (g_ops_per_batch > g_queue_depth) { 776f17e6705Spaul luse fprintf(stderr, "Batch amount > queue depth, resetting to %d\n", g_queue_depth); 777f17e6705Spaul luse g_ops_per_batch = g_queue_depth; 778f17e6705Spaul luse } 779f17e6705Spaul luse 780f17e6705Spaul luse TAILQ_INIT(&worker->in_prep_batches); 781f17e6705Spaul luse TAILQ_INIT(&worker->to_submit_batches); 782f17e6705Spaul luse TAILQ_INIT(&worker->in_use_batches); 783f17e6705Spaul luse 784f17e6705Spaul luse /* A worker_batch will live on one of 3 lists: 785f17e6705Spaul luse * IN_PREP: as individual IOs complete new ones are built on on a 786f17e6705Spaul luse * worker_batch on this list until it reaches g_ops_per_batch. 787f17e6705Spaul luse * TO_SUBMIT: as batches are built up on IO completion they are moved 788f17e6705Spaul luse * to this list once they are full. This list is used in 789f17e6705Spaul luse * batch completion to start new batches. 790f17e6705Spaul luse * IN_USE: the worker_batch is outstanding and will be moved to in prep 791f17e6705Spaul luse * list when the batch is completed. 792f17e6705Spaul luse * 793f17e6705Spaul luse * So we need enough to cover Q depth loading and then one to replace 794f17e6705Spaul luse * each one of those and for when everything is outstanding there needs 795f17e6705Spaul luse * to be one extra batch to build up while the last batch is completing 796f17e6705Spaul luse * IO but before it's completed the batch command. 797f17e6705Spaul luse */ 798f17e6705Spaul luse num_batches = (g_queue_depth / g_ops_per_batch * 2) + 1; 799f17e6705Spaul luse worker->batch_base = calloc(num_batches, sizeof(struct accel_batch)); 800f17e6705Spaul luse worker_batch = worker->batch_base; 801f17e6705Spaul luse for (i = 0; i < num_batches; i++) { 802f17e6705Spaul luse worker_batch->worker = worker; 803f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link); 804f17e6705Spaul luse worker_batch++; 805f17e6705Spaul luse } 806f17e6705Spaul luse } 807f17e6705Spaul luse 808ac9a1a83Spaul luse worker->task_base = calloc(num_tasks, sizeof(struct ap_task)); 809ac9a1a83Spaul luse if (worker->task_base == NULL) { 810ac9a1a83Spaul luse fprintf(stderr, "Could not allocate task base.\n"); 811ac9a1a83Spaul luse goto error; 8120cecfcb1Spaul luse } 813ac9a1a83Spaul luse 814ac9a1a83Spaul luse task = worker->task_base; 815ac9a1a83Spaul luse for (i = 0; i < num_tasks; i++) { 816ac9a1a83Spaul luse TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link); 8174cd7ca9bSJim Harris task->worker = worker; 818ac9a1a83Spaul luse if (_get_task_data_bufs(task)) { 819ac9a1a83Spaul luse fprintf(stderr, "Unable to get data bufs\n"); 820ac9a1a83Spaul luse goto error; 821ac9a1a83Spaul luse } 822ac9a1a83Spaul luse task++; 8239f51cf32Spaul luse } 8249f51cf32Spaul luse 8259f51cf32Spaul luse /* Register a poller that will stop the worker at time elapsed */ 826ab0bc5c2SShuhei Matsumoto worker->stop_poller = SPDK_POLLER_REGISTER(_worker_stop, worker, 8279f51cf32Spaul luse g_time_in_sec * 1000000ULL); 8289f51cf32Spaul luse 829f17e6705Spaul luse /* If batching is enabled load up to the full Q depth before 830f17e6705Spaul luse * processing any completions, then ping pong between two batches, 831f17e6705Spaul luse * one processing and one being built up for when the other completes. 832a34fc12bSpaul luse */ 833f17e6705Spaul luse if (g_ops_per_batch > 0) { 834a34fc12bSpaul luse do { 835f17e6705Spaul luse worker_batch = TAILQ_FIRST(&worker->in_prep_batches); 836f17e6705Spaul luse if (worker_batch == NULL) { 837f17e6705Spaul luse goto error; 838f17e6705Spaul luse } 839f17e6705Spaul luse 840f17e6705Spaul luse worker_batch->batch = spdk_accel_batch_create(worker->ch); 841f17e6705Spaul luse if (worker_batch->batch == NULL) { 842f17e6705Spaul luse raise(SIGINT); 843a34fc12bSpaul luse break; 844a34fc12bSpaul luse } 845a34fc12bSpaul luse 846f17e6705Spaul luse for (i = 0; i < g_ops_per_batch; i++) { 847ac9a1a83Spaul luse task = _get_task(worker); 8484cd7ca9bSJim Harris worker->current_queue_depth++; 849ac9a1a83Spaul luse if (task == NULL) { 850a34fc12bSpaul luse goto error; 8519f51cf32Spaul luse } 852b9218b7aSpaul luse 853f17e6705Spaul luse rc = _batch_prep_cmd(worker, task, worker_batch); 854a34fc12bSpaul luse if (rc) { 855a34fc12bSpaul luse fprintf(stderr, "error preping command\n"); 856a34fc12bSpaul luse goto error; 857a34fc12bSpaul luse } 858a34fc12bSpaul luse } 859a34fc12bSpaul luse 860f17e6705Spaul luse /* for the batch operation itself. */ 861f17e6705Spaul luse task->worker->current_queue_depth++; 862f17e6705Spaul luse TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link); 863f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link); 864f17e6705Spaul luse 865f17e6705Spaul luse rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch); 866a34fc12bSpaul luse if (rc) { 867f17e6705Spaul luse fprintf(stderr, "error ending batch\n"); 868a34fc12bSpaul luse goto error; 869a34fc12bSpaul luse } 870f17e6705Spaul luse assert(remaining >= g_ops_per_batch); 871f17e6705Spaul luse remaining -= g_ops_per_batch; 872f17e6705Spaul luse } while (remaining > 0); 873b9218b7aSpaul luse } 8740ef079c6Spaul luse 875f17e6705Spaul luse /* Submit as singles when no batching is enabled or we ran out of batches. */ 876a34fc12bSpaul luse for (i = 0; i < remaining; i++) { 877ac9a1a83Spaul luse task = _get_task(worker); 8784cd7ca9bSJim Harris worker->current_queue_depth++; 879ac9a1a83Spaul luse if (task == NULL) { 880a34fc12bSpaul luse goto error; 881b9218b7aSpaul luse } 882b9218b7aSpaul luse 8839f51cf32Spaul luse _submit_single(worker, task); 8849f51cf32Spaul luse } 885a34fc12bSpaul luse return; 886a34fc12bSpaul luse error: 887f17e6705Spaul luse if (worker_batch && worker_batch->batch) { 888f17e6705Spaul luse TAILQ_FOREACH_SAFE(worker_batch, &worker->in_use_batches, link, tmp) { 889f17e6705Spaul luse spdk_accel_batch_cancel(worker->ch, worker_batch->batch); 890f17e6705Spaul luse TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link); 891f17e6705Spaul luse } 892f17e6705Spaul luse } 893e150f6b8SZiye Yang 894e150f6b8SZiye Yang _free_task_buffers_in_pool(worker); 895f17e6705Spaul luse free(worker->batch_base); 896ac9a1a83Spaul luse free(worker->task_base); 897a34fc12bSpaul luse free(worker); 898a34fc12bSpaul luse spdk_app_stop(-1); 8999f51cf32Spaul luse } 9009f51cf32Spaul luse 9019f51cf32Spaul luse static void 902e8463f87Spaul luse accel_done(void *cb_arg, int status) 9039f51cf32Spaul luse { 904e8463f87Spaul luse struct ap_task *task = (struct ap_task *)cb_arg; 9059f51cf32Spaul luse struct worker_thread *worker = task->worker; 9069f51cf32Spaul luse 9079f51cf32Spaul luse assert(worker); 9089f51cf32Spaul luse 909b9218b7aSpaul luse task->status = status; 9103e2e2a57Spaul luse if (g_using_sw_engine == false) { 9113e2e2a57Spaul luse _accel_done(task); 9123e2e2a57Spaul luse } else { 9139f51cf32Spaul luse spdk_thread_send_msg(worker->thread, _accel_done, task); 9149f51cf32Spaul luse } 9153e2e2a57Spaul luse } 9169f51cf32Spaul luse 9179f51cf32Spaul luse static void 9189f51cf32Spaul luse accel_perf_start(void *arg1) 9199f51cf32Spaul luse { 920eea826a2Spaul luse struct spdk_cpuset tmp_cpumask = {}; 921eea826a2Spaul luse char thread_name[32]; 922eea826a2Spaul luse uint32_t i; 923445fe74eSpaul luse int j; 924eea826a2Spaul luse struct spdk_thread *thread; 925445fe74eSpaul luse struct display_info *display; 926514be889Spaul luse 9279f51cf32Spaul luse g_tsc_rate = spdk_get_ticks_hz(); 9289f51cf32Spaul luse g_tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate; 9299f51cf32Spaul luse 9309f51cf32Spaul luse printf("Running for %d seconds...\n", g_time_in_sec); 9319f51cf32Spaul luse fflush(stdout); 9329f51cf32Spaul luse 933eea826a2Spaul luse /* Create worker threads for each core that was specified. */ 934eea826a2Spaul luse SPDK_ENV_FOREACH_CORE(i) { 935445fe74eSpaul luse for (j = 0; j < g_threads_per_core; j++) { 936445fe74eSpaul luse snprintf(thread_name, sizeof(thread_name), "ap_worker_%u_%u", i, j); 937eea826a2Spaul luse spdk_cpuset_zero(&tmp_cpumask); 938eea826a2Spaul luse spdk_cpuset_set_cpu(&tmp_cpumask, i, true); 939eea826a2Spaul luse thread = spdk_thread_create(thread_name, &tmp_cpumask); 940445fe74eSpaul luse display = calloc(1, sizeof(*display)); 941445fe74eSpaul luse if (display == NULL) { 942445fe74eSpaul luse fprintf(stderr, "Unable to allocate memory\n"); 943445fe74eSpaul luse spdk_app_stop(-1); 944445fe74eSpaul luse return; 945445fe74eSpaul luse } 946445fe74eSpaul luse display->core = i; 947445fe74eSpaul luse display->thread = j; 948445fe74eSpaul luse spdk_thread_send_msg(thread, _init_thread, display); 949445fe74eSpaul luse } 950eea826a2Spaul luse } 9519f51cf32Spaul luse } 9529f51cf32Spaul luse 9539f51cf32Spaul luse int 9549f51cf32Spaul luse main(int argc, char **argv) 9559f51cf32Spaul luse { 9569f51cf32Spaul luse struct spdk_app_opts opts = {}; 9579f51cf32Spaul luse struct worker_thread *worker, *tmp; 9589f51cf32Spaul luse 9599f51cf32Spaul luse pthread_mutex_init(&g_workers_lock, NULL); 96048701bd9SZiye Yang spdk_app_opts_init(&opts, sizeof(opts)); 9619f51cf32Spaul luse opts.reactor_mask = "0x1"; 96288754353SZiye Yang if (spdk_app_parse_args(argc, argv, &opts, "C:o:q:t:yw:P:f:b:T:", NULL, parse_args, 9631e2b38baSyidong0635 usage) != SPDK_APP_PARSE_ARGS_SUCCESS) { 9649b189667Spaul luse g_rc = -1; 9659f51cf32Spaul luse goto cleanup; 9669f51cf32Spaul luse } 9679f51cf32Spaul luse 968b9218b7aSpaul luse if ((g_workload_selection != ACCEL_COPY) && 969b9218b7aSpaul luse (g_workload_selection != ACCEL_FILL) && 970b9218b7aSpaul luse (g_workload_selection != ACCEL_CRC32C) && 9710ef079c6Spaul luse (g_workload_selection != ACCEL_COMPARE) && 9720ef079c6Spaul luse (g_workload_selection != ACCEL_DUALCAST)) { 9732a0c66d0Spaul luse usage(); 9749b189667Spaul luse g_rc = -1; 9752a0c66d0Spaul luse goto cleanup; 9762a0c66d0Spaul luse } 9772a0c66d0Spaul luse 978f17e6705Spaul luse if (g_ops_per_batch > 0 && (g_queue_depth % g_ops_per_batch > 0)) { 979f17e6705Spaul luse fprintf(stdout, "batch size must be a multiple of queue depth\n"); 980f17e6705Spaul luse usage(); 9819b189667Spaul luse g_rc = -1; 982f17e6705Spaul luse goto cleanup; 983f17e6705Spaul luse } 984f17e6705Spaul luse 98588754353SZiye Yang if (g_workload_selection == ACCEL_CRC32C && 98688754353SZiye Yang g_crc32c_chained_count == 0) { 98788754353SZiye Yang usage(); 98888754353SZiye Yang g_rc = -1; 98988754353SZiye Yang goto cleanup; 99088754353SZiye Yang } 99188754353SZiye Yang 9929f51cf32Spaul luse dump_user_config(&opts); 9939b189667Spaul luse g_rc = spdk_app_start(&opts, accel_perf_start, NULL); 9949b189667Spaul luse if (g_rc) { 9959f51cf32Spaul luse SPDK_ERRLOG("ERROR starting application\n"); 9969f51cf32Spaul luse } 9979f51cf32Spaul luse 9989f51cf32Spaul luse pthread_mutex_destroy(&g_workers_lock); 9999f51cf32Spaul luse 10009f51cf32Spaul luse worker = g_workers; 10019f51cf32Spaul luse while (worker) { 10029f51cf32Spaul luse tmp = worker->next; 10039f51cf32Spaul luse free(worker); 10049f51cf32Spaul luse worker = tmp; 10059f51cf32Spaul luse } 10069f51cf32Spaul luse cleanup: 10079f51cf32Spaul luse spdk_app_fini(); 10089b189667Spaul luse return g_rc; 10099f51cf32Spaul luse } 1010