Skip to content

Commit 627ef5a

Browse files
committed
hrtimer: Avoid double reprogramming in __hrtimer_start_range_ns()
If __hrtimer_start_range_ns() is invoked with an already armed hrtimer then the timer has to be canceled first and then added back. If the timer is the first expiring timer then on removal the clockevent device is reprogrammed to the next expiring timer to avoid that the pending expiry fires needlessly. If the new expiry time ends up to be the first expiry again then the clock event device has to reprogrammed again. Avoid this by checking whether the timer is the first to expire and in that case, keep the timer on the current CPU and delay the reprogramming up to the point where the timer has been enqueued again. Reported-by: Lorenzo Colitti <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent ee37532 commit 627ef5a

File tree

1 file changed

+53
-7
lines changed

1 file changed

+53
-7
lines changed

kernel/time/hrtimer.c

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,12 +1030,13 @@ static void __remove_hrtimer(struct hrtimer *timer,
10301030
* remove hrtimer, called with base lock held
10311031
*/
10321032
static inline int
1033-
remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, bool restart)
1033+
remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base,
1034+
bool restart, bool keep_local)
10341035
{
10351036
u8 state = timer->state;
10361037

10371038
if (state & HRTIMER_STATE_ENQUEUED) {
1038-
int reprogram;
1039+
bool reprogram;
10391040

10401041
/*
10411042
* Remove the timer and force reprogramming when high
@@ -1048,8 +1049,16 @@ remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, bool rest
10481049
debug_deactivate(timer);
10491050
reprogram = base->cpu_base == this_cpu_ptr(&hrtimer_bases);
10501051

1052+
/*
1053+
* If the timer is not restarted then reprogramming is
1054+
* required if the timer is local. If it is local and about
1055+
* to be restarted, avoid programming it twice (on removal
1056+
* and a moment later when it's requeued).
1057+
*/
10511058
if (!restart)
10521059
state = HRTIMER_STATE_INACTIVE;
1060+
else
1061+
reprogram &= !keep_local;
10531062

10541063
__remove_hrtimer(timer, base, state, reprogram);
10551064
return 1;
@@ -1103,9 +1112,31 @@ static int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
11031112
struct hrtimer_clock_base *base)
11041113
{
11051114
struct hrtimer_clock_base *new_base;
1115+
bool force_local, first;
11061116

1107-
/* Remove an active timer from the queue: */
1108-
remove_hrtimer(timer, base, true);
1117+
/*
1118+
* If the timer is on the local cpu base and is the first expiring
1119+
* timer then this might end up reprogramming the hardware twice
1120+
* (on removal and on enqueue). To avoid that by prevent the
1121+
* reprogram on removal, keep the timer local to the current CPU
1122+
* and enforce reprogramming after it is queued no matter whether
1123+
* it is the new first expiring timer again or not.
1124+
*/
1125+
force_local = base->cpu_base == this_cpu_ptr(&hrtimer_bases);
1126+
force_local &= base->cpu_base->next_timer == timer;
1127+
1128+
/*
1129+
* Remove an active timer from the queue. In case it is not queued
1130+
* on the current CPU, make sure that remove_hrtimer() updates the
1131+
* remote data correctly.
1132+
*
1133+
* If it's on the current CPU and the first expiring timer, then
1134+
* skip reprogramming, keep the timer local and enforce
1135+
* reprogramming later if it was the first expiring timer. This
1136+
* avoids programming the underlying clock event twice (once at
1137+
* removal and once after enqueue).
1138+
*/
1139+
remove_hrtimer(timer, base, true, force_local);
11091140

11101141
if (mode & HRTIMER_MODE_REL)
11111142
tim = ktime_add_safe(tim, base->get_time());
@@ -1115,9 +1146,24 @@ static int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
11151146
hrtimer_set_expires_range_ns(timer, tim, delta_ns);
11161147

11171148
/* Switch the timer base, if necessary: */
1118-
new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED);
1149+
if (!force_local) {
1150+
new_base = switch_hrtimer_base(timer, base,
1151+
mode & HRTIMER_MODE_PINNED);
1152+
} else {
1153+
new_base = base;
1154+
}
1155+
1156+
first = enqueue_hrtimer(timer, new_base, mode);
1157+
if (!force_local)
1158+
return first;
11191159

1120-
return enqueue_hrtimer(timer, new_base, mode);
1160+
/*
1161+
* Timer was forced to stay on the current CPU to avoid
1162+
* reprogramming on removal and enqueue. Force reprogram the
1163+
* hardware by evaluating the new first expiring timer.
1164+
*/
1165+
hrtimer_force_reprogram(new_base->cpu_base, 1);
1166+
return 0;
11211167
}
11221168

11231169
/**
@@ -1183,7 +1229,7 @@ int hrtimer_try_to_cancel(struct hrtimer *timer)
11831229
base = lock_hrtimer_base(timer, &flags);
11841230

11851231
if (!hrtimer_callback_running(timer))
1186-
ret = remove_hrtimer(timer, base, false);
1232+
ret = remove_hrtimer(timer, base, false, false);
11871233

11881234
unlock_hrtimer_base(timer, &flags);
11891235

0 commit comments

Comments
 (0)