Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.36 674 lines 15 kB view raw
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 * Debugfs support added by Simon Kagstrom <simon.kagstrom@netinsight.net> 30 * 31 * See Documentation/fault-injection/provoke-crashes.txt for instructions 32 */ 33 34#include <linux/kernel.h> 35#include <linux/fs.h> 36#include <linux/module.h> 37#include <linux/buffer_head.h> 38#include <linux/kprobes.h> 39#include <linux/list.h> 40#include <linux/init.h> 41#include <linux/interrupt.h> 42#include <linux/hrtimer.h> 43#include <linux/slab.h> 44#include <scsi/scsi_cmnd.h> 45#include <linux/debugfs.h> 46 47#ifdef CONFIG_IDE 48#include <linux/ide.h> 49#endif 50 51#define DEFAULT_COUNT 10 52#define REC_NUM_DEFAULT 10 53 54enum cname { 55 INVALID, 56 INT_HARDWARE_ENTRY, 57 INT_HW_IRQ_EN, 58 INT_TASKLET_ENTRY, 59 FS_DEVRW, 60 MEM_SWAPOUT, 61 TIMERADD, 62 SCSI_DISPATCH_CMD, 63 IDE_CORE_CP, 64 DIRECT, 65}; 66 67enum ctype { 68 NONE, 69 PANIC, 70 BUG, 71 EXCEPTION, 72 LOOP, 73 OVERFLOW, 74 CORRUPT_STACK, 75 UNALIGNED_LOAD_STORE_WRITE, 76 OVERWRITE_ALLOCATION, 77 WRITE_AFTER_FREE, 78 SOFTLOCKUP, 79 HARDLOCKUP, 80 HUNG_TASK, 81}; 82 83static char* cp_name[] = { 84 "INT_HARDWARE_ENTRY", 85 "INT_HW_IRQ_EN", 86 "INT_TASKLET_ENTRY", 87 "FS_DEVRW", 88 "MEM_SWAPOUT", 89 "TIMERADD", 90 "SCSI_DISPATCH_CMD", 91 "IDE_CORE_CP", 92 "DIRECT", 93}; 94 95static char* cp_type[] = { 96 "PANIC", 97 "BUG", 98 "EXCEPTION", 99 "LOOP", 100 "OVERFLOW", 101 "CORRUPT_STACK", 102 "UNALIGNED_LOAD_STORE_WRITE", 103 "OVERWRITE_ALLOCATION", 104 "WRITE_AFTER_FREE", 105 "SOFTLOCKUP", 106 "HARDLOCKUP", 107 "HUNG_TASK", 108}; 109 110static struct jprobe lkdtm; 111 112static int lkdtm_parse_commandline(void); 113static void lkdtm_handler(void); 114 115static char* cpoint_name; 116static char* cpoint_type; 117static int cpoint_count = DEFAULT_COUNT; 118static int recur_count = REC_NUM_DEFAULT; 119 120static enum cname cpoint = INVALID; 121static enum ctype cptype = NONE; 122static int count = DEFAULT_COUNT; 123 124module_param(recur_count, int, 0644); 125MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\ 126 "default is 10"); 127module_param(cpoint_name, charp, 0444); 128MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed"); 129module_param(cpoint_type, charp, 0444); 130MODULE_PARM_DESC(cpoint_type, " Crash Point Type, action to be taken on "\ 131 "hitting the crash point"); 132module_param(cpoint_count, int, 0644); 133MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\ 134 "crash point is to be hit to trigger action"); 135 136static unsigned int jp_do_irq(unsigned int irq) 137{ 138 lkdtm_handler(); 139 jprobe_return(); 140 return 0; 141} 142 143static irqreturn_t jp_handle_irq_event(unsigned int irq, 144 struct irqaction *action) 145{ 146 lkdtm_handler(); 147 jprobe_return(); 148 return 0; 149} 150 151static void jp_tasklet_action(struct softirq_action *a) 152{ 153 lkdtm_handler(); 154 jprobe_return(); 155} 156 157static void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[]) 158{ 159 lkdtm_handler(); 160 jprobe_return(); 161} 162 163struct scan_control; 164 165static unsigned long jp_shrink_inactive_list(unsigned long max_scan, 166 struct zone *zone, 167 struct scan_control *sc) 168{ 169 lkdtm_handler(); 170 jprobe_return(); 171 return 0; 172} 173 174static int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim, 175 const enum hrtimer_mode mode) 176{ 177 lkdtm_handler(); 178 jprobe_return(); 179 return 0; 180} 181 182static int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd) 183{ 184 lkdtm_handler(); 185 jprobe_return(); 186 return 0; 187} 188 189#ifdef CONFIG_IDE 190int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file, 191 struct block_device *bdev, unsigned int cmd, 192 unsigned long arg) 193{ 194 lkdtm_handler(); 195 jprobe_return(); 196 return 0; 197} 198#endif 199 200/* Return the crashpoint number or NONE if the name is invalid */ 201static enum ctype parse_cp_type(const char *what, size_t count) 202{ 203 int i; 204 205 for (i = 0; i < ARRAY_SIZE(cp_type); i++) { 206 if (!strcmp(what, cp_type[i])) 207 return i + 1; 208 } 209 210 return NONE; 211} 212 213static const char *cp_type_to_str(enum ctype type) 214{ 215 if (type == NONE || type < 0 || type > ARRAY_SIZE(cp_type)) 216 return "None"; 217 218 return cp_type[type - 1]; 219} 220 221static const char *cp_name_to_str(enum cname name) 222{ 223 if (name == INVALID || name < 0 || name > ARRAY_SIZE(cp_name)) 224 return "INVALID"; 225 226 return cp_name[name - 1]; 227} 228 229 230static int lkdtm_parse_commandline(void) 231{ 232 int i; 233 234 if (cpoint_count < 1 || recur_count < 1) 235 return -EINVAL; 236 237 count = cpoint_count; 238 239 /* No special parameters */ 240 if (!cpoint_type && !cpoint_name) 241 return 0; 242 243 /* Neither or both of these need to be set */ 244 if (!cpoint_type || !cpoint_name) 245 return -EINVAL; 246 247 cptype = parse_cp_type(cpoint_type, strlen(cpoint_type)); 248 if (cptype == NONE) 249 return -EINVAL; 250 251 for (i = 0; i < ARRAY_SIZE(cp_name); i++) { 252 if (!strcmp(cpoint_name, cp_name[i])) { 253 cpoint = i + 1; 254 return 0; 255 } 256 } 257 258 /* Could not find a valid crash point */ 259 return -EINVAL; 260} 261 262static int recursive_loop(int a) 263{ 264 char buf[1024]; 265 266 memset(buf,0xFF,1024); 267 recur_count--; 268 if (!recur_count) 269 return 0; 270 else 271 return recursive_loop(a); 272} 273 274static void lkdtm_do_action(enum ctype which) 275{ 276 switch (which) { 277 case PANIC: 278 panic("dumptest"); 279 break; 280 case BUG: 281 BUG(); 282 break; 283 case EXCEPTION: 284 *((int *) 0) = 0; 285 break; 286 case LOOP: 287 for (;;) 288 ; 289 break; 290 case OVERFLOW: 291 (void) recursive_loop(0); 292 break; 293 case CORRUPT_STACK: { 294 volatile u32 data[8]; 295 volatile u32 *p = data; 296 297 p[12] = 0x12345678; 298 break; 299 } 300 case UNALIGNED_LOAD_STORE_WRITE: { 301 static u8 data[5] __attribute__((aligned(4))) = {1, 2, 302 3, 4, 5}; 303 u32 *p; 304 u32 val = 0x12345678; 305 306 p = (u32 *)(data + 1); 307 if (*p == 0) 308 val = 0x87654321; 309 *p = val; 310 break; 311 } 312 case OVERWRITE_ALLOCATION: { 313 size_t len = 1020; 314 u32 *data = kmalloc(len, GFP_KERNEL); 315 316 data[1024 / sizeof(u32)] = 0x12345678; 317 kfree(data); 318 break; 319 } 320 case WRITE_AFTER_FREE: { 321 size_t len = 1024; 322 u32 *data = kmalloc(len, GFP_KERNEL); 323 324 kfree(data); 325 schedule(); 326 memset(data, 0x78, len); 327 break; 328 } 329 case SOFTLOCKUP: 330 preempt_disable(); 331 for (;;) 332 cpu_relax(); 333 break; 334 case HARDLOCKUP: 335 local_irq_disable(); 336 for (;;) 337 cpu_relax(); 338 break; 339 case HUNG_TASK: 340 set_current_state(TASK_UNINTERRUPTIBLE); 341 schedule(); 342 break; 343 case NONE: 344 default: 345 break; 346 } 347 348} 349 350static void lkdtm_handler(void) 351{ 352 count--; 353 printk(KERN_INFO "lkdtm: Crash point %s of type %s hit, trigger in %d rounds\n", 354 cp_name_to_str(cpoint), cp_type_to_str(cptype), count); 355 356 if (count == 0) { 357 lkdtm_do_action(cptype); 358 count = cpoint_count; 359 } 360} 361 362static int lkdtm_register_cpoint(enum cname which) 363{ 364 int ret; 365 366 cpoint = INVALID; 367 if (lkdtm.entry != NULL) 368 unregister_jprobe(&lkdtm); 369 370 switch (which) { 371 case DIRECT: 372 lkdtm_do_action(cptype); 373 return 0; 374 case INT_HARDWARE_ENTRY: 375 lkdtm.kp.symbol_name = "do_IRQ"; 376 lkdtm.entry = (kprobe_opcode_t*) jp_do_irq; 377 break; 378 case INT_HW_IRQ_EN: 379 lkdtm.kp.symbol_name = "handle_IRQ_event"; 380 lkdtm.entry = (kprobe_opcode_t*) jp_handle_irq_event; 381 break; 382 case INT_TASKLET_ENTRY: 383 lkdtm.kp.symbol_name = "tasklet_action"; 384 lkdtm.entry = (kprobe_opcode_t*) jp_tasklet_action; 385 break; 386 case FS_DEVRW: 387 lkdtm.kp.symbol_name = "ll_rw_block"; 388 lkdtm.entry = (kprobe_opcode_t*) jp_ll_rw_block; 389 break; 390 case MEM_SWAPOUT: 391 lkdtm.kp.symbol_name = "shrink_inactive_list"; 392 lkdtm.entry = (kprobe_opcode_t*) jp_shrink_inactive_list; 393 break; 394 case TIMERADD: 395 lkdtm.kp.symbol_name = "hrtimer_start"; 396 lkdtm.entry = (kprobe_opcode_t*) jp_hrtimer_start; 397 break; 398 case SCSI_DISPATCH_CMD: 399 lkdtm.kp.symbol_name = "scsi_dispatch_cmd"; 400 lkdtm.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd; 401 break; 402 case IDE_CORE_CP: 403#ifdef CONFIG_IDE 404 lkdtm.kp.symbol_name = "generic_ide_ioctl"; 405 lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl; 406#else 407 printk(KERN_INFO "lkdtm: Crash point not available\n"); 408 return -EINVAL; 409#endif 410 break; 411 default: 412 printk(KERN_INFO "lkdtm: Invalid Crash Point\n"); 413 return -EINVAL; 414 } 415 416 cpoint = which; 417 if ((ret = register_jprobe(&lkdtm)) < 0) { 418 printk(KERN_INFO "lkdtm: Couldn't register jprobe\n"); 419 cpoint = INVALID; 420 } 421 422 return ret; 423} 424 425static ssize_t do_register_entry(enum cname which, struct file *f, 426 const char __user *user_buf, size_t count, loff_t *off) 427{ 428 char *buf; 429 int err; 430 431 if (count >= PAGE_SIZE) 432 return -EINVAL; 433 434 buf = (char *)__get_free_page(GFP_KERNEL); 435 if (!buf) 436 return -ENOMEM; 437 if (copy_from_user(buf, user_buf, count)) { 438 free_page((unsigned long) buf); 439 return -EFAULT; 440 } 441 /* NULL-terminate and remove enter */ 442 buf[count] = '\0'; 443 strim(buf); 444 445 cptype = parse_cp_type(buf, count); 446 free_page((unsigned long) buf); 447 448 if (cptype == NONE) 449 return -EINVAL; 450 451 err = lkdtm_register_cpoint(which); 452 if (err < 0) 453 return err; 454 455 *off += count; 456 457 return count; 458} 459 460/* Generic read callback that just prints out the available crash types */ 461static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf, 462 size_t count, loff_t *off) 463{ 464 char *buf; 465 int i, n, out; 466 467 buf = (char *)__get_free_page(GFP_KERNEL); 468 469 n = snprintf(buf, PAGE_SIZE, "Available crash types:\n"); 470 for (i = 0; i < ARRAY_SIZE(cp_type); i++) 471 n += snprintf(buf + n, PAGE_SIZE - n, "%s\n", cp_type[i]); 472 buf[n] = '\0'; 473 474 out = simple_read_from_buffer(user_buf, count, off, 475 buf, n); 476 free_page((unsigned long) buf); 477 478 return out; 479} 480 481static int lkdtm_debugfs_open(struct inode *inode, struct file *file) 482{ 483 return 0; 484} 485 486 487static ssize_t int_hardware_entry(struct file *f, const char __user *buf, 488 size_t count, loff_t *off) 489{ 490 return do_register_entry(INT_HARDWARE_ENTRY, f, buf, count, off); 491} 492 493static ssize_t int_hw_irq_en(struct file *f, const char __user *buf, 494 size_t count, loff_t *off) 495{ 496 return do_register_entry(INT_HW_IRQ_EN, f, buf, count, off); 497} 498 499static ssize_t int_tasklet_entry(struct file *f, const char __user *buf, 500 size_t count, loff_t *off) 501{ 502 return do_register_entry(INT_TASKLET_ENTRY, f, buf, count, off); 503} 504 505static ssize_t fs_devrw_entry(struct file *f, const char __user *buf, 506 size_t count, loff_t *off) 507{ 508 return do_register_entry(FS_DEVRW, f, buf, count, off); 509} 510 511static ssize_t mem_swapout_entry(struct file *f, const char __user *buf, 512 size_t count, loff_t *off) 513{ 514 return do_register_entry(MEM_SWAPOUT, f, buf, count, off); 515} 516 517static ssize_t timeradd_entry(struct file *f, const char __user *buf, 518 size_t count, loff_t *off) 519{ 520 return do_register_entry(TIMERADD, f, buf, count, off); 521} 522 523static ssize_t scsi_dispatch_cmd_entry(struct file *f, 524 const char __user *buf, size_t count, loff_t *off) 525{ 526 return do_register_entry(SCSI_DISPATCH_CMD, f, buf, count, off); 527} 528 529static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf, 530 size_t count, loff_t *off) 531{ 532 return do_register_entry(IDE_CORE_CP, f, buf, count, off); 533} 534 535/* Special entry to just crash directly. Available without KPROBEs */ 536static ssize_t direct_entry(struct file *f, const char __user *user_buf, 537 size_t count, loff_t *off) 538{ 539 enum ctype type; 540 char *buf; 541 542 if (count >= PAGE_SIZE) 543 return -EINVAL; 544 if (count < 1) 545 return -EINVAL; 546 547 buf = (char *)__get_free_page(GFP_KERNEL); 548 if (!buf) 549 return -ENOMEM; 550 if (copy_from_user(buf, user_buf, count)) { 551 free_page((unsigned long) buf); 552 return -EFAULT; 553 } 554 /* NULL-terminate and remove enter */ 555 buf[count] = '\0'; 556 strim(buf); 557 558 type = parse_cp_type(buf, count); 559 free_page((unsigned long) buf); 560 if (type == NONE) 561 return -EINVAL; 562 563 printk(KERN_INFO "lkdtm: Performing direct entry %s\n", 564 cp_type_to_str(type)); 565 lkdtm_do_action(type); 566 *off += count; 567 568 return count; 569} 570 571struct crash_entry { 572 const char *name; 573 const struct file_operations fops; 574}; 575 576static const struct crash_entry crash_entries[] = { 577 {"DIRECT", {.read = lkdtm_debugfs_read, 578 .open = lkdtm_debugfs_open, 579 .write = direct_entry} }, 580 {"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read, 581 .open = lkdtm_debugfs_open, 582 .write = int_hardware_entry} }, 583 {"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read, 584 .open = lkdtm_debugfs_open, 585 .write = int_hw_irq_en} }, 586 {"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read, 587 .open = lkdtm_debugfs_open, 588 .write = int_tasklet_entry} }, 589 {"FS_DEVRW", {.read = lkdtm_debugfs_read, 590 .open = lkdtm_debugfs_open, 591 .write = fs_devrw_entry} }, 592 {"MEM_SWAPOUT", {.read = lkdtm_debugfs_read, 593 .open = lkdtm_debugfs_open, 594 .write = mem_swapout_entry} }, 595 {"TIMERADD", {.read = lkdtm_debugfs_read, 596 .open = lkdtm_debugfs_open, 597 .write = timeradd_entry} }, 598 {"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read, 599 .open = lkdtm_debugfs_open, 600 .write = scsi_dispatch_cmd_entry} }, 601 {"IDE_CORE_CP", {.read = lkdtm_debugfs_read, 602 .open = lkdtm_debugfs_open, 603 .write = ide_core_cp_entry} }, 604}; 605 606static struct dentry *lkdtm_debugfs_root; 607 608static int __init lkdtm_module_init(void) 609{ 610 int ret = -EINVAL; 611 int n_debugfs_entries = 1; /* Assume only the direct entry */ 612 int i; 613 614 /* Register debugfs interface */ 615 lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL); 616 if (!lkdtm_debugfs_root) { 617 printk(KERN_ERR "lkdtm: creating root dir failed\n"); 618 return -ENODEV; 619 } 620 621#ifdef CONFIG_KPROBES 622 n_debugfs_entries = ARRAY_SIZE(crash_entries); 623#endif 624 625 for (i = 0; i < n_debugfs_entries; i++) { 626 const struct crash_entry *cur = &crash_entries[i]; 627 struct dentry *de; 628 629 de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root, 630 NULL, &cur->fops); 631 if (de == NULL) { 632 printk(KERN_ERR "lkdtm: could not create %s\n", 633 cur->name); 634 goto out_err; 635 } 636 } 637 638 if (lkdtm_parse_commandline() == -EINVAL) { 639 printk(KERN_INFO "lkdtm: Invalid command\n"); 640 goto out_err; 641 } 642 643 if (cpoint != INVALID && cptype != NONE) { 644 ret = lkdtm_register_cpoint(cpoint); 645 if (ret < 0) { 646 printk(KERN_INFO "lkdtm: Invalid crash point %d\n", 647 cpoint); 648 goto out_err; 649 } 650 printk(KERN_INFO "lkdtm: Crash point %s of type %s registered\n", 651 cpoint_name, cpoint_type); 652 } else { 653 printk(KERN_INFO "lkdtm: No crash points registered, enable through debugfs\n"); 654 } 655 656 return 0; 657 658out_err: 659 debugfs_remove_recursive(lkdtm_debugfs_root); 660 return ret; 661} 662 663static void __exit lkdtm_module_exit(void) 664{ 665 debugfs_remove_recursive(lkdtm_debugfs_root); 666 667 unregister_jprobe(&lkdtm); 668 printk(KERN_INFO "lkdtm: Crash point unregistered\n"); 669} 670 671module_init(lkdtm_module_init); 672module_exit(lkdtm_module_exit); 673 674MODULE_LICENSE("GPL");