Some platforms execute their timer handler with the interrupt priority
level set below 6. That means the handler could be interrupted by another
driver and this could lead to re-entry of the timer core.

Avoid this by use of local_irq_save/restore for timer interrupt dispatch.
This provides mutual exclusion around the timer interrupt flag access
which is needed later in this series for the clocksource conversion.

Reported-by: Thomas Gleixner <t...@linutronix.de>
Link: 
http://lkml.kernel.org/r/alpine.deb.2.21.1811131407120.2...@nanos.tec.linutronix.de
Signed-off-by: Finn Thain <fth...@telegraphics.com.au>
---
I've only checked 680x0 MMU platforms for this issue.

Changed since v2:
 - Don't waste an iteration of the for loop in cia_handler().
 - Remove q40_timer_routine for better consistency with other platforms.
 - Minor cleanup.
---
 arch/m68k/amiga/cia.c       |  9 +++++++++
 arch/m68k/atari/ataints.c   |  4 ++--
 arch/m68k/atari/time.c      | 15 ++++++++++++++-
 arch/m68k/bvme6000/config.c | 20 ++++++++++----------
 arch/m68k/hp300/time.c      | 10 ++++++++--
 arch/m68k/mac/via.c         | 17 +++++++++++++++++
 arch/m68k/mvme147/config.c  | 18 ++++++++++--------
 arch/m68k/mvme16x/config.c  | 21 +++++++++++----------
 arch/m68k/q40/q40ints.c     | 19 +++++++++++--------
 arch/m68k/sun3/sun3ints.c   |  3 +++
 arch/m68k/sun3x/time.c      | 16 ++++++++++------
 11 files changed, 105 insertions(+), 47 deletions(-)

diff --git a/arch/m68k/amiga/cia.c b/arch/m68k/amiga/cia.c
index 2081b8cd5591..b9aee983e6f4 100644
--- a/arch/m68k/amiga/cia.c
+++ b/arch/m68k/amiga/cia.c
@@ -88,10 +88,19 @@ static irqreturn_t cia_handler(int irq, void *dev_id)
        struct ciabase *base = dev_id;
        int mach_irq;
        unsigned char ints;
+       unsigned long flags;
 
+       /* Interrupts get disabled while the timer irq flag is cleared and
+        * the timer interrupt serviced.
+        */
        mach_irq = base->cia_irq;
+       local_irq_save(flags);
        ints = cia_set_irq(base, CIA_ICR_ALL);
        amiga_custom.intreq = base->int_mask;
+       if (ints & 1)
+               generic_handle_irq(mach_irq);
+       local_irq_restore(flags);
+       mach_irq++, ints >>= 1;
        for (; ints; mach_irq++, ints >>= 1) {
                if (ints & 1)
                        generic_handle_irq(mach_irq);
diff --git a/arch/m68k/atari/ataints.c b/arch/m68k/atari/ataints.c
index 3d2b63bedf05..56f02ea2c248 100644
--- a/arch/m68k/atari/ataints.c
+++ b/arch/m68k/atari/ataints.c
@@ -142,7 +142,7 @@ struct mfptimerbase {
        .name           = "MFP Timer D"
 };
 
-static irqreturn_t mfptimer_handler(int irq, void *dev_id)
+static irqreturn_t mfp_timer_d_handler(int irq, void *dev_id)
 {
        struct mfptimerbase *base = dev_id;
        int mach_irq;
@@ -344,7 +344,7 @@ void __init atari_init_IRQ(void)
        st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 0xf0) | 0x6;
 
        /* request timer D dispatch handler */
-       if (request_irq(IRQ_MFP_TIMD, mfptimer_handler, IRQF_SHARED,
+       if (request_irq(IRQ_MFP_TIMD, mfp_timer_d_handler, IRQF_SHARED,
                        stmfp_base.name, &stmfp_base))
                pr_err("Couldn't register %s interrupt\n", stmfp_base.name);
 
diff --git a/arch/m68k/atari/time.c b/arch/m68k/atari/time.c
index 9cca64286464..fafa20f75ab9 100644
--- a/arch/m68k/atari/time.c
+++ b/arch/m68k/atari/time.c
@@ -24,6 +24,18 @@
 DEFINE_SPINLOCK(rtc_lock);
 EXPORT_SYMBOL_GPL(rtc_lock);
 
+static irqreturn_t mfp_timer_c_handler(int irq, void *dev_id)
+{
+       irq_handler_t timer_routine = dev_id;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       timer_routine(0, NULL);
+       local_irq_restore(flags);
+
+       return IRQ_HANDLED;
+}
+
 void __init
 atari_sched_init(irq_handler_t timer_routine)
 {
@@ -32,7 +44,8 @@ atari_sched_init(irq_handler_t timer_routine)
     /* start timer C, div = 1:100 */
     st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60;
     /* install interrupt service routine for MFP Timer C */
-    if (request_irq(IRQ_MFP_TIMC, timer_routine, 0, "timer", timer_routine))
+    if (request_irq(IRQ_MFP_TIMC, mfp_timer_c_handler, 0, "timer",
+                    timer_routine))
        pr_err("Couldn't register timer interrupt\n");
 }
 
diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c
index 143ee9fa3893..0e5efed4da86 100644
--- a/arch/m68k/bvme6000/config.c
+++ b/arch/m68k/bvme6000/config.c
@@ -44,11 +44,6 @@ extern int bvme6000_hwclk (int, struct rtc_time *);
 extern void bvme6000_reset (void);
 void bvme6000_set_vectors (void);
 
-/* Save tick handler routine pointer, will point to xtime_update() in
- * kernel/timer/timekeeping.c, called via bvme6000_process_int() */
-
-static irq_handler_t tick_handler;
-
 
 int __init bvme6000_parse_bootinfo(const struct bi_record *bi)
 {
@@ -157,12 +152,18 @@ irqreturn_t bvme6000_abort_int (int irq, void *dev_id)
 
 static irqreturn_t bvme6000_timer_int (int irq, void *dev_id)
 {
+    irq_handler_t timer_routine = dev_id;
+    unsigned long flags;
     volatile RtcPtr_t rtc = (RtcPtr_t)BVME_RTC_BASE;
-    unsigned char msr = rtc->msr & 0xc0;
+    unsigned char msr;
 
+    local_irq_save(flags);
+    msr = rtc->msr & 0xc0;
     rtc->msr = msr | 0x20;             /* Ack the interrupt */
+    timer_routine(0, NULL);
+    local_irq_restore(flags);
 
-    return tick_handler(irq, dev_id);
+    return IRQ_HANDLED;
 }
 
 /*
@@ -181,9 +182,8 @@ void bvme6000_sched_init (irq_handler_t timer_routine)
 
     rtc->msr = 0;      /* Ensure timer registers accessible */
 
-    tick_handler = timer_routine;
-    if (request_irq(BVME_IRQ_RTC, bvme6000_timer_int, 0,
-                               "timer", bvme6000_timer_int))
+    if (request_irq(BVME_IRQ_RTC, bvme6000_timer_int, 0, "timer",
+                    timer_routine))
        panic ("Couldn't register timer int");
 
     rtc->t1cr_omr = 0x04;      /* Mode 2, ext clk */
diff --git a/arch/m68k/hp300/time.c b/arch/m68k/hp300/time.c
index 289d928a46cb..d30b03ea93a2 100644
--- a/arch/m68k/hp300/time.c
+++ b/arch/m68k/hp300/time.c
@@ -38,13 +38,19 @@
 
 static irqreturn_t hp300_tick(int irq, void *dev_id)
 {
+       irq_handler_t timer_routine = dev_id;
+       unsigned long flags;
        unsigned long tmp;
-       irq_handler_t vector = dev_id;
+
+       local_irq_save(flags);
        in_8(CLOCKBASE + CLKSR);
        asm volatile ("movpw %1@(5),%0" : "=d" (tmp) : "a" (CLOCKBASE));
+       timer_routine(0, NULL);
+       local_irq_restore(flags);
+
        /* Turn off the network and SCSI leds */
        blinken_leds(0, 0xe0);
-       return vector(irq, NULL);
+       return IRQ_HANDLED;
 }
 
 u32 hp300_gettimeoffset(void)
diff --git a/arch/m68k/mac/via.c b/arch/m68k/mac/via.c
index e4facff0c1f3..f6df83ba2227 100644
--- a/arch/m68k/mac/via.c
+++ b/arch/m68k/mac/via.c
@@ -436,6 +436,8 @@ void via_nubus_irq_shutdown(int irq)
  * via6522.c :-), disable/pending masks added.
  */
 
+#define VIA_TIMER_1_INT BIT(6)
+
 void via1_irq(struct irq_desc *desc)
 {
        int irq_num;
@@ -445,6 +447,21 @@ void via1_irq(struct irq_desc *desc)
        if (!events)
                return;
 
+       irq_num = IRQ_MAC_TIMER_1;
+       irq_bit = VIA_TIMER_1_INT;
+       if (events & irq_bit) {
+               unsigned long flags;
+
+               local_irq_save(flags);
+               via1[vIFR] = irq_bit;
+               generic_handle_irq(irq_num);
+               local_irq_restore(flags);
+
+               events &= ~irq_bit;
+               if (!events)
+                       return;
+       }
+
        irq_num = VIA1_SOURCE_BASE;
        irq_bit = 1;
        do {
diff --git a/arch/m68k/mvme147/config.c b/arch/m68k/mvme147/config.c
index adea549d240e..93c68d2b8e0e 100644
--- a/arch/m68k/mvme147/config.c
+++ b/arch/m68k/mvme147/config.c
@@ -45,11 +45,6 @@ extern void mvme147_reset (void);
 
 static int bcd2int (unsigned char b);
 
-/* Save tick handler routine pointer, will point to xtime_update() in
- * kernel/time/timekeeping.c, called via mvme147_process_int() */
-
-irq_handler_t tick_handler;
-
 
 int __init mvme147_parse_bootinfo(const struct bi_record *bi)
 {
@@ -104,16 +99,23 @@ void __init config_mvme147(void)
 
 static irqreturn_t mvme147_timer_int (int irq, void *dev_id)
 {
+       irq_handler_t timer_routine = dev_id;
+       unsigned long flags;
+
+       local_irq_save(flags);
        m147_pcc->t1_int_cntrl = PCC_TIMER_INT_CLR;
        m147_pcc->t1_int_cntrl = PCC_INT_ENAB|PCC_LEVEL_TIMER1;
-       return tick_handler(irq, dev_id);
+       timer_routine(0, NULL);
+       local_irq_restore(flags);
+
+       return IRQ_HANDLED;
 }
 
 
 void mvme147_sched_init (irq_handler_t timer_routine)
 {
-       tick_handler = timer_routine;
-       if (request_irq(PCC_IRQ_TIMER1, mvme147_timer_int, 0, "timer 1", NULL))
+       if (request_irq(PCC_IRQ_TIMER1, mvme147_timer_int, 0, "timer 1",
+                       timer_routine))
                pr_err("Couldn't register timer interrupt\n");
 
        /* Init the clock with a value */
diff --git a/arch/m68k/mvme16x/config.c b/arch/m68k/mvme16x/config.c
index 6ee36a5b528d..5feb3ab484d0 100644
--- a/arch/m68k/mvme16x/config.c
+++ b/arch/m68k/mvme16x/config.c
@@ -50,11 +50,6 @@ extern void mvme16x_reset (void);
 
 int bcd2int (unsigned char b);
 
-/* Save tick handler routine pointer, will point to xtime_update() in
- * kernel/time/timekeeping.c, called via mvme16x_process_int() */
-
-static irq_handler_t tick_handler;
-
 
 unsigned short mvme16x_config;
 EXPORT_SYMBOL(mvme16x_config);
@@ -352,8 +347,15 @@ static irqreturn_t mvme16x_abort_int (int irq, void 
*dev_id)
 
 static irqreturn_t mvme16x_timer_int (int irq, void *dev_id)
 {
-    *(volatile unsigned char *)0xfff4201b |= 8;
-    return tick_handler(irq, dev_id);
+       irq_handler_t timer_routine = dev_id;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       *(volatile unsigned char *)0xfff4201b |= 8;
+       timer_routine(0, NULL);
+       local_irq_restore(flags);
+
+       return IRQ_HANDLED;
 }
 
 void mvme16x_sched_init (irq_handler_t timer_routine)
@@ -361,14 +363,13 @@ void mvme16x_sched_init (irq_handler_t timer_routine)
     uint16_t brdno = be16_to_cpu(mvme_bdid.brdno);
     int irq;
 
-    tick_handler = timer_routine;
     /* Using PCCchip2 or MC2 chip tick timer 1 */
     *(volatile unsigned long *)0xfff42008 = 0;
     *(volatile unsigned long *)0xfff42004 = 10000;     /* 10ms */
     *(volatile unsigned char *)0xfff42017 |= 3;
     *(volatile unsigned char *)0xfff4201b = 0x16;
-    if (request_irq(MVME16x_IRQ_TIMER, mvme16x_timer_int, 0,
-                               "timer", mvme16x_timer_int))
+    if (request_irq(MVME16x_IRQ_TIMER, mvme16x_timer_int, 0, "timer",
+                    timer_routine))
        panic ("Couldn't register timer int");
 
     if (brdno == 0x0162 || brdno == 0x172)
diff --git a/arch/m68k/q40/q40ints.c b/arch/m68k/q40/q40ints.c
index 3e7603202977..1c696906c159 100644
--- a/arch/m68k/q40/q40ints.c
+++ b/arch/m68k/q40/q40ints.c
@@ -127,10 +127,10 @@ void q40_mksound(unsigned int hz, unsigned int ticks)
        sound_ticks = ticks << 1;
 }
 
-static irq_handler_t q40_timer_routine;
-
-static irqreturn_t q40_timer_int (int irq, void * dev)
+static irqreturn_t q40_timer_int(int irq, void *dev_id)
 {
+       irq_handler_t timer_routine = dev_id;
+
        ql_ticks = ql_ticks ? 0 : 1;
        if (sound_ticks) {
                unsigned char sval=(sound_ticks & 1) ? 128-SVOL : 128+SVOL;
@@ -139,8 +139,13 @@ static irqreturn_t q40_timer_int (int irq, void * dev)
                *DAC_RIGHT=sval;
        }
 
-       if (!ql_ticks)
-               q40_timer_routine(irq, dev);
+       if (!ql_ticks) {
+               unsigned long flags;
+
+               local_irq_save(flags);
+               timer_routine(0, NULL);
+               local_irq_restore(flags);
+       }
        return IRQ_HANDLED;
 }
 
@@ -148,11 +153,9 @@ void q40_sched_init (irq_handler_t timer_routine)
 {
        int timer_irq;
 
-       q40_timer_routine = timer_routine;
        timer_irq = Q40_IRQ_FRAME;
 
-       if (request_irq(timer_irq, q40_timer_int, 0,
-                               "timer", q40_timer_int))
+       if (request_irq(timer_irq, q40_timer_int, 0, "timer", timer_routine))
                panic("Couldn't register timer int");
 
        master_outb(-1, FRAME_CLEAR_REG);
diff --git a/arch/m68k/sun3/sun3ints.c b/arch/m68k/sun3/sun3ints.c
index 6bbca30c9188..a5824abb4a39 100644
--- a/arch/m68k/sun3/sun3ints.c
+++ b/arch/m68k/sun3/sun3ints.c
@@ -61,8 +61,10 @@ static irqreturn_t sun3_int7(int irq, void *dev_id)
 
 static irqreturn_t sun3_int5(int irq, void *dev_id)
 {
+       unsigned long flags;
        unsigned int cnt;
 
+       local_irq_save(flags);
 #ifdef CONFIG_SUN3
        intersil_clear();
 #endif
@@ -76,6 +78,7 @@ static irqreturn_t sun3_int5(int irq, void *dev_id)
        cnt = kstat_irqs_cpu(irq, 0);
        if (!(cnt % 20))
                sun3_leds(led_pattern[cnt % 160 / 20]);
+       local_irq_restore(flags);
        return IRQ_HANDLED;
 }
 
diff --git a/arch/m68k/sun3x/time.c b/arch/m68k/sun3x/time.c
index 047e2bcee3d7..3c8a86d08508 100644
--- a/arch/m68k/sun3x/time.c
+++ b/arch/m68k/sun3x/time.c
@@ -80,15 +80,19 @@ u32 sun3x_gettimeoffset(void)
 }
 
 #if 0
-static void sun3x_timer_tick(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t sun3x_timer_tick(int irq, void *dev_id)
 {
-    void (*vector)(int, void *, struct pt_regs *) = dev_id;
+       irq_handler_t timer_routine = dev_id;
+       unsigned long flags;
 
-    /* Clear the pending interrupt - pulse the enable line low */
-    disable_irq(5);
-    enable_irq(5);
+       local_irq_save(flags);
+       /* Clear the pending interrupt - pulse the enable line low */
+       disable_irq(5);
+       enable_irq(5);
+       timer_routine(0, NULL);
+       local_irq_restore(flags);
 
-    vector(irq, NULL, regs);
+       return IRQ_HANDLED;
 }
 #endif
 
-- 
2.18.1

Reply via email to