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