1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2023 Intel Corporation. All rights reserved. 3 */ 4 5 #include "accel_error.h" 6 #include "spdk/accel.h" 7 #include "spdk/accel_module.h" 8 #include "spdk/json.h" 9 #include "spdk/thread.h" 10 11 struct accel_error_inject_info { 12 /* Error injection options */ 13 struct accel_error_inject_opts opts; 14 /* Number of errors already injected on this channel */ 15 uint64_t count; 16 /* Number of operations executed since last error injection */ 17 uint64_t interval; 18 }; 19 20 struct accel_error_channel { 21 struct spdk_io_channel *swch; 22 struct accel_error_inject_info injects[SPDK_ACCEL_OPC_LAST]; 23 }; 24 25 struct accel_error_task { 26 struct accel_error_channel *ch; 27 spdk_accel_completion_cb cb_fn; 28 void *cb_arg; 29 }; 30 31 static struct spdk_accel_module_if *g_sw_module; 32 static struct accel_error_inject_opts g_injects[SPDK_ACCEL_OPC_LAST]; 33 static size_t g_task_offset; 34 35 static struct accel_error_task * 36 accel_error_get_task_ctx(struct spdk_accel_task *task) 37 { 38 return (void *)((uint8_t *)task + g_task_offset); 39 } 40 41 static void 42 accel_error_corrupt_task(struct spdk_accel_task *task) 43 { 44 switch (task->op_code) { 45 case SPDK_ACCEL_OPC_CRC32C: 46 *task->crc_dst += 1; 47 break; 48 default: 49 break; 50 } 51 } 52 53 static void 54 accel_error_task_complete_cb(void *arg, int status) 55 { 56 struct spdk_accel_task *task = arg; 57 struct accel_error_task *errtask = accel_error_get_task_ctx(task); 58 struct accel_error_channel *ch = errtask->ch; 59 struct accel_error_inject_info *info = &ch->injects[task->op_code]; 60 spdk_accel_completion_cb cb_fn = errtask->cb_fn; 61 void *cb_arg = errtask->cb_arg; 62 63 info->interval++; 64 if (info->interval >= info->opts.interval) { 65 info->interval = 0; 66 info->count++; 67 68 if (info->count <= info->opts.count) { 69 switch (info->opts.type) { 70 case ACCEL_ERROR_INJECT_CORRUPT: 71 accel_error_corrupt_task(task); 72 break; 73 case ACCEL_ERROR_INJECT_FAILURE: 74 status = info->opts.errcode; 75 break; 76 default: 77 break; 78 } 79 } else { 80 info->opts.type = ACCEL_ERROR_INJECT_DISABLE; 81 } 82 } 83 84 cb_fn(cb_arg, status); 85 } 86 87 static int 88 accel_error_submit_tasks(struct spdk_io_channel *ch, struct spdk_accel_task *task) 89 { 90 struct accel_error_channel *errch = spdk_io_channel_get_ctx(ch); 91 struct accel_error_task *errtask = accel_error_get_task_ctx(task); 92 93 errtask->ch = errch; 94 errtask->cb_fn = task->cb_fn; 95 errtask->cb_arg = task->cb_arg; 96 task->cb_fn = accel_error_task_complete_cb; 97 task->cb_arg = task; 98 99 return g_sw_module->submit_tasks(errch->swch, task); 100 } 101 102 static void 103 accel_error_inject_channel(struct spdk_io_channel_iter *iter) 104 { 105 struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(iter); 106 struct accel_error_channel *errch = spdk_io_channel_get_ctx(ch); 107 struct accel_error_inject_opts *opts = spdk_io_channel_iter_get_ctx(iter); 108 struct accel_error_inject_info *info = &errch->injects[opts->opcode]; 109 110 info->count = 0; 111 memcpy(&info->opts, opts, sizeof(info->opts)); 112 113 spdk_for_each_channel_continue(iter, 0); 114 } 115 116 static bool accel_error_supports_opcode(enum spdk_accel_opcode opcode); 117 118 int 119 accel_error_inject_error(struct accel_error_inject_opts *opts) 120 { 121 struct accel_error_inject_opts *curr = &g_injects[opts->opcode]; 122 123 if (!accel_error_supports_opcode(opts->opcode)) { 124 return -EINVAL; 125 } 126 127 memcpy(curr, opts, sizeof(*opts)); 128 if (curr->type == ACCEL_ERROR_INJECT_DISABLE) { 129 curr->count = 0; 130 } 131 if (curr->count == 0) { 132 curr->type = ACCEL_ERROR_INJECT_DISABLE; 133 } 134 135 spdk_for_each_channel(&g_sw_module, accel_error_inject_channel, curr, NULL); 136 137 return 0; 138 } 139 140 static int 141 accel_error_channel_create_cb(void *io_device, void *ctx) 142 { 143 struct accel_error_channel *errch = ctx; 144 size_t i; 145 146 errch->swch = g_sw_module->get_io_channel(); 147 if (errch->swch == NULL) { 148 return -ENOMEM; 149 } 150 151 for (i = 0; i < SPDK_COUNTOF(errch->injects); ++i) { 152 memcpy(&errch->injects[i].opts, &g_injects[i], sizeof(g_injects[i])); 153 errch->injects[i].count = 0; 154 } 155 156 return 0; 157 } 158 159 static void 160 accel_error_channel_destroy_cb(void *io_device, void *ctx) 161 { 162 struct accel_error_channel *errch = ctx; 163 164 spdk_put_io_channel(errch->swch); 165 } 166 167 static int 168 accel_error_module_init(void) 169 { 170 g_sw_module = spdk_accel_get_module("software"); 171 if (g_sw_module == NULL) { 172 /* Should never really happen */ 173 return -ENODEV; 174 } 175 176 g_task_offset = g_sw_module->get_ctx_size(); 177 178 spdk_io_device_register(&g_sw_module, accel_error_channel_create_cb, 179 accel_error_channel_destroy_cb, 180 sizeof(struct accel_error_channel), "accel_error"); 181 182 return 0; 183 } 184 185 static void 186 accel_error_unregister_cb(void *unused) 187 { 188 spdk_accel_module_finish(); 189 } 190 191 static void 192 accel_error_module_fini(void *unused) 193 { 194 spdk_io_device_unregister(&g_sw_module, accel_error_unregister_cb); 195 } 196 197 static bool 198 accel_error_supports_opcode(enum spdk_accel_opcode opcode) 199 { 200 switch (opcode) { 201 case SPDK_ACCEL_OPC_CRC32C: 202 return true; 203 default: 204 return false; 205 } 206 } 207 208 static struct spdk_io_channel * 209 accel_error_get_io_channel(void) 210 { 211 return spdk_get_io_channel(&g_sw_module); 212 } 213 214 static size_t 215 accel_error_get_ctx_size(void) 216 { 217 return g_task_offset + sizeof(struct accel_error_task); 218 } 219 220 const char * 221 accel_error_get_type_name(enum accel_error_inject_type type) 222 { 223 const char *typenames[] = { 224 [ACCEL_ERROR_INJECT_DISABLE] = "disable", 225 [ACCEL_ERROR_INJECT_CORRUPT] = "corrupt", 226 [ACCEL_ERROR_INJECT_FAILURE] = "failure", 227 [ACCEL_ERROR_INJECT_MAX] = NULL 228 }; 229 230 if ((int)type >= ACCEL_ERROR_INJECT_MAX) { 231 return NULL; 232 } 233 234 return typenames[type]; 235 } 236 237 static void 238 accel_error_write_config_json(struct spdk_json_write_ctx *w) 239 { 240 struct accel_error_inject_opts *opts; 241 int opcode; 242 243 for (opcode = 0; opcode < SPDK_ACCEL_OPC_LAST; ++opcode) { 244 opts = &g_injects[opcode]; 245 if (opts->type == ACCEL_ERROR_INJECT_DISABLE) { 246 continue; 247 } 248 spdk_json_write_object_begin(w); 249 spdk_json_write_named_string(w, "method", "accel_error_inject_error"); 250 spdk_json_write_named_object_begin(w, "params"); 251 spdk_json_write_named_string(w, "opcode", spdk_accel_get_opcode_name(opcode)); 252 spdk_json_write_named_string(w, "type", accel_error_get_type_name(opts->type)); 253 spdk_json_write_named_uint64(w, "count", opts->count); 254 spdk_json_write_named_uint64(w, "interval", opts->interval); 255 spdk_json_write_object_end(w); 256 spdk_json_write_object_end(w); 257 } 258 } 259 260 static struct spdk_accel_module_if g_accel_error_module = { 261 .name = "error", 262 .priority = INT_MIN, 263 .module_init = accel_error_module_init, 264 .module_fini = accel_error_module_fini, 265 .supports_opcode = accel_error_supports_opcode, 266 .get_ctx_size = accel_error_get_ctx_size, 267 .get_io_channel = accel_error_get_io_channel, 268 .submit_tasks = accel_error_submit_tasks, 269 .write_config_json = accel_error_write_config_json, 270 }; 271 SPDK_ACCEL_MODULE_REGISTER(error, &g_accel_error_module) 272