cpufreq: interactive: fix policy locking

cpufreq_interactive_speedchange_task() is running as a separate kernel
thread and is calling __cpufreq_driver_target(), which requires callers
to hold policy->rwsem for writing to prevent racing with other parts of
the kernel trying to adjust the frequency, for example kernel thermal
throttling. Let's change the code to take policy->rwsem and while at it
refactor the code a bit.

This was originally 2 changes reviewed at:
	https://chromium-review.googlesource.com/246273
	https://chromium-review.googlesource.com/256120

Change-Id: Icc2d97c6c1b929acd2ee32e8c81d81fd2af778ab
Signed-off-by: Dmitry Torokhov <dtor@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Dmitry Torokhov <dtor@google.com>
This commit is contained in:
Dmitry Torokhov
2015-02-04 13:54:48 -08:00
committed by Dmitry Shmidt
parent e7d40d2bf8
commit 2ebb4d97e0

View File

@@ -511,6 +511,58 @@ static void cpufreq_interactive_idle_end(void)
up_read(&pcpu->enable_sem);
}
static void cpufreq_interactive_get_policy_info(struct cpufreq_policy *policy,
unsigned int *pmax_freq,
u64 *phvt, u64 *pfvt)
{
struct cpufreq_interactive_cpuinfo *pcpu;
unsigned int max_freq = 0;
u64 hvt = ~0ULL, fvt = 0;
unsigned int i;
for_each_cpu(i, policy->cpus) {
pcpu = &per_cpu(cpuinfo, i);
fvt = max(fvt, pcpu->loc_floor_val_time);
if (pcpu->target_freq > max_freq) {
max_freq = pcpu->target_freq;
hvt = pcpu->loc_hispeed_val_time;
} else if (pcpu->target_freq == max_freq) {
hvt = min(hvt, pcpu->loc_hispeed_val_time);
}
}
*pmax_freq = max_freq;
*phvt = hvt;
*pfvt = fvt;
}
static void cpufreq_interactive_adjust_cpu(unsigned int cpu,
struct cpufreq_policy *policy)
{
struct cpufreq_interactive_cpuinfo *pcpu;
u64 hvt, fvt;
unsigned int max_freq;
int i;
cpufreq_interactive_get_policy_info(policy, &max_freq, &hvt, &fvt);
for_each_cpu(i, policy->cpus) {
pcpu = &per_cpu(cpuinfo, i);
pcpu->pol_floor_val_time = fvt;
}
if (max_freq != policy->cur) {
__cpufreq_driver_target(policy, max_freq, CPUFREQ_RELATION_H);
for_each_cpu(i, policy->cpus) {
pcpu = &per_cpu(cpuinfo, i);
pcpu->pol_hispeed_val_time = hvt;
}
}
trace_cpufreq_interactive_setspeed(cpu, max_freq, policy->cur);
}
static int cpufreq_interactive_speedchange_task(void *data)
{
unsigned int cpu;
@@ -539,49 +591,18 @@ static int cpufreq_interactive_speedchange_task(void *data)
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
for_each_cpu(cpu, &tmp_mask) {
unsigned int j;
unsigned int max_freq = 0;
struct cpufreq_interactive_cpuinfo *pjcpu;
u64 hvt = ~0ULL, fvt = 0;
pcpu = &per_cpu(cpuinfo, cpu);
if (!down_read_trylock(&pcpu->enable_sem))
continue;
if (!pcpu->governor_enabled) {
down_write(&pcpu->policy->rwsem);
if (likely(down_read_trylock(&pcpu->enable_sem))) {
if (likely(pcpu->governor_enabled))
cpufreq_interactive_adjust_cpu(cpu,
pcpu->policy);
up_read(&pcpu->enable_sem);
continue;
}
for_each_cpu(j, pcpu->policy->cpus) {
pjcpu = &per_cpu(cpuinfo, j);
fvt = max(fvt, pjcpu->loc_floor_val_time);
if (pjcpu->target_freq > max_freq) {
max_freq = pjcpu->target_freq;
hvt = pjcpu->loc_hispeed_val_time;
} else if (pjcpu->target_freq == max_freq) {
hvt = min(hvt, pjcpu->loc_hispeed_val_time);
}
}
for_each_cpu(j, pcpu->policy->cpus) {
pjcpu = &per_cpu(cpuinfo, j);
pjcpu->pol_floor_val_time = fvt;
}
if (max_freq != pcpu->policy->cur) {
__cpufreq_driver_target(pcpu->policy,
max_freq,
CPUFREQ_RELATION_H);
for_each_cpu(j, pcpu->policy->cpus) {
pjcpu = &per_cpu(cpuinfo, j);
pjcpu->pol_hispeed_val_time = hvt;
}
}
trace_cpufreq_interactive_setspeed(cpu,
pcpu->target_freq,
pcpu->policy->cur);
up_read(&pcpu->enable_sem);
up_write(&pcpu->policy->rwsem);
}
}