diff options
Diffstat (limited to 'drivers/misc/lkdtm.c')
-rw-r--r-- | drivers/misc/lkdtm.c | 473 |
1 files changed, 391 insertions, 82 deletions
diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 3648b23d5c92..31a991161f0a 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c | |||
@@ -26,21 +26,9 @@ | |||
26 | * It is adapted from the Linux Kernel Dump Test Tool by | 26 | * It is adapted from the Linux Kernel Dump Test Tool by |
27 | * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net> | 27 | * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net> |
28 | * | 28 | * |
29 | * Usage : insmod lkdtm.ko [recur_count={>0}] cpoint_name=<> cpoint_type=<> | 29 | * Debugfs support added by Simon Kagstrom <simon.kagstrom@netinsight.net> |
30 | * [cpoint_count={>0}] | ||
31 | * | 30 | * |
32 | * recur_count : Recursion level for the stack overflow test. Default is 10. | 31 | * See Documentation/fault-injection/provoke-crashes.txt for instructions |
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 | */ | 32 | */ |
45 | 33 | ||
46 | #include <linux/kernel.h> | 34 | #include <linux/kernel.h> |
@@ -52,14 +40,14 @@ | |||
52 | #include <linux/init.h> | 40 | #include <linux/init.h> |
53 | #include <linux/interrupt.h> | 41 | #include <linux/interrupt.h> |
54 | #include <linux/hrtimer.h> | 42 | #include <linux/hrtimer.h> |
43 | #include <linux/slab.h> | ||
55 | #include <scsi/scsi_cmnd.h> | 44 | #include <scsi/scsi_cmnd.h> |
45 | #include <linux/debugfs.h> | ||
56 | 46 | ||
57 | #ifdef CONFIG_IDE | 47 | #ifdef CONFIG_IDE |
58 | #include <linux/ide.h> | 48 | #include <linux/ide.h> |
59 | #endif | 49 | #endif |
60 | 50 | ||
61 | #define NUM_CPOINTS 8 | ||
62 | #define NUM_CPOINT_TYPES 5 | ||
63 | #define DEFAULT_COUNT 10 | 51 | #define DEFAULT_COUNT 10 |
64 | #define REC_NUM_DEFAULT 10 | 52 | #define REC_NUM_DEFAULT 10 |
65 | 53 | ||
@@ -72,7 +60,8 @@ enum cname { | |||
72 | MEM_SWAPOUT, | 60 | MEM_SWAPOUT, |
73 | TIMERADD, | 61 | TIMERADD, |
74 | SCSI_DISPATCH_CMD, | 62 | SCSI_DISPATCH_CMD, |
75 | IDE_CORE_CP | 63 | IDE_CORE_CP, |
64 | DIRECT, | ||
76 | }; | 65 | }; |
77 | 66 | ||
78 | enum ctype { | 67 | enum ctype { |
@@ -81,7 +70,11 @@ enum ctype { | |||
81 | BUG, | 70 | BUG, |
82 | EXCEPTION, | 71 | EXCEPTION, |
83 | LOOP, | 72 | LOOP, |
84 | OVERFLOW | 73 | OVERFLOW, |
74 | CORRUPT_STACK, | ||
75 | UNALIGNED_LOAD_STORE_WRITE, | ||
76 | OVERWRITE_ALLOCATION, | ||
77 | WRITE_AFTER_FREE, | ||
85 | }; | 78 | }; |
86 | 79 | ||
87 | static char* cp_name[] = { | 80 | static char* cp_name[] = { |
@@ -92,7 +85,8 @@ static char* cp_name[] = { | |||
92 | "MEM_SWAPOUT", | 85 | "MEM_SWAPOUT", |
93 | "TIMERADD", | 86 | "TIMERADD", |
94 | "SCSI_DISPATCH_CMD", | 87 | "SCSI_DISPATCH_CMD", |
95 | "IDE_CORE_CP" | 88 | "IDE_CORE_CP", |
89 | "DIRECT", | ||
96 | }; | 90 | }; |
97 | 91 | ||
98 | static char* cp_type[] = { | 92 | static char* cp_type[] = { |
@@ -100,7 +94,11 @@ static char* cp_type[] = { | |||
100 | "BUG", | 94 | "BUG", |
101 | "EXCEPTION", | 95 | "EXCEPTION", |
102 | "LOOP", | 96 | "LOOP", |
103 | "OVERFLOW" | 97 | "OVERFLOW", |
98 | "CORRUPT_STACK", | ||
99 | "UNALIGNED_LOAD_STORE_WRITE", | ||
100 | "OVERWRITE_ALLOCATION", | ||
101 | "WRITE_AFTER_FREE", | ||
104 | }; | 102 | }; |
105 | 103 | ||
106 | static struct jprobe lkdtm; | 104 | static struct jprobe lkdtm; |
@@ -193,34 +191,66 @@ int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file, | |||
193 | } | 191 | } |
194 | #endif | 192 | #endif |
195 | 193 | ||
194 | /* Return the crashpoint number or NONE if the name is invalid */ | ||
195 | static enum ctype parse_cp_type(const char *what, size_t count) | ||
196 | { | ||
197 | int i; | ||
198 | |||
199 | for (i = 0; i < ARRAY_SIZE(cp_type); i++) { | ||
200 | if (!strcmp(what, cp_type[i])) | ||
201 | return i + 1; | ||
202 | } | ||
203 | |||
204 | return NONE; | ||
205 | } | ||
206 | |||
207 | static const char *cp_type_to_str(enum ctype type) | ||
208 | { | ||
209 | if (type == NONE || type < 0 || type > ARRAY_SIZE(cp_type)) | ||
210 | return "None"; | ||
211 | |||
212 | return cp_type[type - 1]; | ||
213 | } | ||
214 | |||
215 | static const char *cp_name_to_str(enum cname name) | ||
216 | { | ||
217 | if (name == INVALID || name < 0 || name > ARRAY_SIZE(cp_name)) | ||
218 | return "INVALID"; | ||
219 | |||
220 | return cp_name[name - 1]; | ||
221 | } | ||
222 | |||
223 | |||
196 | static int lkdtm_parse_commandline(void) | 224 | static int lkdtm_parse_commandline(void) |
197 | { | 225 | { |
198 | int i; | 226 | int i; |
199 | 227 | ||
200 | if (cpoint_name == NULL || cpoint_type == NULL || | 228 | if (cpoint_count < 1 || recur_count < 1) |
201 | cpoint_count < 1 || recur_count < 1) | ||
202 | return -EINVAL; | 229 | return -EINVAL; |
203 | 230 | ||
204 | for (i = 0; i < NUM_CPOINTS; ++i) { | 231 | count = cpoint_count; |
232 | |||
233 | /* No special parameters */ | ||
234 | if (!cpoint_type && !cpoint_name) | ||
235 | return 0; | ||
236 | |||
237 | /* Neither or both of these need to be set */ | ||
238 | if (!cpoint_type || !cpoint_name) | ||
239 | return -EINVAL; | ||
240 | |||
241 | cptype = parse_cp_type(cpoint_type, strlen(cpoint_type)); | ||
242 | if (cptype == NONE) | ||
243 | return -EINVAL; | ||
244 | |||
245 | for (i = 0; i < ARRAY_SIZE(cp_name); i++) { | ||
205 | if (!strcmp(cpoint_name, cp_name[i])) { | 246 | if (!strcmp(cpoint_name, cp_name[i])) { |
206 | cpoint = i + 1; | 247 | cpoint = i + 1; |
207 | break; | 248 | return 0; |
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 | } | 249 | } |
216 | } | 250 | } |
217 | 251 | ||
218 | if (cpoint == INVALID || cptype == NONE) | 252 | /* Could not find a valid crash point */ |
219 | return -EINVAL; | 253 | return -EINVAL; |
220 | |||
221 | count = cpoint_count; | ||
222 | |||
223 | return 0; | ||
224 | } | 254 | } |
225 | 255 | ||
226 | static int recursive_loop(int a) | 256 | static int recursive_loop(int a) |
@@ -235,53 +265,92 @@ static int recursive_loop(int a) | |||
235 | return recursive_loop(a); | 265 | return recursive_loop(a); |
236 | } | 266 | } |
237 | 267 | ||
238 | void lkdtm_handler(void) | 268 | static void lkdtm_do_action(enum ctype which) |
239 | { | 269 | { |
240 | printk(KERN_INFO "lkdtm : Crash point %s of type %s hit\n", | 270 | switch (which) { |
241 | cpoint_name, cpoint_type); | 271 | case PANIC: |
242 | --count; | 272 | panic("dumptest"); |
273 | break; | ||
274 | case BUG: | ||
275 | BUG(); | ||
276 | break; | ||
277 | case EXCEPTION: | ||
278 | *((int *) 0) = 0; | ||
279 | break; | ||
280 | case LOOP: | ||
281 | for (;;) | ||
282 | ; | ||
283 | break; | ||
284 | case OVERFLOW: | ||
285 | (void) recursive_loop(0); | ||
286 | break; | ||
287 | case CORRUPT_STACK: { | ||
288 | volatile u32 data[8]; | ||
289 | volatile u32 *p = data; | ||
290 | |||
291 | p[12] = 0x12345678; | ||
292 | break; | ||
293 | } | ||
294 | case UNALIGNED_LOAD_STORE_WRITE: { | ||
295 | static u8 data[5] __attribute__((aligned(4))) = {1, 2, | ||
296 | 3, 4, 5}; | ||
297 | u32 *p; | ||
298 | u32 val = 0x12345678; | ||
299 | |||
300 | p = (u32 *)(data + 1); | ||
301 | if (*p == 0) | ||
302 | val = 0x87654321; | ||
303 | *p = val; | ||
304 | break; | ||
305 | } | ||
306 | case OVERWRITE_ALLOCATION: { | ||
307 | size_t len = 1020; | ||
308 | u32 *data = kmalloc(len, GFP_KERNEL); | ||
309 | |||
310 | data[1024 / sizeof(u32)] = 0x12345678; | ||
311 | kfree(data); | ||
312 | break; | ||
313 | } | ||
314 | case WRITE_AFTER_FREE: { | ||
315 | size_t len = 1024; | ||
316 | u32 *data = kmalloc(len, GFP_KERNEL); | ||
317 | |||
318 | kfree(data); | ||
319 | schedule(); | ||
320 | memset(data, 0x78, len); | ||
321 | break; | ||
322 | } | ||
323 | case NONE: | ||
324 | default: | ||
325 | break; | ||
326 | } | ||
327 | |||
328 | } | ||
329 | |||
330 | static void lkdtm_handler(void) | ||
331 | { | ||
332 | count--; | ||
333 | printk(KERN_INFO "lkdtm: Crash point %s of type %s hit, trigger in %d rounds\n", | ||
334 | cp_name_to_str(cpoint), cp_type_to_str(cptype), count); | ||
243 | 335 | ||
244 | if (count == 0) { | 336 | if (count == 0) { |
245 | switch (cptype) { | 337 | lkdtm_do_action(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; | 338 | count = cpoint_count; |
272 | } | 339 | } |
273 | } | 340 | } |
274 | 341 | ||
275 | static int __init lkdtm_module_init(void) | 342 | static int lkdtm_register_cpoint(enum cname which) |
276 | { | 343 | { |
277 | int ret; | 344 | int ret; |
278 | 345 | ||
279 | if (lkdtm_parse_commandline() == -EINVAL) { | 346 | cpoint = INVALID; |
280 | printk(KERN_INFO "lkdtm : Invalid command\n"); | 347 | if (lkdtm.entry != NULL) |
281 | return -EINVAL; | 348 | unregister_jprobe(&lkdtm); |
282 | } | ||
283 | 349 | ||
284 | switch (cpoint) { | 350 | switch (which) { |
351 | case DIRECT: | ||
352 | lkdtm_do_action(cptype); | ||
353 | return 0; | ||
285 | case INT_HARDWARE_ENTRY: | 354 | case INT_HARDWARE_ENTRY: |
286 | lkdtm.kp.symbol_name = "do_IRQ"; | 355 | lkdtm.kp.symbol_name = "do_IRQ"; |
287 | lkdtm.entry = (kprobe_opcode_t*) jp_do_irq; | 356 | lkdtm.entry = (kprobe_opcode_t*) jp_do_irq; |
@@ -315,28 +384,268 @@ static int __init lkdtm_module_init(void) | |||
315 | lkdtm.kp.symbol_name = "generic_ide_ioctl"; | 384 | lkdtm.kp.symbol_name = "generic_ide_ioctl"; |
316 | lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl; | 385 | lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl; |
317 | #else | 386 | #else |
318 | printk(KERN_INFO "lkdtm : Crash point not available\n"); | 387 | printk(KERN_INFO "lkdtm: Crash point not available\n"); |
388 | return -EINVAL; | ||
319 | #endif | 389 | #endif |
320 | break; | 390 | break; |
321 | default: | 391 | default: |
322 | printk(KERN_INFO "lkdtm : Invalid Crash Point\n"); | 392 | printk(KERN_INFO "lkdtm: Invalid Crash Point\n"); |
323 | break; | 393 | return -EINVAL; |
324 | } | 394 | } |
325 | 395 | ||
396 | cpoint = which; | ||
326 | if ((ret = register_jprobe(&lkdtm)) < 0) { | 397 | if ((ret = register_jprobe(&lkdtm)) < 0) { |
327 | printk(KERN_INFO "lkdtm : Couldn't register jprobe\n"); | 398 | printk(KERN_INFO "lkdtm: Couldn't register jprobe\n"); |
328 | return ret; | 399 | cpoint = INVALID; |
400 | } | ||
401 | |||
402 | return ret; | ||
403 | } | ||
404 | |||
405 | static ssize_t do_register_entry(enum cname which, struct file *f, | ||
406 | const char __user *user_buf, size_t count, loff_t *off) | ||
407 | { | ||
408 | char *buf; | ||
409 | int err; | ||
410 | |||
411 | if (count >= PAGE_SIZE) | ||
412 | return -EINVAL; | ||
413 | |||
414 | buf = (char *)__get_free_page(GFP_KERNEL); | ||
415 | if (!buf) | ||
416 | return -ENOMEM; | ||
417 | if (copy_from_user(buf, user_buf, count)) { | ||
418 | free_page((unsigned long) buf); | ||
419 | return -EFAULT; | ||
420 | } | ||
421 | /* NULL-terminate and remove enter */ | ||
422 | buf[count] = '\0'; | ||
423 | strim(buf); | ||
424 | |||
425 | cptype = parse_cp_type(buf, count); | ||
426 | free_page((unsigned long) buf); | ||
427 | |||
428 | if (cptype == NONE) | ||
429 | return -EINVAL; | ||
430 | |||
431 | err = lkdtm_register_cpoint(which); | ||
432 | if (err < 0) | ||
433 | return err; | ||
434 | |||
435 | *off += count; | ||
436 | |||
437 | return count; | ||
438 | } | ||
439 | |||
440 | /* Generic read callback that just prints out the available crash types */ | ||
441 | static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf, | ||
442 | size_t count, loff_t *off) | ||
443 | { | ||
444 | char *buf; | ||
445 | int i, n, out; | ||
446 | |||
447 | buf = (char *)__get_free_page(GFP_KERNEL); | ||
448 | |||
449 | n = snprintf(buf, PAGE_SIZE, "Available crash types:\n"); | ||
450 | for (i = 0; i < ARRAY_SIZE(cp_type); i++) | ||
451 | n += snprintf(buf + n, PAGE_SIZE - n, "%s\n", cp_type[i]); | ||
452 | buf[n] = '\0'; | ||
453 | |||
454 | out = simple_read_from_buffer(user_buf, count, off, | ||
455 | buf, n); | ||
456 | free_page((unsigned long) buf); | ||
457 | |||
458 | return out; | ||
459 | } | ||
460 | |||
461 | static int lkdtm_debugfs_open(struct inode *inode, struct file *file) | ||
462 | { | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | |||
467 | static ssize_t int_hardware_entry(struct file *f, const char __user *buf, | ||
468 | size_t count, loff_t *off) | ||
469 | { | ||
470 | return do_register_entry(INT_HARDWARE_ENTRY, f, buf, count, off); | ||
471 | } | ||
472 | |||
473 | static ssize_t int_hw_irq_en(struct file *f, const char __user *buf, | ||
474 | size_t count, loff_t *off) | ||
475 | { | ||
476 | return do_register_entry(INT_HW_IRQ_EN, f, buf, count, off); | ||
477 | } | ||
478 | |||
479 | static ssize_t int_tasklet_entry(struct file *f, const char __user *buf, | ||
480 | size_t count, loff_t *off) | ||
481 | { | ||
482 | return do_register_entry(INT_TASKLET_ENTRY, f, buf, count, off); | ||
483 | } | ||
484 | |||
485 | static ssize_t fs_devrw_entry(struct file *f, const char __user *buf, | ||
486 | size_t count, loff_t *off) | ||
487 | { | ||
488 | return do_register_entry(FS_DEVRW, f, buf, count, off); | ||
489 | } | ||
490 | |||
491 | static ssize_t mem_swapout_entry(struct file *f, const char __user *buf, | ||
492 | size_t count, loff_t *off) | ||
493 | { | ||
494 | return do_register_entry(MEM_SWAPOUT, f, buf, count, off); | ||
495 | } | ||
496 | |||
497 | static ssize_t timeradd_entry(struct file *f, const char __user *buf, | ||
498 | size_t count, loff_t *off) | ||
499 | { | ||
500 | return do_register_entry(TIMERADD, f, buf, count, off); | ||
501 | } | ||
502 | |||
503 | static ssize_t scsi_dispatch_cmd_entry(struct file *f, | ||
504 | const char __user *buf, size_t count, loff_t *off) | ||
505 | { | ||
506 | return do_register_entry(SCSI_DISPATCH_CMD, f, buf, count, off); | ||
507 | } | ||
508 | |||
509 | static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf, | ||
510 | size_t count, loff_t *off) | ||
511 | { | ||
512 | return do_register_entry(IDE_CORE_CP, f, buf, count, off); | ||
513 | } | ||
514 | |||
515 | /* Special entry to just crash directly. Available without KPROBEs */ | ||
516 | static ssize_t direct_entry(struct file *f, const char __user *user_buf, | ||
517 | size_t count, loff_t *off) | ||
518 | { | ||
519 | enum ctype type; | ||
520 | char *buf; | ||
521 | |||
522 | if (count >= PAGE_SIZE) | ||
523 | return -EINVAL; | ||
524 | if (count < 1) | ||
525 | return -EINVAL; | ||
526 | |||
527 | buf = (char *)__get_free_page(GFP_KERNEL); | ||
528 | if (!buf) | ||
529 | return -ENOMEM; | ||
530 | if (copy_from_user(buf, user_buf, count)) { | ||
531 | free_page((unsigned long) buf); | ||
532 | return -EFAULT; | ||
533 | } | ||
534 | /* NULL-terminate and remove enter */ | ||
535 | buf[count] = '\0'; | ||
536 | strim(buf); | ||
537 | |||
538 | type = parse_cp_type(buf, count); | ||
539 | free_page((unsigned long) buf); | ||
540 | if (type == NONE) | ||
541 | return -EINVAL; | ||
542 | |||
543 | printk(KERN_INFO "lkdtm: Performing direct entry %s\n", | ||
544 | cp_type_to_str(type)); | ||
545 | lkdtm_do_action(type); | ||
546 | *off += count; | ||
547 | |||
548 | return count; | ||
549 | } | ||
550 | |||
551 | struct crash_entry { | ||
552 | const char *name; | ||
553 | const struct file_operations fops; | ||
554 | }; | ||
555 | |||
556 | static const struct crash_entry crash_entries[] = { | ||
557 | {"DIRECT", {.read = lkdtm_debugfs_read, | ||
558 | .open = lkdtm_debugfs_open, | ||
559 | .write = direct_entry} }, | ||
560 | {"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read, | ||
561 | .open = lkdtm_debugfs_open, | ||
562 | .write = int_hardware_entry} }, | ||
563 | {"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read, | ||
564 | .open = lkdtm_debugfs_open, | ||
565 | .write = int_hw_irq_en} }, | ||
566 | {"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read, | ||
567 | .open = lkdtm_debugfs_open, | ||
568 | .write = int_tasklet_entry} }, | ||
569 | {"FS_DEVRW", {.read = lkdtm_debugfs_read, | ||
570 | .open = lkdtm_debugfs_open, | ||
571 | .write = fs_devrw_entry} }, | ||
572 | {"MEM_SWAPOUT", {.read = lkdtm_debugfs_read, | ||
573 | .open = lkdtm_debugfs_open, | ||
574 | .write = mem_swapout_entry} }, | ||
575 | {"TIMERADD", {.read = lkdtm_debugfs_read, | ||
576 | .open = lkdtm_debugfs_open, | ||
577 | .write = timeradd_entry} }, | ||
578 | {"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read, | ||
579 | .open = lkdtm_debugfs_open, | ||
580 | .write = scsi_dispatch_cmd_entry} }, | ||
581 | {"IDE_CORE_CP", {.read = lkdtm_debugfs_read, | ||
582 | .open = lkdtm_debugfs_open, | ||
583 | .write = ide_core_cp_entry} }, | ||
584 | }; | ||
585 | |||
586 | static struct dentry *lkdtm_debugfs_root; | ||
587 | |||
588 | static int __init lkdtm_module_init(void) | ||
589 | { | ||
590 | int ret = -EINVAL; | ||
591 | int n_debugfs_entries = 1; /* Assume only the direct entry */ | ||
592 | int i; | ||
593 | |||
594 | /* Register debugfs interface */ | ||
595 | lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL); | ||
596 | if (!lkdtm_debugfs_root) { | ||
597 | printk(KERN_ERR "lkdtm: creating root dir failed\n"); | ||
598 | return -ENODEV; | ||
599 | } | ||
600 | |||
601 | #ifdef CONFIG_KPROBES | ||
602 | n_debugfs_entries = ARRAY_SIZE(crash_entries); | ||
603 | #endif | ||
604 | |||
605 | for (i = 0; i < n_debugfs_entries; i++) { | ||
606 | const struct crash_entry *cur = &crash_entries[i]; | ||
607 | struct dentry *de; | ||
608 | |||
609 | de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root, | ||
610 | NULL, &cur->fops); | ||
611 | if (de == NULL) { | ||
612 | printk(KERN_ERR "lkdtm: could not create %s\n", | ||
613 | cur->name); | ||
614 | goto out_err; | ||
615 | } | ||
616 | } | ||
617 | |||
618 | if (lkdtm_parse_commandline() == -EINVAL) { | ||
619 | printk(KERN_INFO "lkdtm: Invalid command\n"); | ||
620 | goto out_err; | ||
621 | } | ||
622 | |||
623 | if (cpoint != INVALID && cptype != NONE) { | ||
624 | ret = lkdtm_register_cpoint(cpoint); | ||
625 | if (ret < 0) { | ||
626 | printk(KERN_INFO "lkdtm: Invalid crash point %d\n", | ||
627 | cpoint); | ||
628 | goto out_err; | ||
629 | } | ||
630 | printk(KERN_INFO "lkdtm: Crash point %s of type %s registered\n", | ||
631 | cpoint_name, cpoint_type); | ||
632 | } else { | ||
633 | printk(KERN_INFO "lkdtm: No crash points registered, enable through debugfs\n"); | ||
329 | } | 634 | } |
330 | 635 | ||
331 | printk(KERN_INFO "lkdtm : Crash point %s of type %s registered\n", | ||
332 | cpoint_name, cpoint_type); | ||
333 | return 0; | 636 | return 0; |
637 | |||
638 | out_err: | ||
639 | debugfs_remove_recursive(lkdtm_debugfs_root); | ||
640 | return ret; | ||
334 | } | 641 | } |
335 | 642 | ||
336 | static void __exit lkdtm_module_exit(void) | 643 | static void __exit lkdtm_module_exit(void) |
337 | { | 644 | { |
338 | unregister_jprobe(&lkdtm); | 645 | debugfs_remove_recursive(lkdtm_debugfs_root); |
339 | printk(KERN_INFO "lkdtm : Crash point unregistered\n"); | 646 | |
647 | unregister_jprobe(&lkdtm); | ||
648 | printk(KERN_INFO "lkdtm: Crash point unregistered\n"); | ||
340 | } | 649 | } |
341 | 650 | ||
342 | module_init(lkdtm_module_init); | 651 | module_init(lkdtm_module_init); |