diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Kconfig.debug | 5 | ||||
-rw-r--r-- | lib/fault-inject.c | 126 |
2 files changed, 130 insertions, 1 deletions
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 336241bf0ced..1449ca1bf572 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
@@ -416,6 +416,11 @@ config LKDTM | |||
416 | 416 | ||
417 | config FAULT_INJECTION | 417 | config FAULT_INJECTION |
418 | bool | 418 | bool |
419 | select STACKTRACE | ||
420 | select FRAME_POINTER | ||
421 | help | ||
422 | Provide fault-injection framework. | ||
423 | For more details, see Documentation/fault-injection/. | ||
419 | 424 | ||
420 | config FAILSLAB | 425 | config FAILSLAB |
421 | bool "Fault-injection capabilitiy for kmalloc" | 426 | bool "Fault-injection capabilitiy for kmalloc" |
diff --git a/lib/fault-inject.c b/lib/fault-inject.c index 03468609d701..361c6e9cd77f 100644 --- a/lib/fault-inject.c +++ b/lib/fault-inject.c | |||
@@ -6,6 +6,9 @@ | |||
6 | #include <linux/fs.h> | 6 | #include <linux/fs.h> |
7 | #include <linux/module.h> | 7 | #include <linux/module.h> |
8 | #include <linux/interrupt.h> | 8 | #include <linux/interrupt.h> |
9 | #include <linux/unwind.h> | ||
10 | #include <linux/stacktrace.h> | ||
11 | #include <linux/kallsyms.h> | ||
9 | #include <linux/fault-inject.h> | 12 | #include <linux/fault-inject.h> |
10 | 13 | ||
11 | /* | 14 | /* |
@@ -50,6 +53,86 @@ static int fail_task(struct fault_attr *attr, struct task_struct *task) | |||
50 | return !in_interrupt() && task->make_it_fail; | 53 | return !in_interrupt() && task->make_it_fail; |
51 | } | 54 | } |
52 | 55 | ||
56 | #ifdef CONFIG_STACK_UNWIND | ||
57 | |||
58 | static asmlinkage int fail_stacktrace_callback(struct unwind_frame_info *info, | ||
59 | void *arg) | ||
60 | { | ||
61 | int depth; | ||
62 | struct fault_attr *attr = arg; | ||
63 | bool found = (attr->require_start == 0 && attr->require_end == ULONG_MAX); | ||
64 | |||
65 | for (depth = 0; depth < attr->stacktrace_depth | ||
66 | && unwind(info) == 0 && UNW_PC(info); depth++) { | ||
67 | if (arch_unw_user_mode(info)) | ||
68 | break; | ||
69 | if (attr->reject_start <= UNW_PC(info) && | ||
70 | UNW_PC(info) < attr->reject_end) | ||
71 | return 0; | ||
72 | if (attr->require_start <= UNW_PC(info) && | ||
73 | UNW_PC(info) < attr->require_end) | ||
74 | found = 1; | ||
75 | } | ||
76 | return found; | ||
77 | } | ||
78 | |||
79 | static int fail_stacktrace(struct fault_attr *attr) | ||
80 | { | ||
81 | struct unwind_frame_info info; | ||
82 | |||
83 | return unwind_init_running(&info, fail_stacktrace_callback, attr); | ||
84 | } | ||
85 | |||
86 | #elif defined(CONFIG_STACKTRACE) | ||
87 | |||
88 | #define MAX_STACK_TRACE_DEPTH 32 | ||
89 | |||
90 | static int fail_stacktrace(struct fault_attr *attr) | ||
91 | { | ||
92 | struct stack_trace trace; | ||
93 | int depth = attr->stacktrace_depth; | ||
94 | unsigned long entries[MAX_STACK_TRACE_DEPTH]; | ||
95 | int n; | ||
96 | bool found = (attr->require_start == 0 && attr->require_end == ULONG_MAX); | ||
97 | |||
98 | if (depth == 0) | ||
99 | return found; | ||
100 | |||
101 | trace.nr_entries = 0; | ||
102 | trace.entries = entries; | ||
103 | trace.max_entries = (depth < MAX_STACK_TRACE_DEPTH) ? | ||
104 | depth : MAX_STACK_TRACE_DEPTH; | ||
105 | trace.skip = 1; | ||
106 | trace.all_contexts = 0; | ||
107 | |||
108 | save_stack_trace(&trace, NULL); | ||
109 | for (n = 0; n < trace.nr_entries; n++) { | ||
110 | if (attr->reject_start <= entries[n] && | ||
111 | entries[n] < attr->reject_end) | ||
112 | return 0; | ||
113 | if (attr->require_start <= entries[n] && | ||
114 | entries[n] < attr->require_end) | ||
115 | found = 1; | ||
116 | } | ||
117 | return found; | ||
118 | } | ||
119 | |||
120 | #else | ||
121 | |||
122 | static inline int fail_stacktrace(struct fault_attr *attr) | ||
123 | { | ||
124 | static int firsttime = 1; | ||
125 | |||
126 | if (firsttime) { | ||
127 | printk(KERN_WARNING | ||
128 | "This architecture does not implement save_stack_trace()\n"); | ||
129 | firsttime = 0; | ||
130 | } | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | #endif | ||
135 | |||
53 | /* | 136 | /* |
54 | * This code is stolen from failmalloc-1.0 | 137 | * This code is stolen from failmalloc-1.0 |
55 | * http://www.nongnu.org/failmalloc/ | 138 | * http://www.nongnu.org/failmalloc/ |
@@ -60,6 +143,9 @@ int should_fail(struct fault_attr *attr, ssize_t size) | |||
60 | if (attr->task_filter && !fail_task(attr, current)) | 143 | if (attr->task_filter && !fail_task(attr, current)) |
61 | return 0; | 144 | return 0; |
62 | 145 | ||
146 | if (!fail_stacktrace(attr)) | ||
147 | return 0; | ||
148 | |||
63 | if (atomic_read(&attr->times) == 0) | 149 | if (atomic_read(&attr->times) == 0) |
64 | return 0; | 150 | return 0; |
65 | 151 | ||
@@ -147,6 +233,21 @@ void cleanup_fault_attr_dentries(struct fault_attr *attr) | |||
147 | debugfs_remove(attr->dentries.task_filter_file); | 233 | debugfs_remove(attr->dentries.task_filter_file); |
148 | attr->dentries.task_filter_file = NULL; | 234 | attr->dentries.task_filter_file = NULL; |
149 | 235 | ||
236 | debugfs_remove(attr->dentries.stacktrace_depth_file); | ||
237 | attr->dentries.stacktrace_depth_file = NULL; | ||
238 | |||
239 | debugfs_remove(attr->dentries.require_start_file); | ||
240 | attr->dentries.require_start_file = NULL; | ||
241 | |||
242 | debugfs_remove(attr->dentries.require_end_file); | ||
243 | attr->dentries.require_end_file = NULL; | ||
244 | |||
245 | debugfs_remove(attr->dentries.reject_start_file); | ||
246 | attr->dentries.reject_start_file = NULL; | ||
247 | |||
248 | debugfs_remove(attr->dentries.reject_end_file); | ||
249 | attr->dentries.reject_end_file = NULL; | ||
250 | |||
150 | if (attr->dentries.dir) | 251 | if (attr->dentries.dir) |
151 | WARN_ON(!simple_empty(attr->dentries.dir)); | 252 | WARN_ON(!simple_empty(attr->dentries.dir)); |
152 | 253 | ||
@@ -184,9 +285,32 @@ int init_fault_attr_dentries(struct fault_attr *attr, const char *name) | |||
184 | attr->dentries.task_filter_file = debugfs_create_bool("task-filter", | 285 | attr->dentries.task_filter_file = debugfs_create_bool("task-filter", |
185 | mode, dir, &attr->task_filter); | 286 | mode, dir, &attr->task_filter); |
186 | 287 | ||
288 | attr->dentries.stacktrace_depth_file = | ||
289 | debugfs_create_ul("stacktrace-depth", mode, dir, | ||
290 | &attr->stacktrace_depth); | ||
291 | |||
292 | attr->dentries.require_start_file = | ||
293 | debugfs_create_ul("require-start", mode, dir, &attr->require_start); | ||
294 | |||
295 | attr->dentries.require_end_file = | ||
296 | debugfs_create_ul("require-end", mode, dir, &attr->require_end); | ||
297 | |||
298 | attr->dentries.reject_start_file = | ||
299 | debugfs_create_ul("reject-start", mode, dir, &attr->reject_start); | ||
300 | |||
301 | attr->dentries.reject_end_file = | ||
302 | debugfs_create_ul("reject-end", mode, dir, &attr->reject_end); | ||
303 | |||
304 | |||
187 | if (!attr->dentries.probability_file || !attr->dentries.interval_file | 305 | if (!attr->dentries.probability_file || !attr->dentries.interval_file |
188 | || !attr->dentries.times_file || !attr->dentries.space_file | 306 | || !attr->dentries.times_file || !attr->dentries.space_file |
189 | || !attr->dentries.verbose_file || !attr->dentries.task_filter_file) | 307 | || !attr->dentries.verbose_file || !attr->dentries.task_filter_file |
308 | || !attr->dentries.stacktrace_depth_file | ||
309 | || !attr->dentries.require_start_file | ||
310 | || !attr->dentries.require_end_file | ||
311 | || !attr->dentries.reject_start_file | ||
312 | || !attr->dentries.reject_end_file | ||
313 | ) | ||
190 | goto fail; | 314 | goto fail; |
191 | 315 | ||
192 | return 0; | 316 | return 0; |