[S390] nohz/sclp: disable timer on synchronous waits.
authorHeiko Carstens <heiko.carstens@de.ibm.com>
Fri, 1 Aug 2008 14:39:11 +0000 (16:39 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 1 Aug 2008 14:39:30 +0000 (16:39 +0200)
sclp_sync_wait wait synchronously for an sclp interrupt and disables
timer interrupts. However on the irq enter paths there is an extra
check if a timer interrupt would be due and calls the timer callback.
This would schedule softirqs in the wrong context.
So introduce local_tick_enable/disable which prevents this.

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/lib/delay.c
drivers/s390/char/sclp.c
include/asm-s390/hardirq.h

index eae21a8ac72de5f86cc3ae2a1138960381975c76..fc6ab6094df811c4511b37ceb7d9783170d98e5a 100644 (file)
@@ -43,7 +43,7 @@ void __udelay(unsigned long usecs)
                local_bh_disable();
        local_irq_save(flags);
        if (raw_irqs_disabled_flags(flags)) {
-               old_cc = S390_lowcore.clock_comparator;
+               old_cc = local_tick_disable();
                S390_lowcore.clock_comparator = -1ULL;
                __ctl_store(cr0, 0, 0);
                dummy = (cr0 & 0xffff00e0) | 0x00000800;
@@ -65,7 +65,7 @@ void __udelay(unsigned long usecs)
 
        if (raw_irqs_disabled_flags(flags)) {
                __ctl_load(cr0, 0, 0);
-               S390_lowcore.clock_comparator = old_cc;
+               local_tick_enable(old_cc);
        }
        if (!irq_context)
                _local_bh_enable();
index 3c8b25e6c3457b528bb8a90c8e2074ff66faf1b8..1fd8f2193ed853b143cdd25d1792224c91f4f1b9 100644 (file)
@@ -399,6 +399,7 @@ sclp_tod_from_jiffies(unsigned long jiffies)
 void
 sclp_sync_wait(void)
 {
+       unsigned long long old_tick;
        unsigned long flags;
        unsigned long cr0, cr0_sync;
        u64 timeout;
@@ -419,11 +420,12 @@ sclp_sync_wait(void)
        if (!irq_context)
                local_bh_disable();
        /* Enable service-signal interruption, disable timer interrupts */
+       old_tick = local_tick_disable();
        trace_hardirqs_on();
        __ctl_store(cr0, 0, 0);
        cr0_sync = cr0;
+       cr0_sync &= 0xffff00a0;
        cr0_sync |= 0x00000200;
-       cr0_sync &= 0xFFFFF3AC;
        __ctl_load(cr0_sync, 0, 0);
        __raw_local_irq_stosm(0x01);
        /* Loop until driver state indicates finished request */
@@ -439,9 +441,9 @@ sclp_sync_wait(void)
        __ctl_load(cr0, 0, 0);
        if (!irq_context)
                _local_bh_enable();
+       local_tick_enable(old_tick);
        local_irq_restore(flags);
 }
-
 EXPORT_SYMBOL(sclp_sync_wait);
 
 /* Dispatch changes in send and receive mask to registered listeners. */
index 4b7cb964ff353f35dd5e7bf87c6803ec57363d02..89ec7056da28f282a2632004ad9216b722aae048 100644 (file)
@@ -34,4 +34,18 @@ typedef struct {
 
 void clock_comparator_work(void);
 
+static inline unsigned long long local_tick_disable(void)
+{
+       unsigned long long old;
+
+       old = S390_lowcore.clock_comparator;
+       S390_lowcore.clock_comparator = -1ULL;
+       return old;
+}
+
+static inline void local_tick_enable(unsigned long long comp)
+{
+       S390_lowcore.clock_comparator = comp;
+}
+
 #endif /* __ASM_HARDIRQ_H */