diff options
-rw-r--r-- | Documentation/fault-injection/fault-injection.txt | 12 | ||||
-rw-r--r-- | include/linux/fault-inject.h | 12 | ||||
-rw-r--r-- | lib/Kconfig.debug | 5 | ||||
-rw-r--r-- | lib/fault-inject.c | 126 |
4 files changed, 150 insertions, 5 deletions
diff --git a/Documentation/fault-injection/fault-injection.txt b/Documentation/fault-injection/fault-injection.txt index cf075c20eda0..6d6e5ac5ea92 100644 --- a/Documentation/fault-injection/fault-injection.txt +++ b/Documentation/fault-injection/fault-injection.txt | |||
@@ -73,13 +73,17 @@ configuration of fault-injection capabilities. | |||
73 | Any positive value limits failures to only processes indicated by | 73 | Any positive value limits failures to only processes indicated by |
74 | /proc/<pid>/make-it-fail==1. | 74 | /proc/<pid>/make-it-fail==1. |
75 | 75 | ||
76 | - /debug/*/address-start: | 76 | - /debug/*/require-start: |
77 | - /debug/*/address-end: | 77 | - /debug/*/require-end: |
78 | - /debug/*/reject-start: | ||
79 | - /debug/*/reject-end: | ||
78 | 80 | ||
79 | specifies the range of virtual addresses tested during | 81 | specifies the range of virtual addresses tested during |
80 | stacktrace walking. Failure is injected only if some caller | 82 | stacktrace walking. Failure is injected only if some caller |
81 | in the walked stacktrace lies within this range. | 83 | in the walked stacktrace lies within the required range, and |
82 | Default is [0,ULONG_MAX) (whole of virtual address space). | 84 | none lies within the rejected range. |
85 | Default required range is [0,ULONG_MAX) (whole of virtual address space). | ||
86 | Default rejected range is [0,0). | ||
83 | 87 | ||
84 | - /debug/*/stacktrace-depth: | 88 | - /debug/*/stacktrace-depth: |
85 | 89 | ||
diff --git a/include/linux/fault-inject.h b/include/linux/fault-inject.h index a525f9b9f015..9bb584e89399 100644 --- a/include/linux/fault-inject.h +++ b/include/linux/fault-inject.h | |||
@@ -18,6 +18,11 @@ struct fault_attr { | |||
18 | atomic_t space; | 18 | atomic_t space; |
19 | unsigned long verbose; | 19 | unsigned long verbose; |
20 | u32 task_filter; | 20 | u32 task_filter; |
21 | unsigned long stacktrace_depth; | ||
22 | unsigned long require_start; | ||
23 | unsigned long require_end; | ||
24 | unsigned long reject_start; | ||
25 | unsigned long reject_end; | ||
21 | 26 | ||
22 | unsigned long count; | 27 | unsigned long count; |
23 | 28 | ||
@@ -32,6 +37,11 @@ struct fault_attr { | |||
32 | struct dentry *space_file; | 37 | struct dentry *space_file; |
33 | struct dentry *verbose_file; | 38 | struct dentry *verbose_file; |
34 | struct dentry *task_filter_file; | 39 | struct dentry *task_filter_file; |
40 | struct dentry *stacktrace_depth_file; | ||
41 | struct dentry *require_start_file; | ||
42 | struct dentry *require_end_file; | ||
43 | struct dentry *reject_start_file; | ||
44 | struct dentry *reject_end_file; | ||
35 | } dentries; | 45 | } dentries; |
36 | 46 | ||
37 | #endif | 47 | #endif |
@@ -40,6 +50,8 @@ struct fault_attr { | |||
40 | #define FAULT_ATTR_INITIALIZER { \ | 50 | #define FAULT_ATTR_INITIALIZER { \ |
41 | .interval = 1, \ | 51 | .interval = 1, \ |
42 | .times = ATOMIC_INIT(1), \ | 52 | .times = ATOMIC_INIT(1), \ |
53 | .require_end = ULONG_MAX, \ | ||
54 | .stacktrace_depth = 32, \ | ||
43 | } | 55 | } |
44 | 56 | ||
45 | #define DECLARE_FAULT_ATTR(name) struct fault_attr name = FAULT_ATTR_INITIALIZER | 57 | #define DECLARE_FAULT_ATTR(name) struct fault_attr name = FAULT_ATTR_INITIALIZER |
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; |