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; 50*9b189667Spaul 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; 57b9218b7aSpaul luse static int g_fail_percent_goal = 0; 5889495464Spaul luse static uint8_t g_fill_pattern = 255; 599f51cf32Spaul luse static bool g_verify = false; 602a0c66d0Spaul luse static const char *g_workload_type = NULL; 61514be889Spaul luse static enum accel_capability g_workload_selection; 629f51cf32Spaul luse static struct worker_thread *g_workers = NULL; 639f51cf32Spaul luse static int g_num_workers = 0; 649f51cf32Spaul luse static pthread_mutex_t g_workers_lock = PTHREAD_MUTEX_INITIALIZER; 65a34fc12bSpaul luse uint64_t g_capabilites; 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; 77cdefd3d3Spaul luse void *dst; 78cdefd3d3Spaul luse void *dst2; 79cdefd3d3Spaul luse struct worker_thread *worker; 80cdefd3d3Spaul luse int status; 81cdefd3d3Spaul luse int expected_status; /* used for the compare operation */ 82cdefd3d3Spaul luse TAILQ_ENTRY(ap_task) link; 83cdefd3d3Spaul luse }; 849f51cf32Spaul luse 85f17e6705Spaul luse struct accel_batch { 86f17e6705Spaul luse int status; 87f17e6705Spaul luse int cmd_count; 88f17e6705Spaul luse struct spdk_accel_batch *batch; 89f17e6705Spaul luse struct worker_thread *worker; 90f17e6705Spaul luse TAILQ_ENTRY(accel_batch) link; 91f17e6705Spaul luse }; 92f17e6705Spaul luse 939f51cf32Spaul luse struct worker_thread { 949f51cf32Spaul luse struct spdk_io_channel *ch; 959f51cf32Spaul luse uint64_t xfer_completed; 969f51cf32Spaul luse uint64_t xfer_failed; 97b9218b7aSpaul luse uint64_t injected_miscompares; 989f51cf32Spaul luse uint64_t current_queue_depth; 99ac9a1a83Spaul luse TAILQ_HEAD(, ap_task) tasks_pool; 1009f51cf32Spaul luse struct worker_thread *next; 1019f51cf32Spaul luse unsigned core; 1029f51cf32Spaul luse struct spdk_thread *thread; 1039f51cf32Spaul luse bool is_draining; 1049f51cf32Spaul luse struct spdk_poller *is_draining_poller; 1059f51cf32Spaul luse struct spdk_poller *stop_poller; 106ac9a1a83Spaul luse void *task_base; 107f17e6705Spaul luse struct accel_batch *batch_base; 108445fe74eSpaul luse struct display_info display; 109f17e6705Spaul luse TAILQ_HEAD(, accel_batch) in_prep_batches; 110f17e6705Spaul luse TAILQ_HEAD(, accel_batch) in_use_batches; 111f17e6705Spaul luse TAILQ_HEAD(, accel_batch) to_submit_batches; 1129f51cf32Spaul luse }; 1139f51cf32Spaul luse 1149f51cf32Spaul luse static void 1159f51cf32Spaul luse dump_user_config(struct spdk_app_opts *opts) 1169f51cf32Spaul luse { 1179f51cf32Spaul luse printf("SPDK Configuration:\n"); 1189f51cf32Spaul luse printf("Core mask: %s\n\n", opts->reactor_mask); 1199f51cf32Spaul luse printf("Accel Perf Configuration:\n"); 1202a0c66d0Spaul luse printf("Workload Type: %s\n", g_workload_type); 121b9218b7aSpaul luse if (g_workload_selection == ACCEL_CRC32C) { 122b9218b7aSpaul luse printf("CRC-32C seed: %u\n", g_crc32c_seed); 12389495464Spaul luse } else if (g_workload_selection == ACCEL_FILL) { 12489495464Spaul luse printf("Fill pattern: 0x%x\n", g_fill_pattern); 125b9218b7aSpaul luse } else if ((g_workload_selection == ACCEL_COMPARE) && g_fail_percent_goal > 0) { 12689495464Spaul luse printf("Failure inject: %u percent\n", g_fail_percent_goal); 127e69375bfSpaul luse } 1289f51cf32Spaul luse printf("Transfer size: %u bytes\n", g_xfer_size_bytes); 1299f51cf32Spaul luse printf("Queue depth: %u\n", g_queue_depth); 130445fe74eSpaul luse printf("# threads/core: %u\n", g_threads_per_core); 1319f51cf32Spaul luse printf("Run time: %u seconds\n", g_time_in_sec); 132f17e6705Spaul luse if (g_ops_per_batch > 0) { 133f17e6705Spaul luse printf("Batching: %u operations\n", g_ops_per_batch); 134f17e6705Spaul luse } else { 135f17e6705Spaul luse printf("Batching: Disabled\n"); 136f17e6705Spaul luse } 1379f51cf32Spaul luse printf("Verify: %s\n\n", g_verify ? "Yes" : "No"); 1389f51cf32Spaul luse } 1399f51cf32Spaul luse 1409f51cf32Spaul luse static void 1419f51cf32Spaul luse usage(void) 1429f51cf32Spaul luse { 1439f51cf32Spaul luse printf("accel_perf options:\n"); 1449f51cf32Spaul luse printf("\t[-h help message]\n"); 145f17e6705Spaul luse printf("\t[-q queue depth per core]\n"); 146445fe74eSpaul luse printf("\t[-T number of threads per core\n"); 1479f51cf32Spaul luse printf("\t[-o transfer size in bytes]\n"); 1489f51cf32Spaul luse printf("\t[-t time in seconds]\n"); 1490ef079c6Spaul luse printf("\t[-w workload type must be one of these: copy, fill, crc32c, compare, dualcast\n"); 150e69375bfSpaul luse printf("\t[-s for crc32c workload, use this seed value (default 0)\n"); 151b9218b7aSpaul luse printf("\t[-P for compare workload, percentage of operations that should miscompare (percent, default 0)\n"); 15289495464Spaul luse printf("\t[-f for fill workload, use this BYTE value (default 255)\n"); 1532a0c66d0Spaul luse printf("\t[-y verify result if this switch is on]\n"); 154f17e6705Spaul luse printf("\t[-b batch this number of operations at a time (default 0 = disabled)]\n"); 1559f51cf32Spaul luse } 1569f51cf32Spaul luse 1579f51cf32Spaul luse static int 1589f51cf32Spaul luse parse_args(int argc, char *argv) 1599f51cf32Spaul luse { 1609f51cf32Spaul luse switch (argc) { 161f17e6705Spaul luse case 'b': 162f17e6705Spaul luse g_ops_per_batch = spdk_strtol(optarg, 10); 163f17e6705Spaul luse break; 16489495464Spaul luse case 'f': 16589495464Spaul luse g_fill_pattern = (uint8_t)spdk_strtol(optarg, 10); 16689495464Spaul luse break; 167445fe74eSpaul luse case 'T': 168445fe74eSpaul luse g_threads_per_core = spdk_strtol(optarg, 10); 169445fe74eSpaul luse break; 1709f51cf32Spaul luse case 'o': 1719f51cf32Spaul luse g_xfer_size_bytes = spdk_strtol(optarg, 10); 1729f51cf32Spaul luse break; 173b9218b7aSpaul luse case 'P': 174b9218b7aSpaul luse g_fail_percent_goal = spdk_strtol(optarg, 10); 175b9218b7aSpaul luse break; 1769f51cf32Spaul luse case 'q': 1779f51cf32Spaul luse g_queue_depth = spdk_strtol(optarg, 10); 1789f51cf32Spaul luse break; 179e69375bfSpaul luse case 's': 180e69375bfSpaul luse g_crc32c_seed = spdk_strtol(optarg, 10); 181e69375bfSpaul luse break; 1829f51cf32Spaul luse case 't': 1839f51cf32Spaul luse g_time_in_sec = spdk_strtol(optarg, 10); 1849f51cf32Spaul luse break; 1859f51cf32Spaul luse case 'y': 1869f51cf32Spaul luse g_verify = true; 1879f51cf32Spaul luse break; 1882a0c66d0Spaul luse case 'w': 1892a0c66d0Spaul luse g_workload_type = optarg; 190514be889Spaul luse if (!strcmp(g_workload_type, "copy")) { 191514be889Spaul luse g_workload_selection = ACCEL_COPY; 192514be889Spaul luse } else if (!strcmp(g_workload_type, "fill")) { 193514be889Spaul luse g_workload_selection = ACCEL_FILL; 194e69375bfSpaul luse } else if (!strcmp(g_workload_type, "crc32c")) { 195e69375bfSpaul luse g_workload_selection = ACCEL_CRC32C; 196b9218b7aSpaul luse } else if (!strcmp(g_workload_type, "compare")) { 197b9218b7aSpaul luse g_workload_selection = ACCEL_COMPARE; 1980ef079c6Spaul luse } else if (!strcmp(g_workload_type, "dualcast")) { 1990ef079c6Spaul luse g_workload_selection = ACCEL_DUALCAST; 200514be889Spaul luse } 2012a0c66d0Spaul luse break; 2029f51cf32Spaul luse default: 2039f51cf32Spaul luse usage(); 2049f51cf32Spaul luse return 1; 2059f51cf32Spaul luse } 2069f51cf32Spaul luse return 0; 2079f51cf32Spaul luse } 2089f51cf32Spaul luse 209eea826a2Spaul luse static int dump_result(void); 2109f51cf32Spaul luse static void 2119f51cf32Spaul luse unregister_worker(void *arg1) 2129f51cf32Spaul luse { 2139f51cf32Spaul luse struct worker_thread *worker = arg1; 2149f51cf32Spaul luse 215ac9a1a83Spaul luse free(worker->task_base); 216f17e6705Spaul luse free(worker->batch_base); 2179f51cf32Spaul luse spdk_put_io_channel(worker->ch); 2189f51cf32Spaul luse pthread_mutex_lock(&g_workers_lock); 2199f51cf32Spaul luse assert(g_num_workers >= 1); 2209f51cf32Spaul luse if (--g_num_workers == 0) { 2219f51cf32Spaul luse pthread_mutex_unlock(&g_workers_lock); 222*9b189667Spaul luse g_rc = dump_result(); 2239f51cf32Spaul luse spdk_app_stop(0); 2249f51cf32Spaul luse } 2259f51cf32Spaul luse pthread_mutex_unlock(&g_workers_lock); 2269f51cf32Spaul luse } 2279f51cf32Spaul luse 2288da995c4Spaul luse static int 2298da995c4Spaul luse _get_task_data_bufs(struct ap_task *task) 2308da995c4Spaul luse { 2318da995c4Spaul luse uint32_t align = 0; 2328da995c4Spaul luse 2338da995c4Spaul luse /* For dualcast, the DSA HW requires 4K alignment on destination addresses but 2348da995c4Spaul luse * we do this for all engines to keep it simple. 2358da995c4Spaul luse */ 2368da995c4Spaul luse if (g_workload_selection == ACCEL_DUALCAST) { 2378da995c4Spaul luse align = ALIGN_4K; 2388da995c4Spaul luse } 2398da995c4Spaul luse 2408da995c4Spaul luse task->src = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL); 2418da995c4Spaul luse if (task->src == NULL) { 2428da995c4Spaul luse fprintf(stderr, "Unable to alloc src buffer\n"); 2438da995c4Spaul luse return -ENOMEM; 2448da995c4Spaul luse } 2458da995c4Spaul luse memset(task->src, DATA_PATTERN, g_xfer_size_bytes); 2468da995c4Spaul luse 2478da995c4Spaul luse task->dst = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL); 2488da995c4Spaul luse if (task->dst == NULL) { 2498da995c4Spaul luse fprintf(stderr, "Unable to alloc dst buffer\n"); 2508da995c4Spaul luse return -ENOMEM; 2518da995c4Spaul luse } 2528da995c4Spaul luse 2538da995c4Spaul luse /* For compare we want the buffers to match, otherwise not. */ 2548da995c4Spaul luse if (g_workload_selection == ACCEL_COMPARE) { 2558da995c4Spaul luse memset(task->dst, DATA_PATTERN, g_xfer_size_bytes); 2568da995c4Spaul luse } else { 2578da995c4Spaul luse memset(task->dst, ~DATA_PATTERN, g_xfer_size_bytes); 2588da995c4Spaul luse } 2598da995c4Spaul luse 2608da995c4Spaul luse /* For fill, set the entire src buffer so we can check if verify is enabled. */ 2618da995c4Spaul luse if (g_workload_selection == ACCEL_FILL) { 2628da995c4Spaul luse memset(task->src, g_fill_pattern, g_xfer_size_bytes); 2638da995c4Spaul luse } 2648da995c4Spaul luse 2658da995c4Spaul luse if (g_workload_selection == ACCEL_DUALCAST) { 2668da995c4Spaul luse task->dst2 = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL); 2678da995c4Spaul luse if (task->dst2 == NULL) { 2688da995c4Spaul luse fprintf(stderr, "Unable to alloc dst buffer\n"); 2698da995c4Spaul luse return -ENOMEM; 2708da995c4Spaul luse } 2718da995c4Spaul luse memset(task->dst2, ~DATA_PATTERN, g_xfer_size_bytes); 2728da995c4Spaul luse } 2738da995c4Spaul luse 2748da995c4Spaul luse return 0; 2758da995c4Spaul luse } 2768da995c4Spaul luse 277ac9a1a83Spaul luse inline static struct ap_task * 278ac9a1a83Spaul luse _get_task(struct worker_thread *worker) 279ac9a1a83Spaul luse { 280ac9a1a83Spaul luse struct ap_task *task; 281ac9a1a83Spaul luse 282ac9a1a83Spaul luse if (!TAILQ_EMPTY(&worker->tasks_pool)) { 283ac9a1a83Spaul luse task = TAILQ_FIRST(&worker->tasks_pool); 284ac9a1a83Spaul luse TAILQ_REMOVE(&worker->tasks_pool, task, link); 285ac9a1a83Spaul luse } else { 286ac9a1a83Spaul luse fprintf(stderr, "Unable to get ap_task\n"); 287ac9a1a83Spaul luse return NULL; 288ac9a1a83Spaul luse } 289ac9a1a83Spaul luse 290ac9a1a83Spaul luse task->worker = worker; 291ac9a1a83Spaul luse task->worker->current_queue_depth++; 292ac9a1a83Spaul luse return task; 293ac9a1a83Spaul luse } 294ac9a1a83Spaul luse 295f17e6705Spaul luse /* Submit one operation using the same ap task that just completed. */ 2969f51cf32Spaul luse static void 297ac9a1a83Spaul luse _submit_single(struct worker_thread *worker, struct ap_task *task) 2989f51cf32Spaul luse { 299b9218b7aSpaul luse int random_num; 30040ec8e97Spaul luse int rc = 0; 3019f51cf32Spaul luse 3029f51cf32Spaul luse assert(worker); 3039f51cf32Spaul luse 304e69375bfSpaul luse switch (g_workload_selection) { 305e69375bfSpaul luse case ACCEL_COPY: 306e8463f87Spaul luse rc = spdk_accel_submit_copy(worker->ch, task->dst, task->src, 307e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 308e69375bfSpaul luse break; 309e69375bfSpaul luse case ACCEL_FILL: 3102a0c66d0Spaul luse /* For fill use the first byte of the task->dst buffer */ 311ee7e31f9Spaul luse rc = spdk_accel_submit_fill(worker->ch, task->dst, *(uint8_t *)task->src, 312e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 313e69375bfSpaul luse break; 314e69375bfSpaul luse case ACCEL_CRC32C: 315e8463f87Spaul luse rc = spdk_accel_submit_crc32c(worker->ch, (uint32_t *)task->dst, 316e8463f87Spaul luse task->src, g_crc32c_seed, 317e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 318e69375bfSpaul luse break; 319b9218b7aSpaul luse case ACCEL_COMPARE: 320b9218b7aSpaul luse random_num = rand() % 100; 321b9218b7aSpaul luse if (random_num < g_fail_percent_goal) { 322b9218b7aSpaul luse task->expected_status = -EILSEQ; 323b9218b7aSpaul luse *(uint8_t *)task->dst = ~DATA_PATTERN; 324b9218b7aSpaul luse } else { 325b9218b7aSpaul luse task->expected_status = 0; 326b9218b7aSpaul luse *(uint8_t *)task->dst = DATA_PATTERN; 327b9218b7aSpaul luse } 328ee7e31f9Spaul luse rc = spdk_accel_submit_compare(worker->ch, task->dst, task->src, 329e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 330b9218b7aSpaul luse break; 3310ef079c6Spaul luse case ACCEL_DUALCAST: 332ee7e31f9Spaul luse rc = spdk_accel_submit_dualcast(worker->ch, task->dst, task->dst2, 333e8463f87Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 3340ef079c6Spaul luse break; 335e69375bfSpaul luse default: 3362a0c66d0Spaul luse assert(false); 337e69375bfSpaul luse break; 338e69375bfSpaul luse 3392a0c66d0Spaul luse } 34040ec8e97Spaul luse 34140ec8e97Spaul luse if (rc) { 342e8463f87Spaul luse accel_done(task, rc); 34340ec8e97Spaul luse } 3449f51cf32Spaul luse } 3459f51cf32Spaul luse 346fab40895Spaul luse static int 347f17e6705Spaul luse _batch_prep_cmd(struct worker_thread *worker, struct ap_task *task, 348f17e6705Spaul luse struct accel_batch *worker_batch) 349fab40895Spaul luse { 350f17e6705Spaul luse struct spdk_accel_batch *batch = worker_batch->batch; 351fab40895Spaul luse int rc = 0; 352fab40895Spaul luse 353f17e6705Spaul luse worker_batch->cmd_count++; 354f17e6705Spaul luse assert(worker_batch->cmd_count <= g_ops_per_batch); 355f17e6705Spaul luse 356fab40895Spaul luse switch (g_workload_selection) { 357fab40895Spaul luse case ACCEL_COPY: 358fab40895Spaul luse rc = spdk_accel_batch_prep_copy(worker->ch, batch, task->dst, 359fab40895Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 360fab40895Spaul luse break; 361fab40895Spaul luse case ACCEL_DUALCAST: 362fab40895Spaul luse rc = spdk_accel_batch_prep_dualcast(worker->ch, batch, task->dst, task->dst2, 363fab40895Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 364fab40895Spaul luse break; 365fab40895Spaul luse case ACCEL_COMPARE: 366fab40895Spaul luse rc = spdk_accel_batch_prep_compare(worker->ch, batch, task->dst, task->src, 367fab40895Spaul luse g_xfer_size_bytes, accel_done, task); 368fab40895Spaul luse break; 369fab40895Spaul luse case ACCEL_FILL: 370fab40895Spaul luse rc = spdk_accel_batch_prep_fill(worker->ch, batch, task->dst, 371fab40895Spaul luse *(uint8_t *)task->src, 372fab40895Spaul luse g_xfer_size_bytes, accel_done, task); 373fab40895Spaul luse break; 374fab40895Spaul luse case ACCEL_CRC32C: 375fab40895Spaul luse rc = spdk_accel_batch_prep_crc32c(worker->ch, batch, (uint32_t *)task->dst, 376fab40895Spaul luse task->src, g_crc32c_seed, g_xfer_size_bytes, accel_done, task); 377fab40895Spaul luse break; 378fab40895Spaul luse default: 379fab40895Spaul luse assert(false); 380fab40895Spaul luse break; 381fab40895Spaul luse } 382fab40895Spaul luse 383fab40895Spaul luse return rc; 384fab40895Spaul luse } 385fab40895Spaul luse 3869f51cf32Spaul luse static void 387e150f6b8SZiye Yang _free_task_buffers(struct ap_task *task) 388ac9a1a83Spaul luse { 389ac9a1a83Spaul luse spdk_dma_free(task->src); 390ac9a1a83Spaul luse spdk_dma_free(task->dst); 391ac9a1a83Spaul luse if (g_workload_selection == ACCEL_DUALCAST) { 392ac9a1a83Spaul luse spdk_dma_free(task->dst2); 393ac9a1a83Spaul luse } 394ac9a1a83Spaul luse } 395ac9a1a83Spaul luse 396f17e6705Spaul luse static void _batch_done(void *cb_arg); 397f17e6705Spaul luse static void 398f17e6705Spaul luse _build_batch(struct worker_thread *worker, struct ap_task *task) 399f17e6705Spaul luse { 400f17e6705Spaul luse struct accel_batch *worker_batch = NULL; 401f17e6705Spaul luse int rc; 402f17e6705Spaul luse 403f17e6705Spaul luse assert(!TAILQ_EMPTY(&worker->in_prep_batches)); 404f17e6705Spaul luse 405f17e6705Spaul luse worker_batch = TAILQ_FIRST(&worker->in_prep_batches); 406f17e6705Spaul luse 407f17e6705Spaul luse /* If an accel batch hasn't been created yet do so now. */ 408f17e6705Spaul luse if (worker_batch->batch == NULL) { 409f17e6705Spaul luse worker_batch->batch = spdk_accel_batch_create(worker->ch); 410f17e6705Spaul luse if (worker_batch->batch == NULL) { 411f17e6705Spaul luse fprintf(stderr, "error unable to create new batch\n"); 412f17e6705Spaul luse return; 413f17e6705Spaul luse } 414f17e6705Spaul luse } 415f17e6705Spaul luse 416f17e6705Spaul luse /* Prep the command re-using the last completed command's task */ 417f17e6705Spaul luse rc = _batch_prep_cmd(worker, task, worker_batch); 418f17e6705Spaul luse if (rc) { 419f17e6705Spaul luse fprintf(stderr, "error preping command for batch\n"); 420f17e6705Spaul luse goto error; 421f17e6705Spaul luse } 422f17e6705Spaul luse 423f17e6705Spaul luse /* If this batch is full move it to the to_submit list so it gets 424f17e6705Spaul luse * submitted as batches complete. 425f17e6705Spaul luse */ 426f17e6705Spaul luse if (worker_batch->cmd_count == g_ops_per_batch) { 427f17e6705Spaul luse TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link); 428f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->to_submit_batches, worker_batch, link); 429f17e6705Spaul luse } 430f17e6705Spaul luse 431f17e6705Spaul luse return; 432f17e6705Spaul luse error: 433f17e6705Spaul luse spdk_accel_batch_cancel(worker->ch, worker_batch->batch); 434f17e6705Spaul luse 435f17e6705Spaul luse } 436f17e6705Spaul luse 437f17e6705Spaul luse static void batch_done(void *cb_arg, int status); 438f17e6705Spaul luse static void 439f17e6705Spaul luse _drain_batch(struct worker_thread *worker) 440f17e6705Spaul luse { 441f17e6705Spaul luse struct accel_batch *worker_batch, *tmp; 442f17e6705Spaul luse int rc; 443f17e6705Spaul luse 444f17e6705Spaul luse /* submit any batches that were being built up. */ 445f17e6705Spaul luse TAILQ_FOREACH_SAFE(worker_batch, &worker->in_prep_batches, link, tmp) { 446f17e6705Spaul luse if (worker_batch->cmd_count == 0) { 447f17e6705Spaul luse continue; 448f17e6705Spaul luse } 449f17e6705Spaul luse worker->current_queue_depth += worker_batch->cmd_count + 1; 450f17e6705Spaul luse 451f17e6705Spaul luse TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link); 452f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link); 453f17e6705Spaul luse rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch); 454f17e6705Spaul luse if (rc == 0) { 455f17e6705Spaul luse worker_batch->cmd_count = 0; 456f17e6705Spaul luse } else { 457f17e6705Spaul luse fprintf(stderr, "error sending final batch\n"); 458f17e6705Spaul luse worker->current_queue_depth -= worker_batch->cmd_count + 1; 459f17e6705Spaul luse break; 460f17e6705Spaul luse } 461f17e6705Spaul luse } 462f17e6705Spaul luse } 463f17e6705Spaul luse 464f17e6705Spaul luse static void 465f17e6705Spaul luse _batch_done(void *cb_arg) 466f17e6705Spaul luse { 467f17e6705Spaul luse struct accel_batch *worker_batch = (struct accel_batch *)cb_arg; 468f17e6705Spaul luse struct worker_thread *worker = worker_batch->worker; 469f17e6705Spaul luse int rc; 470f17e6705Spaul luse 471f17e6705Spaul luse assert(TAILQ_EMPTY(&worker->in_use_batches) == 0); 472f17e6705Spaul luse 473f17e6705Spaul luse if (worker_batch->status) { 474f17e6705Spaul luse SPDK_ERRLOG("error %d\n", worker_batch->status); 475f17e6705Spaul luse } 476f17e6705Spaul luse 477f17e6705Spaul luse worker->current_queue_depth--; 478f17e6705Spaul luse TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link); 479f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link); 480f17e6705Spaul luse worker_batch->batch = NULL; 481f17e6705Spaul luse worker_batch->cmd_count = 0; 482f17e6705Spaul luse 483f17e6705Spaul luse if (!worker->is_draining) { 484f17e6705Spaul luse worker_batch = TAILQ_FIRST(&worker->to_submit_batches); 485f17e6705Spaul luse if (worker_batch != NULL) { 486f17e6705Spaul luse 487f17e6705Spaul luse assert(worker_batch->cmd_count == g_ops_per_batch); 488f17e6705Spaul luse 489f17e6705Spaul luse /* Add one for the batch command itself. */ 490f17e6705Spaul luse worker->current_queue_depth += g_ops_per_batch + 1; 491f17e6705Spaul luse TAILQ_REMOVE(&worker->to_submit_batches, worker_batch, link); 492f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link); 493f17e6705Spaul luse 494f17e6705Spaul luse rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch); 495f17e6705Spaul luse if (rc) { 496f17e6705Spaul luse fprintf(stderr, "error ending batch\n"); 497f17e6705Spaul luse worker->current_queue_depth -= g_ops_per_batch + 1; 498f17e6705Spaul luse return; 499f17e6705Spaul luse } 500f17e6705Spaul luse } 501f17e6705Spaul luse } else { 502f17e6705Spaul luse _drain_batch(worker); 503f17e6705Spaul luse } 504f17e6705Spaul luse } 505f17e6705Spaul luse 506ac9a1a83Spaul luse static void 507fab40895Spaul luse batch_done(void *cb_arg, int status) 508fab40895Spaul luse { 509f17e6705Spaul luse struct accel_batch *worker_batch = (struct accel_batch *)cb_arg; 510fab40895Spaul luse 511f17e6705Spaul luse assert(worker_batch->worker); 512f17e6705Spaul luse 513f17e6705Spaul luse worker_batch->status = status; 514f17e6705Spaul luse spdk_thread_send_msg(worker_batch->worker->thread, _batch_done, worker_batch); 515fab40895Spaul luse } 516fab40895Spaul luse 517fab40895Spaul luse static void 5189f51cf32Spaul luse _accel_done(void *arg1) 5199f51cf32Spaul luse { 5209f51cf32Spaul luse struct ap_task *task = arg1; 5219f51cf32Spaul luse struct worker_thread *worker = task->worker; 522e69375bfSpaul luse uint32_t sw_crc32c; 5239f51cf32Spaul luse 5249f51cf32Spaul luse assert(worker); 5259f51cf32Spaul luse assert(worker->current_queue_depth > 0); 5269f51cf32Spaul luse 527b9218b7aSpaul luse if (g_verify && task->status == 0) { 528b9218b7aSpaul luse switch (g_workload_selection) { 529b9218b7aSpaul luse case ACCEL_CRC32C: 530e69375bfSpaul luse /* calculate sw CRC-32C and compare to sw aceel result. */ 531e69375bfSpaul luse sw_crc32c = spdk_crc32c_update(task->src, g_xfer_size_bytes, ~g_crc32c_seed); 532e69375bfSpaul luse if (*(uint32_t *)task->dst != sw_crc32c) { 533e69375bfSpaul luse SPDK_NOTICELOG("CRC-32C miscompare\n"); 534e69375bfSpaul luse worker->xfer_failed++; 535e69375bfSpaul luse } 536b9218b7aSpaul luse break; 537b9218b7aSpaul luse case ACCEL_COPY: 538b9218b7aSpaul luse if (memcmp(task->src, task->dst, g_xfer_size_bytes)) { 5399f51cf32Spaul luse SPDK_NOTICELOG("Data miscompare\n"); 5409f51cf32Spaul luse worker->xfer_failed++; 541b9218b7aSpaul luse } 542b9218b7aSpaul luse break; 5430ef079c6Spaul luse case ACCEL_DUALCAST: 5440ef079c6Spaul luse if (memcmp(task->src, task->dst, g_xfer_size_bytes)) { 5450ef079c6Spaul luse SPDK_NOTICELOG("Data miscompare, first destination\n"); 5460ef079c6Spaul luse worker->xfer_failed++; 5470ef079c6Spaul luse } 5480ef079c6Spaul luse if (memcmp(task->src, task->dst2, g_xfer_size_bytes)) { 5490ef079c6Spaul luse SPDK_NOTICELOG("Data miscompare, second destination\n"); 5500ef079c6Spaul luse worker->xfer_failed++; 5510ef079c6Spaul luse } 5520ef079c6Spaul luse break; 553d207237fSpaul luse case ACCEL_FILL: 554d207237fSpaul luse if (memcmp(task->dst, task->src, g_xfer_size_bytes)) { 555d207237fSpaul luse SPDK_NOTICELOG("Data miscompare\n"); 556d207237fSpaul luse worker->xfer_failed++; 557d207237fSpaul luse } 558d207237fSpaul luse break; 5598cee297cSpaul luse case ACCEL_COMPARE: 5608cee297cSpaul luse break; 561b9218b7aSpaul luse default: 562b9218b7aSpaul luse assert(false); 563b9218b7aSpaul luse break; 5649f51cf32Spaul luse } 5659f51cf32Spaul luse } 566b9218b7aSpaul luse 567b9218b7aSpaul luse if (task->expected_status == -EILSEQ) { 568b9218b7aSpaul luse assert(task->status != 0); 569b9218b7aSpaul luse worker->injected_miscompares++; 570b9218b7aSpaul luse } else if (task->status) { 571f17e6705Spaul luse /* Expected to pass but the accel engine reported an error (ex: COMPARE operation). */ 572b9218b7aSpaul luse worker->xfer_failed++; 573b9218b7aSpaul luse } 574b9218b7aSpaul luse 5759f51cf32Spaul luse worker->xfer_completed++; 5769f51cf32Spaul luse worker->current_queue_depth--; 5779f51cf32Spaul luse 57840ec8e97Spaul luse if (!worker->is_draining) { 579f17e6705Spaul luse if (g_ops_per_batch == 0) { 5809f51cf32Spaul luse _submit_single(worker, task); 581ac9a1a83Spaul luse worker->current_queue_depth++; 582f17e6705Spaul luse } else { 583f17e6705Spaul luse _build_batch(worker, task); 5849f51cf32Spaul luse } 585f17e6705Spaul luse } else if (g_ops_per_batch > 0) { 586f17e6705Spaul luse _drain_batch(worker); 587b34883e0SZiye Yang } else { 588b34883e0SZiye Yang TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link); 589f17e6705Spaul luse } 5909f51cf32Spaul luse } 5919f51cf32Spaul luse 5929f51cf32Spaul luse static int 5939f51cf32Spaul luse dump_result(void) 5949f51cf32Spaul luse { 5959f51cf32Spaul luse uint64_t total_completed = 0; 5969f51cf32Spaul luse uint64_t total_failed = 0; 597b9218b7aSpaul luse uint64_t total_miscompared = 0; 5989f51cf32Spaul luse uint64_t total_xfer_per_sec, total_bw_in_MiBps; 5999f51cf32Spaul luse struct worker_thread *worker = g_workers; 6009f51cf32Spaul luse 601445fe74eSpaul luse printf("\nCore,Thread Transfers Bandwidth Failed Miscompares\n"); 602445fe74eSpaul luse printf("------------------------------------------------------------------------\n"); 6039f51cf32Spaul luse while (worker != NULL) { 6049f51cf32Spaul luse 6059f51cf32Spaul luse uint64_t xfer_per_sec = worker->xfer_completed / g_time_in_sec; 6069f51cf32Spaul luse uint64_t bw_in_MiBps = (worker->xfer_completed * g_xfer_size_bytes) / 6079f51cf32Spaul luse (g_time_in_sec * 1024 * 1024); 6089f51cf32Spaul luse 6099f51cf32Spaul luse total_completed += worker->xfer_completed; 6109f51cf32Spaul luse total_failed += worker->xfer_failed; 611b9218b7aSpaul luse total_miscompared += worker->injected_miscompares; 6129f51cf32Spaul luse 6139f51cf32Spaul luse if (xfer_per_sec) { 614445fe74eSpaul luse printf("%u,%u%17" PRIu64 "/s%9" PRIu64 " MiB/s%7" PRIu64 " %11" PRIu64 "\n", 615445fe74eSpaul luse worker->display.core, worker->display.thread, xfer_per_sec, 616b9218b7aSpaul luse bw_in_MiBps, worker->xfer_failed, worker->injected_miscompares); 6179f51cf32Spaul luse } 6189f51cf32Spaul luse 6199f51cf32Spaul luse worker = worker->next; 6209f51cf32Spaul luse } 6219f51cf32Spaul luse 6229f51cf32Spaul luse total_xfer_per_sec = total_completed / g_time_in_sec; 6239f51cf32Spaul luse total_bw_in_MiBps = (total_completed * g_xfer_size_bytes) / 6249f51cf32Spaul luse (g_time_in_sec * 1024 * 1024); 6259f51cf32Spaul luse 626445fe74eSpaul luse printf("=========================================================================\n"); 627445fe74eSpaul luse printf("Total:%15" PRIu64 "/s%9" PRIu64 " MiB/s%6" PRIu64 " %11" PRIu64"\n\n", 628b9218b7aSpaul luse total_xfer_per_sec, total_bw_in_MiBps, total_failed, total_miscompared); 6299f51cf32Spaul luse 6309f51cf32Spaul luse return total_failed ? 1 : 0; 6319f51cf32Spaul luse } 6329f51cf32Spaul luse 633e150f6b8SZiye Yang static inline void 634e150f6b8SZiye Yang _free_task_buffers_in_pool(struct worker_thread *worker) 635e150f6b8SZiye Yang { 636e150f6b8SZiye Yang struct ap_task *task; 637e150f6b8SZiye Yang 638e150f6b8SZiye Yang assert(worker); 639e150f6b8SZiye Yang while ((task = TAILQ_FIRST(&worker->tasks_pool))) { 640e150f6b8SZiye Yang TAILQ_REMOVE(&worker->tasks_pool, task, link); 641e150f6b8SZiye Yang _free_task_buffers(task); 642e150f6b8SZiye Yang } 643e150f6b8SZiye Yang } 644e150f6b8SZiye Yang 6459f51cf32Spaul luse static int 6469f51cf32Spaul luse _check_draining(void *arg) 6479f51cf32Spaul luse { 6489f51cf32Spaul luse struct worker_thread *worker = arg; 6499f51cf32Spaul luse 6509f51cf32Spaul luse assert(worker); 6519f51cf32Spaul luse 6529f51cf32Spaul luse if (worker->current_queue_depth == 0) { 653e150f6b8SZiye Yang _free_task_buffers_in_pool(worker); 6549f51cf32Spaul luse spdk_poller_unregister(&worker->is_draining_poller); 6559f51cf32Spaul luse unregister_worker(worker); 6569f51cf32Spaul luse } 6579f51cf32Spaul luse 6589f51cf32Spaul luse return -1; 6599f51cf32Spaul luse } 6609f51cf32Spaul luse 6619f51cf32Spaul luse static int 6629f51cf32Spaul luse _worker_stop(void *arg) 6639f51cf32Spaul luse { 6649f51cf32Spaul luse struct worker_thread *worker = arg; 6659f51cf32Spaul luse 6669f51cf32Spaul luse assert(worker); 6679f51cf32Spaul luse 6689f51cf32Spaul luse spdk_poller_unregister(&worker->stop_poller); 6699f51cf32Spaul luse 6709f51cf32Spaul luse /* now let the worker drain and check it's outstanding IO with a poller */ 6719f51cf32Spaul luse worker->is_draining = true; 672ab0bc5c2SShuhei Matsumoto worker->is_draining_poller = SPDK_POLLER_REGISTER(_check_draining, worker, 0); 6739f51cf32Spaul luse 6749f51cf32Spaul luse return 0; 6759f51cf32Spaul luse } 6769f51cf32Spaul luse 6779f51cf32Spaul luse static void 678a34fc12bSpaul luse _init_thread(void *arg1) 679a34fc12bSpaul luse { 680a34fc12bSpaul luse struct worker_thread *worker; 681a34fc12bSpaul luse struct ap_task *task; 682f17e6705Spaul luse int i, rc, num_batches; 683f17e6705Spaul luse int max_per_batch; 684a34fc12bSpaul luse int remaining = g_queue_depth; 685f17e6705Spaul luse int num_tasks = g_queue_depth; 686f17e6705Spaul luse struct accel_batch *tmp; 687f17e6705Spaul luse struct accel_batch *worker_batch = NULL; 688445fe74eSpaul luse struct display_info *display = arg1; 689a34fc12bSpaul luse 690a34fc12bSpaul luse worker = calloc(1, sizeof(*worker)); 691a34fc12bSpaul luse if (worker == NULL) { 692a34fc12bSpaul luse fprintf(stderr, "Unable to allocate worker\n"); 693445fe74eSpaul luse free(display); 694a34fc12bSpaul luse return; 695a34fc12bSpaul luse } 696a34fc12bSpaul luse 697445fe74eSpaul luse worker->display.core = display->core; 698445fe74eSpaul luse worker->display.thread = display->thread; 699445fe74eSpaul luse free(display); 7009f51cf32Spaul luse worker->core = spdk_env_get_current_core(); 7019f51cf32Spaul luse worker->thread = spdk_get_thread(); 702eea826a2Spaul luse pthread_mutex_lock(&g_workers_lock); 703eea826a2Spaul luse g_num_workers++; 7049f51cf32Spaul luse worker->next = g_workers; 705eea826a2Spaul luse g_workers = worker; 706eea826a2Spaul luse pthread_mutex_unlock(&g_workers_lock); 7079f51cf32Spaul luse worker->ch = spdk_accel_engine_get_io_channel(); 708b9218b7aSpaul luse 709f17e6705Spaul luse TAILQ_INIT(&worker->tasks_pool); 710f17e6705Spaul luse 711f17e6705Spaul luse if (g_ops_per_batch > 0) { 712f17e6705Spaul luse 7130cecfcb1Spaul luse max_per_batch = spdk_accel_batch_get_max(worker->ch); 7140cecfcb1Spaul luse assert(max_per_batch > 0); 7150cecfcb1Spaul luse 716f17e6705Spaul luse if (g_ops_per_batch > max_per_batch) { 717f17e6705Spaul luse fprintf(stderr, "Reducing requested batch amount to max supported of %d\n", max_per_batch); 718f17e6705Spaul luse g_ops_per_batch = max_per_batch; 719f17e6705Spaul luse } 720f17e6705Spaul luse 721f17e6705Spaul luse if (g_ops_per_batch > g_queue_depth) { 722f17e6705Spaul luse fprintf(stderr, "Batch amount > queue depth, resetting to %d\n", g_queue_depth); 723f17e6705Spaul luse g_ops_per_batch = g_queue_depth; 724f17e6705Spaul luse } 725f17e6705Spaul luse 726f17e6705Spaul luse TAILQ_INIT(&worker->in_prep_batches); 727f17e6705Spaul luse TAILQ_INIT(&worker->to_submit_batches); 728f17e6705Spaul luse TAILQ_INIT(&worker->in_use_batches); 729f17e6705Spaul luse 730f17e6705Spaul luse /* A worker_batch will live on one of 3 lists: 731f17e6705Spaul luse * IN_PREP: as individual IOs complete new ones are built on on a 732f17e6705Spaul luse * worker_batch on this list until it reaches g_ops_per_batch. 733f17e6705Spaul luse * TO_SUBMIT: as batches are built up on IO completion they are moved 734f17e6705Spaul luse * to this list once they are full. This list is used in 735f17e6705Spaul luse * batch completion to start new batches. 736f17e6705Spaul luse * IN_USE: the worker_batch is outstanding and will be moved to in prep 737f17e6705Spaul luse * list when the batch is completed. 738f17e6705Spaul luse * 739f17e6705Spaul luse * So we need enough to cover Q depth loading and then one to replace 740f17e6705Spaul luse * each one of those and for when everything is outstanding there needs 741f17e6705Spaul luse * to be one extra batch to build up while the last batch is completing 742f17e6705Spaul luse * IO but before it's completed the batch command. 743f17e6705Spaul luse */ 744f17e6705Spaul luse num_batches = (g_queue_depth / g_ops_per_batch * 2) + 1; 745f17e6705Spaul luse worker->batch_base = calloc(num_batches, sizeof(struct accel_batch)); 746f17e6705Spaul luse worker_batch = worker->batch_base; 747f17e6705Spaul luse for (i = 0; i < num_batches; i++) { 748f17e6705Spaul luse worker_batch->worker = worker; 749f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link); 750f17e6705Spaul luse worker_batch++; 751f17e6705Spaul luse } 752f17e6705Spaul luse } 753f17e6705Spaul luse 754ac9a1a83Spaul luse worker->task_base = calloc(num_tasks, sizeof(struct ap_task)); 755ac9a1a83Spaul luse if (worker->task_base == NULL) { 756ac9a1a83Spaul luse fprintf(stderr, "Could not allocate task base.\n"); 757ac9a1a83Spaul luse goto error; 7580cecfcb1Spaul luse } 759ac9a1a83Spaul luse 760ac9a1a83Spaul luse task = worker->task_base; 761ac9a1a83Spaul luse for (i = 0; i < num_tasks; i++) { 762ac9a1a83Spaul luse TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link); 763ac9a1a83Spaul luse if (_get_task_data_bufs(task)) { 764ac9a1a83Spaul luse fprintf(stderr, "Unable to get data bufs\n"); 765ac9a1a83Spaul luse goto error; 766ac9a1a83Spaul luse } 767ac9a1a83Spaul luse task++; 7689f51cf32Spaul luse } 7699f51cf32Spaul luse 7709f51cf32Spaul luse /* Register a poller that will stop the worker at time elapsed */ 771ab0bc5c2SShuhei Matsumoto worker->stop_poller = SPDK_POLLER_REGISTER(_worker_stop, worker, 7729f51cf32Spaul luse g_time_in_sec * 1000000ULL); 7739f51cf32Spaul luse 774f17e6705Spaul luse /* If batching is enabled load up to the full Q depth before 775f17e6705Spaul luse * processing any completions, then ping pong between two batches, 776f17e6705Spaul luse * one processing and one being built up for when the other completes. 777a34fc12bSpaul luse */ 778f17e6705Spaul luse if (g_ops_per_batch > 0) { 779a34fc12bSpaul luse do { 780f17e6705Spaul luse worker_batch = TAILQ_FIRST(&worker->in_prep_batches); 781f17e6705Spaul luse if (worker_batch == NULL) { 782f17e6705Spaul luse goto error; 783f17e6705Spaul luse } 784f17e6705Spaul luse 785f17e6705Spaul luse worker_batch->batch = spdk_accel_batch_create(worker->ch); 786f17e6705Spaul luse if (worker_batch->batch == NULL) { 787f17e6705Spaul luse raise(SIGINT); 788a34fc12bSpaul luse break; 789a34fc12bSpaul luse } 790a34fc12bSpaul luse 791f17e6705Spaul luse for (i = 0; i < g_ops_per_batch; i++) { 792ac9a1a83Spaul luse task = _get_task(worker); 793ac9a1a83Spaul luse if (task == NULL) { 794a34fc12bSpaul luse goto error; 7959f51cf32Spaul luse } 796b9218b7aSpaul luse 797f17e6705Spaul luse rc = _batch_prep_cmd(worker, task, worker_batch); 798a34fc12bSpaul luse if (rc) { 799a34fc12bSpaul luse fprintf(stderr, "error preping command\n"); 800a34fc12bSpaul luse goto error; 801a34fc12bSpaul luse } 802a34fc12bSpaul luse } 803a34fc12bSpaul luse 804f17e6705Spaul luse /* for the batch operation itself. */ 805f17e6705Spaul luse task->worker->current_queue_depth++; 806f17e6705Spaul luse TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link); 807f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link); 808f17e6705Spaul luse 809f17e6705Spaul luse rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch); 810a34fc12bSpaul luse if (rc) { 811f17e6705Spaul luse fprintf(stderr, "error ending batch\n"); 812a34fc12bSpaul luse goto error; 813a34fc12bSpaul luse } 814f17e6705Spaul luse assert(remaining >= g_ops_per_batch); 815f17e6705Spaul luse remaining -= g_ops_per_batch; 816f17e6705Spaul luse } while (remaining > 0); 817b9218b7aSpaul luse } 8180ef079c6Spaul luse 819f17e6705Spaul luse /* Submit as singles when no batching is enabled or we ran out of batches. */ 820a34fc12bSpaul luse for (i = 0; i < remaining; i++) { 821ac9a1a83Spaul luse task = _get_task(worker); 822ac9a1a83Spaul luse if (task == NULL) { 823a34fc12bSpaul luse goto error; 824b9218b7aSpaul luse } 825b9218b7aSpaul luse 8269f51cf32Spaul luse _submit_single(worker, task); 8279f51cf32Spaul luse } 828a34fc12bSpaul luse return; 829a34fc12bSpaul luse error: 830f17e6705Spaul luse if (worker_batch && worker_batch->batch) { 831f17e6705Spaul luse TAILQ_FOREACH_SAFE(worker_batch, &worker->in_use_batches, link, tmp) { 832f17e6705Spaul luse spdk_accel_batch_cancel(worker->ch, worker_batch->batch); 833f17e6705Spaul luse TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link); 834f17e6705Spaul luse } 835f17e6705Spaul luse } 836e150f6b8SZiye Yang 837e150f6b8SZiye Yang _free_task_buffers_in_pool(worker); 838f17e6705Spaul luse free(worker->batch_base); 839ac9a1a83Spaul luse free(worker->task_base); 840a34fc12bSpaul luse free(worker); 841a34fc12bSpaul luse spdk_app_stop(-1); 8429f51cf32Spaul luse } 8439f51cf32Spaul luse 8449f51cf32Spaul luse static void 845e8463f87Spaul luse accel_done(void *cb_arg, int status) 8469f51cf32Spaul luse { 847e8463f87Spaul luse struct ap_task *task = (struct ap_task *)cb_arg; 8489f51cf32Spaul luse struct worker_thread *worker = task->worker; 8499f51cf32Spaul luse 8509f51cf32Spaul luse assert(worker); 8519f51cf32Spaul luse 852b9218b7aSpaul luse task->status = status; 8539f51cf32Spaul luse spdk_thread_send_msg(worker->thread, _accel_done, task); 8549f51cf32Spaul luse } 8559f51cf32Spaul luse 8569f51cf32Spaul luse static void 8579f51cf32Spaul luse accel_perf_start(void *arg1) 8589f51cf32Spaul luse { 859514be889Spaul luse struct spdk_io_channel *accel_ch; 860eea826a2Spaul luse struct spdk_cpuset tmp_cpumask = {}; 861eea826a2Spaul luse char thread_name[32]; 862eea826a2Spaul luse uint32_t i; 863445fe74eSpaul luse int j; 864eea826a2Spaul luse struct spdk_thread *thread; 865445fe74eSpaul luse struct display_info *display; 866514be889Spaul luse 867514be889Spaul luse accel_ch = spdk_accel_engine_get_io_channel(); 868a34fc12bSpaul luse g_capabilites = spdk_accel_get_capabilities(accel_ch); 869514be889Spaul luse spdk_put_io_channel(accel_ch); 870514be889Spaul luse 871a34fc12bSpaul luse if ((g_capabilites & g_workload_selection) != g_workload_selection) { 872a7dfca5bSpaul luse SPDK_WARNLOG("The selected workload is not natively supported by the current engine\n"); 873a7dfca5bSpaul luse SPDK_WARNLOG("The software engine will be used instead.\n\n"); 874514be889Spaul luse } 875514be889Spaul luse 8769f51cf32Spaul luse g_tsc_rate = spdk_get_ticks_hz(); 8779f51cf32Spaul luse g_tsc_us_rate = g_tsc_rate / (1000 * 1000); 8789f51cf32Spaul luse g_tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate; 8799f51cf32Spaul luse 8809f51cf32Spaul luse printf("Running for %d seconds...\n", g_time_in_sec); 8819f51cf32Spaul luse fflush(stdout); 8829f51cf32Spaul luse 883eea826a2Spaul luse /* Create worker threads for each core that was specified. */ 884eea826a2Spaul luse SPDK_ENV_FOREACH_CORE(i) { 885445fe74eSpaul luse for (j = 0; j < g_threads_per_core; j++) { 886445fe74eSpaul luse snprintf(thread_name, sizeof(thread_name), "ap_worker_%u_%u", i, j); 887eea826a2Spaul luse spdk_cpuset_zero(&tmp_cpumask); 888eea826a2Spaul luse spdk_cpuset_set_cpu(&tmp_cpumask, i, true); 889eea826a2Spaul luse thread = spdk_thread_create(thread_name, &tmp_cpumask); 890445fe74eSpaul luse display = calloc(1, sizeof(*display)); 891445fe74eSpaul luse if (display == NULL) { 892445fe74eSpaul luse fprintf(stderr, "Unable to allocate memory\n"); 893445fe74eSpaul luse spdk_app_stop(-1); 894445fe74eSpaul luse return; 895445fe74eSpaul luse } 896445fe74eSpaul luse display->core = i; 897445fe74eSpaul luse display->thread = j; 898445fe74eSpaul luse spdk_thread_send_msg(thread, _init_thread, display); 899445fe74eSpaul luse } 900eea826a2Spaul luse } 9019f51cf32Spaul luse } 9029f51cf32Spaul luse 9039f51cf32Spaul luse int 9049f51cf32Spaul luse main(int argc, char **argv) 9059f51cf32Spaul luse { 9069f51cf32Spaul luse struct spdk_app_opts opts = {}; 9079f51cf32Spaul luse struct worker_thread *worker, *tmp; 9089f51cf32Spaul luse 9099f51cf32Spaul luse pthread_mutex_init(&g_workers_lock, NULL); 91048701bd9SZiye Yang spdk_app_opts_init(&opts, sizeof(opts)); 9119f51cf32Spaul luse opts.reactor_mask = "0x1"; 912445fe74eSpaul luse if (spdk_app_parse_args(argc, argv, &opts, "o:q:t:yw:P:f:b:T:", NULL, parse_args, 9131e2b38baSyidong0635 usage) != SPDK_APP_PARSE_ARGS_SUCCESS) { 914*9b189667Spaul luse g_rc = -1; 9159f51cf32Spaul luse goto cleanup; 9169f51cf32Spaul luse } 9179f51cf32Spaul luse 918b9218b7aSpaul luse if ((g_workload_selection != ACCEL_COPY) && 919b9218b7aSpaul luse (g_workload_selection != ACCEL_FILL) && 920b9218b7aSpaul luse (g_workload_selection != ACCEL_CRC32C) && 9210ef079c6Spaul luse (g_workload_selection != ACCEL_COMPARE) && 9220ef079c6Spaul luse (g_workload_selection != ACCEL_DUALCAST)) { 9232a0c66d0Spaul luse usage(); 924*9b189667Spaul luse g_rc = -1; 9252a0c66d0Spaul luse goto cleanup; 9262a0c66d0Spaul luse } 9272a0c66d0Spaul luse 928f17e6705Spaul luse if (g_ops_per_batch > 0 && (g_queue_depth % g_ops_per_batch > 0)) { 929f17e6705Spaul luse fprintf(stdout, "batch size must be a multiple of queue depth\n"); 930f17e6705Spaul luse usage(); 931*9b189667Spaul luse g_rc = -1; 932f17e6705Spaul luse goto cleanup; 933f17e6705Spaul luse } 934f17e6705Spaul luse 9359f51cf32Spaul luse dump_user_config(&opts); 936*9b189667Spaul luse g_rc = spdk_app_start(&opts, accel_perf_start, NULL); 937*9b189667Spaul luse if (g_rc) { 9389f51cf32Spaul luse SPDK_ERRLOG("ERROR starting application\n"); 9399f51cf32Spaul luse } 9409f51cf32Spaul luse 9419f51cf32Spaul luse pthread_mutex_destroy(&g_workers_lock); 9429f51cf32Spaul luse 9439f51cf32Spaul luse worker = g_workers; 9449f51cf32Spaul luse while (worker) { 9459f51cf32Spaul luse tmp = worker->next; 9469f51cf32Spaul luse free(worker); 9479f51cf32Spaul luse worker = tmp; 9489f51cf32Spaul luse } 9499f51cf32Spaul luse cleanup: 9509f51cf32Spaul luse spdk_app_fini(); 951*9b189667Spaul luse return g_rc; 9529f51cf32Spaul luse } 953