mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-06 05:06:29 +00:00
hrtimer: Force clock_was_set() handling for the HIGHRES=n, NOHZ=y case
When CONFIG_HIGH_RES_TIMERS is disabled, but NOHZ is enabled then clock_was_set() is not doing anything. With HIGHRES=n the kernel relies on the periodic tick to update the clock offsets, but when NOHZ is enabled and active then CPUs which are in a deep idle sleep do not have a periodic tick which means the expiry of timers affected by clock_was_set() can be arbitrarily delayed up to the point where the CPUs are brought out of idle again. Make the clock_was_set() logic unconditionaly available so that idle CPUs are kicked out of idle to handle the update. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Link: https://lore.kernel.org/r/20210713135158.288697903@linutronix.de
This commit is contained in:
parent
8c3b5e6ec0
commit
e71a4153b7
@ -739,23 +739,7 @@ static inline int hrtimer_is_hres_enabled(void)
|
||||
return hrtimer_hres_enabled;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrigger next event is called after clock was set
|
||||
*
|
||||
* Called with interrupts disabled via on_each_cpu()
|
||||
*/
|
||||
static void retrigger_next_event(void *arg)
|
||||
{
|
||||
struct hrtimer_cpu_base *base = this_cpu_ptr(&hrtimer_bases);
|
||||
|
||||
if (!__hrtimer_hres_active(base))
|
||||
return;
|
||||
|
||||
raw_spin_lock(&base->lock);
|
||||
hrtimer_update_base(base);
|
||||
hrtimer_force_reprogram(base, 0);
|
||||
raw_spin_unlock(&base->lock);
|
||||
}
|
||||
static void retrigger_next_event(void *arg);
|
||||
|
||||
/*
|
||||
* Switch to high resolution mode
|
||||
@ -781,9 +765,50 @@ static void hrtimer_switch_to_hres(void)
|
||||
|
||||
static inline int hrtimer_is_hres_enabled(void) { return 0; }
|
||||
static inline void hrtimer_switch_to_hres(void) { }
|
||||
static inline void retrigger_next_event(void *arg) { }
|
||||
|
||||
#endif /* CONFIG_HIGH_RES_TIMERS */
|
||||
/*
|
||||
* Retrigger next event is called after clock was set with interrupts
|
||||
* disabled through an SMP function call or directly from low level
|
||||
* resume code.
|
||||
*
|
||||
* This is only invoked when:
|
||||
* - CONFIG_HIGH_RES_TIMERS is enabled.
|
||||
* - CONFIG_NOHZ_COMMON is enabled
|
||||
*
|
||||
* For the other cases this function is empty and because the call sites
|
||||
* are optimized out it vanishes as well, i.e. no need for lots of
|
||||
* #ifdeffery.
|
||||
*/
|
||||
static void retrigger_next_event(void *arg)
|
||||
{
|
||||
struct hrtimer_cpu_base *base = this_cpu_ptr(&hrtimer_bases);
|
||||
|
||||
/*
|
||||
* When high resolution mode or nohz is active, then the offsets of
|
||||
* CLOCK_REALTIME/TAI/BOOTTIME have to be updated. Otherwise the
|
||||
* next tick will take care of that.
|
||||
*
|
||||
* If high resolution mode is active then the next expiring timer
|
||||
* must be reevaluated and the clock event device reprogrammed if
|
||||
* necessary.
|
||||
*
|
||||
* In the NOHZ case the update of the offset and the reevaluation
|
||||
* of the next expiring timer is enough. The return from the SMP
|
||||
* function call will take care of the reprogramming in case the
|
||||
* CPU was in a NOHZ idle sleep.
|
||||
*/
|
||||
if (!__hrtimer_hres_active(base) && !tick_nohz_active)
|
||||
return;
|
||||
|
||||
raw_spin_lock(&base->lock);
|
||||
hrtimer_update_base(base);
|
||||
if (__hrtimer_hres_active(base))
|
||||
hrtimer_force_reprogram(base, 0);
|
||||
else
|
||||
hrtimer_update_next_event(base);
|
||||
raw_spin_unlock(&base->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* When a timer is enqueued and expires earlier than the already enqueued
|
||||
@ -842,22 +867,28 @@ static void hrtimer_reprogram(struct hrtimer *timer, bool reprogram)
|
||||
}
|
||||
|
||||
/*
|
||||
* Clock realtime was set
|
||||
* Clock was set. This might affect CLOCK_REALTIME, CLOCK_TAI and
|
||||
* CLOCK_BOOTTIME (for late sleep time injection).
|
||||
*
|
||||
* Change the offset of the realtime clock vs. the monotonic
|
||||
* clock.
|
||||
*
|
||||
* We might have to reprogram the high resolution timer interrupt. On
|
||||
* SMP we call the architecture specific code to retrigger _all_ high
|
||||
* resolution timer interrupts. On UP we just disable interrupts and
|
||||
* call the high resolution interrupt code.
|
||||
* This requires to update the offsets for these clocks
|
||||
* vs. CLOCK_MONOTONIC. When high resolution timers are enabled, then this
|
||||
* also requires to eventually reprogram the per CPU clock event devices
|
||||
* when the change moves an affected timer ahead of the first expiring
|
||||
* timer on that CPU. Obviously remote per CPU clock event devices cannot
|
||||
* be reprogrammed. The other reason why an IPI has to be sent is when the
|
||||
* system is in !HIGH_RES and NOHZ mode. The NOHZ mode updates the offsets
|
||||
* in the tick, which obviously might be stopped, so this has to bring out
|
||||
* the remote CPU which might sleep in idle to get this sorted.
|
||||
*/
|
||||
void clock_was_set(void)
|
||||
{
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
if (!hrtimer_hres_active() && !tick_nohz_active)
|
||||
goto out_timerfd;
|
||||
|
||||
/* Retrigger the CPU local events everywhere */
|
||||
on_each_cpu(retrigger_next_event, NULL, 1);
|
||||
#endif
|
||||
|
||||
out_timerfd:
|
||||
timerfd_clock_was_set();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user