164b16edaSKonrad Sztyber /* SPDX-License-Identifier: BSD-3-Clause 264b16edaSKonrad Sztyber * Copyright (C) 2023 Intel Corporation. All rights reserved. 364b16edaSKonrad Sztyber */ 464b16edaSKonrad Sztyber 58625cc18SKonrad Sztyber #include "accel_error.h" 664b16edaSKonrad Sztyber #include "spdk/accel.h" 764b16edaSKonrad Sztyber #include "spdk/accel_module.h" 838a1f6bfSKonrad Sztyber #include "spdk/json.h" 964b16edaSKonrad Sztyber #include "spdk/thread.h" 1064b16edaSKonrad Sztyber 118625cc18SKonrad Sztyber struct accel_error_inject_info { 128625cc18SKonrad Sztyber /* Error injection options */ 138625cc18SKonrad Sztyber struct accel_error_inject_opts opts; 148625cc18SKonrad Sztyber /* Number of errors already injected on this channel */ 158625cc18SKonrad Sztyber uint64_t count; 16760cb023SKonrad Sztyber /* Number of operations executed since last error injection */ 17760cb023SKonrad Sztyber uint64_t interval; 188625cc18SKonrad Sztyber }; 198625cc18SKonrad Sztyber 200732c143SKonrad Sztyber struct accel_error_task; 210732c143SKonrad Sztyber 2264b16edaSKonrad Sztyber struct accel_error_channel { 2364b16edaSKonrad Sztyber struct spdk_io_channel *swch; 240732c143SKonrad Sztyber struct spdk_poller *poller; 258625cc18SKonrad Sztyber struct accel_error_inject_info injects[SPDK_ACCEL_OPC_LAST]; 260732c143SKonrad Sztyber STAILQ_HEAD(, accel_error_task) tasks; 2764b16edaSKonrad Sztyber }; 2864b16edaSKonrad Sztyber 2964b16edaSKonrad Sztyber struct accel_error_task { 30dce597cbSKonrad Sztyber struct accel_error_channel *ch; 31*b264e22fSKonrad Sztyber union { 32*b264e22fSKonrad Sztyber spdk_accel_completion_cb cpl; 33*b264e22fSKonrad Sztyber spdk_accel_step_cb step; 34*b264e22fSKonrad Sztyber } cb_fn; 3564b16edaSKonrad Sztyber void *cb_arg; 360732c143SKonrad Sztyber int status; 370732c143SKonrad Sztyber STAILQ_ENTRY(accel_error_task) link; 3864b16edaSKonrad Sztyber }; 3964b16edaSKonrad Sztyber 4064b16edaSKonrad Sztyber static struct spdk_accel_module_if *g_sw_module; 418625cc18SKonrad Sztyber static struct accel_error_inject_opts g_injects[SPDK_ACCEL_OPC_LAST]; 4264b16edaSKonrad Sztyber static size_t g_task_offset; 4364b16edaSKonrad Sztyber 4464b16edaSKonrad Sztyber static struct accel_error_task * 4564b16edaSKonrad Sztyber accel_error_get_task_ctx(struct spdk_accel_task *task) 4664b16edaSKonrad Sztyber { 4764b16edaSKonrad Sztyber return (void *)((uint8_t *)task + g_task_offset); 4864b16edaSKonrad Sztyber } 4964b16edaSKonrad Sztyber 500732c143SKonrad Sztyber static struct spdk_accel_task * 510732c143SKonrad Sztyber accel_error_get_task_from_ctx(struct accel_error_task *errtask) 520732c143SKonrad Sztyber { 530732c143SKonrad Sztyber return (void *)((uint8_t *)errtask - g_task_offset); 540732c143SKonrad Sztyber } 550732c143SKonrad Sztyber 5664b16edaSKonrad Sztyber static void 57dce597cbSKonrad Sztyber accel_error_corrupt_task(struct spdk_accel_task *task) 58dce597cbSKonrad Sztyber { 59dce597cbSKonrad Sztyber switch (task->op_code) { 60dce597cbSKonrad Sztyber case SPDK_ACCEL_OPC_CRC32C: 61dce597cbSKonrad Sztyber *task->crc_dst += 1; 62dce597cbSKonrad Sztyber break; 63dce597cbSKonrad Sztyber default: 64dce597cbSKonrad Sztyber break; 65dce597cbSKonrad Sztyber } 66dce597cbSKonrad Sztyber } 67dce597cbSKonrad Sztyber 68dce597cbSKonrad Sztyber static void 690732c143SKonrad Sztyber accel_error_corrupt_cb(void *arg, int status) 7064b16edaSKonrad Sztyber { 7164b16edaSKonrad Sztyber struct spdk_accel_task *task = arg; 7264b16edaSKonrad Sztyber struct accel_error_task *errtask = accel_error_get_task_ctx(task); 73*b264e22fSKonrad Sztyber spdk_accel_completion_cb cb_fn = errtask->cb_fn.cpl; 7464b16edaSKonrad Sztyber void *cb_arg = errtask->cb_arg; 7564b16edaSKonrad Sztyber 76dce597cbSKonrad Sztyber accel_error_corrupt_task(task); 7764b16edaSKonrad Sztyber cb_fn(cb_arg, status); 7864b16edaSKonrad Sztyber } 7964b16edaSKonrad Sztyber 80*b264e22fSKonrad Sztyber static void 81*b264e22fSKonrad Sztyber accel_error_corrupt_step_cb(void *arg) 82*b264e22fSKonrad Sztyber { 83*b264e22fSKonrad Sztyber struct spdk_accel_task *task = arg; 84*b264e22fSKonrad Sztyber struct accel_error_task *errtask = accel_error_get_task_ctx(task); 85*b264e22fSKonrad Sztyber spdk_accel_step_cb cb_fn = errtask->cb_fn.step; 86*b264e22fSKonrad Sztyber void *cb_arg = errtask->cb_arg; 87*b264e22fSKonrad Sztyber 88*b264e22fSKonrad Sztyber accel_error_corrupt_task(task); 89*b264e22fSKonrad Sztyber 90*b264e22fSKonrad Sztyber cb_fn(cb_arg); 91*b264e22fSKonrad Sztyber } 92*b264e22fSKonrad Sztyber 93b53b961cSKonrad Sztyber static bool 94b53b961cSKonrad Sztyber accel_error_should_inject(struct spdk_io_channel *ch, struct spdk_accel_task *task) 9564b16edaSKonrad Sztyber { 9664b16edaSKonrad Sztyber struct accel_error_channel *errch = spdk_io_channel_get_ctx(ch); 97c9f92cbfSKonrad Sztyber struct accel_error_inject_info *info = &errch->injects[task->op_code]; 9864b16edaSKonrad Sztyber 99c9f92cbfSKonrad Sztyber if (info->opts.type == ACCEL_ERROR_INJECT_DISABLE) { 100b53b961cSKonrad Sztyber return false; 101c9f92cbfSKonrad Sztyber } 102c9f92cbfSKonrad Sztyber 103c9f92cbfSKonrad Sztyber info->interval++; 104c9f92cbfSKonrad Sztyber if (info->interval >= info->opts.interval) { 105c9f92cbfSKonrad Sztyber info->interval = 0; 106c9f92cbfSKonrad Sztyber info->count++; 107c9f92cbfSKonrad Sztyber 108c9f92cbfSKonrad Sztyber if (info->count <= info->opts.count) { 109b53b961cSKonrad Sztyber return true; 110c9f92cbfSKonrad Sztyber } else { 111c9f92cbfSKonrad Sztyber info->opts.type = ACCEL_ERROR_INJECT_DISABLE; 112c9f92cbfSKonrad Sztyber info->interval = 0; 113c9f92cbfSKonrad Sztyber info->count = 0; 114c9f92cbfSKonrad Sztyber } 115c9f92cbfSKonrad Sztyber } 116b53b961cSKonrad Sztyber 117b53b961cSKonrad Sztyber return false; 118b53b961cSKonrad Sztyber } 119b53b961cSKonrad Sztyber 120b53b961cSKonrad Sztyber static int 121b53b961cSKonrad Sztyber accel_error_submit_tasks(struct spdk_io_channel *ch, struct spdk_accel_task *task) 122b53b961cSKonrad Sztyber { 123b53b961cSKonrad Sztyber struct accel_error_channel *errch = spdk_io_channel_get_ctx(ch); 124b53b961cSKonrad Sztyber struct accel_error_task *errtask = accel_error_get_task_ctx(task); 1250732c143SKonrad Sztyber struct accel_error_inject_info *info = &errch->injects[task->op_code]; 126b53b961cSKonrad Sztyber 127b53b961cSKonrad Sztyber if (!accel_error_should_inject(ch, task)) { 128b53b961cSKonrad Sztyber goto submit; 129b53b961cSKonrad Sztyber } 130b53b961cSKonrad Sztyber 1310732c143SKonrad Sztyber switch (info->opts.type) { 1320732c143SKonrad Sztyber case ACCEL_ERROR_INJECT_CORRUPT: 133b53b961cSKonrad Sztyber errtask->ch = errch; 134b53b961cSKonrad Sztyber errtask->cb_arg = task->cb_arg; 135b53b961cSKonrad Sztyber task->cb_arg = task; 136*b264e22fSKonrad Sztyber if (task->seq != NULL) { 137*b264e22fSKonrad Sztyber errtask->cb_fn.step = task->step_cb_fn; 138*b264e22fSKonrad Sztyber task->step_cb_fn = accel_error_corrupt_step_cb; 139*b264e22fSKonrad Sztyber } else { 140*b264e22fSKonrad Sztyber errtask->cb_fn.cpl = task->cb_fn; 141*b264e22fSKonrad Sztyber task->cb_fn = accel_error_corrupt_cb; 142*b264e22fSKonrad Sztyber } 1430732c143SKonrad Sztyber break; 1440732c143SKonrad Sztyber case ACCEL_ERROR_INJECT_FAILURE: 1450732c143SKonrad Sztyber errtask->status = info->opts.errcode; 1460732c143SKonrad Sztyber STAILQ_INSERT_TAIL(&errch->tasks, errtask, link); 1470732c143SKonrad Sztyber return 0; 1480732c143SKonrad Sztyber default: 1490732c143SKonrad Sztyber break; 1500732c143SKonrad Sztyber } 151c9f92cbfSKonrad Sztyber submit: 15264b16edaSKonrad Sztyber return g_sw_module->submit_tasks(errch->swch, task); 15364b16edaSKonrad Sztyber } 15464b16edaSKonrad Sztyber 1550732c143SKonrad Sztyber static int 1560732c143SKonrad Sztyber accel_error_poller(void *arg) 1570732c143SKonrad Sztyber { 1580732c143SKonrad Sztyber struct accel_error_channel *errch = arg; 1590732c143SKonrad Sztyber struct accel_error_task *errtask; 1600732c143SKonrad Sztyber STAILQ_HEAD(, accel_error_task) tasks; 1610732c143SKonrad Sztyber struct spdk_accel_task *task; 1620732c143SKonrad Sztyber 1630732c143SKonrad Sztyber if (STAILQ_EMPTY(&errch->tasks)) { 1640732c143SKonrad Sztyber return SPDK_POLLER_IDLE; 1650732c143SKonrad Sztyber } 1660732c143SKonrad Sztyber 1670732c143SKonrad Sztyber STAILQ_INIT(&tasks); 1680732c143SKonrad Sztyber STAILQ_SWAP(&tasks, &errch->tasks, accel_error_task); 1690732c143SKonrad Sztyber 1700732c143SKonrad Sztyber while (!STAILQ_EMPTY(&tasks)) { 1710732c143SKonrad Sztyber errtask = STAILQ_FIRST(&tasks); 1720732c143SKonrad Sztyber STAILQ_REMOVE_HEAD(&tasks, link); 1730732c143SKonrad Sztyber 1740732c143SKonrad Sztyber task = accel_error_get_task_from_ctx(errtask); 1750732c143SKonrad Sztyber spdk_accel_task_complete(task, errtask->status); 1760732c143SKonrad Sztyber } 1770732c143SKonrad Sztyber 1780732c143SKonrad Sztyber return SPDK_POLLER_BUSY; 1790732c143SKonrad Sztyber } 1800732c143SKonrad Sztyber 1818625cc18SKonrad Sztyber static void 1828625cc18SKonrad Sztyber accel_error_inject_channel(struct spdk_io_channel_iter *iter) 1838625cc18SKonrad Sztyber { 1848625cc18SKonrad Sztyber struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(iter); 1858625cc18SKonrad Sztyber struct accel_error_channel *errch = spdk_io_channel_get_ctx(ch); 1868625cc18SKonrad Sztyber struct accel_error_inject_opts *opts = spdk_io_channel_iter_get_ctx(iter); 1878625cc18SKonrad Sztyber struct accel_error_inject_info *info = &errch->injects[opts->opcode]; 1888625cc18SKonrad Sztyber 1898625cc18SKonrad Sztyber info->count = 0; 1908625cc18SKonrad Sztyber memcpy(&info->opts, opts, sizeof(info->opts)); 1918625cc18SKonrad Sztyber 1928625cc18SKonrad Sztyber spdk_for_each_channel_continue(iter, 0); 1938625cc18SKonrad Sztyber } 1948625cc18SKonrad Sztyber 1958625cc18SKonrad Sztyber static bool accel_error_supports_opcode(enum spdk_accel_opcode opcode); 1968625cc18SKonrad Sztyber 1978625cc18SKonrad Sztyber int 1988625cc18SKonrad Sztyber accel_error_inject_error(struct accel_error_inject_opts *opts) 1998625cc18SKonrad Sztyber { 2008625cc18SKonrad Sztyber struct accel_error_inject_opts *curr = &g_injects[opts->opcode]; 2018625cc18SKonrad Sztyber 2028625cc18SKonrad Sztyber if (!accel_error_supports_opcode(opts->opcode)) { 2038625cc18SKonrad Sztyber return -EINVAL; 2048625cc18SKonrad Sztyber } 2058625cc18SKonrad Sztyber 2068625cc18SKonrad Sztyber memcpy(curr, opts, sizeof(*opts)); 2078625cc18SKonrad Sztyber if (curr->type == ACCEL_ERROR_INJECT_DISABLE) { 2088625cc18SKonrad Sztyber curr->count = 0; 2098625cc18SKonrad Sztyber } 2108625cc18SKonrad Sztyber if (curr->count == 0) { 2118625cc18SKonrad Sztyber curr->type = ACCEL_ERROR_INJECT_DISABLE; 2128625cc18SKonrad Sztyber } 2138625cc18SKonrad Sztyber 2148625cc18SKonrad Sztyber spdk_for_each_channel(&g_sw_module, accel_error_inject_channel, curr, NULL); 2158625cc18SKonrad Sztyber 2168625cc18SKonrad Sztyber return 0; 2178625cc18SKonrad Sztyber } 2188625cc18SKonrad Sztyber 21964b16edaSKonrad Sztyber static int 22064b16edaSKonrad Sztyber accel_error_channel_create_cb(void *io_device, void *ctx) 22164b16edaSKonrad Sztyber { 22264b16edaSKonrad Sztyber struct accel_error_channel *errch = ctx; 2238625cc18SKonrad Sztyber size_t i; 22464b16edaSKonrad Sztyber 2250732c143SKonrad Sztyber STAILQ_INIT(&errch->tasks); 2260732c143SKonrad Sztyber errch->poller = SPDK_POLLER_REGISTER(accel_error_poller, errch, 0); 2270732c143SKonrad Sztyber if (errch->poller == NULL) { 2280732c143SKonrad Sztyber return -ENOMEM; 2290732c143SKonrad Sztyber } 2300732c143SKonrad Sztyber 23164b16edaSKonrad Sztyber errch->swch = g_sw_module->get_io_channel(); 23264b16edaSKonrad Sztyber if (errch->swch == NULL) { 2330732c143SKonrad Sztyber spdk_poller_unregister(&errch->poller); 23464b16edaSKonrad Sztyber return -ENOMEM; 23564b16edaSKonrad Sztyber } 23664b16edaSKonrad Sztyber 2378625cc18SKonrad Sztyber for (i = 0; i < SPDK_COUNTOF(errch->injects); ++i) { 2388625cc18SKonrad Sztyber memcpy(&errch->injects[i].opts, &g_injects[i], sizeof(g_injects[i])); 2398625cc18SKonrad Sztyber errch->injects[i].count = 0; 2408625cc18SKonrad Sztyber } 2418625cc18SKonrad Sztyber 24264b16edaSKonrad Sztyber return 0; 24364b16edaSKonrad Sztyber } 24464b16edaSKonrad Sztyber 24564b16edaSKonrad Sztyber static void 24664b16edaSKonrad Sztyber accel_error_channel_destroy_cb(void *io_device, void *ctx) 24764b16edaSKonrad Sztyber { 24864b16edaSKonrad Sztyber struct accel_error_channel *errch = ctx; 24964b16edaSKonrad Sztyber 2500732c143SKonrad Sztyber assert(STAILQ_EMPTY(&errch->tasks)); 2510732c143SKonrad Sztyber spdk_poller_unregister(&errch->poller); 25264b16edaSKonrad Sztyber spdk_put_io_channel(errch->swch); 25364b16edaSKonrad Sztyber } 25464b16edaSKonrad Sztyber 25564b16edaSKonrad Sztyber static int 25664b16edaSKonrad Sztyber accel_error_module_init(void) 25764b16edaSKonrad Sztyber { 25864b16edaSKonrad Sztyber g_sw_module = spdk_accel_get_module("software"); 25964b16edaSKonrad Sztyber if (g_sw_module == NULL) { 26064b16edaSKonrad Sztyber /* Should never really happen */ 2611f88c365STomasz Zawadzki return -ENOTSUP; 26264b16edaSKonrad Sztyber } 26364b16edaSKonrad Sztyber 26464b16edaSKonrad Sztyber g_task_offset = g_sw_module->get_ctx_size(); 26564b16edaSKonrad Sztyber 26664b16edaSKonrad Sztyber spdk_io_device_register(&g_sw_module, accel_error_channel_create_cb, 26764b16edaSKonrad Sztyber accel_error_channel_destroy_cb, 26864b16edaSKonrad Sztyber sizeof(struct accel_error_channel), "accel_error"); 26964b16edaSKonrad Sztyber 27064b16edaSKonrad Sztyber return 0; 27164b16edaSKonrad Sztyber } 27264b16edaSKonrad Sztyber 27364b16edaSKonrad Sztyber static void 27464b16edaSKonrad Sztyber accel_error_unregister_cb(void *unused) 27564b16edaSKonrad Sztyber { 27664b16edaSKonrad Sztyber spdk_accel_module_finish(); 27764b16edaSKonrad Sztyber } 27864b16edaSKonrad Sztyber 27964b16edaSKonrad Sztyber static void 28064b16edaSKonrad Sztyber accel_error_module_fini(void *unused) 28164b16edaSKonrad Sztyber { 28264b16edaSKonrad Sztyber spdk_io_device_unregister(&g_sw_module, accel_error_unregister_cb); 28364b16edaSKonrad Sztyber } 28464b16edaSKonrad Sztyber 28564b16edaSKonrad Sztyber static bool 28664b16edaSKonrad Sztyber accel_error_supports_opcode(enum spdk_accel_opcode opcode) 28764b16edaSKonrad Sztyber { 288dce597cbSKonrad Sztyber switch (opcode) { 289dce597cbSKonrad Sztyber case SPDK_ACCEL_OPC_CRC32C: 290dce597cbSKonrad Sztyber return true; 291dce597cbSKonrad Sztyber default: 29264b16edaSKonrad Sztyber return false; 29364b16edaSKonrad Sztyber } 294dce597cbSKonrad Sztyber } 29564b16edaSKonrad Sztyber 29664b16edaSKonrad Sztyber static struct spdk_io_channel * 29764b16edaSKonrad Sztyber accel_error_get_io_channel(void) 29864b16edaSKonrad Sztyber { 29964b16edaSKonrad Sztyber return spdk_get_io_channel(&g_sw_module); 30064b16edaSKonrad Sztyber } 30164b16edaSKonrad Sztyber 30264b16edaSKonrad Sztyber static size_t 30364b16edaSKonrad Sztyber accel_error_get_ctx_size(void) 30464b16edaSKonrad Sztyber { 30564b16edaSKonrad Sztyber return g_task_offset + sizeof(struct accel_error_task); 30664b16edaSKonrad Sztyber } 30764b16edaSKonrad Sztyber 3088625cc18SKonrad Sztyber const char * 3098625cc18SKonrad Sztyber accel_error_get_type_name(enum accel_error_inject_type type) 3108625cc18SKonrad Sztyber { 3118625cc18SKonrad Sztyber const char *typenames[] = { 3128625cc18SKonrad Sztyber [ACCEL_ERROR_INJECT_DISABLE] = "disable", 3138625cc18SKonrad Sztyber [ACCEL_ERROR_INJECT_CORRUPT] = "corrupt", 314bb019612SKonrad Sztyber [ACCEL_ERROR_INJECT_FAILURE] = "failure", 3158625cc18SKonrad Sztyber [ACCEL_ERROR_INJECT_MAX] = NULL 3168625cc18SKonrad Sztyber }; 3178625cc18SKonrad Sztyber 3188625cc18SKonrad Sztyber if ((int)type >= ACCEL_ERROR_INJECT_MAX) { 3198625cc18SKonrad Sztyber return NULL; 3208625cc18SKonrad Sztyber } 3218625cc18SKonrad Sztyber 3228625cc18SKonrad Sztyber return typenames[type]; 3238625cc18SKonrad Sztyber } 3248625cc18SKonrad Sztyber 32538a1f6bfSKonrad Sztyber static void 32638a1f6bfSKonrad Sztyber accel_error_write_config_json(struct spdk_json_write_ctx *w) 32738a1f6bfSKonrad Sztyber { 32838a1f6bfSKonrad Sztyber struct accel_error_inject_opts *opts; 32938a1f6bfSKonrad Sztyber int opcode; 33038a1f6bfSKonrad Sztyber 33138a1f6bfSKonrad Sztyber for (opcode = 0; opcode < SPDK_ACCEL_OPC_LAST; ++opcode) { 33238a1f6bfSKonrad Sztyber opts = &g_injects[opcode]; 33338a1f6bfSKonrad Sztyber if (opts->type == ACCEL_ERROR_INJECT_DISABLE) { 33438a1f6bfSKonrad Sztyber continue; 33538a1f6bfSKonrad Sztyber } 33638a1f6bfSKonrad Sztyber spdk_json_write_object_begin(w); 33738a1f6bfSKonrad Sztyber spdk_json_write_named_string(w, "method", "accel_error_inject_error"); 33838a1f6bfSKonrad Sztyber spdk_json_write_named_object_begin(w, "params"); 33938a1f6bfSKonrad Sztyber spdk_json_write_named_string(w, "opcode", spdk_accel_get_opcode_name(opcode)); 34038a1f6bfSKonrad Sztyber spdk_json_write_named_string(w, "type", accel_error_get_type_name(opts->type)); 34138a1f6bfSKonrad Sztyber spdk_json_write_named_uint64(w, "count", opts->count); 342760cb023SKonrad Sztyber spdk_json_write_named_uint64(w, "interval", opts->interval); 34338a1f6bfSKonrad Sztyber spdk_json_write_object_end(w); 34438a1f6bfSKonrad Sztyber spdk_json_write_object_end(w); 34538a1f6bfSKonrad Sztyber } 34638a1f6bfSKonrad Sztyber } 34738a1f6bfSKonrad Sztyber 34864b16edaSKonrad Sztyber static struct spdk_accel_module_if g_accel_error_module = { 34964b16edaSKonrad Sztyber .name = "error", 35064b16edaSKonrad Sztyber .priority = INT_MIN, 35164b16edaSKonrad Sztyber .module_init = accel_error_module_init, 35264b16edaSKonrad Sztyber .module_fini = accel_error_module_fini, 35364b16edaSKonrad Sztyber .supports_opcode = accel_error_supports_opcode, 35464b16edaSKonrad Sztyber .get_ctx_size = accel_error_get_ctx_size, 35564b16edaSKonrad Sztyber .get_io_channel = accel_error_get_io_channel, 35664b16edaSKonrad Sztyber .submit_tasks = accel_error_submit_tasks, 35738a1f6bfSKonrad Sztyber .write_config_json = accel_error_write_config_json, 35864b16edaSKonrad Sztyber }; 35964b16edaSKonrad Sztyber SPDK_ACCEL_MODULE_REGISTER(error, &g_accel_error_module) 360