2006-12-08 13:39:43 +03:00
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/random.h>
2009-10-07 17:09:06 +04:00
# include <linux/sched.h>
2006-12-08 13:39:43 +03:00
# include <linux/stat.h>
# include <linux/types.h>
# include <linux/fs.h>
# include <linux/module.h>
2006-12-08 13:39:47 +03:00
# include <linux/interrupt.h>
2006-12-08 13:39:48 +03:00
# include <linux/stacktrace.h>
# include <linux/kallsyms.h>
2006-12-08 13:39:43 +03:00
# include <linux/fault-inject.h>
/*
* setup_fault_attr ( ) is a helper function for various __setup handlers , so it
* returns 0 on error , because that is what __setup handlers do .
*/
int __init setup_fault_attr ( struct fault_attr * attr , char * str )
{
unsigned long probability ;
unsigned long interval ;
int times ;
int space ;
/* "<interval>,<probability>,<space>,<times>" */
if ( sscanf ( str , " %lu,%lu,%d,%d " ,
& interval , & probability , & space , & times ) < 4 ) {
printk ( KERN_WARNING
" FAULT_INJECTION: failed to parse arguments \n " ) ;
return 0 ;
}
attr - > probability = probability ;
attr - > interval = interval ;
atomic_set ( & attr - > times , times ) ;
atomic_set ( & attr - > space , space ) ;
return 1 ;
}
static void fail_dump ( struct fault_attr * attr )
{
if ( attr - > verbose > 0 )
printk ( KERN_NOTICE " FAULT_INJECTION: forcing a failure \n " ) ;
if ( attr - > verbose > 1 )
dump_stack ( ) ;
}
# define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0)
2006-12-08 13:39:51 +03:00
static bool fail_task ( struct fault_attr * attr , struct task_struct * task )
2006-12-08 13:39:47 +03:00
{
return ! in_interrupt ( ) & & task - > make_it_fail ;
}
2006-12-08 13:39:52 +03:00
# define MAX_STACK_TRACE_DEPTH 32
2007-02-21 00:57:56 +03:00
# ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
2006-12-08 13:39:48 +03:00
2006-12-08 13:39:51 +03:00
static bool fail_stacktrace ( struct fault_attr * attr )
2006-12-08 13:39:48 +03:00
{
struct stack_trace trace ;
int depth = attr - > stacktrace_depth ;
unsigned long entries [ MAX_STACK_TRACE_DEPTH ] ;
int n ;
bool found = ( attr - > require_start = = 0 & & attr - > require_end = = ULONG_MAX ) ;
if ( depth = = 0 )
return found ;
trace . nr_entries = 0 ;
trace . entries = entries ;
2006-12-08 13:39:52 +03:00
trace . max_entries = depth ;
2006-12-08 13:39:48 +03:00
trace . skip = 1 ;
2007-05-08 11:23:29 +04:00
save_stack_trace ( & trace ) ;
2006-12-08 13:39:48 +03:00
for ( n = 0 ; n < trace . nr_entries ; n + + ) {
if ( attr - > reject_start < = entries [ n ] & &
entries [ n ] < attr - > reject_end )
2006-12-08 13:39:51 +03:00
return false ;
2006-12-08 13:39:48 +03:00
if ( attr - > require_start < = entries [ n ] & &
entries [ n ] < attr - > require_end )
2006-12-08 13:39:51 +03:00
found = true ;
2006-12-08 13:39:48 +03:00
}
return found ;
}
# else
2006-12-08 13:39:51 +03:00
static inline bool fail_stacktrace ( struct fault_attr * attr )
2006-12-08 13:39:48 +03:00
{
2007-02-21 00:57:56 +03:00
return true ;
2006-12-08 13:39:48 +03:00
}
2007-02-21 00:57:56 +03:00
# endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
2006-12-08 13:39:48 +03:00
2006-12-08 13:39:43 +03:00
/*
* This code is stolen from failmalloc - 1.0
* http : //www.nongnu.org/failmalloc/
*/
2006-12-08 13:39:51 +03:00
bool should_fail ( struct fault_attr * attr , ssize_t size )
2006-12-08 13:39:43 +03:00
{
2006-12-08 13:39:47 +03:00
if ( attr - > task_filter & & ! fail_task ( attr , current ) )
2006-12-08 13:39:51 +03:00
return false ;
2006-12-08 13:39:47 +03:00
2006-12-08 13:39:43 +03:00
if ( atomic_read ( & attr - > times ) = = 0 )
2006-12-08 13:39:51 +03:00
return false ;
2006-12-08 13:39:43 +03:00
if ( atomic_read ( & attr - > space ) > size ) {
atomic_sub ( size , & attr - > space ) ;
2006-12-08 13:39:51 +03:00
return false ;
2006-12-08 13:39:43 +03:00
}
if ( attr - > interval > 1 ) {
attr - > count + + ;
if ( attr - > count % attr - > interval )
2006-12-08 13:39:51 +03:00
return false ;
2006-12-08 13:39:43 +03:00
}
2006-12-08 13:39:53 +03:00
if ( attr - > probability < = random32 ( ) % 100 )
return false ;
2006-12-08 13:39:43 +03:00
2006-12-08 13:39:53 +03:00
if ( ! fail_stacktrace ( attr ) )
return false ;
2006-12-08 13:39:43 +03:00
fail_dump ( attr ) ;
if ( atomic_read ( & attr - > times ) ! = - 1 )
atomic_dec_not_zero ( & attr - > times ) ;
2006-12-08 13:39:51 +03:00
return true ;
2006-12-08 13:39:43 +03:00
}
# ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
2008-02-08 15:20:26 +03:00
static int debugfs_ul_set ( void * data , u64 val )
2006-12-08 13:39:43 +03:00
{
* ( unsigned long * ) data = val ;
2008-02-08 15:20:26 +03:00
return 0 ;
2006-12-08 13:39:43 +03:00
}
2007-07-24 05:43:55 +04:00
# ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
2008-02-08 15:20:26 +03:00
static int debugfs_ul_set_MAX_STACK_TRACE_DEPTH ( void * data , u64 val )
2006-12-08 13:39:52 +03:00
{
* ( unsigned long * ) data =
val < MAX_STACK_TRACE_DEPTH ?
val : MAX_STACK_TRACE_DEPTH ;
2008-02-08 15:20:26 +03:00
return 0 ;
2006-12-08 13:39:52 +03:00
}
2007-07-24 05:43:55 +04:00
# endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
2006-12-08 13:39:52 +03:00
2008-02-08 15:20:26 +03:00
static int debugfs_ul_get ( void * data , u64 * val )
2006-12-08 13:39:43 +03:00
{
2008-02-08 15:20:26 +03:00
* val = * ( unsigned long * ) data ;
return 0 ;
2006-12-08 13:39:43 +03:00
}
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 ) ;
}
2007-07-24 05:43:55 +04:00
# ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
2006-12-08 13:39:52 +03:00
DEFINE_SIMPLE_ATTRIBUTE ( fops_ul_MAX_STACK_TRACE_DEPTH , debugfs_ul_get ,
debugfs_ul_set_MAX_STACK_TRACE_DEPTH , " %llu \n " ) ;
static struct dentry * debugfs_create_ul_MAX_STACK_TRACE_DEPTH (
const char * name , mode_t mode ,
struct dentry * parent , unsigned long * value )
{
return debugfs_create_file ( name , mode , parent , value ,
& fops_ul_MAX_STACK_TRACE_DEPTH ) ;
}
2007-07-24 05:43:55 +04:00
# endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
2006-12-08 13:39:52 +03:00
2008-02-08 15:20:26 +03:00
static int debugfs_atomic_t_set ( void * data , u64 val )
2006-12-08 13:39:43 +03:00
{
atomic_set ( ( atomic_t * ) data , val ) ;
2008-02-08 15:20:26 +03:00
return 0 ;
2006-12-08 13:39:43 +03:00
}
2008-02-08 15:20:26 +03:00
static int debugfs_atomic_t_get ( void * data , u64 * val )
2006-12-08 13:39:43 +03:00
{
2008-02-08 15:20:26 +03:00
* val = atomic_read ( ( atomic_t * ) data ) ;
return 0 ;
2006-12-08 13:39:43 +03:00
}
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 ) ;
}
void cleanup_fault_attr_dentries ( struct fault_attr * attr )
{
debugfs_remove ( attr - > dentries . probability_file ) ;
attr - > dentries . probability_file = NULL ;
debugfs_remove ( attr - > dentries . interval_file ) ;
attr - > dentries . interval_file = NULL ;
debugfs_remove ( attr - > dentries . times_file ) ;
attr - > dentries . times_file = NULL ;
debugfs_remove ( attr - > dentries . space_file ) ;
attr - > dentries . space_file = NULL ;
debugfs_remove ( attr - > dentries . verbose_file ) ;
attr - > dentries . verbose_file = NULL ;
2006-12-08 13:39:47 +03:00
debugfs_remove ( attr - > dentries . task_filter_file ) ;
attr - > dentries . task_filter_file = NULL ;
2007-02-21 00:57:56 +03:00
# ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
2006-12-08 13:39:48 +03:00
debugfs_remove ( attr - > dentries . stacktrace_depth_file ) ;
attr - > dentries . stacktrace_depth_file = NULL ;
debugfs_remove ( attr - > dentries . require_start_file ) ;
attr - > dentries . require_start_file = NULL ;
debugfs_remove ( attr - > dentries . require_end_file ) ;
attr - > dentries . require_end_file = NULL ;
debugfs_remove ( attr - > dentries . reject_start_file ) ;
attr - > dentries . reject_start_file = NULL ;
debugfs_remove ( attr - > dentries . reject_end_file ) ;
attr - > dentries . reject_end_file = NULL ;
2007-02-21 00:57:56 +03:00
# endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
2006-12-08 13:39:43 +03:00
if ( attr - > dentries . dir )
WARN_ON ( ! simple_empty ( attr - > dentries . dir ) ) ;
debugfs_remove ( attr - > dentries . dir ) ;
attr - > dentries . dir = NULL ;
}
int init_fault_attr_dentries ( struct fault_attr * attr , const char * name )
{
mode_t mode = S_IFREG | S_IRUSR | S_IWUSR ;
struct dentry * dir ;
memset ( & attr - > dentries , 0 , sizeof ( attr - > dentries ) ) ;
dir = debugfs_create_dir ( name , NULL ) ;
if ( ! dir )
goto fail ;
attr - > dentries . dir = dir ;
attr - > dentries . probability_file =
debugfs_create_ul ( " probability " , mode , dir , & attr - > probability ) ;
attr - > dentries . interval_file =
debugfs_create_ul ( " interval " , mode , dir , & attr - > interval ) ;
attr - > dentries . times_file =
debugfs_create_atomic_t ( " times " , mode , dir , & attr - > times ) ;
attr - > dentries . space_file =
debugfs_create_atomic_t ( " space " , mode , dir , & attr - > space ) ;
attr - > dentries . verbose_file =
debugfs_create_ul ( " verbose " , mode , dir , & attr - > verbose ) ;
2006-12-08 13:39:47 +03:00
attr - > dentries . task_filter_file = debugfs_create_bool ( " task-filter " ,
mode , dir , & attr - > task_filter ) ;
2007-02-21 00:57:56 +03:00
if ( ! attr - > dentries . probability_file | | ! attr - > dentries . interval_file | |
! attr - > dentries . times_file | | ! attr - > dentries . space_file | |
! attr - > dentries . verbose_file | | ! attr - > dentries . task_filter_file )
goto fail ;
# ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
2006-12-08 13:39:48 +03:00
attr - > dentries . stacktrace_depth_file =
2006-12-08 13:39:52 +03:00
debugfs_create_ul_MAX_STACK_TRACE_DEPTH (
" stacktrace-depth " , mode , dir , & attr - > stacktrace_depth ) ;
2006-12-08 13:39:48 +03:00
attr - > dentries . require_start_file =
debugfs_create_ul ( " require-start " , mode , dir , & attr - > require_start ) ;
attr - > dentries . require_end_file =
debugfs_create_ul ( " require-end " , mode , dir , & attr - > require_end ) ;
attr - > dentries . reject_start_file =
debugfs_create_ul ( " reject-start " , mode , dir , & attr - > reject_start ) ;
attr - > dentries . reject_end_file =
debugfs_create_ul ( " reject-end " , mode , dir , & attr - > reject_end ) ;
2007-02-21 00:57:56 +03:00
if ( ! attr - > dentries . stacktrace_depth_file | |
! attr - > dentries . require_start_file | |
! attr - > dentries . require_end_file | |
! attr - > dentries . reject_start_file | |
! attr - > dentries . reject_end_file )
2006-12-08 13:39:43 +03:00
goto fail ;
2007-02-21 00:57:56 +03:00
# endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
2006-12-08 13:39:43 +03:00
return 0 ;
fail :
cleanup_fault_attr_dentries ( attr ) ;
return - ENOMEM ;
}
# endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */