2006-10-02 13:17:36 +04:00
/*
* Kprobe module for testing crash dumps
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*
* Copyright ( C ) IBM Corporation , 2006
*
* Author : Ankita Garg < ankita @ in . ibm . com >
*
* This module induces system failures at predefined crashpoints to
* evaluate the reliability of crash dumps obtained using different dumping
* solutions .
*
* It is adapted from the Linux Kernel Dump Test Tool by
* Fernando Luis Vazquez Cao < http : //lkdtt.sourceforge.net>
*
* Usage : insmod lkdtm . ko [ recur_count = { > 0 } ] cpoint_name = < > cpoint_type = < >
* [ cpoint_count = { > 0 } ]
*
* recur_count : Recursion level for the stack overflow test . Default is 10.
*
* cpoint_name : Crash point where the kernel is to be crashed . It can be
* one of INT_HARDWARE_ENTRY , INT_HW_IRQ_EN , INT_TASKLET_ENTRY ,
* FS_DEVRW , MEM_SWAPOUT , TIMERADD , SCSI_DISPATCH_CMD ,
* IDE_CORE_CP
*
* cpoint_type : Indicates the action to be taken on hitting the crash point .
* It can be one of PANIC , BUG , EXCEPTION , LOOP , OVERFLOW
*
* cpoint_count : Indicates the number of times the crash point is to be hit
* to trigger an action . The default is 10.
*/
# include <linux/kernel.h>
2006-11-03 09:07:06 +03:00
# include <linux/fs.h>
2006-10-02 13:17:36 +04:00
# include <linux/module.h>
2006-11-03 09:07:06 +03:00
# include <linux/buffer_head.h>
2006-10-02 13:17:36 +04:00
# include <linux/kprobes.h>
2006-11-03 09:07:06 +03:00
# include <linux/list.h>
2006-10-02 13:17:36 +04:00
# include <linux/init.h>
# include <linux/interrupt.h>
2006-11-03 09:07:06 +03:00
# include <linux/hrtimer.h>
2006-10-02 13:17:36 +04:00
# include <scsi/scsi_cmnd.h>
# ifdef CONFIG_IDE
# include <linux/ide.h>
# endif
# define NUM_CPOINTS 8
# define NUM_CPOINT_TYPES 5
# define DEFAULT_COUNT 10
# define REC_NUM_DEFAULT 10
enum cname {
INVALID ,
INT_HARDWARE_ENTRY ,
INT_HW_IRQ_EN ,
INT_TASKLET_ENTRY ,
FS_DEVRW ,
MEM_SWAPOUT ,
TIMERADD ,
SCSI_DISPATCH_CMD ,
IDE_CORE_CP
} ;
enum ctype {
NONE ,
PANIC ,
BUG ,
EXCEPTION ,
LOOP ,
OVERFLOW
} ;
static char * cp_name [ ] = {
" INT_HARDWARE_ENTRY " ,
" INT_HW_IRQ_EN " ,
" INT_TASKLET_ENTRY " ,
" FS_DEVRW " ,
" MEM_SWAPOUT " ,
" TIMERADD " ,
" SCSI_DISPATCH_CMD " ,
" IDE_CORE_CP "
} ;
static char * cp_type [ ] = {
" PANIC " ,
" BUG " ,
" EXCEPTION " ,
" LOOP " ,
" OVERFLOW "
} ;
static struct jprobe lkdtm ;
static int lkdtm_parse_commandline ( void ) ;
static void lkdtm_handler ( void ) ;
2007-02-09 19:05:17 +03:00
static char * cpoint_name ;
static char * cpoint_type ;
2006-10-02 13:17:36 +04:00
static int cpoint_count = DEFAULT_COUNT ;
static int recur_count = REC_NUM_DEFAULT ;
static enum cname cpoint = INVALID ;
static enum ctype cptype = NONE ;
static int count = DEFAULT_COUNT ;
module_param ( recur_count , int , 0644 ) ;
2006-11-03 09:07:06 +03:00
MODULE_PARM_DESC ( recur_count , " Recursion level for the stack overflow test, " \
" default is 10 " ) ;
2006-10-02 13:17:36 +04:00
module_param ( cpoint_name , charp , 0644 ) ;
2006-11-03 09:07:06 +03:00
MODULE_PARM_DESC ( cpoint_name , " Crash Point, where kernel is to be crashed " ) ;
module_param ( cpoint_type , charp , 0644 ) ;
MODULE_PARM_DESC ( cpoint_type , " Crash Point Type, action to be taken on " \
" hitting the crash point " ) ;
module_param ( cpoint_count , int , 0644 ) ;
MODULE_PARM_DESC ( cpoint_count , " Crash Point Count, number of times the " \
" crash point is to be hit to trigger action " ) ;
2006-10-02 13:17:36 +04:00
2008-02-06 12:36:50 +03:00
static unsigned int jp_do_irq ( unsigned int irq )
2006-10-02 13:17:36 +04:00
{
lkdtm_handler ( ) ;
jprobe_return ( ) ;
return 0 ;
}
2008-02-06 12:36:50 +03:00
static irqreturn_t jp_handle_irq_event ( unsigned int irq ,
struct irqaction * action )
2006-10-02 13:17:36 +04:00
{
lkdtm_handler ( ) ;
jprobe_return ( ) ;
return 0 ;
}
2008-02-06 12:36:50 +03:00
static void jp_tasklet_action ( struct softirq_action * a )
2006-10-02 13:17:36 +04:00
{
lkdtm_handler ( ) ;
jprobe_return ( ) ;
}
2008-02-06 12:36:50 +03:00
static void jp_ll_rw_block ( int rw , int nr , struct buffer_head * bhs [ ] )
2006-10-02 13:17:36 +04:00
{
lkdtm_handler ( ) ;
jprobe_return ( ) ;
}
struct scan_control ;
2008-02-06 12:36:50 +03:00
static unsigned long jp_shrink_inactive_list ( unsigned long max_scan ,
struct zone * zone ,
struct scan_control * sc )
2006-10-02 13:17:36 +04:00
{
lkdtm_handler ( ) ;
jprobe_return ( ) ;
return 0 ;
}
2008-02-06 12:36:50 +03:00
static int jp_hrtimer_start ( struct hrtimer * timer , ktime_t tim ,
const enum hrtimer_mode mode )
2006-10-02 13:17:36 +04:00
{
lkdtm_handler ( ) ;
jprobe_return ( ) ;
return 0 ;
}
2008-02-06 12:36:50 +03:00
static int jp_scsi_dispatch_cmd ( struct scsi_cmnd * cmd )
2006-10-02 13:17:36 +04:00
{
lkdtm_handler ( ) ;
jprobe_return ( ) ;
return 0 ;
}
# ifdef CONFIG_IDE
int jp_generic_ide_ioctl ( ide_drive_t * drive , struct file * file ,
struct block_device * bdev , unsigned int cmd ,
unsigned long arg )
{
lkdtm_handler ( ) ;
jprobe_return ( ) ;
return 0 ;
}
# endif
static int lkdtm_parse_commandline ( void )
{
int i ;
2008-03-29 06:07:48 +03:00
if ( cpoint_name = = NULL | | cpoint_type = = NULL | |
2006-10-02 13:17:36 +04:00
cpoint_count < 1 | | recur_count < 1 )
return - EINVAL ;
for ( i = 0 ; i < NUM_CPOINTS ; + + i ) {
if ( ! strcmp ( cpoint_name , cp_name [ i ] ) ) {
cpoint = i + 1 ;
break ;
}
}
for ( i = 0 ; i < NUM_CPOINT_TYPES ; + + i ) {
if ( ! strcmp ( cpoint_type , cp_type [ i ] ) ) {
cptype = i + 1 ;
break ;
}
}
if ( cpoint = = INVALID | | cptype = = NONE )
return - EINVAL ;
count = cpoint_count ;
return 0 ;
}
static int recursive_loop ( int a )
{
char buf [ 1024 ] ;
memset ( buf , 0xFF , 1024 ) ;
recur_count - - ;
if ( ! recur_count )
return 0 ;
else
return recursive_loop ( a ) ;
}
void lkdtm_handler ( void )
{
printk ( KERN_INFO " lkdtm : Crash point %s of type %s hit \n " ,
cpoint_name , cpoint_type ) ;
- - count ;
if ( count = = 0 ) {
switch ( cptype ) {
case NONE :
break ;
case PANIC :
printk ( KERN_INFO " lkdtm : PANIC \n " ) ;
panic ( " dumptest " ) ;
break ;
case BUG :
printk ( KERN_INFO " lkdtm : BUG \n " ) ;
BUG ( ) ;
break ;
case EXCEPTION :
printk ( KERN_INFO " lkdtm : EXCEPTION \n " ) ;
* ( ( int * ) 0 ) = 0 ;
break ;
case LOOP :
printk ( KERN_INFO " lkdtm : LOOP \n " ) ;
for ( ; ; ) ;
break ;
case OVERFLOW :
printk ( KERN_INFO " lkdtm : OVERFLOW \n " ) ;
( void ) recursive_loop ( 0 ) ;
break ;
default :
break ;
}
count = cpoint_count ;
}
}
2008-02-06 12:36:50 +03:00
static int __init lkdtm_module_init ( void )
2006-10-02 13:17:36 +04:00
{
int ret ;
if ( lkdtm_parse_commandline ( ) = = - EINVAL ) {
printk ( KERN_INFO " lkdtm : Invalid command \n " ) ;
return - EINVAL ;
}
switch ( cpoint ) {
case INT_HARDWARE_ENTRY :
lkdtm . kp . symbol_name = " __do_IRQ " ;
lkdtm . entry = ( kprobe_opcode_t * ) jp_do_irq ;
break ;
case INT_HW_IRQ_EN :
lkdtm . kp . symbol_name = " handle_IRQ_event " ;
lkdtm . entry = ( kprobe_opcode_t * ) jp_handle_irq_event ;
break ;
case INT_TASKLET_ENTRY :
lkdtm . kp . symbol_name = " tasklet_action " ;
lkdtm . entry = ( kprobe_opcode_t * ) jp_tasklet_action ;
break ;
case FS_DEVRW :
lkdtm . kp . symbol_name = " ll_rw_block " ;
lkdtm . entry = ( kprobe_opcode_t * ) jp_ll_rw_block ;
break ;
case MEM_SWAPOUT :
2006-11-06 10:52:07 +03:00
lkdtm . kp . symbol_name = " shrink_inactive_list " ;
lkdtm . entry = ( kprobe_opcode_t * ) jp_shrink_inactive_list ;
2006-10-02 13:17:36 +04:00
break ;
case TIMERADD :
lkdtm . kp . symbol_name = " hrtimer_start " ;
lkdtm . entry = ( kprobe_opcode_t * ) jp_hrtimer_start ;
break ;
case SCSI_DISPATCH_CMD :
lkdtm . kp . symbol_name = " scsi_dispatch_cmd " ;
lkdtm . entry = ( kprobe_opcode_t * ) jp_scsi_dispatch_cmd ;
break ;
case IDE_CORE_CP :
# ifdef CONFIG_IDE
lkdtm . kp . symbol_name = " generic_ide_ioctl " ;
lkdtm . entry = ( kprobe_opcode_t * ) jp_generic_ide_ioctl ;
# else
printk ( KERN_INFO " lkdtm : Crash point not available \n " ) ;
# endif
break ;
default :
printk ( KERN_INFO " lkdtm : Invalid Crash Point \n " ) ;
break ;
}
if ( ( ret = register_jprobe ( & lkdtm ) ) < 0 ) {
printk ( KERN_INFO " lkdtm : Couldn't register jprobe \n " ) ;
return ret ;
}
printk ( KERN_INFO " lkdtm : Crash point %s of type %s registered \n " ,
cpoint_name , cpoint_type ) ;
return 0 ;
}
2008-02-06 12:36:50 +03:00
static void __exit lkdtm_module_exit ( void )
2006-10-02 13:17:36 +04:00
{
unregister_jprobe ( & lkdtm ) ;
printk ( KERN_INFO " lkdtm : Crash point unregistered \n " ) ;
}
module_init ( lkdtm_module_init ) ;
module_exit ( lkdtm_module_exit ) ;
MODULE_LICENSE ( " GPL " ) ;