diff options
| author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2010-11-10 04:05:57 -0500 |
|---|---|---|
| committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2010-11-10 04:05:54 -0500 |
| commit | adb45839817392102e659c19e5c19aa39530021f (patch) | |
| tree | 00a28ba170a4f82e083942863997a55e5389b30a /arch | |
| parent | becf91f18750cf1c60828aa2ee63a36b05c2e4d0 (diff) | |
[S390] kprobes: disable interrupts throughout
Execute the kprobe exception and fault handler with interrupts disabled.
To disable the interrupts only while a single step is in progress is not
good enough, a kprobe from interrupt context while another kprobe is
handled can confuse the internal house keeping.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/s390/kernel/kprobes.c | 41 |
1 files changed, 27 insertions, 14 deletions
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index d60fc4398516..70cf73bdba25 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c | |||
| @@ -30,6 +30,7 @@ | |||
| 30 | #include <asm/sections.h> | 30 | #include <asm/sections.h> |
| 31 | #include <linux/module.h> | 31 | #include <linux/module.h> |
| 32 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
| 33 | #include <linux/hardirq.h> | ||
| 33 | 34 | ||
| 34 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; | 35 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; |
| 35 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); | 36 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); |
| @@ -212,7 +213,7 @@ static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | |||
| 212 | /* Set the PER control regs, turns on single step for this address */ | 213 | /* Set the PER control regs, turns on single step for this address */ |
| 213 | __ctl_load(kprobe_per_regs, 9, 11); | 214 | __ctl_load(kprobe_per_regs, 9, 11); |
| 214 | regs->psw.mask |= PSW_MASK_PER; | 215 | regs->psw.mask |= PSW_MASK_PER; |
| 215 | regs->psw.mask &= ~(PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK); | 216 | regs->psw.mask &= ~(PSW_MASK_IO | PSW_MASK_EXT); |
| 216 | } | 217 | } |
| 217 | 218 | ||
| 218 | static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) | 219 | static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) |
| @@ -239,7 +240,7 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, | |||
| 239 | __get_cpu_var(current_kprobe) = p; | 240 | __get_cpu_var(current_kprobe) = p; |
| 240 | /* Save the interrupt and per flags */ | 241 | /* Save the interrupt and per flags */ |
| 241 | kcb->kprobe_saved_imask = regs->psw.mask & | 242 | kcb->kprobe_saved_imask = regs->psw.mask & |
| 242 | (PSW_MASK_PER | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK); | 243 | (PSW_MASK_PER | PSW_MASK_IO | PSW_MASK_EXT); |
| 243 | /* Save the control regs that govern PER */ | 244 | /* Save the control regs that govern PER */ |
| 244 | __ctl_store(kcb->kprobe_saved_ctl, 9, 11); | 245 | __ctl_store(kcb->kprobe_saved_ctl, 9, 11); |
| 245 | } | 246 | } |
| @@ -316,8 +317,6 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) | |||
| 316 | return 1; | 317 | return 1; |
| 317 | 318 | ||
| 318 | ss_probe: | 319 | ss_probe: |
| 319 | if (regs->psw.mask & (PSW_MASK_PER | PSW_MASK_IO)) | ||
| 320 | local_irq_disable(); | ||
| 321 | prepare_singlestep(p, regs); | 320 | prepare_singlestep(p, regs); |
| 322 | kcb->kprobe_status = KPROBE_HIT_SS; | 321 | kcb->kprobe_status = KPROBE_HIT_SS; |
| 323 | return 1; | 322 | return 1; |
| @@ -465,8 +464,6 @@ static int __kprobes post_kprobe_handler(struct pt_regs *regs) | |||
| 465 | goto out; | 464 | goto out; |
| 466 | } | 465 | } |
| 467 | reset_current_kprobe(); | 466 | reset_current_kprobe(); |
| 468 | if (regs->psw.mask & (PSW_MASK_PER | PSW_MASK_IO)) | ||
| 469 | local_irq_enable(); | ||
| 470 | out: | 467 | out: |
| 471 | preempt_enable_no_resched(); | 468 | preempt_enable_no_resched(); |
| 472 | 469 | ||
| @@ -482,7 +479,7 @@ out: | |||
| 482 | return 1; | 479 | return 1; |
| 483 | } | 480 | } |
| 484 | 481 | ||
| 485 | int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) | 482 | static int __kprobes kprobe_trap_handler(struct pt_regs *regs, int trapnr) |
| 486 | { | 483 | { |
| 487 | struct kprobe *cur = kprobe_running(); | 484 | struct kprobe *cur = kprobe_running(); |
| 488 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 485 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
| @@ -508,8 +505,6 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) | |||
| 508 | restore_previous_kprobe(kcb); | 505 | restore_previous_kprobe(kcb); |
| 509 | else { | 506 | else { |
| 510 | reset_current_kprobe(); | 507 | reset_current_kprobe(); |
| 511 | if (regs->psw.mask & (PSW_MASK_PER | PSW_MASK_IO)) | ||
| 512 | local_irq_enable(); | ||
| 513 | } | 508 | } |
| 514 | preempt_enable_no_resched(); | 509 | preempt_enable_no_resched(); |
| 515 | break; | 510 | break; |
| @@ -553,6 +548,18 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) | |||
| 553 | return 0; | 548 | return 0; |
| 554 | } | 549 | } |
| 555 | 550 | ||
| 551 | int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) | ||
| 552 | { | ||
| 553 | int ret; | ||
| 554 | |||
| 555 | if (regs->psw.mask & (PSW_MASK_IO | PSW_MASK_EXT)) | ||
| 556 | local_irq_disable(); | ||
| 557 | ret = kprobe_trap_handler(regs, trapnr); | ||
| 558 | if (regs->psw.mask & (PSW_MASK_IO | PSW_MASK_EXT)) | ||
| 559 | local_irq_restore(regs->psw.mask & ~PSW_MASK_PER); | ||
| 560 | return ret; | ||
| 561 | } | ||
| 562 | |||
| 556 | /* | 563 | /* |
| 557 | * Wrapper routine to for handling exceptions. | 564 | * Wrapper routine to for handling exceptions. |
| 558 | */ | 565 | */ |
| @@ -560,8 +567,12 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self, | |||
| 560 | unsigned long val, void *data) | 567 | unsigned long val, void *data) |
| 561 | { | 568 | { |
| 562 | struct die_args *args = (struct die_args *)data; | 569 | struct die_args *args = (struct die_args *)data; |
| 570 | struct pt_regs *regs = args->regs; | ||
| 563 | int ret = NOTIFY_DONE; | 571 | int ret = NOTIFY_DONE; |
| 564 | 572 | ||
| 573 | if (regs->psw.mask & (PSW_MASK_IO | PSW_MASK_EXT)) | ||
| 574 | local_irq_disable(); | ||
| 575 | |||
| 565 | switch (val) { | 576 | switch (val) { |
| 566 | case DIE_BPT: | 577 | case DIE_BPT: |
| 567 | if (kprobe_handler(args->regs)) | 578 | if (kprobe_handler(args->regs)) |
| @@ -572,16 +583,17 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self, | |||
| 572 | ret = NOTIFY_STOP; | 583 | ret = NOTIFY_STOP; |
| 573 | break; | 584 | break; |
| 574 | case DIE_TRAP: | 585 | case DIE_TRAP: |
| 575 | /* kprobe_running() needs smp_processor_id() */ | 586 | if (!preemptible() && kprobe_running() && |
| 576 | preempt_disable(); | 587 | kprobe_trap_handler(args->regs, args->trapnr)) |
| 577 | if (kprobe_running() && | ||
| 578 | kprobe_fault_handler(args->regs, args->trapnr)) | ||
| 579 | ret = NOTIFY_STOP; | 588 | ret = NOTIFY_STOP; |
| 580 | preempt_enable(); | ||
| 581 | break; | 589 | break; |
| 582 | default: | 590 | default: |
| 583 | break; | 591 | break; |
| 584 | } | 592 | } |
| 593 | |||
| 594 | if (regs->psw.mask & (PSW_MASK_IO | PSW_MASK_EXT)) | ||
| 595 | local_irq_restore(regs->psw.mask & ~PSW_MASK_PER); | ||
| 596 | |||
| 585 | return ret; | 597 | return ret; |
| 586 | } | 598 | } |
| 587 | 599 | ||
| @@ -595,6 +607,7 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) | |||
| 595 | 607 | ||
| 596 | /* setup return addr to the jprobe handler routine */ | 608 | /* setup return addr to the jprobe handler routine */ |
| 597 | regs->psw.addr = (unsigned long)(jp->entry) | PSW_ADDR_AMODE; | 609 | regs->psw.addr = (unsigned long)(jp->entry) | PSW_ADDR_AMODE; |
| 610 | regs->psw.mask &= ~(PSW_MASK_IO | PSW_MASK_EXT); | ||
| 598 | 611 | ||
| 599 | /* r14 is the function return address */ | 612 | /* r14 is the function return address */ |
| 600 | kcb->jprobe_saved_r14 = (unsigned long)regs->gprs[14]; | 613 | kcb->jprobe_saved_r14 = (unsigned long)regs->gprs[14]; |
