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" 42*0cecfcb1Spaul 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*0cecfcb1Spaul luse struct ap_task; 649f51cf32Spaul luse 659f51cf32Spaul luse struct worker_thread { 669f51cf32Spaul luse struct spdk_io_channel *ch; 679f51cf32Spaul luse uint64_t xfer_completed; 689f51cf32Spaul luse uint64_t xfer_failed; 69b9218b7aSpaul luse uint64_t injected_miscompares; 709f51cf32Spaul luse uint64_t current_queue_depth; 71*0cecfcb1Spaul luse TAILQ_HEAD(, ap_task) tasks; 729f51cf32Spaul luse struct worker_thread *next; 739f51cf32Spaul luse unsigned core; 749f51cf32Spaul luse struct spdk_thread *thread; 759f51cf32Spaul luse bool is_draining; 769f51cf32Spaul luse struct spdk_poller *is_draining_poller; 779f51cf32Spaul luse struct spdk_poller *stop_poller; 789f51cf32Spaul luse }; 799f51cf32Spaul luse 809f51cf32Spaul luse struct ap_task { 819f51cf32Spaul luse void *src; 829f51cf32Spaul luse void *dst; 830ef079c6Spaul luse void *dst2; 849f51cf32Spaul luse struct worker_thread *worker; 85b9218b7aSpaul luse int status; 86b9218b7aSpaul luse int expected_status; /* used for compare */ 87*0cecfcb1Spaul luse TAILQ_ENTRY(ap_task) link; 889f51cf32Spaul luse }; 899f51cf32Spaul luse 909f51cf32Spaul luse static void 919f51cf32Spaul luse dump_user_config(struct spdk_app_opts *opts) 929f51cf32Spaul luse { 939f51cf32Spaul luse printf("SPDK Configuration:\n"); 949f51cf32Spaul luse printf("Core mask: %s\n\n", opts->reactor_mask); 959f51cf32Spaul luse printf("Accel Perf Configuration:\n"); 962a0c66d0Spaul luse printf("Workload Type: %s\n", g_workload_type); 97b9218b7aSpaul luse if (g_workload_selection == ACCEL_CRC32C) { 98b9218b7aSpaul luse printf("CRC-32C seed: %u\n", g_crc32c_seed); 9989495464Spaul luse } else if (g_workload_selection == ACCEL_FILL) { 10089495464Spaul luse printf("Fill pattern: 0x%x\n", g_fill_pattern); 101b9218b7aSpaul luse } else if ((g_workload_selection == ACCEL_COMPARE) && g_fail_percent_goal > 0) { 10289495464Spaul luse printf("Failure inject: %u percent\n", g_fail_percent_goal); 103e69375bfSpaul luse } 1049f51cf32Spaul luse printf("Transfer size: %u bytes\n", g_xfer_size_bytes); 1059f51cf32Spaul luse printf("Queue depth: %u\n", g_queue_depth); 1069f51cf32Spaul luse printf("Run time: %u seconds\n", g_time_in_sec); 1079f51cf32Spaul luse printf("Verify: %s\n\n", g_verify ? "Yes" : "No"); 1089f51cf32Spaul luse } 1099f51cf32Spaul luse 1109f51cf32Spaul luse static void 1119f51cf32Spaul luse usage(void) 1129f51cf32Spaul luse { 1139f51cf32Spaul luse printf("accel_perf options:\n"); 1149f51cf32Spaul luse printf("\t[-h help message]\n"); 1159f51cf32Spaul luse printf("\t[-q queue depth]\n"); 1169f51cf32Spaul luse printf("\t[-n number of channels]\n"); 1179f51cf32Spaul luse printf("\t[-o transfer size in bytes]\n"); 1189f51cf32Spaul luse printf("\t[-t time in seconds]\n"); 1190ef079c6Spaul luse printf("\t[-w workload type must be one of these: copy, fill, crc32c, compare, dualcast\n"); 120e69375bfSpaul luse printf("\t[-s for crc32c workload, use this seed value (default 0)\n"); 121b9218b7aSpaul luse printf("\t[-P for compare workload, percentage of operations that should miscompare (percent, default 0)\n"); 12289495464Spaul luse printf("\t[-f for fill workload, use this BYTE value (default 255)\n"); 1232a0c66d0Spaul luse printf("\t[-y verify result if this switch is on]\n"); 1249f51cf32Spaul luse } 1259f51cf32Spaul luse 1269f51cf32Spaul luse static int 1279f51cf32Spaul luse parse_args(int argc, char *argv) 1289f51cf32Spaul luse { 1299f51cf32Spaul luse switch (argc) { 13089495464Spaul luse case 'f': 13189495464Spaul luse g_fill_pattern = (uint8_t)spdk_strtol(optarg, 10); 13289495464Spaul luse break; 1339f51cf32Spaul luse case 'o': 1349f51cf32Spaul luse g_xfer_size_bytes = spdk_strtol(optarg, 10); 1359f51cf32Spaul luse break; 136b9218b7aSpaul luse case 'P': 137b9218b7aSpaul luse g_fail_percent_goal = spdk_strtol(optarg, 10); 138b9218b7aSpaul luse break; 1399f51cf32Spaul luse case 'q': 1409f51cf32Spaul luse g_queue_depth = spdk_strtol(optarg, 10); 1419f51cf32Spaul luse break; 142e69375bfSpaul luse case 's': 143e69375bfSpaul luse g_crc32c_seed = spdk_strtol(optarg, 10); 144e69375bfSpaul luse break; 1459f51cf32Spaul luse case 't': 1469f51cf32Spaul luse g_time_in_sec = spdk_strtol(optarg, 10); 1479f51cf32Spaul luse break; 1489f51cf32Spaul luse case 'y': 1499f51cf32Spaul luse g_verify = true; 1509f51cf32Spaul luse break; 1512a0c66d0Spaul luse case 'w': 1522a0c66d0Spaul luse g_workload_type = optarg; 153514be889Spaul luse if (!strcmp(g_workload_type, "copy")) { 154514be889Spaul luse g_workload_selection = ACCEL_COPY; 155514be889Spaul luse } else if (!strcmp(g_workload_type, "fill")) { 156514be889Spaul luse g_workload_selection = ACCEL_FILL; 157e69375bfSpaul luse } else if (!strcmp(g_workload_type, "crc32c")) { 158e69375bfSpaul luse g_workload_selection = ACCEL_CRC32C; 159b9218b7aSpaul luse } else if (!strcmp(g_workload_type, "compare")) { 160b9218b7aSpaul luse g_workload_selection = ACCEL_COMPARE; 1610ef079c6Spaul luse } else if (!strcmp(g_workload_type, "dualcast")) { 1620ef079c6Spaul luse g_workload_selection = ACCEL_DUALCAST; 163514be889Spaul luse } 1642a0c66d0Spaul luse break; 1659f51cf32Spaul luse default: 1669f51cf32Spaul luse usage(); 1679f51cf32Spaul luse return 1; 1689f51cf32Spaul luse } 1699f51cf32Spaul luse return 0; 1709f51cf32Spaul luse } 1719f51cf32Spaul luse 1729f51cf32Spaul luse static void 1739f51cf32Spaul luse unregister_worker(void *arg1) 1749f51cf32Spaul luse { 1759f51cf32Spaul luse struct worker_thread *worker = arg1; 176*0cecfcb1Spaul luse struct ap_task *task; 1779f51cf32Spaul luse 178*0cecfcb1Spaul luse while (!TAILQ_EMPTY(&worker->tasks)) { 179*0cecfcb1Spaul luse task = TAILQ_FIRST(&worker->tasks); 180*0cecfcb1Spaul luse TAILQ_REMOVE(&worker->tasks, task, link); 181*0cecfcb1Spaul luse free(task); 182*0cecfcb1Spaul luse } 1839f51cf32Spaul luse spdk_put_io_channel(worker->ch); 1849f51cf32Spaul luse pthread_mutex_lock(&g_workers_lock); 1859f51cf32Spaul luse assert(g_num_workers >= 1); 1869f51cf32Spaul luse if (--g_num_workers == 0) { 1879f51cf32Spaul luse pthread_mutex_unlock(&g_workers_lock); 1889f51cf32Spaul luse spdk_app_stop(0); 1899f51cf32Spaul luse } 1909f51cf32Spaul luse pthread_mutex_unlock(&g_workers_lock); 1919f51cf32Spaul luse } 1929f51cf32Spaul luse 1939f51cf32Spaul luse static void accel_done(void *ref, int status); 1949f51cf32Spaul luse 1959f51cf32Spaul luse static void 1969f51cf32Spaul luse _submit_single(void *arg1, void *arg2) 1979f51cf32Spaul luse { 1989f51cf32Spaul luse struct worker_thread *worker = arg1; 1999f51cf32Spaul luse struct ap_task *task = arg2; 200b9218b7aSpaul luse int random_num; 20140ec8e97Spaul luse int rc = 0; 2029f51cf32Spaul luse 2039f51cf32Spaul luse assert(worker); 2049f51cf32Spaul luse 2059f51cf32Spaul luse task->worker = worker; 2069f51cf32Spaul luse task->worker->current_queue_depth++; 207e69375bfSpaul luse switch (g_workload_selection) { 208e69375bfSpaul luse case ACCEL_COPY: 209e8463f87Spaul luse rc = spdk_accel_submit_copy(worker->ch, task->dst, task->src, 210e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 211e69375bfSpaul luse break; 212e69375bfSpaul luse case ACCEL_FILL: 2132a0c66d0Spaul luse /* For fill use the first byte of the task->dst buffer */ 214ee7e31f9Spaul luse rc = spdk_accel_submit_fill(worker->ch, task->dst, *(uint8_t *)task->src, 215e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 216e69375bfSpaul luse break; 217e69375bfSpaul luse case ACCEL_CRC32C: 218e8463f87Spaul luse rc = spdk_accel_submit_crc32c(worker->ch, (uint32_t *)task->dst, 219e8463f87Spaul luse task->src, g_crc32c_seed, 220e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 221e69375bfSpaul luse break; 222b9218b7aSpaul luse case ACCEL_COMPARE: 223b9218b7aSpaul luse random_num = rand() % 100; 224b9218b7aSpaul luse if (random_num < g_fail_percent_goal) { 225b9218b7aSpaul luse task->expected_status = -EILSEQ; 226b9218b7aSpaul luse *(uint8_t *)task->dst = ~DATA_PATTERN; 227b9218b7aSpaul luse } else { 228b9218b7aSpaul luse task->expected_status = 0; 229b9218b7aSpaul luse *(uint8_t *)task->dst = DATA_PATTERN; 230b9218b7aSpaul luse } 231ee7e31f9Spaul luse rc = spdk_accel_submit_compare(worker->ch, task->dst, task->src, 232e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 233b9218b7aSpaul luse break; 2340ef079c6Spaul luse case ACCEL_DUALCAST: 235ee7e31f9Spaul luse rc = spdk_accel_submit_dualcast(worker->ch, task->dst, task->dst2, 236e8463f87Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 2370ef079c6Spaul luse break; 238e69375bfSpaul luse default: 2392a0c66d0Spaul luse assert(false); 240e69375bfSpaul luse break; 241e69375bfSpaul luse 2422a0c66d0Spaul luse } 24340ec8e97Spaul luse 24440ec8e97Spaul luse if (rc) { 245e8463f87Spaul luse accel_done(task, rc); 24640ec8e97Spaul luse } 2479f51cf32Spaul luse } 2489f51cf32Spaul luse 2499f51cf32Spaul luse static void 2509f51cf32Spaul luse _accel_done(void *arg1) 2519f51cf32Spaul luse { 2529f51cf32Spaul luse struct ap_task *task = arg1; 2539f51cf32Spaul luse struct worker_thread *worker = task->worker; 254e69375bfSpaul luse uint32_t sw_crc32c; 2559f51cf32Spaul luse 2569f51cf32Spaul luse assert(worker); 2579f51cf32Spaul luse assert(worker->current_queue_depth > 0); 2589f51cf32Spaul luse 259b9218b7aSpaul luse if (g_verify && task->status == 0) { 260b9218b7aSpaul luse switch (g_workload_selection) { 261b9218b7aSpaul luse case ACCEL_CRC32C: 262e69375bfSpaul luse /* calculate sw CRC-32C and compare to sw aceel result. */ 263e69375bfSpaul luse sw_crc32c = spdk_crc32c_update(task->src, g_xfer_size_bytes, ~g_crc32c_seed); 264e69375bfSpaul luse if (*(uint32_t *)task->dst != sw_crc32c) { 265e69375bfSpaul luse SPDK_NOTICELOG("CRC-32C miscompare\n"); 266e69375bfSpaul luse worker->xfer_failed++; 267e69375bfSpaul luse } 268b9218b7aSpaul luse break; 269b9218b7aSpaul luse case ACCEL_COPY: 270b9218b7aSpaul luse if (memcmp(task->src, task->dst, g_xfer_size_bytes)) { 2719f51cf32Spaul luse SPDK_NOTICELOG("Data miscompare\n"); 2729f51cf32Spaul luse worker->xfer_failed++; 273b9218b7aSpaul luse } 274b9218b7aSpaul luse break; 2750ef079c6Spaul luse case ACCEL_DUALCAST: 2760ef079c6Spaul luse if (memcmp(task->src, task->dst, g_xfer_size_bytes)) { 2770ef079c6Spaul luse SPDK_NOTICELOG("Data miscompare, first destination\n"); 2780ef079c6Spaul luse worker->xfer_failed++; 2790ef079c6Spaul luse } 2800ef079c6Spaul luse if (memcmp(task->src, task->dst2, g_xfer_size_bytes)) { 2810ef079c6Spaul luse SPDK_NOTICELOG("Data miscompare, second destination\n"); 2820ef079c6Spaul luse worker->xfer_failed++; 2830ef079c6Spaul luse } 2840ef079c6Spaul luse break; 285d207237fSpaul luse case ACCEL_FILL: 286d207237fSpaul luse if (memcmp(task->dst, task->src, g_xfer_size_bytes)) { 287d207237fSpaul luse SPDK_NOTICELOG("Data miscompare\n"); 288d207237fSpaul luse worker->xfer_failed++; 289d207237fSpaul luse } 290d207237fSpaul luse break; 2918cee297cSpaul luse case ACCEL_COMPARE: 2928cee297cSpaul luse break; 293b9218b7aSpaul luse default: 294b9218b7aSpaul luse assert(false); 295b9218b7aSpaul luse break; 2969f51cf32Spaul luse } 2979f51cf32Spaul luse } 298b9218b7aSpaul luse 299b9218b7aSpaul luse if (task->expected_status == -EILSEQ) { 300b9218b7aSpaul luse assert(task->status != 0); 301b9218b7aSpaul luse worker->injected_miscompares++; 302b9218b7aSpaul luse } else if (task->status) { 303b9218b7aSpaul luse /* Expected to pass but API reported error. */ 304b9218b7aSpaul luse worker->xfer_failed++; 305b9218b7aSpaul luse } 306b9218b7aSpaul luse 3079f51cf32Spaul luse worker->xfer_completed++; 3089f51cf32Spaul luse worker->current_queue_depth--; 3099f51cf32Spaul luse 31040ec8e97Spaul luse if (!worker->is_draining) { 3119f51cf32Spaul luse _submit_single(worker, task); 3129f51cf32Spaul luse } else { 313b9218b7aSpaul luse spdk_free(task->src); 314b9218b7aSpaul luse spdk_free(task->dst); 3150ef079c6Spaul luse if (g_workload_selection == ACCEL_DUALCAST) { 3160ef079c6Spaul luse spdk_free(task->dst2); 3170ef079c6Spaul luse } 318*0cecfcb1Spaul luse TAILQ_INSERT_TAIL(&worker->tasks, task, link); 3199f51cf32Spaul luse } 3209f51cf32Spaul luse } 3219f51cf32Spaul luse 322a34fc12bSpaul luse static void 323e8463f87Spaul luse batch_done(void *cb_arg, int status) 324a34fc12bSpaul luse { 325e8463f87Spaul luse struct ap_task *task = (struct ap_task *)cb_arg; 326a34fc12bSpaul luse struct worker_thread *worker = task->worker; 327a34fc12bSpaul luse 328a34fc12bSpaul luse worker->current_queue_depth--; 329*0cecfcb1Spaul luse TAILQ_INSERT_TAIL(&worker->tasks, task, link); 330a34fc12bSpaul luse } 331a34fc12bSpaul luse 3329f51cf32Spaul luse static int 3339f51cf32Spaul luse dump_result(void) 3349f51cf32Spaul luse { 3359f51cf32Spaul luse uint64_t total_completed = 0; 3369f51cf32Spaul luse uint64_t total_failed = 0; 337b9218b7aSpaul luse uint64_t total_miscompared = 0; 3389f51cf32Spaul luse uint64_t total_xfer_per_sec, total_bw_in_MiBps; 3399f51cf32Spaul luse struct worker_thread *worker = g_workers; 3409f51cf32Spaul luse 341b9218b7aSpaul luse printf("\nCore Transfers Bandwidth Failed Miscompares\n"); 342b9218b7aSpaul luse printf("-----------------------------------------------------------------\n"); 3439f51cf32Spaul luse while (worker != NULL) { 3449f51cf32Spaul luse 3459f51cf32Spaul luse uint64_t xfer_per_sec = worker->xfer_completed / g_time_in_sec; 3469f51cf32Spaul luse uint64_t bw_in_MiBps = (worker->xfer_completed * g_xfer_size_bytes) / 3479f51cf32Spaul luse (g_time_in_sec * 1024 * 1024); 3489f51cf32Spaul luse 3499f51cf32Spaul luse total_completed += worker->xfer_completed; 3509f51cf32Spaul luse total_failed += worker->xfer_failed; 351b9218b7aSpaul luse total_miscompared += worker->injected_miscompares; 3529f51cf32Spaul luse 3539f51cf32Spaul luse if (xfer_per_sec) { 354b9218b7aSpaul luse printf("%10d%12" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 " %11" PRIu64 "\n", 3559f51cf32Spaul luse worker->core, xfer_per_sec, 356b9218b7aSpaul luse bw_in_MiBps, worker->xfer_failed, worker->injected_miscompares); 3579f51cf32Spaul luse } 3589f51cf32Spaul luse 3599f51cf32Spaul luse worker = worker->next; 3609f51cf32Spaul luse } 3619f51cf32Spaul luse 3629f51cf32Spaul luse total_xfer_per_sec = total_completed / g_time_in_sec; 3639f51cf32Spaul luse total_bw_in_MiBps = (total_completed * g_xfer_size_bytes) / 3649f51cf32Spaul luse (g_time_in_sec * 1024 * 1024); 3659f51cf32Spaul luse 366b9218b7aSpaul luse printf("==================================================================\n"); 367b9218b7aSpaul luse printf("Total:%16" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 " %11" PRIu64"\n\n", 368b9218b7aSpaul luse total_xfer_per_sec, total_bw_in_MiBps, total_failed, total_miscompared); 3699f51cf32Spaul luse 3709f51cf32Spaul luse return total_failed ? 1 : 0; 3719f51cf32Spaul luse } 3729f51cf32Spaul luse 3739f51cf32Spaul luse static int 3749f51cf32Spaul luse _check_draining(void *arg) 3759f51cf32Spaul luse { 3769f51cf32Spaul luse struct worker_thread *worker = arg; 3779f51cf32Spaul luse 3789f51cf32Spaul luse assert(worker); 3799f51cf32Spaul luse 3809f51cf32Spaul luse if (worker->current_queue_depth == 0) { 3819f51cf32Spaul luse spdk_poller_unregister(&worker->is_draining_poller); 3829f51cf32Spaul luse unregister_worker(worker); 3839f51cf32Spaul luse } 3849f51cf32Spaul luse 3859f51cf32Spaul luse return -1; 3869f51cf32Spaul luse } 3879f51cf32Spaul luse 3889f51cf32Spaul luse static int 3899f51cf32Spaul luse _worker_stop(void *arg) 3909f51cf32Spaul luse { 3919f51cf32Spaul luse struct worker_thread *worker = arg; 3929f51cf32Spaul luse 3939f51cf32Spaul luse assert(worker); 3949f51cf32Spaul luse 3959f51cf32Spaul luse spdk_poller_unregister(&worker->stop_poller); 3969f51cf32Spaul luse 3979f51cf32Spaul luse /* now let the worker drain and check it's outstanding IO with a poller */ 3989f51cf32Spaul luse worker->is_draining = true; 399ab0bc5c2SShuhei Matsumoto worker->is_draining_poller = SPDK_POLLER_REGISTER(_check_draining, worker, 0); 4009f51cf32Spaul luse 4019f51cf32Spaul luse return 0; 4029f51cf32Spaul luse } 4039f51cf32Spaul luse 4049f51cf32Spaul luse static void 4059f51cf32Spaul luse _init_thread_done(void *ctx) 4069f51cf32Spaul luse { 4079f51cf32Spaul luse } 4089f51cf32Spaul luse 409a34fc12bSpaul luse static int 410a34fc12bSpaul luse _get_task_data_bufs(struct ap_task *task) 4119f51cf32Spaul luse { 4120ef079c6Spaul luse uint32_t align = 0; 4139f51cf32Spaul luse 4140ef079c6Spaul luse /* For dualcast, the DSA HW requires 4K alignment on destination addresses but 4150ef079c6Spaul luse * we do this for all engines to keep it simple. 4160ef079c6Spaul luse */ 4170ef079c6Spaul luse if (g_workload_selection == ACCEL_DUALCAST) { 4180ef079c6Spaul luse align = ALIGN_4K; 4190ef079c6Spaul luse } 4200ef079c6Spaul luse 421a34fc12bSpaul luse task->src = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL); 422a34fc12bSpaul luse if (task->src == NULL) { 423a34fc12bSpaul luse fprintf(stderr, "Unable to alloc src buffer\n"); 424a34fc12bSpaul luse return -ENOMEM; 425a34fc12bSpaul luse } 426a34fc12bSpaul luse memset(task->src, DATA_PATTERN, g_xfer_size_bytes); 427a34fc12bSpaul luse 428a34fc12bSpaul luse task->dst = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL); 429a34fc12bSpaul luse if (task->dst == NULL) { 430a34fc12bSpaul luse fprintf(stderr, "Unable to alloc dst buffer\n"); 431a34fc12bSpaul luse return -ENOMEM; 432a34fc12bSpaul luse } 433a34fc12bSpaul luse 434a34fc12bSpaul luse /* For compare we want the buffers to match, otherwise not. */ 435a34fc12bSpaul luse if (g_workload_selection == ACCEL_COMPARE) { 436a34fc12bSpaul luse memset(task->dst, DATA_PATTERN, g_xfer_size_bytes); 437a34fc12bSpaul luse } else { 438a34fc12bSpaul luse memset(task->dst, ~DATA_PATTERN, g_xfer_size_bytes); 439a34fc12bSpaul luse } 440a34fc12bSpaul luse 441d207237fSpaul luse /* For fill, set the entire src buffer so we can check if verify is enabled. */ 442d207237fSpaul luse if (g_workload_selection == ACCEL_FILL) { 443d207237fSpaul luse memset(task->src, g_fill_pattern, g_xfer_size_bytes); 444d207237fSpaul luse } 445d207237fSpaul luse 446a34fc12bSpaul luse if (g_workload_selection == ACCEL_DUALCAST) { 447a34fc12bSpaul luse task->dst2 = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL); 448a34fc12bSpaul luse if (task->dst2 == NULL) { 449a34fc12bSpaul luse fprintf(stderr, "Unable to alloc dst buffer\n"); 450a34fc12bSpaul luse return -ENOMEM; 451a34fc12bSpaul luse } 452a34fc12bSpaul luse memset(task->dst2, ~DATA_PATTERN, g_xfer_size_bytes); 453a34fc12bSpaul luse } 454a34fc12bSpaul luse 455a34fc12bSpaul luse return 0; 456a34fc12bSpaul luse } 457a34fc12bSpaul luse 458a34fc12bSpaul luse static int 459a34fc12bSpaul luse _batch_prep_cmd(struct worker_thread *worker, struct ap_task *task, struct spdk_accel_batch *batch) 460a34fc12bSpaul luse { 461a34fc12bSpaul luse int rc = 0; 462a34fc12bSpaul luse 463a34fc12bSpaul luse switch (g_workload_selection) { 464a34fc12bSpaul luse case ACCEL_COPY: 465ee7e31f9Spaul luse rc = spdk_accel_batch_prep_copy(worker->ch, batch, task->dst, 466e8463f87Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 467a34fc12bSpaul luse break; 468ec086e6fSpaul luse case ACCEL_DUALCAST: 469ee7e31f9Spaul luse rc = spdk_accel_batch_prep_dualcast(worker->ch, batch, task->dst, task->dst2, 470e8463f87Spaul luse task->src, g_xfer_size_bytes, accel_done, task); 471ec086e6fSpaul luse break; 472d137ba30Spaul luse case ACCEL_COMPARE: 473ee7e31f9Spaul luse rc = spdk_accel_batch_prep_compare(worker->ch, batch, task->dst, task->src, 474e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 475d137ba30Spaul luse break; 476d207237fSpaul luse case ACCEL_FILL: 477e8463f87Spaul luse rc = spdk_accel_batch_prep_fill(worker->ch, batch, task->dst, 478e8463f87Spaul luse *(uint8_t *)task->src, 479e8463f87Spaul luse g_xfer_size_bytes, accel_done, task); 480d207237fSpaul luse break; 481e54f14a5Spaul luse case ACCEL_CRC32C: 482e8463f87Spaul luse rc = spdk_accel_batch_prep_crc32c(worker->ch, batch, (uint32_t *)task->dst, 483e8463f87Spaul luse task->src, g_crc32c_seed, g_xfer_size_bytes, accel_done, task); 484e54f14a5Spaul luse break; 485a34fc12bSpaul luse default: 486a34fc12bSpaul luse assert(false); 487a34fc12bSpaul luse break; 488a34fc12bSpaul luse } 489a34fc12bSpaul luse 490a34fc12bSpaul luse return rc; 491a34fc12bSpaul luse } 492a34fc12bSpaul luse 493a34fc12bSpaul luse static void 494a34fc12bSpaul luse _init_thread(void *arg1) 495a34fc12bSpaul luse { 496a34fc12bSpaul luse struct worker_thread *worker; 497a34fc12bSpaul luse struct ap_task *task; 498*0cecfcb1Spaul luse int i, rc, max_per_batch, batch_count, num_tasks; 499a34fc12bSpaul luse int remaining = g_queue_depth; 500a34fc12bSpaul luse struct spdk_accel_batch *batch, *new_batch; 501a34fc12bSpaul luse 502a34fc12bSpaul luse worker = calloc(1, sizeof(*worker)); 503a34fc12bSpaul luse if (worker == NULL) { 504a34fc12bSpaul luse fprintf(stderr, "Unable to allocate worker\n"); 505a34fc12bSpaul luse return; 506a34fc12bSpaul luse } 507a34fc12bSpaul luse 5089f51cf32Spaul luse worker->core = spdk_env_get_current_core(); 5099f51cf32Spaul luse worker->thread = spdk_get_thread(); 5109f51cf32Spaul luse worker->next = g_workers; 5119f51cf32Spaul luse worker->ch = spdk_accel_engine_get_io_channel(); 512b9218b7aSpaul luse 513*0cecfcb1Spaul luse max_per_batch = spdk_accel_batch_get_max(worker->ch); 514*0cecfcb1Spaul luse assert(max_per_batch > 0); 515*0cecfcb1Spaul luse num_tasks = g_queue_depth + spdk_divide_round_up(g_queue_depth, max_per_batch); 516*0cecfcb1Spaul luse 517*0cecfcb1Spaul luse TAILQ_INIT(&worker->tasks); 518*0cecfcb1Spaul luse for (i = 0; i < num_tasks; i++) { 519*0cecfcb1Spaul luse task = calloc(1, sizeof(struct ap_task)); 520*0cecfcb1Spaul luse if (task == NULL) { 521*0cecfcb1Spaul luse fprintf(stderr, "Could not allocate task.\n"); 5229f51cf32Spaul luse return; 523*0cecfcb1Spaul luse /* TODO cleanup */ 524*0cecfcb1Spaul luse } 525*0cecfcb1Spaul luse TAILQ_INSERT_TAIL(&worker->tasks, task, link); 5269f51cf32Spaul luse } 5279f51cf32Spaul luse 5289f51cf32Spaul luse /* Register a poller that will stop the worker at time elapsed */ 529ab0bc5c2SShuhei Matsumoto worker->stop_poller = SPDK_POLLER_REGISTER(_worker_stop, worker, 5309f51cf32Spaul luse g_time_in_sec * 1000000ULL); 5319f51cf32Spaul luse 5329f51cf32Spaul luse g_workers = worker; 5339f51cf32Spaul luse pthread_mutex_lock(&g_workers_lock); 5349f51cf32Spaul luse g_num_workers++; 5359f51cf32Spaul luse pthread_mutex_unlock(&g_workers_lock); 5369f51cf32Spaul luse 537f295f5b3Spaul luse /* Batching is only possible if there is at least 2 operations. */ 538f295f5b3Spaul luse if (g_queue_depth > 1) { 539a34fc12bSpaul luse 540a34fc12bSpaul luse /* Outter loop sets up each batch command, inner loop populates the 541a34fc12bSpaul luse * batch descriptors. 542a34fc12bSpaul luse */ 543a34fc12bSpaul luse do { 544a34fc12bSpaul luse new_batch = spdk_accel_batch_create(worker->ch); 545a34fc12bSpaul luse if (new_batch == NULL) { 546a34fc12bSpaul luse break; 547a34fc12bSpaul luse } 548a34fc12bSpaul luse 549a34fc12bSpaul luse batch = new_batch; 550a34fc12bSpaul luse batch_count = 0; 551a34fc12bSpaul luse 552a34fc12bSpaul luse do { 553*0cecfcb1Spaul luse if (!TAILQ_EMPTY(&worker->tasks)) { 554*0cecfcb1Spaul luse task = TAILQ_FIRST(&worker->tasks); 555*0cecfcb1Spaul luse TAILQ_REMOVE(&worker->tasks, task, link); 556*0cecfcb1Spaul luse } else { 5579f51cf32Spaul luse fprintf(stderr, "Unable to get accel_task\n"); 558a34fc12bSpaul luse goto error; 559a34fc12bSpaul luse } 560a34fc12bSpaul luse task->worker = worker; 561a34fc12bSpaul luse task->worker->current_queue_depth++; 562a34fc12bSpaul luse 563a34fc12bSpaul luse if (_get_task_data_bufs(task)) { 564a34fc12bSpaul luse fprintf(stderr, "Unable to get data bufs\n"); 565a34fc12bSpaul luse goto error; 5669f51cf32Spaul luse } 567b9218b7aSpaul luse 568a34fc12bSpaul luse rc = _batch_prep_cmd(worker, task, batch); 569a34fc12bSpaul luse if (rc) { 570a34fc12bSpaul luse fprintf(stderr, "error preping command\n"); 571a34fc12bSpaul luse goto error; 572a34fc12bSpaul luse } 573a34fc12bSpaul luse remaining--; 574a34fc12bSpaul luse batch_count++; 575a34fc12bSpaul luse } while (batch_count < max_per_batch && remaining > 0); 576a34fc12bSpaul luse 577a34fc12bSpaul luse /* Now send the batch command. */ 578*0cecfcb1Spaul luse if (!TAILQ_EMPTY(&worker->tasks)) { 579*0cecfcb1Spaul luse task = TAILQ_FIRST(&worker->tasks); 580*0cecfcb1Spaul luse TAILQ_REMOVE(&worker->tasks, task, link); 581*0cecfcb1Spaul luse } else { 582a34fc12bSpaul luse fprintf(stderr, "Unable to get accel_task\n"); 583a34fc12bSpaul luse goto error; 584a34fc12bSpaul luse } 585a34fc12bSpaul luse task->worker = worker; 586a34fc12bSpaul luse task->worker->current_queue_depth++; 587a34fc12bSpaul luse 588e8463f87Spaul luse rc = spdk_accel_batch_submit(worker->ch, batch, batch_done, task); 589a34fc12bSpaul luse if (rc) { 590a34fc12bSpaul luse fprintf(stderr, "error ending batch %d\n", rc); 591a34fc12bSpaul luse goto error; 592a34fc12bSpaul luse } 593a34fc12bSpaul luse /* We can't build a batch unless it has 2 descriptors (per spec). */ 594a34fc12bSpaul luse } while (remaining > 1); 595a34fc12bSpaul luse 596a34fc12bSpaul luse /* If there are no more left, we're done. */ 597a34fc12bSpaul luse if (remaining == 0) { 598b9218b7aSpaul luse return; 599b9218b7aSpaul luse } 600b9218b7aSpaul luse } 6010ef079c6Spaul luse 602a34fc12bSpaul luse /* For engines that don't support batch or for the odd event that 603a34fc12bSpaul luse * a batch ends with only one descriptor left. 604a34fc12bSpaul luse */ 605a34fc12bSpaul luse for (i = 0; i < remaining; i++) { 606a34fc12bSpaul luse 607*0cecfcb1Spaul luse if (!TAILQ_EMPTY(&worker->tasks)) { 608*0cecfcb1Spaul luse task = TAILQ_FIRST(&worker->tasks); 609*0cecfcb1Spaul luse TAILQ_REMOVE(&worker->tasks, task, link); 610*0cecfcb1Spaul luse } else { 611a34fc12bSpaul luse fprintf(stderr, "Unable to get accel_task\n"); 612a34fc12bSpaul luse goto error; 6130ef079c6Spaul luse } 6140ef079c6Spaul luse 615a34fc12bSpaul luse if (_get_task_data_bufs(task)) { 616a34fc12bSpaul luse fprintf(stderr, "Unable to get data bufs\n"); 617a34fc12bSpaul luse goto error; 618b9218b7aSpaul luse } 619b9218b7aSpaul luse 6209f51cf32Spaul luse _submit_single(worker, task); 6219f51cf32Spaul luse } 622a34fc12bSpaul luse return; 623a34fc12bSpaul luse error: 624a34fc12bSpaul luse /* TODO clean exit */ 625a34fc12bSpaul luse raise(SIGINT); 626*0cecfcb1Spaul luse while (!TAILQ_EMPTY(&worker->tasks)) { 627*0cecfcb1Spaul luse task = TAILQ_FIRST(&worker->tasks); 628*0cecfcb1Spaul luse TAILQ_REMOVE(&worker->tasks, task, link); 629*0cecfcb1Spaul luse free(task); 630*0cecfcb1Spaul luse } 631a34fc12bSpaul luse free(worker); 632a34fc12bSpaul luse spdk_app_stop(-1); 6339f51cf32Spaul luse } 6349f51cf32Spaul luse 6359f51cf32Spaul luse static void 636e8463f87Spaul luse accel_done(void *cb_arg, int status) 6379f51cf32Spaul luse { 638e8463f87Spaul luse struct ap_task *task = (struct ap_task *)cb_arg; 6399f51cf32Spaul luse struct worker_thread *worker = task->worker; 6409f51cf32Spaul luse 6419f51cf32Spaul luse assert(worker); 6429f51cf32Spaul luse 643b9218b7aSpaul luse task->status = status; 6449f51cf32Spaul luse spdk_thread_send_msg(worker->thread, _accel_done, task); 6459f51cf32Spaul luse } 6469f51cf32Spaul luse 6479f51cf32Spaul luse static void 6489f51cf32Spaul luse accel_perf_start(void *arg1) 6499f51cf32Spaul luse { 650514be889Spaul luse struct spdk_io_channel *accel_ch; 651514be889Spaul luse 652514be889Spaul luse accel_ch = spdk_accel_engine_get_io_channel(); 653a34fc12bSpaul luse g_capabilites = spdk_accel_get_capabilities(accel_ch); 654514be889Spaul luse spdk_put_io_channel(accel_ch); 655514be889Spaul luse 656a34fc12bSpaul luse if ((g_capabilites & g_workload_selection) != g_workload_selection) { 657a7dfca5bSpaul luse SPDK_WARNLOG("The selected workload is not natively supported by the current engine\n"); 658a7dfca5bSpaul luse SPDK_WARNLOG("The software engine will be used instead.\n\n"); 659514be889Spaul luse } 660514be889Spaul luse 6619f51cf32Spaul luse g_tsc_rate = spdk_get_ticks_hz(); 6629f51cf32Spaul luse g_tsc_us_rate = g_tsc_rate / (1000 * 1000); 6639f51cf32Spaul luse g_tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate; 6649f51cf32Spaul luse 6659f51cf32Spaul luse printf("Running for %d seconds...\n", g_time_in_sec); 6669f51cf32Spaul luse fflush(stdout); 6679f51cf32Spaul luse 6689f51cf32Spaul luse spdk_for_each_thread(_init_thread, NULL, _init_thread_done); 6699f51cf32Spaul luse } 6709f51cf32Spaul luse 6719f51cf32Spaul luse int 6729f51cf32Spaul luse main(int argc, char **argv) 6739f51cf32Spaul luse { 6749f51cf32Spaul luse struct spdk_app_opts opts = {}; 6759f51cf32Spaul luse struct worker_thread *worker, *tmp; 6769f51cf32Spaul luse int rc = 0; 6779f51cf32Spaul luse 6789f51cf32Spaul luse pthread_mutex_init(&g_workers_lock, NULL); 6799f51cf32Spaul luse spdk_app_opts_init(&opts); 6809f51cf32Spaul luse opts.reactor_mask = "0x1"; 68189495464Spaul luse if ((rc = spdk_app_parse_args(argc, argv, &opts, "o:q:t:yw:P:f:", NULL, parse_args, 6829f51cf32Spaul luse usage)) != SPDK_APP_PARSE_ARGS_SUCCESS) { 6839f51cf32Spaul luse rc = -1; 6849f51cf32Spaul luse goto cleanup; 6859f51cf32Spaul luse } 6869f51cf32Spaul luse 687b9218b7aSpaul luse if ((g_workload_selection != ACCEL_COPY) && 688b9218b7aSpaul luse (g_workload_selection != ACCEL_FILL) && 689b9218b7aSpaul luse (g_workload_selection != ACCEL_CRC32C) && 6900ef079c6Spaul luse (g_workload_selection != ACCEL_COMPARE) && 6910ef079c6Spaul luse (g_workload_selection != ACCEL_DUALCAST)) { 6922a0c66d0Spaul luse usage(); 6932a0c66d0Spaul luse rc = -1; 6942a0c66d0Spaul luse goto cleanup; 6952a0c66d0Spaul luse } 6962a0c66d0Spaul luse 6979f51cf32Spaul luse dump_user_config(&opts); 6989f51cf32Spaul luse rc = spdk_app_start(&opts, accel_perf_start, NULL); 6999f51cf32Spaul luse if (rc) { 7009f51cf32Spaul luse SPDK_ERRLOG("ERROR starting application\n"); 7019f51cf32Spaul luse } else { 7029f51cf32Spaul luse dump_result(); 7039f51cf32Spaul luse } 7049f51cf32Spaul luse 7059f51cf32Spaul luse pthread_mutex_destroy(&g_workers_lock); 7069f51cf32Spaul luse 7079f51cf32Spaul luse worker = g_workers; 7089f51cf32Spaul luse while (worker) { 7099f51cf32Spaul luse tmp = worker->next; 7109f51cf32Spaul luse free(worker); 7119f51cf32Spaul luse worker = tmp; 7129f51cf32Spaul luse } 7139f51cf32Spaul luse cleanup: 7149f51cf32Spaul luse spdk_app_fini(); 7159f51cf32Spaul luse return rc; 7169f51cf32Spaul luse } 717