[PATCH] powerpc/64s: Fix unrecoverable SLB crashes due to preemption check

Hugh Dickins hughd at google.com
Sun May 3 17:10:10 AEST 2020


On Sun, 3 May 2020, Michael Ellerman wrote:

> 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: 68b34588e202 ("powerpc/64/sycall: Implement syscall entry/exit logic in C")
> Reported-by: Hugh Dickins <hughd at google.com>
> Signed-off-by: Michael Ellerman <mpe at ellerman.id.au>

Thank you, Michael and Nick: this has been running fine all day for me.

Hugh

> ---
>  arch/powerpc/include/asm/hw_irq.h | 20 +++++++++++++++++++-
>  arch/powerpc/kernel/syscall_64.c  |  6 +++---
>  2 files changed, 22 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h
> index e0e71777961f..3a0db7b0b46e 100644
> --- a/arch/powerpc/include/asm/hw_irq.h
> +++ b/arch/powerpc/include/asm/hw_irq.h
> @@ -250,9 +250,27 @@ static inline bool arch_irqs_disabled(void)
>  	}								\
>  } while(0)
>  
> +static inline bool __lazy_irq_pending(u8 irq_happened)
> +{
> +	return !!(irq_happened & ~PACA_IRQ_HARD_DIS);
> +}
> +
> +/*
> + * Check if a lazy IRQ is pending. Should be called with IRQs hard disabled.
> + */
>  static inline bool lazy_irq_pending(void)
>  {
> -	return !!(get_paca()->irq_happened & ~PACA_IRQ_HARD_DIS);
> +	return __lazy_irq_pending(get_paca()->irq_happened);
> +}
> +
> +/*
> + * Check if a lazy IRQ is pending, with no debugging checks.
> + * Should be called with IRQs hard disabled.
> + * For use in RI disabled code or other constrained situations.
> + */
> +static inline bool lazy_irq_pending_nocheck(void)
> +{
> +	return __lazy_irq_pending(local_paca->irq_happened);
>  }
>  
>  /*
> diff --git a/arch/powerpc/kernel/syscall_64.c b/arch/powerpc/kernel/syscall_64.c
> index c74295a7765b..1fe94dd9de32 100644
> --- a/arch/powerpc/kernel/syscall_64.c
> +++ b/arch/powerpc/kernel/syscall_64.c
> @@ -189,7 +189,7 @@ notrace unsigned long syscall_exit_prepare(unsigned long r3,
>  
>  	/* This pattern matches prep_irq_for_idle */
>  	__hard_EE_RI_disable();
> -	if (unlikely(lazy_irq_pending())) {
> +	if (unlikely(lazy_irq_pending_nocheck())) {
>  		__hard_RI_enable();
>  		trace_hardirqs_off();
>  		local_paca->irq_happened |= PACA_IRQ_HARD_DIS;
> @@ -264,7 +264,7 @@ notrace unsigned long interrupt_exit_user_prepare(struct pt_regs *regs, unsigned
>  
>  	trace_hardirqs_on();
>  	__hard_EE_RI_disable();
> -	if (unlikely(lazy_irq_pending())) {
> +	if (unlikely(lazy_irq_pending_nocheck())) {
>  		__hard_RI_enable();
>  		trace_hardirqs_off();
>  		local_paca->irq_happened |= PACA_IRQ_HARD_DIS;
> @@ -334,7 +334,7 @@ notrace unsigned long interrupt_exit_kernel_prepare(struct pt_regs *regs, unsign
>  
>  		trace_hardirqs_on();
>  		__hard_EE_RI_disable();
> -		if (unlikely(lazy_irq_pending())) {
> +		if (unlikely(lazy_irq_pending_nocheck())) {
>  			__hard_RI_enable();
>  			irq_soft_mask_set(IRQS_ALL_DISABLED);
>  			trace_hardirqs_off();
> -- 
> 2.25.1
> 
> 


More information about the Linuxppc-dev mailing list