summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c70
1 files changed, 68 insertions, 2 deletions
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index 5b7770c78583..5d15e375ec0c 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -35,11 +35,13 @@ struct cpufreq_interactive_cpuinfo {
struct timer_list cpu_timer;
int timer_idlecancel;
u64 time_in_idle;
+ u64 time_in_iowait;
u64 idle_exit_time;
u64 timer_run_time;
int idling;
u64 freq_change_time;
u64 freq_change_time_in_idle;
+ u64 freq_change_time_in_iowait;
struct cpufreq_policy *policy;
struct cpufreq_frequency_table *freq_table;
unsigned int target_freq;
@@ -68,6 +70,9 @@ static unsigned long boost_factor;
/* Max frequency boost in Hz; if 0 - no max is enforced */
static unsigned long max_boost;
+/* Consider IO as busy */
+static unsigned long io_is_busy;
+
/*
* Targeted sustainable load relatively to current frequency.
* If 0, target is set realtively to the max speed
@@ -132,17 +137,31 @@ static unsigned int cpufreq_interactive_get_target(
return target_freq;
}
+static inline cputime64_t get_cpu_iowait_time(
+ unsigned int cpu, cputime64_t *wall)
+{
+ u64 iowait_time = get_cpu_iowait_time_us(cpu, wall);
+
+ if (iowait_time == -1ULL)
+ return 0;
+
+ return iowait_time;
+}
+
static void cpufreq_interactive_timer(unsigned long data)
{
unsigned int delta_idle;
+ unsigned int delta_iowait;
unsigned int delta_time;
int cpu_load;
int load_since_change;
u64 time_in_idle;
+ u64 time_in_iowait;
u64 idle_exit_time;
struct cpufreq_interactive_cpuinfo *pcpu =
&per_cpu(cpuinfo, data);
u64 now_idle;
+ u64 now_iowait;
unsigned int new_freq;
unsigned int index;
unsigned long flags;
@@ -162,8 +181,10 @@ static void cpufreq_interactive_timer(unsigned long data)
* until more time passes).
*/
time_in_idle = pcpu->time_in_idle;
+ time_in_iowait = pcpu->time_in_iowait;
idle_exit_time = pcpu->idle_exit_time;
now_idle = get_cpu_idle_time_us(data, &pcpu->timer_run_time);
+ now_iowait = get_cpu_iowait_time(data, NULL);
smp_wmb();
/* If we raced with cancelling a timer, skip. */
@@ -171,6 +192,7 @@ static void cpufreq_interactive_timer(unsigned long data)
goto exit;
delta_idle = (unsigned int) cputime64_sub(now_idle, time_in_idle);
+ delta_iowait = (unsigned int) cputime64_sub(now_iowait, time_in_iowait);
delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time,
idle_exit_time);
@@ -182,19 +204,29 @@ static void cpufreq_interactive_timer(unsigned long data)
if (delta_idle > delta_time)
cpu_load = 0;
- else
+ else {
+ if (io_is_busy && delta_idle >= delta_iowait)
+ delta_idle -= delta_iowait;
+
cpu_load = 100 * (delta_time - delta_idle) / delta_time;
+ }
delta_idle = (unsigned int) cputime64_sub(now_idle,
pcpu->freq_change_time_in_idle);
+ delta_iowait = (unsigned int) cputime64_sub(now_iowait,
+ pcpu->freq_change_time_in_iowait);
delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time,
pcpu->freq_change_time);
if ((delta_time == 0) || (delta_idle > delta_time))
load_since_change = 0;
- else
+ else {
+ if (io_is_busy && delta_idle >= delta_iowait)
+ delta_idle -= delta_iowait;
+
load_since_change =
100 * (delta_time - delta_idle) / delta_time;
+ }
/*
* Combine short-term load (since last idle timer started or timer
@@ -267,6 +299,9 @@ rearm:
pcpu->time_in_idle = get_cpu_idle_time_us(
data, &pcpu->idle_exit_time);
+ pcpu->time_in_iowait = get_cpu_iowait_time(
+ data, NULL);
+
mod_timer(&pcpu->cpu_timer,
jiffies + usecs_to_jiffies(timer_rate));
}
@@ -301,6 +336,8 @@ static void cpufreq_interactive_idle_start(void)
if (!pending) {
pcpu->time_in_idle = get_cpu_idle_time_us(
smp_processor_id(), &pcpu->idle_exit_time);
+ pcpu->time_in_iowait = get_cpu_iowait_time(
+ smp_processor_id(), NULL);
pcpu->timer_idlecancel = 0;
mod_timer(&pcpu->cpu_timer,
jiffies + usecs_to_jiffies(timer_rate));
@@ -352,6 +389,9 @@ static void cpufreq_interactive_idle_end(void)
pcpu->time_in_idle =
get_cpu_idle_time_us(smp_processor_id(),
&pcpu->idle_exit_time);
+ pcpu->time_in_iowait =
+ get_cpu_iowait_time(smp_processor_id(),
+ NULL);
pcpu->timer_idlecancel = 0;
mod_timer(&pcpu->cpu_timer,
jiffies + usecs_to_jiffies(timer_rate));
@@ -414,6 +454,8 @@ static int cpufreq_interactive_up_task(void *data)
pcpu->freq_change_time_in_idle =
get_cpu_idle_time_us(cpu,
&pcpu->freq_change_time);
+ pcpu->freq_change_time_in_iowait =
+ get_cpu_iowait_time(cpu, NULL);
}
}
@@ -460,6 +502,8 @@ 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);
+ pcpu->freq_change_time_in_iowait =
+ get_cpu_iowait_time(cpu, NULL);
}
}
@@ -507,6 +551,23 @@ static ssize_t store_boost_factor(struct kobject *kobj,
static struct global_attr boost_factor_attr = __ATTR(boost_factor, 0644,
show_boost_factor, store_boost_factor);
+static ssize_t show_io_is_busy(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%lu\n", io_is_busy);
+}
+
+static ssize_t store_io_is_busy(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ if (!strict_strtoul(buf, 0, &io_is_busy))
+ return count;
+ return -EINVAL;
+}
+
+static struct global_attr io_is_busy_attr = __ATTR(io_is_busy, 0644,
+ show_io_is_busy, store_io_is_busy);
+
static ssize_t show_max_boost(struct kobject *kobj,
struct attribute *attr, char *buf)
{
@@ -600,6 +661,7 @@ static struct attribute *interactive_attributes[] = {
&go_maxspeed_load_attr.attr,
&boost_factor_attr.attr,
&max_boost_attr.attr,
+ &io_is_busy_attr.attr,
&sustain_load_attr.attr,
&min_sample_time_attr.attr,
&timer_rate_attr.attr,
@@ -637,6 +699,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
&pcpu->freq_change_time);
pcpu->time_in_idle = pcpu->freq_change_time_in_idle;
pcpu->idle_exit_time = pcpu->freq_change_time;
+ pcpu->freq_change_time_in_iowait =
+ get_cpu_iowait_time(j, NULL);
+ pcpu->time_in_iowait = pcpu->freq_change_time_in_iowait;
+
pcpu->timer_idlecancel = 1;
pcpu->governor_enabled = 1;
smp_wmb();