Commit | Line | Data |
---|---|---|
09c434b8 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
8d438288 AM |
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(fops_errno, debugfs_errno_get, debugfs_errno_set, | |
19 | "%lld\n"); | |
20 | ||
0ecc833b | 21 | static struct dentry *debugfs_create_errno(const char *name, umode_t mode, |
8d438288 AM |
22 | struct dentry *parent, int *value) |
23 | { | |
24 | return debugfs_create_file(name, mode, parent, value, &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; | |
0ecc833b | 54 | umode_t mode = S_IFREG | S_IRUSR | S_IWUSR; |
8d438288 AM |
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 | if (!dir) | |
63 | return ERR_PTR(-ENOMEM); | |
64 | ||
65 | actions_dir = debugfs_create_dir("actions", dir); | |
66 | if (!actions_dir) | |
67 | goto fail; | |
68 | ||
69 | for (action = err_inject->actions; action->name; action++) { | |
70 | struct dentry *action_dir; | |
71 | ||
72 | action_dir = debugfs_create_dir(action->name, actions_dir); | |
73 | if (!action_dir) | |
74 | goto fail; | |
75 | ||
76 | /* | |
77 | * Create debugfs r/w file containing action->error. If | |
78 | * notifier call chain is called with action->val, it will | |
79 | * fail with the error code | |
80 | */ | |
81 | if (!debugfs_create_errno("error", mode, action_dir, | |
82 | &action->error)) | |
83 | goto fail; | |
84 | } | |
85 | return dir; | |
86 | fail: | |
87 | debugfs_remove_recursive(dir); | |
88 | return ERR_PTR(-ENOMEM); | |
89 | } | |
90 | EXPORT_SYMBOL_GPL(notifier_err_inject_init); | |
91 | ||
92 | static int __init err_inject_init(void) | |
93 | { | |
94 | notifier_err_inject_dir = | |
95 | debugfs_create_dir("notifier-error-inject", NULL); | |
96 | ||
97 | if (!notifier_err_inject_dir) | |
98 | return -ENOMEM; | |
99 | ||
100 | return 0; | |
101 | } | |
102 | ||
103 | static void __exit err_inject_exit(void) | |
104 | { | |
105 | debugfs_remove_recursive(notifier_err_inject_dir); | |
106 | } | |
107 | ||
108 | module_init(err_inject_init); | |
109 | module_exit(err_inject_exit); | |
110 | ||
111 | MODULE_DESCRIPTION("Notifier error injection module"); | |
112 | MODULE_LICENSE("GPL"); | |
113 | MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>"); |