Skip to content

Commit 106a266

Browse files
committed
cpuidle: governors: teo: Rearrange stopped tick handling
This change is based on the observation that it is not in fact necessary to select a deep idle state every time the scheduler tick has been stopped before the idle state selection takes place. Namely, if the time till the closest timer (that is not the tick) is short enough, a shallow idle state can be selected because the timer will kick the CPU out of that state, so the damage from a possible overly optimistic selection will be limited. Update the teo governor in accordance with the above in analogy with the previous analogous menu governor update. Among other things, this will cause the teo governor to call tick_nohz_get_sleep_length() every time when the tick has been stopped already and only change the original idle state selection if the time till the closest timer is beyond SAFE_TIMER_RANGE_NS which is way more straightforward than the current code flow. Of course, this effectively throws away some of the recent teo governor changes made recently, but the resulting simplification is worth it in my view. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Christian Loehle <christian.loehle@arm.com> Link: https://patch.msgid.link/1865078.VLH7GnMWUR@rafael.j.wysocki
1 parent e57c2bf commit 106a266

1 file changed

Lines changed: 34 additions & 47 deletions

File tree

  • drivers/cpuidle/governors

drivers/cpuidle/governors/teo.c

Lines changed: 34 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -407,50 +407,13 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
407407
* better choice.
408408
*/
409409
if (2 * idx_intercept_sum > cpu_data->total - idx_hit_sum) {
410-
int min_idx = idx0;
411-
412-
if (tick_nohz_tick_stopped()) {
413-
/*
414-
* Look for the shallowest idle state below the current
415-
* candidate one whose target residency is at least
416-
* equal to the tick period length.
417-
*/
418-
while (min_idx < idx &&
419-
drv->states[min_idx].target_residency_ns < TICK_NSEC)
420-
min_idx++;
421-
422-
/*
423-
* Avoid selecting a state with a lower index, but with
424-
* the same target residency as the current candidate
425-
* one.
426-
*/
427-
if (drv->states[min_idx].target_residency_ns ==
428-
drv->states[idx].target_residency_ns)
429-
goto constraint;
430-
}
431-
432-
/*
433-
* If the minimum state index is greater than or equal to the
434-
* index of the state with the maximum intercepts metric and
435-
* the corresponding state is enabled, there is no need to look
436-
* at the deeper states.
437-
*/
438-
if (min_idx >= intercept_max_idx &&
439-
!dev->states_usage[min_idx].disable) {
440-
idx = min_idx;
441-
goto constraint;
442-
}
443-
444410
/*
445411
* Look for the deepest enabled idle state, at most as deep as
446412
* the one with the maximum intercepts metric, whose target
447413
* residency had not been greater than the idle duration in over
448414
* a half of the relevant cases in the past.
449-
*
450-
* Take the possible duration limitation present if the tick
451-
* has been stopped already into account.
452415
*/
453-
for (i = idx - 1, intercept_sum = 0; i >= min_idx; i--) {
416+
for (i = idx - 1, intercept_sum = 0; i >= idx0; i--) {
454417
intercept_sum += cpu_data->state_bins[i].intercepts;
455418

456419
if (dev->states_usage[i].disable)
@@ -463,7 +426,6 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
463426
}
464427
}
465428

466-
constraint:
467429
/*
468430
* If there is a latency constraint, it may be necessary to select an
469431
* idle state shallower than the current candidate one.
@@ -472,13 +434,13 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
472434
idx = constraint_idx;
473435

474436
/*
475-
* If either the candidate state is state 0 or its target residency is
476-
* low enough, there is basically nothing more to do, but if the sleep
477-
* length is not updated, the subsequent wakeup will be counted as an
478-
* "intercept" which may be problematic in the cases when timer wakeups
479-
* are dominant. Namely, it may effectively prevent deeper idle states
480-
* from being selected at one point even if no imminent timers are
481-
* scheduled.
437+
* If the tick has not been stopped and either the candidate state is
438+
* state 0 or its target residency is low enough, there is basically
439+
* nothing more to do, but if the sleep length is not updated, the
440+
* subsequent wakeup will be counted as an "intercept". That may be
441+
* problematic in the cases when timer wakeups are dominant because it
442+
* may effectively prevent deeper idle states from being selected at one
443+
* point even if no imminent timers are scheduled.
482444
*
483445
* However, frequent timers in the RESIDENCY_THRESHOLD_NS range on one
484446
* CPU are unlikely (user space has a default 50 us slack value for
@@ -494,14 +456,39 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
494456
* shallow idle states regardless of the wakeup type, so the sleep
495457
* length need not be known in that case.
496458
*/
497-
if ((!idx || drv->states[idx].target_residency_ns < RESIDENCY_THRESHOLD_NS) &&
459+
if (!tick_nohz_tick_stopped() && (!idx ||
460+
drv->states[idx].target_residency_ns < RESIDENCY_THRESHOLD_NS) &&
498461
(2 * cpu_data->short_idles >= cpu_data->total ||
499462
latency_req < LATENCY_THRESHOLD_NS))
500463
goto out_tick;
501464

502465
duration_ns = tick_nohz_get_sleep_length(&delta_tick);
503466
cpu_data->sleep_length_ns = duration_ns;
504467

468+
/*
469+
* If the tick has been stopped and the closest timer is too far away,
470+
* update the selection to prevent the CPU from getting stuck in a
471+
* shallow idle state for too long.
472+
*/
473+
if (tick_nohz_tick_stopped() && duration_ns > SAFE_TIMER_RANGE_NS &&
474+
drv->states[idx].target_residency_ns < TICK_NSEC) {
475+
/*
476+
* Look for the deepest enabled idle state with exit latency
477+
* within the PM QoS limit and with target residency within
478+
* duration_ns.
479+
*/
480+
for (i = constraint_idx; i > idx; i--) {
481+
if (dev->states_usage[i].disable)
482+
continue;
483+
484+
if (drv->states[i].target_residency_ns <= duration_ns) {
485+
idx = i;
486+
break;
487+
}
488+
}
489+
return idx;
490+
}
491+
505492
if (!idx)
506493
goto out_tick;
507494

0 commit comments

Comments
 (0)