mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-13 00:29:50 +00:00
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>
This commit is contained in:
parent
4e25651b70
commit
7c0482e3d0
@ -588,23 +588,19 @@ _GLOBAL(ret_from_except_lite)
|
|||||||
fast_exc_return_irq:
|
fast_exc_return_irq:
|
||||||
restore:
|
restore:
|
||||||
/*
|
/*
|
||||||
* This is the main kernel exit path, we first check if we
|
* This is the main kernel exit path. First we check if we
|
||||||
* have to change our interrupt state.
|
* are about to re-enable interrupts
|
||||||
*/
|
*/
|
||||||
ld r5,SOFTE(r1)
|
ld r5,SOFTE(r1)
|
||||||
lbz r6,PACASOFTIRQEN(r13)
|
lbz r6,PACASOFTIRQEN(r13)
|
||||||
cmpwi cr1,r5,0
|
cmpwi cr0,r5,0
|
||||||
cmpw cr0,r5,r6
|
beq restore_irq_off
|
||||||
beq cr0,4f
|
|
||||||
|
|
||||||
/* We do, handle disable first, which is easy */
|
/* We are enabling, were we already enabled ? Yes, just return */
|
||||||
bne cr1,3f;
|
cmpwi cr0,r6,1
|
||||||
li r0,0
|
beq cr0,do_restore
|
||||||
stb r0,PACASOFTIRQEN(r13);
|
|
||||||
TRACE_DISABLE_INTS
|
|
||||||
b 4f
|
|
||||||
|
|
||||||
3: /*
|
/*
|
||||||
* We are about to soft-enable interrupts (we are hard disabled
|
* We are about to soft-enable interrupts (we are hard disabled
|
||||||
* at this point). We check if there's anything that needs to
|
* at this point). We check if there's anything that needs to
|
||||||
* be replayed first.
|
* be replayed first.
|
||||||
@ -626,7 +622,7 @@ restore_no_replay:
|
|||||||
/*
|
/*
|
||||||
* Final return path. BookE is handled in a different file
|
* Final return path. BookE is handled in a different file
|
||||||
*/
|
*/
|
||||||
4:
|
do_restore:
|
||||||
#ifdef CONFIG_PPC_BOOK3E
|
#ifdef CONFIG_PPC_BOOK3E
|
||||||
b .exception_return_book3e
|
b .exception_return_book3e
|
||||||
#else
|
#else
|
||||||
@ -699,6 +695,25 @@ fast_exception_return:
|
|||||||
|
|
||||||
#endif /* CONFIG_PPC_BOOK3E */
|
#endif /* CONFIG_PPC_BOOK3E */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We are returning to a context with interrupts soft disabled.
|
||||||
|
*
|
||||||
|
* However, we may also about to hard enable, so we need to
|
||||||
|
* make sure that in this case, we also clear PACA_IRQ_HARD_DIS
|
||||||
|
* or that bit can get out of sync and bad things will happen
|
||||||
|
*/
|
||||||
|
restore_irq_off:
|
||||||
|
ld r3,_MSR(r1)
|
||||||
|
lbz r7,PACAIRQHAPPENED(r13)
|
||||||
|
andi. r0,r3,MSR_EE
|
||||||
|
beq 1f
|
||||||
|
rlwinm r7,r7,0,~PACA_IRQ_HARD_DIS
|
||||||
|
stb r7,PACAIRQHAPPENED(r13)
|
||||||
|
1: li r0,0
|
||||||
|
stb r0,PACASOFTIRQEN(r13);
|
||||||
|
TRACE_DISABLE_INTS
|
||||||
|
b do_restore
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Something did happen, check if a re-emit is needed
|
* Something did happen, check if a re-emit is needed
|
||||||
* (this also clears paca->irq_happened)
|
* (this also clears paca->irq_happened)
|
||||||
@ -748,6 +763,9 @@ restore_check_irq_replay:
|
|||||||
#endif /* CONFIG_PPC_BOOK3E */
|
#endif /* CONFIG_PPC_BOOK3E */
|
||||||
1: b .ret_from_except /* What else to do here ? */
|
1: b .ret_from_except /* What else to do here ? */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
3:
|
||||||
do_work:
|
do_work:
|
||||||
#ifdef CONFIG_PREEMPT
|
#ifdef CONFIG_PREEMPT
|
||||||
andi. r0,r3,MSR_PR /* Returning to user mode? */
|
andi. r0,r3,MSR_PR /* Returning to user mode? */
|
||||||
|
@ -229,6 +229,19 @@ notrace void arch_local_irq_restore(unsigned long en)
|
|||||||
*/
|
*/
|
||||||
if (unlikely(irq_happened != PACA_IRQ_HARD_DIS))
|
if (unlikely(irq_happened != PACA_IRQ_HARD_DIS))
|
||||||
__hard_irq_disable();
|
__hard_irq_disable();
|
||||||
|
#ifdef CONFIG_TRACE_IRQFLAG
|
||||||
|
else {
|
||||||
|
/*
|
||||||
|
* We should already be hard disabled here. We had bugs
|
||||||
|
* where that wasn't the case so let's dbl check it and
|
||||||
|
* warn if we are wrong. Only do that when IRQ tracing
|
||||||
|
* is enabled as mfmsr() can be costly.
|
||||||
|
*/
|
||||||
|
if (WARN_ON(mfmsr() & MSR_EE))
|
||||||
|
__hard_irq_disable();
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_TRACE_IRQFLAG */
|
||||||
|
|
||||||
set_soft_enabled(0);
|
set_soft_enabled(0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user