Decrementer updates must always check for new irq work to avoid an
irq work decrementer interrupt being lost.

Add an API for this in the timer code so callers don't have to care
about details.

Reviewed-by: Alexey Kardashevskiy <a...@ozlabs.ru>
Signed-off-by: Nicholas Piggin <npig...@gmail.com>
---
 arch/powerpc/include/asm/time.h |  9 +++++++++
 arch/powerpc/kernel/time.c      | 20 +++++++++++---------
 2 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/arch/powerpc/include/asm/time.h b/arch/powerpc/include/asm/time.h
index 0128cd9769bc..d62bde57bf02 100644
--- a/arch/powerpc/include/asm/time.h
+++ b/arch/powerpc/include/asm/time.h
@@ -78,6 +78,15 @@ static inline void set_dec(u64 val)
                mtspr(SPRN_DEC, val - 1);
 }
 
+#ifdef CONFIG_IRQ_WORK
+void set_dec_or_work(u64 val);
+#else
+static inline void set_dec_or_work(u64 val)
+{
+       set_dec(val);
+}
+#endif
+
 static inline unsigned long tb_ticks_since(unsigned long tstamp)
 {
        return mftb() - tstamp;
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index c5d524622c17..341cc8442e5e 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -562,6 +562,15 @@ void arch_irq_work_raise(void)
        preempt_enable();
 }
 
+void set_dec_or_work(u64 val)
+{
+       set_dec(val);
+       /* We may have raced with new irq work */
+       if (unlikely(test_irq_work_pending()))
+               set_dec(1);
+}
+EXPORT_SYMBOL_GPL(set_dec_or_work);
+
 #else  /* CONFIG_IRQ_WORK */
 
 #define test_irq_work_pending()        0
@@ -629,10 +638,7 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(timer_interrupt)
        } else {
                now = *next_tb - now;
                if (now <= decrementer_max)
-                       set_dec(now);
-               /* We may have raced with new irq work */
-               if (test_irq_work_pending())
-                       set_dec(1);
+                       set_dec_or_work(now);
                __this_cpu_inc(irq_stat.timer_irqs_others);
        }
 
@@ -874,11 +880,7 @@ static int decrementer_set_next_event(unsigned long evt,
                                      struct clock_event_device *dev)
 {
        __this_cpu_write(decrementers_next_tb, get_tb() + evt);
-       set_dec(evt);
-
-       /* We may have raced with new irq work */
-       if (test_irq_work_pending())
-               set_dec(1);
+       set_dec_or_work(evt);
 
        return 0;
 }
-- 
2.23.0

Reply via email to