diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-05-10 12:12:38 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-05-11 19:40:41 -0400 |
commit | 7c0482e3d055e5de056d3c693b821e39205b99ae (patch) | |
tree | ad8d3ff6965d675c6bd255c5665deab7fba5df9f /arch | |
parent | 4e25651b70b8d6ded7229ead8181619e121b648d (diff) |
powerpc/irq: Fix another case of lazy IRQ state getting out of sync
So we have another case of paca->irq_happened getting out of
sync with the HW irq state. This can happen when a perfmon
interrupt occurs while soft disabled, as it will return to a
soft disabled but hard enabled context while leaving a stale
PACA_IRQ_HARD_DIS flag set.
This patch fixes it, and also adds a test for the condition
of those flags being out of sync in arch_local_irq_restore()
when CONFIG_TRACE_IRQFLAGS is enabled.
This helps catching those gremlins faster (and so far I
can't seem see any anymore, so that's good news).
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/kernel/entry_64.S | 44 | ||||
-rw-r--r-- | arch/powerpc/kernel/irq.c | 13 |
2 files changed, 44 insertions, 13 deletions
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index fc6015027a86..ef2074c3e906 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S | |||
@@ -588,23 +588,19 @@ _GLOBAL(ret_from_except_lite) | |||
588 | fast_exc_return_irq: | 588 | fast_exc_return_irq: |
589 | restore: | 589 | restore: |
590 | /* | 590 | /* |
591 | * This is the main kernel exit path, we first check if we | 591 | * This is the main kernel exit path. First we check if we |
592 | * have to change our interrupt state. | 592 | * are about to re-enable interrupts |
593 | */ | 593 | */ |
594 | ld r5,SOFTE(r1) | 594 | ld r5,SOFTE(r1) |
595 | lbz r6,PACASOFTIRQEN(r13) | 595 | lbz r6,PACASOFTIRQEN(r13) |
596 | cmpwi cr1,r5,0 | 596 | cmpwi cr0,r5,0 |
597 | cmpw cr0,r5,r6 | 597 | beq restore_irq_off |
598 | beq cr0,4f | ||
599 | 598 | ||
600 | /* We do, handle disable first, which is easy */ | 599 | /* We are enabling, were we already enabled ? Yes, just return */ |
601 | bne cr1,3f; | 600 | cmpwi cr0,r6,1 |
602 | li r0,0 | 601 | beq cr0,do_restore |
603 | stb r0,PACASOFTIRQEN(r13); | ||
604 | TRACE_DISABLE_INTS | ||
605 | b 4f | ||
606 | 602 | ||
607 | 3: /* | 603 | /* |
608 | * We are about to soft-enable interrupts (we are hard disabled | 604 | * We are about to soft-enable interrupts (we are hard disabled |
609 | * at this point). We check if there's anything that needs to | 605 | * at this point). We check if there's anything that needs to |
610 | * be replayed first. | 606 | * be replayed first. |
@@ -626,7 +622,7 @@ restore_no_replay: | |||
626 | /* | 622 | /* |
627 | * Final return path. BookE is handled in a different file | 623 | * Final return path. BookE is handled in a different file |
628 | */ | 624 | */ |
629 | 4: | 625 | do_restore: |
630 | #ifdef CONFIG_PPC_BOOK3E | 626 | #ifdef CONFIG_PPC_BOOK3E |
631 | b .exception_return_book3e | 627 | b .exception_return_book3e |
632 | #else | 628 | #else |
@@ -700,6 +696,25 @@ fast_exception_return: | |||
700 | #endif /* CONFIG_PPC_BOOK3E */ | 696 | #endif /* CONFIG_PPC_BOOK3E */ |
701 | 697 | ||
702 | /* | 698 | /* |
699 | * We are returning to a context with interrupts soft disabled. | ||
700 | * | ||
701 | * However, we may also about to hard enable, so we need to | ||
702 | * make sure that in this case, we also clear PACA_IRQ_HARD_DIS | ||
703 | * or that bit can get out of sync and bad things will happen | ||
704 | */ | ||
705 | restore_irq_off: | ||
706 | ld r3,_MSR(r1) | ||
707 | lbz r7,PACAIRQHAPPENED(r13) | ||
708 | andi. r0,r3,MSR_EE | ||
709 | beq 1f | ||
710 | rlwinm r7,r7,0,~PACA_IRQ_HARD_DIS | ||
711 | stb r7,PACAIRQHAPPENED(r13) | ||
712 | 1: li r0,0 | ||
713 | stb r0,PACASOFTIRQEN(r13); | ||
714 | TRACE_DISABLE_INTS | ||
715 | b do_restore | ||
716 | |||
717 | /* | ||
703 | * Something did happen, check if a re-emit is needed | 718 | * Something did happen, check if a re-emit is needed |
704 | * (this also clears paca->irq_happened) | 719 | * (this also clears paca->irq_happened) |
705 | */ | 720 | */ |
@@ -748,6 +763,9 @@ restore_check_irq_replay: | |||
748 | #endif /* CONFIG_PPC_BOOK3E */ | 763 | #endif /* CONFIG_PPC_BOOK3E */ |
749 | 1: b .ret_from_except /* What else to do here ? */ | 764 | 1: b .ret_from_except /* What else to do here ? */ |
750 | 765 | ||
766 | |||
767 | |||
768 | 3: | ||
751 | do_work: | 769 | do_work: |
752 | #ifdef CONFIG_PREEMPT | 770 | #ifdef CONFIG_PREEMPT |
753 | andi. r0,r3,MSR_PR /* Returning to user mode? */ | 771 | andi. r0,r3,MSR_PR /* Returning to user mode? */ |
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index c6c6f3b7f8cd..641da9e868ce 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c | |||
@@ -229,6 +229,19 @@ notrace void arch_local_irq_restore(unsigned long en) | |||
229 | */ | 229 | */ |
230 | if (unlikely(irq_happened != PACA_IRQ_HARD_DIS)) | 230 | if (unlikely(irq_happened != PACA_IRQ_HARD_DIS)) |
231 | __hard_irq_disable(); | 231 | __hard_irq_disable(); |
232 | #ifdef CONFIG_TRACE_IRQFLAG | ||
233 | else { | ||
234 | /* | ||
235 | * We should already be hard disabled here. We had bugs | ||
236 | * where that wasn't the case so let's dbl check it and | ||
237 | * warn if we are wrong. Only do that when IRQ tracing | ||
238 | * is enabled as mfmsr() can be costly. | ||
239 | */ | ||
240 | if (WARN_ON(mfmsr() & MSR_EE)) | ||
241 | __hard_irq_disable(); | ||
242 | } | ||
243 | #endif /* CONFIG_TRACE_IRQFLAG */ | ||
244 | |||
232 | set_soft_enabled(0); | 245 | set_soft_enabled(0); |
233 | 246 | ||
234 | /* | 247 | /* |