19f51cf32Spaul luse /*- 29f51cf32Spaul luse * BSD LICENSE 39f51cf32Spaul luse * 49f51cf32Spaul luse * Copyright (c) Intel Corporation. 59f51cf32Spaul luse * All rights reserved. 69f51cf32Spaul luse * 79f51cf32Spaul luse * Redistribution and use in source and binary forms, with or without 89f51cf32Spaul luse * modification, are permitted provided that the following conditions 99f51cf32Spaul luse * are met: 109f51cf32Spaul luse * 119f51cf32Spaul luse * * Redistributions of source code must retain the above copyright 129f51cf32Spaul luse * notice, this list of conditions and the following disclaimer. 139f51cf32Spaul luse * * Redistributions in binary form must reproduce the above copyright 149f51cf32Spaul luse * notice, this list of conditions and the following disclaimer in 159f51cf32Spaul luse * the documentation and/or other materials provided with the 169f51cf32Spaul luse * distribution. 179f51cf32Spaul luse * * Neither the name of Intel Corporation nor the names of its 189f51cf32Spaul luse * contributors may be used to endorse or promote products derived 199f51cf32Spaul luse * from this software without specific prior written permission. 209f51cf32Spaul luse * 219f51cf32Spaul luse * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 229f51cf32Spaul luse * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 239f51cf32Spaul luse * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 249f51cf32Spaul luse * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 259f51cf32Spaul luse * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 269f51cf32Spaul luse * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 279f51cf32Spaul luse * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 289f51cf32Spaul luse * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 299f51cf32Spaul luse * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 309f51cf32Spaul luse * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 319f51cf32Spaul luse * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 329f51cf32Spaul luse */ 339f51cf32Spaul luse 349f51cf32Spaul luse #include "spdk/stdinc.h" 359f51cf32Spaul luse #include "spdk/thread.h" 369f51cf32Spaul luse #include "spdk/env.h" 379f51cf32Spaul luse #include "spdk/event.h" 389f51cf32Spaul luse #include "spdk/log.h" 399f51cf32Spaul luse #include "spdk/string.h" 409f51cf32Spaul luse #include "spdk/accel_engine.h" 41e69375bfSpaul luse #include "spdk/crc32.h" 420cecfcb1Spaul luse #include "spdk/util.h" 439f51cf32Spaul luse 44b9218b7aSpaul luse #define DATA_PATTERN 0x5a 450ef079c6Spaul luse #define ALIGN_4K 0x1000 46b9218b7aSpaul luse 479f51cf32Spaul luse static uint64_t g_tsc_rate; 489f51cf32Spaul luse static uint64_t g_tsc_us_rate; 499f51cf32Spaul luse static uint64_t g_tsc_end; 509f51cf32Spaul luse static int g_xfer_size_bytes = 4096; 519f51cf32Spaul luse static int g_queue_depth = 32; 52f17e6705Spaul luse static int g_ops_per_batch = 0; 539f51cf32Spaul luse static int g_time_in_sec = 5; 54e69375bfSpaul luse static uint32_t g_crc32c_seed = 0; 55b9218b7aSpaul luse static int g_fail_percent_goal = 0; 5689495464Spaul luse static uint8_t g_fill_pattern = 255; 579f51cf32Spaul luse static bool g_verify = false; 582a0c66d0Spaul luse static const char *g_workload_type = NULL; 59514be889Spaul luse static enum accel_capability g_workload_selection; 609f51cf32Spaul luse static struct worker_thread *g_workers = NULL; 619f51cf32Spaul luse static int g_num_workers = 0; 629f51cf32Spaul luse static pthread_mutex_t g_workers_lock = PTHREAD_MUTEX_INITIALIZER; 63a34fc12bSpaul luse uint64_t g_capabilites; 64cdefd3d3Spaul luse 65cdefd3d3Spaul luse struct worker_thread; 66cdefd3d3Spaul luse static void accel_done(void *ref, int status); 67cdefd3d3Spaul luse 68cdefd3d3Spaul luse struct ap_task { 69cdefd3d3Spaul luse void *src; 70cdefd3d3Spaul luse void *dst; 71cdefd3d3Spaul luse void *dst2; 72cdefd3d3Spaul luse struct worker_thread *worker; 73cdefd3d3Spaul luse int status; 74cdefd3d3Spaul luse int expected_status; /* used for the compare operation */ 75cdefd3d3Spaul luse TAILQ_ENTRY(ap_task) link; 76cdefd3d3Spaul luse }; 779f51cf32Spaul luse 78f17e6705Spaul luse struct accel_batch { 79f17e6705Spaul luse int status; 80f17e6705Spaul luse int cmd_count; 81f17e6705Spaul luse struct spdk_accel_batch *batch; 82f17e6705Spaul luse struct worker_thread *worker; 83f17e6705Spaul luse TAILQ_ENTRY(accel_batch) link; 84f17e6705Spaul luse }; 85f17e6705Spaul luse 869f51cf32Spaul luse struct worker_thread { 879f51cf32Spaul luse struct spdk_io_channel *ch; 889f51cf32Spaul luse uint64_t xfer_completed; 899f51cf32Spaul luse uint64_t xfer_failed; 90b9218b7aSpaul luse uint64_t injected_miscompares; 919f51cf32Spaul luse uint64_t current_queue_depth; 92ac9a1a83Spaul luse TAILQ_HEAD(, ap_task) tasks_pool; 939f51cf32Spaul luse struct worker_thread *next; 949f51cf32Spaul luse unsigned core; 959f51cf32Spaul luse struct spdk_thread *thread; 969f51cf32Spaul luse bool is_draining; 979f51cf32Spaul luse struct spdk_poller *is_draining_poller; 989f51cf32Spaul luse struct spdk_poller *stop_poller; 99ac9a1a83Spaul luse void *task_base; 100f17e6705Spaul luse struct accel_batch *batch_base; 101f17e6705Spaul luse TAILQ_HEAD(, accel_batch) in_prep_batches; 102f17e6705Spaul luse TAILQ_HEAD(, accel_batch) in_use_batches; 103f17e6705Spaul luse TAILQ_HEAD(, accel_batch) to_submit_batches; 1049f51cf32Spaul luse }; 1059f51cf32Spaul luse 1069f51cf32Spaul luse static void 1079f51cf32Spaul luse dump_user_config(struct spdk_app_opts *opts) 1089f51cf32Spaul luse { 1099f51cf32Spaul luse printf("SPDK Configuration:\n"); 1109f51cf32Spaul luse printf("Core mask: %s\n\n", opts->reactor_mask); 1119f51cf32Spaul luse printf("Accel Perf Configuration:\n"); 1122a0c66d0Spaul luse printf("Workload Type: %s\n", g_workload_type); 113b9218b7aSpaul luse if (g_workload_selection == ACCEL_CRC32C) { 114b9218b7aSpaul luse printf("CRC-32C seed: %u\n", g_crc32c_seed); 11589495464Spaul luse } else if (g_workload_selection == ACCEL_FILL) { 11689495464Spaul luse printf("Fill pattern: 0x%x\n", g_fill_pattern); 117b9218b7aSpaul luse } else if ((g_workload_selection == ACCEL_COMPARE) && g_fail_percent_goal > 0) { 11889495464Spaul luse printf("Failure inject: %u percent\n", g_fail_percent_goal); 119e69375bfSpaul luse } 1209f51cf32Spaul luse printf("Transfer size: %u bytes\n", g_xfer_size_bytes); 1219f51cf32Spaul luse printf("Queue depth: %u\n", g_queue_depth); 1229f51cf32Spaul luse printf("Run time: %u seconds\n", g_time_in_sec); 123f17e6705Spaul luse if (g_ops_per_batch > 0) { 124f17e6705Spaul luse printf("Batching: %u operations\n", g_ops_per_batch); 125f17e6705Spaul luse } else { 126f17e6705Spaul luse printf("Batching: Disabled\n"); 127f17e6705Spaul luse } 1289f51cf32Spaul luse printf("Verify: %s\n\n", g_verify ? "Yes" : "No"); 1299f51cf32Spaul luse } 1309f51cf32Spaul luse 1319f51cf32Spaul luse static void 1329f51cf32Spaul luse usage(void) 1339f51cf32Spaul luse { 1349f51cf32Spaul luse printf("accel_perf options:\n"); 1359f51cf32Spaul luse printf("\t[-h help message]\n"); 136f17e6705Spaul luse printf("\t[-q queue depth per core]\n"); 1379f51cf32Spaul luse printf("\t[-n number of channels]\n"); 1389f51cf32Spaul luse printf("\t[-o transfer size in bytes]\n"); 1399f51cf32Spaul luse printf("\t[-t time in seconds]\n"); 1400ef079c6Spaul luse printf("\t[-w workload type must be one of these: copy, fill, crc32c, compare, dualcast\n"); 141e69375bfSpaul luse printf("\t[-s for crc32c workload, use this seed value (default 0)\n"); 142b9218b7aSpaul luse printf("\t[-P for compare workload, percentage of operations that should miscompare (percent, default 0)\n"); 14389495464Spaul luse printf("\t[-f for fill workload, use this BYTE value (default 255)\n"); 1442a0c66d0Spaul luse printf("\t[-y verify result if this switch is on]\n"); 145f17e6705Spaul luse printf("\t[-b batch this number of operations at a time (default 0 = disabled)]\n"); 1469f51cf32Spaul luse } 1479f51cf32Spaul luse 1489f51cf32Spaul luse static int 1499f51cf32Spaul luse parse_args(int argc, char *argv) 1509f51cf32Spaul luse { 1519f51cf32Spaul luse switch (argc) { 152f17e6705Spaul luse case 'b': 153f17e6705Spaul luse g_ops_per_batch = spdk_strtol(optarg, 10); 154f17e6705Spaul luse break; 15589495464Spaul luse case 'f': 15689495464Spaul luse g_fill_pattern = (uint8_t)spdk_strtol(optarg, 10); 15789495464Spaul luse break; 1589f51cf32Spaul luse case 'o': 1599f51cf32Spaul luse g_xfer_size_bytes = spdk_strtol(optarg, 10); 1609f51cf32Spaul luse break; 161b9218b7aSpaul luse case 'P': 162b9218b7aSpaul luse g_fail_percent_goal = spdk_strtol(optarg, 10); 163b9218b7aSpaul luse break; 1649f51cf32Spaul luse case 'q': 1659f51cf32Spaul luse g_queue_depth = spdk_strtol(optarg, 10); 1669f51cf32Spaul luse break; 167e69375bfSpaul luse case 's': 168e69375bfSpaul luse g_crc32c_seed = spdk_strtol(optarg, 10); 169e69375bfSpaul luse break; 1709f51cf32Spaul luse case 't': 1719f51cf32Spaul luse g_time_in_sec = spdk_strtol(optarg, 10); 1729f51cf32Spaul luse break; 1739f51cf32Spaul luse case 'y': 1749f51cf32Spaul luse g_verify = true; 1759f51cf32Spaul luse break; 1762a0c66d0Spaul luse case 'w': 1772a0c66d0Spaul luse g_workload_type = optarg; 178514be889Spaul luse if (!strcmp(g_workload_type, "copy")) { 179514be889Spaul luse g_workload_selection = ACCEL_COPY; 180514be889Spaul luse } else if (!strcmp(g_workload_type, "fill")) { 181514be889Spaul luse g_workload_selection = ACCEL_FILL; 182e69375bfSpaul luse } else if (!strcmp(g_workload_type, "crc32c")) { 183e69375bfSpaul luse g_workload_selection = ACCEL_CRC32C; 184b9218b7aSpaul luse } else if (!strcmp(g_workload_type, "compare")) { 185b9218b7aSpaul luse g_workload_selection = ACCEL_COMPARE; 1860ef079c6Spaul luse } else if (!strcmp(g_workload_type, "dualcast")) { 1870ef079c6Spaul luse g_workload_selection = ACCEL_DUALCAST; 188514be889Spaul luse } 1892a0c66d0Spaul luse break; 1909f51cf32Spaul luse default: 1919f51cf32Spaul luse usage(); 1929f51cf32Spaul luse return 1; 1939f51cf32Spaul luse } 1949f51cf32Spaul luse return 0; 1959f51cf32Spaul luse } 1969f51cf32Spaul luse 1979f51cf32Spaul luse static void 1989f51cf32Spaul luse unregister_worker(void *arg1) 1999f51cf32Spaul luse { 2009f51cf32Spaul luse struct worker_thread *worker = arg1; 2019f51cf32Spaul luse 202ac9a1a83Spaul luse free(worker->task_base); 203f17e6705Spaul luse free(worker->batch_base); 2049f51cf32Spaul luse spdk_put_io_channel(worker->ch); 2059f51cf32Spaul luse pthread_mutex_lock(&g_workers_lock); 2069f51cf32Spaul luse assert(g_num_workers >= 1); 2079f51cf32Spaul luse if (--g_num_workers == 0) { 2089f51cf32Spaul luse pthread_mutex_unlock(&g_workers_lock); 2099f51cf32Spaul luse spdk_app_stop(0); 2109f51cf32Spaul luse } 2119f51cf32Spaul luse pthread_mutex_unlock(&g_workers_lock); 2129f51cf32Spaul luse } 2139f51cf32Spaul luse 2148da995c4Spaul luse static int 2158da995c4Spaul luse _get_task_data_bufs(struct ap_task *task) 2168da995c4Spaul luse { 2178da995c4Spaul luse uint32_t align = 0; 2188da995c4Spaul luse 2198da995c4Spaul luse /* For dualcast, the DSA HW requires 4K alignment on destination addresses but 2208da995c4Spaul luse * we do this for all engines to keep it simple. 2218da995c4Spaul luse */ 2228da995c4Spaul luse if (g_workload_selection == ACCEL_DUALCAST) { 2238da995c4Spaul luse align = ALIGN_4K; 2248da995c4Spaul luse } 2258da995c4Spaul luse 2268da995c4Spaul luse task->src = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL); 2278da995c4Spaul luse if (task->src == NULL) { 2288da995c4Spaul luse fprintf(stderr, "Unable to alloc src buffer\n"); 2298da995c4Spaul luse return -ENOMEM; 2308da995c4Spaul luse } 2318da995c4Spaul luse memset(task->src, DATA_PATTERN, g_xfer_size_bytes); 2328da995c4Spaul luse 2338da995c4Spaul luse task->dst = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL); 2348da995c4Spaul luse if (task->dst == NULL) { 2358da995c4Spaul luse fprintf(stderr, "Unable to alloc dst buffer\n"); 2368da995c4Spaul luse return -ENOMEM; 2378da995c4Spaul luse } 2388da995c4Spaul luse 2398da995c4Spaul luse /* For compare we want the buffers to match, otherwise not. */ 2408da995c4Spaul luse if (g_workload_selection == ACCEL_COMPARE) { 2418da995c4Spaul luse memset(task->dst, DATA_PATTERN, g_xfer_size_bytes); 2428da995c4Spaul luse } else { 2438da995c4Spaul luse memset(task->dst, ~DATA_PATTERN, g_xfer_size_bytes); 2448da995c4Spaul luse } 2458da995c4Spaul luse 2468da995c4Spaul luse /* For fill, set the entire src buffer so we can check if verify is enabled. */ 2478da995c4Spaul luse if (g_workload_selection == ACCEL_FILL) { 2488da995c4Spaul luse memset(task->src, g_fill_pattern, g_xfer_size_bytes); 2498da995c4Spaul luse } 2508da995c4Spaul luse 2518da995c4Spaul luse if (g_workload_selection == ACCEL_DUALCAST) { 2528da995c4Spaul luse task->dst2 = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL); 2538da995c4Spaul luse if (task->dst2 == NULL) { 2548da995c4Spaul luse fprintf(stderr, "Unable to alloc dst buffer\n"); 2558da995c4Spaul luse return -ENOMEM; 2568da995c4Spaul luse } 2578da995c4Spaul luse memset(task->dst2, ~DATA_PATTERN, g_xfer_size_bytes); 2588da995c4Spaul luse } 2598da995c4Spaul luse 2608da995c4Spaul luse return 0; 2618da995c4Spaul luse } 2628da995c4Spaul luse 263ac9a1a83Spaul luse inline static struct ap_task * 264ac9a1a83Spaul luse _get_task(struct worker_thread *worker) 265ac9a1a83Spaul luse { 266ac9a1a83Spaul luse struct ap_task *task; 267ac9a1a83Spaul luse 268ac9a1a83Spaul luse if (!TAILQ_EMPTY(&worker->tasks_pool)) { 269ac9a1a83Spaul luse task = TAILQ_FIRST(&worker->tasks_pool); 270ac9a1a83Spaul luse TAILQ_REMOVE(&worker->tasks_pool, task, link); 271ac9a1a83Spaul luse } else { 272ac9a1a83Spaul luse fprintf(stderr, "Unable to get ap_task\n"); 273ac9a1a83Spaul luse return NULL; 274ac9a1a83Spaul luse } 275ac9a1a83Spaul luse 276ac9a1a83Spaul luse task->worker = worker; 277ac9a1a83Spaul luse task->worker->current_queue_depth++; 278ac9a1a83Spaul luse return task; 279ac9a1a83Spaul luse } 280ac9a1a83Spaul luse 281f17e6705Spaul luse /* Submit one operation using the same ap task that just completed. */ 2829f51cf32Spaul luse static void 283ac9a1a83Spaul luse _submit_single(struct worker_thread *worker, struct ap_task *task) 2849f51cf32Spaul luse { 285b9218b7aSpaul luse int random_num; 28640ec8e97Spaul luse int rc = 0; 2879f51cf32Spaul luse 2889f51cf32Spaul luse assert(worker); 2899f51cf32Spaul luse 290e69375bfSpaul luse switch (g_workload_selection) { 291e69375bfSpaul luse case ACCEL_COPY: 292e8463f87Spaul luse rc = spdk_accel_submit_copy(worker->ch, task->dst, task->src, 293e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 294e69375bfSpaul luse break; 295e69375bfSpaul luse case ACCEL_FILL: 2962a0c66d0Spaul luse /* For fill use the first byte of the task->dst buffer */ 297ee7e31f9Spaul luse rc = spdk_accel_submit_fill(worker->ch, task->dst, *(uint8_t *)task->src, 298e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 299e69375bfSpaul luse break; 300e69375bfSpaul luse case ACCEL_CRC32C: 301e8463f87Spaul luse rc = spdk_accel_submit_crc32c(worker->ch, (uint32_t *)task->dst, 302e8463f87Spaul luse task->src, g_crc32c_seed, 303e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 304e69375bfSpaul luse break; 305b9218b7aSpaul luse case ACCEL_COMPARE: 306b9218b7aSpaul luse random_num = rand() % 100; 307b9218b7aSpaul luse if (random_num < g_fail_percent_goal) { 308b9218b7aSpaul luse task->expected_status = -EILSEQ; 309b9218b7aSpaul luse *(uint8_t *)task->dst = ~DATA_PATTERN; 310b9218b7aSpaul luse } else { 311b9218b7aSpaul luse task->expected_status = 0; 312b9218b7aSpaul luse *(uint8_t *)task->dst = DATA_PATTERN; 313b9218b7aSpaul luse } 314ee7e31f9Spaul luse rc = spdk_accel_submit_compare(worker->ch, task->dst, task->src, 315e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 316b9218b7aSpaul luse break; 3170ef079c6Spaul luse case ACCEL_DUALCAST: 318ee7e31f9Spaul luse rc = spdk_accel_submit_dualcast(worker->ch, task->dst, task->dst2, 319e8463f87Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 3200ef079c6Spaul luse break; 321e69375bfSpaul luse default: 3222a0c66d0Spaul luse assert(false); 323e69375bfSpaul luse break; 324e69375bfSpaul luse 3252a0c66d0Spaul luse } 32640ec8e97Spaul luse 32740ec8e97Spaul luse if (rc) { 328e8463f87Spaul luse accel_done(task, rc); 32940ec8e97Spaul luse } 3309f51cf32Spaul luse } 3319f51cf32Spaul luse 332fab40895Spaul luse static int 333f17e6705Spaul luse _batch_prep_cmd(struct worker_thread *worker, struct ap_task *task, 334f17e6705Spaul luse struct accel_batch *worker_batch) 335fab40895Spaul luse { 336f17e6705Spaul luse struct spdk_accel_batch *batch = worker_batch->batch; 337fab40895Spaul luse int rc = 0; 338fab40895Spaul luse 339f17e6705Spaul luse worker_batch->cmd_count++; 340f17e6705Spaul luse assert(worker_batch->cmd_count <= g_ops_per_batch); 341f17e6705Spaul luse 342fab40895Spaul luse switch (g_workload_selection) { 343fab40895Spaul luse case ACCEL_COPY: 344fab40895Spaul luse rc = spdk_accel_batch_prep_copy(worker->ch, batch, task->dst, 345fab40895Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 346fab40895Spaul luse break; 347fab40895Spaul luse case ACCEL_DUALCAST: 348fab40895Spaul luse rc = spdk_accel_batch_prep_dualcast(worker->ch, batch, task->dst, task->dst2, 349fab40895Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 350fab40895Spaul luse break; 351fab40895Spaul luse case ACCEL_COMPARE: 352fab40895Spaul luse rc = spdk_accel_batch_prep_compare(worker->ch, batch, task->dst, task->src, 353fab40895Spaul luse g_xfer_size_bytes, accel_done, task); 354fab40895Spaul luse break; 355fab40895Spaul luse case ACCEL_FILL: 356fab40895Spaul luse rc = spdk_accel_batch_prep_fill(worker->ch, batch, task->dst, 357fab40895Spaul luse *(uint8_t *)task->src, 358fab40895Spaul luse g_xfer_size_bytes, accel_done, task); 359fab40895Spaul luse break; 360fab40895Spaul luse case ACCEL_CRC32C: 361fab40895Spaul luse rc = spdk_accel_batch_prep_crc32c(worker->ch, batch, (uint32_t *)task->dst, 362fab40895Spaul luse task->src, g_crc32c_seed, g_xfer_size_bytes, accel_done, task); 363fab40895Spaul luse break; 364fab40895Spaul luse default: 365fab40895Spaul luse assert(false); 366fab40895Spaul luse break; 367fab40895Spaul luse } 368fab40895Spaul luse 369fab40895Spaul luse return rc; 370fab40895Spaul luse } 371fab40895Spaul luse 3729f51cf32Spaul luse static void 373ac9a1a83Spaul luse _free_task(struct ap_task *task) 374ac9a1a83Spaul luse { 375ac9a1a83Spaul luse spdk_dma_free(task->src); 376ac9a1a83Spaul luse spdk_dma_free(task->dst); 377ac9a1a83Spaul luse if (g_workload_selection == ACCEL_DUALCAST) { 378ac9a1a83Spaul luse spdk_dma_free(task->dst2); 379ac9a1a83Spaul luse } 380ac9a1a83Spaul luse } 381ac9a1a83Spaul luse 382f17e6705Spaul luse static void _batch_done(void *cb_arg); 383f17e6705Spaul luse static void 384f17e6705Spaul luse _build_batch(struct worker_thread *worker, struct ap_task *task) 385f17e6705Spaul luse { 386f17e6705Spaul luse struct accel_batch *worker_batch = NULL; 387f17e6705Spaul luse int rc; 388f17e6705Spaul luse 389f17e6705Spaul luse assert(!TAILQ_EMPTY(&worker->in_prep_batches)); 390f17e6705Spaul luse 391f17e6705Spaul luse worker_batch = TAILQ_FIRST(&worker->in_prep_batches); 392f17e6705Spaul luse 393f17e6705Spaul luse /* If an accel batch hasn't been created yet do so now. */ 394f17e6705Spaul luse if (worker_batch->batch == NULL) { 395f17e6705Spaul luse worker_batch->batch = spdk_accel_batch_create(worker->ch); 396f17e6705Spaul luse if (worker_batch->batch == NULL) { 397f17e6705Spaul luse fprintf(stderr, "error unable to create new batch\n"); 398f17e6705Spaul luse return; 399f17e6705Spaul luse } 400f17e6705Spaul luse } 401f17e6705Spaul luse 402f17e6705Spaul luse /* Prep the command re-using the last completed command's task */ 403f17e6705Spaul luse rc = _batch_prep_cmd(worker, task, worker_batch); 404f17e6705Spaul luse if (rc) { 405f17e6705Spaul luse fprintf(stderr, "error preping command for batch\n"); 406f17e6705Spaul luse goto error; 407f17e6705Spaul luse } 408f17e6705Spaul luse 409f17e6705Spaul luse /* If this batch is full move it to the to_submit list so it gets 410f17e6705Spaul luse * submitted as batches complete. 411f17e6705Spaul luse */ 412f17e6705Spaul luse if (worker_batch->cmd_count == g_ops_per_batch) { 413f17e6705Spaul luse TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link); 414f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->to_submit_batches, worker_batch, link); 415f17e6705Spaul luse } 416f17e6705Spaul luse 417f17e6705Spaul luse return; 418f17e6705Spaul luse error: 419f17e6705Spaul luse spdk_accel_batch_cancel(worker->ch, worker_batch->batch); 420f17e6705Spaul luse 421f17e6705Spaul luse } 422f17e6705Spaul luse 423f17e6705Spaul luse static void batch_done(void *cb_arg, int status); 424f17e6705Spaul luse static void 425f17e6705Spaul luse _drain_batch(struct worker_thread *worker) 426f17e6705Spaul luse { 427f17e6705Spaul luse struct accel_batch *worker_batch, *tmp; 428f17e6705Spaul luse int rc; 429f17e6705Spaul luse 430f17e6705Spaul luse /* submit any batches that were being built up. */ 431f17e6705Spaul luse TAILQ_FOREACH_SAFE(worker_batch, &worker->in_prep_batches, link, tmp) { 432f17e6705Spaul luse if (worker_batch->cmd_count == 0) { 433f17e6705Spaul luse continue; 434f17e6705Spaul luse } 435f17e6705Spaul luse worker->current_queue_depth += worker_batch->cmd_count + 1; 436f17e6705Spaul luse 437f17e6705Spaul luse TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link); 438f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link); 439f17e6705Spaul luse rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch); 440f17e6705Spaul luse if (rc == 0) { 441f17e6705Spaul luse worker_batch->cmd_count = 0; 442f17e6705Spaul luse } else { 443f17e6705Spaul luse fprintf(stderr, "error sending final batch\n"); 444f17e6705Spaul luse worker->current_queue_depth -= worker_batch->cmd_count + 1; 445f17e6705Spaul luse break; 446f17e6705Spaul luse } 447f17e6705Spaul luse } 448f17e6705Spaul luse } 449f17e6705Spaul luse 450f17e6705Spaul luse static void 451f17e6705Spaul luse _batch_done(void *cb_arg) 452f17e6705Spaul luse { 453f17e6705Spaul luse struct accel_batch *worker_batch = (struct accel_batch *)cb_arg; 454f17e6705Spaul luse struct worker_thread *worker = worker_batch->worker; 455f17e6705Spaul luse int rc; 456f17e6705Spaul luse 457f17e6705Spaul luse assert(TAILQ_EMPTY(&worker->in_use_batches) == 0); 458f17e6705Spaul luse 459f17e6705Spaul luse if (worker_batch->status) { 460f17e6705Spaul luse SPDK_ERRLOG("error %d\n", worker_batch->status); 461f17e6705Spaul luse } 462f17e6705Spaul luse 463f17e6705Spaul luse worker->current_queue_depth--; 464f17e6705Spaul luse TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link); 465f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link); 466f17e6705Spaul luse worker_batch->batch = NULL; 467f17e6705Spaul luse worker_batch->cmd_count = 0; 468f17e6705Spaul luse 469f17e6705Spaul luse if (!worker->is_draining) { 470f17e6705Spaul luse worker_batch = TAILQ_FIRST(&worker->to_submit_batches); 471f17e6705Spaul luse if (worker_batch != NULL) { 472f17e6705Spaul luse 473f17e6705Spaul luse assert(worker_batch->cmd_count == g_ops_per_batch); 474f17e6705Spaul luse 475f17e6705Spaul luse /* Add one for the batch command itself. */ 476f17e6705Spaul luse worker->current_queue_depth += g_ops_per_batch + 1; 477f17e6705Spaul luse TAILQ_REMOVE(&worker->to_submit_batches, worker_batch, link); 478f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link); 479f17e6705Spaul luse 480f17e6705Spaul luse rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch); 481f17e6705Spaul luse if (rc) { 482f17e6705Spaul luse fprintf(stderr, "error ending batch\n"); 483f17e6705Spaul luse worker->current_queue_depth -= g_ops_per_batch + 1; 484f17e6705Spaul luse return; 485f17e6705Spaul luse } 486f17e6705Spaul luse } 487f17e6705Spaul luse } else { 488f17e6705Spaul luse _drain_batch(worker); 489f17e6705Spaul luse } 490f17e6705Spaul luse } 491f17e6705Spaul luse 492ac9a1a83Spaul luse static void 493fab40895Spaul luse batch_done(void *cb_arg, int status) 494fab40895Spaul luse { 495f17e6705Spaul luse struct accel_batch *worker_batch = (struct accel_batch *)cb_arg; 496fab40895Spaul luse 497f17e6705Spaul luse assert(worker_batch->worker); 498f17e6705Spaul luse 499f17e6705Spaul luse worker_batch->status = status; 500f17e6705Spaul luse spdk_thread_send_msg(worker_batch->worker->thread, _batch_done, worker_batch); 501fab40895Spaul luse } 502fab40895Spaul luse 503fab40895Spaul luse static void 5049f51cf32Spaul luse _accel_done(void *arg1) 5059f51cf32Spaul luse { 5069f51cf32Spaul luse struct ap_task *task = arg1; 5079f51cf32Spaul luse struct worker_thread *worker = task->worker; 508e69375bfSpaul luse uint32_t sw_crc32c; 5099f51cf32Spaul luse 5109f51cf32Spaul luse assert(worker); 5119f51cf32Spaul luse assert(worker->current_queue_depth > 0); 5129f51cf32Spaul luse 513b9218b7aSpaul luse if (g_verify && task->status == 0) { 514b9218b7aSpaul luse switch (g_workload_selection) { 515b9218b7aSpaul luse case ACCEL_CRC32C: 516e69375bfSpaul luse /* calculate sw CRC-32C and compare to sw aceel result. */ 517e69375bfSpaul luse sw_crc32c = spdk_crc32c_update(task->src, g_xfer_size_bytes, ~g_crc32c_seed); 518e69375bfSpaul luse if (*(uint32_t *)task->dst != sw_crc32c) { 519e69375bfSpaul luse SPDK_NOTICELOG("CRC-32C miscompare\n"); 520e69375bfSpaul luse worker->xfer_failed++; 521e69375bfSpaul luse } 522b9218b7aSpaul luse break; 523b9218b7aSpaul luse case ACCEL_COPY: 524b9218b7aSpaul luse if (memcmp(task->src, task->dst, g_xfer_size_bytes)) { 5259f51cf32Spaul luse SPDK_NOTICELOG("Data miscompare\n"); 5269f51cf32Spaul luse worker->xfer_failed++; 527b9218b7aSpaul luse } 528b9218b7aSpaul luse break; 5290ef079c6Spaul luse case ACCEL_DUALCAST: 5300ef079c6Spaul luse if (memcmp(task->src, task->dst, g_xfer_size_bytes)) { 5310ef079c6Spaul luse SPDK_NOTICELOG("Data miscompare, first destination\n"); 5320ef079c6Spaul luse worker->xfer_failed++; 5330ef079c6Spaul luse } 5340ef079c6Spaul luse if (memcmp(task->src, task->dst2, g_xfer_size_bytes)) { 5350ef079c6Spaul luse SPDK_NOTICELOG("Data miscompare, second destination\n"); 5360ef079c6Spaul luse worker->xfer_failed++; 5370ef079c6Spaul luse } 5380ef079c6Spaul luse break; 539d207237fSpaul luse case ACCEL_FILL: 540d207237fSpaul luse if (memcmp(task->dst, task->src, g_xfer_size_bytes)) { 541d207237fSpaul luse SPDK_NOTICELOG("Data miscompare\n"); 542d207237fSpaul luse worker->xfer_failed++; 543d207237fSpaul luse } 544d207237fSpaul luse break; 5458cee297cSpaul luse case ACCEL_COMPARE: 5468cee297cSpaul luse break; 547b9218b7aSpaul luse default: 548b9218b7aSpaul luse assert(false); 549b9218b7aSpaul luse break; 5509f51cf32Spaul luse } 5519f51cf32Spaul luse } 552b9218b7aSpaul luse 553b9218b7aSpaul luse if (task->expected_status == -EILSEQ) { 554b9218b7aSpaul luse assert(task->status != 0); 555b9218b7aSpaul luse worker->injected_miscompares++; 556b9218b7aSpaul luse } else if (task->status) { 557f17e6705Spaul luse /* Expected to pass but the accel engine reported an error (ex: COMPARE operation). */ 558b9218b7aSpaul luse worker->xfer_failed++; 559b9218b7aSpaul luse } 560b9218b7aSpaul luse 5619f51cf32Spaul luse worker->xfer_completed++; 5629f51cf32Spaul luse worker->current_queue_depth--; 5639f51cf32Spaul luse 56440ec8e97Spaul luse if (!worker->is_draining) { 565f17e6705Spaul luse if (g_ops_per_batch == 0) { 5669f51cf32Spaul luse _submit_single(worker, task); 567ac9a1a83Spaul luse worker->current_queue_depth++; 568f17e6705Spaul luse } else { 569f17e6705Spaul luse _build_batch(worker, task); 5709f51cf32Spaul luse } 571f17e6705Spaul luse } else if (g_ops_per_batch > 0) { 572f17e6705Spaul luse _drain_batch(worker); 573*b34883e0SZiye Yang } else { 574*b34883e0SZiye Yang TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link); 575f17e6705Spaul luse } 5769f51cf32Spaul luse } 5779f51cf32Spaul luse 5789f51cf32Spaul luse static int 5799f51cf32Spaul luse dump_result(void) 5809f51cf32Spaul luse { 5819f51cf32Spaul luse uint64_t total_completed = 0; 5829f51cf32Spaul luse uint64_t total_failed = 0; 583b9218b7aSpaul luse uint64_t total_miscompared = 0; 5849f51cf32Spaul luse uint64_t total_xfer_per_sec, total_bw_in_MiBps; 5859f51cf32Spaul luse struct worker_thread *worker = g_workers; 5869f51cf32Spaul luse 587b9218b7aSpaul luse printf("\nCore Transfers Bandwidth Failed Miscompares\n"); 588b9218b7aSpaul luse printf("-----------------------------------------------------------------\n"); 5899f51cf32Spaul luse while (worker != NULL) { 5909f51cf32Spaul luse 5919f51cf32Spaul luse uint64_t xfer_per_sec = worker->xfer_completed / g_time_in_sec; 5929f51cf32Spaul luse uint64_t bw_in_MiBps = (worker->xfer_completed * g_xfer_size_bytes) / 5939f51cf32Spaul luse (g_time_in_sec * 1024 * 1024); 5949f51cf32Spaul luse 5959f51cf32Spaul luse total_completed += worker->xfer_completed; 5969f51cf32Spaul luse total_failed += worker->xfer_failed; 597b9218b7aSpaul luse total_miscompared += worker->injected_miscompares; 5989f51cf32Spaul luse 5999f51cf32Spaul luse if (xfer_per_sec) { 600b9218b7aSpaul luse printf("%10d%12" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 " %11" PRIu64 "\n", 6019f51cf32Spaul luse worker->core, xfer_per_sec, 602b9218b7aSpaul luse bw_in_MiBps, worker->xfer_failed, worker->injected_miscompares); 6039f51cf32Spaul luse } 6049f51cf32Spaul luse 6059f51cf32Spaul luse worker = worker->next; 6069f51cf32Spaul luse } 6079f51cf32Spaul luse 6089f51cf32Spaul luse total_xfer_per_sec = total_completed / g_time_in_sec; 6099f51cf32Spaul luse total_bw_in_MiBps = (total_completed * g_xfer_size_bytes) / 6109f51cf32Spaul luse (g_time_in_sec * 1024 * 1024); 6119f51cf32Spaul luse 612b9218b7aSpaul luse printf("==================================================================\n"); 613b9218b7aSpaul luse printf("Total:%16" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 " %11" PRIu64"\n\n", 614b9218b7aSpaul luse total_xfer_per_sec, total_bw_in_MiBps, total_failed, total_miscompared); 6159f51cf32Spaul luse 6169f51cf32Spaul luse return total_failed ? 1 : 0; 6179f51cf32Spaul luse } 6189f51cf32Spaul luse 6199f51cf32Spaul luse static int 6209f51cf32Spaul luse _check_draining(void *arg) 6219f51cf32Spaul luse { 6229f51cf32Spaul luse struct worker_thread *worker = arg; 623ac9a1a83Spaul luse struct ap_task *task; 6249f51cf32Spaul luse 6259f51cf32Spaul luse assert(worker); 6269f51cf32Spaul luse 6279f51cf32Spaul luse if (worker->current_queue_depth == 0) { 628ac9a1a83Spaul luse while ((task = TAILQ_FIRST(&worker->tasks_pool))) { 629ac9a1a83Spaul luse TAILQ_REMOVE(&worker->tasks_pool, task, link); 630ac9a1a83Spaul luse _free_task(task); 631ac9a1a83Spaul luse } 6329f51cf32Spaul luse spdk_poller_unregister(&worker->is_draining_poller); 6339f51cf32Spaul luse unregister_worker(worker); 6349f51cf32Spaul luse } 6359f51cf32Spaul luse 6369f51cf32Spaul luse return -1; 6379f51cf32Spaul luse } 6389f51cf32Spaul luse 6399f51cf32Spaul luse static int 6409f51cf32Spaul luse _worker_stop(void *arg) 6419f51cf32Spaul luse { 6429f51cf32Spaul luse struct worker_thread *worker = arg; 6439f51cf32Spaul luse 6449f51cf32Spaul luse assert(worker); 6459f51cf32Spaul luse 6469f51cf32Spaul luse spdk_poller_unregister(&worker->stop_poller); 6479f51cf32Spaul luse 6489f51cf32Spaul luse /* now let the worker drain and check it's outstanding IO with a poller */ 6499f51cf32Spaul luse worker->is_draining = true; 650ab0bc5c2SShuhei Matsumoto worker->is_draining_poller = SPDK_POLLER_REGISTER(_check_draining, worker, 0); 6519f51cf32Spaul luse 6529f51cf32Spaul luse return 0; 6539f51cf32Spaul luse } 6549f51cf32Spaul luse 6559f51cf32Spaul luse static void 6569f51cf32Spaul luse _init_thread_done(void *ctx) 6579f51cf32Spaul luse { 6589f51cf32Spaul luse } 6599f51cf32Spaul luse 660a34fc12bSpaul luse static void 661a34fc12bSpaul luse _init_thread(void *arg1) 662a34fc12bSpaul luse { 663a34fc12bSpaul luse struct worker_thread *worker; 664a34fc12bSpaul luse struct ap_task *task; 665f17e6705Spaul luse int i, rc, num_batches; 666f17e6705Spaul luse int max_per_batch; 667a34fc12bSpaul luse int remaining = g_queue_depth; 668f17e6705Spaul luse int num_tasks = g_queue_depth; 669f17e6705Spaul luse struct accel_batch *tmp; 670f17e6705Spaul luse struct accel_batch *worker_batch = NULL; 671a34fc12bSpaul luse 672a34fc12bSpaul luse worker = calloc(1, sizeof(*worker)); 673a34fc12bSpaul luse if (worker == NULL) { 674a34fc12bSpaul luse fprintf(stderr, "Unable to allocate worker\n"); 675a34fc12bSpaul luse return; 676a34fc12bSpaul luse } 677a34fc12bSpaul luse 6789f51cf32Spaul luse worker->core = spdk_env_get_current_core(); 6799f51cf32Spaul luse worker->thread = spdk_get_thread(); 6809f51cf32Spaul luse worker->next = g_workers; 6819f51cf32Spaul luse worker->ch = spdk_accel_engine_get_io_channel(); 682b9218b7aSpaul luse 683f17e6705Spaul luse TAILQ_INIT(&worker->tasks_pool); 684f17e6705Spaul luse 685f17e6705Spaul luse if (g_ops_per_batch > 0) { 686f17e6705Spaul luse 6870cecfcb1Spaul luse max_per_batch = spdk_accel_batch_get_max(worker->ch); 6880cecfcb1Spaul luse assert(max_per_batch > 0); 6890cecfcb1Spaul luse 690f17e6705Spaul luse if (g_ops_per_batch > max_per_batch) { 691f17e6705Spaul luse fprintf(stderr, "Reducing requested batch amount to max supported of %d\n", max_per_batch); 692f17e6705Spaul luse g_ops_per_batch = max_per_batch; 693f17e6705Spaul luse } 694f17e6705Spaul luse 695f17e6705Spaul luse if (g_ops_per_batch > g_queue_depth) { 696f17e6705Spaul luse fprintf(stderr, "Batch amount > queue depth, resetting to %d\n", g_queue_depth); 697f17e6705Spaul luse g_ops_per_batch = g_queue_depth; 698f17e6705Spaul luse } 699f17e6705Spaul luse 700f17e6705Spaul luse TAILQ_INIT(&worker->in_prep_batches); 701f17e6705Spaul luse TAILQ_INIT(&worker->to_submit_batches); 702f17e6705Spaul luse TAILQ_INIT(&worker->in_use_batches); 703f17e6705Spaul luse 704f17e6705Spaul luse /* A worker_batch will live on one of 3 lists: 705f17e6705Spaul luse * IN_PREP: as individual IOs complete new ones are built on on a 706f17e6705Spaul luse * worker_batch on this list until it reaches g_ops_per_batch. 707f17e6705Spaul luse * TO_SUBMIT: as batches are built up on IO completion they are moved 708f17e6705Spaul luse * to this list once they are full. This list is used in 709f17e6705Spaul luse * batch completion to start new batches. 710f17e6705Spaul luse * IN_USE: the worker_batch is outstanding and will be moved to in prep 711f17e6705Spaul luse * list when the batch is completed. 712f17e6705Spaul luse * 713f17e6705Spaul luse * So we need enough to cover Q depth loading and then one to replace 714f17e6705Spaul luse * each one of those and for when everything is outstanding there needs 715f17e6705Spaul luse * to be one extra batch to build up while the last batch is completing 716f17e6705Spaul luse * IO but before it's completed the batch command. 717f17e6705Spaul luse */ 718f17e6705Spaul luse num_batches = (g_queue_depth / g_ops_per_batch * 2) + 1; 719f17e6705Spaul luse worker->batch_base = calloc(num_batches, sizeof(struct accel_batch)); 720f17e6705Spaul luse worker_batch = worker->batch_base; 721f17e6705Spaul luse for (i = 0; i < num_batches; i++) { 722f17e6705Spaul luse worker_batch->worker = worker; 723f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link); 724f17e6705Spaul luse worker_batch++; 725f17e6705Spaul luse } 726f17e6705Spaul luse } 727f17e6705Spaul luse 728ac9a1a83Spaul luse worker->task_base = calloc(num_tasks, sizeof(struct ap_task)); 729ac9a1a83Spaul luse if (worker->task_base == NULL) { 730ac9a1a83Spaul luse fprintf(stderr, "Could not allocate task base.\n"); 731ac9a1a83Spaul luse goto error; 7320cecfcb1Spaul luse } 733ac9a1a83Spaul luse 734ac9a1a83Spaul luse task = worker->task_base; 735ac9a1a83Spaul luse for (i = 0; i < num_tasks; i++) { 736ac9a1a83Spaul luse TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link); 737ac9a1a83Spaul luse if (_get_task_data_bufs(task)) { 738ac9a1a83Spaul luse fprintf(stderr, "Unable to get data bufs\n"); 739ac9a1a83Spaul luse goto error; 740ac9a1a83Spaul luse } 741ac9a1a83Spaul luse task++; 7429f51cf32Spaul luse } 7439f51cf32Spaul luse 7449f51cf32Spaul luse /* Register a poller that will stop the worker at time elapsed */ 745ab0bc5c2SShuhei Matsumoto worker->stop_poller = SPDK_POLLER_REGISTER(_worker_stop, worker, 7469f51cf32Spaul luse g_time_in_sec * 1000000ULL); 7479f51cf32Spaul luse 7489f51cf32Spaul luse g_workers = worker; 7499f51cf32Spaul luse pthread_mutex_lock(&g_workers_lock); 7509f51cf32Spaul luse g_num_workers++; 7519f51cf32Spaul luse pthread_mutex_unlock(&g_workers_lock); 7529f51cf32Spaul luse 753f17e6705Spaul luse /* If batching is enabled load up to the full Q depth before 754f17e6705Spaul luse * processing any completions, then ping pong between two batches, 755f17e6705Spaul luse * one processing and one being built up for when the other completes. 756a34fc12bSpaul luse */ 757f17e6705Spaul luse if (g_ops_per_batch > 0) { 758a34fc12bSpaul luse do { 759f17e6705Spaul luse worker_batch = TAILQ_FIRST(&worker->in_prep_batches); 760f17e6705Spaul luse if (worker_batch == NULL) { 761f17e6705Spaul luse goto error; 762f17e6705Spaul luse } 763f17e6705Spaul luse 764f17e6705Spaul luse worker_batch->batch = spdk_accel_batch_create(worker->ch); 765f17e6705Spaul luse if (worker_batch->batch == NULL) { 766f17e6705Spaul luse raise(SIGINT); 767a34fc12bSpaul luse break; 768a34fc12bSpaul luse } 769a34fc12bSpaul luse 770f17e6705Spaul luse for (i = 0; i < g_ops_per_batch; i++) { 771ac9a1a83Spaul luse task = _get_task(worker); 772ac9a1a83Spaul luse if (task == NULL) { 773a34fc12bSpaul luse goto error; 7749f51cf32Spaul luse } 775b9218b7aSpaul luse 776f17e6705Spaul luse rc = _batch_prep_cmd(worker, task, worker_batch); 777a34fc12bSpaul luse if (rc) { 778a34fc12bSpaul luse fprintf(stderr, "error preping command\n"); 779a34fc12bSpaul luse goto error; 780a34fc12bSpaul luse } 781a34fc12bSpaul luse } 782a34fc12bSpaul luse 783f17e6705Spaul luse /* for the batch operation itself. */ 784f17e6705Spaul luse task->worker->current_queue_depth++; 785f17e6705Spaul luse TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link); 786f17e6705Spaul luse TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link); 787f17e6705Spaul luse 788f17e6705Spaul luse rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch); 789a34fc12bSpaul luse if (rc) { 790f17e6705Spaul luse fprintf(stderr, "error ending batch\n"); 791a34fc12bSpaul luse goto error; 792a34fc12bSpaul luse } 793f17e6705Spaul luse assert(remaining >= g_ops_per_batch); 794f17e6705Spaul luse remaining -= g_ops_per_batch; 795f17e6705Spaul luse } while (remaining > 0); 796b9218b7aSpaul luse } 7970ef079c6Spaul luse 798f17e6705Spaul luse /* Submit as singles when no batching is enabled or we ran out of batches. */ 799a34fc12bSpaul luse for (i = 0; i < remaining; i++) { 800ac9a1a83Spaul luse task = _get_task(worker); 801ac9a1a83Spaul luse if (task == NULL) { 802a34fc12bSpaul luse goto error; 803b9218b7aSpaul luse } 804b9218b7aSpaul luse 8059f51cf32Spaul luse _submit_single(worker, task); 8069f51cf32Spaul luse } 807a34fc12bSpaul luse return; 808a34fc12bSpaul luse error: 809f17e6705Spaul luse if (worker_batch && worker_batch->batch) { 810f17e6705Spaul luse TAILQ_FOREACH_SAFE(worker_batch, &worker->in_use_batches, link, tmp) { 811f17e6705Spaul luse spdk_accel_batch_cancel(worker->ch, worker_batch->batch); 812f17e6705Spaul luse TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link); 813f17e6705Spaul luse } 814f17e6705Spaul luse } 815ac9a1a83Spaul luse while ((task = TAILQ_FIRST(&worker->tasks_pool))) { 816ac9a1a83Spaul luse TAILQ_REMOVE(&worker->tasks_pool, task, link); 817ac9a1a83Spaul luse _free_task(task); 8180cecfcb1Spaul luse } 819f17e6705Spaul luse free(worker->batch_base); 820ac9a1a83Spaul luse free(worker->task_base); 821a34fc12bSpaul luse free(worker); 822a34fc12bSpaul luse spdk_app_stop(-1); 8239f51cf32Spaul luse } 8249f51cf32Spaul luse 8259f51cf32Spaul luse static void 826e8463f87Spaul luse accel_done(void *cb_arg, int status) 8279f51cf32Spaul luse { 828e8463f87Spaul luse struct ap_task *task = (struct ap_task *)cb_arg; 8299f51cf32Spaul luse struct worker_thread *worker = task->worker; 8309f51cf32Spaul luse 8319f51cf32Spaul luse assert(worker); 8329f51cf32Spaul luse 833b9218b7aSpaul luse task->status = status; 8349f51cf32Spaul luse spdk_thread_send_msg(worker->thread, _accel_done, task); 8359f51cf32Spaul luse } 8369f51cf32Spaul luse 8379f51cf32Spaul luse static void 8389f51cf32Spaul luse accel_perf_start(void *arg1) 8399f51cf32Spaul luse { 840514be889Spaul luse struct spdk_io_channel *accel_ch; 841514be889Spaul luse 842514be889Spaul luse accel_ch = spdk_accel_engine_get_io_channel(); 843a34fc12bSpaul luse g_capabilites = spdk_accel_get_capabilities(accel_ch); 844514be889Spaul luse spdk_put_io_channel(accel_ch); 845514be889Spaul luse 846a34fc12bSpaul luse if ((g_capabilites & g_workload_selection) != g_workload_selection) { 847a7dfca5bSpaul luse SPDK_WARNLOG("The selected workload is not natively supported by the current engine\n"); 848a7dfca5bSpaul luse SPDK_WARNLOG("The software engine will be used instead.\n\n"); 849514be889Spaul luse } 850514be889Spaul luse 8519f51cf32Spaul luse g_tsc_rate = spdk_get_ticks_hz(); 8529f51cf32Spaul luse g_tsc_us_rate = g_tsc_rate / (1000 * 1000); 8539f51cf32Spaul luse g_tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate; 8549f51cf32Spaul luse 8559f51cf32Spaul luse printf("Running for %d seconds...\n", g_time_in_sec); 8569f51cf32Spaul luse fflush(stdout); 8579f51cf32Spaul luse 8589f51cf32Spaul luse spdk_for_each_thread(_init_thread, NULL, _init_thread_done); 8599f51cf32Spaul luse } 8609f51cf32Spaul luse 8619f51cf32Spaul luse int 8629f51cf32Spaul luse main(int argc, char **argv) 8639f51cf32Spaul luse { 8649f51cf32Spaul luse struct spdk_app_opts opts = {}; 8659f51cf32Spaul luse struct worker_thread *worker, *tmp; 8669f51cf32Spaul luse int rc = 0; 8679f51cf32Spaul luse 8689f51cf32Spaul luse pthread_mutex_init(&g_workers_lock, NULL); 86948701bd9SZiye Yang spdk_app_opts_init(&opts, sizeof(opts)); 8709f51cf32Spaul luse opts.reactor_mask = "0x1"; 871f17e6705Spaul luse if (spdk_app_parse_args(argc, argv, &opts, "o:q:t:yw:P:f:b:", NULL, parse_args, 8721e2b38baSyidong0635 usage) != SPDK_APP_PARSE_ARGS_SUCCESS) { 8739f51cf32Spaul luse rc = -1; 8749f51cf32Spaul luse goto cleanup; 8759f51cf32Spaul luse } 8769f51cf32Spaul luse 877b9218b7aSpaul luse if ((g_workload_selection != ACCEL_COPY) && 878b9218b7aSpaul luse (g_workload_selection != ACCEL_FILL) && 879b9218b7aSpaul luse (g_workload_selection != ACCEL_CRC32C) && 8800ef079c6Spaul luse (g_workload_selection != ACCEL_COMPARE) && 8810ef079c6Spaul luse (g_workload_selection != ACCEL_DUALCAST)) { 8822a0c66d0Spaul luse usage(); 8832a0c66d0Spaul luse rc = -1; 8842a0c66d0Spaul luse goto cleanup; 8852a0c66d0Spaul luse } 8862a0c66d0Spaul luse 887f17e6705Spaul luse if (g_ops_per_batch > 0 && (g_queue_depth % g_ops_per_batch > 0)) { 888f17e6705Spaul luse fprintf(stdout, "batch size must be a multiple of queue depth\n"); 889f17e6705Spaul luse usage(); 890f17e6705Spaul luse rc = -1; 891f17e6705Spaul luse goto cleanup; 892f17e6705Spaul luse } 893f17e6705Spaul luse 8949f51cf32Spaul luse dump_user_config(&opts); 8959f51cf32Spaul luse rc = spdk_app_start(&opts, accel_perf_start, NULL); 8969f51cf32Spaul luse if (rc) { 8979f51cf32Spaul luse SPDK_ERRLOG("ERROR starting application\n"); 8989f51cf32Spaul luse } else { 8999f51cf32Spaul luse dump_result(); 9009f51cf32Spaul luse } 9019f51cf32Spaul luse 9029f51cf32Spaul luse pthread_mutex_destroy(&g_workers_lock); 9039f51cf32Spaul luse 9049f51cf32Spaul luse worker = g_workers; 9059f51cf32Spaul luse while (worker) { 9069f51cf32Spaul luse tmp = worker->next; 9079f51cf32Spaul luse free(worker); 9089f51cf32Spaul luse worker = tmp; 9099f51cf32Spaul luse } 9109f51cf32Spaul luse cleanup: 9119f51cf32Spaul luse spdk_app_fini(); 9129f51cf32Spaul luse return rc; 9139f51cf32Spaul luse } 914