[Defy Android Development] Blechd0se:
So i had some spare time (some exams still left) and thought i might can adress this issues with incoming calls or other apps.

My idea was to

a) let the user decide if the new suspend/resume-handling should happen at all

b) let the user change the suspend frequency

c) build in some sort of barrier, if the current load cpu is above 80% (thats the go_hispeed_load tunable),

it jumps directly to the "old" algorithm.

Therefore i implemented two new tunables for interactive (suspend_enabled and suspend_freq).

Beside these changes (i'll add the code below, if someone is interested) i cleaned up the governor a bit

and let the debug messages enabled, so you can check the frequencies for yourself in realtime (cat /proc/kmsg in terminal).

Feedback as always appreciated :)

From f51ccc6dd124a1f8c33a739519dc9978167dd5f1 Mon Sep 17 00:00:00 2001
From: Blechd0se <alex.christ@hotmail.de>
Date: Sat, 1 Jun 2013 23:59:55 +0200
Subject: [PATCH] cpufreq: interactive: optimized suspend handling

Change-Id: I744b0598ef0210ef9f654f23e48ca3a36d3a0e8c
Signed-off-by: Blechd0se <alex.christ@hotmail.de>
---
drivers/cpufreq/cpufreq_interactive.c | 245 +++++++++++++++++++++-------------
1 file changed, 153 insertions(+), 92 deletions(-)

diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index 1e8bfd4..af870af 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -62,6 +62,7 @@ struct cpufreq_interactive_cpuinfo {
u64 hispeed_validate_time;
int governor_enabled;
unsigned int *load_history;
+	unsigned int history_load_index;
unsigned int total_avg_load;
unsigned int total_load_history;
unsigned int low_power_rate_history;
@@ -92,10 +93,16 @@ static bool io_is_busy = true;
static unsigned int last_freq;

/* Suspend/Resume flag */
-static bool suspend_freq;
+static bool suspend_state;
+
+/* Suspend Optimization Enabled ? */
+static bool suspend_enabled = true;
+
+/* Suspend Freq */
+#define SUSPEND_FREQ 300000
+static int suspend_freq = SUSPEND_FREQ;

static unsigned int sampling_periods;
-static unsigned int history_load_index;
static unsigned int low_power_threshold;
static unsigned int hi_perf_threshold;
static unsigned int low_power_rate;
@@ -173,10 +180,6 @@ static int boost_val;

static int input_boost_freq = DEFAULT_HISPEED_FREQ;

-/* Duration of a boot pulse in usecs */
-static int boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME;
-/* End time of boost pulse in ktime converted to usecs */
-static u64 boostpulse_endtime;

static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
unsigned int event);
@@ -261,7 +264,7 @@ static void cpufreq_interactive_timer(unsigned long data)
bool boosted;

now = 0;
-
+
smp_rmb();

if (!pcpu->governor_enabled)
@@ -331,13 +334,14 @@ static void cpufreq_interactive_timer(unsigned long data)
*/
if (load_since_change > cpu_load)
cpu_load = load_since_change;
-	pcpu->load_history[history_load_index] = cpu_load;
+	pcpu->load_history[pcpu->history_load_index] = cpu_load;

pcpu->total_load_history = 0;
pcpu->low_power_rate_history = 0;

/* compute average load across in & out sampling periods */
-	for (i = 0, j = history_load_index; i < sampling_periods; i++, j--) {
+	for (i = 0, j = pcpu->history_load_index;
+	i < sampling_periods; i++, j--) {
pcpu->total_load_history += pcpu->load_history[j];
if (low_power_rate < sampling_periods)
if (i < low_power_rate)
@@ -348,8 +352,17 @@ static void cpufreq_interactive_timer(unsigned long data)
}

/* return to first element if we're at the circular buffer's end */
-	if (++history_load_index == sampling_periods)
-	history_load_index = 0;
+	if (++pcpu->history_load_index == sampling_periods)
+	pcpu->history_load_index = 0;
+	else if (unlikely(pcpu->history_load_index > sampling_periods)) {
+	/*
+	* This not supposed to happen.
+	* If we got here - means something is wrong.
+	*/
+	pr_err("%s: have gone beyond allocated buffer of history!\n",
+	__func__);
+	pcpu->history_load_index = 0;
+	}

pcpu->total_avg_load = pcpu->total_load_history / sampling_periods;

@@ -378,8 +391,8 @@ static void cpufreq_interactive_timer(unsigned long data)
else
cpu_load = pcpu->total_avg_load;
}
-
-	boosted = boost_val || now < boostpulse_endtime;
+
+	boosted = boost_val;

if ((cpu_load >= go_hispeed_load || boosted) &&
pcpu->target_freq < hispeed_freq) {
@@ -414,8 +427,7 @@ static void cpufreq_interactive_timer(unsigned long data)
* floor frequency for the minimum sample time since last validated.
*/
if (new_freq < pcpu->floor_freq) {
-	if (cputime64_sub(pcpu->timer_run_time,
-	pcpu->floor_validate_time)
+	if (pcpu->timer_run_time - pcpu->floor_validate_time
< min_sample_time) {
trace_cpufreq_interactive_notyet(
data, cpu_load, pcpu->target_freq,
@@ -424,18 +436,8 @@ static void cpufreq_interactive_timer(unsigned long data)
}
}

-	/*
-	* Update the timestamp for checking whether speed has been held at
-	* or above the selected frequency for a minimum of min_sample_time,
-	* if not boosted to hispeed_freq. If boosted to hispeed_freq then we
-	* allow the speed to drop as soon as the boostpulse duration expires
-	* (or the indefinite boost is turned off).
-	*/
-
-	if (!boosted || new_freq > hispeed_freq) {
-	pcpu->floor_freq = new_freq;
-	pcpu->floor_validate_time = pcpu->timer_run_time;
-	}
+	pcpu->floor_freq = new_freq;
+	pcpu->floor_validate_time = pcpu->timer_run_time;

if (pcpu->target_freq == new_freq) {
trace_cpufreq_interactive_already(data, cpu_load,
@@ -683,9 +685,12 @@ static int cpufreq_interactive_up_task(void *data)

/*
* Make a break point here, if suspended it should not scale
+	* Only enable it, if user wants to
*/
-	if (suspend_freq)
-	break;
+	if (suspend_enabled) {
+	if (suspend_state)
+	break;
+	}

mutex_lock(&set_speed_lock);

@@ -709,6 +714,8 @@ static int cpufreq_interactive_up_task(void *data)
pcpu->freq_change_time_in_iowait =
get_cpu_iowait_time(cpu, NULL);

+	printk("interactive: Current Freq: %u \n", pcpu->policy->cur);
+
}
}

@@ -721,13 +728,28 @@ static void interactive_early_suspend(struct early_suspend *handler)
unsigned int cpu;
pcpu = &per_cpu(cpuinfo, cpu);

-	/* Safe the last frequency, so we can restore it later */
-	last_freq = pcpu->target_freq;

+	if (suspend_enabled) {
+	/* Safe the last frequency, so we can restore it later */
+	last_freq = pcpu->target_freq;
+	printk("interactive: Entering suspend with: %u \n", last_freq);

-	/* set it to the lowest possible frequency */
-	pcpu->target_freq = pcpu->policy->min;
-	suspend_freq = true;
+	if (pcpu->total_avg_load > go_hispeed_load) {
+	/*
+	* The screen is off, we are near deep-sleep.
+	* Something needs power and it needs it fast!
+	* So break out of the suspend code and go into normal
+	* scheduling algorithm.
+	*/
+	suspend_state = false;
+	pcpu->target_freq = last_freq;
+	}
+	else {
+	/* set it to the lowest possible frequency */
+	pcpu->target_freq = suspend_freq;
+	suspend_state = true;
+	}
+	}
}

@@ -737,9 +759,13 @@ static void interactive_late_resume(struct early_suspend *handler)
unsigned int cpu;
pcpu = &per_cpu(cpuinfo, cpu);

-	/* Resume */
-	pcpu->target_freq = last_freq;
-	suspend_freq = false;
+	if (suspend_enabled) {
+	/* Resume */
+	pcpu->target_freq = last_freq;
+
+	printk("interactive: Wake up with: %u \n", last_freq);
+	suspend_state = false;
+	}
}

static struct early_suspend interactive_suspend = {
@@ -788,6 +814,8 @@ static void cpufreq_interactive_freq_down(struct work_struct *work)
pcpu->policy->cur);
pcpu->freq_change_time_in_iowait =
get_cpu_iowait_time(cpu, NULL);
+
+	printk("interactive: Current Freq: %u \n", pcpu->policy->cur);

last_freq = pcpu->target_freq;

@@ -992,6 +1020,55 @@ static ssize_t store_io_is_busy(struct kobject *kobj,
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_suspend_enabled(struct kobject *kobj,
+	struct attribute *attr, char *buf)
+{
+	return sprintf(buf, "%u\n", suspend_enabled);
+}
+
+static ssize_t store_suspend_enabled(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;
+
+	suspend_enabled = val;
+
+	return count;
+}
+
+static struct global_attr suspend_enabled_attr = __ATTR(suspend_enabled, 0644,
+	show_suspend_enabled, store_suspend_enabled);
+
+static ssize_t show_suspend_freq(struct kobject *kobj, struct attribute *attr,
+	char *buf)
+{
+	return sprintf(buf, "%d\n", suspend_freq);
+}
+
+static ssize_t store_suspend_freq(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;
+
+	suspend_freq = val;
+
+	return count;
+}
+
+static struct global_attr suspend_freq_attr = __ATTR(suspend_freq, 0644,
+	show_suspend_freq, store_suspend_freq);
+
static ssize_t show_go_hispeed_load(struct kobject *kobj,
struct attribute *attr, char *buf)
{
@@ -1141,37 +1218,13 @@ static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr,
if (ret < 0)
return ret;

-	boostpulse_endtime = ktime_to_us(ktime_get()) + boostpulse_duration_val;
trace_cpufreq_interactive_boost("pulse");
cpufreq_interactive_boost();
return count;
}

static struct global_attr boostpulse =
-	__ATTR(boostpulse, 0200, NULL, store_boostpulse);
-
-static ssize_t show_boostpulse_duration(
-	struct kobject *kobj, struct attribute *attr, char *buf)
-{
-	return sprintf(buf, "%d\n", boostpulse_duration_val);
-}
-
-static ssize_t store_boostpulse_duration(
-	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;
-
-	boostpulse_duration_val = val;
-	return count;
-}
-
-define_one_global_rw(boostpulse_duration);
+	__ATTR(boostpulse, 0644, NULL, store_boostpulse);

static ssize_t show_input_boost_freq(struct kobject *kobj, struct attribute *attr,
char *buf)
@@ -1207,9 +1260,10 @@ static ssize_t store_sampling_periods(struct kobject *kobj,
struct attribute *attr, const char *buf, size_t count)
{
int ret;
-	unsigned int val;
+	unsigned int val, t_mask = 0;
unsigned int *temp;
unsigned int j, i;
+	struct cpufreq_interactive_cpuinfo *pcpu;

ret = sscanf(buf, "%u", &val);
if (ret != 1)
@@ -1218,40 +1272,43 @@ static ssize_t store_sampling_periods(struct kobject *kobj,
if (val == sampling_periods)
return count;

-	if (val <= sampling_periods) {
-	sampling_periods = val;
-	history_load_index = 0;
-	return count;
-	}
-
mutex_lock(&set_speed_lock);

-	for_each_online_cpu(j) {
-	struct cpufreq_interactive_cpuinfo *pcpu;
+	for_each_present_cpu(j) {
+	pcpu = &per_cpu(cpuinfo, j);
+	ret = del_timer_sync(&pcpu->cpu_timer);
+	if (ret)
+	t_mask |= BIT(j);
+	pcpu->history_load_index = 0;

temp = kmalloc((sizeof(unsigned int) * val), GFP_KERNEL);
if (!temp) {
-	mutex_unlock(&set_speed_lock);
pr_err("%s:can't allocate memory for history\n",
__func__);
-	return -ENOMEM;
+	count = -ENOMEM;
+	goto out;
}
-	pcpu = &per_cpu(cpuinfo, j);
-	ret = del_timer_sync(&pcpu->cpu_timer);
memcpy(temp, pcpu->load_history,
-	(sampling_periods * sizeof(unsigned int)));
-	for (i = sampling_periods; i < val; i++)
-	temp[i] = 50;
+	(min(sampling_periods, val) * sizeof(unsigned int)));
+	if (val > sampling_periods)
+	for (i = sampling_periods; i < val; i++)
+	temp[i] = 50;

kfree(pcpu->load_history);
pcpu->load_history = temp;

-	if (ret)
+	}
+
+out:
+	if (!(count < 0 && val > sampling_periods))
+	sampling_periods = val;
+
+	for_each_online_cpu(j) {
+	pcpu = &per_cpu(cpuinfo, j);
+	if (t_mask & BIT(j))
mod_timer(&pcpu->cpu_timer,
-	jiffies + usecs_to_jiffies(timer_rate));
+	jiffies + usecs_to_jiffies(timer_rate));
}
-	sampling_periods = val;
-	history_load_index = 0;

mutex_unlock(&set_speed_lock);

@@ -1338,6 +1395,8 @@ static struct attribute *interactive_attributes[] = {
&io_is_busy_attr.attr,
&timer_rate_attr.attr,
&input_boost.attr,
+	&suspend_enabled_attr.attr,
+	&suspend_freq_attr.attr,
&boost.attr,
&boostpulse.attr,
&input_boost_freq_attr.attr,
@@ -1345,7 +1404,6 @@ static struct attribute *interactive_attributes[] = {
&hi_perf_threshold_attr.attr,
&sampling_periods_attr.attr,
&low_power_rate_attr.attr,
-	&boostpulse_duration.attr,
NULL,
};

@@ -1395,6 +1453,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
return -ENOMEM;
for (i = 0; i < sampling_periods; i++)
pcpu->load_history[i] = 0;
+	pcpu->history_load_index = 0;
+	pcpu->idle_exit_time = pcpu->target_set_time;
+	mod_timer(&pcpu->cpu_timer,
+	jiffies + usecs_to_jiffies(timer_rate));
smp_wmb();
}

@@ -1402,9 +1464,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
hispeed_freq = policy->max;
input_boost_freq = hispeed_freq;
}
-	history_load_index = 0;
-
-
+
/*
* Do not register the idle hook and create sysfs
* entries if we have already done so.
@@ -1466,8 +1526,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
}

static int cpufreq_interactive_idle_notifier(struct notifier_block *nb,
-	unsigned long val,
-	void *data)
+	unsigned long val,
+	void *data)
{
switch (val) {
case IDLE_START:
@@ -1476,11 +1536,11 @@ static int cpufreq_interactive_idle_notifier(struct notifier_block *nb,
case IDLE_END:
cpufreq_interactive_idle_end();
break;
-	}
+}

return 0;
}
-
+
static struct notifier_block cpufreq_interactive_idle_nb = {
.notifier_call = cpufreq_interactive_idle_notifier,
};
@@ -1532,9 +1592,11 @@ static int __init cpufreq_interactive_init(void)

spin_lock_init(&up_cpumask_lock);
spin_lock_init(&down_cpumask_lock);
-	spin_lock_init(&tune_cpumask_lock);
mutex_init(&set_speed_lock);

+	/* Kick the kthread to idle */
+	wake_up_process(up_task);
+
idle_notifier_register(&cpufreq_interactive_idle_nb);
INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open);
return cpufreq_register_governor(&cpufreq_gov_interactive);
@@ -1556,7 +1618,6 @@ static void __exit cpufreq_interactive_exit(void)
kthread_stop(up_task);
put_task_struct(up_task);
destroy_workqueue(down_wq);
-	destroy_workqueue(tune_wq);
}

module_exit(cpufreq_interactive_exit);
--
1.8.0.3