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_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 : 617890 : accel_error_get_task_ctx(struct spdk_accel_task *task)
37 : : {
38 : 617890 : return (void *)((uint8_t *)task + g_task_offset);
39 : : }
40 : :
41 : : static void
42 : 3536 : accel_error_corrupt_task(struct spdk_accel_task *task)
43 : : {
44 [ + - ]: 3536 : switch (task->op_code) {
45 : 3536 : case SPDK_ACCEL_OPC_CRC32C:
46 : 3536 : *task->crc_dst += 1;
47 : 3536 : break;
48 : 0 : default:
49 : 0 : break;
50 : : }
51 : 3536 : }
52 : :
53 : : static void
54 : 308945 : accel_error_task_complete_cb(void *arg, int status)
55 : : {
56 : 308945 : struct spdk_accel_task *task = arg;
57 : 308945 : struct accel_error_task *errtask = accel_error_get_task_ctx(task);
58 : 308945 : struct accel_error_channel *ch = errtask->ch;
59 : 308945 : struct accel_error_inject_info *info = &ch->injects[task->op_code];
60 : 308945 : spdk_accel_completion_cb cb_fn = errtask->cb_fn;
61 : 308945 : void *cb_arg = errtask->cb_arg;
62 : :
63 : 308945 : info->interval++;
64 [ + + ]: 308945 : if (info->interval >= info->opts.interval) {
65 : 3620 : info->interval = 0;
66 : 3620 : info->count++;
67 : :
68 [ + + ]: 3620 : if (info->count <= info->opts.count) {
69 [ + - - ]: 3536 : switch (info->opts.type) {
70 : 3536 : case ACCEL_ERROR_INJECT_CORRUPT:
71 : 3536 : accel_error_corrupt_task(task);
72 : 3536 : break;
73 : 0 : case ACCEL_ERROR_INJECT_FAILURE:
74 : 0 : status = info->opts.errcode;
75 : 0 : break;
76 : 0 : default:
77 : 0 : break;
78 : : }
79 : : } else {
80 : 84 : info->opts.type = ACCEL_ERROR_INJECT_DISABLE;
81 : : }
82 : : }
83 : :
84 : 308945 : cb_fn(cb_arg, status);
85 : 308945 : }
86 : :
87 : : static int
88 : 308945 : accel_error_submit_tasks(struct spdk_io_channel *ch, struct spdk_accel_task *task)
89 : : {
90 : 308945 : struct accel_error_channel *errch = spdk_io_channel_get_ctx(ch);
91 : 308945 : struct accel_error_task *errtask = accel_error_get_task_ctx(task);
92 : :
93 : 308945 : errtask->ch = errch;
94 : 308945 : errtask->cb_fn = task->cb_fn;
95 : 308945 : errtask->cb_arg = task->cb_arg;
96 : 308945 : task->cb_fn = accel_error_task_complete_cb;
97 : 308945 : task->cb_arg = task;
98 : :
99 : 308945 : return g_sw_module->submit_tasks(errch->swch, task);
100 : : }
101 : :
102 : : static void
103 : 24 : accel_error_inject_channel(struct spdk_io_channel_iter *iter)
104 : : {
105 : 24 : struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(iter);
106 : 24 : struct accel_error_channel *errch = spdk_io_channel_get_ctx(ch);
107 : 24 : struct accel_error_inject_opts *opts = spdk_io_channel_iter_get_ctx(iter);
108 : 24 : struct accel_error_inject_info *info = &errch->injects[opts->opcode];
109 : :
110 : 24 : info->count = 0;
111 [ - + - + ]: 24 : memcpy(&info->opts, opts, sizeof(info->opts));
112 : :
113 : 24 : spdk_for_each_channel_continue(iter, 0);
114 : 24 : }
115 : :
116 : : static bool accel_error_supports_opcode(enum spdk_accel_opcode opcode);
117 : :
118 : : int
119 : 24 : accel_error_inject_error(struct accel_error_inject_opts *opts)
120 : : {
121 : 24 : struct accel_error_inject_opts *curr = &g_injects[opts->opcode];
122 : :
123 [ - + ]: 24 : if (!accel_error_supports_opcode(opts->opcode)) {
124 : 0 : return -EINVAL;
125 : : }
126 : :
127 [ - + - + ]: 24 : memcpy(curr, opts, sizeof(*opts));
128 [ + + ]: 24 : if (curr->type == ACCEL_ERROR_INJECT_DISABLE) {
129 : 12 : curr->count = 0;
130 : : }
131 [ + + ]: 24 : if (curr->count == 0) {
132 : 12 : curr->type = ACCEL_ERROR_INJECT_DISABLE;
133 : : }
134 : :
135 : 24 : spdk_for_each_channel(&g_sw_module, accel_error_inject_channel, curr, NULL);
136 : :
137 : 24 : return 0;
138 : : }
139 : :
140 : : static int
141 : 6 : accel_error_channel_create_cb(void *io_device, void *ctx)
142 : : {
143 : 6 : struct accel_error_channel *errch = ctx;
144 : : size_t i;
145 : :
146 : 6 : errch->swch = g_sw_module->get_io_channel();
147 [ - + ]: 6 : if (errch->swch == NULL) {
148 : 0 : return -ENOMEM;
149 : : }
150 : :
151 [ + + ]: 90 : for (i = 0; i < SPDK_COUNTOF(errch->injects); ++i) {
152 [ - + - + ]: 84 : memcpy(&errch->injects[i].opts, &g_injects[i], sizeof(g_injects[i]));
153 : 84 : errch->injects[i].count = 0;
154 : : }
155 : :
156 : 6 : return 0;
157 : : }
158 : :
159 : : static void
160 : 6 : accel_error_channel_destroy_cb(void *io_device, void *ctx)
161 : : {
162 : 6 : struct accel_error_channel *errch = ctx;
163 : :
164 : 6 : spdk_put_io_channel(errch->swch);
165 : 6 : }
166 : :
167 : : static int
168 : 2774 : accel_error_module_init(void)
169 : : {
170 : 2774 : g_sw_module = spdk_accel_get_module("software");
171 [ - + ]: 2774 : if (g_sw_module == NULL) {
172 : : /* Should never really happen */
173 : 0 : return -ENODEV;
174 : : }
175 : :
176 : 2774 : g_task_offset = g_sw_module->get_ctx_size();
177 : :
178 : 2774 : 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 : 2774 : return 0;
183 : : }
184 : :
185 : : static void
186 : 2774 : accel_error_unregister_cb(void *unused)
187 : : {
188 : 2774 : spdk_accel_module_finish();
189 : 2774 : }
190 : :
191 : : static void
192 : 2774 : accel_error_module_fini(void *unused)
193 : : {
194 : 2774 : spdk_io_device_unregister(&g_sw_module, accel_error_unregister_cb);
195 : 2774 : }
196 : :
197 : : static bool
198 : 38863 : accel_error_supports_opcode(enum spdk_accel_opcode opcode)
199 : : {
200 [ + + ]: 38863 : switch (opcode) {
201 : 2801 : case SPDK_ACCEL_OPC_CRC32C:
202 : 2801 : return true;
203 : 36062 : default:
204 : 36062 : return false;
205 : : }
206 : : }
207 : :
208 : : static struct spdk_io_channel *
209 : 6 : accel_error_get_io_channel(void)
210 : : {
211 : 6 : return spdk_get_io_channel(&g_sw_module);
212 : : }
213 : :
214 : : static size_t
215 : 5548 : accel_error_get_ctx_size(void)
216 : : {
217 : 5548 : return g_task_offset + sizeof(struct accel_error_task);
218 : : }
219 : :
220 : : const char *
221 : 36 : accel_error_get_type_name(enum accel_error_inject_type type)
222 : : {
223 : 36 : 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 [ - + ]: 36 : if ((int)type >= ACCEL_ERROR_INJECT_MAX) {
231 : 0 : return NULL;
232 : : }
233 : :
234 : 36 : return typenames[type];
235 : : }
236 : :
237 : : static void
238 : 117 : accel_error_write_config_json(struct spdk_json_write_ctx *w)
239 : : {
240 : : struct accel_error_inject_opts *opts;
241 : : int opcode;
242 : :
243 [ + + ]: 1755 : for (opcode = 0; opcode < SPDK_ACCEL_OPC_LAST; ++opcode) {
244 : 1638 : opts = &g_injects[opcode];
245 [ + - ]: 1638 : if (opts->type == ACCEL_ERROR_INJECT_DISABLE) {
246 : 1638 : continue;
247 : : }
248 : 0 : spdk_json_write_object_begin(w);
249 : 0 : spdk_json_write_named_string(w, "method", "accel_error_inject_error");
250 : 0 : spdk_json_write_named_object_begin(w, "params");
251 : 0 : spdk_json_write_named_string(w, "opcode", spdk_accel_get_opcode_name(opcode));
252 : 0 : spdk_json_write_named_string(w, "type", accel_error_get_type_name(opts->type));
253 : 0 : spdk_json_write_named_uint64(w, "count", opts->count);
254 : 0 : spdk_json_write_named_uint64(w, "interval", opts->interval);
255 : 0 : spdk_json_write_object_end(w);
256 : 0 : spdk_json_write_object_end(w);
257 : : }
258 : 117 : }
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 : 2989 : SPDK_ACCEL_MODULE_REGISTER(error, &g_accel_error_module)
|