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