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