diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2013-07-05 06:09:18 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2013-07-05 11:25:58 -0400 |
commit | 5ec2481b7b47a4005bb446d176e5d0257400c77d (patch) | |
tree | e7ae9e34dc6b1db5149669947b6cd93814f447ec /kernel/hrtimer.c | |
parent | 332962f2c88868ed3cdab466870baaa34dd58612 (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.c | 37 |
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 | ||
724 | static void clock_was_set_work(struct work_struct *work) | ||
725 | { | ||
726 | clock_was_set(); | ||
727 | } | ||
728 | |||
729 | static 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 | */ |
729 | void clock_was_set_delayed(void) | 735 | void 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 | */ |
785 | void hrtimers_resume(void) | 783 | void 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 | ||
796 | static inline void timer_stats_hrtimer_set_start_info(struct hrtimer *timer) | 796 | static inline void timer_stats_hrtimer_set_start_info(struct hrtimer *timer) |
@@ -1441,13 +1441,6 @@ void hrtimer_peek_ahead_timers(void) | |||
1441 | 1441 | ||
1442 | static void run_hrtimer_softirq(struct softirq_action *h) | 1442 | static 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 | ||