aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYouquan Song <youquan.song@intel.com>2012-10-26 06:26:50 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2012-11-14 18:34:20 -0500
commite11538d1f03914eb92af5a1a378375c05ae8520c (patch)
treec2c750f26f6c4de57da94e8a0a95a7d7e742bcee
parent69a37beabf1f0a6705c08e879bdd5d82ff6486c4 (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.c34
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 @@
34static DEFINE_PER_CPU(struct hrtimer, menu_hrtimer); 34static DEFINE_PER_CPU(struct hrtimer, menu_hrtimer);
35static DEFINE_PER_CPU(int, hrtimer_status); 35static DEFINE_PER_CPU(int, hrtimer_status);
36/* menu hrtimer mode */ 36/* menu hrtimer mode */
37enum {MENU_HRTIMER_STOP, MENU_HRTIMER_REPEAT}; 37enum {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 */
123static unsigned int perfect_cstate_ms __read_mostly = 30;
124module_param(perfect_cstate_ms, uint, 0000);
125
119struct menu_device { 126struct 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);
216static enum hrtimer_restart menu_hrtimer_notify(struct hrtimer *hrtimer) 223static 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;