[PATCH 2/2] cpufreq: cppc: Sanitize lockless policy limit snapshots

From: Christian Loehle

Date: Wed Jul 22 2026 - 05:44:17 EST


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: stable@xxxxxxxxxxxxxxx
Signed-off-by: Christian Loehle <christian.loehle@xxxxxxx>
---
drivers/cpufreq/cppc_cpufreq.c | 31 ++++++++++++++++++++++---------
1 file changed, 22 insertions(+), 9 deletions(-)

diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index b943bf78d3f5..6fe0e972952a 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -290,19 +290,32 @@ static inline void cppc_freq_invariance_exit(void)
}
#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,
--
2.34.1