Re: [PATCH v3 2/2] cpufreq: fix policy max not returning to non-boost freq on boost disable
From: Zhongqiu Han
Date: Sun Oct 11 2026 - 12:26:47 EST
On 9/29/2026 7:38 PM, Ananthu C V wrote:
Commit 538b0188da46 ("cpufreq: ACPI: Set cpuinfo.max_freq directly if
max boost is known") introduced a guard for cpuinfo max updates to only
increase, to preserve driver-set values above the freq table maximum.
I think as discussed in v2 the changelog should describe the impact of
commit 6e39ba4e5a82 ("cpufreq: Add boost_freq_req QoS request"), since
it is relevant to understanding this fix.
Later commit db80ad776cd2 ("cpufreq: Remove driver default policy->min/max
init") dropped the 'policy->max = max_freq' assignment from
cpufreq_frequency_table_cpuinfo, leaving that one-way-guard to be the only
path for cpuinfo max and consequently policy max to be updated, causing
both values to be stuck at boost frequency even when boost is disabled.
Unconditionally track the highest non-boost frequency (max_base_freq)
in the freq table. If boost frequencies are available, use the highest
frequency from the table/max_base_freq instead of cpuinfo->max_freq to
control boost values, so the value can decrease again when boost is
disabled.
Fixes: db80ad776cd2 ("cpufreq: Remove driver default policy->min/max init")
Patch 2/2 currently depends on a helper introduced by patch 1/2, which
prevents it from being backported independently. Since 2/2 is an
independent fix that should be backported to stable on its own, I think
it should be placed before patch 1/2 in the series and kept self
contained.
Signed-off-by: Ananthu C V <ananthu.cv@xxxxxxxxxxxxxxxx>
---
drivers/cpufreq/cpufreq.c | 16 +++++++++++++++-
drivers/cpufreq/freq_table.c | 9 ++++++++-
include/linux/cpufreq.h | 3 +++
3 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 96515880b4ac..5e6afb6f8826 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -574,6 +574,8 @@ static ssize_t show_local_boost(struct cpufreq_policy *policy, char *buf)
static int policy_set_boost(struct cpufreq_policy *policy, bool enable)
{
+ unsigned int max_freq;
+ unsigned int max_table_freq;
int ret;
if (policy->boost_enabled == enable)
@@ -587,7 +589,19 @@ static int policy_set_boost(struct cpufreq_policy *policy, bool enable)
return ret;
}
- ret = freq_qos_update_request(&policy->boost_freq_req, policy->cpuinfo.max_freq);
+ max_table_freq = cpufreq_frequency_table_max(policy);
+
+ if (policy_has_boost_freq(policy)) {
+ max_freq = enable ? max_table_freq : policy->cpuinfo.max_base_freq;
+
+ if (!max_freq)
+ /* when the freq table contains only boost frequencies */
+ max_freq = max_table_freq;
+ } else {
+ max_freq = policy->cpuinfo.max_freq;
+ }
+
+ ret = freq_qos_update_request(&policy->boost_freq_req, max_freq);
if (ret < 0) {
policy->boost_enabled = !policy->boost_enabled;
cpufreq_driver->set_boost(policy, policy->boost_enabled);
diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
index ea994647abc8..820ed4559f6c 100644
--- a/drivers/cpufreq/freq_table.c
+++ b/drivers/cpufreq/freq_table.c
@@ -14,7 +14,7 @@
* FREQUENCY TABLE HELPERS *
*********************************************************************/
-static bool policy_has_boost_freq(struct cpufreq_policy *policy)
+bool policy_has_boost_freq(struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *pos, *table = policy->freq_table;
@@ -27,17 +27,22 @@ static bool policy_has_boost_freq(struct cpufreq_policy *policy)
return false;
}
+EXPORT_SYMBOL_GPL(policy_has_boost_freq);
int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *pos, *table = policy->freq_table;
unsigned int min_freq = ~0;
unsigned int max_freq = 0;
+ unsigned int max_base_freq = 0;
unsigned int freq, i;
cpufreq_for_each_valid_entry_idx(pos, table, i) {
freq = pos->frequency;
+ if (!(pos->flags & CPUFREQ_BOOST_FREQ) && freq > max_base_freq)
+ max_base_freq = freq;
+
if ((!cpufreq_boost_enabled() || !policy->boost_enabled)
&& (pos->flags & CPUFREQ_BOOST_FREQ))
continue;
@@ -57,6 +62,8 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy)
if (policy->cpuinfo.max_freq < max_freq)
policy->cpuinfo.max_freq = max_freq;
+ policy->cpuinfo.max_base_freq = max_base_freq;
+
if (min_freq == ~0)
return -EINVAL;
else
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index f048c925fd98..7bb58405e004 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -45,6 +45,7 @@ enum cpufreq_table_sorting {
struct cpufreq_cpuinfo {
unsigned int max_freq;
unsigned int min_freq;
+ unsigned int max_base_freq; /* Highest non-boost frequency in the table */
To avoid introducing a new member that is only used by frequency-table
drivers, please consider the following implementation instead:
If there are boost entries in the frequency table, the table is the only
source of cpuinfo.max_freq, so let cpufreq_frequency_table_cpuinfo() set
it unconditionally in that case.
diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
index ea99464..8c8bad2 100644
--- a/drivers/cpufreq/freq_table.c
+++ b/drivers/cpufreq/freq_table.c
@@ -33,14 +33,17 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy)
struct cpufreq_frequency_table *pos, *table = policy->freq_table;
unsigned int min_freq = ~0;
unsigned int max_freq = 0;
+ bool has_boost = false;
unsigned int freq, i;
cpufreq_for_each_valid_entry_idx(pos, table, i) {
freq = pos->frequency;
- if ((!cpufreq_boost_enabled() || !policy->boost_enabled)
- && (pos->flags & CPUFREQ_BOOST_FREQ))
- continue;
+ if (pos->flags & CPUFREQ_BOOST_FREQ) {
+ has_boost = true;
+ if (!cpufreq_boost_enabled() || !policy->boost_enabled)
+ continue;
+ }
pr_debug("table entry %u: %u kHz\n", i, freq);
if (freq < min_freq)
@@ -50,17 +53,20 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy)
}
policy->cpuinfo.min_freq = min_freq;
+
+ if (min_freq == ~0)
+ return -EINVAL;
+
/*
* If the driver has set its own cpuinfo.max_freq above max_freq, leave
- * it as is.
+ * it as is. This does not apply to tables with boost entries, because
+ * the table is the only source of cpuinfo.max_freq then and it needs
+ * to go down again when boost is disabled.
*/
- if (policy->cpuinfo.max_freq < max_freq)
+ if (has_boost || policy->cpuinfo.max_freq < max_freq)
policy->cpuinfo.max_freq = max_freq;
- if (min_freq == ~0)
- return -EINVAL;
- else
- return 0;
+ return 0;
}
int cpufreq_frequency_table_verify(struct cpufreq_policy_data *policy)
/* in 10^(-9) s = nanoseconds */
unsigned int transition_latency;
@@ -791,6 +792,8 @@ struct cpufreq_frequency_table {
int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy);
+bool policy_has_boost_freq(struct cpufreq_policy *policy);
+
int cpufreq_frequency_table_verify(struct cpufreq_policy_data *policy);
int cpufreq_generic_frequency_table_verify(struct cpufreq_policy_data *policy);
--
Thx and BRs,
Zhongqiu Han