| From d8c06a7d00bf2b393eefd74ec2e6874c602b32b8 Mon Sep 17 00:00:00 2001 |
| From: Nicholas Piggin <npiggin@gmail.com> |
| Date: Thu, 2 Apr 2020 22:04:01 +1000 |
| Subject: [PATCH] Revert "powerpc/64: irq_work avoid interrupt when called with |
| hardware irqs enabled" |
| |
| commit abc3fce76adbdfa8f87272c784b388cd20b46049 upstream. |
| |
| This reverts commit ebb37cf3ffd39fdb6ec5b07111f8bb2f11d92c5f. |
| |
| That commit does not play well with soft-masked irq state |
| manipulations in idle, interrupt replay, and possibly others due to |
| tracing code sometimes using irq_work_queue (e.g., in |
| trace_hardirqs_on()). That can cause PACA_IRQ_DEC to become set when |
| it is not expected, and be ignored or cleared or cause warnings. |
| |
| The net result seems to be missing an irq_work until the next timer |
| interrupt in the worst case which is usually not going to be noticed, |
| however it could be a long time if the tick is disabled, which is |
| against the spirit of irq_work and might cause real problems. |
| |
| The idea is still solid, but it would need more work. It's not really |
| clear if it would be worth added complexity, so revert this for |
| now (not a straight revert, but replace with a comment explaining why |
| we might see interrupts happening, and gives git blame something to |
| find). |
| |
| Fixes: ebb37cf3ffd3 ("powerpc/64: irq_work avoid interrupt when called with hardware irqs enabled") |
| Signed-off-by: Nicholas Piggin <npiggin@gmail.com> |
| Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> |
| Link: https://lore.kernel.org/r/20200402120401.1115883-1-npiggin@gmail.com |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c |
| index 11301a1187f3..0e0a2227af7d 100644 |
| --- a/arch/powerpc/kernel/time.c |
| +++ b/arch/powerpc/kernel/time.c |
| @@ -522,35 +522,6 @@ static inline void clear_irq_work_pending(void) |
| "i" (offsetof(struct paca_struct, irq_work_pending))); |
| } |
| |
| -void arch_irq_work_raise(void) |
| -{ |
| - preempt_disable(); |
| - set_irq_work_pending_flag(); |
| - /* |
| - * Non-nmi code running with interrupts disabled will replay |
| - * irq_happened before it re-enables interrupts, so setthe |
| - * decrementer there instead of causing a hardware exception |
| - * which would immediately hit the masked interrupt handler |
| - * and have the net effect of setting the decrementer in |
| - * irq_happened. |
| - * |
| - * NMI interrupts can not check this when they return, so the |
| - * decrementer hardware exception is raised, which will fire |
| - * when interrupts are next enabled. |
| - * |
| - * BookE does not support this yet, it must audit all NMI |
| - * interrupt handlers to ensure they call nmi_enter() so this |
| - * check would be correct. |
| - */ |
| - if (IS_ENABLED(CONFIG_BOOKE) || !irqs_disabled() || in_nmi()) { |
| - set_dec(1); |
| - } else { |
| - hard_irq_disable(); |
| - local_paca->irq_happened |= PACA_IRQ_DEC; |
| - } |
| - preempt_enable(); |
| -} |
| - |
| #else /* 32-bit */ |
| |
| DEFINE_PER_CPU(u8, irq_work_pending); |
| @@ -559,16 +530,27 @@ DEFINE_PER_CPU(u8, irq_work_pending); |
| #define test_irq_work_pending() __this_cpu_read(irq_work_pending) |
| #define clear_irq_work_pending() __this_cpu_write(irq_work_pending, 0) |
| |
| +#endif /* 32 vs 64 bit */ |
| + |
| void arch_irq_work_raise(void) |
| { |
| + /* |
| + * 64-bit code that uses irq soft-mask can just cause an immediate |
| + * interrupt here that gets soft masked, if this is called under |
| + * local_irq_disable(). It might be possible to prevent that happening |
| + * by noticing interrupts are disabled and setting decrementer pending |
| + * to be replayed when irqs are enabled. The problem there is that |
| + * tracing can call irq_work_raise, including in code that does low |
| + * level manipulations of irq soft-mask state (e.g., trace_hardirqs_on) |
| + * which could get tangled up if we're messing with the same state |
| + * here. |
| + */ |
| preempt_disable(); |
| set_irq_work_pending_flag(); |
| set_dec(1); |
| preempt_enable(); |
| } |
| |
| -#endif /* 32 vs 64 bit */ |
| - |
| #else /* CONFIG_IRQ_WORK */ |
| |
| #define test_irq_work_pending() 0 |
| -- |
| 2.7.4 |
| |