1/* 2 * Kprobe module for testing crash dumps 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 * 18 * Copyright (C) IBM Corporation, 2006 19 * 20 * Author: Ankita Garg <ankita@in.ibm.com> 21 * 22 * This module induces system failures at predefined crashpoints to 23 * evaluate the reliability of crash dumps obtained using different dumping 24 * solutions. 25 * 26 * It is adapted from the Linux Kernel Dump Test Tool by 27 * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net> 28 * 29 * Usage : insmod lkdtm.ko [recur_count={>0}] cpoint_name=<> cpoint_type=<> 30 * [cpoint_count={>0}] 31 * 32 * recur_count : Recursion level for the stack overflow test. Default is 10. 33 * 34 * cpoint_name : Crash point where the kernel is to be crashed. It can be 35 * one of INT_HARDWARE_ENTRY, INT_HW_IRQ_EN, INT_TASKLET_ENTRY, 36 * FS_DEVRW, MEM_SWAPOUT, TIMERADD, SCSI_DISPATCH_CMD, 37 * IDE_CORE_CP 38 * 39 * cpoint_type : Indicates the action to be taken on hitting the crash point. 40 * It can be one of PANIC, BUG, EXCEPTION, LOOP, OVERFLOW 41 * 42 * cpoint_count : Indicates the number of times the crash point is to be hit 43 * to trigger an action. The default is 10. 44 */ 45 46#include <linux/kernel.h> 47#include <linux/fs.h> 48#include <linux/module.h> 49#include <linux/buffer_head.h> 50#include <linux/kprobes.h> 51#include <linux/list.h> 52#include <linux/init.h> 53#include <linux/interrupt.h> 54#include <linux/hrtimer.h> 55#include <scsi/scsi_cmnd.h> 56 57#ifdef CONFIG_IDE 58#include <linux/ide.h> 59#endif 60 61#define NUM_CPOINTS 8 62#define NUM_CPOINT_TYPES 5 63#define DEFAULT_COUNT 10 64#define REC_NUM_DEFAULT 10 65 66enum cname { 67 INVALID, 68 INT_HARDWARE_ENTRY, 69 INT_HW_IRQ_EN, 70 INT_TASKLET_ENTRY, 71 FS_DEVRW, 72 MEM_SWAPOUT, 73 TIMERADD, 74 SCSI_DISPATCH_CMD, 75 IDE_CORE_CP 76}; 77 78enum ctype { 79 NONE, 80 PANIC, 81 BUG, 82 EXCEPTION, 83 LOOP, 84 OVERFLOW 85}; 86 87static char* cp_name[] = { 88 "INT_HARDWARE_ENTRY", 89 "INT_HW_IRQ_EN", 90 "INT_TASKLET_ENTRY", 91 "FS_DEVRW", 92 "MEM_SWAPOUT", 93 "TIMERADD", 94 "SCSI_DISPATCH_CMD", 95 "IDE_CORE_CP" 96}; 97 98static char* cp_type[] = { 99 "PANIC", 100 "BUG", 101 "EXCEPTION", 102 "LOOP", 103 "OVERFLOW" 104}; 105 106static struct jprobe lkdtm; 107 108static int lkdtm_parse_commandline(void); 109static void lkdtm_handler(void); 110 111static char* cpoint_name; 112static char* cpoint_type; 113static int cpoint_count = DEFAULT_COUNT; 114static int recur_count = REC_NUM_DEFAULT; 115 116static enum cname cpoint = INVALID; 117static enum ctype cptype = NONE; 118static int count = DEFAULT_COUNT; 119 120module_param(recur_count, int, 0644); 121MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\ 122 "default is 10"); 123module_param(cpoint_name, charp, 0644); 124MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed"); 125module_param(cpoint_type, charp, 0644); 126MODULE_PARM_DESC(cpoint_type, " Crash Point Type, action to be taken on "\ 127 "hitting the crash point"); 128module_param(cpoint_count, int, 0644); 129MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\ 130 "crash point is to be hit to trigger action"); 131 132unsigned int jp_do_irq(unsigned int irq) 133{ 134 lkdtm_handler(); 135 jprobe_return(); 136 return 0; 137} 138 139irqreturn_t jp_handle_irq_event(unsigned int irq, struct irqaction *action) 140{ 141 lkdtm_handler(); 142 jprobe_return(); 143 return 0; 144} 145 146void jp_tasklet_action(struct softirq_action *a) 147{ 148 lkdtm_handler(); 149 jprobe_return(); 150} 151 152void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[]) 153{ 154 lkdtm_handler(); 155 jprobe_return(); 156} 157 158struct scan_control; 159 160unsigned long jp_shrink_inactive_list(unsigned long max_scan, 161 struct zone *zone, struct scan_control *sc) 162{ 163 lkdtm_handler(); 164 jprobe_return(); 165 return 0; 166} 167 168int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim, 169 const enum hrtimer_mode mode) 170{ 171 lkdtm_handler(); 172 jprobe_return(); 173 return 0; 174} 175 176int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd) 177{ 178 lkdtm_handler(); 179 jprobe_return(); 180 return 0; 181} 182 183#ifdef CONFIG_IDE 184int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file, 185 struct block_device *bdev, unsigned int cmd, 186 unsigned long arg) 187{ 188 lkdtm_handler(); 189 jprobe_return(); 190 return 0; 191} 192#endif 193 194static int lkdtm_parse_commandline(void) 195{ 196 int i; 197 198 if (cpoint_name == INVALID || cpoint_type == NONE || 199 cpoint_count < 1 || recur_count < 1) 200 return -EINVAL; 201 202 for (i = 0; i < NUM_CPOINTS; ++i) { 203 if (!strcmp(cpoint_name, cp_name[i])) { 204 cpoint = i + 1; 205 break; 206 } 207 } 208 209 for (i = 0; i < NUM_CPOINT_TYPES; ++i) { 210 if (!strcmp(cpoint_type, cp_type[i])) { 211 cptype = i + 1; 212 break; 213 } 214 } 215 216 if (cpoint == INVALID || cptype == NONE) 217 return -EINVAL; 218 219 count = cpoint_count; 220 221 return 0; 222} 223 224static int recursive_loop(int a) 225{ 226 char buf[1024]; 227 228 memset(buf,0xFF,1024); 229 recur_count--; 230 if (!recur_count) 231 return 0; 232 else 233 return recursive_loop(a); 234} 235 236void lkdtm_handler(void) 237{ 238 printk(KERN_INFO "lkdtm : Crash point %s of type %s hit\n", 239 cpoint_name, cpoint_type); 240 --count; 241 242 if (count == 0) { 243 switch (cptype) { 244 case NONE: 245 break; 246 case PANIC: 247 printk(KERN_INFO "lkdtm : PANIC\n"); 248 panic("dumptest"); 249 break; 250 case BUG: 251 printk(KERN_INFO "lkdtm : BUG\n"); 252 BUG(); 253 break; 254 case EXCEPTION: 255 printk(KERN_INFO "lkdtm : EXCEPTION\n"); 256 *((int *) 0) = 0; 257 break; 258 case LOOP: 259 printk(KERN_INFO "lkdtm : LOOP\n"); 260 for (;;); 261 break; 262 case OVERFLOW: 263 printk(KERN_INFO "lkdtm : OVERFLOW\n"); 264 (void) recursive_loop(0); 265 break; 266 default: 267 break; 268 } 269 count = cpoint_count; 270 } 271} 272 273int lkdtm_module_init(void) 274{ 275 int ret; 276 277 if (lkdtm_parse_commandline() == -EINVAL) { 278 printk(KERN_INFO "lkdtm : Invalid command\n"); 279 return -EINVAL; 280 } 281 282 switch (cpoint) { 283 case INT_HARDWARE_ENTRY: 284 lkdtm.kp.symbol_name = "__do_IRQ"; 285 lkdtm.entry = (kprobe_opcode_t*) jp_do_irq; 286 break; 287 case INT_HW_IRQ_EN: 288 lkdtm.kp.symbol_name = "handle_IRQ_event"; 289 lkdtm.entry = (kprobe_opcode_t*) jp_handle_irq_event; 290 break; 291 case INT_TASKLET_ENTRY: 292 lkdtm.kp.symbol_name = "tasklet_action"; 293 lkdtm.entry = (kprobe_opcode_t*) jp_tasklet_action; 294 break; 295 case FS_DEVRW: 296 lkdtm.kp.symbol_name = "ll_rw_block"; 297 lkdtm.entry = (kprobe_opcode_t*) jp_ll_rw_block; 298 break; 299 case MEM_SWAPOUT: 300 lkdtm.kp.symbol_name = "shrink_inactive_list"; 301 lkdtm.entry = (kprobe_opcode_t*) jp_shrink_inactive_list; 302 break; 303 case TIMERADD: 304 lkdtm.kp.symbol_name = "hrtimer_start"; 305 lkdtm.entry = (kprobe_opcode_t*) jp_hrtimer_start; 306 break; 307 case SCSI_DISPATCH_CMD: 308 lkdtm.kp.symbol_name = "scsi_dispatch_cmd"; 309 lkdtm.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd; 310 break; 311 case IDE_CORE_CP: 312#ifdef CONFIG_IDE 313 lkdtm.kp.symbol_name = "generic_ide_ioctl"; 314 lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl; 315#else 316 printk(KERN_INFO "lkdtm : Crash point not available\n"); 317#endif 318 break; 319 default: 320 printk(KERN_INFO "lkdtm : Invalid Crash Point\n"); 321 break; 322 } 323 324 if ((ret = register_jprobe(&lkdtm)) < 0) { 325 printk(KERN_INFO "lkdtm : Couldn't register jprobe\n"); 326 return ret; 327 } 328 329 printk(KERN_INFO "lkdtm : Crash point %s of type %s registered\n", 330 cpoint_name, cpoint_type); 331 return 0; 332} 333 334void lkdtm_module_exit(void) 335{ 336 unregister_jprobe(&lkdtm); 337 printk(KERN_INFO "lkdtm : Crash point unregistered\n"); 338} 339 340module_init(lkdtm_module_init); 341module_exit(lkdtm_module_exit); 342 343MODULE_LICENSE("GPL"); 344