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
Signed-off-by: Junjie Wu <junjiew@codeaurora.org>
This commit is contained in:
Junjie Wu 2014-12-09 13:20:26 -08:00
parent 51c75088b8
commit e58cb9f6ee

View file

@ -56,6 +56,7 @@ struct cpufreq_interactive_cpuinfo {
u64 local_hvtime; /* per-cpu hispeed_validate_time */
u64 max_freq_idle_start_time;
struct rw_semaphore enable_sem;
bool reject_notification;
int governor_enabled;
struct cpufreq_interactive_tunables *cached_tunables;
int first_cpu;
@ -746,6 +747,9 @@ static int load_change_callback(struct notifier_block *nb, unsigned long val,
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) {
@ -1642,6 +1646,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
pcpu->floor_validate_time;
pcpu->local_hvtime = pcpu->floor_validate_time;
pcpu->max_freq = policy->max;
pcpu->reject_notification = true;
down_write(&pcpu->enable_sem);
del_timer_sync(&pcpu->cpu_timer);
del_timer_sync(&pcpu->cpu_slack_timer);
@ -1649,6 +1654,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
cpufreq_interactive_timer_start(tunables, j);
pcpu->governor_enabled = 1;
up_write(&pcpu->enable_sem);
pcpu->reject_notification = false;
}
mutex_unlock(&gov_lock);
@ -1658,12 +1664,14 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
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;
pcpu->target_freq = 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);
@ -1702,11 +1710,13 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
*/
if (policy->max > pcpu->max_freq) {
pcpu->reject_notification = true;
down_write(&pcpu->enable_sem);
del_timer_sync(&pcpu->cpu_timer);
del_timer_sync(&pcpu->cpu_slack_timer);
cpufreq_interactive_timer_resched(j);
up_write(&pcpu->enable_sem);
pcpu->reject_notification = false;
}
pcpu->max_freq = policy->max;