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_task; 21 22 struct accel_error_channel { 23 struct spdk_io_channel *swch; 24 struct spdk_poller *poller; 25 struct accel_error_inject_info injects[SPDK_ACCEL_OPC_LAST]; 26 STAILQ_HEAD(, accel_error_task) tasks; 27 }; 28 29 struct accel_error_task { 30 struct accel_error_channel *ch; 31 union { 32 spdk_accel_completion_cb cpl; 33 spdk_accel_step_cb step; 34 } cb_fn; 35 void *cb_arg; 36 int status; 37 STAILQ_ENTRY(accel_error_task) link; 38 }; 39 40 static struct spdk_accel_module_if *g_sw_module; 41 static struct accel_error_inject_opts g_injects[SPDK_ACCEL_OPC_LAST]; 42 static size_t g_task_offset; 43 44 static struct accel_error_task * 45 accel_error_get_task_ctx(struct spdk_accel_task *task) 46 { 47 return (void *)((uint8_t *)task + g_task_offset); 48 } 49 50 static struct spdk_accel_task * 51 accel_error_get_task_from_ctx(struct accel_error_task *errtask) 52 { 53 return (void *)((uint8_t *)errtask - g_task_offset); 54 } 55 56 static void 57 accel_error_corrupt_task(struct spdk_accel_task *task) 58 { 59 switch (task->op_code) { 60 case SPDK_ACCEL_OPC_CRC32C: 61 *task->crc_dst += 1; 62 break; 63 default: 64 break; 65 } 66 } 67 68 static void 69 accel_error_corrupt_cb(void *arg, int status) 70 { 71 struct spdk_accel_task *task = arg; 72 struct accel_error_task *errtask = accel_error_get_task_ctx(task); 73 spdk_accel_completion_cb cb_fn = errtask->cb_fn.cpl; 74 void *cb_arg = errtask->cb_arg; 75 76 accel_error_corrupt_task(task); 77 cb_fn(cb_arg, status); 78 } 79 80 static void 81 accel_error_corrupt_step_cb(void *arg) 82 { 83 struct spdk_accel_task *task = arg; 84 struct accel_error_task *errtask = accel_error_get_task_ctx(task); 85 spdk_accel_step_cb cb_fn = errtask->cb_fn.step; 86 void *cb_arg = errtask->cb_arg; 87 88 accel_error_corrupt_task(task); 89 90 cb_fn(cb_arg); 91 } 92 93 static bool 94 accel_error_should_inject(struct spdk_io_channel *ch, struct spdk_accel_task *task) 95 { 96 struct accel_error_channel *errch = spdk_io_channel_get_ctx(ch); 97 struct accel_error_inject_info *info = &errch->injects[task->op_code]; 98 99 if (info->opts.type == ACCEL_ERROR_INJECT_DISABLE) { 100 return false; 101 } 102 103 info->interval++; 104 if (info->interval >= info->opts.interval) { 105 info->interval = 0; 106 info->count++; 107 108 if (info->count <= info->opts.count) { 109 return true; 110 } else { 111 info->opts.type = ACCEL_ERROR_INJECT_DISABLE; 112 info->interval = 0; 113 info->count = 0; 114 } 115 } 116 117 return false; 118 } 119 120 static int 121 accel_error_submit_tasks(struct spdk_io_channel *ch, struct spdk_accel_task *task) 122 { 123 struct accel_error_channel *errch = spdk_io_channel_get_ctx(ch); 124 struct accel_error_task *errtask = accel_error_get_task_ctx(task); 125 struct accel_error_inject_info *info = &errch->injects[task->op_code]; 126 127 if (!accel_error_should_inject(ch, task)) { 128 goto submit; 129 } 130 131 switch (info->opts.type) { 132 case ACCEL_ERROR_INJECT_CORRUPT: 133 errtask->ch = errch; 134 errtask->cb_arg = task->cb_arg; 135 task->cb_arg = task; 136 if (task->seq != NULL) { 137 errtask->cb_fn.step = task->step_cb_fn; 138 task->step_cb_fn = accel_error_corrupt_step_cb; 139 } else { 140 errtask->cb_fn.cpl = task->cb_fn; 141 task->cb_fn = accel_error_corrupt_cb; 142 } 143 break; 144 case ACCEL_ERROR_INJECT_FAILURE: 145 errtask->status = info->opts.errcode; 146 STAILQ_INSERT_TAIL(&errch->tasks, errtask, link); 147 return 0; 148 default: 149 break; 150 } 151 submit: 152 return g_sw_module->submit_tasks(errch->swch, task); 153 } 154 155 static int 156 accel_error_poller(void *arg) 157 { 158 struct accel_error_channel *errch = arg; 159 struct accel_error_task *errtask; 160 STAILQ_HEAD(, accel_error_task) tasks; 161 struct spdk_accel_task *task; 162 163 if (STAILQ_EMPTY(&errch->tasks)) { 164 return SPDK_POLLER_IDLE; 165 } 166 167 STAILQ_INIT(&tasks); 168 STAILQ_SWAP(&tasks, &errch->tasks, accel_error_task); 169 170 while (!STAILQ_EMPTY(&tasks)) { 171 errtask = STAILQ_FIRST(&tasks); 172 STAILQ_REMOVE_HEAD(&tasks, link); 173 174 task = accel_error_get_task_from_ctx(errtask); 175 spdk_accel_task_complete(task, errtask->status); 176 } 177 178 return SPDK_POLLER_BUSY; 179 } 180 181 static void 182 accel_error_inject_channel(struct spdk_io_channel_iter *iter) 183 { 184 struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(iter); 185 struct accel_error_channel *errch = spdk_io_channel_get_ctx(ch); 186 struct accel_error_inject_opts *opts = spdk_io_channel_iter_get_ctx(iter); 187 struct accel_error_inject_info *info = &errch->injects[opts->opcode]; 188 189 info->count = 0; 190 memcpy(&info->opts, opts, sizeof(info->opts)); 191 192 spdk_for_each_channel_continue(iter, 0); 193 } 194 195 static bool accel_error_supports_opcode(enum spdk_accel_opcode opcode); 196 197 int 198 accel_error_inject_error(struct accel_error_inject_opts *opts) 199 { 200 struct accel_error_inject_opts *curr = &g_injects[opts->opcode]; 201 202 if (!accel_error_supports_opcode(opts->opcode)) { 203 return -EINVAL; 204 } 205 206 memcpy(curr, opts, sizeof(*opts)); 207 if (curr->type == ACCEL_ERROR_INJECT_DISABLE) { 208 curr->count = 0; 209 } 210 if (curr->count == 0) { 211 curr->type = ACCEL_ERROR_INJECT_DISABLE; 212 } 213 214 spdk_for_each_channel(&g_sw_module, accel_error_inject_channel, curr, NULL); 215 216 return 0; 217 } 218 219 static int 220 accel_error_channel_create_cb(void *io_device, void *ctx) 221 { 222 struct accel_error_channel *errch = ctx; 223 size_t i; 224 225 STAILQ_INIT(&errch->tasks); 226 errch->poller = SPDK_POLLER_REGISTER(accel_error_poller, errch, 0); 227 if (errch->poller == NULL) { 228 return -ENOMEM; 229 } 230 231 errch->swch = g_sw_module->get_io_channel(); 232 if (errch->swch == NULL) { 233 spdk_poller_unregister(&errch->poller); 234 return -ENOMEM; 235 } 236 237 for (i = 0; i < SPDK_COUNTOF(errch->injects); ++i) { 238 memcpy(&errch->injects[i].opts, &g_injects[i], sizeof(g_injects[i])); 239 errch->injects[i].count = 0; 240 } 241 242 return 0; 243 } 244 245 static void 246 accel_error_channel_destroy_cb(void *io_device, void *ctx) 247 { 248 struct accel_error_channel *errch = ctx; 249 250 assert(STAILQ_EMPTY(&errch->tasks)); 251 spdk_poller_unregister(&errch->poller); 252 spdk_put_io_channel(errch->swch); 253 } 254 255 static int 256 accel_error_module_init(void) 257 { 258 g_sw_module = spdk_accel_get_module("software"); 259 if (g_sw_module == NULL) { 260 /* Should never really happen */ 261 return -ENOTSUP; 262 } 263 264 g_task_offset = g_sw_module->get_ctx_size(); 265 266 spdk_io_device_register(&g_sw_module, accel_error_channel_create_cb, 267 accel_error_channel_destroy_cb, 268 sizeof(struct accel_error_channel), "accel_error"); 269 270 return 0; 271 } 272 273 static void 274 accel_error_unregister_cb(void *unused) 275 { 276 spdk_accel_module_finish(); 277 } 278 279 static void 280 accel_error_module_fini(void *unused) 281 { 282 spdk_io_device_unregister(&g_sw_module, accel_error_unregister_cb); 283 } 284 285 static bool 286 accel_error_supports_opcode(enum spdk_accel_opcode opcode) 287 { 288 switch (opcode) { 289 case SPDK_ACCEL_OPC_CRC32C: 290 return true; 291 default: 292 return false; 293 } 294 } 295 296 static struct spdk_io_channel * 297 accel_error_get_io_channel(void) 298 { 299 return spdk_get_io_channel(&g_sw_module); 300 } 301 302 static size_t 303 accel_error_get_ctx_size(void) 304 { 305 return g_task_offset + sizeof(struct accel_error_task); 306 } 307 308 const char * 309 accel_error_get_type_name(enum accel_error_inject_type type) 310 { 311 const char *typenames[] = { 312 [ACCEL_ERROR_INJECT_DISABLE] = "disable", 313 [ACCEL_ERROR_INJECT_CORRUPT] = "corrupt", 314 [ACCEL_ERROR_INJECT_FAILURE] = "failure", 315 [ACCEL_ERROR_INJECT_MAX] = NULL 316 }; 317 318 if ((int)type >= ACCEL_ERROR_INJECT_MAX) { 319 return NULL; 320 } 321 322 return typenames[type]; 323 } 324 325 static void 326 accel_error_write_config_json(struct spdk_json_write_ctx *w) 327 { 328 struct accel_error_inject_opts *opts; 329 int opcode; 330 331 for (opcode = 0; opcode < SPDK_ACCEL_OPC_LAST; ++opcode) { 332 opts = &g_injects[opcode]; 333 if (opts->type == ACCEL_ERROR_INJECT_DISABLE) { 334 continue; 335 } 336 spdk_json_write_object_begin(w); 337 spdk_json_write_named_string(w, "method", "accel_error_inject_error"); 338 spdk_json_write_named_object_begin(w, "params"); 339 spdk_json_write_named_string(w, "opcode", spdk_accel_get_opcode_name(opcode)); 340 spdk_json_write_named_string(w, "type", accel_error_get_type_name(opts->type)); 341 spdk_json_write_named_uint64(w, "count", opts->count); 342 spdk_json_write_named_uint64(w, "interval", opts->interval); 343 spdk_json_write_object_end(w); 344 spdk_json_write_object_end(w); 345 } 346 } 347 348 static struct spdk_accel_module_if g_accel_error_module = { 349 .name = "error", 350 .priority = INT_MIN, 351 .module_init = accel_error_module_init, 352 .module_fini = accel_error_module_fini, 353 .supports_opcode = accel_error_supports_opcode, 354 .get_ctx_size = accel_error_get_ctx_size, 355 .get_io_channel = accel_error_get_io_channel, 356 .submit_tasks = accel_error_submit_tasks, 357 .write_config_json = accel_error_write_config_json, 358 }; 359 SPDK_ACCEL_MODULE_REGISTER(error, &g_accel_error_module) 360