aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/cpuidle/governors/menu.c69
1 files changed, 46 insertions, 23 deletions
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 43a54fd6bfa2..2efee27714a0 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -245,36 +245,59 @@ static enum hrtimer_restart menu_hrtimer_notify(struct hrtimer *hrtimer)
245 * of points is below a threshold. If it is... then use the 245 * of points is below a threshold. If it is... then use the
246 * average of these 8 points as the estimated value. 246 * average of these 8 points as the estimated value.
247 */ 247 */
248static int detect_repeating_patterns(struct menu_device *data) 248static u32 get_typical_interval(struct menu_device *data)
249{ 249{
250 int i; 250 int i = 0, divisor = 0;
251 uint64_t avg = 0; 251 uint64_t max = 0, avg = 0, stddev = 0;
252 uint64_t stddev = 0; /* contains the square of the std deviation */ 252 int64_t thresh = LLONG_MAX; /* Discard outliers above this value. */
253 int ret = 0; 253 unsigned int ret = 0;
254
255 /* first calculate average and standard deviation of the past */
256 for (i = 0; i < INTERVALS; i++)
257 avg += data->intervals[i];
258 avg = avg / INTERVALS;
259 254
260 /* if the avg is beyond the known next tick, it's worthless */ 255again:
261 if (avg > data->expected_us)
262 return 0;
263
264 for (i = 0; i < INTERVALS; i++)
265 stddev += (data->intervals[i] - avg) *
266 (data->intervals[i] - avg);
267 256
268 stddev = stddev / INTERVALS; 257 /* first calculate average and standard deviation of the past */
258 max = avg = divisor = stddev = 0;
259 for (i = 0; i < INTERVALS; i++) {
260 int64_t value = data->intervals[i];
261 if (value <= thresh) {
262 avg += value;
263 divisor++;
264 if (value > max)
265 max = value;
266 }
267 }
268 do_div(avg, divisor);
269 269
270 for (i = 0; i < INTERVALS; i++) {
271 int64_t value = data->intervals[i];
272 if (value <= thresh) {
273 int64_t diff = value - avg;
274 stddev += diff * diff;
275 }
276 }
277 do_div(stddev, divisor);
278 stddev = int_sqrt(stddev);
270 /* 279 /*
271 * now.. if stddev is small.. then assume we have a 280 * If we have outliers to the upside in our distribution, discard
272 * repeating pattern and predict we keep doing this. 281 * those by setting the threshold to exclude these outliers, then
282 * calculate the average and standard deviation again. Once we get
283 * down to the bottom 3/4 of our samples, stop excluding samples.
284 *
285 * This can deal with workloads that have long pauses interspersed
286 * with sporadic activity with a bunch of short pauses.
287 *
288 * The typical interval is obtained when standard deviation is small
289 * or standard deviation is small compared to the average interval.
273 */ 290 */
274 291 if (((avg > stddev * 6) && (divisor * 4 >= INTERVALS * 3))
275 if (avg && stddev < STDDEV_THRESH) { 292 || stddev <= 20) {
276 data->predicted_us = avg; 293 data->predicted_us = avg;
277 ret = 1; 294 ret = 1;
295 return ret;
296
297 } else if ((divisor * 4) > INTERVALS * 3) {
298 /* Exclude the max interval */
299 thresh = max - 1;
300 goto again;
278 } 301 }
279 302
280 return ret; 303 return ret;
@@ -330,7 +353,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
330 data->predicted_us = div_round64(data->expected_us * data->correction_factor[data->bucket], 353 data->predicted_us = div_round64(data->expected_us * data->correction_factor[data->bucket],
331 RESOLUTION * DECAY); 354 RESOLUTION * DECAY);
332 355
333 repeat = detect_repeating_patterns(data); 356 repeat = get_typical_interval(data);
334 357
335 /* 358 /*
336 * We want to default to C1 (hlt), not to busy polling 359 * We want to default to C1 (hlt), not to busy polling