cpufreq: cppc: Sanitize lockless policy limit snapshots

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: ea3db45ae4 ("cpufreq: cppc: Update MIN_PERF/MAX_PERF in target callbacks")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Loehle <christian.loehle@arm.com>
Link: https://patch.msgid.link/20260722093825.1030594-3-christian.loehle@arm.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Christian Loehle 2026-07-22 10:38:25 +01:00 committed by Rafael J. Wysocki
parent 11055a46f3
commit 9753c0ab89

View File

@ -290,19 +290,32 @@ static inline void cppc_freq_invariance_exit(void)
}
#endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */
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;
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 = clamp_t(u32, min, caps->lowest_perf, caps->highest_perf);
*max_perf = clamp_t(u32, max, caps->lowest_perf, caps->highest_perf);
}
static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data,
struct cpufreq_policy *policy)
{
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
u32 min_perf, max_perf;
min_perf = cppc_khz_to_perf(caps, policy->min);
max_perf = cppc_khz_to_perf(caps, policy->max);
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);
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,