cpufreq: interactive: Avoid down_read_trylock if down_write() is held
down_read_trylock is not always non-blocking if the same thread calls
down_write() before.
CPU1 CPU2
down_read()
down_write()
__down_write_nested()
schedule()
__down_read_trylock()
up_read()
acquires sem->wait_lock
__rwsem_wake_one_writer()
tries to lock sem->wait_lock
Now CPU2 is waiting for CPU1's schedule() to complete, while holding
sem->wait_lock. CPU1 needs sem->wait_lock to continue.
This problem only happens after cpufreq_interactive introduced load
change notification that could be called within schedule().
Add a separate flag to ignore notification if current thread is in
middle of down_write(). This avoids attempting to hold sem->wait_lock.
The additional flag doesn't have any side effects because
down_read_trylock() would have failed anyway.
Change-Id: Iff97cac36c170cf6d03f36de695141289c3d6930
[junjiew@codeaurora.org: Resolved merge conflicts. Dropped changes
to code that no longer exists.]
Signed-off-by: Junjie Wu <junjiew@codeaurora.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index b4a19f4..bcb60ac 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -54,6 +54,7 @@
u64 loc_hispeed_val_time; /* per-cpu hispeed_validate_time */
u64 max_freq_hyst_start_time;
struct rw_semaphore enable_sem;
+ bool reject_notification;
int governor_enabled;
struct cpufreq_interactive_tunables *cached_tunables;
int first_cpu;
@@ -723,6 +724,9 @@
if (speedchange_task == current)
return 0;
+ if (pcpu->reject_notification)
+ return 0;
+
if (!down_read_trylock(&pcpu->enable_sem))
return 0;
if (!pcpu->governor_enabled) {
@@ -1607,6 +1611,7 @@
pcpu->loc_floor_val_time = pcpu->pol_floor_val_time;
pcpu->pol_hispeed_val_time = pcpu->pol_floor_val_time;
pcpu->loc_hispeed_val_time = pcpu->pol_floor_val_time;
+ pcpu->reject_notification = true;
down_write(&pcpu->enable_sem);
del_timer_sync(&pcpu->cpu_timer);
del_timer_sync(&pcpu->cpu_slack_timer);
@@ -1614,6 +1619,7 @@
cpufreq_interactive_timer_start(tunables, j);
pcpu->governor_enabled = 1;
up_write(&pcpu->enable_sem);
+ pcpu->reject_notification = false;
}
mutex_unlock(&gov_lock);
@@ -1623,11 +1629,13 @@
mutex_lock(&gov_lock);
for_each_cpu(j, policy->cpus) {
pcpu = &per_cpu(cpuinfo, j);
+ pcpu->reject_notification = true;
down_write(&pcpu->enable_sem);
pcpu->governor_enabled = 0;
del_timer_sync(&pcpu->cpu_timer);
del_timer_sync(&pcpu->cpu_slack_timer);
up_write(&pcpu->enable_sem);
+ pcpu->reject_notification = false;
}
mutex_unlock(&gov_lock);