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 * Sachin Sant <sachinp@in.ibm.com>
22 * Cai Qian <qcai@redhat.com>
23 *
24 * This module induces system failures at predefined crashpoints to
25 * evaluate the reliability of crash dumps obtained using different dumping
26 * solutions.
27 *
28 * It is adapted from the Linux Kernel Dump Test Tool by
29 * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net>
30 *
31 * Usage : insmod lkdtm.ko [recur_count={>0}] cpoint_name=<> cpoint_type=<>
32 * [cpoint_count={>0}]
33 *
34 * recur_count : Recursion level for the stack overflow test. Default is 10.
35 *
36 * cpoint_name : Crash point where the kernel is to be crashed. It can be
37 * one of INT_HARDWARE_ENTRY, INT_HW_IRQ_EN, INT_TASKLET_ENTRY,
38 * FS_DEVRW, MEM_SWAPOUT, TIMERADD, SCSI_DISPATCH_CMD,
39 * IDE_CORE_CP
40 *
41 * cpoint_type : Indicates the action to be taken on hitting the crash point.
42 * It can be one of PANIC, BUG, EXCEPTION, LOOP, OVERFLOW
43 *
44 * cpoint_count : Indicates the number of times the crash point is to be hit
45 * to trigger an action. The default is 10.
46 */
47
48 #include <linux/kernel.h>
49 #include <linux/fs.h>
50 #include <linux/module.h>
51 #include <linux/buffer_head.h>
52 #include <linux/kprobes.h>
53 #include <linux/list.h>
54 #include <linux/init.h>
55 #include <linux/interrupt.h>
56 #include <linux/hrtimer.h>
57 #include <scsi/scsi_cmnd.h>
58 #include <linux/version.h>
59 #include <linux/kallsyms.h>
60
61 #ifdef CONFIG_IDE
62 #include <linux/ide.h>
63 #endif
64
65 #define NUM_CPOINTS 8
66 #define NUM_CPOINT_TYPES 5
67 #define DEFAULT_COUNT 10
68 #define REC_NUM_DEFAULT 10
69
70 enum cname {
71 INVALID,
72 INT_HARDWARE_ENTRY,
73 INT_HW_IRQ_EN,
74 INT_TASKLET_ENTRY,
75 FS_DEVRW,
76 MEM_SWAPOUT,
77 TIMERADD,
78 SCSI_DISPATCH_CMD,
79 IDE_CORE_CP
80 };
81
82 enum ctype {
83 NONE,
84 PANIC,
85 BUG,
86 EXCEPTION,
87 LOOP,
88 OVERFLOW
89 };
90
91 static char *cp_name[] = {
92 "INT_HARDWARE_ENTRY",
93 "INT_HW_IRQ_EN",
94 "INT_TASKLET_ENTRY",
95 "FS_DEVRW",
96 "MEM_SWAPOUT",
97 "TIMERADD",
98 "SCSI_DISPATCH_CMD",
99 "IDE_CORE_CP"
100 };
101
102 static char *cp_type[] = {
103 "PANIC",
104 "BUG",
105 "EXCEPTION",
106 "LOOP",
107 "OVERFLOW"
108 };
109
110 static struct jprobe lkdtm;
111
112 static int lkdtm_parse_commandline(void);
113 static void lkdtm_handler(void);
114
115 static char *cpoint_name = INVALID;
116 static char *cpoint_type = NONE;
117 static int cpoint_count = DEFAULT_COUNT;
118 static int recur_count = REC_NUM_DEFAULT;
119
120 static enum cname cpoint = INVALID;
121 static enum ctype cptype = NONE;
122 static int count = DEFAULT_COUNT;
123
124 module_param(recur_count, int, 0644);
125 MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "
126 "default is 10");
127 module_param(cpoint_name, charp, 0644);
128 MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed");
129 module_param(cpoint_type, charp, 0644);
130 MODULE_PARM_DESC(cpoint_type, " Crash Point Type, action to be taken on "
131 "hitting the crash point");
132 module_param(cpoint_count, int, 0644);
133 MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "
134 "crash point is to be hit to trigger action");
135
jp_do_irq(unsigned int irq)136 unsigned int jp_do_irq(unsigned int irq)
137 {
138 lkdtm_handler();
139 jprobe_return();
140 return 0;
141 }
142
jp_handle_irq_event(unsigned int irq,struct irqaction * action)143 irqreturn_t jp_handle_irq_event(unsigned int irq, struct irqaction * action)
144 {
145 lkdtm_handler();
146 jprobe_return();
147 return 0;
148 }
149
jp_tasklet_action(struct softirq_action * a)150 void jp_tasklet_action(struct softirq_action *a)
151 {
152 lkdtm_handler();
153 jprobe_return();
154 }
155
jp_ll_rw_block(int rw,int nr,struct buffer_head * bhs[])156 void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
157 {
158 lkdtm_handler();
159 jprobe_return();
160 }
161
162 struct scan_control;
163
jp_shrink_page_list(struct list_head * page_list,struct scan_control * sc)164 unsigned long jp_shrink_page_list(struct list_head *page_list,
165 struct scan_control *sc)
166 {
167 lkdtm_handler();
168 jprobe_return();
169 return 0;
170 }
171
jp_hrtimer_start(struct hrtimer * timer,ktime_t tim,const enum hrtimer_mode mode)172 int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim,
173 const enum hrtimer_mode mode)
174 {
175 lkdtm_handler();
176 jprobe_return();
177 return 0;
178 }
179
jp_scsi_dispatch_cmd(struct scsi_cmnd * cmd)180 int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd)
181 {
182 lkdtm_handler();
183 jprobe_return();
184 return 0;
185 }
186
187 #ifdef CONFIG_IDE
jp_generic_ide_ioctl(ide_drive_t * drive,struct file * file,struct block_device * bdev,unsigned int cmd,unsigned long arg)188 int jp_generic_ide_ioctl(ide_drive_t * drive, struct file *file,
189 struct block_device *bdev, unsigned int cmd,
190 unsigned long arg)
191 {
192 lkdtm_handler();
193 jprobe_return();
194 return 0;
195 }
196 #endif
197
lkdtm_parse_commandline(void)198 static int lkdtm_parse_commandline(void)
199 {
200 int i;
201
202 if (cpoint_name == INVALID || cpoint_type == NONE ||
203 cpoint_count < 1 || recur_count < 1)
204 return -EINVAL;
205
206 for (i = 0; i < NUM_CPOINTS; ++i) {
207 if (!strcmp(cpoint_name, cp_name[i])) {
208 cpoint = i + 1;
209 break;
210 }
211 }
212
213 for (i = 0; i < NUM_CPOINT_TYPES; ++i) {
214 if (!strcmp(cpoint_type, cp_type[i])) {
215 cptype = i + 1;
216 break;
217 }
218 }
219
220 if (cpoint == INVALID || cptype == NONE)
221 return -EINVAL;
222
223 count = cpoint_count;
224
225 return 0;
226 }
227
recursive_loop(int a)228 static int recursive_loop(int a)
229 {
230 char buf[1024];
231
232 memset(buf, 0xFF, 1024);
233 recur_count--;
234 if (!recur_count)
235 return 0;
236 else
237 return recursive_loop(a);
238 }
239
lkdtm_handler(void)240 void lkdtm_handler(void)
241 {
242 /* Escape endless loop. */
243 if (count < 0)
244 return;
245
246 printk(KERN_INFO "lkdtm : Crash point %s of type %s hit\n",
247 cpoint_name, cpoint_type);
248 --count;
249
250 if (count == 0) {
251 switch (cptype) {
252 case NONE:
253 break;
254 case PANIC:
255 printk(KERN_INFO "lkdtm : PANIC\n");
256 panic("dumptest");
257 break;
258 case BUG:
259 printk(KERN_INFO "lkdtm : BUG\n");
260 BUG();
261 break;
262 case EXCEPTION:
263 printk(KERN_INFO "lkdtm : EXCEPTION\n");
264 *((int *)0) = 0;
265 break;
266 case LOOP:
267 printk(KERN_INFO "lkdtm : LOOP\n");
268 for (;;) ;
269 break;
270 case OVERFLOW:
271 printk(KERN_INFO "lkdtm : OVERFLOW\n");
272 (void)recursive_loop(0);
273 break;
274 default:
275 break;
276 }
277 count = cpoint_count;
278 }
279 }
280
281 #ifdef USE_SYMBOL_NAME
lkdtm_symbol_name(char * name,void (* entry)(void))282 void lkdtm_symbol_name(char *name, void (*entry) (void))
283 {
284 lkdtm.kp.symbol_name = name;
285 lkdtm.entry = (kprobe_opcode_t *) entry;
286 }
287
288 #else
lkdtm_lookup_name(char * name,void (* entry)(void))289 void lkdtm_lookup_name(char *name, void (*entry) (void))
290 {
291 unsigned long addr;
292
293 addr = kallsyms_lookup_name(name);
294 if (addr) {
295 *(lkdtm.kp.addr) = addr;
296 lkdtm.entry = JPROBE_ENTRY(entry);
297 } else
298 printk(KERN_INFO "lkdtm : Crash point not available\n");
299 }
300 #endif
301
lkdtm_module_init(void)302 int lkdtm_module_init(void)
303 {
304 int ret;
305
306 if (lkdtm_parse_commandline() == -EINVAL) {
307 printk(KERN_INFO "lkdtm : Invalid command\n");
308 return -EINVAL;
309 }
310
311 switch (cpoint) {
312 case INT_HARDWARE_ENTRY:
313
314 #ifdef USE_SYMBOL_NAME
315
316 #ifdef __powerpc__
317 lkdtm_symbol_name(".__do_IRQ", (void (*)(void))jp_do_irq);
318 #else
319 lkdtm_symbol_name("__do_IRQ", (void (*)(void))jp_do_irq);
320 #endif /*__powerpc__*/
321
322 #else /* USE_SYMBOL_NAME */
323 lkdtm_lookup_name("__do_IRQ", (void (*)(void))jp_do_irq);
324
325 #endif /* USE_SYMBOL_NAME */
326 break;
327
328 case INT_HW_IRQ_EN:
329
330 #ifdef USE_SYMBOL_NAME
331
332 #ifdef __powerpc__
333 lkdtm_symbol_name(".handle_IRQ_event",
334 (void (*)(void))jp_handle_irq_event);
335 #else
336 lkdtm_symbol_name("handle_IRQ_event",
337 (void (*)(void))jp_handle_irq_event);
338 #endif /*__powerpc__*/
339
340 #else /* USE_SYMBOL_NAME */
341 lkdtm_lookup_name("handle_IRQ_event",
342 (void (*)(void))jp_handle_irq_event);
343
344 #endif /* USE_SYMBOL_NAME */
345 break;
346
347 case INT_TASKLET_ENTRY:
348
349 #ifdef USE_SYMBOL_NAME
350
351 #ifdef __powerpc__
352 lkdtm_symbol_name(".tasklet_action",
353 (void (*)(void))jp_tasklet_action);
354 #else
355 lkdtm_symbol_name("tasklet_action",
356 (void (*)(void))jp_tasklet_action);
357 #endif /*__powerpc__*/
358
359 #else /* USE_SYMBOL_NAME */
360 lkdtm_lookup_name("tasklet_action",
361 (void (*)(void))jp_tasklet_action);
362
363 #endif /* USE_SYMBOL_NAME */
364 break;
365
366 case FS_DEVRW:
367
368 #ifdef USE_SYMBOL_NAME
369
370 #ifdef __powerpc__
371 lkdtm_symbol_name(".ll_rw_block",
372 (void (*)(void))jp_ll_rw_block);
373 #else
374 lkdtm_symbol_name("ll_rw_block",
375 (void (*)(void))jp_ll_rw_block);
376 #endif /*__powerpc__*/
377
378 #else /* USE_SYMBOL_NAME */
379 lkdtm_lookup_name("ll_rw_block",
380 (void (*)(void))jp_ll_rw_block);
381
382 #endif /* USE_SYMBOL_NAME */
383 break;
384
385 case MEM_SWAPOUT:
386
387 #ifdef USE_SYMBOL_NAME
388
389 #ifdef __powerpc__
390 lkdtm_symbol_name(".shrink_inactive_list",
391 (void (*)(void))jp_shrink_page_list);
392 #else
393 lkdtm_symbol_name("shrink_inactive_list",
394 (void (*)(void))jp_shrink_page_list);
395 #endif /*__powerpc__*/
396
397 #else /* USE_SYMBOL_NAME */
398 lkdtm_lookup_name("shrink_inactive_list",
399 (void (*)(void))jp_shrink_page_list);
400
401 #endif /* USE_SYMBOL_NAME */
402 break;
403
404 case TIMERADD:
405
406 #ifdef USE_SYMBOL_NAME
407
408 #ifdef __powerpc__
409 lkdtm_symbol_name(".hrtimer_start",
410 (void (*)(void))jp_hrtimer_start);
411 #else
412 lkdtm_symbol_name("hrtimer_start",
413 (void (*)(void))jp_hrtimer_start);
414 #endif /*__powerpc__*/
415
416 #else /* USE_SYMBOL_NAME */
417 lkdtm_lookup_name("hrtimer_start",
418 (void (*)(void))jp_hrtimer_start);
419
420 #endif /* USE_SYMBOL_NAME */
421 break;
422
423 case SCSI_DISPATCH_CMD:
424
425 #ifdef USE_SYMBOL_NAME
426
427 #ifdef __powerpc__
428 lkdtm_symbol_name(".scsi_dispatch_cmd",
429 (void (*)(void))jp_scsi_dispatch_cmd);
430 #else
431 lkdtm_symbol_name("scsi_dispatch_cmd",
432 (void (*)(void))jp_scsi_dispatch_cmd);
433 #endif /*__powerpc__*/
434
435 #else /* USE_SYMBOL_NAME */
436 lkdtm_lookup_name("scsi_dispatch_cmd",
437 (void (*)(void))jp_scsi_dispatch_cmd);
438
439 #endif /* USE_SYMBOL_NAME */
440 break;
441
442 case IDE_CORE_CP:
443 #ifdef CONFIG_IDE
444
445 #ifdef USE_SYMBOL_NAME
446
447 #ifdef __powerpc__
448 lkdtm_symbol_name(".scsi_dispatch_cmd",
449 (void (*)(void))jp_scsi_dispatch_cmd);
450 #else
451 lkdtm_symbol_name("scsi_dispatch_cmd",
452 (void (*)(void))jp_scsi_dispatch_cmd);
453 #endif /*__powerpc__*/
454
455 #else /* USE_SYMBOL_NAME */
456 lkdtm_lookup_name("scsi_dispatch_cmd",
457 (void (*)(void))jp_scsi_dispatch_cmd);
458
459 #endif /* USE_SYMBOL_NAME */
460 #endif /* CONFIG_IDE */
461 break;
462
463 default:
464 printk(KERN_INFO "lkdtm : Invalid Crash Point\n");
465 break;
466 }
467
468 if ((ret = register_jprobe(&lkdtm)) < 0) {
469 printk(KERN_INFO "lkdtm : Couldn't register jprobe\n");
470 return ret;
471 }
472
473 printk(KERN_INFO "lkdtm : Crash point %s of type %s registered\n",
474 cpoint_name, cpoint_type);
475 return 0;
476 }
477
lkdtm_module_exit(void)478 void lkdtm_module_exit(void)
479 {
480 unregister_jprobe(&lkdtm);
481 printk(KERN_INFO "lkdtm : Crash point unregistered\n");
482 }
483
484 module_init(lkdtm_module_init);
485 module_exit(lkdtm_module_exit);
486
487 MODULE_LICENSE("GPL");
488