Skip to content

Commit 0094368

Browse files
committed
powerpc/64s: Fix unrecoverable SLB crashes due to preemption check
Hugh reported that his trusty G5 crashed after a few hours under load with an "Unrecoverable exception 380". The crash is in interrupt_return() where we check lazy_irq_pending(), which calls get_paca() and with CONFIG_DEBUG_PREEMPT=y that goes to check_preemption_disabled() via debug_smp_processor_id(). As Nick explained on the list: Problem is MSR[RI] is cleared here, ready to do the last few things for interrupt return where we're not allowed to take any other interrupts. SLB interrupts can happen just about anywhere aside from kernel text, global variables, and stack. When that hits, it appears to be unrecoverable due to RI=0. The problematic access is in preempt_count() which is: return READ_ONCE(current_thread_info()->preempt_count); Because of THREAD_INFO_IN_TASK, current_thread_info() just points to current, so the access is to somewhere in kernel memory, but not on the stack or in .data, which means it can cause an SLB miss. If we take an SLB miss with RI=0 it is fatal. The easiest solution is to add a version of lazy_irq_pending() that doesn't do the preemption check and call it from the interrupt return path. Fixes: 68b3458 ("powerpc/64/sycall: Implement syscall entry/exit logic in C") Reported-by: Hugh Dickins <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 07ad112 commit 0094368

File tree

2 files changed

+22
-4
lines changed

2 files changed

+22
-4
lines changed

arch/powerpc/include/asm/hw_irq.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,27 @@ static inline bool arch_irqs_disabled(void)
250250
} \
251251
} while(0)
252252

253+
static inline bool __lazy_irq_pending(u8 irq_happened)
254+
{
255+
return !!(irq_happened & ~PACA_IRQ_HARD_DIS);
256+
}
257+
258+
/*
259+
* Check if a lazy IRQ is pending. Should be called with IRQs hard disabled.
260+
*/
253261
static inline bool lazy_irq_pending(void)
254262
{
255-
return !!(get_paca()->irq_happened & ~PACA_IRQ_HARD_DIS);
263+
return __lazy_irq_pending(get_paca()->irq_happened);
264+
}
265+
266+
/*
267+
* Check if a lazy IRQ is pending, with no debugging checks.
268+
* Should be called with IRQs hard disabled.
269+
* For use in RI disabled code or other constrained situations.
270+
*/
271+
static inline bool lazy_irq_pending_nocheck(void)
272+
{
273+
return __lazy_irq_pending(local_paca->irq_happened);
256274
}
257275

258276
/*

arch/powerpc/kernel/syscall_64.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3,
189189

190190
/* This pattern matches prep_irq_for_idle */
191191
__hard_EE_RI_disable();
192-
if (unlikely(lazy_irq_pending())) {
192+
if (unlikely(lazy_irq_pending_nocheck())) {
193193
__hard_RI_enable();
194194
trace_hardirqs_off();
195195
local_paca->irq_happened |= PACA_IRQ_HARD_DIS;
@@ -264,7 +264,7 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs, unsigned
264264

265265
trace_hardirqs_on();
266266
__hard_EE_RI_disable();
267-
if (unlikely(lazy_irq_pending())) {
267+
if (unlikely(lazy_irq_pending_nocheck())) {
268268
__hard_RI_enable();
269269
trace_hardirqs_off();
270270
local_paca->irq_happened |= PACA_IRQ_HARD_DIS;
@@ -334,7 +334,7 @@ notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs, unsign
334334

335335
trace_hardirqs_on();
336336
__hard_EE_RI_disable();
337-
if (unlikely(lazy_irq_pending())) {
337+
if (unlikely(lazy_irq_pending_nocheck())) {
338338
__hard_RI_enable();
339339
irq_soft_mask_set(IRQS_ALL_DISABLED);
340340
trace_hardirqs_off();

0 commit comments

Comments
 (0)