linux/lib/fault-inject.c
<<
>>
Prefs
   1#include <linux/kernel.h>
   2#include <linux/init.h>
   3#include <linux/random.h>
   4#include <linux/stat.h>
   5#include <linux/types.h>
   6#include <linux/fs.h>
   7#include <linux/module.h>
   8#include <linux/interrupt.h>
   9#include <linux/stacktrace.h>
  10#include <linux/kallsyms.h>
  11#include <linux/fault-inject.h>
  12
  13/*
  14 * setup_fault_attr() is a helper function for various __setup handlers, so it
  15 * returns 0 on error, because that is what __setup handlers do.
  16 */
  17int __init setup_fault_attr(struct fault_attr *attr, char *str)
  18{
  19        unsigned long probability;
  20        unsigned long interval;
  21        int times;
  22        int space;
  23
  24        /* "<interval>,<probability>,<space>,<times>" */
  25        if (sscanf(str, "%lu,%lu,%d,%d",
  26                        &interval, &probability, &space, &times) < 4) {
  27                printk(KERN_WARNING
  28                        "FAULT_INJECTION: failed to parse arguments\n");
  29                return 0;
  30        }
  31
  32        attr->probability = probability;
  33        attr->interval = interval;
  34        atomic_set(&attr->times, times);
  35        atomic_set(&attr->space, space);
  36
  37        return 1;
  38}
  39
  40static void fail_dump(struct fault_attr *attr)
  41{
  42        if (attr->verbose > 0)
  43                printk(KERN_NOTICE "FAULT_INJECTION: forcing a failure\n");
  44        if (attr->verbose > 1)
  45                dump_stack();
  46}
  47
  48#define atomic_dec_not_zero(v)          atomic_add_unless((v), -1, 0)
  49
  50static bool fail_task(struct fault_attr *attr, struct task_struct *task)
  51{
  52        return !in_interrupt() && task->make_it_fail;
  53}
  54
  55#define MAX_STACK_TRACE_DEPTH 32
  56
  57#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
  58
  59static bool fail_stacktrace(struct fault_attr *attr)
  60{
  61        struct stack_trace trace;
  62        int depth = attr->stacktrace_depth;
  63        unsigned long entries[MAX_STACK_TRACE_DEPTH];
  64        int n;
  65        bool found = (attr->require_start == 0 && attr->require_end == ULONG_MAX);
  66
  67        if (depth == 0)
  68                return found;
  69
  70        trace.nr_entries = 0;
  71        trace.entries = entries;
  72        trace.max_entries = depth;
  73        trace.skip = 1;
  74
  75        save_stack_trace(&trace);
  76        for (n = 0; n < trace.nr_entries; n++) {
  77                if (attr->reject_start <= entries[n] &&
  78                               entries[n] < attr->reject_end)
  79                        return false;
  80                if (attr->require_start <= entries[n] &&
  81                               entries[n] < attr->require_end)
  82                        found = true;
  83        }
  84        return found;
  85}
  86
  87#else
  88
  89static inline bool fail_stacktrace(struct fault_attr *attr)
  90{
  91        return true;
  92}
  93
  94#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
  95
  96/*
  97 * This code is stolen from failmalloc-1.0
  98 * http://www.nongnu.org/failmalloc/
  99 */
 100
 101bool should_fail(struct fault_attr *attr, ssize_t size)
 102{
 103        if (attr->task_filter && !fail_task(attr, current))
 104                return false;
 105
 106        if (atomic_read(&attr->times) == 0)
 107                return false;
 108
 109        if (atomic_read(&attr->space) > size) {
 110                atomic_sub(size, &attr->space);
 111                return false;
 112        }
 113
 114        if (attr->interval > 1) {
 115                attr->count++;
 116                if (attr->count % attr->interval)
 117                        return false;
 118        }
 119
 120        if (attr->probability <= random32() % 100)
 121                return false;
 122
 123        if (!fail_stacktrace(attr))
 124                return false;
 125
 126        fail_dump(attr);
 127
 128        if (atomic_read(&attr->times) != -1)
 129                atomic_dec_not_zero(&attr->times);
 130
 131        return true;
 132}
 133
 134#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
 135
 136static int debugfs_ul_set(void *data, u64 val)
 137{
 138        *(unsigned long *)data = val;
 139        return 0;
 140}
 141
 142#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
 143static int debugfs_ul_set_MAX_STACK_TRACE_DEPTH(void *data, u64 val)
 144{
 145        *(unsigned long *)data =
 146                val < MAX_STACK_TRACE_DEPTH ?
 147                val : MAX_STACK_TRACE_DEPTH;
 148        return 0;
 149}
 150#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
 151
 152static int debugfs_ul_get(void *data, u64 *val)
 153{
 154        *val = *(unsigned long *)data;
 155        return 0;
 156}
 157
 158DEFINE_SIMPLE_ATTRIBUTE(fops_ul, debugfs_ul_get, debugfs_ul_set, "%llu\n");
 159
 160static struct dentry *debugfs_create_ul(const char *name, mode_t mode,
 161                                struct dentry *parent, unsigned long *value)
 162{
 163        return debugfs_create_file(name, mode, parent, value, &fops_ul);
 164}
 165
 166#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
 167DEFINE_SIMPLE_ATTRIBUTE(fops_ul_MAX_STACK_TRACE_DEPTH, debugfs_ul_get,
 168                        debugfs_ul_set_MAX_STACK_TRACE_DEPTH, "%llu\n");
 169
 170static struct dentry *debugfs_create_ul_MAX_STACK_TRACE_DEPTH(
 171        const char *name, mode_t mode,
 172        struct dentry *parent, unsigned long *value)
 173{
 174        return debugfs_create_file(name, mode, parent, value,
 175                                   &fops_ul_MAX_STACK_TRACE_DEPTH);
 176}
 177#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
 178
 179static int debugfs_atomic_t_set(void *data, u64 val)
 180{
 181        atomic_set((atomic_t *)data, val);
 182        return 0;
 183}
 184
 185static int debugfs_atomic_t_get(void *data, u64 *val)
 186{
 187        *val = atomic_read((atomic_t *)data);
 188        return 0;
 189}
 190
 191DEFINE_SIMPLE_ATTRIBUTE(fops_atomic_t, debugfs_atomic_t_get,
 192                        debugfs_atomic_t_set, "%lld\n");
 193
 194static struct dentry *debugfs_create_atomic_t(const char *name, mode_t mode,
 195                                struct dentry *parent, atomic_t *value)
 196{
 197        return debugfs_create_file(name, mode, parent, value, &fops_atomic_t);
 198}
 199
 200void cleanup_fault_attr_dentries(struct fault_attr *attr)
 201{
 202        debugfs_remove(attr->dentries.probability_file);
 203        attr->dentries.probability_file = NULL;
 204
 205        debugfs_remove(attr->dentries.interval_file);
 206        attr->dentries.interval_file = NULL;
 207
 208        debugfs_remove(attr->dentries.times_file);
 209        attr->dentries.times_file = NULL;
 210
 211        debugfs_remove(attr->dentries.space_file);
 212        attr->dentries.space_file = NULL;
 213
 214        debugfs_remove(attr->dentries.verbose_file);
 215        attr->dentries.verbose_file = NULL;
 216
 217        debugfs_remove(attr->dentries.task_filter_file);
 218        attr->dentries.task_filter_file = NULL;
 219
 220#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
 221
 222        debugfs_remove(attr->dentries.stacktrace_depth_file);
 223        attr->dentries.stacktrace_depth_file = NULL;
 224
 225        debugfs_remove(attr->dentries.require_start_file);
 226        attr->dentries.require_start_file = NULL;
 227
 228        debugfs_remove(attr->dentries.require_end_file);
 229        attr->dentries.require_end_file = NULL;
 230
 231        debugfs_remove(attr->dentries.reject_start_file);
 232        attr->dentries.reject_start_file = NULL;
 233
 234        debugfs_remove(attr->dentries.reject_end_file);
 235        attr->dentries.reject_end_file = NULL;
 236
 237#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
 238
 239        if (attr->dentries.dir)
 240                WARN_ON(!simple_empty(attr->dentries.dir));
 241
 242        debugfs_remove(attr->dentries.dir);
 243        attr->dentries.dir = NULL;
 244}
 245
 246int init_fault_attr_dentries(struct fault_attr *attr, const char *name)
 247{
 248        mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
 249        struct dentry *dir;
 250
 251        memset(&attr->dentries, 0, sizeof(attr->dentries));
 252
 253        dir = debugfs_create_dir(name, NULL);
 254        if (!dir)
 255                goto fail;
 256        attr->dentries.dir = dir;
 257
 258        attr->dentries.probability_file =
 259                debugfs_create_ul("probability", mode, dir, &attr->probability);
 260
 261        attr->dentries.interval_file =
 262                debugfs_create_ul("interval", mode, dir, &attr->interval);
 263
 264        attr->dentries.times_file =
 265                debugfs_create_atomic_t("times", mode, dir, &attr->times);
 266
 267        attr->dentries.space_file =
 268                debugfs_create_atomic_t("space", mode, dir, &attr->space);
 269
 270        attr->dentries.verbose_file =
 271                debugfs_create_ul("verbose", mode, dir, &attr->verbose);
 272
 273        attr->dentries.task_filter_file = debugfs_create_bool("task-filter",
 274                                                mode, dir, &attr->task_filter);
 275
 276        if (!attr->dentries.probability_file || !attr->dentries.interval_file ||
 277            !attr->dentries.times_file || !attr->dentries.space_file ||
 278            !attr->dentries.verbose_file || !attr->dentries.task_filter_file)
 279                goto fail;
 280
 281#ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
 282
 283        attr->dentries.stacktrace_depth_file =
 284                debugfs_create_ul_MAX_STACK_TRACE_DEPTH(
 285                        "stacktrace-depth", mode, dir, &attr->stacktrace_depth);
 286
 287        attr->dentries.require_start_file =
 288                debugfs_create_ul("require-start", mode, dir, &attr->require_start);
 289
 290        attr->dentries.require_end_file =
 291                debugfs_create_ul("require-end", mode, dir, &attr->require_end);
 292
 293        attr->dentries.reject_start_file =
 294                debugfs_create_ul("reject-start", mode, dir, &attr->reject_start);
 295
 296        attr->dentries.reject_end_file =
 297                debugfs_create_ul("reject-end", mode, dir, &attr->reject_end);
 298
 299        if (!attr->dentries.stacktrace_depth_file ||
 300            !attr->dentries.require_start_file ||
 301            !attr->dentries.require_end_file ||
 302            !attr->dentries.reject_start_file ||
 303            !attr->dentries.reject_end_file)
 304                goto fail;
 305
 306#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
 307
 308        return 0;
 309fail:
 310        cleanup_fault_attr_dentries(attr);
 311        return -ENOMEM;
 312}
 313
 314#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
 315