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
70enum 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
82enum ctype {
83	NONE,
84	PANIC,
85	BUG,
86	EXCEPTION,
87	LOOP,
88	OVERFLOW
89};
90
91static 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
102static char *cp_type[] = {
103	"PANIC",
104	"BUG",
105	"EXCEPTION",
106	"LOOP",
107	"OVERFLOW"
108};
109
110static struct jprobe lkdtm;
111
112static int lkdtm_parse_commandline(void);
113static void lkdtm_handler(void);
114
115static char *cpoint_name = INVALID;
116static char *cpoint_type = NONE;
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, 0644);
128MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed");
129module_param(cpoint_type, charp, 0644);
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
136unsigned int jp_do_irq(unsigned int irq)
137{
138	lkdtm_handler();
139	jprobe_return();
140	return 0;
141}
142
143irqreturn_t jp_handle_irq_event(unsigned int irq, struct irqaction * action)
144{
145	lkdtm_handler();
146	jprobe_return();
147	return 0;
148}
149
150void jp_tasklet_action(struct softirq_action *a)
151{
152	lkdtm_handler();
153	jprobe_return();
154}
155
156void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
157{
158	lkdtm_handler();
159	jprobe_return();
160}
161
162struct scan_control;
163
164unsigned 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
172int 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
180int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd)
181{
182	lkdtm_handler();
183	jprobe_return();
184	return 0;
185}
186
187#ifdef CONFIG_IDE
188int 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
198static 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
228static 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
240void 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
282void 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
289void 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
302int 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
478void lkdtm_module_exit(void)
479{
480	unregister_jprobe(&lkdtm);
481	printk(KERN_INFO "lkdtm : Crash point unregistered\n");
482}
483
484module_init(lkdtm_module_init);
485module_exit(lkdtm_module_exit);
486
487MODULE_LICENSE("GPL");
488