Skip to content

Commit 083654d

Browse files
committed
cpuidle: governors: teo: Rework the handling of tick wakeups
If the wakeup pattern is clearly dominated by tick wakeups, count those wakeups as hits on the deepest available idle state to increase the likelihood of stopping the tick, especially on systems where there are only 2 usable idle states and the tick can only be stopped when the deeper state is selected. This change is expected to reduce power on some systems where state 0 is selected relatively often even though they are almost idle. Without it, the governor may end up selecting the shallowest idle state all the time even if the system is almost completely idle due all tick wakeups being counted as hits on that state and preventing the tick from being stopped at all. Fixes: 4b20b07 ("cpuidle: teo: Don't count non-existent intercepts") Reported-by: Reka Norman <rekanorman@chromium.org> Closes: https://lore.kernel.org/linux-pm/CAEmPcwsNMNnNXuxgvHTQ93Mx-q3Oz9U57THQsU_qdcCx1m4w5g@mail.gmail.com/ Tested-by: Reka Norman <rekanorman@chromium.org> Tested-by: Christian Loehle <christian.loehle@arm.com> Cc: 6.11+ <stable@vger.kernel.org> # 6.11+: 92ce5c0: cpuidle: teo: Reorder candidate state index checks Cc: 6.11+ <stable@vger.kernel.org> # 6.11+: ea18540: cpuidle: teo: Combine candidate state index checks against 0 Cc: 6.11+ <stable@vger.kernel.org> # 6.11+: b9a6af2: cpuidle: teo: Drop local variable prev_intercept_idx Cc: 6.11+ <stable@vger.kernel.org> # 6.11+: e24f8a5: cpuidle: teo: Clarify two code comments Cc: 6.11+ <stable@vger.kernel.org> # 6.11+: d619b5c: cpuidle: teo: Simplify counting events used for tick management Cc: 6.11+ <stable@vger.kernel.org> # 6.11+: 13ed5c4: cpuidle: teo: Skip getting the sleep length if wakeups are very frequent Cc: 6.11+ <stable@vger.kernel.org> # 6.11+: ddcfa79: cpuidle: teo: Simplify handling of total events count Cc: 6.11+ <stable@vger.kernel.org> # 6.11+: 65e18e6: cpuidle: teo: Replace time_span_ns with a flag Cc: 6.11+ <stable@vger.kernel.org> # 6.11+: 0796ddf: cpuidle: teo: Use this_cpu_ptr() where possible Cc: 6.11+ <stable@vger.kernel.org> # 6.11+: 8f3f010: cpuidle: governors: teo: Use s64 consistently in teo_update() Cc: 6.11+ <stable@vger.kernel.org> # 6.11+: b54df61: cpuidle: governors: teo: Decay metrics below DECAY_SHIFT threshold Cc: 6.11+ <stable@vger.kernel.org> # 6.11+ Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> [ rjw: Rebase on commit 0796ddf, changelog update ] Link: https://patch.msgid.link/6228387.lOV4Wx5bFT@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent b54df61 commit 083654d

1 file changed

Lines changed: 24 additions & 15 deletions

File tree

  • drivers/cpuidle/governors

drivers/cpuidle/governors/teo.c

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,19 @@ struct teo_bin {
133133
* @sleep_length_ns: Time till the closest timer event (at the selection time).
134134
* @state_bins: Idle state data bins for this CPU.
135135
* @total: Grand total of the "intercepts" and "hits" metrics for all bins.
136+
* @total_tick: Wakeups by the scheduler tick.
136137
* @tick_intercepts: "Intercepts" before TICK_NSEC.
137138
* @short_idles: Wakeups after short idle periods.
138-
* @artificial_wakeup: Set if the wakeup has been triggered by a safety net.
139+
* @tick_wakeup: Set if the last wakeup was by the scheduler tick.
139140
*/
140141
struct teo_cpu {
141142
s64 sleep_length_ns;
142143
struct teo_bin state_bins[CPUIDLE_STATE_MAX];
143144
unsigned int total;
145+
unsigned int total_tick;
144146
unsigned int tick_intercepts;
145147
unsigned int short_idles;
146-
bool artificial_wakeup;
148+
bool tick_wakeup;
147149
};
148150

149151
static DEFINE_PER_CPU(struct teo_cpu, teo_cpus);
@@ -172,9 +174,10 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
172174

173175
teo_decay(&cpu_data->short_idles);
174176

175-
if (cpu_data->artificial_wakeup) {
177+
if (dev->poll_time_limit) {
178+
dev->poll_time_limit = false;
176179
/*
177-
* If one of the safety nets has triggered, assume that this
180+
* Polling state timeout has triggered, so assume that this
178181
* might have been a long sleep.
179182
*/
180183
measured_ns = S64_MAX;
@@ -223,6 +226,21 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
223226
cpu_data->total = total + PULSE;
224227

225228
teo_decay(&cpu_data->tick_intercepts);
229+
230+
teo_decay(&cpu_data->total_tick);
231+
if (cpu_data->tick_wakeup) {
232+
cpu_data->total_tick += PULSE;
233+
/*
234+
* If tick wakeups dominate the wakeup pattern, count this one
235+
* as a hit on the deepest available idle state to increase the
236+
* likelihood of stopping the tick.
237+
*/
238+
if (3 * cpu_data->total_tick > 2 * cpu_data->total) {
239+
cpu_data->state_bins[drv->state_count-1].hits += PULSE;
240+
return;
241+
}
242+
}
243+
226244
/*
227245
* If the measured idle duration falls into the same bin as the sleep
228246
* length, this is a "hit", so update the "hits" metric for that bin.
@@ -512,18 +530,9 @@ static void teo_reflect(struct cpuidle_device *dev, int state)
512530
{
513531
struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus);
514532

533+
cpu_data->tick_wakeup = tick_nohz_idle_got_tick();
534+
515535
dev->last_state_idx = state;
516-
if (dev->poll_time_limit ||
517-
(tick_nohz_idle_got_tick() && cpu_data->sleep_length_ns > TICK_NSEC)) {
518-
/*
519-
* The wakeup was not "genuine", but triggered by one of the
520-
* safety nets.
521-
*/
522-
dev->poll_time_limit = false;
523-
cpu_data->artificial_wakeup = true;
524-
} else {
525-
cpu_data->artificial_wakeup = false;
526-
}
527536
}
528537

529538
/**

0 commit comments

Comments
 (0)