Skip to content

Commit 2662342

Browse files
committed
cpuidle: teo: Gather statistics regarding whether or not to stop the tick
Currently, if the target residency of the deepest idle state is less than the tick period length, which is quite likely for HZ=100, and the deepest idle state is about to be selected by the TEO idle governor, the decision on whether or not to stop the scheduler tick is based entirely on the time till the closest timer. This is often insufficient, because timers may not be in heavy use and there may be a plenty of other CPU wakeup events between the deepest idle state's target residency and the closest tick. Allow the governor to count those events by making the deepest idle state's bin effectively end at TICK_NSEC and introducing an additional "bin" for collecting "hit" events (ie. the ones in which the measured idle duration falls into the same bin as the time till the closest timer) with idle duration values past TICK_NSEC. This way the "intercepts" metric for the deepest idle state's bin becomes nonzero in general, and so it can influence the decision on whether or not to stop the tick possibly increasing the governor's accuracy in that respect. Signed-off-by: Rafael J. Wysocki <[email protected]> Tested-by: Kajetan Puchalski <[email protected]> Tested-by: Anna-Maria Behnsen <[email protected]>
1 parent 6da8f9b commit 2662342

File tree

1 file changed

+40
-1
lines changed
  • drivers/cpuidle/governors

1 file changed

+40
-1
lines changed

drivers/cpuidle/governors/teo.c

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ struct teo_bin {
192192
* @total: Grand total of the "intercepts" and "hits" metrics for all bins.
193193
* @next_recent_idx: Index of the next @recent_idx entry to update.
194194
* @recent_idx: Indices of bins corresponding to recent "intercepts".
195+
* @tick_hits: Number of "hits" after TICK_NSEC.
195196
* @util_threshold: Threshold above which the CPU is considered utilized
196197
*/
197198
struct teo_cpu {
@@ -201,6 +202,7 @@ struct teo_cpu {
201202
unsigned int total;
202203
int next_recent_idx;
203204
int recent_idx[NR_RECENT];
205+
unsigned int tick_hits;
204206
unsigned long util_threshold;
205207
};
206208

@@ -232,6 +234,7 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
232234
{
233235
struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu);
234236
int i, idx_timer = 0, idx_duration = 0;
237+
s64 target_residency_ns;
235238
u64 measured_ns;
236239

237240
if (cpu_data->time_span_ns >= cpu_data->sleep_length_ns) {
@@ -272,14 +275,15 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
272275
* fall into.
273276
*/
274277
for (i = 0; i < drv->state_count; i++) {
275-
s64 target_residency_ns = drv->states[i].target_residency_ns;
276278
struct teo_bin *bin = &cpu_data->state_bins[i];
277279

278280
bin->hits -= bin->hits >> DECAY_SHIFT;
279281
bin->intercepts -= bin->intercepts >> DECAY_SHIFT;
280282

281283
cpu_data->total += bin->hits + bin->intercepts;
282284

285+
target_residency_ns = drv->states[i].target_residency_ns;
286+
283287
if (target_residency_ns <= cpu_data->sleep_length_ns) {
284288
idx_timer = i;
285289
if (target_residency_ns <= measured_ns)
@@ -294,6 +298,26 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
294298
if (cpu_data->recent_idx[i] >= 0)
295299
cpu_data->state_bins[cpu_data->recent_idx[i]].recent--;
296300

301+
/*
302+
* If the deepest state's target residency is below the tick length,
303+
* make a record of it to help teo_select() decide whether or not
304+
* to stop the tick. This effectively adds an extra hits-only bin
305+
* beyond the last state-related one.
306+
*/
307+
if (target_residency_ns < TICK_NSEC) {
308+
cpu_data->tick_hits -= cpu_data->tick_hits >> DECAY_SHIFT;
309+
310+
cpu_data->total += cpu_data->tick_hits;
311+
312+
if (TICK_NSEC <= cpu_data->sleep_length_ns) {
313+
idx_timer = drv->state_count;
314+
if (TICK_NSEC <= measured_ns) {
315+
cpu_data->tick_hits += PULSE;
316+
goto end;
317+
}
318+
}
319+
}
320+
297321
/*
298322
* If the measured idle duration falls into the same bin as the sleep
299323
* length, this is a "hit", so update the "hits" metric for that bin.
@@ -309,6 +333,7 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
309333
cpu_data->recent_idx[i] = idx_duration;
310334
}
311335

336+
end:
312337
cpu_data->total += PULSE;
313338
}
314339

@@ -356,6 +381,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
356381
struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu);
357382
s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
358383
ktime_t delta_tick = TICK_NSEC / 2;
384+
unsigned int tick_intercept_sum = 0;
359385
unsigned int idx_intercept_sum = 0;
360386
unsigned int intercept_sum = 0;
361387
unsigned int idx_recent_sum = 0;
@@ -429,6 +455,8 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
429455
hit_sum += prev_bin->hits;
430456
recent_sum += prev_bin->recent;
431457

458+
tick_intercept_sum = intercept_sum;
459+
432460
if (dev->states_usage[i].disable)
433461
continue;
434462

@@ -461,6 +489,8 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
461489
goto end;
462490
}
463491

492+
tick_intercept_sum += cpu_data->state_bins[drv->state_count-1].intercepts;
493+
464494
/*
465495
* If the sum of the intercepts metric for all of the idle states
466496
* shallower than the current candidate one (idx) is greater than the
@@ -577,6 +607,15 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
577607
idx = i;
578608
}
579609

610+
/*
611+
* If the selected state's target residency is below the tick length
612+
* and intercepts occurring before the tick length are the majority of
613+
* total wakeup events, do not stop the tick.
614+
*/
615+
if (drv->states[idx].target_residency_ns < TICK_NSEC &&
616+
tick_intercept_sum > cpu_data->total / 2 + cpu_data->total / 8)
617+
duration_ns = TICK_NSEC / 2;
618+
580619
end:
581620
/*
582621
* Allow the tick to be stopped unless the selected state is a polling

0 commit comments

Comments
 (0)