cpufreq: interactive: add timer slack to limit idle at speed > min
authorTodd Poynor <toddpoynor@google.com>
Wed, 19 Dec 2012 01:50:10 +0000 (17:50 -0800)
committerJohn Stultz <john.stultz@linaro.org>
Tue, 16 Feb 2016 21:52:48 +0000 (13:52 -0800)
Always use deferrable timer for load sampling.

Set a non-deferrable timer to an additional slack time to allow prior to
waking up from idle to drop speed when not at minimum speed.  Slack value
-1 avoids wakeups to drop speed.  Default is 80ms.

Remove the governidle module param and its timer management in idle.  For
platforms on which holding speed above mimum in idle costs power, use the
new timer slack to select how long to wait before waking up to drop speed.

Change-Id: I270b3980667e2c70a68e5bff534124b4411dbad5
Signed-off-by: Todd Poynor <toddpoynor@google.com>
drivers/cpufreq/cpufreq_interactive.c

index 8b44a82aa14630587bbb3e9a6c891827ebcf2a5c..690be16aef8a04e289219429f853b127717a7ee3 100644 (file)
@@ -40,7 +40,7 @@ static atomic_t active_count = ATOMIC_INIT(0);
 
 struct cpufreq_interactive_cpuinfo {
        struct timer_list cpu_timer;
-       int timer_idlecancel;
+       struct timer_list cpu_slack_timer;
        spinlock_t load_lock; /* protects the next 4 fields */
        u64 time_in_idle;
        u64 time_in_idle_timestamp;
@@ -102,10 +102,12 @@ static int boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME;
 /* End time of boost pulse in ktime converted to usecs */
 static u64 boostpulse_endtime;
 
-static bool governidle;
-module_param(governidle, bool, S_IWUSR | S_IRUGO);
-MODULE_PARM_DESC(governidle,
-       "Set to 1 to wake up CPUs from idle to reduce speed (default 0)");
+/*
+ * Max additional time to wait in idle, beyond timer_rate, at speeds above
+ * minimum before wakeup to reduce speed, or -1 if unnecessary.
+ */
+#define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE)
+static int timer_slack_val = DEFAULT_TIMER_SLACK;
 
 static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
                unsigned int event);
@@ -123,8 +125,14 @@ struct cpufreq_governor cpufreq_gov_interactive = {
 static void cpufreq_interactive_timer_resched(
        struct cpufreq_interactive_cpuinfo *pcpu)
 {
-       mod_timer_pinned(&pcpu->cpu_timer,
-                        jiffies + usecs_to_jiffies(timer_rate));
+       unsigned long expires = jiffies + usecs_to_jiffies(timer_rate);
+
+       mod_timer_pinned(&pcpu->cpu_timer, expires);
+       if (timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) {
+               expires += usecs_to_jiffies(timer_slack_val);
+               mod_timer_pinned(&pcpu->cpu_slack_timer, expires);
+       }
+
        spin_lock(&pcpu->load_lock);
        pcpu->time_in_idle =
                get_cpu_idle_time_us(smp_processor_id(),
@@ -368,17 +376,8 @@ rearm_if_notmax:
                goto exit;
 
 rearm:
-       if (!timer_pending(&pcpu->cpu_timer)) {
-               /*
-                * If governing speed in idle and already at min, cancel the
-                * timer if that CPU goes idle.  We don't need to re-evaluate
-                * speed until the next idle exit.
-                */
-               if (governidle && pcpu->target_freq == pcpu->policy->min)
-                       pcpu->timer_idlecancel = 1;
-
+       if (!timer_pending(&pcpu->cpu_timer))
                cpufreq_interactive_timer_resched(pcpu);
-       }
 
 exit:
        return;
@@ -404,21 +403,8 @@ static void cpufreq_interactive_idle_start(void)
                 * min indefinitely.  This should probably be a quirk of
                 * the CPUFreq driver.
                 */
-               if (!pending) {
-                       pcpu->timer_idlecancel = 0;
+               if (!pending)
                        cpufreq_interactive_timer_resched(pcpu);
-               }
-       } else if (governidle) {
-               /*
-                * If at min speed and entering idle after load has
-                * already been evaluated, and a timer has been set just in
-                * case the CPU suddenly goes busy, cancel that timer.  The
-                * CPU didn't go busy; we'll recheck things upon idle exit.
-                */
-               if (pending && pcpu->timer_idlecancel) {
-                       del_timer(&pcpu->cpu_timer);
-                       pcpu->timer_idlecancel = 0;
-               }
        }
 
 }
@@ -433,11 +419,10 @@ static void cpufreq_interactive_idle_end(void)
 
        /* Arm the timer for 1-2 ticks later if not already. */
        if (!timer_pending(&pcpu->cpu_timer)) {
-               pcpu->timer_idlecancel = 0;
                cpufreq_interactive_timer_resched(pcpu);
-       } else if (!governidle &&
-                  time_after_eq(jiffies, pcpu->cpu_timer.expires)) {
+       } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) {
                del_timer(&pcpu->cpu_timer);
+               del_timer(&pcpu->cpu_slack_timer);
                cpufreq_interactive_timer(smp_processor_id());
        }
 }
@@ -747,6 +732,29 @@ static ssize_t store_timer_rate(struct kobject *kobj,
 static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644,
                show_timer_rate, store_timer_rate);
 
+static ssize_t show_timer_slack(
+       struct kobject *kobj, struct attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", timer_slack_val);
+}
+
+static ssize_t store_timer_slack(
+       struct kobject *kobj, struct attribute *attr, const char *buf,
+       size_t count)
+{
+       int ret;
+       unsigned long val;
+
+       ret = kstrtol(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       timer_slack_val = val;
+       return count;
+}
+
+define_one_global_rw(timer_slack);
+
 static ssize_t show_boost(struct kobject *kobj, struct attribute *attr,
                          char *buf)
 {
@@ -826,6 +834,7 @@ static struct attribute *interactive_attributes[] = {
        &above_hispeed_delay.attr,
        &min_sample_time_attr.attr,
        &timer_rate_attr.attr,
+       &timer_slack.attr,
        &boost.attr,
        &boostpulse.attr,
        &boostpulse_duration.attr,
@@ -876,6 +885,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
                        hispeed_freq = policy->max;
 
                for_each_cpu(j, policy->cpus) {
+                       unsigned long expires;
+
                        pcpu = &per_cpu(cpuinfo, j);
                        pcpu->policy = policy;
                        pcpu->target_freq = policy->cur;
@@ -887,9 +898,15 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
                                pcpu->floor_validate_time;
                        pcpu->governor_enabled = 1;
                        smp_wmb();
-                       pcpu->cpu_timer.expires =
-                               jiffies + usecs_to_jiffies(timer_rate);
+                       expires = jiffies + usecs_to_jiffies(timer_rate);
+                       pcpu->cpu_timer.expires = expires;
                        add_timer_on(&pcpu->cpu_timer, j);
+
+                       if (timer_slack_val >= 0) {
+                               expires += usecs_to_jiffies(timer_slack_val);
+                               pcpu->cpu_slack_timer.expires = expires;
+                               add_timer_on(&pcpu->cpu_slack_timer, j);
+                       }
                }
 
                /*
@@ -915,6 +932,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
                        pcpu->governor_enabled = 0;
                        smp_wmb();
                        del_timer_sync(&pcpu->cpu_timer);
+                       del_timer_sync(&pcpu->cpu_slack_timer);
                }
 
                if (atomic_dec_return(&active_count) > 0)
@@ -940,6 +958,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
        return 0;
 }
 
+static void cpufreq_interactive_nop_timer(unsigned long data)
+{
+}
+
 static int __init cpufreq_interactive_init(void)
 {
        unsigned int i;
@@ -954,12 +976,11 @@ static int __init cpufreq_interactive_init(void)
        /* Initalize per-cpu timers */
        for_each_possible_cpu(i) {
                pcpu = &per_cpu(cpuinfo, i);
-               if (governidle)
-                       init_timer(&pcpu->cpu_timer);
-               else
-                       init_timer_deferrable(&pcpu->cpu_timer);
+               init_timer_deferrable(&pcpu->cpu_timer);
                pcpu->cpu_timer.function = cpufreq_interactive_timer;
                pcpu->cpu_timer.data = i;
+               init_timer(&pcpu->cpu_slack_timer);
+               pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer;
                spin_lock_init(&pcpu->load_lock);
        }