1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | #include <linux/module.h> |
3 | |
4 | #include "notifier-error-inject.h" |
5 | |
6 | static int debugfs_errno_set(void *data, u64 val) |
7 | { |
8 | *(int *)data = clamp_t(int, val, -MAX_ERRNO, 0); |
9 | return 0; |
10 | } |
11 | |
12 | static int debugfs_errno_get(void *data, u64 *val) |
13 | { |
14 | *val = *(int *)data; |
15 | return 0; |
16 | } |
17 | |
18 | DEFINE_SIMPLE_ATTRIBUTE_SIGNED(fops_errno, debugfs_errno_get, debugfs_errno_set, |
19 | "%lld\n" ); |
20 | |
21 | static struct dentry *debugfs_create_errno(const char *name, umode_t mode, |
22 | struct dentry *parent, int *value) |
23 | { |
24 | return debugfs_create_file(name, mode, parent, data: value, fops: &fops_errno); |
25 | } |
26 | |
27 | static int notifier_err_inject_callback(struct notifier_block *nb, |
28 | unsigned long val, void *p) |
29 | { |
30 | int err = 0; |
31 | struct notifier_err_inject *err_inject = |
32 | container_of(nb, struct notifier_err_inject, nb); |
33 | struct notifier_err_inject_action *action; |
34 | |
35 | for (action = err_inject->actions; action->name; action++) { |
36 | if (action->val == val) { |
37 | err = action->error; |
38 | break; |
39 | } |
40 | } |
41 | if (err) |
42 | pr_info("Injecting error (%d) to %s\n" , err, action->name); |
43 | |
44 | return notifier_from_errno(err); |
45 | } |
46 | |
47 | struct dentry *notifier_err_inject_dir; |
48 | EXPORT_SYMBOL_GPL(notifier_err_inject_dir); |
49 | |
50 | struct dentry *notifier_err_inject_init(const char *name, struct dentry *parent, |
51 | struct notifier_err_inject *err_inject, int priority) |
52 | { |
53 | struct notifier_err_inject_action *action; |
54 | umode_t mode = S_IFREG | S_IRUSR | S_IWUSR; |
55 | struct dentry *dir; |
56 | struct dentry *actions_dir; |
57 | |
58 | err_inject->nb.notifier_call = notifier_err_inject_callback; |
59 | err_inject->nb.priority = priority; |
60 | |
61 | dir = debugfs_create_dir(name, parent); |
62 | |
63 | actions_dir = debugfs_create_dir(name: "actions" , parent: dir); |
64 | |
65 | for (action = err_inject->actions; action->name; action++) { |
66 | struct dentry *action_dir; |
67 | |
68 | action_dir = debugfs_create_dir(name: action->name, parent: actions_dir); |
69 | |
70 | /* |
71 | * Create debugfs r/w file containing action->error. If |
72 | * notifier call chain is called with action->val, it will |
73 | * fail with the error code |
74 | */ |
75 | debugfs_create_errno(name: "error" , mode, parent: action_dir, value: &action->error); |
76 | } |
77 | return dir; |
78 | } |
79 | EXPORT_SYMBOL_GPL(notifier_err_inject_init); |
80 | |
81 | static int __init err_inject_init(void) |
82 | { |
83 | notifier_err_inject_dir = |
84 | debugfs_create_dir(name: "notifier-error-inject" , NULL); |
85 | |
86 | return 0; |
87 | } |
88 | |
89 | static void __exit err_inject_exit(void) |
90 | { |
91 | debugfs_remove_recursive(dentry: notifier_err_inject_dir); |
92 | } |
93 | |
94 | module_init(err_inject_init); |
95 | module_exit(err_inject_exit); |
96 | |
97 | MODULE_DESCRIPTION("Notifier error injection module" ); |
98 | MODULE_LICENSE("GPL" ); |
99 | MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>" ); |
100 | |