1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | #include <linux/kernel.h> |
3 | #include <linux/init.h> |
4 | #include <linux/random.h> |
5 | #include <linux/sched.h> |
6 | #include <linux/stat.h> |
7 | #include <linux/types.h> |
8 | #include <linux/fs.h> |
9 | #include <linux/export.h> |
10 | #include <linux/interrupt.h> |
11 | #include <linux/stacktrace.h> |
12 | #include <linux/fault-inject.h> |
13 | |
14 | /* |
15 | * setup_fault_attr() is a helper function for various __setup handlers, so it |
16 | * returns 0 on error, because that is what __setup handlers do. |
17 | */ |
18 | int setup_fault_attr(struct fault_attr *attr, char *str) |
19 | { |
20 | unsigned long probability; |
21 | unsigned long interval; |
22 | int times; |
23 | int space; |
24 | |
25 | /* "<interval>,<probability>,<space>,<times>" */ |
26 | if (sscanf(str, "%lu,%lu,%d,%d" , |
27 | &interval, &probability, &space, ×) < 4) { |
28 | printk(KERN_WARNING |
29 | "FAULT_INJECTION: failed to parse arguments\n" ); |
30 | return 0; |
31 | } |
32 | |
33 | attr->probability = probability; |
34 | attr->interval = interval; |
35 | atomic_set(v: &attr->times, i: times); |
36 | atomic_set(v: &attr->space, i: space); |
37 | |
38 | return 1; |
39 | } |
40 | EXPORT_SYMBOL_GPL(setup_fault_attr); |
41 | |
42 | static void fail_dump(struct fault_attr *attr) |
43 | { |
44 | if (attr->verbose > 0 && __ratelimit(&attr->ratelimit_state)) { |
45 | printk(KERN_NOTICE "FAULT_INJECTION: forcing a failure.\n" |
46 | "name %pd, interval %lu, probability %lu, " |
47 | "space %d, times %d\n" , attr->dname, |
48 | attr->interval, attr->probability, |
49 | atomic_read(&attr->space), |
50 | atomic_read(&attr->times)); |
51 | if (attr->verbose > 1) |
52 | dump_stack(); |
53 | } |
54 | } |
55 | |
56 | #define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) |
57 | |
58 | static bool fail_task(struct fault_attr *attr, struct task_struct *task) |
59 | { |
60 | return in_task() && task->make_it_fail; |
61 | } |
62 | |
63 | #define MAX_STACK_TRACE_DEPTH 32 |
64 | |
65 | #ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER |
66 | |
67 | static bool fail_stacktrace(struct fault_attr *attr) |
68 | { |
69 | int depth = attr->stacktrace_depth; |
70 | unsigned long entries[MAX_STACK_TRACE_DEPTH]; |
71 | int n, nr_entries; |
72 | bool found = (attr->require_start == 0 && attr->require_end == ULONG_MAX); |
73 | |
74 | if (depth == 0 || (found && !attr->reject_start && !attr->reject_end)) |
75 | return found; |
76 | |
77 | nr_entries = stack_trace_save(store: entries, size: depth, skipnr: 1); |
78 | for (n = 0; n < nr_entries; n++) { |
79 | if (attr->reject_start <= entries[n] && |
80 | entries[n] < attr->reject_end) |
81 | return false; |
82 | if (attr->require_start <= entries[n] && |
83 | entries[n] < attr->require_end) |
84 | found = true; |
85 | } |
86 | return found; |
87 | } |
88 | |
89 | #else |
90 | |
91 | static inline bool fail_stacktrace(struct fault_attr *attr) |
92 | { |
93 | return true; |
94 | } |
95 | |
96 | #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */ |
97 | |
98 | /* |
99 | * This code is stolen from failmalloc-1.0 |
100 | * http://www.nongnu.org/failmalloc/ |
101 | */ |
102 | |
103 | bool should_fail_ex(struct fault_attr *attr, ssize_t size, int flags) |
104 | { |
105 | bool stack_checked = false; |
106 | |
107 | if (in_task()) { |
108 | unsigned int fail_nth = READ_ONCE(current->fail_nth); |
109 | |
110 | if (fail_nth) { |
111 | if (!fail_stacktrace(attr)) |
112 | return false; |
113 | |
114 | stack_checked = true; |
115 | fail_nth--; |
116 | WRITE_ONCE(current->fail_nth, fail_nth); |
117 | if (!fail_nth) |
118 | goto fail; |
119 | |
120 | return false; |
121 | } |
122 | } |
123 | |
124 | /* No need to check any other properties if the probability is 0 */ |
125 | if (attr->probability == 0) |
126 | return false; |
127 | |
128 | if (attr->task_filter && !fail_task(attr, current)) |
129 | return false; |
130 | |
131 | if (atomic_read(v: &attr->times) == 0) |
132 | return false; |
133 | |
134 | if (!stack_checked && !fail_stacktrace(attr)) |
135 | return false; |
136 | |
137 | if (atomic_read(v: &attr->space) > size) { |
138 | atomic_sub(i: size, v: &attr->space); |
139 | return false; |
140 | } |
141 | |
142 | if (attr->interval > 1) { |
143 | attr->count++; |
144 | if (attr->count % attr->interval) |
145 | return false; |
146 | } |
147 | |
148 | if (attr->probability <= get_random_u32_below(ceil: 100)) |
149 | return false; |
150 | |
151 | fail: |
152 | if (!(flags & FAULT_NOWARN)) |
153 | fail_dump(attr); |
154 | |
155 | if (atomic_read(v: &attr->times) != -1) |
156 | atomic_dec_not_zero(&attr->times); |
157 | |
158 | return true; |
159 | } |
160 | |
161 | bool should_fail(struct fault_attr *attr, ssize_t size) |
162 | { |
163 | return should_fail_ex(attr, size, flags: 0); |
164 | } |
165 | EXPORT_SYMBOL_GPL(should_fail); |
166 | |
167 | #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS |
168 | |
169 | static int debugfs_ul_set(void *data, u64 val) |
170 | { |
171 | *(unsigned long *)data = val; |
172 | return 0; |
173 | } |
174 | |
175 | static int debugfs_ul_get(void *data, u64 *val) |
176 | { |
177 | *val = *(unsigned long *)data; |
178 | return 0; |
179 | } |
180 | |
181 | DEFINE_SIMPLE_ATTRIBUTE(fops_ul, debugfs_ul_get, debugfs_ul_set, "%llu\n" ); |
182 | |
183 | static void debugfs_create_ul(const char *name, umode_t mode, |
184 | struct dentry *parent, unsigned long *value) |
185 | { |
186 | debugfs_create_file(name, mode, parent, data: value, fops: &fops_ul); |
187 | } |
188 | |
189 | #ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER |
190 | |
191 | static int debugfs_stacktrace_depth_set(void *data, u64 val) |
192 | { |
193 | *(unsigned long *)data = |
194 | min_t(unsigned long, val, MAX_STACK_TRACE_DEPTH); |
195 | |
196 | return 0; |
197 | } |
198 | |
199 | DEFINE_SIMPLE_ATTRIBUTE(fops_stacktrace_depth, debugfs_ul_get, |
200 | debugfs_stacktrace_depth_set, "%llu\n" ); |
201 | |
202 | static void debugfs_create_stacktrace_depth(const char *name, umode_t mode, |
203 | struct dentry *parent, |
204 | unsigned long *value) |
205 | { |
206 | debugfs_create_file(name, mode, parent, data: value, fops: &fops_stacktrace_depth); |
207 | } |
208 | |
209 | #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */ |
210 | |
211 | struct dentry *fault_create_debugfs_attr(const char *name, |
212 | struct dentry *parent, struct fault_attr *attr) |
213 | { |
214 | umode_t mode = S_IFREG | S_IRUSR | S_IWUSR; |
215 | struct dentry *dir; |
216 | |
217 | dir = debugfs_create_dir(name, parent); |
218 | if (IS_ERR(ptr: dir)) |
219 | return dir; |
220 | |
221 | debugfs_create_ul(name: "probability" , mode, parent: dir, value: &attr->probability); |
222 | debugfs_create_ul(name: "interval" , mode, parent: dir, value: &attr->interval); |
223 | debugfs_create_atomic_t(name: "times" , mode, parent: dir, value: &attr->times); |
224 | debugfs_create_atomic_t(name: "space" , mode, parent: dir, value: &attr->space); |
225 | debugfs_create_ul(name: "verbose" , mode, parent: dir, value: &attr->verbose); |
226 | debugfs_create_u32(name: "verbose_ratelimit_interval_ms" , mode, parent: dir, |
227 | value: &attr->ratelimit_state.interval); |
228 | debugfs_create_u32(name: "verbose_ratelimit_burst" , mode, parent: dir, |
229 | value: &attr->ratelimit_state.burst); |
230 | debugfs_create_bool(name: "task-filter" , mode, parent: dir, value: &attr->task_filter); |
231 | |
232 | #ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER |
233 | debugfs_create_stacktrace_depth(name: "stacktrace-depth" , mode, parent: dir, |
234 | value: &attr->stacktrace_depth); |
235 | debugfs_create_xul(name: "require-start" , mode, parent: dir, value: &attr->require_start); |
236 | debugfs_create_xul(name: "require-end" , mode, parent: dir, value: &attr->require_end); |
237 | debugfs_create_xul(name: "reject-start" , mode, parent: dir, value: &attr->reject_start); |
238 | debugfs_create_xul(name: "reject-end" , mode, parent: dir, value: &attr->reject_end); |
239 | #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */ |
240 | |
241 | attr->dname = dget(dentry: dir); |
242 | return dir; |
243 | } |
244 | EXPORT_SYMBOL_GPL(fault_create_debugfs_attr); |
245 | |
246 | #endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */ |
247 | |
248 | #ifdef CONFIG_FAULT_INJECTION_CONFIGFS |
249 | |
250 | /* These configfs attribute utilities are copied from drivers/block/null_blk/main.c */ |
251 | |
252 | static ssize_t fault_uint_attr_show(unsigned int val, char *page) |
253 | { |
254 | return snprintf(buf: page, PAGE_SIZE, fmt: "%u\n" , val); |
255 | } |
256 | |
257 | static ssize_t fault_ulong_attr_show(unsigned long val, char *page) |
258 | { |
259 | return snprintf(buf: page, PAGE_SIZE, fmt: "%lu\n" , val); |
260 | } |
261 | |
262 | static ssize_t fault_bool_attr_show(bool val, char *page) |
263 | { |
264 | return snprintf(buf: page, PAGE_SIZE, fmt: "%u\n" , val); |
265 | } |
266 | |
267 | static ssize_t fault_atomic_t_attr_show(atomic_t val, char *page) |
268 | { |
269 | return snprintf(buf: page, PAGE_SIZE, fmt: "%d\n" , atomic_read(v: &val)); |
270 | } |
271 | |
272 | static ssize_t fault_uint_attr_store(unsigned int *val, const char *page, size_t count) |
273 | { |
274 | unsigned int tmp; |
275 | int result; |
276 | |
277 | result = kstrtouint(s: page, base: 0, res: &tmp); |
278 | if (result < 0) |
279 | return result; |
280 | |
281 | *val = tmp; |
282 | return count; |
283 | } |
284 | |
285 | static ssize_t fault_ulong_attr_store(unsigned long *val, const char *page, size_t count) |
286 | { |
287 | int result; |
288 | unsigned long tmp; |
289 | |
290 | result = kstrtoul(s: page, base: 0, res: &tmp); |
291 | if (result < 0) |
292 | return result; |
293 | |
294 | *val = tmp; |
295 | return count; |
296 | } |
297 | |
298 | static ssize_t fault_bool_attr_store(bool *val, const char *page, size_t count) |
299 | { |
300 | bool tmp; |
301 | int result; |
302 | |
303 | result = kstrtobool(s: page, res: &tmp); |
304 | if (result < 0) |
305 | return result; |
306 | |
307 | *val = tmp; |
308 | return count; |
309 | } |
310 | |
311 | static ssize_t fault_atomic_t_attr_store(atomic_t *val, const char *page, size_t count) |
312 | { |
313 | int tmp; |
314 | int result; |
315 | |
316 | result = kstrtoint(s: page, base: 0, res: &tmp); |
317 | if (result < 0) |
318 | return result; |
319 | |
320 | atomic_set(v: val, i: tmp); |
321 | return count; |
322 | } |
323 | |
324 | #define CONFIGFS_ATTR_NAMED(_pfx, _name, _attr_name) \ |
325 | static struct configfs_attribute _pfx##attr_##_name = { \ |
326 | .ca_name = _attr_name, \ |
327 | .ca_mode = 0644, \ |
328 | .ca_owner = THIS_MODULE, \ |
329 | .show = _pfx##_name##_show, \ |
330 | .store = _pfx##_name##_store, \ |
331 | } |
332 | |
333 | static struct fault_config *to_fault_config(struct config_item *item) |
334 | { |
335 | return container_of(to_config_group(item), struct fault_config, group); |
336 | } |
337 | |
338 | #define FAULT_CONFIGFS_ATTR_NAMED(NAME, ATTR_NAME, MEMBER, TYPE) \ |
339 | static ssize_t fault_##NAME##_show(struct config_item *item, char *page) \ |
340 | { \ |
341 | return fault_##TYPE##_attr_show(to_fault_config(item)->attr.MEMBER, page); \ |
342 | } \ |
343 | static ssize_t fault_##NAME##_store(struct config_item *item, const char *page, size_t count) \ |
344 | { \ |
345 | struct fault_config *config = to_fault_config(item); \ |
346 | return fault_##TYPE##_attr_store(&config->attr.MEMBER, page, count); \ |
347 | } \ |
348 | CONFIGFS_ATTR_NAMED(fault_, NAME, ATTR_NAME) |
349 | |
350 | #define FAULT_CONFIGFS_ATTR(NAME, TYPE) \ |
351 | FAULT_CONFIGFS_ATTR_NAMED(NAME, __stringify(NAME), NAME, TYPE) |
352 | |
353 | FAULT_CONFIGFS_ATTR(probability, ulong); |
354 | FAULT_CONFIGFS_ATTR(interval, ulong); |
355 | FAULT_CONFIGFS_ATTR(times, atomic_t); |
356 | FAULT_CONFIGFS_ATTR(space, atomic_t); |
357 | FAULT_CONFIGFS_ATTR(verbose, ulong); |
358 | FAULT_CONFIGFS_ATTR_NAMED(ratelimit_interval, "verbose_ratelimit_interval_ms" , |
359 | ratelimit_state.interval, uint); |
360 | FAULT_CONFIGFS_ATTR_NAMED(ratelimit_burst, "verbose_ratelimit_burst" , |
361 | ratelimit_state.burst, uint); |
362 | FAULT_CONFIGFS_ATTR_NAMED(task_filter, "task-filter" , task_filter, bool); |
363 | |
364 | #ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER |
365 | |
366 | static ssize_t fault_stacktrace_depth_show(struct config_item *item, char *page) |
367 | { |
368 | return fault_ulong_attr_show(val: to_fault_config(item)->attr.stacktrace_depth, page); |
369 | } |
370 | |
371 | static ssize_t fault_stacktrace_depth_store(struct config_item *item, const char *page, |
372 | size_t count) |
373 | { |
374 | int result; |
375 | unsigned long tmp; |
376 | |
377 | result = kstrtoul(s: page, base: 0, res: &tmp); |
378 | if (result < 0) |
379 | return result; |
380 | |
381 | to_fault_config(item)->attr.stacktrace_depth = |
382 | min_t(unsigned long, tmp, MAX_STACK_TRACE_DEPTH); |
383 | |
384 | return count; |
385 | } |
386 | |
387 | CONFIGFS_ATTR_NAMED(fault_, stacktrace_depth, "stacktrace-depth" ); |
388 | |
389 | static ssize_t fault_xul_attr_show(unsigned long val, char *page) |
390 | { |
391 | return snprintf(buf: page, PAGE_SIZE, |
392 | fmt: sizeof(val) == sizeof(u32) ? "0x%08lx\n" : "0x%016lx\n" , val); |
393 | } |
394 | |
395 | static ssize_t fault_xul_attr_store(unsigned long *val, const char *page, size_t count) |
396 | { |
397 | return fault_ulong_attr_store(val, page, count); |
398 | } |
399 | |
400 | FAULT_CONFIGFS_ATTR_NAMED(require_start, "require-start" , require_start, xul); |
401 | FAULT_CONFIGFS_ATTR_NAMED(require_end, "require-end" , require_end, xul); |
402 | FAULT_CONFIGFS_ATTR_NAMED(reject_start, "reject-start" , reject_start, xul); |
403 | FAULT_CONFIGFS_ATTR_NAMED(reject_end, "reject-end" , reject_end, xul); |
404 | |
405 | #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */ |
406 | |
407 | static struct configfs_attribute *fault_config_attrs[] = { |
408 | &fault_attr_probability, |
409 | &fault_attr_interval, |
410 | &fault_attr_times, |
411 | &fault_attr_space, |
412 | &fault_attr_verbose, |
413 | &fault_attr_ratelimit_interval, |
414 | &fault_attr_ratelimit_burst, |
415 | &fault_attr_task_filter, |
416 | #ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER |
417 | &fault_attr_stacktrace_depth, |
418 | &fault_attr_require_start, |
419 | &fault_attr_require_end, |
420 | &fault_attr_reject_start, |
421 | &fault_attr_reject_end, |
422 | #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */ |
423 | NULL, |
424 | }; |
425 | |
426 | static const struct config_item_type fault_config_type = { |
427 | .ct_attrs = fault_config_attrs, |
428 | .ct_owner = THIS_MODULE, |
429 | }; |
430 | |
431 | void fault_config_init(struct fault_config *config, const char *name) |
432 | { |
433 | config_group_init_type_name(group: &config->group, name, type: &fault_config_type); |
434 | } |
435 | EXPORT_SYMBOL_GPL(fault_config_init); |
436 | |
437 | #endif /* CONFIG_FAULT_INJECTION_CONFIGFS */ |
438 | |