diff options
author | Todd Poynor <toddpoynor@google.com> | 2012-12-11 16:05:03 -0800 |
---|---|---|
committer | Arve Hjønnevåg <arve@android.com> | 2013-07-01 14:16:17 -0700 |
commit | 5b28b65949ea3279f6883d7bb89e3ef2768d8c28 (patch) | |
tree | 89def1541001b44007cae263420ed1bef041c822 /drivers/cpufreq | |
parent | f2fc3257231d227006a53f6c782e9709fdd7f6b7 (diff) |
cpufreq: interactive: adjust load for changes in speed
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>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r-- | drivers/cpufreq/cpufreq_interactive.c | 87 |
1 files changed, 71 insertions, 16 deletions
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 3d8e7b4360a5..d0d51ee30727 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -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); |