From d85bd86bd9db50e3f9a3ac568756c53ce6453c6a Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Tue, 22 Jun 2010 11:26:45 -0700 Subject: cpufreq: interactive: New 'interactive' governor This governor is designed for latency-sensitive workloads, such as interactive user interfaces. The interactive governor aims to be significantly more responsive to ramp CPU quickly up when CPU-intensive activity begins. Existing governors sample CPU load at a particular rate, typically every X ms. This can lead to under-powering UI threads for the period of time during which the user begins interacting with a previously-idle system until the next sample period happens. The 'interactive' governor uses a different approach. Instead of sampling the CPU at a specified rate, the governor will check whether to scale the CPU frequency up soon after coming out of idle. When the CPU comes out of idle, a timer is configured to fire within 1-2 ticks. If the CPU is very busy from exiting idle to when the timer fires then we assume the CPU is underpowered and ramp to MAX speed. If the CPU was not sufficiently busy to immediately ramp to MAX speed, then the governor evaluates the CPU load since the last speed adjustment, choosing the highest value between that longer-term load or the short-term load since idle exit to determine the CPU speed to ramp to. A realtime thread is used for scaling up, giving the remaining tasks the CPU performance benefit, unlike existing governors which are more likely to schedule rampup work to occur after your performance starved tasks have completed. The tuneables for this governor are: /sys/devices/system/cpu/cpufreq/interactive/min_sample_time: The minimum amount of time to spend at the current frequency before ramping down. This is to ensure that the governor has seen enough historic CPU load data to determine the appropriate workload. Default is 80000 uS. /sys/devices/system/cpu/cpufreq/interactive/go_maxspeed_load The CPU load at which to ramp to max speed. Default is 85. Change-Id: Ib2b362607c62f7c56d35f44a9ef3280f98c17585 Signed-off-by: Mike Chan Signed-off-by: Todd Poynor Bug: 3152864 --- drivers/cpufreq/Kconfig | 16 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/cpufreq_interactive.c | 681 ++++++++++++++++++++++++++++++++++ 3 files changed, 698 insertions(+) create mode 100644 drivers/cpufreq/cpufreq_interactive.c (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index e24a2a1b6666..2cdc38b5e973 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -99,6 +99,16 @@ config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE Be aware that not all cpufreq drivers support the conservative governor. If unsure have a look at the help section of the driver. Fallback governor will be the performance governor. + +config CPU_FREQ_DEFAULT_GOV_INTERACTIVE + bool "interactive" + select CPU_FREQ_GOV_INTERACTIVE + help + Use the CPUFreq governor 'interactive' as default. This allows + you to get a full dynamic cpu frequency capable system by simply + loading your cpufreq low-level hardware driver, using the + 'interactive' governor for latency-sensitive workloads. + endchoice config CPU_FREQ_GOV_PERFORMANCE @@ -156,6 +166,12 @@ config CPU_FREQ_GOV_ONDEMAND If in doubt, say N. +config CPU_FREQ_GOV_INTERACTIVE + tristate "'interactive' cpufreq policy governor" + help + 'interactive' - This driver adds a dynamic cpufreq policy governor + designed for latency-sensitive workloads. + config CPU_FREQ_GOV_CONSERVATIVE tristate "'conservative' cpufreq governor" depends on CPU_FREQ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index a48bc02cd765..d43b39150efa 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o +obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c new file mode 100644 index 000000000000..6069ca20a014 --- /dev/null +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -0,0 +1,681 @@ +/* + * drivers/cpufreq/cpufreq_interactive.c + * + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Author: Mike Chan (mike@android.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static void (*pm_idle_old)(void); +static atomic_t active_count = ATOMIC_INIT(0); + +struct cpufreq_interactive_cpuinfo { + struct timer_list cpu_timer; + int timer_idlecancel; + u64 time_in_idle; + u64 idle_exit_time; + u64 timer_run_time; + int idling; + u64 freq_change_time; + u64 freq_change_time_in_idle; + struct cpufreq_policy *policy; + struct cpufreq_frequency_table *freq_table; + unsigned int target_freq; + int governor_enabled; +}; + +static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); + +/* Workqueues handle frequency scaling */ +static struct task_struct *up_task; +static struct workqueue_struct *down_wq; +static struct work_struct freq_scale_down_work; +static cpumask_t up_cpumask; +static spinlock_t up_cpumask_lock; +static cpumask_t down_cpumask; +static spinlock_t down_cpumask_lock; + +/* Go to max speed when CPU load at or above this value. */ +#define DEFAULT_GO_MAXSPEED_LOAD 85 +static unsigned long go_maxspeed_load; + +/* + * The minimum amount of time to spend at a frequency before we can ramp down. + */ +#define DEFAULT_MIN_SAMPLE_TIME 80000; +static unsigned long min_sample_time; + +#define DEBUG 0 +#define BUFSZ 128 + +#if DEBUG +#include + +struct dbgln { + int cpu; + unsigned long jiffy; + unsigned long run; + char buf[BUFSZ]; +}; + +#define NDBGLNS 256 + +static struct dbgln dbgbuf[NDBGLNS]; +static int dbgbufs; +static int dbgbufe; +static struct proc_dir_entry *dbg_proc; +static spinlock_t dbgpr_lock; + +static u64 up_request_time; +static unsigned int up_max_latency; + +static void dbgpr(char *fmt, ...) +{ + va_list args; + int n; + unsigned long flags; + + spin_lock_irqsave(&dbgpr_lock, flags); + n = dbgbufe; + va_start(args, fmt); + vsnprintf(dbgbuf[n].buf, BUFSZ, fmt, args); + va_end(args); + dbgbuf[n].cpu = smp_processor_id(); + dbgbuf[n].run = nr_running(); + dbgbuf[n].jiffy = jiffies; + + if (++dbgbufe >= NDBGLNS) + dbgbufe = 0; + + if (dbgbufe == dbgbufs) + if (++dbgbufs >= NDBGLNS) + dbgbufs = 0; + + spin_unlock_irqrestore(&dbgpr_lock, flags); +} + +static void dbgdump(void) +{ + int i, j; + unsigned long flags; + static struct dbgln prbuf[NDBGLNS]; + + spin_lock_irqsave(&dbgpr_lock, flags); + i = dbgbufs; + j = dbgbufe; + memcpy(prbuf, dbgbuf, sizeof(dbgbuf)); + dbgbufs = 0; + dbgbufe = 0; + spin_unlock_irqrestore(&dbgpr_lock, flags); + + while (i != j) + { + printk("%lu %d %lu %s", + prbuf[i].jiffy, prbuf[i].cpu, prbuf[i].run, + prbuf[i].buf); + if (++i == NDBGLNS) + i = 0; + } +} + +static int dbg_proc_read(char *buffer, char **start, off_t offset, + int count, int *peof, void *dat) +{ + printk("max up_task latency=%uus\n", up_max_latency); + dbgdump(); + *peof = 1; + return 0; +} + + +#else +#define dbgpr(...) do {} while (0) +#endif + +static int cpufreq_governor_interactive(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +static +#endif +struct cpufreq_governor cpufreq_gov_interactive = { + .name = "interactive", + .governor = cpufreq_governor_interactive, + .max_transition_latency = 10000000, + .owner = THIS_MODULE, +}; + +static void cpufreq_interactive_timer(unsigned long data) +{ + unsigned int delta_idle; + unsigned int delta_time; + int cpu_load; + int load_since_change; + u64 time_in_idle; + u64 idle_exit_time; + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, data); + u64 now_idle; + unsigned int new_freq; + unsigned int index; + + /* + * Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time, + * this lets idle exit know the current idle time sample has + * been processed, and idle exit can generate a new sample and + * re-arm the timer. This prevents a concurrent idle + * exit on that CPU from writing a new set of info at the same time + * the timer function runs (the timer function can't use that info + * until more time passes). + */ + time_in_idle = pcpu->time_in_idle; + idle_exit_time = pcpu->idle_exit_time; + now_idle = get_cpu_idle_time_us(data, &pcpu->timer_run_time); + smp_wmb(); + + /* If we raced with cancelling a timer, skip. */ + if (!idle_exit_time) { + dbgpr("timer %d: no valid idle exit sample\n", (int) data); + goto exit; + } + +#if DEBUG + if ((int) jiffies - (int) pcpu->cpu_timer.expires >= 10) + dbgpr("timer %d: late by %d ticks\n", + (int) data, jiffies - pcpu->cpu_timer.expires); +#endif + + delta_idle = (unsigned int) cputime64_sub(now_idle, time_in_idle); + delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, + idle_exit_time); + + /* + * If timer ran less than 1ms after short-term sample started, retry. + */ + if (delta_time < 1000) { + dbgpr("timer %d: time delta %u too short exit=%llu now=%llu\n", (int) data, + delta_time, idle_exit_time, pcpu->timer_run_time); + goto rearm; + } + + if (delta_idle > delta_time) + cpu_load = 0; + else + cpu_load = 100 * (delta_time - delta_idle) / delta_time; + + delta_idle = (unsigned int) cputime64_sub(now_idle, + pcpu->freq_change_time_in_idle); + delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, + pcpu->freq_change_time); + + if (delta_idle > delta_time) + load_since_change = 0; + else + load_since_change = + 100 * (delta_time - delta_idle) / delta_time; + + /* + * Choose greater of short-term load (since last idle timer + * started or timer function re-armed itself) or long-term load + * (since last frequency change). + */ + if (load_since_change > cpu_load) + cpu_load = load_since_change; + + if (cpu_load >= go_maxspeed_load) + new_freq = pcpu->policy->max; + else + new_freq = pcpu->policy->max * cpu_load / 100; + + if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, + new_freq, CPUFREQ_RELATION_H, + &index)) { + dbgpr("timer %d: cpufreq_frequency_table_target error\n", (int) data); + goto rearm; + } + + new_freq = pcpu->freq_table[index].frequency; + + if (pcpu->target_freq == new_freq) + { + dbgpr("timer %d: load=%d, already at %d\n", (int) data, cpu_load, new_freq); + goto rearm_if_notmax; + } + + /* + * Do not scale down unless we have been at this frequency for the + * minimum sample time. + */ + if (new_freq < pcpu->target_freq) { + if (cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time) < + min_sample_time) { + dbgpr("timer %d: load=%d cur=%d tgt=%d not yet\n", (int) data, cpu_load, pcpu->target_freq, new_freq); + goto rearm; + } + } + + dbgpr("timer %d: load=%d cur=%d tgt=%d queue\n", (int) data, cpu_load, pcpu->target_freq, new_freq); + + if (new_freq < pcpu->target_freq) { + pcpu->target_freq = new_freq; + spin_lock(&down_cpumask_lock); + cpumask_set_cpu(data, &down_cpumask); + spin_unlock(&down_cpumask_lock); + queue_work(down_wq, &freq_scale_down_work); + } else { + pcpu->target_freq = new_freq; +#if DEBUG + up_request_time = ktime_to_us(ktime_get()); +#endif + spin_lock(&up_cpumask_lock); + cpumask_set_cpu(data, &up_cpumask); + spin_unlock(&up_cpumask_lock); + wake_up_process(up_task); + } + +rearm_if_notmax: + /* + * Already set max speed and don't see a need to change that, + * wait until next idle to re-evaluate, don't need timer. + */ + if (pcpu->target_freq == pcpu->policy->max) + goto exit; + +rearm: + if (!timer_pending(&pcpu->cpu_timer)) { + /* + * If already at min: if that CPU is idle, don't set timer. + * Else cancel the timer if that CPU goes idle. We don't + * need to re-evaluate speed until the next idle exit. + */ + if (pcpu->target_freq == pcpu->policy->min) { + smp_rmb(); + + if (pcpu->idling) { + dbgpr("timer %d: cpu idle, don't re-arm\n", (int) data); + goto exit; + } + + pcpu->timer_idlecancel = 1; + } + + pcpu->time_in_idle = get_cpu_idle_time_us( + data, &pcpu->idle_exit_time); + mod_timer(&pcpu->cpu_timer, jiffies + 2); + dbgpr("timer %d: set timer for %lu exit=%llu\n", (int) data, pcpu->cpu_timer.expires, pcpu->idle_exit_time); + } + +exit: + return; +} + +static void cpufreq_interactive_idle(void) +{ + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, smp_processor_id()); + int pending; + + if (!pcpu->governor_enabled) { + pm_idle_old(); + return; + } + + pcpu->idling = 1; + smp_wmb(); + pending = timer_pending(&pcpu->cpu_timer); + + if (pcpu->target_freq != pcpu->policy->min) { +#ifdef CONFIG_SMP + /* + * Entering idle while not at lowest speed. On some + * platforms this can hold the other CPU(s) at that speed + * even though the CPU is idle. Set a timer to re-evaluate + * speed so this idle CPU doesn't hold the other CPUs above + * min indefinitely. This should probably be a quirk of + * the CPUFreq driver. + */ + if (!pending) { + pcpu->time_in_idle = get_cpu_idle_time_us( + smp_processor_id(), &pcpu->idle_exit_time); + pcpu->timer_idlecancel = 0; + mod_timer(&pcpu->cpu_timer, jiffies + 2); + dbgpr("idle: enter at %d, set timer for %lu exit=%llu\n", + pcpu->target_freq, pcpu->cpu_timer.expires, + pcpu->idle_exit_time); + } +#endif + } else { + /* + * 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) { + dbgpr("idle: cancel timer for %lu\n", pcpu->cpu_timer.expires); + del_timer(&pcpu->cpu_timer); + /* + * Ensure last timer run time is after current idle + * sample start time, so next idle exit will always + * start a new idle sampling period. + */ + pcpu->idle_exit_time = 0; + pcpu->timer_idlecancel = 0; + } + } + + pm_idle_old(); + pcpu->idling = 0; + smp_wmb(); + + /* + * Arm the timer for 1-2 ticks later if not already, and if the timer + * function has already processed the previous load sampling + * interval. (If the timer is not pending but has not processed + * the previous interval, it is probably racing with us on another + * CPU. Let it compute load based on the previous sample and then + * re-arm the timer for another interval when it's done, rather + * than updating the interval start time to be "now", which doesn't + * give the timer function enough time to make a decision on this + * run.) + */ + if (timer_pending(&pcpu->cpu_timer) == 0 && + pcpu->timer_run_time >= pcpu->idle_exit_time) { + pcpu->time_in_idle = + get_cpu_idle_time_us(smp_processor_id(), + &pcpu->idle_exit_time); + pcpu->timer_idlecancel = 0; + mod_timer(&pcpu->cpu_timer, jiffies + 2); + dbgpr("idle: exit, set timer for %lu exit=%llu\n", pcpu->cpu_timer.expires, pcpu->idle_exit_time); +#if DEBUG + } else if (timer_pending(&pcpu->cpu_timer) == 0 && + pcpu->timer_run_time < pcpu->idle_exit_time) { + dbgpr("idle: timer not run yet: exit=%llu tmrrun=%llu\n", + pcpu->idle_exit_time, pcpu->timer_run_time); +#endif + } + +} + +static int cpufreq_interactive_up_task(void *data) +{ + unsigned int cpu; + cpumask_t tmp_mask; + struct cpufreq_interactive_cpuinfo *pcpu; + +#if DEBUG + u64 now; + u64 then; + unsigned int lat; +#endif + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock(&up_cpumask_lock); + + if (cpumask_empty(&up_cpumask)) { + spin_unlock(&up_cpumask_lock); + schedule(); + + if (kthread_should_stop()) + break; + + spin_lock(&up_cpumask_lock); + } + + set_current_state(TASK_RUNNING); + +#if DEBUG + then = up_request_time; + now = ktime_to_us(ktime_get()); + + if (now > then) { + lat = ktime_to_us(ktime_get()) - then; + + if (lat > up_max_latency) + up_max_latency = lat; + } +#endif + + tmp_mask = up_cpumask; + cpumask_clear(&up_cpumask); + spin_unlock(&up_cpumask_lock); + + for_each_cpu(cpu, &tmp_mask) { + pcpu = &per_cpu(cpuinfo, cpu); + + if (nr_running() == 1) { + dbgpr("up %d: tgt=%d nothing else running\n", cpu, + pcpu->target_freq); + } + + __cpufreq_driver_target(pcpu->policy, + pcpu->target_freq, + CPUFREQ_RELATION_H); + pcpu->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu, + &pcpu->freq_change_time); + dbgpr("up %d: set tgt=%d (actual=%d)\n", cpu, pcpu->target_freq, pcpu->policy->cur); + } + } + + return 0; +} + +static void cpufreq_interactive_freq_down(struct work_struct *work) +{ + unsigned int cpu; + cpumask_t tmp_mask; + struct cpufreq_interactive_cpuinfo *pcpu; + + spin_lock(&down_cpumask_lock); + tmp_mask = down_cpumask; + cpumask_clear(&down_cpumask); + spin_unlock(&down_cpumask_lock); + + for_each_cpu(cpu, &tmp_mask) { + pcpu = &per_cpu(cpuinfo, cpu); + __cpufreq_driver_target(pcpu->policy, + pcpu->target_freq, + CPUFREQ_RELATION_H); + pcpu->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu, + &pcpu->freq_change_time); + dbgpr("down %d: set tgt=%d (actual=%d)\n", cpu, pcpu->target_freq, pcpu->policy->cur); + } +} + +static ssize_t show_go_maxspeed_load(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", go_maxspeed_load); +} + +static ssize_t store_go_maxspeed_load(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + return strict_strtoul(buf, 0, &go_maxspeed_load); +} + +static struct global_attr go_maxspeed_load_attr = __ATTR(go_maxspeed_load, 0644, + show_go_maxspeed_load, store_go_maxspeed_load); + +static ssize_t show_min_sample_time(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", min_sample_time); +} + +static ssize_t store_min_sample_time(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + return strict_strtoul(buf, 0, &min_sample_time); +} + +static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, + show_min_sample_time, store_min_sample_time); + +static struct attribute *interactive_attributes[] = { + &go_maxspeed_load_attr.attr, + &min_sample_time_attr.attr, + NULL, +}; + +static struct attribute_group interactive_attr_group = { + .attrs = interactive_attributes, + .name = "interactive", +}; + +static int cpufreq_governor_interactive(struct cpufreq_policy *new_policy, + unsigned int event) +{ + int rc; + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, new_policy->cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if (!cpu_online(new_policy->cpu)) + return -EINVAL; + + pcpu->policy = new_policy; + pcpu->freq_table = cpufreq_frequency_get_table(new_policy->cpu); + pcpu->target_freq = new_policy->cur; + pcpu->freq_change_time_in_idle = + get_cpu_idle_time_us(new_policy->cpu, + &pcpu->freq_change_time); + pcpu->governor_enabled = 1; + /* + * Do not register the idle hook and create sysfs + * entries if we have already done so. + */ + if (atomic_inc_return(&active_count) > 1) + return 0; + + rc = sysfs_create_group(cpufreq_global_kobject, + &interactive_attr_group); + if (rc) + return rc; + + pm_idle_old = pm_idle; + pm_idle = cpufreq_interactive_idle; + break; + + case CPUFREQ_GOV_STOP: + pcpu->governor_enabled = 0; + + if (atomic_dec_return(&active_count) > 0) + return 0; + + sysfs_remove_group(cpufreq_global_kobject, + &interactive_attr_group); + + pm_idle = pm_idle_old; + del_timer(&pcpu->cpu_timer); + break; + + case CPUFREQ_GOV_LIMITS: + if (new_policy->max < new_policy->cur) + __cpufreq_driver_target(new_policy, + new_policy->max, CPUFREQ_RELATION_H); + else if (new_policy->min > new_policy->cur) + __cpufreq_driver_target(new_policy, + new_policy->min, CPUFREQ_RELATION_L); + break; + } + return 0; +} + +static int __init cpufreq_interactive_init(void) +{ + unsigned int i; + struct cpufreq_interactive_cpuinfo *pcpu; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + + go_maxspeed_load = DEFAULT_GO_MAXSPEED_LOAD; + min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + + /* Initalize per-cpu timers */ + for_each_possible_cpu(i) { + pcpu = &per_cpu(cpuinfo, i); + init_timer(&pcpu->cpu_timer); + pcpu->cpu_timer.function = cpufreq_interactive_timer; + pcpu->cpu_timer.data = i; + } + + up_task = kthread_create(cpufreq_interactive_up_task, NULL, + "kinteractiveup"); + if (IS_ERR(up_task)) + return PTR_ERR(up_task); + + sched_setscheduler_nocheck(up_task, SCHED_FIFO, ¶m); + get_task_struct(up_task); + + /* No rescuer thread, bind to CPU queuing the work for possibly + warm cache (probably doesn't matter much). */ + down_wq = alloc_workqueue("knteractive_down", 0, 1); + + if (! down_wq) + goto err_freeuptask; + + INIT_WORK(&freq_scale_down_work, + cpufreq_interactive_freq_down); + + spin_lock_init(&up_cpumask_lock); + spin_lock_init(&down_cpumask_lock); + +#if DEBUG + spin_lock_init(&dbgpr_lock); + dbg_proc = create_proc_entry("igov", S_IWUSR | S_IRUGO, NULL); + dbg_proc->read_proc = dbg_proc_read; +#endif + + return cpufreq_register_governor(&cpufreq_gov_interactive); + +err_freeuptask: + put_task_struct(up_task); + return -ENOMEM; +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +fs_initcall(cpufreq_interactive_init); +#else +module_init(cpufreq_interactive_init); +#endif + +static void __exit cpufreq_interactive_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_interactive); + kthread_stop(up_task); + put_task_struct(up_task); + destroy_workqueue(down_wq); +} + +module_exit(cpufreq_interactive_exit); + +MODULE_AUTHOR("Mike Chan "); +MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for " + "Latency sensitive workloads"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 977087e8d324ac7f19d3690b5ecdf65382ce2a72 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 3 Dec 2010 11:20:09 -0800 Subject: cpufreq interactive governor save/restore IRQs around cpumask spinlocks Need to use irqsave/restore spin locking for cpumasks since these are accessed in timers and in thread context. Change-Id: I4a53eaf0ced7e73b445feddba90ec11482de9126 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 6069ca20a014..4909c7bb741b 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -180,6 +180,7 @@ static void cpufreq_interactive_timer(unsigned long data) u64 now_idle; unsigned int new_freq; unsigned int index; + unsigned long flags; /* * Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time, @@ -280,18 +281,18 @@ static void cpufreq_interactive_timer(unsigned long data) if (new_freq < pcpu->target_freq) { pcpu->target_freq = new_freq; - spin_lock(&down_cpumask_lock); + spin_lock_irqsave(&down_cpumask_lock, flags); cpumask_set_cpu(data, &down_cpumask); - spin_unlock(&down_cpumask_lock); + spin_unlock_irqrestore(&down_cpumask_lock, flags); queue_work(down_wq, &freq_scale_down_work); } else { pcpu->target_freq = new_freq; #if DEBUG up_request_time = ktime_to_us(ktime_get()); #endif - spin_lock(&up_cpumask_lock); + spin_lock_irqsave(&up_cpumask_lock, flags); cpumask_set_cpu(data, &up_cpumask); - spin_unlock(&up_cpumask_lock); + spin_unlock_irqrestore(&up_cpumask_lock, flags); wake_up_process(up_task); } @@ -423,6 +424,7 @@ static int cpufreq_interactive_up_task(void *data) { unsigned int cpu; cpumask_t tmp_mask; + unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; #if DEBUG @@ -433,16 +435,16 @@ static int cpufreq_interactive_up_task(void *data) while (1) { set_current_state(TASK_INTERRUPTIBLE); - spin_lock(&up_cpumask_lock); + spin_lock_irqsave(&up_cpumask_lock, flags); if (cpumask_empty(&up_cpumask)) { - spin_unlock(&up_cpumask_lock); + spin_unlock_irqrestore(&up_cpumask_lock, flags); schedule(); if (kthread_should_stop()) break; - spin_lock(&up_cpumask_lock); + spin_lock_irqsave(&up_cpumask_lock, flags); } set_current_state(TASK_RUNNING); @@ -461,7 +463,7 @@ static int cpufreq_interactive_up_task(void *data) tmp_mask = up_cpumask; cpumask_clear(&up_cpumask); - spin_unlock(&up_cpumask_lock); + spin_unlock_irqrestore(&up_cpumask_lock, flags); for_each_cpu(cpu, &tmp_mask) { pcpu = &per_cpu(cpuinfo, cpu); @@ -488,12 +490,13 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) { unsigned int cpu; cpumask_t tmp_mask; + unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; - spin_lock(&down_cpumask_lock); + spin_lock_irqsave(&down_cpumask_lock, flags); tmp_mask = down_cpumask; cpumask_clear(&down_cpumask); - spin_unlock(&down_cpumask_lock); + spin_unlock_irqrestore(&down_cpumask_lock, flags); for_each_cpu(cpu, &tmp_mask) { pcpu = &per_cpu(cpuinfo, cpu); -- cgit v1.2.3 From e7acddc3e04566e66040d8ce47bb4be43a05da6c Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 23 Dec 2010 17:33:07 -0800 Subject: cpufreq interactive governor: fix crash on CPU shutdown Don't reference the saved copy of the CPU's cpufreq policy pointer after the governor has been stopped for the CPU. When the governor is stopped for a CPU: * Use del_timer_sync() to wait for a currently-running timer function to stop. * Delete the timer when the governor is stopped for the associated CPU, not when the last CPU is stopped. * Flush any speed down work ongoing. * Reset the timestamp that is used to tell if the timer function has had a chance to run since last idle exit. Check the governor enabled flag for the CPU before re-arming the timer from within the timer function and at idle exit (in case stopping the governor at runtime). Check the governor enabled flag for the CPU in the worker function and thread before using the policy pointer. (There is still a tiny window in the thread that needs more work to close.) Change-Id: Ifaddf7a495a8dae15a579a57bdc654f7c47f6ada Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 4909c7bb741b..81783286cad2 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -182,6 +182,11 @@ static void cpufreq_interactive_timer(unsigned long data) unsigned int index; unsigned long flags; + smp_rmb(); + + if (!pcpu->governor_enabled) + goto exit; + /* * Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time, * this lets idle exit know the current idle time sample has @@ -403,7 +408,8 @@ static void cpufreq_interactive_idle(void) * run.) */ if (timer_pending(&pcpu->cpu_timer) == 0 && - pcpu->timer_run_time >= pcpu->idle_exit_time) { + pcpu->timer_run_time >= pcpu->idle_exit_time && + pcpu->governor_enabled) { pcpu->time_in_idle = get_cpu_idle_time_us(smp_processor_id(), &pcpu->idle_exit_time); @@ -473,6 +479,11 @@ static int cpufreq_interactive_up_task(void *data) pcpu->target_freq); } + smp_rmb(); + + if (!pcpu->governor_enabled) + continue; + __cpufreq_driver_target(pcpu->policy, pcpu->target_freq, CPUFREQ_RELATION_H); @@ -500,6 +511,12 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) for_each_cpu(cpu, &tmp_mask) { pcpu = &per_cpu(cpuinfo, cpu); + + smp_rmb(); + + if (!pcpu->governor_enabled) + continue; + __cpufreq_driver_target(pcpu->policy, pcpu->target_freq, CPUFREQ_RELATION_H); @@ -570,6 +587,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *new_policy, get_cpu_idle_time_us(new_policy->cpu, &pcpu->freq_change_time); pcpu->governor_enabled = 1; + smp_wmb(); /* * Do not register the idle hook and create sysfs * entries if we have already done so. @@ -588,6 +606,16 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *new_policy, case CPUFREQ_GOV_STOP: pcpu->governor_enabled = 0; + smp_wmb(); + del_timer_sync(&pcpu->cpu_timer); + flush_work(&freq_scale_down_work); + /* + * Reset idle exit time since we may cancel the timer + * before it can run after the last idle exit time, + * to avoid tripping the check in idle exit for a timer + * that is trying to run. + */ + pcpu->idle_exit_time = 0; if (atomic_dec_return(&active_count) > 0) return 0; @@ -596,7 +624,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *new_policy, &interactive_attr_group); pm_idle = pm_idle_old; - del_timer(&pcpu->cpu_timer); break; case CPUFREQ_GOV_LIMITS: -- cgit v1.2.3 From ae5665eb25692e684769126af44b3d2c8c04cba3 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 28 Jan 2011 19:32:31 -0800 Subject: cpufreq: Prevent memory leak in cpufreq_stats on hotplug Ensures that cpufreq_stats_free_table is called before __cpufreq_remove_dev on cpu hotplug (which also occurs during suspend on SMP systems) to make sure that sysfs_remove_group can get called before the cpufreq kobj is freed. Otherwise, the sysfs file structures are leaked. Change-Id: I87e55277272f5cfad47e9e7c92630e990bb90069 Signed-off-by: Colin Cross --- drivers/cpufreq/cpufreq_stats.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index faf7c5217848..c315ec9d5686 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -317,6 +317,27 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, return 0; } +static int cpufreq_stats_create_table_cpu(unsigned int cpu) +{ + struct cpufreq_policy *policy; + struct cpufreq_frequency_table *table; + int ret = -ENODEV; + + policy = cpufreq_cpu_get(cpu); + if (!policy) + return -ENODEV; + + table = cpufreq_frequency_get_table(cpu); + if (!table) + goto out; + + ret = cpufreq_stats_create_table(policy, table); + +out: + cpufreq_cpu_put(policy); + return ret; +} + static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) @@ -335,6 +356,10 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, case CPU_DEAD_FROZEN: cpufreq_stats_free_table(cpu); break; + case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: + cpufreq_stats_create_table_cpu(cpu); + break; } return NOTIFY_OK; } -- cgit v1.2.3 From 5a6c838f9270b588ca86489684149f6801ba9ac0 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 6 Jun 2011 18:30:23 -0700 Subject: cpufreq interactive: support shared CPU scaling Change-Id: Id5267f04067bf023f6b140b4de2e88ef7287e941 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 68 ++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 28 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 81783286cad2..bcbb7ac8306c 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -568,26 +568,34 @@ static struct attribute_group interactive_attr_group = { .name = "interactive", }; -static int cpufreq_governor_interactive(struct cpufreq_policy *new_policy, +static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event) { int rc; - struct cpufreq_interactive_cpuinfo *pcpu = - &per_cpu(cpuinfo, new_policy->cpu); + unsigned int j; + struct cpufreq_interactive_cpuinfo *pcpu; + struct cpufreq_frequency_table *freq_table; switch (event) { case CPUFREQ_GOV_START: - if (!cpu_online(new_policy->cpu)) + if (!cpu_online(policy->cpu)) return -EINVAL; - pcpu->policy = new_policy; - pcpu->freq_table = cpufreq_frequency_get_table(new_policy->cpu); - pcpu->target_freq = new_policy->cur; - pcpu->freq_change_time_in_idle = - get_cpu_idle_time_us(new_policy->cpu, + freq_table = + cpufreq_frequency_get_table(policy->cpu); + + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + pcpu->policy = policy; + pcpu->target_freq = policy->cur; + pcpu->freq_table = freq_table; + pcpu->freq_change_time_in_idle = + get_cpu_idle_time_us(j, &pcpu->freq_change_time); - pcpu->governor_enabled = 1; - smp_wmb(); + pcpu->governor_enabled = 1; + smp_wmb(); + } + /* * Do not register the idle hook and create sysfs * entries if we have already done so. @@ -605,18 +613,22 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *new_policy, break; case CPUFREQ_GOV_STOP: - pcpu->governor_enabled = 0; - smp_wmb(); - del_timer_sync(&pcpu->cpu_timer); - flush_work(&freq_scale_down_work); - /* - * Reset idle exit time since we may cancel the timer - * before it can run after the last idle exit time, - * to avoid tripping the check in idle exit for a timer - * that is trying to run. - */ - pcpu->idle_exit_time = 0; + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + pcpu->governor_enabled = 0; + smp_wmb(); + del_timer_sync(&pcpu->cpu_timer); + /* + * Reset idle exit time since we may cancel the timer + * before it can run after the last idle exit time, + * to avoid tripping the check in idle exit for a timer + * that is trying to run. + */ + pcpu->idle_exit_time = 0; + } + + flush_work(&freq_scale_down_work); if (atomic_dec_return(&active_count) > 0) return 0; @@ -627,12 +639,12 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *new_policy, break; case CPUFREQ_GOV_LIMITS: - if (new_policy->max < new_policy->cur) - __cpufreq_driver_target(new_policy, - new_policy->max, CPUFREQ_RELATION_H); - else if (new_policy->min > new_policy->cur) - __cpufreq_driver_target(new_policy, - new_policy->min, CPUFREQ_RELATION_L); + if (policy->max < policy->cur) + __cpufreq_driver_target(policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > policy->cur) + __cpufreq_driver_target(policy, + policy->min, CPUFREQ_RELATION_L); break; } return 0; -- cgit v1.2.3 From 5cdaac0eca9378c7ce71352dc99ba9b69c7f6342 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Tue, 28 Jun 2011 10:40:30 -0700 Subject: cpufreq: interactive: remove debug trace code Remove debug trace code in preparation of upstreaming Change-Id: I0905885e75031f5e9d7cb06878fb68c1fd06d4fe Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 164 ++-------------------------------- 1 file changed, 6 insertions(+), 158 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index bcbb7ac8306c..26a423bb3da0 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -67,93 +67,6 @@ static unsigned long go_maxspeed_load; #define DEFAULT_MIN_SAMPLE_TIME 80000; static unsigned long min_sample_time; -#define DEBUG 0 -#define BUFSZ 128 - -#if DEBUG -#include - -struct dbgln { - int cpu; - unsigned long jiffy; - unsigned long run; - char buf[BUFSZ]; -}; - -#define NDBGLNS 256 - -static struct dbgln dbgbuf[NDBGLNS]; -static int dbgbufs; -static int dbgbufe; -static struct proc_dir_entry *dbg_proc; -static spinlock_t dbgpr_lock; - -static u64 up_request_time; -static unsigned int up_max_latency; - -static void dbgpr(char *fmt, ...) -{ - va_list args; - int n; - unsigned long flags; - - spin_lock_irqsave(&dbgpr_lock, flags); - n = dbgbufe; - va_start(args, fmt); - vsnprintf(dbgbuf[n].buf, BUFSZ, fmt, args); - va_end(args); - dbgbuf[n].cpu = smp_processor_id(); - dbgbuf[n].run = nr_running(); - dbgbuf[n].jiffy = jiffies; - - if (++dbgbufe >= NDBGLNS) - dbgbufe = 0; - - if (dbgbufe == dbgbufs) - if (++dbgbufs >= NDBGLNS) - dbgbufs = 0; - - spin_unlock_irqrestore(&dbgpr_lock, flags); -} - -static void dbgdump(void) -{ - int i, j; - unsigned long flags; - static struct dbgln prbuf[NDBGLNS]; - - spin_lock_irqsave(&dbgpr_lock, flags); - i = dbgbufs; - j = dbgbufe; - memcpy(prbuf, dbgbuf, sizeof(dbgbuf)); - dbgbufs = 0; - dbgbufe = 0; - spin_unlock_irqrestore(&dbgpr_lock, flags); - - while (i != j) - { - printk("%lu %d %lu %s", - prbuf[i].jiffy, prbuf[i].cpu, prbuf[i].run, - prbuf[i].buf); - if (++i == NDBGLNS) - i = 0; - } -} - -static int dbg_proc_read(char *buffer, char **start, off_t offset, - int count, int *peof, void *dat) -{ - printk("max up_task latency=%uus\n", up_max_latency); - dbgdump(); - *peof = 1; - return 0; -} - - -#else -#define dbgpr(...) do {} while (0) -#endif - static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -202,16 +115,8 @@ static void cpufreq_interactive_timer(unsigned long data) smp_wmb(); /* If we raced with cancelling a timer, skip. */ - if (!idle_exit_time) { - dbgpr("timer %d: no valid idle exit sample\n", (int) data); + if (!idle_exit_time) goto exit; - } - -#if DEBUG - if ((int) jiffies - (int) pcpu->cpu_timer.expires >= 10) - dbgpr("timer %d: late by %d ticks\n", - (int) data, jiffies - pcpu->cpu_timer.expires); -#endif delta_idle = (unsigned int) cputime64_sub(now_idle, time_in_idle); delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, @@ -220,11 +125,8 @@ static void cpufreq_interactive_timer(unsigned long data) /* * If timer ran less than 1ms after short-term sample started, retry. */ - if (delta_time < 1000) { - dbgpr("timer %d: time delta %u too short exit=%llu now=%llu\n", (int) data, - delta_time, idle_exit_time, pcpu->timer_run_time); + if (delta_time < 1000) goto rearm; - } if (delta_idle > delta_time) cpu_load = 0; @@ -258,17 +160,15 @@ static void cpufreq_interactive_timer(unsigned long data) if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_H, &index)) { - dbgpr("timer %d: cpufreq_frequency_table_target error\n", (int) data); + pr_warn_once("timer %d: cpufreq_frequency_table_target error\n", + (int) data); goto rearm; } new_freq = pcpu->freq_table[index].frequency; if (pcpu->target_freq == new_freq) - { - dbgpr("timer %d: load=%d, already at %d\n", (int) data, cpu_load, new_freq); goto rearm_if_notmax; - } /* * Do not scale down unless we have been at this frequency for the @@ -276,14 +176,10 @@ static void cpufreq_interactive_timer(unsigned long data) */ if (new_freq < pcpu->target_freq) { if (cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time) < - min_sample_time) { - dbgpr("timer %d: load=%d cur=%d tgt=%d not yet\n", (int) data, cpu_load, pcpu->target_freq, new_freq); + min_sample_time) goto rearm; - } } - dbgpr("timer %d: load=%d cur=%d tgt=%d queue\n", (int) data, cpu_load, pcpu->target_freq, new_freq); - if (new_freq < pcpu->target_freq) { pcpu->target_freq = new_freq; spin_lock_irqsave(&down_cpumask_lock, flags); @@ -292,9 +188,6 @@ static void cpufreq_interactive_timer(unsigned long data) queue_work(down_wq, &freq_scale_down_work); } else { pcpu->target_freq = new_freq; -#if DEBUG - up_request_time = ktime_to_us(ktime_get()); -#endif spin_lock_irqsave(&up_cpumask_lock, flags); cpumask_set_cpu(data, &up_cpumask); spin_unlock_irqrestore(&up_cpumask_lock, flags); @@ -319,10 +212,8 @@ rearm: if (pcpu->target_freq == pcpu->policy->min) { smp_rmb(); - if (pcpu->idling) { - dbgpr("timer %d: cpu idle, don't re-arm\n", (int) data); + if (pcpu->idling) goto exit; - } pcpu->timer_idlecancel = 1; } @@ -330,7 +221,6 @@ rearm: pcpu->time_in_idle = get_cpu_idle_time_us( data, &pcpu->idle_exit_time); mod_timer(&pcpu->cpu_timer, jiffies + 2); - dbgpr("timer %d: set timer for %lu exit=%llu\n", (int) data, pcpu->cpu_timer.expires, pcpu->idle_exit_time); } exit: @@ -367,9 +257,6 @@ static void cpufreq_interactive_idle(void) smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; mod_timer(&pcpu->cpu_timer, jiffies + 2); - dbgpr("idle: enter at %d, set timer for %lu exit=%llu\n", - pcpu->target_freq, pcpu->cpu_timer.expires, - pcpu->idle_exit_time); } #endif } else { @@ -380,7 +267,6 @@ static void cpufreq_interactive_idle(void) * CPU didn't go busy; we'll recheck things upon idle exit. */ if (pending && pcpu->timer_idlecancel) { - dbgpr("idle: cancel timer for %lu\n", pcpu->cpu_timer.expires); del_timer(&pcpu->cpu_timer); /* * Ensure last timer run time is after current idle @@ -415,13 +301,6 @@ static void cpufreq_interactive_idle(void) &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; mod_timer(&pcpu->cpu_timer, jiffies + 2); - dbgpr("idle: exit, set timer for %lu exit=%llu\n", pcpu->cpu_timer.expires, pcpu->idle_exit_time); -#if DEBUG - } else if (timer_pending(&pcpu->cpu_timer) == 0 && - pcpu->timer_run_time < pcpu->idle_exit_time) { - dbgpr("idle: timer not run yet: exit=%llu tmrrun=%llu\n", - pcpu->idle_exit_time, pcpu->timer_run_time); -#endif } } @@ -433,12 +312,6 @@ static int cpufreq_interactive_up_task(void *data) unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; -#if DEBUG - u64 now; - u64 then; - unsigned int lat; -#endif - while (1) { set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&up_cpumask_lock, flags); @@ -455,18 +328,6 @@ static int cpufreq_interactive_up_task(void *data) set_current_state(TASK_RUNNING); -#if DEBUG - then = up_request_time; - now = ktime_to_us(ktime_get()); - - if (now > then) { - lat = ktime_to_us(ktime_get()) - then; - - if (lat > up_max_latency) - up_max_latency = lat; - } -#endif - tmp_mask = up_cpumask; cpumask_clear(&up_cpumask); spin_unlock_irqrestore(&up_cpumask_lock, flags); @@ -474,11 +335,6 @@ static int cpufreq_interactive_up_task(void *data) for_each_cpu(cpu, &tmp_mask) { pcpu = &per_cpu(cpuinfo, cpu); - if (nr_running() == 1) { - dbgpr("up %d: tgt=%d nothing else running\n", cpu, - pcpu->target_freq); - } - smp_rmb(); if (!pcpu->governor_enabled) @@ -490,7 +346,6 @@ static int cpufreq_interactive_up_task(void *data) pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); - dbgpr("up %d: set tgt=%d (actual=%d)\n", cpu, pcpu->target_freq, pcpu->policy->cur); } } @@ -523,7 +378,6 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); - dbgpr("down %d: set tgt=%d (actual=%d)\n", cpu, pcpu->target_freq, pcpu->policy->cur); } } @@ -688,12 +542,6 @@ static int __init cpufreq_interactive_init(void) spin_lock_init(&up_cpumask_lock); spin_lock_init(&down_cpumask_lock); -#if DEBUG - spin_lock_init(&dbgpr_lock); - dbg_proc = create_proc_entry("igov", S_IWUSR | S_IRUGO, NULL); - dbg_proc->read_proc = dbg_proc_read; -#endif - return cpufreq_register_governor(&cpufreq_gov_interactive); err_freeuptask: -- cgit v1.2.3 From 841212573f95bb02a1b447211300fd9b31e5fa80 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Tue, 28 Jun 2011 09:58:39 -0700 Subject: cpufreq: interactive: use idle notifier Convert interactive governor to use idle notifier instead of hooking pm_idle directly. Change-Id: I47e007f330468ac559240a0ae8a3cb06a89ccb67 Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 37 ++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 26a423bb3da0..9100c3b6adf5 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -28,7 +28,6 @@ #include -static void (*pm_idle_old)(void); static atomic_t active_count = ATOMIC_INIT(0); struct cpufreq_interactive_cpuinfo { @@ -227,14 +226,13 @@ exit: return; } -static void cpufreq_interactive_idle(void) +static void cpufreq_interactive_idle_start(void) { struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, smp_processor_id()); int pending; if (!pcpu->governor_enabled) { - pm_idle_old(); return; } @@ -278,7 +276,13 @@ static void cpufreq_interactive_idle(void) } } - pm_idle_old(); +} + +static void cpufreq_interactive_idle_end(void) +{ + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, smp_processor_id()); + pcpu->idling = 0; smp_wmb(); @@ -462,8 +466,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (rc) return rc; - pm_idle_old = pm_idle; - pm_idle = cpufreq_interactive_idle; break; case CPUFREQ_GOV_STOP: @@ -489,7 +491,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, sysfs_remove_group(cpufreq_global_kobject, &interactive_attr_group); - pm_idle = pm_idle_old; break; case CPUFREQ_GOV_LIMITS: @@ -504,6 +505,26 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, return 0; } +static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, + unsigned long val, + void *data) +{ + switch (val) { + case IDLE_START: + cpufreq_interactive_idle_start(); + break; + case IDLE_END: + cpufreq_interactive_idle_end(); + break; + } + + return 0; +} + +static struct notifier_block cpufreq_interactive_idle_nb = { + .notifier_call = cpufreq_interactive_idle_notifier, +}; + static int __init cpufreq_interactive_init(void) { unsigned int i; @@ -542,6 +563,8 @@ static int __init cpufreq_interactive_init(void) spin_lock_init(&up_cpumask_lock); spin_lock_init(&down_cpumask_lock); + idle_notifier_register(&cpufreq_interactive_idle_nb); + return cpufreq_register_governor(&cpufreq_gov_interactive); err_freeuptask: -- cgit v1.2.3 From a7c29e35d3c78f9180eeedf9a740479a5f50aa5a Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Thu, 30 Jun 2011 16:59:19 -0700 Subject: cpufreq: interactive: Add error checking on sysfs interfaces This adds better error checking on tunable parameters on sysfs interfaces. Also fixes return value from these functions, previously on success they would return 0 which would cause a infinite loop. Change-Id: Ic05038492166f8673d007202092471f98a2f0dfa Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 9100c3b6adf5..fe20e3142c6e 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -394,7 +394,14 @@ static ssize_t show_go_maxspeed_load(struct kobject *kobj, static ssize_t store_go_maxspeed_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { - return strict_strtoul(buf, 0, &go_maxspeed_load); + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + go_maxspeed_load = val; + return count; } static struct global_attr go_maxspeed_load_attr = __ATTR(go_maxspeed_load, 0644, @@ -409,7 +416,14 @@ static ssize_t show_min_sample_time(struct kobject *kobj, static ssize_t store_min_sample_time(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { - return strict_strtoul(buf, 0, &min_sample_time); + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + min_sample_time = val; + return count; } static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, -- cgit v1.2.3 From 224cea23b09eab33edd4be419b7cbc1a0748dc53 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Thu, 30 Jun 2011 23:54:07 -0700 Subject: cpufreq: interactive: add sysfs control for timer rate Add a new sysfs control that tunes the rate of the timer used to increase cpu frequency Change-Id: I1aa13ae54bb43aff5b3688984d2955f56aae1658 Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 36 ++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index fe20e3142c6e..6a98631d2947 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -66,6 +66,12 @@ static unsigned long go_maxspeed_load; #define DEFAULT_MIN_SAMPLE_TIME 80000; static unsigned long min_sample_time; +/* + * The sample rate of the timer used to increase frequency + */ +#define DEFAULT_TIMER_RATE 30000; +static unsigned long timer_rate; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -219,7 +225,7 @@ rearm: pcpu->time_in_idle = get_cpu_idle_time_us( data, &pcpu->idle_exit_time); - mod_timer(&pcpu->cpu_timer, jiffies + 2); + mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); } exit: @@ -254,7 +260,7 @@ static void cpufreq_interactive_idle_start(void) pcpu->time_in_idle = get_cpu_idle_time_us( smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, jiffies + 2); + mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); } #endif } else { @@ -304,7 +310,7 @@ static void cpufreq_interactive_idle_end(void) get_cpu_idle_time_us(smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, jiffies + 2); + mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); } } @@ -429,9 +435,32 @@ static ssize_t store_min_sample_time(struct kobject *kobj, static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, show_min_sample_time, store_min_sample_time); +static ssize_t show_timer_rate(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", timer_rate); +} + +static ssize_t store_timer_rate(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + timer_rate = val; + return count; +} + +static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, + show_timer_rate, store_timer_rate); + static struct attribute *interactive_attributes[] = { &go_maxspeed_load_attr.attr, &min_sample_time_attr.attr, + &timer_rate_attr.attr, NULL, }; @@ -547,6 +576,7 @@ static int __init cpufreq_interactive_init(void) go_maxspeed_load = DEFAULT_GO_MAXSPEED_LOAD; min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + timer_rate = DEFAULT_TIMER_RATE; /* Initalize per-cpu timers */ for_each_possible_cpu(i) { -- cgit v1.2.3 From cd66822eba1bd5c5d141051311e66cb367a71bdf Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Fri, 1 Jul 2011 11:17:39 -0700 Subject: cpufreq: Add more verbose description of interactive governor Update the Kconfig help paragraph to give more detail about interactive governor. Change-Id: I607b817b370accac3a685001649a15e2f7894f59 Signed-off-by: Allen Martin --- drivers/cpufreq/Kconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 2cdc38b5e973..57f96ebbce4b 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -172,6 +172,17 @@ config CPU_FREQ_GOV_INTERACTIVE 'interactive' - This driver adds a dynamic cpufreq policy governor designed for latency-sensitive workloads. + This governor attempts to reduce the latency of clock + increases so that the system is more responsive to + interactive workloads. + + To compile this driver as a module, choose M here: the + module will be called cpufreq_interactive. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + config CPU_FREQ_GOV_CONSERVATIVE tristate "'conservative' cpufreq governor" depends on CPU_FREQ -- cgit v1.2.3 From 577ac660666e3abb76a15ae00e152a2959a76482 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Fri, 1 Jul 2011 11:19:14 -0700 Subject: cpufreq: interactive: cleanup checkpatch warnings Cleanup some style warnings reported by checkpatch Change-Id: Ie2e6903d52867fb3347e009d7efa3bc4ca755cea Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 6a98631d2947..d9c6d5b0db4e 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -139,7 +139,7 @@ static void cpufreq_interactive_timer(unsigned long data) cpu_load = 100 * (delta_time - delta_idle) / delta_time; delta_idle = (unsigned int) cputime64_sub(now_idle, - pcpu->freq_change_time_in_idle); + pcpu->freq_change_time_in_idle); delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time); @@ -180,8 +180,8 @@ static void cpufreq_interactive_timer(unsigned long data) * minimum sample time. */ if (new_freq < pcpu->target_freq) { - if (cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time) < - min_sample_time) + if (cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time) + < min_sample_time) goto rearm; } @@ -238,9 +238,8 @@ static void cpufreq_interactive_idle_start(void) &per_cpu(cpuinfo, smp_processor_id()); int pending; - if (!pcpu->governor_enabled) { + if (!pcpu->governor_enabled) return; - } pcpu->idling = 1; smp_wmb(); @@ -598,7 +597,7 @@ static int __init cpufreq_interactive_init(void) warm cache (probably doesn't matter much). */ down_wq = alloc_workqueue("knteractive_down", 0, 1); - if (! down_wq) + if (!down_wq) goto err_freeuptask; INIT_WORK(&freq_scale_down_work, -- cgit v1.2.3 From 26383eba7938495ed477e75c6ba88017fd47c442 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Tue, 19 Jul 2011 14:56:21 -0700 Subject: cpufreq: interactive: fix checkpatch warnings on long lines Fix up checkpatch warning introduced by long lines in timer_rate patch. Change-Id: I22b105dafb1b49390799bb7577464da03f0f8afb Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index d9c6d5b0db4e..f90d3a5d52ee 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -225,7 +225,8 @@ rearm: pcpu->time_in_idle = get_cpu_idle_time_us( data, &pcpu->idle_exit_time); - mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); } exit: @@ -259,7 +260,8 @@ static void cpufreq_interactive_idle_start(void) pcpu->time_in_idle = get_cpu_idle_time_us( smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); } #endif } else { @@ -309,7 +311,8 @@ static void cpufreq_interactive_idle_end(void) get_cpu_idle_time_us(smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); } } -- cgit v1.2.3 From 88dd0e29b6390220fca5c2800bf16feb245004c7 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 22 Jun 2011 14:34:23 -0700 Subject: cpufreq: interactive: Fix handling of SMP where CPUs must scale in tandem The interactive governor relies on quirks of the Tegra 2 cpufreq implementation for handling SMP systems where the CPUs do not have separate rate controls. It needs to determine the maximum rate for all CPUs covered by the policy and set that speed. Change-Id: I1ed9fa171e5a9c45a1fa5944e3fa823eb157e81f Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 49 ++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 9 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index f90d3a5d52ee..d8f62f402bf2 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -55,6 +56,7 @@ static cpumask_t up_cpumask; static spinlock_t up_cpumask_lock; static cpumask_t down_cpumask; static spinlock_t down_cpumask_lock; +static struct mutex set_speed_lock; /* Go to max speed when CPU load at or above this value. */ #define DEFAULT_GO_MAXSPEED_LOAD 85 @@ -339,22 +341,36 @@ static int cpufreq_interactive_up_task(void *data) } set_current_state(TASK_RUNNING); - tmp_mask = up_cpumask; cpumask_clear(&up_cpumask); spin_unlock_irqrestore(&up_cpumask_lock, flags); for_each_cpu(cpu, &tmp_mask) { - pcpu = &per_cpu(cpuinfo, cpu); + unsigned int j; + unsigned int max_freq = 0; + pcpu = &per_cpu(cpuinfo, cpu); smp_rmb(); if (!pcpu->governor_enabled) continue; - __cpufreq_driver_target(pcpu->policy, - pcpu->target_freq, - CPUFREQ_RELATION_H); + mutex_lock(&set_speed_lock); + + for_each_cpu(j, pcpu->policy->cpus) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, j); + + if (pjcpu->target_freq > max_freq) + max_freq = pjcpu->target_freq; + } + + if (max_freq != pcpu->policy->cur) + __cpufreq_driver_target(pcpu->policy, + max_freq, + CPUFREQ_RELATION_H); + mutex_unlock(&set_speed_lock); + pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); @@ -377,16 +393,30 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) spin_unlock_irqrestore(&down_cpumask_lock, flags); for_each_cpu(cpu, &tmp_mask) { - pcpu = &per_cpu(cpuinfo, cpu); + unsigned int j; + unsigned int max_freq = 0; + pcpu = &per_cpu(cpuinfo, cpu); smp_rmb(); if (!pcpu->governor_enabled) continue; - __cpufreq_driver_target(pcpu->policy, - pcpu->target_freq, - CPUFREQ_RELATION_H); + mutex_lock(&set_speed_lock); + + for_each_cpu(j, pcpu->policy->cpus) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, j); + + if (pjcpu->target_freq > max_freq) + max_freq = pjcpu->target_freq; + } + + if (max_freq != pcpu->policy->cur) + __cpufreq_driver_target(pcpu->policy, max_freq, + CPUFREQ_RELATION_H); + + mutex_unlock(&set_speed_lock); pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); @@ -608,6 +638,7 @@ static int __init cpufreq_interactive_init(void) spin_lock_init(&up_cpumask_lock); spin_lock_init(&down_cpumask_lock); + mutex_init(&set_speed_lock); idle_notifier_register(&cpufreq_interactive_idle_nb); -- cgit v1.2.3 From c8913146a4f0d725ad13112d5d5612f9b202597a Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Wed, 5 Oct 2011 14:04:57 -0700 Subject: cpufreq: interactive: fix possible Division by zero. The following dump was seen sometimes while resuming, the only division by zero on this function can happen after delta_time is reassigned, since at the start of the function, there is jump that protects against values less than 1000. After that, If delta_time and delta_idle == 0, we will hit a div 0 Division by zero in kernel. Backtrace: [] (dump_backtrace+0x0/0x110) from [] (dump_stack+0x18/0x1c) r6:010f3000 r5:c113dfb0 r4:c004afb0 r3:c6ff0000 [] (dump_stack+0x0/0x1c) from [] (__div0+0x1c/0x20) [] (__div0+0x0/0x20) from [] (Ldiv0+0x8/0x10) [] (cpufreq_interactive_timer+0x0/0x2c0) from [] (run_timer_softirq+0x154/0x260) [] (run_timer_softirq+0x0/0x260) from [] (__do_softirq+0xc8/0x194) [] (__do_softirq+0x0/0x194) from [] (irq_exit+0xb4/0xb8) [] (irq_exit+0x0/0xb8) from [] (ipi_timer+0x44/0x48) r4:c004a040 r3:00000001 [] (ipi_timer+0x0/0x48) from [] (do_local_timer+0x68/0x84) r5:c004ae2c r4:c07991e8 [] (do_local_timer+0x0/0x84) from [] (__irq_svc+0x48/0xe0) Change-Id: I639882db67b8d711c5710778ebc212f0f6a998e3 Acked-by: Nishanth Menon Signed-off-by: Axel Haslam --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index d8f62f402bf2..d60cb380add1 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -145,7 +145,7 @@ static void cpufreq_interactive_timer(unsigned long data) delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time); - if (delta_idle > delta_time) + if ((delta_time == 0) || (delta_idle > delta_time)) load_since_change = 0; else load_since_change = -- cgit v1.2.3 From 986a0a03503382f3b70bb9db890f548cd53ebfa5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Sun, 16 Oct 2011 22:40:29 -0700 Subject: cpufreq: interactive governor: default timer 10ms, maxspeed load 95% Modify default timer from 30ms to 10ms, sampling 2 jiffies after idle exit on ARM as in Honeycomb. Modify default go_maxspeed_load from 85% loaded to 95% loaded, for use in phones where power savings is more important (tablets may be best served overriding this). Change-Id: I3361a6279979bfae1df5262666a2e30ea7a44328 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index d60cb380add1..adf5f6856299 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -59,7 +59,7 @@ static spinlock_t down_cpumask_lock; static struct mutex set_speed_lock; /* Go to max speed when CPU load at or above this value. */ -#define DEFAULT_GO_MAXSPEED_LOAD 85 +#define DEFAULT_GO_MAXSPEED_LOAD 95 static unsigned long go_maxspeed_load; /* @@ -71,7 +71,7 @@ static unsigned long min_sample_time; /* * The sample rate of the timer used to increase frequency */ -#define DEFAULT_TIMER_RATE 30000; +#define DEFAULT_TIMER_RATE 10000; static unsigned long timer_rate; static int cpufreq_governor_interactive(struct cpufreq_policy *policy, -- cgit v1.2.3 From d3556e45b9d1c461968e646abedecd081c69bdcd Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 19 Oct 2011 13:12:10 -0700 Subject: power: cpufreq interactive governor: use default sample time 20ms Lower the default time at which a higher speed is allowed to run before lowering based on lower CPU load from 80ms to 20ms. Most Android devices should trade power for performance here, although tablets and non-battery-powered devices may want to override this default. Change-Id: I1a4f7faeca12793c51d5b92db30a63cca8d4f1be Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index adf5f6856299..e1d5015f1f5e 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -65,7 +65,7 @@ static unsigned long go_maxspeed_load; /* * The minimum amount of time to spend at a frequency before we can ramp down. */ -#define DEFAULT_MIN_SAMPLE_TIME 80000; +#define DEFAULT_MIN_SAMPLE_TIME 20000; static unsigned long min_sample_time; /* -- cgit v1.2.3 From c50272ac294c648c23e9b90054f93efac8da5544 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 27 Oct 2011 18:25:59 -0700 Subject: cpufreq: interactive governor: apply intermediate load on current speed Calculate intermediate speed by applyng CPU load to current speed, not max speed. Change-Id: Idecf598b9a203b07c989c5d9e9c6efc67a1afc2e Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index e1d5015f1f5e..a085fc86733b 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -162,7 +162,7 @@ static void cpufreq_interactive_timer(unsigned long data) if (cpu_load >= go_maxspeed_load) new_freq = pcpu->policy->max; else - new_freq = pcpu->policy->max * cpu_load / 100; + new_freq = pcpu->policy->cur * cpu_load / 100; if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_H, -- cgit v1.2.3 From cf9f9c59cf46c60fe41712a057b70f754c46cca6 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 3 Nov 2011 21:22:54 -0700 Subject: cpufreq: interactive governor: scale to max only if at min speed Change-Id: Ieffb2aa56b5290036285c948718be7be0d3af9e8 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index a085fc86733b..72ae6533a2d4 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -159,10 +159,14 @@ static void cpufreq_interactive_timer(unsigned long data) if (load_since_change > cpu_load) cpu_load = load_since_change; - if (cpu_load >= go_maxspeed_load) - new_freq = pcpu->policy->max; - else + if (cpu_load >= go_maxspeed_load) { + if (pcpu->policy->cur == pcpu->policy->min) + new_freq = pcpu->policy->max; + else + new_freq = pcpu->policy->max * cpu_load / 100; + } else { new_freq = pcpu->policy->cur * cpu_load / 100; + } if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_H, -- cgit v1.2.3 From d48fe37bd7413252e3fa39d6497657ade72e84bc Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 8 Nov 2011 19:54:07 -0800 Subject: cpufreq: interactive governor: go to intermediate hi speed before max * Add attribute hispeed_freq, which defaults to max. * Rename go_maxspeed_load to go_hispeed_load. * If hit go_hispeed_load and at min speed, go to hispeed_freq; if hit go_hispeed_load and already above min speed go to max speed. Change-Id: I1050dec5f013fc1177387352ba787a7e1c68703e Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 57 +++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 13 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 72ae6533a2d4..859fdc14166f 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -58,9 +58,12 @@ static cpumask_t down_cpumask; static spinlock_t down_cpumask_lock; static struct mutex set_speed_lock; -/* Go to max speed when CPU load at or above this value. */ -#define DEFAULT_GO_MAXSPEED_LOAD 95 -static unsigned long go_maxspeed_load; +/* Hi speed to bump to from lo speed when load burst (default max) */ +static u64 hispeed_freq; + +/* Go to hi speed when CPU load at or above this value. */ +#define DEFAULT_GO_HISPEED_LOAD 95 +static unsigned long go_hispeed_load; /* * The minimum amount of time to spend at a frequency before we can ramp down. @@ -159,9 +162,9 @@ static void cpufreq_interactive_timer(unsigned long data) if (load_since_change > cpu_load) cpu_load = load_since_change; - if (cpu_load >= go_maxspeed_load) { + if (cpu_load >= go_hispeed_load) { if (pcpu->policy->cur == pcpu->policy->min) - new_freq = pcpu->policy->max; + new_freq = hispeed_freq; else new_freq = pcpu->policy->max * cpu_load / 100; } else { @@ -427,13 +430,37 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) } } -static ssize_t show_go_maxspeed_load(struct kobject *kobj, +static ssize_t show_hispeed_freq(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%llu\n", hispeed_freq); +} + +static ssize_t store_hispeed_freq(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + u64 val; + + ret = strict_strtoull(buf, 0, &val); + if (ret < 0) + return ret; + hispeed_freq = val; + return count; +} + +static struct global_attr hispeed_freq_attr = __ATTR(hispeed_freq, 0644, + show_hispeed_freq, store_hispeed_freq); + + +static ssize_t show_go_hispeed_load(struct kobject *kobj, struct attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", go_maxspeed_load); + return sprintf(buf, "%lu\n", go_hispeed_load); } -static ssize_t store_go_maxspeed_load(struct kobject *kobj, +static ssize_t store_go_hispeed_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { int ret; @@ -442,12 +469,12 @@ static ssize_t store_go_maxspeed_load(struct kobject *kobj, ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - go_maxspeed_load = val; + go_hispeed_load = val; return count; } -static struct global_attr go_maxspeed_load_attr = __ATTR(go_maxspeed_load, 0644, - show_go_maxspeed_load, store_go_maxspeed_load); +static struct global_attr go_hispeed_load_attr = __ATTR(go_hispeed_load, 0644, + show_go_hispeed_load, store_go_hispeed_load); static ssize_t show_min_sample_time(struct kobject *kobj, struct attribute *attr, char *buf) @@ -494,7 +521,8 @@ static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, show_timer_rate, store_timer_rate); static struct attribute *interactive_attributes[] = { - &go_maxspeed_load_attr.attr, + &hispeed_freq_attr.attr, + &go_hispeed_load_attr.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, NULL, @@ -533,6 +561,9 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, smp_wmb(); } + if (!hispeed_freq) + hispeed_freq = policy->max; + /* * Do not register the idle hook and create sysfs * entries if we have already done so. @@ -610,7 +641,7 @@ static int __init cpufreq_interactive_init(void) struct cpufreq_interactive_cpuinfo *pcpu; struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; - go_maxspeed_load = DEFAULT_GO_MAXSPEED_LOAD; + go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; min_sample_time = DEFAULT_MIN_SAMPLE_TIME; timer_rate = DEFAULT_TIMER_RATE; -- cgit v1.2.3 From dea7398ad29e1bae1e00145e7eadd3cb1e690b16 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 9 Nov 2011 16:56:18 -0800 Subject: cpufreq: interactive governor: default 20ms timer Change-Id: Ie9952f07b38667f2932474090044195c57976faa Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 859fdc14166f..45266d5b6cd5 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -68,13 +69,13 @@ static unsigned long go_hispeed_load; /* * The minimum amount of time to spend at a frequency before we can ramp down. */ -#define DEFAULT_MIN_SAMPLE_TIME 20000; +#define DEFAULT_MIN_SAMPLE_TIME 20 * USEC_PER_MSEC static unsigned long min_sample_time; /* * The sample rate of the timer used to increase frequency */ -#define DEFAULT_TIMER_RATE 10000; +#define DEFAULT_TIMER_RATE 20 * USEC_PER_MSEC static unsigned long timer_rate; static int cpufreq_governor_interactive(struct cpufreq_policy *policy, -- cgit v1.2.3