[PATCH 4/5] ppc64: make soft_enabled irqs preempt safe
From: Hugh Dickins <hidden>
Date: 2006-10-31 19:03:53
Rewrite local_get_flags and local_irq_disable to use r13 explicitly, to avoid the risk that gcc will split get_paca()->soft_enabled into a sequence unsafe against preemption. local_irq_restore be careful to access hard_enabled and lppaca before setting soft_enabled, which may well permit preemption. Use local_paca instead of get_paca() when setting hard_enabled: looking ahead to the DEBUG_PREEMPT patch, which uses local_paca as the raw unchecked version. Signed-off-by: Hugh Dickins <redacted> --- As before, my powerpc and inline asm may be nonsense: please do fix it. This patch is to powerpc git code, not intended for 2.6.19. arch/powerpc/kernel/irq.c | 20 +++++++++++++++----- include/asm-powerpc/hw_irq.h | 20 +++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-)
--- git-powerpc/arch/powerpc/kernel/irq.c 2006-10-30 19:30:57.000000000 +0000
+++ linux/arch/powerpc/kernel/irq.c 2006-10-30 19:32:38.000000000 +0000@@ -99,20 +99,30 @@ int distribute_irqs = 1; void local_irq_restore(unsigned long en) { - get_paca()->soft_enabled = en; - if (!en) + unsigned long hard_enabled; + + if (!en) { + get_paca()->soft_enabled = en; return; + } if (firmware_has_feature(FW_FEATURE_ISERIES)) { - if (get_paca()->lppaca_ptr->int_dword.any_int) + unsigned long any_int; + + any_int = get_lppaca()->int_dword.any_int; + get_paca()->soft_enabled = en; + if (any_int) iseries_handle_interrupts(); return; } - if (get_paca()->hard_enabled) + hard_enabled = get_paca()->hard_enabled; + get_paca()->soft_enabled = en; + if (hard_enabled) return; + /* need to hard-enable interrupts here */ - get_paca()->hard_enabled = en; + local_paca->hard_enabled = en; if ((int)mfspr(SPRN_DEC) < 0) mtspr(SPRN_DEC, 1); hard_irq_enable(); --- git-powerpc/include/asm-powerpc/hw_irq.h 2006-10-30 19:30:57.000000000 +0000 +++ linux/include/asm-powerpc/hw_irq.h 2006-10-30 19:32:38.000000000 +0000
@@ -18,15 +18,25 @@ extern void timer_interrupt(struct pt_re static inline unsigned long local_get_flags(void) { - return get_paca()->soft_enabled; + unsigned long flags; + + __asm__ __volatile__("lbz %0,%1(13)" + : "=r" (flags) + : "i" (offsetof(struct paca_struct, soft_enabled))); + + return flags; } static inline unsigned long local_irq_disable(void) { - unsigned long flag = get_paca()->soft_enabled; - get_paca()->soft_enabled = 0; - barrier(); - return flag; + unsigned long flags, zero; + + __asm__ __volatile__("li %1,0; lbz %0,%2(13); stb %1,%2(13)" + : "=r" (flags), "=&r" (zero) + : "i" (offsetof(struct paca_struct, soft_enabled)) + : "memory"); + + return flags; } extern void local_irq_restore(unsigned long);