diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-01-21 09:52:28 +1100 |
---|---|---|
committer | Eli Qiao <taget@linux.vnet.ibm.com> | 2014-01-22 10:26:23 +0800 |
commit | b556711a354676da0cf487271dd9f50b55029cb1 (patch) | |
tree | e3491e65ef5c38604c312981286fcd9e152d9a0a | |
parent | 84e2df8396b413091a736bab5b0af9cd35f392ce (diff) | |
download | powerkvm-b556711a354676da0cf487271dd9f50b55029cb1.tar.gz |
powerpc: Fix races with irq_work
If we set irq_work on a processor and immediately afterward, before the
irq work has a chance to be processed, we change the decrementer value,
we can seriously delay the handling of that irq_work.
Fix it by checking in a few places for pending irq work, first before
changing the decrementer in decrementer_set_next_event() and after
changing it in the same function and in timer_interrupt().
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r-- | arch/powerpc/kernel/time.c | 12 |
1 files changed, 12 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 931c8f35c7450..af6bff72dee97 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -520,6 +520,9 @@ static void __timer_interrupt(void) now = *next_tb - now; if (now <= DECREMENTER_MAX) set_dec((int)now); + /* We may have raced with new irq work */ + if (test_irq_work_pending()) + set_dec(1); } #ifdef CONFIG_PPC64 @@ -571,6 +574,7 @@ void timer_interrupt(struct pt_regs * regs) irq_enter(); __timer_interrupt(); + irq_exit(); set_irq_regs(old_regs); } @@ -825,8 +829,16 @@ static void __init clocksource_init(void) static int decrementer_set_next_event(unsigned long evt, struct clock_event_device *dev) { + /* Don't adjust the decrementer if some irq work is pending */ + if (test_irq_work_pending()) + return 0; __get_cpu_var(decrementers_next_tb) = get_tb_or_rtc() + evt; set_dec(evt); + + /* We may have raced with new irq work */ + if (test_irq_work_pending()) + set_dec(1); + return 0; } |