in governors/teo.c [285:476]
static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
bool *stop_tick)
{
struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu);
s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
unsigned int idx_intercept_sum = 0;
unsigned int intercept_sum = 0;
unsigned int idx_recent_sum = 0;
unsigned int recent_sum = 0;
unsigned int idx_hit_sum = 0;
unsigned int hit_sum = 0;
int constraint_idx = 0;
int idx0 = 0, idx = -1;
bool alt_intercepts, alt_recent;
ktime_t delta_tick;
s64 duration_ns;
int i;
if (dev->last_state_idx >= 0) {
teo_update(drv, dev);
dev->last_state_idx = -1;
}
cpu_data->time_span_ns = local_clock();
duration_ns = tick_nohz_get_sleep_length(&delta_tick);
cpu_data->sleep_length_ns = duration_ns;
/* Check if there is any choice in the first place. */
if (drv->state_count < 2) {
idx = 0;
goto end;
}
if (!dev->states_usage[0].disable) {
idx = 0;
if (drv->states[1].target_residency_ns > duration_ns)
goto end;
}
/*
* Find the deepest idle state whose target residency does not exceed
* the current sleep length and the deepest idle state not deeper than
* the former whose exit latency does not exceed the current latency
* constraint. Compute the sums of metrics for early wakeup pattern
* detection.
*/
for (i = 1; i < drv->state_count; i++) {
struct teo_bin *prev_bin = &cpu_data->state_bins[i-1];
struct cpuidle_state *s = &drv->states[i];
/*
* Update the sums of idle state mertics for all of the states
* shallower than the current one.
*/
intercept_sum += prev_bin->intercepts;
hit_sum += prev_bin->hits;
recent_sum += prev_bin->recent;
if (dev->states_usage[i].disable)
continue;
if (idx < 0) {
idx = i; /* first enabled state */
idx0 = i;
}
if (s->target_residency_ns > duration_ns)
break;
idx = i;
if (s->exit_latency_ns <= latency_req)
constraint_idx = i;
idx_intercept_sum = intercept_sum;
idx_hit_sum = hit_sum;
idx_recent_sum = recent_sum;
}
/* Avoid unnecessary overhead. */
if (idx < 0) {
idx = 0; /* No states enabled, must use 0. */
goto end;
} else if (idx == idx0) {
goto end;
}
/*
* If the sum of the intercepts metric for all of the idle states
* shallower than the current candidate one (idx) is greater than the
* sum of the intercepts and hits metrics for the candidate state and
* all of the deeper states, or the sum of the numbers of recent
* intercepts over all of the states shallower than the candidate one
* is greater than a half of the number of recent events taken into
* account, the CPU is likely to wake up early, so find an alternative
* idle state to select.
*/
alt_intercepts = 2 * idx_intercept_sum > cpu_data->total - idx_hit_sum;
alt_recent = idx_recent_sum > NR_RECENT / 2;
if (alt_recent || alt_intercepts) {
s64 first_suitable_span_ns = duration_ns;
int first_suitable_idx = idx;
/*
* Look for the deepest idle state whose target residency had
* not exceeded the idle duration in over a half of the relevant
* cases (both with respect to intercepts overall and with
* respect to the recent intercepts only) in the past.
*
* Take the possible latency constraint and duration limitation
* present if the tick has been stopped already into account.
*/
intercept_sum = 0;
recent_sum = 0;
for (i = idx - 1; i >= 0; i--) {
struct teo_bin *bin = &cpu_data->state_bins[i];
s64 span_ns;
intercept_sum += bin->intercepts;
recent_sum += bin->recent;
span_ns = teo_middle_of_bin(i, drv);
if ((!alt_recent || 2 * recent_sum > idx_recent_sum) &&
(!alt_intercepts ||
2 * intercept_sum > idx_intercept_sum)) {
if (teo_time_ok(span_ns) &&
!dev->states_usage[i].disable) {
idx = i;
duration_ns = span_ns;
} else {
/*
* The current state is too shallow or
* disabled, so take the first enabled
* deeper state with suitable time span.
*/
idx = first_suitable_idx;
duration_ns = first_suitable_span_ns;
}
break;
}
if (dev->states_usage[i].disable)
continue;
if (!teo_time_ok(span_ns)) {
/*
* The current state is too shallow, but if an
* alternative candidate state has been found,
* it may still turn out to be a better choice.
*/
if (first_suitable_idx != idx)
continue;
break;
}
first_suitable_span_ns = span_ns;
first_suitable_idx = i;
}
}
/*
* If there is a latency constraint, it may be necessary to select an
* idle state shallower than the current candidate one.
*/
if (idx > constraint_idx)
idx = constraint_idx;
end:
/*
* Don't stop the tick if the selected state is a polling one or if the
* expected idle duration is shorter than the tick period length.
*/
if (((drv->states[idx].flags & CPUIDLE_FLAG_POLLING) ||
duration_ns < TICK_NSEC) && !tick_nohz_tick_stopped()) {
*stop_tick = false;
/*
* The tick is not going to be stopped, so if the target
* residency of the state to be returned is not within the time
* till the closest timer including the tick, try to correct
* that.
*/
if (idx > idx0 &&
drv->states[idx].target_residency_ns > delta_tick)
idx = teo_find_shallower_state(drv, dev, idx, delta_tick);
}
return idx;
}