History

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.

Thomas Gleixner committed 5 years ago

Commit 1e7f7fb1 parent 81d741d1 file · +65 −9Browse files

Unified · Split

kernel/time/hrtimer.c+65−9
@@ -866,6 +866,68 @@ static void hrtimer_reprogram(struct hrtimer *timer, bool reprogram) · 865 unmodified lines
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) · 28 unmodified lines
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

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t