Added Interactive Governor

file:aed082f49d09894be277ebcbfef898cf75ee1074 -> file:3fc9cfec2be73ad28bb4b0e48c3c66d5710d10ab
--- a/Documentation/cpu-freq/governors.txt
+++ b/Documentation/cpu-freq/governors.txt
@@ -28,6 +28,7 @@ Contents:
2.3 Userspace
2.4 Ondemand
2.5 Conservative
+2.6 Interactive
3. The Governor Interface in the CPUfreq Core
@@ -182,6 +183,27 @@ governor but for the opposite direction.
default value of '20' it means that if the CPU usage needs to be below
20% between samples to have the frequency decreased.
+2.6 Interactive
+---------------
+
+The CPUfreq governor "interactive" is designed for low latency,
+interactive workloads. This governor sets the CPU speed depending on
+usage, similar to "ondemand" and "conservative" governors. However
+there is no polling, or 'sample_rate' required to scale the CPU up.
+
+Sampling CPU load every X ms can lead to under powering the CPU
+for X ms, leading to dropped framerate, stuttering UI etc..
+
+Scaling the CPU up is done when coming out of idle, and like "ondemand"
+scaling up will always go to MAX, then step down based off of cpu load.
+
+There is only one tuneable value for this governor:
+
+min_sample_time: The ammount of time the CPU must spend (in uS)
+at the current frequency before scaling DOWN. This is done to
+more accurately determine the cpu workload and the best speed for that
+workload. The default is 50ms.
+
3. The Governor Interface in the CPUfreq Core
=============================================
file:a8c8d9c19d740d4686e312be1ebbf1f4b15967e3 -> file:9cd024fea297b51f6a35a2dec6ecbd8e3dbb5169
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -110,6 +110,14 @@ 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 'interactive' governor as default. This gets full cpu frequency
+ scaling for workloads that are latency sensitive, typically interactive
+ workloads..
endchoice
config CPU_FREQ_GOV_PERFORMANCE
@@ -167,6 +175,13 @@ 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 low latency burst workloads. Scaling it done when coming
+ out of idle instead of polling.
+
config CPU_FREQ_GOV_CONSERVATIVE
tristate "'conservative' cpufreq governor"
depends on CPU_FREQ
file:71fc3b4173f10d2d81930ae003445f0037fb1e7d -> file:30629f7dc747c88e72885a8ead04fddfe3607a06
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) +=
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
file:580707a1bab24832128b730862c8010b0448f143(new)
--- /dev/null
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -0,0 +1,324 @@
+/*
+ * 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 <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/cpufreq.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/tick.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+
+#include <asm/cputime.h>
+
+static void (*pm_idle_old)(void);
+static atomic_t active_count = ATOMIC_INIT(0);
+
+static DEFINE_PER_CPU(struct timer_list, cpu_timer);
+
+static DEFINE_PER_CPU(u64, time_in_idle);
+static DEFINE_PER_CPU(u64, idle_exit_time);
+
+static struct cpufreq_policy *policy;
+static unsigned int target_freq;
+
+/* Workqueues handle frequency scaling */
+static struct workqueue_struct *up_wq;
+static struct workqueue_struct *down_wq;
+static struct work_struct freq_scale_work;
+
+static u64 freq_change_time;
+static u64 freq_change_time_in_idle;
+
+static cpumask_t work_cpumask;
+
+/*
+ * The minimum ammount of time to spend at a frequency before we can ramp down,
+ * default is 50ms.
+ */
+#define DEFAULT_MIN_SAMPLE_TIME 50000;
+static unsigned long min_sample_time;
+
+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)
+{
+ u64 delta_idle;
+ u64 update_time;
+ u64 *cpu_time_in_idle;
+ u64 *cpu_idle_exit_time;
+ struct timer_list *t;
+
+ u64 now_idle = get_cpu_idle_time_us(data,
+ &update_time);
+
+
+ cpu_time_in_idle = &per_cpu(time_in_idle, data);
+ cpu_idle_exit_time = &per_cpu(idle_exit_time, data);
+
+ if (update_time == *cpu_idle_exit_time)
+ return;
+
+ delta_idle = cputime64_sub(now_idle, *cpu_time_in_idle);
+
+ /* Scale up if there were no idle cycles since coming out of idle */
+ if (delta_idle == 0) {
+ if (policy->cur == policy->max)
+ return;
+
+ if (nr_running() < 1)
+ return;
+
+ target_freq = policy->max;
+ cpumask_set_cpu(data, &work_cpumask);
+ queue_work(up_wq, &freq_scale_work);
+ return;
+ }
+
+ /*
+ * There is a window where if the cpu utlization can go from low to high
+ * between the timer expiring, delta_idle will be > 0 and the cpu will
+ * be 100% busy, preventing idle from running, and this timer from
+ * firing. So setup another timer to fire to check cpu utlization.
+ * Do not setup the timer if there is no scheduled work.
+ */
+ t = &per_cpu(cpu_timer, data);
+ if (!timer_pending(t) && nr_running() > 0) {
+ *cpu_time_in_idle = get_cpu_idle_time_us(
+ data, cpu_idle_exit_time);
+ mod_timer(t, jiffies + 2);
+ }
+
+ if (policy->cur == policy->min)
+ return;
+
+ /*
+ * Do not scale down unless we have been at this frequency for the
+ * minimum sample time.
+ */
+ if (cputime64_sub(update_time, freq_change_time) < min_sample_time)
+ return;
+
+ target_freq = policy->min;
+ cpumask_set_cpu(data, &work_cpumask);
+ queue_work(down_wq, &freq_scale_work);
+}
+
+static void cpufreq_idle(void)
+{
+ struct timer_list *t;
+ u64 *cpu_time_in_idle;
+ u64 *cpu_idle_exit_time;
+
+ pm_idle_old();
+
+ if (!cpumask_test_cpu(smp_processor_id(), policy->cpus))
+ return;
+
+ /* Timer to fire in 1-2 ticks, jiffie aligned. */
+ t = &per_cpu(cpu_timer, smp_processor_id());
+ cpu_idle_exit_time = &per_cpu(idle_exit_time, smp_processor_id());
+ cpu_time_in_idle = &per_cpu(time_in_idle, smp_processor_id());
+
+ if (timer_pending(t) == 0) {
+ *cpu_time_in_idle = get_cpu_idle_time_us(
+ smp_processor_id(), cpu_idle_exit_time);
+ mod_timer(t, jiffies + 2);
+ }
+}
+
+/*
+ * Choose the cpu frequency based off the load. For now choose the minimum
+ * frequency that will satisfy the load, which is not always the lower power.
+ */
+static unsigned int cpufreq_interactive_calc_freq(unsigned int cpu)
+{
+ unsigned int delta_time;
+ unsigned int idle_time;
+ unsigned int cpu_load;
+ u64 current_wall_time;
+ u64 current_idle_time;;
+
+ current_idle_time = get_cpu_idle_time_us(cpu, &current_wall_time);
+
+ idle_time = (unsigned int) current_idle_time - freq_change_time_in_idle;
+ delta_time = (unsigned int) current_wall_time - freq_change_time;
+
+ cpu_load = 100 * (delta_time - idle_time) / delta_time;
+
+ return policy->cur * cpu_load / 100;
+}
+
+
+/* We use the same work function to sale up and down */
+static void cpufreq_interactive_freq_change_time_work(struct work_struct *work)
+{
+ unsigned int cpu;
+ cpumask_t tmp_mask = work_cpumask;
+ for_each_cpu(cpu, tmp_mask) {
+ if (target_freq == policy->max) {
+ if (nr_running() == 1) {
+ cpumask_clear_cpu(cpu, &work_cpumask);
+ return;
+ }
+
+ __cpufreq_driver_target(policy, target_freq,
+ CPUFREQ_RELATION_H);
+ } else {
+ target_freq = cpufreq_interactive_calc_freq(cpu);
+ __cpufreq_driver_target(policy, target_freq,
+ CPUFREQ_RELATION_L);
+ }
+ freq_change_time_in_idle = get_cpu_idle_time_us(cpu,
+ &freq_change_time);
+
+ cpumask_clear_cpu(cpu, &work_cpumask);
+ }
+
+
+}
+
+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[] = {
+ &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;
+ switch (event) {
+ case CPUFREQ_GOV_START:
+ if (!cpu_online(new_policy->cpu))
+ return -EINVAL;
+
+ /*
+ * 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_idle;
+ policy = new_policy;
+ break;
+
+ case CPUFREQ_GOV_STOP:
+ if (atomic_dec_return(&active_count) > 1)
+ return 0;
+
+ sysfs_remove_group(cpufreq_global_kobject,
+ &interactive_attr_group);
+
+ pm_idle = pm_idle_old;
+ del_timer(&per_cpu(cpu_timer, new_policy->cpu));
+ 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 timer_list *t;
+ min_sample_time = DEFAULT_MIN_SAMPLE_TIME;
+
+ /* Initalize per-cpu timers */
+ for_each_possible_cpu(i) {
+ t = &per_cpu(cpu_timer, i);
+ init_timer_deferrable(t);
+ t->function = cpufreq_interactive_timer;
+ t->data = i;
+ }
+
+ /* Scale up is high priority */
+ up_wq = create_rt_workqueue("kinteractive_up");
+ down_wq = create_workqueue("knteractive_down");
+
+ INIT_WORK(&freq_scale_work, cpufreq_interactive_freq_change_time_work);
+
+ return cpufreq_register_governor(&cpufreq_gov_interactive);
+}
+
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE
+pure_initcall(cpufreq_interactive_init);
+#else
+module_init(cpufreq_interactive_init);
+#endif
+
+static void __exit cpufreq_interactive_exit(void)
+{
+ cpufreq_unregister_governor(&cpufreq_gov_interactive);
+ destroy_workqueue(up_wq);
+ destroy_workqueue(down_wq);
+}
+
+module_exit(cpufreq_interactive_exit);
+
+MODULE_AUTHOR("Mike Chan <mike@android.com>");
+MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for "
+ "Latency sensitive workloads");
+MODULE_LICENSE("GPL");
+
file:79a2340d83cda45c88138841fe4b2a8a3892bcad -> file:7599a69ce3232a7c4ba6ef644b97e844a5dd99a8
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -338,6 +338,9 @@ extern struct cpufreq_governor cpufreq_g
#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE)
extern struct cpufreq_governor cpufreq_gov_conservative;
#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_conservative)
+#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE)
+extern struct cpufreq_governor cpufreq_gov_interactive;
+#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_interactive)
#endif