cpufreq: interactive: adjust load for changes in speed
authorTodd Poynor <toddpoynor@google.com>
Wed, 12 Dec 2012 00:05:03 +0000 (16:05 -0800)
committerArve Hjønnevåg <arve@android.com>
Mon, 1 Jul 2013 21:16:17 +0000 (14:16 -0700)
Add notifier for speed transitions.  Keep a count of CPU active
microseconds times current frequency, converted to a percentage relative
to the current frequency when load is evaluated.

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

index 3d8e7b4360a5d75e8b9029db02322905bb363a40..d0d51ee30727dda728a9a9902a04b45d3f6b2460 100644 (file)
@@ -41,8 +41,11 @@ static atomic_t active_count = ATOMIC_INIT(0);
 struct cpufreq_interactive_cpuinfo {
        struct timer_list cpu_timer;
        int timer_idlecancel;
+       spinlock_t load_lock; /* protects the next 4 fields */
        u64 time_in_idle;
        u64 time_in_idle_timestamp;
+       u64 cputime_speedadj;
+       u64 cputime_speedadj_timestamp;
        struct cpufreq_policy *policy;
        struct cpufreq_frequency_table *freq_table;
        unsigned int target_freq;
@@ -121,9 +124,13 @@ static void cpufreq_interactive_timer_resched(
 {
        mod_timer_pinned(&pcpu->cpu_timer,
                         jiffies + usecs_to_jiffies(timer_rate));
+       spin_lock(&pcpu->load_lock);
        pcpu->time_in_idle =
                get_cpu_idle_time_us(smp_processor_id(),
                                     &pcpu->time_in_idle_timestamp);
+       pcpu->cputime_speedadj = 0;
+       pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp;
+       spin_unlock(&pcpu->load_lock);
 }
 
 static unsigned int freq_to_targetload(unsigned int freq)
@@ -148,10 +155,9 @@ static unsigned int freq_to_targetload(unsigned int freq)
  */
 
 static unsigned int choose_freq(
-       struct cpufreq_interactive_cpuinfo *pcpu, unsigned int curload)
+       struct cpufreq_interactive_cpuinfo *pcpu, unsigned int loadadjfreq)
 {
        unsigned int freq = pcpu->policy->cur;
-       unsigned int loadadjfreq = freq * curload;
        unsigned int prevfreq, freqmin, freqmax;
        unsigned int tl;
        int index;
@@ -230,16 +236,36 @@ static unsigned int choose_freq(
        return freq;
 }
 
-static void cpufreq_interactive_timer(unsigned long data)
+static u64 update_load(int cpu)
 {
+       struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
        u64 now;
+       u64 now_idle;
        unsigned int delta_idle;
        unsigned int delta_time;
+       u64 active_time;
+
+       now_idle = get_cpu_idle_time_us(cpu, &now);
+       delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle);
+       delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp);
+       active_time = delta_time - delta_idle;
+       pcpu->cputime_speedadj += active_time * pcpu->policy->cur;
+
+       pcpu->time_in_idle = now_idle;
+       pcpu->time_in_idle_timestamp = now;
+       return now;
+}
+
+static void cpufreq_interactive_timer(unsigned long data)
+{
+       u64 now;
+       unsigned int delta_time;
+       u64 cputime_speedadj;
        int cpu_load;
        struct cpufreq_interactive_cpuinfo *pcpu =
                &per_cpu(cpuinfo, data);
-       u64 now_idle;
        unsigned int new_freq;
+       unsigned int loadadjfreq;
        unsigned int index;
        unsigned long flags;
 
@@ -248,26 +274,24 @@ static void cpufreq_interactive_timer(unsigned long data)
        if (!pcpu->governor_enabled)
                goto exit;
 
-       now_idle = get_cpu_idle_time_us(data, &now);
-       delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle);
-       delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp);
+       spin_lock(&pcpu->load_lock);
+       now = update_load(data);
+       delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp);
+       cputime_speedadj = pcpu->cputime_speedadj;
+       spin_unlock(&pcpu->load_lock);
 
-       /*
-        * If timer ran less than 1ms after short-term sample started, retry.
-        */
-       if (delta_time < 1000)
+       if (WARN_ON_ONCE(!delta_time))
                goto rearm;
 
-       if (delta_idle > delta_time)
-               cpu_load = 0;
-       else
-               cpu_load = 100 * (delta_time - delta_idle) / delta_time;
+       do_div(cputime_speedadj, delta_time);
+       loadadjfreq = (unsigned int)cputime_speedadj * 100;
+       cpu_load = loadadjfreq / pcpu->target_freq;
 
        if ((cpu_load >= go_hispeed_load || boost_val) &&
            pcpu->target_freq < hispeed_freq)
                new_freq = hispeed_freq;
        else
-               new_freq = choose_freq(pcpu, cpu_load);
+               new_freq = choose_freq(pcpu, loadadjfreq);
 
        if (pcpu->target_freq >= hispeed_freq &&
            new_freq > pcpu->target_freq &&
@@ -498,6 +522,32 @@ static void cpufreq_interactive_boost(void)
                wake_up_process(speedchange_task);
 }
 
+static int cpufreq_interactive_notifier(
+       struct notifier_block *nb, unsigned long val, void *data)
+{
+       struct cpufreq_freqs *freq = data;
+       struct cpufreq_interactive_cpuinfo *pcpu;
+       int cpu;
+
+       if (val == CPUFREQ_POSTCHANGE) {
+               pcpu = &per_cpu(cpuinfo, freq->cpu);
+
+               for_each_cpu(cpu, pcpu->policy->cpus) {
+                       struct cpufreq_interactive_cpuinfo *pjcpu =
+                               &per_cpu(cpuinfo, cpu);
+                       spin_lock(&pjcpu->load_lock);
+                       update_load(cpu);
+                       spin_unlock(&pjcpu->load_lock);
+               }
+       }
+
+       return 0;
+}
+
+static struct notifier_block cpufreq_notifier_block = {
+       .notifier_call = cpufreq_interactive_notifier,
+};
+
 static ssize_t show_target_loads(
        struct kobject *kobj, struct attribute *attr, char *buf)
 {
@@ -817,6 +867,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
                        return rc;
 
                idle_notifier_register(&cpufreq_interactive_idle_nb);
+               cpufreq_register_notifier(
+                       &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
                break;
 
        case CPUFREQ_GOV_STOP:
@@ -830,6 +882,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
                if (atomic_dec_return(&active_count) > 0)
                        return 0;
 
+               cpufreq_unregister_notifier(
+                       &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
                idle_notifier_unregister(&cpufreq_interactive_idle_nb);
                sysfs_remove_group(cpufreq_global_kobject,
                                &interactive_attr_group);
@@ -868,6 +922,7 @@ static int __init cpufreq_interactive_init(void)
                        init_timer_deferrable(&pcpu->cpu_timer);
                pcpu->cpu_timer.function = cpufreq_interactive_timer;
                pcpu->cpu_timer.data = i;
+               spin_lock_init(&pcpu->load_lock);
        }
 
        spin_lock_init(&target_loads_lock);