RFC: fault-injection capabilities (v2)
Subsystems (6)
cover RFC: fault-injection capabilities (v2)
Changes from last version
- use lightweight random simulator instead of get_random_int()
- added per queue filter for disk IO failures (/sys/blocks/sda/sda1/make-it-fail, /sys/blocks/sda/make-it-fail)
- added process filter (/debug/{failslab,fail_page_alloc,fail_make_request}/process-filter, /proc/<pid>/make-it-fail)
---
This patch set provides some fault-injection capabilities.
- kmalloc failures
- alloc_pages() failures
- disk IO errors
We can see what really happens if those failures happen.
In order to enable these fault-injection capabilities:
1. Enable relevant config options (CONFIG_FAILSLAB, CONFIG_PAGE_ALLOC, CONFIG_MAKE_REQUEST) and runtime configuration kernel module (CONFIG_SHOULD_FAIL_KNOBS)
2. build and boot with this kernel
3. modprobe should_fail_knob
4. configure fault-injection capabilities behavior by debugfs
For example about kmalloc failures:
/debug/failslab/probability
specifies how often it should fail in percent.
/debug/failslab/interval
specifies the interval of failures.
/debug/failslab/times
specifies how many times failures may happen at most.
/debug/failslab/space
specifies the size of free space where memory can be allocated safely in bytes.
/debug/failslab/process-filter
enable process filter.
5. see what really happens.
PATCH 1/6 fault-injection capabilities infrastructure
This patch provides some functions for implement fault-injection capabilities.
Lightweight random simulator is taken from crasher module for SUSE kernel. The function should_fail() is taken from failmalloc-1.0
(http://www.nongnu.org/failmalloc/)
include/linux/should_fail.h | 45 ++++++++++++++++++++++++ lib/Kconfig.debug | 4 ++ lib/Makefile | 1 lib/should_fail.c | 82 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+)
include/linux/should_fail.h new file +45 -0
@@ -0,0 +1,45 @@ +#ifndef _LINUX_SHOULD_FAIL_H +#define _LINUX_SHOULD_FAIL_H + +#ifdef CONFIG_SHOULD_FAIL + +#include <linux/types.h> +#include <asm/atomic.h> + +struct should_fail_data { + +/* how often it should fail in percent. */ +unsigned long probability; + +/* the interval of failures. */ +unsigned long interval; + +/* +* how many times failures may happen at most. +* A value of '-1' means infinity. +*/ +atomic_t times; + +/* +* the size of free space where memory can be allocated safely. +* A value of '0' means infinity. +*/ +atomic_t space; + +unsigned long count; +}; + +#define DEFINE_SHOULD_FAIL (name) \ + struct should_fail_data name = { .times = ATOMIC_INIT(-1), } + +int setup_should_fail (struct should_fail_data *data,char *str); +void should_fail_srandom (unsigned long entropy); +int should_fail (struct should_fail_data *data,ssize_t size); + +#else + +#define should_fail (data, size) (0) + +#endif /* CONFIG_SHOULD_FAIL */ + +#endif /* _LINUX_SHOULD_FAIL_H */
lib/should_fail.c new file +82 -0
82 lines not on this page · raw message
lib/Kconfig.debug +4 -0
@@ -368,3 +368,7 @@ config RCU_TORTURE_TEST at boot time (you probably don't). Say M if you want the RCU torture tests to build as a module. Say N if you are unsure. + +config SHOULD_FAIL + bool +
lib/Makefile +1 -0
@@ -51,6 +51,7 @@ obj-$(CONFIG_TEXTSEARCH_FSM) += ts_fsm.o obj-$(CONFIG_SMP) += percpu_counter.o obj-$(CONFIG_SWIOTLB) += swiotlb.o +obj-$(CONFIG_SHOULD_FAIL) += should_fail.o hostprogs-y := gen_crc32table clean-files := crc32table.h
--
Re: [patch 1/6] fault-injection capabilities infrastructure
lib/should_fail.c
Quoted 6 lines · Akinobu Mita
Index:
work-shouldfail/lib/should_fail.c
===================================================================lib/should_fail.c new file +0
-0@@ -0,0 +1,82 @@
You really need some more comments in this file, describing what it does in a high level way. Or ideally something in Documentation/* describing what the various modules do and how to use them?
-Andi
PATCH 2/6 fault-injection capability for kmalloc.
In reply to Akinobu MitaThis patch provides fault-injection capability for kmalloc.
Boot option:
failslab=<probability>,<interval>,<times>,<space>
<probability>
specifies how often it should fail in percent.
<interval>
specifies the interval of failures.
<times>
specifies how many times failures may happen at most.
<space>
specifies the size of free space where memory can be allocated safely in bytes.
Example:
failslab=100,10,-1,0
slab allocation (kmalloc, kmem_cache_alloc,..) fails once per 10 times.
include/linux/should_fail.h | 4 ++++ lib/Kconfig.debug | 7 +++++++ mm/slab.c | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+)
mm/slab.c +21 -0
@@ -108,6 +108,7 @@#include <linux/mempolicy.h> #include <linux/mutex.h> #include <linux/rtmutex.h> +#include <linux/should_fail.h> #include <asm/uaccess.h> #include <asm/cacheflush.h>
@@ -2963,11 +2964,31 @@ static void *cache_alloc_debugcheck_afte#define cache_alloc_debugcheck_after (a,b,objp,d) (objp)#endif +#ifdef CONFIG_FAILSLAB + +static DEFINE_SHOULD_FAIL (failslab_data); + +static int __init setup_failslab (char *str) +{ +should_fail_srandom (jiffies); +return setup_should_fail (&failslab_data, str); +} +__setup ("failslab=" , setup_failslab); + +struct should_fail_data *failslab = &failslab_data; +EXPORT_SYMBOL_GPL (failslab); + +#endif +static inline void *____cache_alloc (struct kmem_cache *cachep,gfp_t flags) {void *objp;struct array_cache *ac; +if (!(flags & __GFP_NOFAIL) && cachep != &cache_cache && +should_fail (failslab,obj_size (cachep))) +return NULL ; +#ifdef CONFIG_NUMA if (unlikely (current->flags & (PF_SPREAD_SLAB |PF_MEMPOLICY ))) { objp =alternate_node_alloc (cachep, flags);
lib/Kconfig.debug +7 -0
@@ -372,3 +372,10 @@ config RCU_TORTURE_TEST config SHOULD_FAIL bool +config FAILSLAB + bool "fault-injection capabilitiy for kmalloc" + depends on DEBUG_KERNEL + select SHOULD_FAIL + help + This option provides fault-injection capabilitiy for kmalloc. +
include/linux/should_fail.h +4 -0
@@ -36,6 +36,10 @@ int setup_should_fail(struct should_failvoid should_fail_srandom (unsigned long entropy);int should_fail (struct should_fail_data *data,ssize_t size); +#ifdef CONFIG_FAILSLAB +extern struct should_fail_data *failslab; +#endif +#else #define should_fail (data, size) (0)
--
PATCH 3/6 fault-injection capability for alloc_pages()
In reply to Akinobu MitaThis patch provides fault-injection capability for alloc_pages()
boot option:
fail_page_alloc=<probability>,<interval>,<times>,<space>
<probability>
specifies how often it should fail in percent.
<interval>
specifies the interval of failures.
<times>
specifies how many times failures may happen at most.
<space>
specifies the size of free space where memory can be allocated safely in pages.
Example:
fail_page_alloc=100,10,-1,0
page allocation fails once per 10 times.
include/linux/should_fail.h | 4 ++++ lib/Kconfig.debug | 7 +++++++ mm/page_alloc.c | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+)
lib/Kconfig.debug +7 -0
@@ -379,3 +379,10 @@ config FAILSLAB help This option provides fault-injection capabilitiy for kmalloc. +config FAIL_PAGE_ALLOC + bool "fault-injection capabilitiy for alloc_pages()" + depends on DEBUG_KERNEL + select SHOULD_FAIL + help + This option provides fault-injection capabilitiy for alloc_pages(). +
mm/page_alloc.c +21 -0
@@ -37,6 +37,7 @@#include <linux/vmalloc.h> #include <linux/mempolicy.h> #include <linux/stop_machine.h> +#include <linux/should_fail.h> #include <asm/tlbflush.h> #include <asm/div64.h>
@@ -903,6 +904,22 @@ get_page_from_freelist(gfp_t gfp_mask, ureturn page; } +#ifdef CONFIG_FAIL_PAGE_ALLOC + +static DEFINE_SHOULD_FAIL (fail_page_alloc_data); + +static int __init setup_fail_page_alloc (char *str) +{ +should_fail_srandom (jiffies); +return setup_should_fail (&fail_page_alloc_data, str); +} +__setup ("fail_page_alloc=" , setup_fail_page_alloc); + +struct should_fail_data *fail_page_alloc = &fail_page_alloc_data; +EXPORT_SYMBOL_GPL (fail_page_alloc); + +#endif +/* * This is the 'heart' of the zoned buddy allocator. */
@@ -921,6 +938,10 @@ __alloc_pages(gfp_t gfp_mask, unsigned imight_sleep_if (wait); +if (!(gfp_mask & __GFP_NOFAIL) && +should_fail (fail_page_alloc,1 << order)) +return NULL ; + restart: z = zonelist->zones;/* the list of zones suitable for gfp_mask */
include/linux/should_fail.h +4 -0
@@ -40,6 +40,10 @@ int should_fail(struct should_fail_dataextern struct should_fail_data *failslab;#endif +#ifdef CONFIG_FAIL_PAGE_ALLOC +extern struct should_fail_data *fail_page_alloc; +#endif +#else #define should_fail (data, size) (0)
--
Re: [patch 3/6] fault-injection capability for alloc_pages()
Quoted 20 lines · Akinobu Mita
This patch provides fault-injection capability for
alloc_pages()boot option:
fail_page_alloc=<probability>,<interval>,<times>,<space>
<probability>
specifies how often it should fail in percent.
<interval>
specifies the interval of failures.
<times>
specifies how many times failures may happen at most.
<space>
Quoted 8 lines
specifies the size of free space where memory can be allocated safely in pages.
Example:
fail_page_alloc=100,10,-1,0page allocation fails once per 10 times.
I still think this will need some better filters to be useful. At least a optional uid filter perhaps (make sure to handle the interrupt case correctly, interrupts don't belong to the uid) , and perhaps an option to only fail GFP_ATOMIC.
With arbitary failing the system will just be unusable, right? Or would you run some system you use this way? @)
Another possibility would be to look up __builtin_return_address(0) in the module table and allow failing only for a specific module.
-andi
Re: [patch 3/6] fault-injection capability for alloc_pages()
Quoted 4 lines · Andi Kleen
I still think this will need some better filters to be useful. At least a optional uid filter perhaps (make sure to handle the interrupt case correctly, interrupts don't belong to the uid) , and perhaps an option to only fail
GFP_ATOMIC.
I wrote process filter. Please patch 6/6. But I forgot to ignore in_interrupt() case.
Quoted 5 lines
With arbitary failing the system will just be unusable, right? Or would you run some system you use this way? @)
Another possibility would be to look up
__builtin_return_address(0)in the module table and allow failing only for a specific module.
That will be useful. Thanks.
Re: [patch 3/6] fault-injection capability for alloc_pages()
Quoted 8 lines · Akinobu Mita
Quoted 4 lines · Andi Kleen
I still think this will need some better filters to be useful. At least a optional uid filter perhaps (make sure to handle the interrupt case correctly, interrupts don't belong to the uid) , and perhaps an option to only fail
GFP_ATOMIC.I wrote process filter.
Oops sorry. I overlooked that.
Quoted 2 lines
Please patch 6/6. But I forgot to ignore
in_interrupt()case.
Ok fine then.
Quoted 8 lines
Quoted 6 lines
With arbitary failing the system will just be unusable, right? Or would you run some system you use this way? @)
Another possibility would be to look up
__builtin_return_address(0)in the module table and allow failing only for a specific module.That will be useful. Thanks.
It might unfortunately need architecture specific code. But I guess a i386 only implementation as start would be useful enough.
-Andi
PATCH 4/6 fault-injection capability for disk IO
In reply to Akinobu MitaThis patch provides fault-injection capability for disk IO.
Boot option:
fail_make_request=<probability>,<interval>,<times>,<space>
<probability>
specifies how often it should fail in percent.
<interval>
specifies the interval of failures.
<times>
specifies how many times failures may happen at most.
<space>
specifies the size of free space where disk IO can be issued safely in bytes.
Example:
fail_make_request=100,10,-1,0 echo 1 > /sys/blocks/hda/hda1/make-it-fail
generic_make_request() on /dev/hda1 fails once per 10 times.
block/genhd.c | 31 +++++++++++++++++++++++++++++++ block/ll_rw_blk.c | 32 ++++++++++++++++++++++++++++++++ fs/partitions/check.c | 27 +++++++++++++++++++++++++++ include/linux/genhd.h | 4 ++++ include/linux/should_fail.h | 4 ++++ lib/Kconfig.debug | 7 +++++++ 6 files changed, 105 insertions(+)
block/ll_rw_blk.c +32 -0
@@ -28,6 +28,7 @@#include <linux/interrupt.h> #include <linux/cpu.h> #include <linux/blktrace_api.h> +#include <linux/should_fail.h> /* * for max sense size
@@ -2993,6 +2994,34 @@ static void handle_bad_sector(struct bioset_bit (BIO_EOF , &bio->bi_flags); } +#ifdef CONFIG_FAIL_MAKE_REQUEST + +static DEFINE_SHOULD_FAIL (fail_make_request_data); + +static int __init setup_fail_make_request (char *str) +{ +should_fail_srandom (jiffies); +return setup_should_fail (&fail_make_request_data, str); +} +__setup ("fail_make_request=" , setup_fail_make_request); + +struct should_fail_data *fail_make_request = &fail_make_request_data; +EXPORT_SYMBOL_GPL (fail_make_request); + +static int should_fail_request (struct should_fail_data *data,struct bio *bio) +{ +if ((bio->bi_bdev->bd_disk->flags &GENHD_FL_FAIL ) || + (bio->bi_bdev->bd_part && bio->bi_bdev->bd_part->make_it_fail)) +return should_fail (data, bio->bi_size); + +return 0 ; +} +#else + +#define should_fail_request (data, bio) (0) + +#endif +/** * generic_make_request: hand a buffer to its device driver for I/O * @bio: The bio describing the location in memory and on the device.
@@ -3077,6 +3106,9 @@ end_io:if (unlikely (test_bit (QUEUE_FLAG_DEAD , &q->queue_flags))) goto end_io; +if (should_fail_request (fail_make_request, bio)) + goto end_io; +/* * If this device has partitions, remap block n * of partition p to block n+start(p) of the disk.
lib/Kconfig.debug +7 -0
@@ -386,3 +386,10 @@ config FAIL_PAGE_ALLOC help This option provides fault-injection capabilitiy for alloc_pages(). +config FAIL_MAKE_REQUEST + bool "fault-injection capabilitiy for disk IO" + depends on DEBUG_KERNEL + select SHOULD_FAIL + help + This option provides fault-injection capabilitiy to disk IO. +
include/linux/should_fail.h +4 -0
@@ -44,6 +44,10 @@ extern struct should_fail_data *failslabextern struct should_fail_data *fail_page_alloc;#endif +#ifdef CONFIG_FAIL_MAKE_REQUEST +extern struct should_fail_data *fail_make_request; +#endif +#else #define should_fail (data, size) (0)
block/genhd.c +31 -0
@@ -412,6 +412,34 @@ static struct disk_attribute disk_attr_s .show = disk_stats_read }; +#ifdef CONFIG_FAIL_MAKE_REQUEST + +static ssize_t disk_fail_store (struct gendisk * disk, +const char *buf,size_t count) +{ +int i; + +if (count >0 &&sscanf (buf,"%d" , &i) >0 ) { +if (i ==0 ) + disk->flags &= ~GENHD_FL_FAIL ; +else + disk->flags |=GENHD_FL_FAIL ; + } + +return count; +} +static ssize_t disk_fail_read (struct gendisk * disk,char *page) +{ +return sprintf (page,"%d\n" , disk->flags &GENHD_FL_FAIL ?1 :0 ); +} +static struct disk_attribute disk_attr_fail = { + .attr = {.name ="make-it-fail" , .mode =S_IRUGO |S_IWUSR }, + .store = disk_fail_store, + .show = disk_fail_read +}; + +#endif +static struct attribute * default_attrs[] = { &disk_attr_uevent.attr, &disk_attr_dev.attr,
@@ -419,6 +447,9 @@ static struct attribute * default_attrs[ &disk_attr_removable.attr, &disk_attr_size.attr, &disk_attr_stat.attr, +#ifdef CONFIG_FAIL_MAKE_REQUEST + &disk_attr_fail.attr, +#endif NULL, };
include/linux/genhd.h +4 -0
@@ -81,6 +81,9 @@ struct hd_struct {struct kobject *holder_dir;unsigned ios[2 ], sectors[2 ];/* READs and WRITEs */ int policy, partno; +#ifdef CONFIG_FAIL_MAKE_REQUEST +int make_it_fail; +#endif };#define GENHD_FL_REMOVABLE 1
@@ -88,6 +91,7 @@ struct hd_struct {#define GENHD_FL_CD 8#define GENHD_FL_UP 16#define GENHD_FL_SUPPRESS_PARTITION_INFO 32 +#define GENHD_FL_FAIL 64struct disk_stats {unsigned long sectors[2 ];/* READs and WRITEs */
fs/partitions/check.c +27 -0
@@ -265,12 +265,39 @@ static struct part_attribute part_attr_s .show = part_stat_read }; +#ifdef CONFIG_FAIL_MAKE_REQUEST + +static ssize_t part_fail_store (struct hd_struct * p, +const char *buf,size_t count) +{ +int i; + +if (count >0 &&sscanf (buf,"%d" , &i) >0 ) + p->make_it_fail = (i ==0 ) ?0 :1 ; + +return count; +} +static ssize_t part_fail_read (struct hd_struct * p,char *page) +{ +return sprintf (page,"%d\n" , p->make_it_fail); +} +static struct part_attribute part_attr_fail = { + .attr = {.name ="make-it-fail" , .mode =S_IRUGO |S_IWUSR }, + .store = part_fail_store, + .show = part_fail_read +}; + +#endif +static struct attribute * default_attrs[] = { &part_attr_uevent.attr, &part_attr_dev.attr, &part_attr_start.attr, &part_attr_size.attr, &part_attr_stat.attr, +#ifdef CONFIG_FAIL_MAKE_REQUEST + &part_attr_fail.attr, +#endifNULL , };
--
PATCH 5/6 debugfs entries for configuration
In reply to Akinobu MitaThis kernel module provides debugfs entries for fault-injection capabilities configuation.
/debug/
|-- fail_make_request
| |-- interval
| |-- probability
| |-- space
| `-- times
|-- fail_page_alloc
| |-- interval
| |-- probability
| |-- space
| `-- times
`-- failslab
|-- interval
|-- probability
|-- space
`-- timeslib/Kconfig.debug | 8 ++ lib/Makefile | 1 lib/should_fail_knobs.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+)
lib/should_fail_knobs.c new file +168 -0
@@ -0,0 +1,168 @@ +#include <linux/module.h> +#include <linux/should_fail.h> +#include <linux/debugfs.h> + +struct should_fail_knobs { +struct dentry *dir; +struct dentry *probability_file; +struct dentry *interval_file; +struct dentry *times_file; +struct dentry *space_file; +}; + +static void debugfs_ul_set (void *data,u64 val) +{ + *(unsigned long *)data = val; +} + +static u64 debugfs_ul_get (void *data) +{ +return *(unsigned long *)data; +} + +DEFINE_SIMPLE_ATTRIBUTE (fops_ul, debugfs_ul_get, debugfs_ul_set,"%llu\n" ); + +static struct dentry *debugfs_create_ul (const char *name,mode_t mode, +struct dentry *parent,unsigned long *value) +{ +return debugfs_create_file (name, mode, parent, value, &fops_ul); +} + +static void debugfs_atomic_t_set (void *data,u64 val) +{ +atomic_set ((atomic_t *)data, val); +} + +static u64 debugfs_atomic_t_get (void *data) +{ +return atomic_read ((atomic_t *)data); +} + +DEFINE_SIMPLE_ATTRIBUTE (fops_atomic_t, debugfs_atomic_t_get, + debugfs_atomic_t_set,"%lld\n" ); + +static struct dentry *debugfs_create_atomic_t (const char *name,mode_t mode, +struct dentry *parent,atomic_t *value) +{ +return debugfs_create_file (name, mode, parent, value, &fops_atomic_t); +} + +static void cleanup_should_fail_knobs (struct should_fail_knobs *knobs) +{ +if (knobs->dir) { +if (knobs->probability_file) { +debugfs_remove (knobs->probability_file); + knobs->probability_file =NULL ; + } +if (knobs->interval_file) { +debugfs_remove (knobs->interval_file); + knobs->interval_file =NULL ; + } +if (knobs->times_file) { +debugfs_remove (knobs->times_file); + knobs->times_file =NULL ; + } +if (knobs->space_file) { +debugfs_remove (knobs->space_file); + knobs->space_file =NULL ; + } +debugfs_remove (knobs->dir); + knobs->dir =NULL ; + } +} + +static int init_should_fail_knobs (struct should_fail_knobs *knobs, +struct should_fail_data *data,const char *name) +{ +mode_t mode =S_IFREG |S_IRUSR |S_IWUSR ; +struct dentry *dir; +struct dentry *file; + +memset (knobs,0 ,sizeof (*knobs)); + + dir =debugfs_create_dir (name,NULL ); +if (!dir) + goto fail; + knobs->dir = dir; + + file =debugfs_create_ul ("probability" , mode, dir, &data->probability); +if (!file) + goto fail; + knobs->probability_file = file; + + file =debugfs_create_ul ("interval" , mode, dir, &data->interval); +if (!file) + goto fail; + knobs->interval_file = file; + + file =debugfs_create_atomic_t ("times" , mode, dir, &data->times); +if (!file) + goto fail; + knobs->times_file = file; + + file =debugfs_create_atomic_t ("space" , mode, dir, &data->space); +if (!file) + goto fail; + knobs->space_file = file; + +return 0 ; +fail: +cleanup_should_fail_knobs (knobs); +return -ENOMEM ; +} + +#ifdef CONFIG_FAILSLAB +static struct should_fail_knobs failslab_knobs; +#endif +#ifdef CONFIG_FAIL_PAGE_ALLOC +static struct should_fail_knobs fail_page_alloc_knobs; +#endif +#ifdef CONFIG_FAIL_MAKE_REQUEST +static struct should_fail_knobs fail_make_request_knobs; +#endif + +static void cleanup_knobs (void ) +{ +#ifdef CONFIG_FAILSLAB +cleanup_should_fail_knobs (&fail_make_request_knobs); +#endif +#ifdef CONFIG_FAIL_PAGE_ALLOC +cleanup_should_fail_knobs (&fail_page_alloc_knobs); +#endif +#ifdef CONFIG_FAIL_MAKE_REQUEST +cleanup_should_fail_knobs (&failslab_knobs); +#endif +} + +static int init_knobs (void ) +{ +int err; + +#ifdef CONFIG_FAILSLAB + err =init_should_fail_knobs (&failslab_knobs, failslab,"failslab" ); +if (err) + goto fail; +#endif +#ifdef CONFIG_FAIL_PAGE_ALLOC + err =init_should_fail_knobs (&fail_page_alloc_knobs, fail_page_alloc, +"fail_page_alloc" ); +if (err) + goto fail; +#endif +#ifdef CONFIG_FAIL_MAKE_REQUEST + err =init_should_fail_knobs (&fail_make_request_knobs, + fail_make_request,"fail_make_request" ); +if (err) + goto fail; +#endif + +return 0 ; +fail: +cleanup_knobs (); + +return err; +} + +module_init (init_knobs); +module_exit (cleanup_knobs); +MODULE_LICENSE ("GPL" );
lib/Kconfig.debug +8 -0
@@ -393,3 +393,11 @@ config FAIL_MAKE_REQUEST help This option provides fault-injection capabilitiy to disk IO. +config SHOULD_FAIL_KNOBS + tristate "runtime configuration for fault-injection capabilities" + depends on DEBUG_KERNEL && SYSFS && SHOULD_FAIL + select DEBUG_FS + help + This option provides kernel module that provides runtime + configuration interface by debugfs. +
lib/Makefile +1 -0
@@ -52,6 +52,7 @@ obj-$(CONFIG_SMP) += percpu_counter.o obj-$(CONFIG_SWIOTLB) += swiotlb.o obj-$(CONFIG_SHOULD_FAIL) += should_fail.o +obj-$(CONFIG_SHOULD_FAIL_KNOBS) += should_fail_knobs.o hostprogs-y := gen_crc32table clean-files := crc32table.h
--
PATCH 6/6 process filtering for fault-injection capabilities
In reply to Akinobu MitaThis patch provides process filtering feature.
Example:
# modprobe should_fail_knobs
# echo 1 > /debug/failslab/process-filter
# echo 30 > /debug/failslab/probability
# find /lib/modules/`uname -r` -name '*.ko' -exec basename {} .ko \; > list
# for i in `cat list`;do failmodprobe $i;donefailmodprobe:
-------------
#!/bin/bash
echo 1 > /proc/self/make-it-fail
exec modprobe $*
fs/proc/base.c | 77 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/sched.h | 3 + include/linux/should_fail.h | 3 + lib/should_fail.c | 11 ++++-- lib/should_fail_knobs.c | 10 +++++ 5 files changed, 101 insertions(+), 3 deletions(-)
fs/proc/base.c +77 -0
@@ -138,6 +138,9 @@ enum pid_directory_inos {#endif PROC_TGID_OOM_SCORE, PROC_TGID_OOM_ADJUST, +#ifdef CONFIG_SHOULD_FAIL +PROC_TGID_SHOULD_FAIL , +#endifPROC_TID_INO ,PROC_TID_STATUS , PROC_TID_MEM,
@@ -181,6 +184,9 @@ enum pid_directory_inos {#endif PROC_TID_OOM_SCORE, PROC_TID_OOM_ADJUST, +#ifdef CONFIG_SHOULD_FAIL + PROC_TID_SHOULD_FAIL, +#endif /* Add new entries before this */ PROC_TID_FD_DIR = 0x8000, /* 0x8000-0xffff */
@@ -240,6 +246,9 @@ static struct pid_entry tgid_base_stuff[#ifdef CONFIG_AUDITSYSCALL E(PROC_TGID_LOGINUID, "loginuid", S_IFREG|S_IWUSR|S_IRUGO),#endif +#ifdef CONFIG_SHOULD_FAIL + E(PROC_TGID_SHOULD_FAIL, "make-it-fail", S_IFREG|S_IWUSR|S_IRUGO), +#endif {0,0,NULL,0} }; static struct pid_entry tid_base_stuff[] = {
@@ -282,6 +291,9 @@ static struct pid_entry tid_base_stuff[]#ifdef CONFIG_AUDITSYSCALL E(PROC_TID_LOGINUID, "loginuid", S_IFREG|S_IWUSR|S_IRUGO),#endif +#ifdef CONFIG_SHOULD_FAIL +E (PROC_TID_SHOULD_FAIL ,"make-it-fail" ,S_IFREG |S_IWUSR |S_IRUGO ), +#endif {0,0,NULL,0} };
@@ -992,6 +1004,65 @@ static struct file_operations proc_login };#endif +#ifdef CONFIG_SHOULD_FAIL +static ssize_t proc_should_fail_read (struct file * file,char __user * buf, +size_t count,loff_t *ppos) +{ +struct task_struct *task =get_proc_task (file->f_dentry->d_inode); +char buffer[PROC_NUMBUF ]; +size_t len; +int make_it_fail; +loff_t __ppos = *ppos; + +if (!task) +return -ESRCH ; + make_it_fail = task->make_it_fail; +put_task_struct (task); + + len =snprintf (buffer,sizeof (buffer),"%i\n" , make_it_fail); +if (__ppos >= len) +return 0 ; +if (count > len-__ppos) + count = len-__ppos; +if (copy_to_user (buf, buffer + __ppos, count)) +return -EFAULT ; + *ppos = __ppos + count; +return count; +} + +static ssize_t proc_should_fail_write (struct file * file, +const char __user * buf,size_t count,loff_t *ppos) +{ +struct task_struct *task; +char buffer[PROC_NUMBUF ], *end; +int make_it_fail; + +if (!capable (CAP_SYS_RESOURCE )) +return -EPERM ; +memset (buffer,0 ,sizeof (buffer)); +if (count >sizeof (buffer) -1 ) + count =sizeof (buffer) -1 ; +if (copy_from_user (buffer, buf, count)) +return -EFAULT ; + make_it_fail =simple_strtol (buffer, &end,0 ); +if (*end =='\n' ) + end++; + task =get_proc_task (file->f_dentry->d_inode); +if (!task) +return -ESRCH ; + task->make_it_fail = make_it_fail; +put_task_struct (task); +if (end - buffer ==0 ) +return -EIO ; +return end - buffer; +} + +static struct file_operations proc_should_fail_operations = { + .read = proc_should_fail_read, + .write = proc_should_fail_write, +}; +#endif +#ifdef CONFIG_SECCOMP static ssize_t seccomp_read (struct file *file,char __user *buf,size_t count,loff_t *ppos)
@@ -1834,6 +1905,12 @@ static struct dentry *proc_pident_lookup inode->i_fop = &proc_loginuid_operations;break ;#endif +#ifdef CONFIG_SHOULD_FAIL +case PROC_TID_SHOULD_FAIL : +case PROC_TGID_SHOULD_FAIL : + inode->i_fop = &proc_should_fail_operations; +break ; +#endif default :printk ("procfs: impossible type (%d)" ,p->type);iput (inode);
include/linux/sched.h +3 -0
@@ -997,6 +997,9 @@ struct task_struct {spinlock_t delays_lock;struct task_delay_info *delays;#endif +#ifdef CONFIG_SHOULD_FAIL +int make_it_fail; +#endif };static inline pid_t process_group (struct task_struct *tsk)
include/linux/should_fail.h +3 -0
@@ -27,6 +27,9 @@ struct should_fail_data {atomic_t space;unsigned long count; + +/* process filter */ +unsigned long process_filter; };#define DEFINE_SHOULD_FAIL (name) \
lib/should_fail.c +8 -3
@@ -13,16 +13,18 @@ int setup_should_fail(struct should_failunsigned long interval;int times;int space; +unsigned long filter; -/* "<probability>,<interval>,<times>,<space>" */ -if (sscanf (str,"%lu,%lu,%d,%d" , &probability, &interval, ×, - &space) <4 ) +/* "<probability>,<interval>,<times>,<space>,<process-filter>" */ +if (sscanf (str,"%lu,%lu,%d,%d,%d" , &probability, &interval, ×, + &space, &filter) <5 )return 0 ; data->probability = probability; data->interval = interval;atomic_set (&data->times, times);atomic_set (&data->space, space); + data->process_filter = filter;return 1 ; }
@@ -54,6 +56,9 @@ void should_fail_srandom(unsigned long eint should_fail (struct should_fail_data *data,ssize_t size) { +if (data->process_filter && !current->make_it_fail) +return 0 ; +if (atomic_read (&max_failures (data)) ==0 )return 0 ;
lib/should_fail_knobs.c +10 -0
@@ -8,6 +8,7 @@ struct should_fail_knobs {struct dentry *interval_file;struct dentry *times_file;struct dentry *space_file; +struct dentry *filter_file; };static void debugfs_ul_set (void *data,u64 val)
@@ -66,6 +67,10 @@ static void cleanup_should_fail_knobs(stdebugfs_remove (knobs->space_file); knobs->space_file =NULL ; } +if (knobs->filter_file) { +debugfs_remove (knobs->filter_file); + knobs->filter_file =NULL ; + }debugfs_remove (knobs->dir); knobs->dir =NULL ; }
@@ -105,6 +110,11 @@ static int init_should_fail_knobs(struct goto fail; knobs->space_file = file; + file =debugfs_create_ul ("process-filter" , mode, dir, &data->process_filter); +if (!file) + goto fail; + knobs->filter_file = file; +return 0 ; fail:cleanup_should_fail_knobs (knobs);
--
Quoted 9 lines · Akinobu Mita
This patch set provides some fault-injection capabilities.
- kmalloc failures
-
alloc_pages()failures- disk IO errors
We can see what really happens if those failures happen.
Looks very useful for testing error paths; nice work.
Should this perhaps taint the kernel when used?
- Josh Triplett
Quoted 7 lines · Josh Triplett
Quoted 6 lines · Akinobu Mita
This patch set provides some fault-injection capabilities.
- kmalloc failures
-
alloc_pages()failures
Quoted 7 lines
Quoted 3 lines
- disk IO errors
We can see what really happens if those failures happen.
Looks very useful for testing error paths; nice work.
Should this perhaps taint the kernel when used?
It shouldn't; these are all failures that could quite possibly happen during normal operation even without this enabled, they're just a few orders of magnitude less likely (in most situations).
Cheers,
Kyle moffett