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
 132static unsigned int jp_do_irq(unsigned int irq)
 133{
 134        lkdtm_handler();
 135        jprobe_return();
 136        return 0;
 137}
 138
 139static irqreturn_t jp_handle_irq_event(unsigned int irq,
 140                                       struct irqaction *action)
 141{
 142        lkdtm_handler();
 143        jprobe_return();
 144        return 0;
 145}
 146
 147static void jp_tasklet_action(struct softirq_action *a)
 148{
 149        lkdtm_handler();
 150        jprobe_return();
 151}
 152
 153static void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
 154{
 155        lkdtm_handler();
 156        jprobe_return();
 157}
 158
 159struct scan_control;
 160
 161static unsigned long jp_shrink_inactive_list(unsigned long max_scan,
 162                                             struct zone *zone,
 163                                             struct scan_control *sc)
 164{
 165        lkdtm_handler();
 166        jprobe_return();
 167        return 0;
 168}
 169
 170static int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim,
 171                            const enum hrtimer_mode mode)
 172{
 173        lkdtm_handler();
 174        jprobe_return();
 175        return 0;
 176}
 177
 178static int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd)
 179{
 180        lkdtm_handler();
 181        jprobe_return();
 182        return 0;
 183}
 184
 185#ifdef CONFIG_IDE
 186int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file,
 187                        struct block_device *bdev, unsigned int cmd,
 188                        unsigned long arg)
 189{
 190        lkdtm_handler();
 191        jprobe_return();
 192        return 0;
 193}
 194#endif
 195
 196static int lkdtm_parse_commandline(void)
 197{
 198        int i;
 199
 200        if (cpoint_name == NULL || cpoint_type == NULL ||
 201                                        cpoint_count < 1 || recur_count < 1)
 202                return -EINVAL;
 203
 204        for (i = 0; i < NUM_CPOINTS; ++i) {
 205                if (!strcmp(cpoint_name, cp_name[i])) {
 206                        cpoint = i + 1;
 207                        break;
 208                }
 209        }
 210
 211        for (i = 0; i < NUM_CPOINT_TYPES; ++i) {
 212                if (!strcmp(cpoint_type, cp_type[i])) {
 213                        cptype = i + 1;
 214                        break;
 215                }
 216        }
 217
 218        if (cpoint == INVALID || cptype == NONE)
 219                return -EINVAL;
 220
 221        count = cpoint_count;
 222
 223        return 0;
 224}
 225
 226static int recursive_loop(int a)
 227{
 228        char buf[1024];
 229
 230        memset(buf,0xFF,1024);
 231        recur_count--;
 232        if (!recur_count)
 233                return 0;
 234        else
 235                return recursive_loop(a);
 236}
 237
 238void lkdtm_handler(void)
 239{
 240        printk(KERN_INFO "lkdtm : Crash point %s of type %s hit\n",
 241                                         cpoint_name, cpoint_type);
 242        --count;
 243
 244        if (count == 0) {
 245                switch (cptype) {
 246                case NONE:
 247                        break;
 248                case PANIC:
 249                        printk(KERN_INFO "lkdtm : PANIC\n");
 250                        panic("dumptest");
 251                        break;
 252                case BUG:
 253                        printk(KERN_INFO "lkdtm : BUG\n");
 254                        BUG();
 255                        break;
 256                case EXCEPTION:
 257                        printk(KERN_INFO "lkdtm : EXCEPTION\n");
 258                        *((int *) 0) = 0;
 259                        break;
 260                case LOOP:
 261                        printk(KERN_INFO "lkdtm : LOOP\n");
 262                        for (;;);
 263                        break;
 264                case OVERFLOW:
 265                        printk(KERN_INFO "lkdtm : OVERFLOW\n");
 266                        (void) recursive_loop(0);
 267                        break;
 268                default:
 269                        break;
 270                }
 271                count = cpoint_count;
 272        }
 273}
 274
 275static int __init lkdtm_module_init(void)
 276{
 277        int ret;
 278
 279        if (lkdtm_parse_commandline() == -EINVAL) {
 280                printk(KERN_INFO "lkdtm : Invalid command\n");
 281                return -EINVAL;
 282        }
 283
 284        switch (cpoint) {
 285        case INT_HARDWARE_ENTRY:
 286                lkdtm.kp.symbol_name = "do_IRQ";
 287                lkdtm.entry = (kprobe_opcode_t*) jp_do_irq;
 288                break;
 289        case INT_HW_IRQ_EN:
 290                lkdtm.kp.symbol_name = "handle_IRQ_event";
 291                lkdtm.entry = (kprobe_opcode_t*) jp_handle_irq_event;
 292                break;
 293        case INT_TASKLET_ENTRY:
 294                lkdtm.kp.symbol_name = "tasklet_action";
 295                lkdtm.entry = (kprobe_opcode_t*) jp_tasklet_action;
 296                break;
 297        case FS_DEVRW:
 298                lkdtm.kp.symbol_name = "ll_rw_block";
 299                lkdtm.entry = (kprobe_opcode_t*) jp_ll_rw_block;
 300                break;
 301        case MEM_SWAPOUT:
 302                lkdtm.kp.symbol_name = "shrink_inactive_list";
 303                lkdtm.entry = (kprobe_opcode_t*) jp_shrink_inactive_list;
 304                break;
 305        case TIMERADD:
 306                lkdtm.kp.symbol_name = "hrtimer_start";
 307                lkdtm.entry = (kprobe_opcode_t*) jp_hrtimer_start;
 308                break;
 309        case SCSI_DISPATCH_CMD:
 310                lkdtm.kp.symbol_name = "scsi_dispatch_cmd";
 311                lkdtm.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd;
 312                break;
 313        case IDE_CORE_CP:
 314#ifdef CONFIG_IDE
 315                lkdtm.kp.symbol_name = "generic_ide_ioctl";
 316                lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
 317#else
 318                printk(KERN_INFO "lkdtm : Crash point not available\n");
 319#endif
 320                break;
 321        default:
 322                printk(KERN_INFO "lkdtm : Invalid Crash Point\n");
 323                break;
 324        }
 325
 326        if ((ret = register_jprobe(&lkdtm)) < 0) {
 327                printk(KERN_INFO "lkdtm : Couldn't register jprobe\n");
 328                return ret;
 329        }
 330
 331        printk(KERN_INFO "lkdtm : Crash point %s of type %s registered\n",
 332                                                cpoint_name, cpoint_type);
 333        return 0;
 334}
 335
 336static void __exit lkdtm_module_exit(void)
 337{
 338        unregister_jprobe(&lkdtm);
 339        printk(KERN_INFO "lkdtm : Crash point unregistered\n");
 340}
 341
 342module_init(lkdtm_module_init);
 343module_exit(lkdtm_module_exit);
 344
 345MODULE_LICENSE("GPL");
 346