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>
This commit is contained in:
parent
332962f2c8
commit
5ec2481b7b
|
@ -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)
|
||||||
* 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)
|
||||||
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)
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue