diff options
author | Youquan Song <youquan.song@intel.com> | 2012-10-26 06:26:50 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2012-11-14 18:34:20 -0500 |
commit | e11538d1f03914eb92af5a1a378375c05ae8520c (patch) | |
tree | c2c750f26f6c4de57da94e8a0a95a7d7e742bcee | |
parent | 69a37beabf1f0a6705c08e879bdd5d82ff6486c4 (diff) |
cpuidle: Quickly notice prediction failure in general case
The prediction for future is difficult and when the cpuidle governor prediction
fails and govenor possibly choose the shallower C-state than it should. How to
quickly notice and find the failure becomes important for power saving.
The patch extends to general case that prediction logic get a small predicted
residency, so it choose a shallow C-state though the expected residency is large
. Once the prediction will be fail, the CPU will keep staying at shallow C-state
for a long time. Acutally, the CPU has change enter into deep C-state.
So when the expected residency is long enough but governor choose a shallow
C-state, an timer will be added in order to monitor if the prediction failure.
When C-state is waken up prior to the adding timer, the timer will be cancelled
initiatively. When the timer is triggered and menu governor will quickly notice
prediction failure and re-evaluates deeper C-states possibility.
Signed-off-by: Rik van Riel <riel@redhat.com>
Signed-off-by: Youquan Song <youquan.song@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/cpuidle/governors/menu.c | 34 |
1 files changed, 33 insertions, 1 deletions
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 37c0ff6c805c..43a54fd6bfa2 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c | |||
@@ -34,7 +34,7 @@ | |||
34 | static DEFINE_PER_CPU(struct hrtimer, menu_hrtimer); | 34 | static DEFINE_PER_CPU(struct hrtimer, menu_hrtimer); |
35 | static DEFINE_PER_CPU(int, hrtimer_status); | 35 | static DEFINE_PER_CPU(int, hrtimer_status); |
36 | /* menu hrtimer mode */ | 36 | /* menu hrtimer mode */ |
37 | enum {MENU_HRTIMER_STOP, MENU_HRTIMER_REPEAT}; | 37 | enum {MENU_HRTIMER_STOP, MENU_HRTIMER_REPEAT, MENU_HRTIMER_GENERAL}; |
38 | 38 | ||
39 | /* | 39 | /* |
40 | * Concepts and ideas behind the menu governor | 40 | * Concepts and ideas behind the menu governor |
@@ -116,6 +116,13 @@ enum {MENU_HRTIMER_STOP, MENU_HRTIMER_REPEAT}; | |||
116 | * | 116 | * |
117 | */ | 117 | */ |
118 | 118 | ||
119 | /* | ||
120 | * The C-state residency is so long that is is worthwhile to exit | ||
121 | * from the shallow C-state and re-enter into a deeper C-state. | ||
122 | */ | ||
123 | static unsigned int perfect_cstate_ms __read_mostly = 30; | ||
124 | module_param(perfect_cstate_ms, uint, 0000); | ||
125 | |||
119 | struct menu_device { | 126 | struct menu_device { |
120 | int last_state_idx; | 127 | int last_state_idx; |
121 | int needs_update; | 128 | int needs_update; |
@@ -216,6 +223,16 @@ EXPORT_SYMBOL_GPL(menu_hrtimer_cancel); | |||
216 | static enum hrtimer_restart menu_hrtimer_notify(struct hrtimer *hrtimer) | 223 | static enum hrtimer_restart menu_hrtimer_notify(struct hrtimer *hrtimer) |
217 | { | 224 | { |
218 | int cpu = smp_processor_id(); | 225 | int cpu = smp_processor_id(); |
226 | struct menu_device *data = &per_cpu(menu_devices, cpu); | ||
227 | |||
228 | /* In general case, the expected residency is much larger than | ||
229 | * deepest C-state target residency, but prediction logic still | ||
230 | * predicts a small predicted residency, so the prediction | ||
231 | * history is totally broken if the timer is triggered. | ||
232 | * So reset the correction factor. | ||
233 | */ | ||
234 | if (per_cpu(hrtimer_status, cpu) == MENU_HRTIMER_GENERAL) | ||
235 | data->correction_factor[data->bucket] = RESOLUTION * DECAY; | ||
219 | 236 | ||
220 | per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_STOP; | 237 | per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_STOP; |
221 | 238 | ||
@@ -353,6 +370,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) | |||
353 | /* not deepest C-state chosen for low predicted residency */ | 370 | /* not deepest C-state chosen for low predicted residency */ |
354 | if (low_predicted) { | 371 | if (low_predicted) { |
355 | unsigned int timer_us = 0; | 372 | unsigned int timer_us = 0; |
373 | unsigned int perfect_us = 0; | ||
356 | 374 | ||
357 | /* | 375 | /* |
358 | * Set a timer to detect whether this sleep is much | 376 | * Set a timer to detect whether this sleep is much |
@@ -363,12 +381,26 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) | |||
363 | */ | 381 | */ |
364 | timer_us = 2 * (data->predicted_us + MAX_DEVIATION); | 382 | timer_us = 2 * (data->predicted_us + MAX_DEVIATION); |
365 | 383 | ||
384 | perfect_us = perfect_cstate_ms * 1000; | ||
385 | |||
366 | if (repeat && (4 * timer_us < data->expected_us)) { | 386 | if (repeat && (4 * timer_us < data->expected_us)) { |
367 | hrtimer_start(hrtmr, ns_to_ktime(1000 * timer_us), | 387 | hrtimer_start(hrtmr, ns_to_ktime(1000 * timer_us), |
368 | HRTIMER_MODE_REL_PINNED); | 388 | HRTIMER_MODE_REL_PINNED); |
369 | /* In repeat case, menu hrtimer is started */ | 389 | /* In repeat case, menu hrtimer is started */ |
370 | per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_REPEAT; | 390 | per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_REPEAT; |
391 | } else if (perfect_us < data->expected_us) { | ||
392 | /* | ||
393 | * The next timer is long. This could be because | ||
394 | * we did not make a useful prediction. | ||
395 | * In that case, it makes sense to re-enter | ||
396 | * into a deeper C-state after some time. | ||
397 | */ | ||
398 | hrtimer_start(hrtmr, ns_to_ktime(1000 * timer_us), | ||
399 | HRTIMER_MODE_REL_PINNED); | ||
400 | /* In general case, menu hrtimer is started */ | ||
401 | per_cpu(hrtimer_status, cpu) = MENU_HRTIMER_GENERAL; | ||
371 | } | 402 | } |
403 | |||
372 | } | 404 | } |
373 | 405 | ||
374 | return data->last_state_idx; | 406 | return data->last_state_idx; |