[PATCH v5 2/2] cpufreq: acpi-cpufreq: don't mask firmware P-state changes
From: lirongqing
Date: Fri Nov 06 2026 - 13:41:18 EST
From: Li RongQing <lirongqing@xxxxxxxxx>
get_cur_freq_on_cpu() detects a firmware frequency change behind the
OS's back by comparing the frequency decoded from the hardware with the
frequency cached by the cpufreq core. The previous patch fixes the
cached side of that comparison; this patch fixes the decoded side.
policy->freq_table is built by filtering out _PSS entries that are not
lower in frequency than the previous one, so firmware may autonomously
switch the CPU to one of the filtered-out P-states. The status of such
a P-state matches no freq_table entry, and extract_msr() then falls
back to freq_table[0].frequency, i.e. the highest frequency, instead of
reporting that the status is unknown.
As a result the comparison is defeated exactly when the OS is already
at the P-state corresponding to freq_table[0], which is particularly likely
under the performance governor: freq equals cached_freq, data->resume is
left clear, and the next ->target() call short-circuits without
rewriting the control register, leaving the CPU at the firmware-selected
P-state. Fixing the index mismatch alone therefore does not make the
detection work in that case.
Return 0 from extract_msr() when the status cannot be matched so that
the fallback no longer hides the firmware change, and treat a zero
result as a firmware frequency change in get_cur_freq_on_cpu(). Keep
returning the cached frequency to the cpufreq core in that case: a zero
return value there would be indistinguishable from a read failure and
would feed 0 into the cpufreq statistics. This also covers the
ACPI_ADR_SPACE_SYSTEM_IO path, where extract_io() already returns 0 for
an unmatched status.
check_freqs() is unaffected: it compares against a frequency taken from
freq_table, which is never 0, so an unknown status keeps the
verification loop from declaring success.
Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Closes: https://sashiko.dev/#/patchset/20260910111329.2220-1-lirongqing@xxxxxxxxx
Fixes: e56a727b023d ("[CPUFREQ] Make acpi-cpufreq more robust against BIOS freq changes behind our back.")
Signed-off-by: Li RongQing <lirongqing@xxxxxxxxx>
---
drivers/cpufreq/acpi-cpufreq.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index 2d7d147..1e7dc1c 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -225,7 +225,7 @@ static unsigned extract_msr(struct cpufreq_policy *policy, u32 msr)
cpufreq_for_each_entry(pos, policy->freq_table)
if (msr == perf->states[pos->driver_data].status)
return pos->frequency;
- return policy->freq_table[0].frequency;
+ return 0;
}
static unsigned extract_freq(struct cpufreq_policy *policy, u32 val)
@@ -373,12 +373,14 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
cached_freq = perf->states[perf->state].core_frequency * 1000;
freq = extract_freq(policy, get_cur_val(cpumask_of(cpu), data));
- if (freq != cached_freq) {
+ if (!freq || freq != cached_freq) {
/*
* The dreaded BIOS frequency change behind our back.
* Force set the frequency on next target call.
*/
data->resume = 1;
+ if (!freq)
+ freq = cached_freq;
}
pr_debug("cur freq = %u\n", freq);
--
2.9.4