Skip to content

Commit 21d28cd

Browse files
committed
cpuidle: teo: Do not call tick_nohz_get_sleep_length() upfront
Because the cost of calling tick_nohz_get_sleep_length() may increase in the future, reorder the code in teo_select() so it first uses the statistics to pick up a candidate idle state and applies the utilization heuristic to it and only then calls tick_nohz_get_sleep_length() to obtain the sleep length value and refine the selection if necessary. This change by itself does not cause tick_nohz_get_sleep_length() to be called less often, but it prepares the code for subsequent changes that will do so. Signed-off-by: Rafael J. Wysocki <[email protected]> Tested-by: Kajetan Puchalski <[email protected]> Tested-by: Anna-Maria Behnsen <[email protected]>
1 parent 9a41e16 commit 21d28cd

File tree

1 file changed

+44
-61
lines changed
  • drivers/cpuidle/governors

1 file changed

+44
-61
lines changed

drivers/cpuidle/governors/teo.c

Lines changed: 44 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -306,15 +306,10 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
306306
cpu_data->total += PULSE;
307307
}
308308

309-
static bool teo_time_ok(u64 interval_ns)
309+
static bool teo_state_ok(int i, struct cpuidle_driver *drv)
310310
{
311-
return !tick_nohz_tick_stopped() || interval_ns >= TICK_NSEC;
312-
}
313-
314-
static s64 teo_middle_of_bin(int idx, struct cpuidle_driver *drv)
315-
{
316-
return (drv->states[idx].target_residency_ns +
317-
drv->states[idx+1].target_residency_ns) / 2;
311+
return !tick_nohz_tick_stopped() ||
312+
drv->states[i].target_residency_ns >= TICK_NSEC;
318313
}
319314

320315
/**
@@ -354,6 +349,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
354349
{
355350
struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu);
356351
s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
352+
ktime_t delta_tick = TICK_NSEC / 2;
357353
unsigned int idx_intercept_sum = 0;
358354
unsigned int intercept_sum = 0;
359355
unsigned int idx_recent_sum = 0;
@@ -363,7 +359,6 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
363359
int constraint_idx = 0;
364360
int idx0 = 0, idx = -1;
365361
bool alt_intercepts, alt_recent;
366-
ktime_t delta_tick;
367362
bool cpu_utilized;
368363
s64 duration_ns;
369364
int i;
@@ -374,21 +369,20 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
374369
}
375370

376371
cpu_data->time_span_ns = local_clock();
377-
378-
duration_ns = tick_nohz_get_sleep_length(&delta_tick);
379-
cpu_data->sleep_length_ns = duration_ns;
372+
/*
373+
* Set the expected sleep length to infinity in case of an early
374+
* return.
375+
*/
376+
cpu_data->sleep_length_ns = KTIME_MAX;
380377

381378
/* Check if there is any choice in the first place. */
382379
if (drv->state_count < 2) {
383380
idx = 0;
384381
goto out_tick;
385382
}
386383

387-
if (!dev->states_usage[0].disable) {
384+
if (!dev->states_usage[0].disable)
388385
idx = 0;
389-
if (drv->states[1].target_residency_ns > duration_ns)
390-
goto out_tick;
391-
}
392386

393387
cpu_utilized = teo_cpu_is_utilized(dev->cpu, cpu_data);
394388
/*
@@ -397,8 +391,6 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
397391
* the shallowest non-polling state and exit.
398392
*/
399393
if (drv->state_count < 3 && cpu_utilized) {
400-
/* The CPU is utilized, so assume a short idle duration. */
401-
duration_ns = teo_middle_of_bin(0, drv);
402394
/*
403395
* If state 0 is enabled and it is not a polling one, select it
404396
* right away unless the scheduler tick has been stopped, in
@@ -408,22 +400,17 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
408400
* anyway.
409401
*/
410402
if ((!idx && !(drv->states[0].flags & CPUIDLE_FLAG_POLLING) &&
411-
teo_time_ok(duration_ns)) || dev->states_usage[1].disable) {
403+
teo_state_ok(0, drv)) || dev->states_usage[1].disable) {
412404
idx = 0;
413405
goto out_tick;
414406
}
415407
/* Assume that state 1 is not a polling one and use it. */
416408
idx = 1;
409+
duration_ns = drv->states[1].target_residency_ns;
417410
goto end;
418411
}
419412

420-
/*
421-
* Find the deepest idle state whose target residency does not exceed
422-
* the current sleep length and the deepest idle state not deeper than
423-
* the former whose exit latency does not exceed the current latency
424-
* constraint. Compute the sums of metrics for early wakeup pattern
425-
* detection.
426-
*/
413+
/* Compute the sums of metrics for early wakeup pattern detection. */
427414
for (i = 1; i < drv->state_count; i++) {
428415
struct teo_bin *prev_bin = &cpu_data->state_bins[i-1];
429416
struct cpuidle_state *s = &drv->states[i];
@@ -439,19 +426,15 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
439426
if (dev->states_usage[i].disable)
440427
continue;
441428

442-
if (idx < 0) {
443-
idx = i; /* first enabled state */
444-
idx0 = i;
445-
}
446-
447-
if (s->target_residency_ns > duration_ns)
448-
break;
429+
if (idx < 0)
430+
idx0 = i; /* first enabled state */
449431

450432
idx = i;
451433

452434
if (s->exit_latency_ns <= latency_req)
453435
constraint_idx = i;
454436

437+
/* Save the sums for the current state. */
455438
idx_intercept_sum = intercept_sum;
456439
idx_hit_sum = hit_sum;
457440
idx_recent_sum = recent_sum;
@@ -465,7 +448,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
465448

466449
if (idx == idx0) {
467450
/*
468-
* This is the first enabled idle state, so use it, but do not
451+
* Only one idle state is enabled, so use it, but do not
469452
* allow the tick to be stopped it is shallow enough.
470453
*/
471454
duration_ns = drv->states[idx].target_residency_ns;
@@ -479,13 +462,11 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
479462
* all of the deeper states, or the sum of the numbers of recent
480463
* intercepts over all of the states shallower than the candidate one
481464
* is greater than a half of the number of recent events taken into
482-
* account, the CPU is likely to wake up early, so find an alternative
483-
* idle state to select.
465+
* account, a shallower idle state is likely to be a better choice.
484466
*/
485467
alt_intercepts = 2 * idx_intercept_sum > cpu_data->total - idx_hit_sum;
486468
alt_recent = idx_recent_sum > NR_RECENT / 2;
487469
if (alt_recent || alt_intercepts) {
488-
s64 first_suitable_span_ns = duration_ns;
489470
int first_suitable_idx = idx;
490471

491472
/*
@@ -494,44 +475,39 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
494475
* cases (both with respect to intercepts overall and with
495476
* respect to the recent intercepts only) in the past.
496477
*
497-
* Take the possible latency constraint and duration limitation
498-
* present if the tick has been stopped already into account.
478+
* Take the possible duration limitation present if the tick
479+
* has been stopped already into account.
499480
*/
500481
intercept_sum = 0;
501482
recent_sum = 0;
502483

503484
for (i = idx - 1; i >= 0; i--) {
504485
struct teo_bin *bin = &cpu_data->state_bins[i];
505-
s64 span_ns;
506486

507487
intercept_sum += bin->intercepts;
508488
recent_sum += bin->recent;
509489

510-
span_ns = teo_middle_of_bin(i, drv);
511-
512490
if ((!alt_recent || 2 * recent_sum > idx_recent_sum) &&
513491
(!alt_intercepts ||
514492
2 * intercept_sum > idx_intercept_sum)) {
515-
if (teo_time_ok(span_ns) &&
516-
!dev->states_usage[i].disable) {
493+
/*
494+
* Use the current state unless it is too
495+
* shallow or disabled, in which case take the
496+
* first enabled state that is deep enough.
497+
*/
498+
if (teo_state_ok(i, drv) &&
499+
!dev->states_usage[i].disable)
517500
idx = i;
518-
duration_ns = span_ns;
519-
} else {
520-
/*
521-
* The current state is too shallow or
522-
* disabled, so take the first enabled
523-
* deeper state with suitable time span.
524-
*/
501+
else
525502
idx = first_suitable_idx;
526-
duration_ns = first_suitable_span_ns;
527-
}
503+
528504
break;
529505
}
530506

531507
if (dev->states_usage[i].disable)
532508
continue;
533509

534-
if (!teo_time_ok(span_ns)) {
510+
if (!teo_state_ok(i, drv)) {
535511
/*
536512
* The current state is too shallow, but if an
537513
* alternative candidate state has been found,
@@ -543,7 +519,6 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
543519
break;
544520
}
545521

546-
first_suitable_span_ns = span_ns;
547522
first_suitable_idx = i;
548523
}
549524
}
@@ -562,14 +537,22 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
562537
* not sufficiently large.
563538
*/
564539
if (cpu_utilized) {
565-
s64 span_ns;
540+
i = teo_find_shallower_state(drv, dev, idx, KTIME_MAX, true);
541+
if (teo_state_ok(i, drv))
542+
idx = i;
543+
}
566544

567-
i = teo_find_shallower_state(drv, dev, idx, duration_ns, true);
568-
span_ns = teo_middle_of_bin(i, drv);
569-
if (teo_time_ok(span_ns)) {
545+
duration_ns = tick_nohz_get_sleep_length(&delta_tick);
546+
cpu_data->sleep_length_ns = duration_ns;
547+
548+
/*
549+
* If the closest expected timer is before the terget residency of the
550+
* candidate state, a shallower one needs to be found.
551+
*/
552+
if (drv->states[idx].target_residency_ns > duration_ns) {
553+
i = teo_find_shallower_state(drv, dev, idx, duration_ns, false);
554+
if (teo_state_ok(i, drv))
570555
idx = i;
571-
duration_ns = span_ns;
572-
}
573556
}
574557

575558
end:

0 commit comments

Comments
 (0)