]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ACPI: CPPC: Check all controls for fast switching
authorChristian Loehle <christian.loehle@arm.com>
Wed, 22 Jul 2026 09:38:24 +0000 (10:38 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 22 Jul 2026 13:09:14 +0000 (15:09 +0200)
ACPI 6.2, Section 6.2.11.2 permits _CPC registers to use flexible
address spaces. Linux advertises that capability through _OSC and parses
the address space of each _CPC register independently. A directly
accessible DESIRED_PERF combined with PCC-backed limits is therefore a
valid configuration.

cppc_allow_fast_switch() only checks DESIRED_PERF, although the fast-switch
callback passes DESIRED_PERF, MIN_PERF and MAX_PERF to cppc_set_perf(). If
a limit uses PCC, that function can sleep while called from scheduler
context.

Allow fast switching only when every supported control used by the
callback has an address space already accepted for fast access. Check the
complete policy domain, including initialized CPUs that are currently
offline and may later become the policy's managing CPU.

Fixes: 658fa7b1c47a ("ACPI: CPPC: Add cppc_get_perf() API to read performance controls")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Loehle <christian.loehle@arm.com>
Link: https://patch.msgid.link/20260722093825.1030594-2-christian.loehle@arm.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/cppc_acpi.c
drivers/cpufreq/cppc_cpufreq.c
include/acpi/cppc_acpi.h

index 9f572f481241aad99ff4b2b61d600decd02b224a..1d3a941004912147cb5bacb44f30e8d87b03d372 100644 (file)
@@ -475,17 +475,29 @@ bool acpi_cpc_valid(void)
 }
 EXPORT_SYMBOL_GPL(acpi_cpc_valid);
 
-bool cppc_allow_fast_switch(void)
+bool cppc_allow_fast_switch(const struct cpumask *cpus)
 {
-       struct cpc_register_resource *desired_reg;
+       struct cpc_register_resource *desired_reg, *min_reg, *max_reg;
        struct cpc_desc *cpc_ptr;
        int cpu;
 
-       for_each_online_cpu(cpu) {
+       for_each_cpu(cpu, cpus) {
                cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
+               if (!cpc_ptr)
+                       return false;
                desired_reg = &cpc_ptr->cpc_regs[DESIRED_PERF];
-               if (!CPC_IN_SYSTEM_MEMORY(desired_reg) &&
-                               !CPC_IN_SYSTEM_IO(desired_reg))
+               min_reg = &cpc_ptr->cpc_regs[MIN_PERF];
+               max_reg = &cpc_ptr->cpc_regs[MAX_PERF];
+
+               if (!CPC_SUPPORTED(desired_reg) ||
+                   (!CPC_IN_SYSTEM_MEMORY(desired_reg) &&
+                    !CPC_IN_SYSTEM_IO(desired_reg)) ||
+                   (CPC_SUPPORTED(min_reg) &&
+                    !CPC_IN_SYSTEM_MEMORY(min_reg) &&
+                    !CPC_IN_SYSTEM_IO(min_reg)) ||
+                   (CPC_SUPPORTED(max_reg) &&
+                    !CPC_IN_SYSTEM_MEMORY(max_reg) &&
+                    !CPC_IN_SYSTEM_IO(max_reg)))
                        return false;
        }
 
index f6cea0c54dd9cfd04d41b8d464c29725b0079e63..b943bf78d3f5d2ad90f63eaf5426fb7094d86ec1 100644 (file)
@@ -693,7 +693,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
                goto out;
        }
 
-       policy->fast_switch_possible = cppc_allow_fast_switch();
+       policy->fast_switch_possible = cppc_allow_fast_switch(policy->cpus);
        policy->dvfs_possible_from_any_cpu = true;
 
        /*
index 8693890a72756471fa6ccea003a0b8c81d0c95ae..8c191b9ac18ff4dae7a8b903c793a1285b3013d1 100644 (file)
@@ -170,7 +170,7 @@ extern u64 cppc_get_dmi_max_khz(void);
 extern unsigned int cppc_perf_to_khz(struct cppc_perf_caps *caps, unsigned int perf);
 extern unsigned int cppc_khz_to_perf(struct cppc_perf_caps *caps, unsigned int freq);
 extern bool acpi_cpc_valid(void);
-extern bool cppc_allow_fast_switch(void);
+bool cppc_allow_fast_switch(const struct cpumask *cpus);
 extern int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data);
 extern int cppc_get_transition_latency(int cpu);
 extern bool cpc_ffh_supported(void);
@@ -234,7 +234,8 @@ static inline bool acpi_cpc_valid(void)
 {
        return false;
 }
-static inline bool cppc_allow_fast_switch(void)
+
+static inline bool cppc_allow_fast_switch(const struct cpumask *cpus)
 {
        return false;
 }