Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78e6913a authored by Dmitry Torokhov's avatar Dmitry Torokhov Committed by Amit Pundir
Browse files

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: default avatarDmitry Torokhov <dtor@chromium.org>
Reviewed-by: default avatarDylan Reid <dgreid@chromium.org>
Reviewed-by: default avatarDouglas Anderson <dianders@chromium.org>
Signed-off-by: default avatarDmitry Torokhov <dtor@google.com>
parent 12988635
Loading
Loading
Loading
Loading
+59 −38
Original line number Diff line number Diff line
@@ -489,6 +489,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;
@@ -517,49 +569,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) {
				up_read(&pcpu->enable_sem);
				continue;
			}

			for_each_cpu(j, pcpu->policy->cpus) {
				pjcpu = &per_cpu(cpuinfo, j);
			down_write(&pcpu->policy->rwsem);

				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;
				}
			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);
			}
			trace_cpufreq_interactive_setspeed(cpu,
						     pcpu->target_freq,
						     pcpu->policy->cur);

			up_read(&pcpu->enable_sem);
			up_write(&pcpu->policy->rwsem);
		}
	}