[PATCH 7.1 331/438] cpufreq: cppc: Sanitize lockless policy limit snapshots
Greg Kroah-Hartman <[email protected]>
| Newsgroups | dev.linux.lists.patches,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
7.1-stable review patch. If anyone has any objections, please let me know. ------------------ From: Christian Loehle <[email protected]> commit 9753c0ab89b7516aba4884dc3cc725ca33c2e3da upstream. cppc_cpufreq_update_perf_limits() reads policy->min and policy->max without holding the policy lock. The cpufreq core updates those fields with separate stores, so a reader can observe the old minimum together with the new maximum and construct MIN_PERF greater than MAX_PERF. Read both fields once and, if the lockless snapshot is inconsistent, reduce the minimum to the observed maximum. This matches the conservative correction used by cpufreq_driver_resolve_freq() and ensures that CPPC never receives an inverted limit pair. Fixes: ea3db45ae476 ("cpufreq: cppc: Update MIN_PERF/MAX_PERF in target callbacks") Cc: [email protected] Signed-off-by: Christian Loehle <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Rafael J. Wysocki <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> --- drivers/cpufreq/cppc_cpufreq.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -290,19 +290,32 @@ static inline void cppc_freq_invariance_ } #endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */ -static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data, - struct cpufreq_policy *policy) +static void cppc_cpufreq_get_perf_limits(struct cppc_cpudata *cpu_data, + struct cpufreq_policy *policy, + u32 *min_perf, u32 *max_perf) { struct cppc_perf_caps *caps = &cpu_data->perf_caps; - u32 min_perf, max_perf; + unsigned int min_freq, max_freq; + u32 min, max; + + min_freq = READ_ONCE(policy->min); + max_freq = READ_ONCE(policy->max); + if (unlikely(min_freq > max_freq)) + min_freq = max_freq; + + min = cppc_khz_to_perf(caps, min_freq); + max = cppc_khz_to_perf(caps, max_freq); - min_perf = cppc_khz_to_perf(caps, policy->min); - max_perf = cppc_khz_to_perf(caps, policy->max); + *min_perf = clamp_t(u32, min, caps->lowest_perf, caps->highest_perf); + *max_perf = clamp_t(u32, max, caps->lowest_perf, caps->highest_perf); +} - cpu_data->perf_ctrls.min_perf = - clamp_t(u32, min_perf, caps->lowest_perf, caps->highest_perf); - cpu_data->perf_ctrls.max_perf = - clamp_t(u32, max_perf, caps->lowest_perf, caps->highest_perf); +static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data, + struct cpufreq_policy *policy) +{ + cppc_cpufreq_get_perf_limits(cpu_data, policy, + &cpu_data->perf_ctrls.min_perf, + &cpu_data->perf_ctrls.max_perf); } static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,