OSDN Git Service

m68k: Call timer_interrupt() with interrupts disabled
authorFinn Thain <fthain@telegraphics.com.au>
Sat, 1 Dec 2018 00:53:10 +0000 (11:53 +1100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Jan 2020 09:21:50 +0000 (10:21 +0100)
[ Upstream commit 1efdd4bd254311498123a15fa0acd565f454da97 ]

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 <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/alpine.DEB.2.21.1811131407120.2697@nanos.tec.linutronix.de
Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
arch/m68k/amiga/cia.c
arch/m68k/atari/ataints.c
arch/m68k/atari/time.c
arch/m68k/bvme6000/config.c
arch/m68k/hp300/time.c
arch/m68k/mac/via.c
arch/m68k/mvme147/config.c
arch/m68k/mvme16x/config.c
arch/m68k/q40/q40ints.c
arch/m68k/sun3/sun3ints.c
arch/m68k/sun3x/time.c

index 2081b8c..b9aee98 100644 (file)
@@ -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);
index 3d2b63b..56f02ea 100644 (file)
@@ -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);
 
index c549b48..972181c 100644 (file)
 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");
 }
 
index 478623d..62054c0 100644 (file)
@@ -46,11 +46,6 @@ extern int bvme6000_set_clock_mmss (unsigned long);
 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)
 {
@@ -160,12 +155,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;
 }
 
 /*
@@ -184,9 +185,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 */
index 749543b..03c83b8 100644 (file)
 
 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)
index 2d68751..49f9fa4 100644 (file)
@@ -397,6 +397,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;
@@ -406,6 +408,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 {
index e6a3b56..152fbde 100644 (file)
@@ -47,11 +47,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)
 {
@@ -107,16 +102,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 */
index a53803c..0d43bfb 100644 (file)
@@ -52,11 +52,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);
@@ -355,8 +350,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)
@@ -364,14 +366,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)
index 513f9bb..60b51f5 100644 (file)
@@ -126,10 +126,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;
@@ -138,8 +138,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;
 }
 
@@ -147,11 +152,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);
index 6bbca30..a5824ab 100644 (file)
@@ -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;
 }
 
index c8eb08a..7a19531 100644 (file)
@@ -77,15 +77,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