@@ -137,15 +137,17 @@ static inline notrace int decrementer_check_overflow(void)*/notraceunsignedint__check_irq_replay(void){+unsignedintret_val;/**Weuselocal_pacaratherthanget_paca()toavoidall*thedebug_smp_processor_id()businessinthislowlevel*function*/-unsignedcharhappened=local_paca->irq_happened;+unsignedcharhappened,irq_happened;+happened=irq_happened=local_paca->irq_happened;/* Clear bit 0 which we wouldn't clear otherwise */-local_paca->irq_happened&=~PACA_IRQ_HARD_DIS;+irq_happened&=~PACA_IRQ_HARD_DIS;/**Forcethedeliveryofpendingsoft-disabledinterruptsonPS3.
@@ -161,33 +163,45 @@ notrace unsigned int __check_irq_replay(void)*decrementeritselfratherthanthepacairq_happenedfield*incasewealsohadarolloverwhileharddisabled*/-local_paca->irq_happened&=~PACA_IRQ_DEC;-if(decrementer_check_overflow())-return0x900;+irq_happened&=~PACA_IRQ_DEC;+if(decrementer_check_overflow()){+ret_val=0x900;+gotoreplay;+}/* Finally check if an external interrupt happened */-local_paca->irq_happened&=~PACA_IRQ_EE;-if(happened&PACA_IRQ_EE)-return0x500;+irq_happened&=~PACA_IRQ_EE;+if(happened&PACA_IRQ_EE){+ret_val=0x500;+gotoreplay;+}#ifdef CONFIG_PPC_BOOK3E/* Finally check if an EPR external interrupt happened*thisbitistypicallysetifweneedtohandleanother*"edge"interruptfromwithintheMPIC"EPR"handler*/-local_paca->irq_happened&=~PACA_IRQ_EE_EDGE;-if(happened&PACA_IRQ_EE_EDGE)-return0x500;+irq_happened&=~PACA_IRQ_EE_EDGE;+if(happened&PACA_IRQ_EE_EDGE){+ret_val=0x500;+gotoreplay;+}-local_paca->irq_happened&=~PACA_IRQ_DBELL;-if(happened&PACA_IRQ_DBELL)-return0x280;+irq_happened&=~PACA_IRQ_DBELL;+if(happened&PACA_IRQ_DBELL){+ret_val=0x280;+gotoreplay;+}#endif /* CONFIG_PPC_BOOK3E *//* There should be nothing left ! */-BUG_ON(local_paca->irq_happened!=0);+BUG_ON(irq_happened!=0);+ret_val=0;-return0;+replay:+local_paca->irq_happened=irq_happened;++returnret_val;}notracevoidarch_local_irq_restore(unsignedlongen)
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2012-05-03 02:15:34
On Thu, 2012-05-03 at 09:53 +0800, Wang Sheng-Hui wrote:
local_paca->irq_happened may be changed asychronously.
In my test env (IBM Power 9117-MMA), I installed the RHEL6.2 with the shipped
oprofile. Then I run into kernel v3.4-rc4, setup/start oprofile and start the
LTP test suite.
In a short while, the system would crash. Seems that oprofile may change
the irq_happened.
.../...
Use local var instead of local_paca->irq_happened directly in this function here.
Please check this patch. Any comments are welcome.
It should not as __check_irq_replay() should always be called
with interrupts hard disabled... Do you see any code path
where that is not the case ?
Cheers,
Ben.
@@ -137,15 +137,17 @@ static inline notrace int decrementer_check_overflow(void)*/notraceunsignedint__check_irq_replay(void){+unsignedintret_val;/**Weuselocal_pacaratherthanget_paca()toavoidall*thedebug_smp_processor_id()businessinthislowlevel*function*/-unsignedcharhappened=local_paca->irq_happened;+unsignedcharhappened,irq_happened;+happened=irq_happened=local_paca->irq_happened;/* Clear bit 0 which we wouldn't clear otherwise */-local_paca->irq_happened&=~PACA_IRQ_HARD_DIS;+irq_happened&=~PACA_IRQ_HARD_DIS;/**Forcethedeliveryofpendingsoft-disabledinterruptsonPS3.
@@ -161,33 +163,45 @@ notrace unsigned int __check_irq_replay(void)*decrementeritselfratherthanthepacairq_happenedfield*incasewealsohadarolloverwhileharddisabled*/-local_paca->irq_happened&=~PACA_IRQ_DEC;-if(decrementer_check_overflow())-return0x900;+irq_happened&=~PACA_IRQ_DEC;+if(decrementer_check_overflow()){+ret_val=0x900;+gotoreplay;+}/* Finally check if an external interrupt happened */-local_paca->irq_happened&=~PACA_IRQ_EE;-if(happened&PACA_IRQ_EE)-return0x500;+irq_happened&=~PACA_IRQ_EE;+if(happened&PACA_IRQ_EE){+ret_val=0x500;+gotoreplay;+}#ifdef CONFIG_PPC_BOOK3E/* Finally check if an EPR external interrupt happened*thisbitistypicallysetifweneedtohandleanother*"edge"interruptfromwithintheMPIC"EPR"handler*/-local_paca->irq_happened&=~PACA_IRQ_EE_EDGE;-if(happened&PACA_IRQ_EE_EDGE)-return0x500;+irq_happened&=~PACA_IRQ_EE_EDGE;+if(happened&PACA_IRQ_EE_EDGE){+ret_val=0x500;+gotoreplay;+}-local_paca->irq_happened&=~PACA_IRQ_DBELL;-if(happened&PACA_IRQ_DBELL)-return0x280;+irq_happened&=~PACA_IRQ_DBELL;+if(happened&PACA_IRQ_DBELL){+ret_val=0x280;+gotoreplay;+}#endif /* CONFIG_PPC_BOOK3E *//* There should be nothing left ! */-BUG_ON(local_paca->irq_happened!=0);+BUG_ON(irq_happened!=0);+ret_val=0;-return0;+replay:+local_paca->irq_happened=irq_happened;++returnret_val;}notracevoidarch_local_irq_restore(unsignedlongen)
From: Wang Sheng-Hui <hidden> Date: 2012-05-03 02:27:58
On 2012年05月03日 10:15, Benjamin Herrenschmidt wrote:
On Thu, 2012-05-03 at 09:53 +0800, Wang Sheng-Hui wrote:
quoted
local_paca->irq_happened may be changed asychronously.
In my test env (IBM Power 9117-MMA), I installed the RHEL6.2 with the shipped
oprofile. Then I run into kernel v3.4-rc4, setup/start oprofile and start the
LTP test suite.
In a short while, the system would crash. Seems that oprofile may change
the irq_happened.
.../...
quoted
Use local var instead of local_paca->irq_happened directly in this function here.
Please check this patch. Any comments are welcome.
It should not as __check_irq_replay() should always be called
with interrupts hard disabled... Do you see any code path
where that is not the case ?
This is the only case.
I have run LTP test suite on my system without oprofile over 24 hours
with 3.4-rc4 kernel.
Then I started oprofile, and the system crashed quickly.
I wonder if oprofile does some special changes with the running.
But I'm not familiar with the internal of oprofile.
I tried to change BUG_ON to WARN_ON, and got lots of warnning messages
in dmesg. So I changed it to local var here.
@@ -137,15 +137,17 @@ static inline notrace int decrementer_check_overflow(void)*/notraceunsignedint__check_irq_replay(void){+unsignedintret_val;/**Weuselocal_pacaratherthanget_paca()toavoidall*thedebug_smp_processor_id()businessinthislowlevel*function*/-unsignedcharhappened=local_paca->irq_happened;+unsignedcharhappened,irq_happened;+happened=irq_happened=local_paca->irq_happened;/* Clear bit 0 which we wouldn't clear otherwise */-local_paca->irq_happened&=~PACA_IRQ_HARD_DIS;+irq_happened&=~PACA_IRQ_HARD_DIS;/**Forcethedeliveryofpendingsoft-disabledinterruptsonPS3.
@@ -161,33 +163,45 @@ notrace unsigned int __check_irq_replay(void)*decrementeritselfratherthanthepacairq_happenedfield*incasewealsohadarolloverwhileharddisabled*/-local_paca->irq_happened&=~PACA_IRQ_DEC;-if(decrementer_check_overflow())-return0x900;+irq_happened&=~PACA_IRQ_DEC;+if(decrementer_check_overflow()){+ret_val=0x900;+gotoreplay;+}/* Finally check if an external interrupt happened */-local_paca->irq_happened&=~PACA_IRQ_EE;-if(happened&PACA_IRQ_EE)-return0x500;+irq_happened&=~PACA_IRQ_EE;+if(happened&PACA_IRQ_EE){+ret_val=0x500;+gotoreplay;+}#ifdef CONFIG_PPC_BOOK3E/* Finally check if an EPR external interrupt happened*thisbitistypicallysetifweneedtohandleanother*"edge"interruptfromwithintheMPIC"EPR"handler*/-local_paca->irq_happened&=~PACA_IRQ_EE_EDGE;-if(happened&PACA_IRQ_EE_EDGE)-return0x500;+irq_happened&=~PACA_IRQ_EE_EDGE;+if(happened&PACA_IRQ_EE_EDGE){+ret_val=0x500;+gotoreplay;+}-local_paca->irq_happened&=~PACA_IRQ_DBELL;-if(happened&PACA_IRQ_DBELL)-return0x280;+irq_happened&=~PACA_IRQ_DBELL;+if(happened&PACA_IRQ_DBELL){+ret_val=0x280;+gotoreplay;+}#endif /* CONFIG_PPC_BOOK3E *//* There should be nothing left ! */-BUG_ON(local_paca->irq_happened!=0);+BUG_ON(irq_happened!=0);+ret_val=0;-return0;+replay:+local_paca->irq_happened=irq_happened;++returnret_val;}notracevoidarch_local_irq_restore(unsignedlongen)
From: Wang Sheng-Hui <hidden> Date: 2012-05-03 02:32:24
On 2012年05月03日 10:15, Benjamin Herrenschmidt wrote:
On Thu, 2012-05-03 at 09:53 +0800, Wang Sheng-Hui wrote:
quoted
local_paca->irq_happened may be changed asychronously.
In my test env (IBM Power 9117-MMA), I installed the RHEL6.2 with the shipped
oprofile. Then I run into kernel v3.4-rc4, setup/start oprofile and start the
LTP test suite.
In a short while, the system would crash. Seems that oprofile may change
the irq_happened.
.../...
quoted
Use local var instead of local_paca->irq_happened directly in this function here.
Please check this patch. Any comments are welcome.
It should not as __check_irq_replay() should always be called
with interrupts hard disabled... Do you see any code path
where that is not the case ?
Since __check_irq_replay() should always be called with interrupts hard disabled,
I think it's harmless to use local var here.
@@ -137,15 +137,17 @@ static inline notrace int decrementer_check_overflow(void)*/notraceunsignedint__check_irq_replay(void){+unsignedintret_val;/**Weuselocal_pacaratherthanget_paca()toavoidall*thedebug_smp_processor_id()businessinthislowlevel*function*/-unsignedcharhappened=local_paca->irq_happened;+unsignedcharhappened,irq_happened;+happened=irq_happened=local_paca->irq_happened;/* Clear bit 0 which we wouldn't clear otherwise */-local_paca->irq_happened&=~PACA_IRQ_HARD_DIS;+irq_happened&=~PACA_IRQ_HARD_DIS;/**Forcethedeliveryofpendingsoft-disabledinterruptsonPS3.
@@ -161,33 +163,45 @@ notrace unsigned int __check_irq_replay(void)*decrementeritselfratherthanthepacairq_happenedfield*incasewealsohadarolloverwhileharddisabled*/-local_paca->irq_happened&=~PACA_IRQ_DEC;-if(decrementer_check_overflow())-return0x900;+irq_happened&=~PACA_IRQ_DEC;+if(decrementer_check_overflow()){+ret_val=0x900;+gotoreplay;+}/* Finally check if an external interrupt happened */-local_paca->irq_happened&=~PACA_IRQ_EE;-if(happened&PACA_IRQ_EE)-return0x500;+irq_happened&=~PACA_IRQ_EE;+if(happened&PACA_IRQ_EE){+ret_val=0x500;+gotoreplay;+}#ifdef CONFIG_PPC_BOOK3E/* Finally check if an EPR external interrupt happened*thisbitistypicallysetifweneedtohandleanother*"edge"interruptfromwithintheMPIC"EPR"handler*/-local_paca->irq_happened&=~PACA_IRQ_EE_EDGE;-if(happened&PACA_IRQ_EE_EDGE)-return0x500;+irq_happened&=~PACA_IRQ_EE_EDGE;+if(happened&PACA_IRQ_EE_EDGE){+ret_val=0x500;+gotoreplay;+}-local_paca->irq_happened&=~PACA_IRQ_DBELL;-if(happened&PACA_IRQ_DBELL)-return0x280;+irq_happened&=~PACA_IRQ_DBELL;+if(happened&PACA_IRQ_DBELL){+ret_val=0x280;+gotoreplay;+}#endif /* CONFIG_PPC_BOOK3E *//* There should be nothing left ! */-BUG_ON(local_paca->irq_happened!=0);+BUG_ON(irq_happened!=0);+ret_val=0;-return0;+replay:+local_paca->irq_happened=irq_happened;++returnret_val;}notracevoidarch_local_irq_restore(unsignedlongen)
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2012-05-03 04:23:04
It should not as __check_irq_replay() should always be called
with interrupts hard disabled... Do you see any code path
where that is not the case ?
More specifically, your backtrace seems to indicate that
__check_irq_repay() was called from arch_local_irq_restore() which
should have done this before calling __check_irq_replay():
if (unlikely(irq_happened != PACA_IRQ_HARD_DIS))
__hard_irq_disable();
Now, the only possibility that I can see for an interrupt to come in
and trip the problem you observed would be if for some reason we
had irq_happened set to PACA_IRQ_HARD_DIS while interrupts were
not hard disabled.
Can you try if removing the test (and thus unconditionally calling
__hard_irq_disable()) fixes the problem for you ?
If that is the case, then we need to audit the code to figure out how we
can end up with that bit in irq_happened set and interrupts hard
enabled.
Something like may_hard_irq_enable() shouldn't cause it since it should
only be called while hard disabled but adding a check in there might be
worth it (something like WARN_ON(mfmsr() & MSR_EE)).
Cheers,
Ben.
@@ -137,15 +137,17 @@ static inline notrace int decrementer_check_overflow(void)*/notraceunsignedint__check_irq_replay(void){+unsignedintret_val;/**Weuselocal_pacaratherthanget_paca()toavoidall*thedebug_smp_processor_id()businessinthislowlevel*function*/-unsignedcharhappened=local_paca->irq_happened;+unsignedcharhappened,irq_happened;+happened=irq_happened=local_paca->irq_happened;/* Clear bit 0 which we wouldn't clear otherwise */-local_paca->irq_happened&=~PACA_IRQ_HARD_DIS;+irq_happened&=~PACA_IRQ_HARD_DIS;/**Forcethedeliveryofpendingsoft-disabledinterruptsonPS3.
@@ -161,33 +163,45 @@ notrace unsigned int __check_irq_replay(void)*decrementeritselfratherthanthepacairq_happenedfield*incasewealsohadarolloverwhileharddisabled*/-local_paca->irq_happened&=~PACA_IRQ_DEC;-if(decrementer_check_overflow())-return0x900;+irq_happened&=~PACA_IRQ_DEC;+if(decrementer_check_overflow()){+ret_val=0x900;+gotoreplay;+}/* Finally check if an external interrupt happened */-local_paca->irq_happened&=~PACA_IRQ_EE;-if(happened&PACA_IRQ_EE)-return0x500;+irq_happened&=~PACA_IRQ_EE;+if(happened&PACA_IRQ_EE){+ret_val=0x500;+gotoreplay;+}#ifdef CONFIG_PPC_BOOK3E/* Finally check if an EPR external interrupt happened*thisbitistypicallysetifweneedtohandleanother*"edge"interruptfromwithintheMPIC"EPR"handler*/-local_paca->irq_happened&=~PACA_IRQ_EE_EDGE;-if(happened&PACA_IRQ_EE_EDGE)-return0x500;+irq_happened&=~PACA_IRQ_EE_EDGE;+if(happened&PACA_IRQ_EE_EDGE){+ret_val=0x500;+gotoreplay;+}-local_paca->irq_happened&=~PACA_IRQ_DBELL;-if(happened&PACA_IRQ_DBELL)-return0x280;+irq_happened&=~PACA_IRQ_DBELL;+if(happened&PACA_IRQ_DBELL){+ret_val=0x280;+gotoreplay;+}#endif /* CONFIG_PPC_BOOK3E *//* There should be nothing left ! */-BUG_ON(local_paca->irq_happened!=0);+BUG_ON(irq_happened!=0);+ret_val=0;-return0;+replay:+local_paca->irq_happened=irq_happened;++returnret_val;}notracevoidarch_local_irq_restore(unsignedlongen)
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2012-05-03 04:26:33
On Thu, 2012-05-03 at 10:32 +0800, Wang Sheng-Hui wrote:
quoted
It should not as __check_irq_replay() should always be called
with interrupts hard disabled... Do you see any code path
where that is not the case ?
Since __check_irq_replay() should always be called with interrupts
hard disabled, I think it's harmless to use local var here.
No, that would be papering over the real problem. All oprofile does is
trigger perfmon interrupts (which act as some kind of NMI when
soft-disabled but should be masked by MSR:EE when hard disabled).
So there's a deeper issue here that we need to understand before we can
propose a fix. IE. It should not have happened.
Cheers,
Ben.
From: Wang Sheng-Hui <hidden> Date: 2012-05-03 05:51:38
On 2012年05月03日 12:22, Benjamin Herrenschmidt wrote:
quoted
It should not as __check_irq_replay() should always be called
with interrupts hard disabled... Do you see any code path
where that is not the case ?
More specifically, your backtrace seems to indicate that
__check_irq_repay() was called from arch_local_irq_restore() which
should have done this before calling __check_irq_replay():
if (unlikely(irq_happened != PACA_IRQ_HARD_DIS))
__hard_irq_disable();
Now, the only possibility that I can see for an interrupt to come in
and trip the problem you observed would be if for some reason we
had irq_happened set to PACA_IRQ_HARD_DIS while interrupts were
not hard disabled.
I have a chance to notice that the value is 0x05, not just 0x01.
So I think this is not the case.
Can you try if removing the test (and thus unconditionally calling
__hard_irq_disable()) fixes the problem for you ?
If that is the case, then we need to audit the code to figure out how we
can end up with that bit in irq_happened set and interrupts hard
enabled.
Something like may_hard_irq_enable() shouldn't cause it since it should
only be called while hard disabled but adding a check in there might be
worth it (something like WARN_ON(mfmsr() & MSR_EE)).
Cheers,
Ben.
@@ -137,15 +137,17 @@ static inline notrace int decrementer_check_overflow(void)*/notraceunsignedint__check_irq_replay(void){+unsignedintret_val;/**Weuselocal_pacaratherthanget_paca()toavoidall*thedebug_smp_processor_id()businessinthislowlevel*function*/-unsignedcharhappened=local_paca->irq_happened;+unsignedcharhappened,irq_happened;+happened=irq_happened=local_paca->irq_happened;/* Clear bit 0 which we wouldn't clear otherwise */-local_paca->irq_happened&=~PACA_IRQ_HARD_DIS;+irq_happened&=~PACA_IRQ_HARD_DIS;/**Forcethedeliveryofpendingsoft-disabledinterruptsonPS3.
@@ -161,33 +163,45 @@ notrace unsigned int __check_irq_replay(void)*decrementeritselfratherthanthepacairq_happenedfield*incasewealsohadarolloverwhileharddisabled*/-local_paca->irq_happened&=~PACA_IRQ_DEC;-if(decrementer_check_overflow())-return0x900;+irq_happened&=~PACA_IRQ_DEC;+if(decrementer_check_overflow()){+ret_val=0x900;+gotoreplay;+}/* Finally check if an external interrupt happened */-local_paca->irq_happened&=~PACA_IRQ_EE;-if(happened&PACA_IRQ_EE)-return0x500;+irq_happened&=~PACA_IRQ_EE;+if(happened&PACA_IRQ_EE){+ret_val=0x500;+gotoreplay;+}#ifdef CONFIG_PPC_BOOK3E/* Finally check if an EPR external interrupt happened*thisbitistypicallysetifweneedtohandleanother*"edge"interruptfromwithintheMPIC"EPR"handler*/-local_paca->irq_happened&=~PACA_IRQ_EE_EDGE;-if(happened&PACA_IRQ_EE_EDGE)-return0x500;+irq_happened&=~PACA_IRQ_EE_EDGE;+if(happened&PACA_IRQ_EE_EDGE){+ret_val=0x500;+gotoreplay;+}-local_paca->irq_happened&=~PACA_IRQ_DBELL;-if(happened&PACA_IRQ_DBELL)-return0x280;+irq_happened&=~PACA_IRQ_DBELL;+if(happened&PACA_IRQ_DBELL){+ret_val=0x280;+gotoreplay;+}#endif /* CONFIG_PPC_BOOK3E *//* There should be nothing left ! */-BUG_ON(local_paca->irq_happened!=0);+BUG_ON(irq_happened!=0);+ret_val=0;-return0;+replay:+local_paca->irq_happened=irq_happened;++returnret_val;}notracevoidarch_local_irq_restore(unsignedlongen)
From: Wang Sheng-Hui <hidden> Date: 2012-05-03 06:33:22
On 2012年05月03日 12:22, Benjamin Herrenschmidt wrote:
quoted
It should not as __check_irq_replay() should always be called
with interrupts hard disabled... Do you see any code path
where that is not the case ?
More specifically, your backtrace seems to indicate that
__check_irq_repay() was called from arch_local_irq_restore() which
should have done this before calling __check_irq_replay():
if (unlikely(irq_happened != PACA_IRQ_HARD_DIS))
__hard_irq_disable();
Now, the only possibility that I can see for an interrupt to come in
and trip the problem you observed would be if for some reason we
had irq_happened set to PACA_IRQ_HARD_DIS while interrupts were
not hard disabled.
Can you try if removing the test (and thus unconditionally calling
__hard_irq_disable()) fixes the problem for you ?
The system crashed before I started the LTP test run.
========================================================
kernel BUG at /usr/src/kernels/linux/arch/powerpc/kernel/irq.c:188!
cpu 0x3: Vector: 700 (Program Check) at [c00000026ffd3bb0]
pc: c00000000000ea9c: .__check_irq_replay+0x7c/0x90
lr: c00000000001058c: .arch_local_irq_restore+0x4c/0x90
sp: c00000026ffd3e30
msr: 8000000000029032
current = 0xc0000002694e0110
paca = 0xc000000003580900 softe: 0 irq_happened: 0x01
pid = 0, comm = swapper/3
kernel BUG at /usr/src/kernels/linux/arch/powerpc/kernel/irq.c:188!
enter ? for help
[link register ] c00000000001058c .arch_local_irq_restore+0x4c/0x90
[c00000026ffd3e30] c000000000f42100 softirq_vec+0x0/0x80 (unreliable)
[c00000026ffd3ea0] c0000000000857d4 .__do_softirq+0xa4/0x2a0
[c00000026ffd3f90] c000000000022958 .call_do_softirq+0x14/0x24
[c0000002694778e0] c0000000000106c8 .do_softirq+0xf8/0x130
[c000000269477980] c0000000000854c4 .irq_exit+0xc4/0xf0
[c000000269477a00] c00000000001e970 .timer_interrupt+0x120/0x290
[c000000269477ab0] c000000000003a40 decrementer_common+0x140/0x180
--- Exception: 901 (Decrementer) at c0000000000105c4 .arch_local_irq_restore+0x84/0x90
If that is the case, then we need to audit the code to figure out how we
can end up with that bit in irq_happened set and interrupts hard
enabled.
Something like may_hard_irq_enable() shouldn't cause it since it should
only be called while hard disabled but adding a check in there might be
worth it (something like WARN_ON(mfmsr() & MSR_EE)).
Cheers,
Ben.
@@ -137,15 +137,17 @@ static inline notrace int decrementer_check_overflow(void)*/notraceunsignedint__check_irq_replay(void){+unsignedintret_val;/**Weuselocal_pacaratherthanget_paca()toavoidall*thedebug_smp_processor_id()businessinthislowlevel*function*/-unsignedcharhappened=local_paca->irq_happened;+unsignedcharhappened,irq_happened;+happened=irq_happened=local_paca->irq_happened;/* Clear bit 0 which we wouldn't clear otherwise */-local_paca->irq_happened&=~PACA_IRQ_HARD_DIS;+irq_happened&=~PACA_IRQ_HARD_DIS;/**Forcethedeliveryofpendingsoft-disabledinterruptsonPS3.
@@ -161,33 +163,45 @@ notrace unsigned int __check_irq_replay(void)*decrementeritselfratherthanthepacairq_happenedfield*incasewealsohadarolloverwhileharddisabled*/-local_paca->irq_happened&=~PACA_IRQ_DEC;-if(decrementer_check_overflow())-return0x900;+irq_happened&=~PACA_IRQ_DEC;+if(decrementer_check_overflow()){+ret_val=0x900;+gotoreplay;+}/* Finally check if an external interrupt happened */-local_paca->irq_happened&=~PACA_IRQ_EE;-if(happened&PACA_IRQ_EE)-return0x500;+irq_happened&=~PACA_IRQ_EE;+if(happened&PACA_IRQ_EE){+ret_val=0x500;+gotoreplay;+}#ifdef CONFIG_PPC_BOOK3E/* Finally check if an EPR external interrupt happened*thisbitistypicallysetifweneedtohandleanother*"edge"interruptfromwithintheMPIC"EPR"handler*/-local_paca->irq_happened&=~PACA_IRQ_EE_EDGE;-if(happened&PACA_IRQ_EE_EDGE)-return0x500;+irq_happened&=~PACA_IRQ_EE_EDGE;+if(happened&PACA_IRQ_EE_EDGE){+ret_val=0x500;+gotoreplay;+}-local_paca->irq_happened&=~PACA_IRQ_DBELL;-if(happened&PACA_IRQ_DBELL)-return0x280;+irq_happened&=~PACA_IRQ_DBELL;+if(happened&PACA_IRQ_DBELL){+ret_val=0x280;+gotoreplay;+}#endif /* CONFIG_PPC_BOOK3E *//* There should be nothing left ! */-BUG_ON(local_paca->irq_happened!=0);+BUG_ON(irq_happened!=0);+ret_val=0;-return0;+replay:+local_paca->irq_happened=irq_happened;++returnret_val;}notracevoidarch_local_irq_restore(unsignedlongen)
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2012-05-03 06:52:51
On Thu, 2012-05-03 at 13:51 +0800, Wang Sheng-Hui wrote:
On 2012年05月03日 12:22, Benjamin Herrenschmidt wrote:
quoted
quoted
It should not as __check_irq_replay() should always be called
with interrupts hard disabled... Do you see any code path
where that is not the case ?
More specifically, your backtrace seems to indicate that
__check_irq_repay() was called from arch_local_irq_restore() which
should have done this before calling __check_irq_replay():
if (unlikely(irq_happened != PACA_IRQ_HARD_DIS))
__hard_irq_disable();
Now, the only possibility that I can see for an interrupt to come in
and trip the problem you observed would be if for some reason we
had irq_happened set to PACA_IRQ_HARD_DIS while interrupts were
not hard disabled.
I have a chance to notice that the value is 0x05, not just 0x01.
So I think this is not the case.
Well, it depends, the value could have been 0x01 before it hit there...
However 0x05 means that EE is set too which means it should never have
hard-enabled to begin with. This is all very odd, we'll need to dig.
If the value had been anything other than 0x01 it would have hard
disabled interrupts meaning that paca->irq_happened cannot change
anymore until they are re-enabled at the bottom of the function.
So please try making this disable unconditional see if that makes any
difference...
Cheers,
Ben.
quoted
Can you try if removing the test (and thus unconditionally calling
__hard_irq_disable()) fixes the problem for you ?
If that is the case, then we need to audit the code to figure out how we
can end up with that bit in irq_happened set and interrupts hard
enabled.
Something like may_hard_irq_enable() shouldn't cause it since it should
only be called while hard disabled but adding a check in there might be
worth it (something like WARN_ON(mfmsr() & MSR_EE)).
Cheers,
Ben.
@@ -137,15 +137,17 @@ static inline notrace int decrementer_check_overflow(void)*/notraceunsignedint__check_irq_replay(void){+unsignedintret_val;/**Weuselocal_pacaratherthanget_paca()toavoidall*thedebug_smp_processor_id()businessinthislowlevel*function*/-unsignedcharhappened=local_paca->irq_happened;+unsignedcharhappened,irq_happened;+happened=irq_happened=local_paca->irq_happened;/* Clear bit 0 which we wouldn't clear otherwise */-local_paca->irq_happened&=~PACA_IRQ_HARD_DIS;+irq_happened&=~PACA_IRQ_HARD_DIS;/**Forcethedeliveryofpendingsoft-disabledinterruptsonPS3.
@@ -161,33 +163,45 @@ notrace unsigned int __check_irq_replay(void)*decrementeritselfratherthanthepacairq_happenedfield*incasewealsohadarolloverwhileharddisabled*/-local_paca->irq_happened&=~PACA_IRQ_DEC;-if(decrementer_check_overflow())-return0x900;+irq_happened&=~PACA_IRQ_DEC;+if(decrementer_check_overflow()){+ret_val=0x900;+gotoreplay;+}/* Finally check if an external interrupt happened */-local_paca->irq_happened&=~PACA_IRQ_EE;-if(happened&PACA_IRQ_EE)-return0x500;+irq_happened&=~PACA_IRQ_EE;+if(happened&PACA_IRQ_EE){+ret_val=0x500;+gotoreplay;+}#ifdef CONFIG_PPC_BOOK3E/* Finally check if an EPR external interrupt happened*thisbitistypicallysetifweneedtohandleanother*"edge"interruptfromwithintheMPIC"EPR"handler*/-local_paca->irq_happened&=~PACA_IRQ_EE_EDGE;-if(happened&PACA_IRQ_EE_EDGE)-return0x500;+irq_happened&=~PACA_IRQ_EE_EDGE;+if(happened&PACA_IRQ_EE_EDGE){+ret_val=0x500;+gotoreplay;+}-local_paca->irq_happened&=~PACA_IRQ_DBELL;-if(happened&PACA_IRQ_DBELL)-return0x280;+irq_happened&=~PACA_IRQ_DBELL;+if(happened&PACA_IRQ_DBELL){+ret_val=0x280;+gotoreplay;+}#endif /* CONFIG_PPC_BOOK3E *//* There should be nothing left ! */-BUG_ON(local_paca->irq_happened!=0);+BUG_ON(irq_happened!=0);+ret_val=0;-return0;+replay:+local_paca->irq_happened=irq_happened;++returnret_val;}notracevoidarch_local_irq_restore(unsignedlongen)
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2012-05-03 08:09:45
On Thu, 2012-05-03 at 14:59 +0800, Wang Sheng-Hui wrote:
On 2012年05月03日 14:33, Wang Sheng-Hui wrote:
quoted
if (unlikely(irq_happened != PACA_IRQ_HARD_DIS))
quoted
__hard_irq_disable();
I have commented out the 2 lines.
No, Only comment the test, you must absolutely leave the
__hard_irq_disable() call ! That's the whole point of the test, make
sure we unconditionally disable to see if that fixes the problem, in
which case that will tell us that we somewhere accidentally leave
irq_happened set to 0x01 while irqs are hard enabled.
Cheers,
Ben.
From: Wang Sheng-Hui <hidden> Date: 2012-05-03 23:35:56
On 2012年05月03日 16:09, Benjamin Herrenschmidt wrote:
On Thu, 2012-05-03 at 14:59 +0800, Wang Sheng-Hui wrote:
quoted
On 2012年05月03日 14:33, Wang Sheng-Hui wrote:
quoted
if (unlikely(irq_happened != PACA_IRQ_HARD_DIS))
quoted
__hard_irq_disable();
I have commented out the 2 lines.
No, Only comment the test, you must absolutely leave the
__hard_irq_disable() call ! That's the whole point of the test, make
sure we unconditionally disable to see if that fixes the problem, in
which case that will tell us that we somewhere accidentally leave
irq_happened set to 0x01 while irqs are hard enabled.
It can work.
My system has been running for about 15 hours without crash.
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2012-05-04 00:10:35
On Fri, 2012-05-04 at 07:35 +0800, Wang Sheng-Hui wrote:
quoted
No, Only comment the test, you must absolutely leave the
__hard_irq_disable() call ! That's the whole point of the test, make
sure we unconditionally disable to see if that fixes the problem, in
which case that will tell us that we somewhere accidentally leave
irq_happened set to 0x01 while irqs are hard enabled.
It can work.
My system has been running for about 15 hours without crash.
Ok, so now we need to understand under what circumstances we end up
in a situation where paca->irq_happened is 0x01 and IRQs are hard
enabled. I have a few ideas of things to look at but I'm also off
for the week-end.
I'll have a look next week.
Cheers,
Ben.
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2012-05-08 03:47:06
Hi Wang !
Does this patch fixes it for you ?
From 249f8649bf95a4c3e6637284754a165c1d83c394 Mon Sep 17 00:00:00 2001
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Date: Tue, 8 May 2012 13:31:59 +1000
Subject: [PATCH 2/3] powerpc/irq: Fix bug with new lazy IRQ handling code
We had a case where we could turn on hard interrupts while
leaving the PACA_IRQ_HARD_DIS bit set in the PACA. This can
in turn cause a BUG_ON() to hit in __check_irq_replay() due
to interrupt state getting out of sync.
The assembly code was also way too convoluted. Instead, we
now leave it to the C code to do the right thing which ends
up being smaller and more readable.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/kernel/entry_64.S | 18 ------------------
arch/powerpc/kernel/irq.c | 8 +++++++-
2 files changed, 7 insertions(+), 19 deletions(-)
From 249f8649bf95a4c3e6637284754a165c1d83c394 Mon Sep 17 00:00:00 2001
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Date: Tue, 8 May 2012 13:31:59 +1000
Subject: [PATCH 2/3] powerpc/irq: Fix bug with new lazy IRQ handling code
We had a case where we could turn on hard interrupts while
leaving the PACA_IRQ_HARD_DIS bit set in the PACA. This can
in turn cause a BUG_ON() to hit in __check_irq_replay() due
to interrupt state getting out of sync.
The assembly code was also way too convoluted. Instead, we
now leave it to the C code to do the right thing which ends
up being smaller and more readable.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/kernel/entry_64.S | 18 ------------------
arch/powerpc/kernel/irq.c | 8 +++++++-
2 files changed, 7 insertions(+), 19 deletions(-)