[PATCH v4 10/17] powerpc/64: interrupt soft-enable race fix
Christophe Leroy
christophe.leroy at csgroup.eu
Tue May 17 02:36:52 AEST 2022
Hi Nick.
Le 17/06/2021 à 17:51, Nicholas Piggin a écrit :
> Prevent interrupt restore from allowing racing hard interrupts going
> ahead of previous soft-pending ones, by using the soft-masked restart
> handler to allow a store to clear the soft-mask while knowing nothing
> is soft-pending.
>
> This probably doesn't matter much in practice, but it's a simple
> demonstrator / test case to exercise the restart table logic.
>
> Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
> ---
> arch/powerpc/kernel/irq.c | 95 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 95 insertions(+)
>
> diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
> index 72cb45393ef2..8428caf3194e 100644
> --- a/arch/powerpc/kernel/irq.c
> +++ b/arch/powerpc/kernel/irq.c
> @@ -217,6 +217,100 @@ static inline void replay_soft_interrupts_irqrestore(void)
> #define replay_soft_interrupts_irqrestore() replay_soft_interrupts()
> #endif
>
> +#ifdef CONFIG_CC_HAS_ASM_GOTO
Wondering why you added that #ifdef CONFIG_CC_HAS_ASM_GOTO ? In 2021 the
minimum GCC version was already supporting asm goto and we were already
using asm goto unconditionnaly in uaccess.
I just sent a patch to remove it.
> +notrace void arch_local_irq_restore(unsigned long mask)
> +{
> + unsigned char irq_happened;
> +
> + /* Write the new soft-enabled value if it is a disable */
> + if (mask) {
> + irq_soft_mask_set(mask);
> + return;
> + }
> +
> + /*
> + * After the stb, interrupts are unmasked and there are no interrupts
> + * pending replay. The restart sequence makes this atomic with
> + * respect to soft-masked interrupts. If this was just a simple code
> + * sequence, a soft-masked interrupt could become pending right after
> + * the comparison and before the stb.
> + *
> + * This allows interrupts to be unmasked without hard disabling, and
> + * also without new hard interrupts coming in ahead of pending ones.
> + */
> + asm_volatile_goto(
> +"1: \n"
> +" lbz 9,%0(13) \n"
> +" cmpwi 9,0 \n"
> +" bne %l[happened] \n"
> +" stb 9,%1(13) \n"
> +"2: \n"
> + RESTART_TABLE(1b, 2b, 1b)
> + : : "i" (offsetof(struct paca_struct, irq_happened)),
> + "i" (offsetof(struct paca_struct, irq_soft_mask))
> + : "cr0", "r9"
> + : happened);
> +
> + if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG))
> + WARN_ON_ONCE(!(mfmsr() & MSR_EE));
> +
> + return;
> +
> +happened:
> + irq_happened = get_irq_happened();
> + if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG))
> + WARN_ON_ONCE(!irq_happened);
> +
> + if (irq_happened == PACA_IRQ_HARD_DIS) {
> + if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG))
> + WARN_ON_ONCE(mfmsr() & MSR_EE);
> + irq_soft_mask_set(IRQS_ENABLED);
> + local_paca->irq_happened = 0;
> + __hard_irq_enable();
> + return;
> + }
> +
> + /* Have interrupts to replay, need to hard disable first */
> + if (!(irq_happened & PACA_IRQ_HARD_DIS)) {
> + if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) {
> + if (!(mfmsr() & MSR_EE)) {
> + /*
> + * An interrupt could have come in and cleared
> + * MSR[EE] and set IRQ_HARD_DIS, so check
> + * IRQ_HARD_DIS again and warn if it is still
> + * clear.
> + */
> + irq_happened = get_irq_happened();
> + WARN_ON_ONCE(!(irq_happened & PACA_IRQ_HARD_DIS));
> + }
> + }
> + __hard_irq_disable();
> + local_paca->irq_happened |= PACA_IRQ_HARD_DIS;
> + } else {
> + if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) {
> + if (WARN_ON_ONCE(mfmsr() & MSR_EE))
> + __hard_irq_disable();
> + }
> + }
> +
> + /*
> + * Disable preempt here, so that the below preempt_enable will
> + * perform resched if required (a replayed interrupt may set
> + * need_resched).
> + */
> + preempt_disable();
> + irq_soft_mask_set(IRQS_ALL_DISABLED);
> + trace_hardirqs_off();
> +
> + replay_soft_interrupts_irqrestore();
> + local_paca->irq_happened = 0;
> +
> + trace_hardirqs_on();
> + irq_soft_mask_set(IRQS_ENABLED);
> + __hard_irq_enable();
> + preempt_enable();
> +}
> +#else
> notrace void arch_local_irq_restore(unsigned long mask)
> {
> unsigned char irq_happened;
> @@ -288,6 +382,7 @@ notrace void arch_local_irq_restore(unsigned long mask)
> __hard_irq_enable();
> preempt_enable();
> }
> +#endif
> EXPORT_SYMBOL(arch_local_irq_restore);
>
> /*
More information about the Linuxppc-dev
mailing list