aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrea Arcangeli <andrea@cpushare.com>2012-07-02 05:34:17 +0200
committerAndrea Arcangeli <andrea@cpushare.com>2013-05-28 14:01:50 +0200
commitf4156e2bef7098483448980ad6ea4b0164087e37 (patch)
treeefb1f780c6ed7f81eee2d485a9d08ce1b7b490fa
parent4377ebe6456b2cbadd7be9b3970e2412a2001654 (diff)
downloadkernel_samsung_smdk4412-f4156e2bef7098483448980ad6ea4b0164087e37.tar.gz
kernel_samsung_smdk4412-f4156e2bef7098483448980ad6ea4b0164087e37.tar.bz2
kernel_samsung_smdk4412-f4156e2bef7098483448980ad6ea4b0164087e37.zip
ondemand: cpuidle detection
I found a problem with the ondemand governor while in earlysuspend suspend mode (ondemand keeping the freq close to the max at all times despite near 0% load). The problem is that the cpu starts to go in long cpuidle cycles, a sampling_rate of 10000 (usec) is applied only once in a while and the real sampling_rate becomes 1 second (or more). So when a wakeup happens we return to the 10000usec sampling rate. The ondemand sees lots of activity after the wakeup but those "loads" must be adjusted down if the previous wall_time delta was huge and the current one is tiny. We're too close to long cpuidle to worry about the cpu freq anyway and it may be just a jitter load that if we take into account without adjusting it down, will lead to the next long cpuidle to be entered at the max freq again, and this repeats forever. So my solution is to tweak the ondemand to scale down the "load" according to the decrease in the wall_time delta ratio (deep_sleep_ratio variable). This allows the CPU to stay at the lowest freq during the deep sleeps even when the ondemand governor is enabled and the sampling_rate is set to 10000. (setting the sampling_rate to values >100000 would also tend to hide the problem but it'd screw the interactive behavior by running at the lowest frequency for too long during interactive usage) During interactive usage with the screen on (not in earlysuspend mode), or during playback or voip with screen off the deep_sleep_rate is set to 1 practically all the time so it won't alter the behavior of the ondemand governor unless the system is very idle (and in turn when we want it to stay at low freq). Change-Id: If0391e6d6c41c1c8c9fa590502e88e681a800b04 Signed-off-by: Andrea Arcangeli <andrea@cpushare.com>
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c34
1 files changed, 34 insertions, 0 deletions
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index a87dc5d6608..e151adcdb6a 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -85,6 +85,7 @@ struct cpu_dbs_info_s {
cputime64_t prev_cpu_idle;
cputime64_t prev_cpu_iowait;
cputime64_t prev_cpu_wall;
+ unsigned int prev_cpu_wall_delta;
cputime64_t prev_cpu_nice;
struct cpufreq_policy *cur_policy;
struct delayed_work work;
@@ -608,6 +609,10 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
unsigned int idle_time, wall_time, iowait_time;
unsigned int load, load_freq;
int freq_avg;
+ bool deep_sleep_detected = false;
+ /* the evil magic numbers, only 2 at least */
+ const unsigned int deep_sleep_backoff = 10;
+ const unsigned int deep_sleep_factor = 5;
j_dbs_info = &per_cpu(od_cpu_dbs_info, j);
@@ -618,6 +623,32 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
j_dbs_info->prev_cpu_wall);
j_dbs_info->prev_cpu_wall = cur_wall_time;
+ /*
+ * Ignore wall delta jitters in both directions. An
+ * exceptionally long wall_time will likely result
+ * idle but it was waken up to do work so the next
+ * slice is less likely to want to run at low
+ * frequency. Let's evaluate the next slice instead of
+ * the idle long one that passed already and it's too
+ * late to reduce in frequency. As opposed an
+ * exceptionally short slice that just run at low
+ * frequency is unlikely to be idle, but we may go
+ * back to idle pretty soon and that not idle slice
+ * already passed. If short slices will keep coming
+ * after a series of long slices the exponential
+ * backoff will converge faster and we'll react faster
+ * to high load. As opposed we'll decay slower
+ * towards low load and long idle times.
+ */
+ if (j_dbs_info->prev_cpu_wall_delta >
+ wall_time * deep_sleep_factor ||
+ j_dbs_info->prev_cpu_wall_delta * deep_sleep_factor <
+ wall_time)
+ deep_sleep_detected = true;
+ j_dbs_info->prev_cpu_wall_delta =
+ (j_dbs_info->prev_cpu_wall_delta * deep_sleep_backoff
+ + wall_time) / (deep_sleep_backoff+1);
+
idle_time = (unsigned int) cputime64_sub(cur_idle_time,
j_dbs_info->prev_cpu_idle);
j_dbs_info->prev_cpu_idle = cur_idle_time;
@@ -643,6 +674,9 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
idle_time += jiffies_to_usecs(cur_nice_jiffies);
}
+ if (deep_sleep_detected)
+ continue;
+
/*
* For the purpose of ondemand, waiting for disk IO is an
* indication that you're performance critical, and not that