Setting the realtime clock triggers an IPI to all CPUs to reprogram hrtimers.
However, only base, boottime and tai clocks have their offsets updated (and therefore potentially require a reprogram). If the CPU is a nohz_full one, check if it only has monotonic active timers, and in that case update the realtime base offsets, skipping the IPI. This reduces interruptions to nohz_full CPUs. Signed-off-by: Marcelo Tosatti <mtosa...@redhat.com> diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 743c852e10f2..b42b1a434b22 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -853,6 +853,28 @@ static void hrtimer_reprogram(struct hrtimer *timer, bool reprogram) tick_program_event(expires, 1); } +#define CLOCK_SET_BASES ((1U << HRTIMER_BASE_REALTIME)| \ + (1U << HRTIMER_BASE_REALTIME_SOFT)| \ + (1U << HRTIMER_BASE_BOOTTIME)| \ + (1U << HRTIMER_BASE_BOOTTIME_SOFT)| \ + (1U << HRTIMER_BASE_TAI)| \ + (1U << HRTIMER_BASE_TAI_SOFT)) + +static bool need_reprogram_timer(struct hrtimer_cpu_base *cpu_base) +{ + unsigned int active = 0; + + if (!cpu_base->softirq_activated) + active = cpu_base->active_bases & HRTIMER_ACTIVE_SOFT; + + active = active | (cpu_base->active_bases & HRTIMER_ACTIVE_HARD); + + if ((active & CLOCK_SET_BASES) == 0) + return false; + + return true; +} + /* * Clock realtime was set * @@ -867,9 +889,41 @@ static void hrtimer_reprogram(struct hrtimer *timer, bool reprogram) void clock_was_set(void) { #ifdef CONFIG_HIGH_RES_TIMERS - /* Retrigger the CPU local events everywhere */ - on_each_cpu(retrigger_next_event, NULL, 1); + cpumask_var_t mask; + int cpu; + + if (!tick_nohz_full_enabled()) { + /* Retrigger the CPU local events everywhere */ + on_each_cpu(retrigger_next_event, NULL, 1); + goto set_timerfd; + } + + if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { + on_each_cpu(retrigger_next_event, NULL, 1); + goto set_timerfd; + } + + /* Avoid interrupting nohz_full CPUs if possible */ + preempt_disable(); + for_each_online_cpu(cpu) { + if (tick_nohz_full_cpu(cpu)) { + unsigned long flags; + struct hrtimer_cpu_base *cpu_base = &per_cpu(hrtimer_bases, cpu); + + raw_spin_lock_irqsave(&cpu_base->lock, flags); + if (need_reprogram_timer(cpu_base)) + cpumask_set_cpu(cpu, mask); + else + hrtimer_update_base(cpu_base); + raw_spin_unlock_irqrestore(&cpu_base->lock, flags); + } + } + + smp_call_function_many(mask, retrigger_next_event, NULL, 1); + preempt_enable(); + free_cpumask_var(mask); #endif +set_timerfd: timerfd_clock_was_set(); }