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>
2011-11-17 06:29:17 +04:00
# include <linux/export.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>
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 .
*/
2011-09-14 01:03:28 +04:00
int setup_fault_attr ( struct fault_attr * attr , char * str )
2006-12-08 13:39:43 +03:00
{
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 ;
}
2011-09-14 01:03:28 +04:00
EXPORT_SYMBOL_GPL ( setup_fault_attr ) ;
2006-12-08 13:39:43 +03:00
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
}
2011-08-19 16:52:36 +04:00
EXPORT_SYMBOL_GPL ( should_fail ) ;
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
}
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 " ) ;
2011-07-24 12:33:43 +04:00
static struct dentry * debugfs_create_ul ( const char * name , umode_t mode ,
2006-12-08 13:39:43 +03:00
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
2011-07-27 03:09:01 +04:00
static int debugfs_stacktrace_depth_set ( void * data , u64 val )
{
* ( unsigned long * ) data =
min_t ( unsigned long , val , MAX_STACK_TRACE_DEPTH ) ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( fops_stacktrace_depth , debugfs_ul_get ,
debugfs_stacktrace_depth_set , " %llu \n " ) ;
static struct dentry * debugfs_create_stacktrace_depth (
2011-07-24 12:33:43 +04:00
const char * name , umode_t mode ,
2006-12-08 13:39:52 +03:00
struct dentry * parent , unsigned long * value )
{
return debugfs_create_file ( name , mode , parent , value ,
2011-07-27 03:09:01 +04:00
& fops_stacktrace_depth ) ;
2006-12-08 13:39:52 +03:00
}
2011-07-27 03:09:01 +04: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_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 " ) ;
2011-07-24 12:33:43 +04:00
static struct dentry * debugfs_create_atomic_t ( const char * name , umode_t mode ,
2006-12-08 13:39:43 +03:00
struct dentry * parent , atomic_t * value )
{
return debugfs_create_file ( name , mode , parent , value , & fops_atomic_t ) ;
}
2011-08-04 03:21:01 +04:00
struct dentry * fault_create_debugfs_attr ( const char * name ,
struct dentry * parent , struct fault_attr * attr )
2006-12-08 13:39:43 +03:00
{
2011-07-24 12:33:43 +04:00
umode_t mode = S_IFREG | S_IRUSR | S_IWUSR ;
2006-12-08 13:39:43 +03:00
struct dentry * dir ;
2011-08-04 03:21:01 +04:00
dir = debugfs_create_dir ( name , parent ) ;
2006-12-08 13:39:43 +03:00
if ( ! dir )
2011-08-04 03:21:01 +04:00
return ERR_PTR ( - ENOMEM ) ;
2006-12-08 13:39:43 +03:00
2011-07-27 03:09:02 +04:00
if ( ! debugfs_create_ul ( " probability " , mode , dir , & attr - > probability ) )
goto fail ;
if ( ! debugfs_create_ul ( " interval " , mode , dir , & attr - > interval ) )
goto fail ;
if ( ! debugfs_create_atomic_t ( " times " , mode , dir , & attr - > times ) )
goto fail ;
if ( ! debugfs_create_atomic_t ( " space " , mode , dir , & attr - > space ) )
goto fail ;
if ( ! debugfs_create_ul ( " verbose " , mode , dir , & attr - > verbose ) )
goto fail ;
if ( ! debugfs_create_bool ( " task-filter " , mode , dir , & attr - > task_filter ) )
2007-02-21 00:57:56 +03:00
goto fail ;
# ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER
2011-07-27 03:09:02 +04:00
if ( ! debugfs_create_stacktrace_depth ( " stacktrace-depth " , mode , dir ,
& attr - > stacktrace_depth ) )
goto fail ;
if ( ! debugfs_create_ul ( " require-start " , mode , dir ,
& attr - > require_start ) )
goto fail ;
if ( ! debugfs_create_ul ( " require-end " , mode , dir , & attr - > require_end ) )
goto fail ;
if ( ! debugfs_create_ul ( " reject-start " , mode , dir , & attr - > reject_start ) )
goto fail ;
if ( ! debugfs_create_ul ( " reject-end " , mode , dir , & attr - > reject_end ) )
2006-12-08 13:39:43 +03:00
goto fail ;
2007-02-21 00:57:56 +03:00
# endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
2011-08-04 03:21:01 +04:00
return dir ;
2006-12-08 13:39:43 +03:00
fail :
2011-08-04 03:21:01 +04:00
debugfs_remove_recursive ( dir ) ;
2011-07-27 03:09:02 +04:00
2011-08-04 03:21:01 +04:00
return ERR_PTR ( - ENOMEM ) ;
2006-12-08 13:39:43 +03:00
}
2011-08-19 16:52:36 +04:00
EXPORT_SYMBOL_GPL ( fault_create_debugfs_attr ) ;
2006-12-08 13:39:43 +03:00
# endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */