* [PATCH v3 0/2] sched/cpufreq: fix schedutil's boost frequency handling
@ 2026-09-29 11:38 Ananthu C V
2026-09-29 11:38 ` [PATCH v3 1/2] arch_topology: seed capacity_freq_ref with highest available frequency Ananthu C V
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Ananthu C V @ 2026-09-29 11:38 UTC (permalink / raw)
To: Vincent Guittot, Sudeep Holla, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Viresh Kumar, Jie Zhan,
Pierre Gondois
Cc: linux-kernel, driver-core, linux-pm, Rafael J. Wysocki,
Oleg Keri, Ananthu C V
Schedutil's ability to reach boost frequencies depends on two values
being correct: policy max, which caps the resolved target frequency,
and the per-CPU capacity frequency reference, which anchors the
utilization-to-frequency mapping.
This series fixes a few gaps in how these values are maintained across
boost transitions:
The per-CPU capacity frequency reference is set once at policy
creation and never updated when boost is enabled afterwards, leaving
schedutil unable to target boost frequencies even at full utilization.
Introduce a helper cpufreq_frequency_table_max tp retrieve the highest
value listed in the frequency table and use the higher of it and cpuinfo
max_freq to seed capacity freq ref, letting it know the highest available
frequency from policy creation.
The generic boost callback only raises cpuinfo max, never lowers it.
Once boost is enabled, disabling it leaves cpuinfo max pinned at the
boost ceiling, keeping policy max stuck there too. Track the max
available non-boost frequency and use this in conjunction with the the
highest frequency retrieved from the table with cpufreq_frequency_table_max
to control boost frequencies when boost frequenies are available, allowing
the frequency to drop back to non boost values on boost disable. In
the absence of boost frequencies, the handling will fall back to using
cpuinfo->max_freq, preserving the current behaviour.
Oleg Keri tested the v2 on Lenovo Yoga Slim 7x Gen 11 and provided Tested-by,
but it is not picked up because of the implementation changes in v3.
Logs below for clear context:
Intermediate values from the time_in_state output and logs from bench
runs are truncated for brevity.
Before fix
----------
\# cd /sys/devices/system/cpu/cpufreq/
\# ls
boost policy0 policy12 policy6
\# cat boost
0
\# cat policy6/scaling_max_freq
4454400
\# echo 1 > boost
\# cat policy6/scaling_max_freq
4723200
\# echo 0 > boost
\# cat policy6/scaling_max_freq
4723200
\# echo 1 > boost
\# cat policy6/stats/time_in_state
355200 36958
4454400 650
4588800 0
4723200 0
\#
After fix
---------
\# cd /sys/devices/system/cpu/cpufreq/
\# ls
boost policy0 policy12 policy6
\# cat boost
0
\# cat policy6/scaling_max_freq
4454400
\# echo 1 > boost
\# cat policy6/scaling_max_freq
4723200
\# echo 0 > policy6/boost
\# cat policy6/scaling_max_freq
4454400
\# cat policy6/boost
0
\# cat policy6/stats/time_in_state
355200 40147
4454400 79
4588800 0
4723200 0
\# echo 1 > policy6/boost
\# cat policy6/stats/time_in_state
355200 44834
4454400 93
4588800 25
4723200 569
To: Vincent Guittot <vincent.guittot@linaro.org>
To: Sudeep Holla <sudeep.holla@kernel.org>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: Rafael J. Wysocki <rafael@kernel.org>
To: Danilo Krummrich <dakr@kernel.org>
To: Viresh Kumar <viresh.kumar@linaro.org>
To: Jie Zhan <zhanjie9@hisilicon.com>
To: Pierre Gondois <pierre.gondois@arm.com>
Cc: linux-kernel@vger.kernel.org
Cc: driver-core@lists.linux.dev
Cc: linux-pm@vger.kernel.org
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Cc: Oleg Keri <okerixx@gmail.com>
Signed-off-by: Ananthu C V <ananthu.cv@oss.qualcomm.com>
Changes in v3:
- dropped max_table_freq field and added cpuinfo_frequency_table_max function to retrieve max
available freq instead
- changed the condition in policy_st_boost for not relying in cpuinfo max_freq to be whether
boost frequencies are available, instead of whether a freq table is present.
- changed fixes tag for second patch to Fixes: db80ad776cd2 ("cpufreq: Remove driver default
policy->min/max init") - thanks to Zhongqiu Han for the checks and suggestions
- Link to v2: https://lore.kernel.org/r/20260908-schedutil-boost-frequency-handling-v2-0-25312a713699@oss.qualcomm.com
Changes in v2:
- introduced max_table_freq/max_base_freq fields in cpufreq policy struct to use for seeding
capacity freq ref and handle frequency switching during boost cycling.
- does not try to update get_next_freq or drop the one way guard in cpufreq_frequency_table_cpuinfo
anymore
- Link to v1: https://lore.kernel.org/all/20260806044230.909961-1-sibi.sankar@oss.qualcomm.com/
---
Ananthu C V (2):
arch_topology: seed capacity_freq_ref with highest available frequency
cpufreq: fix policy max not returning to non-boost freq on boost disable
drivers/base/arch_topology.c | 3 ++-
drivers/cpufreq/cpufreq.c | 16 +++++++++++++++-
drivers/cpufreq/freq_table.c | 9 ++++++++-
include/linux/cpufreq.h | 16 ++++++++++++++++
4 files changed, 41 insertions(+), 3 deletions(-)
---
base-commit: a8c591ed6b672915e0be57843f943a2a723aff40
change-id: 20260804-schedutil-boost-frequency-handling-8e6bf2387a4a
Best regards,
--
Ananthu C V <ananthu.cv@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v3 1/2] arch_topology: seed capacity_freq_ref with highest available frequency
2026-09-29 11:38 [PATCH v3 0/2] sched/cpufreq: fix schedutil's boost frequency handling Ananthu C V
@ 2026-09-29 11:38 ` Ananthu C V
2026-10-11 14:16 ` Zhongqiu Han
2026-09-29 11:38 ` [PATCH v3 2/2] cpufreq: fix policy max not returning to non-boost freq on boost disable Ananthu C V
2026-09-30 9:10 ` [PATCH v3 0/2] sched/cpufreq: fix schedutil's boost frequency handling Oleg Keri
2 siblings, 1 reply; 7+ messages in thread
From: Ananthu C V @ 2026-09-29 11:38 UTC (permalink / raw)
To: Vincent Guittot, Sudeep Holla, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Viresh Kumar, Jie Zhan,
Pierre Gondois
Cc: linux-kernel, driver-core, linux-pm, Rafael J. Wysocki,
Oleg Keri, Ananthu C V
capacity_freq_ref, the per-CPU frequency-invariance reference used by
schedutil, is seeded from policy->cpuinfo.max_freq at policy creation.
If boost isn't enabled yet at boot, max_freq reflects only the
non-boost ceiling, so capacity_freq_ref never learns about boost
frequencies for the policy's lifetime. Enabling boost later raises
policy->max, but capacity_freq_ref stays stale, leaving schedutil
unable to scale utilization or target a frequency above the non-boost
maximum.
Seed capacity_freq_ref with the highest available frequency regardless
of whether boost is enabled. Runtime enforcement, still handled by
policy->max, is unaffected.
Suggested-by: Vincent Guittot <vincent.guittot@linaro.org>
Signed-off-by: Ananthu C V <ananthu.cv@oss.qualcomm.com>
---
drivers/base/arch_topology.c | 3 ++-
include/linux/cpufreq.h | 13 +++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index 8c5e47c28d9a..92966be712d5 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -404,7 +404,8 @@ init_cpu_capacity_callback(struct notifier_block *nb,
cpumask_andnot(cpus_to_visit, cpus_to_visit, policy->related_cpus);
for_each_cpu(cpu, policy->related_cpus) {
- per_cpu(capacity_freq_ref, cpu) = policy->cpuinfo.max_freq;
+ per_cpu(capacity_freq_ref, cpu) = max(policy->cpuinfo.max_freq,
+ cpufreq_frequency_table_max(policy));
freq_inv_set_max_ratio(cpu,
per_cpu(capacity_freq_ref, cpu) * HZ_PER_KHZ);
}
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 35ce665edfd8..f048c925fd98 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -1107,6 +1107,19 @@ static inline int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
return idx;
}
+static inline unsigned int cpufreq_frequency_table_max(struct cpufreq_policy *policy)
+{
+ int idx;
+
+ if (!policy->freq_table)
+ return policy->cpuinfo.max_freq;
+
+ idx = cpufreq_frequency_table_target(policy, UINT_MAX, 0, UINT_MAX,
+ CPUFREQ_RELATION_H);
+
+ return policy->freq_table[idx].frequency;
+}
+
static inline int cpufreq_table_count_valid_entries(const struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *pos;
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v3 2/2] cpufreq: fix policy max not returning to non-boost freq on boost disable
2026-09-29 11:38 [PATCH v3 0/2] sched/cpufreq: fix schedutil's boost frequency handling Ananthu C V
2026-09-29 11:38 ` [PATCH v3 1/2] arch_topology: seed capacity_freq_ref with highest available frequency Ananthu C V
@ 2026-09-29 11:38 ` Ananthu C V
2026-10-11 14:17 ` Zhongqiu Han
2026-10-11 23:42 ` Dietmar Eggemann
2026-09-30 9:10 ` [PATCH v3 0/2] sched/cpufreq: fix schedutil's boost frequency handling Oleg Keri
2 siblings, 2 replies; 7+ messages in thread
From: Ananthu C V @ 2026-09-29 11:38 UTC (permalink / raw)
To: Vincent Guittot, Sudeep Holla, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Viresh Kumar, Jie Zhan,
Pierre Gondois
Cc: linux-kernel, driver-core, linux-pm, Rafael J. Wysocki,
Oleg Keri, Ananthu C V
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.
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")
Signed-off-by: Ananthu C V <ananthu.cv@oss.qualcomm.com>
---
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 */
/* 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);
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 0/2] sched/cpufreq: fix schedutil's boost frequency handling
2026-09-29 11:38 [PATCH v3 0/2] sched/cpufreq: fix schedutil's boost frequency handling Ananthu C V
2026-09-29 11:38 ` [PATCH v3 1/2] arch_topology: seed capacity_freq_ref with highest available frequency Ananthu C V
2026-09-29 11:38 ` [PATCH v3 2/2] cpufreq: fix policy max not returning to non-boost freq on boost disable Ananthu C V
@ 2026-09-30 9:10 ` Oleg Keri
2 siblings, 0 replies; 7+ messages in thread
From: Oleg Keri @ 2026-09-30 9:10 UTC (permalink / raw)
To: Ananthu C V, Vincent Guittot, Sudeep Holla, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Viresh Kumar, Jie Zhan,
Pierre Gondois
Cc: linux-kernel, driver-core, linux-pm, Rafael J. Wysocki
Tested on a Lenovo Yoga Slim 7x Gen 11 (Glymur): boost off 4032000,
boost on 4723200, back to 4032000 when boost is disabled again.
Tested-by: Oleg Keri <okerixx@gmail.com> # Lenovo Yoga Slim 7x Gen 11
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/2] arch_topology: seed capacity_freq_ref with highest available frequency
2026-09-29 11:38 ` [PATCH v3 1/2] arch_topology: seed capacity_freq_ref with highest available frequency Ananthu C V
@ 2026-10-11 14:16 ` Zhongqiu Han
0 siblings, 0 replies; 7+ messages in thread
From: Zhongqiu Han @ 2026-10-11 14:16 UTC (permalink / raw)
To: Ananthu C V, Vincent Guittot, Sudeep Holla, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Viresh Kumar, Jie Zhan,
Pierre Gondois
Cc: linux-kernel, driver-core, linux-pm, Rafael J. Wysocki,
Oleg Keri, zhongqiu.han
On 9/29/2026 7:38 PM, Ananthu C V wrote:
> capacity_freq_ref, the per-CPU frequency-invariance reference used by
> schedutil, is seeded from policy->cpuinfo.max_freq at policy creation.
> If boost isn't enabled yet at boot, max_freq reflects only the
> non-boost ceiling, so capacity_freq_ref never learns about boost
> frequencies for the policy's lifetime. Enabling boost later raises
> policy->max, but capacity_freq_ref stays stale, leaving schedutil
> unable to scale utilization or target a frequency above the non-boost
> maximum.
>
> Seed capacity_freq_ref with the highest available frequency regardless
> of whether boost is enabled. Runtime enforcement, still handled by
> policy->max, is unaffected.
>
> Suggested-by: Vincent Guittot <vincent.guittot@linaro.org>
> Signed-off-by: Ananthu C V <ananthu.cv@oss.qualcomm.com>
> ---
> drivers/base/arch_topology.c | 3 ++-
> include/linux/cpufreq.h | 13 +++++++++++++
> 2 files changed, 15 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
> index 8c5e47c28d9a..92966be712d5 100644
> --- a/drivers/base/arch_topology.c
> +++ b/drivers/base/arch_topology.c
> @@ -404,7 +404,8 @@ init_cpu_capacity_callback(struct notifier_block *nb,
> cpumask_andnot(cpus_to_visit, cpus_to_visit, policy->related_cpus);
>
> for_each_cpu(cpu, policy->related_cpus) {
> - per_cpu(capacity_freq_ref, cpu) = policy->cpuinfo.max_freq;
> + per_cpu(capacity_freq_ref, cpu) = max(policy->cpuinfo.max_freq,
> + cpufreq_frequency_table_max(policy));
> freq_inv_set_max_ratio(cpu,
> per_cpu(capacity_freq_ref, cpu) * HZ_PER_KHZ);
> }
> diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
> index 35ce665edfd8..f048c925fd98 100644
> --- a/include/linux/cpufreq.h
> +++ b/include/linux/cpufreq.h
> @@ -1107,6 +1107,19 @@ static inline int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
> return idx;
> }
>
> +static inline unsigned int cpufreq_frequency_table_max(struct cpufreq_policy *policy)
> +{
> + int idx;
> +
> + if (!policy->freq_table)
> + return policy->cpuinfo.max_freq;
> +
> + idx = cpufreq_frequency_table_target(policy, UINT_MAX, 0, UINT_MAX,
> + CPUFREQ_RELATION_H);
> +
> + return policy->freq_table[idx].frequency;
> +}
> +
The current implementation looks correct and works well. That said, we
might be able to optimize it with a simpler implementation as shown
below:
+/**
+ * cpufreq_frequency_table_max - Get the highest frequency in a
policy's table
+ * @policy: the &struct cpufreq_policy to inspect
+ *
+ * Boost entries (%CPUFREQ_BOOST_FREQ) are taken into account regardless of
+ * whether or not boost is currently enabled.
+ *
+ * Return: the highest valid frequency in @policy->freq_table, or 0 if
there
+ * is no frequency table.
+ */
+static inline unsigned int
+cpufreq_frequency_table_max(const struct cpufreq_policy *policy)
+{
+ struct cpufreq_frequency_table *pos;
+ unsigned int max_freq = 0;
+
+ if (!policy->freq_table)
+ return 0;
+
+ cpufreq_for_each_valid_entry(pos, policy->freq_table)
+ max_freq = max(max_freq, pos->frequency);
+
+ return max_freq;
+}
+
> static inline int cpufreq_table_count_valid_entries(const struct cpufreq_policy *policy)
> {
> struct cpufreq_frequency_table *pos;
>
Besides, could you comment patch v2 Dietmar's comments? It seems that
patch 1/2 exposes a pre-existing ordering issue. On DT systems,
cpu_scale is normalized only from the CPUFREQ_CREATE_POLICY notification
of the last policy, so the cpufreq pressure of every other policy is
computed in cpufreq_init_policy() from the raw, not yet normalized,
capacity, and stays cached until the next cpufreq_set_policy() for that
policy. So far this was harmless because capacity_freq_ref ==
policy->max at boot, so the pressure was 0 regardless of the capacity.
With 1/2, capacity_freq_ref covers the boost frequencies while boost is
disabled at boot, so policy->max < capacity_freq_ref becomes the common
case and the stale value becomes visible (102 vs 79 on Juno R0, as
reported by Dietmar). Perhaps the limits of all policies could be
refreshed once from parsing_done_workfn(), after the capacities have
been normalized?
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -454,9 +454,27 @@ core_initcall(register_cpufreq_notifier);
static void parsing_done_workfn(struct work_struct *work)
{
+ int cpu;
+
cpufreq_unregister_notifier(&init_cpu_capacity_notifier,
CPUFREQ_POLICY_NOTIFIER);
free_cpumask_var(cpus_to_visit);
+
+ /*
+ * The cpufreq pressure of all policies but the last one has been
+ * computed before topology_normalize_cpu_scale(), so refresh it now
+ * that cpu_scale is final.
+ */
+ for_each_possible_cpu(cpu) {
+ struct cpufreq_policy *policy __free(put_cpufreq_policy) =
+ cpufreq_cpu_get(cpu);
+
+ if (!policy || cpu != policy->cpu)
+ continue;
+
+ guard(cpufreq_policy_write)(policy);
+ refresh_frequency_limits(policy);
+ }
--
Thx and BRs,
Zhongqiu Han
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/2] cpufreq: fix policy max not returning to non-boost freq on boost disable
2026-09-29 11:38 ` [PATCH v3 2/2] cpufreq: fix policy max not returning to non-boost freq on boost disable Ananthu C V
@ 2026-10-11 14:17 ` Zhongqiu Han
2026-10-11 23:42 ` Dietmar Eggemann
1 sibling, 0 replies; 7+ messages in thread
From: Zhongqiu Han @ 2026-10-11 14:17 UTC (permalink / raw)
To: Ananthu C V, Vincent Guittot, Sudeep Holla, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Viresh Kumar, Jie Zhan,
Pierre Gondois
Cc: linux-kernel, driver-core, linux-pm, Rafael J. Wysocki,
Oleg Keri, zhongqiu.han
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@oss.qualcomm.com>
> ---
> 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
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/2] cpufreq: fix policy max not returning to non-boost freq on boost disable
2026-09-29 11:38 ` [PATCH v3 2/2] cpufreq: fix policy max not returning to non-boost freq on boost disable Ananthu C V
2026-10-11 14:17 ` Zhongqiu Han
@ 2026-10-11 23:42 ` Dietmar Eggemann
1 sibling, 0 replies; 7+ messages in thread
From: Dietmar Eggemann @ 2026-10-11 23:42 UTC (permalink / raw)
To: Ananthu C V, Vincent Guittot, Sudeep Holla, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Viresh Kumar, Jie Zhan,
Pierre Gondois
Cc: linux-kernel, driver-core, linux-pm, Rafael J. Wysocki, Oleg Keri
On 29.09.26 13:38, 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.
> 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")
> Signed-off-by: Ananthu C V <ananthu.cv@oss.qualcomm.com>
> ---
> 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);
This looks pretty convoluted. You have to find the correct freq argument
for freq_qos_update_request() for freq_table drivers but work around
this for e.g. the CPPC driver.
cpufreq_driver->set_boost() of e.g. the freq_table scmi-cpufreq driver
does not adjust policy->cpuinfo.max_freq for 'boost state = 0':
cpufreq_boost_set_sw() {
cpufreq_frequency_table_cpuinfo()
policy->cpuinfo.min_freq = min_freq
if (policy->cpuinfo.max_freq < max_freq) <-- (*)
policy->cpuinfo.max_freq = max_freq
}
whereas the one of the cppc_cpufreq driver does:
cppc_cpufreq_set_boost() {
if (state)
policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, caps->highest_perf)
else
policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, caps->nominal_perf)
}
(*) This was added by 538b0188da46 ("cpufreq: ACPI: Set cpuinfo.max_freq
directly if max boost is known"). With this the
policy_set_boost(policy, enable) {
cpufreq_driver->set_boost(policy, enable)
freq_qos_update_request(..., policy->cpuinfo.max_freq)
}
[...]
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-11 23:42 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-29 11:38 [PATCH v3 0/2] sched/cpufreq: fix schedutil's boost frequency handling Ananthu C V
2026-09-29 11:38 ` [PATCH v3 1/2] arch_topology: seed capacity_freq_ref with highest available frequency Ananthu C V
2026-10-11 14:16 ` Zhongqiu Han
2026-09-29 11:38 ` [PATCH v3 2/2] cpufreq: fix policy max not returning to non-boost freq on boost disable Ananthu C V
2026-10-11 14:17 ` Zhongqiu Han
2026-10-11 23:42 ` Dietmar Eggemann
2026-09-30 9:10 ` [PATCH v3 0/2] sched/cpufreq: fix schedutil's boost frequency handling Oleg Keri
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®