From: Akinobu Mita This patch provides stacktrace filtering feature. The stacktrace filter allows failing only for the caller you are interested in. stacktrace filter is enabled by setting the value of /debugfs/*/stacktrace-depth more than 0. and specify the range of the virtual address by the /debugfs/*/address-start and /debugfs/*/address-end Please see the example that demostrates how to inject slab allocation failures only for a specific module in Documentation/fault-injection/fault-injection.txt Cc: Valdis.Kletnieks@vt.edu Signed-off-by: Akinobu Mita Signed-off-by: Don Mullis include/linux/fault-inject.h | 7 ++ lib/Kconfig.debug | 2 lib/fault-inject.c | 111 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) Index: work-fault-inject/lib/fault-inject.c =================================================================== --- work-fault-inject.orig/lib/fault-inject.c +++ work-fault-inject/lib/fault-inject.c @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include #include int setup_fault_attr(struct fault_attr *attr, char *str) @@ -64,6 +67,82 @@ static int fail_task(struct fault_attr * return !in_interrupt() && task->make_it_fail; } +static int fail_any_address(struct fault_attr *attr) +{ + return (attr->address_start == 0 && attr->address_end == ULONG_MAX); +} + +#ifdef CONFIG_STACK_UNWIND + +static asmlinkage int fail_stacktrace_callback(struct unwind_frame_info *info, + void *arg) +{ + int depth; + struct fault_attr *attr = arg; + + for (depth = 0; depth < attr->stacktrace_depth + && unwind(info) == 0 && UNW_PC(info); depth++) { + if (arch_unw_user_mode(info)) + break; + if (attr->address_start <= UNW_PC(info) && + UNW_PC(info) < attr->address_end) + return 1; + } + return 0; +} + +static int fail_stacktrace(struct fault_attr *attr) +{ + struct unwind_frame_info info; + + return unwind_init_running(&info, fail_stacktrace_callback, attr); +} + +#elif defined(CONFIG_STACKTRACE) + +#define MAX_STACK_TRACE_DEPTH 10 + +static int fail_stacktrace(struct fault_attr *attr) +{ + struct stack_trace trace; + int depth = attr->stacktrace_depth; + unsigned long entries[MAX_STACK_TRACE_DEPTH]; + int n; + + if (depth == 0) + return 0; + + trace.nr_entries = 0; + trace.entries = entries; + trace.max_entries = (depth < MAX_STACK_TRACE_DEPTH) ? + depth : MAX_STACK_TRACE_DEPTH; + trace.skip = 1; + trace.all_contexts = 0; + + save_stack_trace(&trace, NULL); + for (n = 0; n < trace.nr_entries; n++) + if (attr->address_start <= entries[n] && + entries[n] < attr->address_end) + return 1; + return 0; +} + +#else + +static inline int fail_stacktrace(struct fault_attr *attr) +{ + static int firsttime = 1; + + if (firsttime) { + printk(KERN_WARNING + "This architecture does not implement save_stack_trace()"\n"); + firsttime = 0; + } + return 0; +} + +#endif + /* * This code is stolen from failmalloc-1.0 * http://www.nongnu.org/failmalloc/ @@ -74,6 +153,9 @@ int should_fail(struct fault_attr *attr, if (attr->task_filter && !fail_task(attr, current)) return 0; + if (!fail_any_address(attr) && !fail_stacktrace(attr)) + return 0; + if (atomic_read(&max_failures(attr)) == 0) return 0; @@ -168,6 +250,18 @@ void cleanup_fault_attr_entries(struct f debugfs_remove(attr->entries.task_filter_file); attr->entries.task_filter_file = NULL; } + if (attr->entries.stacktrace_depth_file) { + debugfs_remove(attr->entries.stacktrace_depth_file); + attr->entries.stacktrace_depth_file = NULL; + } + if (attr->entries.address_start_file) { + debugfs_remove(attr->entries.address_start_file); + attr->entries.address_start_file = NULL; + } + if (attr->entries.address_end_file) { + debugfs_remove(attr->entries.address_end_file); + attr->entries.address_end_file = NULL; + } debugfs_remove(attr->entries.dir); attr->entries.dir = NULL; } @@ -217,6 +311,23 @@ int init_fault_attr_entries(struct fault goto fail; attr->entries.task_filter_file = file; + file = debugfs_create_ul("stacktrace-depth", mode, dir, + &attr->stacktrace_depth); + if (!file) + goto fail; + attr->entries.stacktrace_depth_file = file; + + file = debugfs_create_ul("address-start", mode, dir, + &attr->address_start); + if (!file) + goto fail; + attr->entries.address_start_file = file; + + file = debugfs_create_ul("address-end", mode, dir, &attr->address_end); + if (!file) + goto fail; + attr->entries.address_end_file = file; + return 0; fail: cleanup_fault_attr_entries(attr); Index: work-fault-inject/include/linux/fault-inject.h =================================================================== --- work-fault-inject.orig/include/linux/fault-inject.h +++ work-fault-inject/include/linux/fault-inject.h @@ -18,6 +18,9 @@ struct fault_attr { atomic_t space; unsigned long verbose; u32 task_filter; + unsigned long stacktrace_depth; + unsigned long address_start; + unsigned long address_end; unsigned long count; @@ -32,6 +35,9 @@ struct fault_attr { struct dentry *space_file; struct dentry *verbose_file; struct dentry *task_filter_file; + struct dentry *stacktrace_depth_file; + struct dentry *address_start_file; + struct dentry *address_end_file; } entries; #endif @@ -41,6 +47,7 @@ struct fault_attr { struct fault_attr name = { \ .interval = 1, \ .times = ATOMIC_INIT(1), \ + .address_end = ULONG_MAX, \ } int setup_fault_attr(struct fault_attr *attr, char *str); Index: work-fault-inject/lib/Kconfig.debug =================================================================== --- work-fault-inject.orig/lib/Kconfig.debug +++ work-fault-inject/lib/Kconfig.debug @@ -472,6 +472,8 @@ config LKDTM config FAULT_INJECTION bool + select STACKTRACE + select FRAME_POINTER config FAILSLAB bool "fault-injection capabilitiy for kmalloc" -- - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/