]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
cpufreq: cppc: Sanitize lockless policy limit snapshots
authorChristian Loehle <christian.loehle@arm.com>
Wed, 22 Jul 2026 09:38:25 +0000 (10:38 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 22 Jul 2026 13:09:14 +0000 (15:09 +0200)
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@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>
drivers/cpufreq/cppc_cpufreq.c

index b943bf78d3f5d2ad90f63eaf5426fb7094d86ec1..6fe0e972952af30ddab0a730fb0597f23dd8862c 100644 (file)
@@ -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,