linux/drivers/misc/lkdtm.c
<<
>>
Prefs
   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