aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Poynor <toddpoynor@google.com>2010-12-23 17:33:07 -0800
committerDima Zavin <dima@android.com>2011-01-07 19:11:23 -0800
commitf9a8d69a133f967d3ef21a5842c5355c8ee84fef (patch)
treead53dd32da75d007526fd46f85b4012e4b27170d
parent4a872537ad3b56a5255b00c189f5ddc19a71df01 (diff)
downloadexperimental-sandbox/dima/android-2.6.37-tmp2.tar.gz
cpufreq interactive governor: fix crash on CPU shutdownsandbox/dima/android-2.6.37-tmp2
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 <toddpoynor@google.com>
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c31
1 files changed, 29 insertions, 2 deletions
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index 4909c7bb741..81783286cad 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: