aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/hrtimer.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2013-07-05 06:09:18 -0400
committerThomas Gleixner <tglx@linutronix.de>2013-07-05 11:25:58 -0400
commit5ec2481b7b47a4005bb446d176e5d0257400c77d (patch)
treee7ae9e34dc6b1db5149669947b6cd93814f447ec /kernel/hrtimer.c
parent332962f2c88868ed3cdab466870baaa34dd58612 (diff)
hrtimers: Move SMP function call to thread context
smp_call_function_* must not be called from softirq context. But clock_was_set() which calls on_each_cpu() is called from softirq context to implement a delayed clock_was_set() for the timer interrupt handler. Though that almost never gets invoked. A recent change in the resume code uses the softirq based delayed clock_was_set to support Xens resume mechanism. linux-next contains a new warning which warns if smp_call_function_* is called from softirq context which gets triggered by that Xen change. Fix this by moving the delayed clock_was_set() call to a work context. Reported-and-tested-by: Artem Savkov <artem.savkov@gmail.com> Reported-by: Sasha Levin <sasha.levin@oracle.com> Cc: David Vrabel <david.vrabel@citrix.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: H. Peter Anvin <hpa@zytor.com>, Cc: Konrad Wilk <konrad.wilk@oracle.com> Cc: John Stultz <john.stultz@linaro.org> Cc: xen-devel@lists.xen.org Cc: stable@vger.kernel.org Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/hrtimer.c')
-rw-r--r--kernel/hrtimer.c37
1 files changed, 15 insertions, 22 deletions
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index e86827e94c9a..b9b9420a1297 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -721,17 +721,20 @@ static int hrtimer_switch_to_hres(void)
721 return 1; 721 return 1;
722} 722}
723 723
724static void clock_was_set_work(struct work_struct *work)
725{
726 clock_was_set();
727}
728
729static DECLARE_WORK(hrtimer_work, clock_was_set_work);
730
724/* 731/*
725 * Called from timekeeping code to reprogramm the hrtimer interrupt 732 * Called from timekeeping and resume code to reprogramm the hrtimer
726 * device. If called from the timer interrupt context we defer it to 733 * interrupt device on all cpus.
727 * softirq context.
728 */ 734 */
729void clock_was_set_delayed(void) 735void clock_was_set_delayed(void)
730{ 736{
731 struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); 737 schedule_work(&hrtimer_work);
732
733 cpu_base->clock_was_set = 1;
734 __raise_softirq_irqoff(HRTIMER_SOFTIRQ);
735} 738}
736 739
737#else 740#else
@@ -775,12 +778,7 @@ void clock_was_set(void)
775 * During resume we might have to reprogram the high resolution timer 778 * During resume we might have to reprogram the high resolution timer
776 * interrupt on all online CPUs. However, all other CPUs will be 779 * interrupt on all online CPUs. However, all other CPUs will be
777 * stopped with IRQs interrupts disabled so the clock_was_set() call 780 * stopped with IRQs interrupts disabled so the clock_was_set() call
778 * must be deferred to the softirq. 781 * must be deferred.
779 *
780 * The one-shot timer has already been programmed to fire immediately
781 * (see tick_resume_oneshot()) and this interrupt will trigger the
782 * softirq to run early enough to correctly reprogram the timers on
783 * all CPUs.
784 */ 782 */
785void hrtimers_resume(void) 783void hrtimers_resume(void)
786{ 784{
@@ -789,8 +787,10 @@ void hrtimers_resume(void)
789 WARN_ONCE(!irqs_disabled(), 787 WARN_ONCE(!irqs_disabled(),
790 KERN_INFO "hrtimers_resume() called with IRQs enabled!"); 788 KERN_INFO "hrtimers_resume() called with IRQs enabled!");
791 789
792 cpu_base->clock_was_set = 1; 790 /* Retrigger on the local CPU */
793 __raise_softirq_irqoff(HRTIMER_SOFTIRQ); 791 retrigger_next_event(NULL);
792 /* And schedule a retrigger for all others */
793 clock_was_set_delayed();
794} 794}
795 795
796static inline void timer_stats_hrtimer_set_start_info(struct hrtimer *timer) 796static inline void timer_stats_hrtimer_set_start_info(struct hrtimer *timer)
@@ -1441,13 +1441,6 @@ void hrtimer_peek_ahead_timers(void)
1441 1441
1442static void run_hrtimer_softirq(struct softirq_action *h) 1442static void run_hrtimer_softirq(struct softirq_action *h)
1443{ 1443{
1444 struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
1445
1446 if (cpu_base->clock_was_set) {
1447 cpu_base->clock_was_set = 0;
1448 clock_was_set();
1449 }
1450
1451 hrtimer_peek_ahead_timers(); 1444 hrtimer_peek_ahead_timers();
1452} 1445}
1453 1446