Skip to content

Commit 1e7f7fb

Browse files
committed
hrtimer: Avoid more SMP function calls in clock_was_set()
By unconditionally updating the offsets there are more indicators whether the SMP function calls on clock_was_set() can be avoided: - When the offset update already happened on the remote CPU then the remote update attempt will yield the same seqeuence number and no IPI is required. - When the remote CPU is currently handling hrtimer_interrupt(). In that case the remote CPU will reevaluate the timer bases before reprogramming anyway, so nothing to do. - After updating it can be checked whether the first expiring timer in the affected clock bases moves before the first expiring (softirq) timer of the CPU. If that's not the case then sending the IPI is not required. Signed-off-by: Thomas Gleixner <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 81d741d commit 1e7f7fb

File tree

1 file changed

+65
-9
lines changed

1 file changed

+65
-9
lines changed

kernel/time/hrtimer.c

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,68 @@ static void hrtimer_reprogram(struct hrtimer *timer, bool reprogram)
866866
__hrtimer_reprogram(cpu_base, true, timer, expires);
867867
}
868868

869+
static bool update_needs_ipi(struct hrtimer_cpu_base *cpu_base,
870+
unsigned int active)
871+
{
872+
struct hrtimer_clock_base *base;
873+
unsigned int seq;
874+
ktime_t expires;
875+
876+
/*
877+
* Update the base offsets unconditionally so the following
878+
* checks whether the SMP function call is required works.
879+
*
880+
* The update is safe even when the remote CPU is in the hrtimer
881+
* interrupt or the hrtimer soft interrupt and expiring affected
882+
* bases. Either it will see the update before handling a base or
883+
* it will see it when it finishes the processing and reevaluates
884+
* the next expiring timer.
885+
*/
886+
seq = cpu_base->clock_was_set_seq;
887+
hrtimer_update_base(cpu_base);
888+
889+
/*
890+
* If the sequence did not change over the update then the
891+
* remote CPU already handled it.
892+
*/
893+
if (seq == cpu_base->clock_was_set_seq)
894+
return false;
895+
896+
/*
897+
* If the remote CPU is currently handling an hrtimer interrupt, it
898+
* will reevaluate the first expiring timer of all clock bases
899+
* before reprogramming. Nothing to do here.
900+
*/
901+
if (cpu_base->in_hrtirq)
902+
return false;
903+
904+
/*
905+
* Walk the affected clock bases and check whether the first expiring
906+
* timer in a clock base is moving ahead of the first expiring timer of
907+
* @cpu_base. If so, the IPI must be invoked because per CPU clock
908+
* event devices cannot be remotely reprogrammed.
909+
*/
910+
active &= cpu_base->active_bases;
911+
912+
for_each_active_base(base, cpu_base, active) {
913+
struct timerqueue_node *next;
914+
915+
next = timerqueue_getnext(&base->active);
916+
expires = ktime_sub(next->expires, base->offset);
917+
if (expires < cpu_base->expires_next)
918+
return true;
919+
920+
/* Extra check for softirq clock bases */
921+
if (base->clockid < HRTIMER_BASE_MONOTONIC_SOFT)
922+
continue;
923+
if (cpu_base->softirq_activated)
924+
continue;
925+
if (expires < cpu_base->softirq_expires_next)
926+
return true;
927+
}
928+
return false;
929+
}
930+
869931
/*
870932
* Clock was set. This might affect CLOCK_REALTIME, CLOCK_TAI and
871933
* CLOCK_BOOTTIME (for late sleep time injection).
@@ -900,16 +962,10 @@ void clock_was_set(unsigned int bases)
900962
unsigned long flags;
901963

902964
raw_spin_lock_irqsave(&cpu_base->lock, flags);
903-
/*
904-
* Only send the IPI when there are timers queued in one of
905-
* the affected clock bases. Otherwise update the base
906-
* remote to ensure that the next enqueue of a timer on
907-
* such a clock base will see the correct offsets.
908-
*/
909-
if (cpu_base->active_bases & bases)
965+
966+
if (update_needs_ipi(cpu_base, bases))
910967
cpumask_set_cpu(cpu, mask);
911-
else
912-
hrtimer_update_base(cpu_base);
968+
913969
raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
914970
}
915971

0 commit comments

Comments
 (0)