xref: /spdk/module/accel/error/accel_error.c (revision 66289a6dbe28217365daa40fd92dcf327871c2e8)
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