Commit 5ec2481b authored by Thomas Gleixner's avatar Thomas Gleixner

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: default avatarArtem Savkov <artem.savkov@gmail.com>
Reported-by: default avatarSasha 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: default avatarThomas Gleixner <tglx@linutronix.de>
parent 332962f2
...@@ -721,17 +721,20 @@ static int hrtimer_switch_to_hres(void) ...@@ -721,17 +721,20 @@ static int hrtimer_switch_to_hres(void)
return 1; return 1;
} }
static void clock_was_set_work(struct work_struct *work)
{
clock_was_set();
}
static DECLARE_WORK(hrtimer_work, clock_was_set_work);
/* /*
* Called from timekeeping code to reprogramm the hrtimer interrupt * Called from timekeeping and resume code to reprogramm the hrtimer
* device. If called from the timer interrupt context we defer it to * interrupt device on all cpus.
* softirq context.
*/ */
void clock_was_set_delayed(void) void clock_was_set_delayed(void)
{ {
struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); schedule_work(&hrtimer_work);
cpu_base->clock_was_set = 1;
__raise_softirq_irqoff(HRTIMER_SOFTIRQ);
} }
#else #else
...@@ -775,12 +778,7 @@ void clock_was_set(void) ...@@ -775,12 +778,7 @@ void clock_was_set(void)
* During resume we might have to reprogram the high resolution timer * During resume we might have to reprogram the high resolution timer
* interrupt on all online CPUs. However, all other CPUs will be * interrupt on all online CPUs. However, all other CPUs will be
* stopped with IRQs interrupts disabled so the clock_was_set() call * stopped with IRQs interrupts disabled so the clock_was_set() call
* must be deferred to the softirq. * must be deferred.
*
* The one-shot timer has already been programmed to fire immediately
* (see tick_resume_oneshot()) and this interrupt will trigger the
* softirq to run early enough to correctly reprogram the timers on
* all CPUs.
*/ */
void hrtimers_resume(void) void hrtimers_resume(void)
{ {
...@@ -789,8 +787,10 @@ void hrtimers_resume(void) ...@@ -789,8 +787,10 @@ void hrtimers_resume(void)
WARN_ONCE(!irqs_disabled(), WARN_ONCE(!irqs_disabled(),
KERN_INFO "hrtimers_resume() called with IRQs enabled!"); KERN_INFO "hrtimers_resume() called with IRQs enabled!");
cpu_base->clock_was_set = 1; /* Retrigger on the local CPU */
__raise_softirq_irqoff(HRTIMER_SOFTIRQ); retrigger_next_event(NULL);
/* And schedule a retrigger for all others */
clock_was_set_delayed();
} }
static inline void timer_stats_hrtimer_set_start_info(struct hrtimer *timer) static inline void timer_stats_hrtimer_set_start_info(struct hrtimer *timer)
...@@ -1441,13 +1441,6 @@ void hrtimer_peek_ahead_timers(void) ...@@ -1441,13 +1441,6 @@ void hrtimer_peek_ahead_timers(void)
static void run_hrtimer_softirq(struct softirq_action *h) static void run_hrtimer_softirq(struct softirq_action *h)
{ {
struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
if (cpu_base->clock_was_set) {
cpu_base->clock_was_set = 0;
clock_was_set();
}
hrtimer_peek_ahead_timers(); hrtimer_peek_ahead_timers();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment