From: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
To: Christian Loehle <christian.loehle@arm.com>, rafael@kernel.org
Cc: zhenglifeng1@huawei.com, viresh.kumar@linaro.org,
linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, linux-doc@vger.kernel.org,
Mario Limonciello <mario.limonciello@amd.com>,
K Prateek Nayak <kprateek.nayak@amd.com>,
Huang Rui <ray.huang@amd.com>, Perry Yuan <perry.yuan@amd.com>,
"Gautham R . Shenoy" <gautham.shenoy@amd.com>,
Vanshidhar Konda <vanshikonda@os.amperecomputing.com>,
Shubhang Kaushik <sh@gentwo.org>,
Pierre Gondois <pierre.gondois@arm.com>,
Beata Michalska <beata.michalska@arm.com>,
Dietmar Eggemann <dietmar.eggemann@arm.com>,
Ionela Voinescu <ionela.voinescu@arm.com>,
Sudeep Holla <sudeep.holla@kernel.org>,
Lukasz Luba <lukasz.luba@arm.com>,
Jeremy Linton <jeremy.linton@arm.com>,
Peter Zijlstra <peterz@infradead.org>,
jonathanh@nvidia.com, zhanjie9@hisilicon.com,
Vincent Guittot <vincent.guittot@linaro.org>,
Jonathan Corbet <corbet@lwn.net>,
Shuah Khan <skhan@linuxfoundation.org>,
Randy Dunlap <rdunlap@infradead.org>,
zhongqiu.han@oss.qualcomm.com
Subject: Re: [PATCH 3/3] cpufreq: Skip updates for unchanged resolved limits
Date: Fri, 2 Oct 2026 20:46:05 +0800 [thread overview]
Message-ID: <4e218e26-19ac-4c05-94a8-c156952f4da0@oss.qualcomm.com> (raw)
In-Reply-To: <20260929102957.2591657-4-christian.loehle@arm.com>
On 9/29/2026 6:29 PM, Christian Loehle wrote:
> Commit 9801be8bef65 ("cpufreq: Avoid redundant target() calls for unchanged
> limits") introduced policy->update_limits to skip ->target() when both the
> frequency and effective limits are unchanged. schedutil still bypasses its
> frequency cache on every limit notification for CPUFREQ_NEED_UPDATE_LIMITS
> drivers, and fast switching never consumes the pending state.
>
> Keep raw QoS/limit notifications triggering schedutil recomputation, but
> only let CPUFREQ_NEED_UPDATE_LIMITS force a same-frequency callback when
> resolved policy->{min,max} changes remain pending. Expose that test through
> a cpufreq-core helper.
>
> Consume pending state before both slow and fast callbacks, including
> frequency changes, instead of clearing it only in the target == cur case.
> Use release/acquire ordering for the limits and restore pending state on
> failure, preserving concurrent updates and retries. Favor the maximum
> if lockless limit reads observe an inverted pair.
>
> This applies to cppc-cpufreq and amd-pstate's frequency-based paths; the
> ->adjust_perf() path is unchanged.
>
> Signed-off-by: Christian Loehle <christian.loehle@arm.com>
> ---
> drivers/cpufreq/cpufreq.c | 82 ++++++++++++++++++++++++++------
> include/linux/cpufreq.h | 1 +
> kernel/sched/cpufreq_schedutil.c | 19 ++------
> 3 files changed, 72 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> index 44bda2f32fcf..9e932c2a986d 100644
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -2058,6 +2058,21 @@ bool cpufreq_driver_test_flags(u16 flags)
> return !!(cpufreq_driver->flags & flags);
> }
>
> +/**
> + * cpufreq_driver_needs_limits_update - Check for a pending driver limit update.
> + * @policy: CPU frequency policy to check.
> + *
> + * Return: Whether changed resolved limits require a driver callback.
> + */
> +bool cpufreq_driver_needs_limits_update(struct cpufreq_policy *policy)
> +{
> + if (!cpufreq_driver_test_flags(CPUFREQ_NEED_UPDATE_LIMITS))
> + return false;
> +
> + /* Pairs with cpufreq_set_update_limits(). */
> + return smp_load_acquire(&policy->update_limits);
> +}
> +
> /**
> * cpufreq_get_current_driver - Return the current driver's name.
> *
> @@ -2183,6 +2198,30 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier);
> * GOVERNORS *
> *********************************************************************/
>
> +static bool cpufreq_test_and_clear_update_limits(struct cpufreq_policy *policy)
> +{
> + if (!cpufreq_driver_needs_limits_update(policy))
> + return false;
> +
> + return xchg(&policy->update_limits, false);
Since policy->update_limits is a bool, this would require a 1-byte
xchg(), which, as far as I know, is not supported on all architectures.
such as on sparc:
static __always_inline unsigned long
__arch_xchg(unsigned long x, __volatile__ void * ptr, int size)
{
switch (size) {
case 2:
return xchg16(ptr, x);
case 4:
return xchg32(ptr, x);
case 8:
return xchg64(ptr, x);
}
__xchg_called_with_bad_pointer();
return x;
}
> +}
> +
> +static void cpufreq_set_update_limits(struct cpufreq_policy *policy)
> +{
> + /* Publish the new limits before making their update pending. */
> + smp_store_release(&policy->update_limits, true);
> +}
> +
> +static void cpufreq_read_policy_limits(struct cpufreq_policy *policy,
> + unsigned int *min, unsigned int *max)
> +{
> + /* Lockless reads can mix limit updates; favor max if inverted. */
> + *min = READ_ONCE(policy->min);
> + *max = READ_ONCE(policy->max);
> + if (unlikely(*min > *max))
> + *min = *max;
> +}
> +
> /**
> * cpufreq_driver_fast_switch - Carry out a fast CPU frequency switch.
> * @policy: cpufreq policy to switch the frequency for.
> @@ -2209,14 +2248,20 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier);
> unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy,
> unsigned int target_freq)
> {
> - unsigned int freq;
> + bool update_limits;
> + unsigned int min, max, freq;
> int cpu;
>
> - target_freq = clamp_val(target_freq, policy->min, policy->max);
> + update_limits = cpufreq_test_and_clear_update_limits(policy);
> + cpufreq_read_policy_limits(policy, &min, &max);
> + target_freq = clamp_val(target_freq, min, max);
> freq = cpufreq_driver->fast_switch(policy, target_freq);
>
> - if (!freq)
> + if (!freq) {
> + if (update_limits)
> + cpufreq_set_update_limits(policy);
> return 0;
> + }
>
> policy->cur = freq;
> arch_set_freq_scale(policy->related_cpus, freq,
> @@ -2367,12 +2412,16 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
> unsigned int relation)
> {
> unsigned int old_target_freq = target_freq;
> + unsigned int min, max;
> + bool update_limits;
> + int ret;
>
> if (cpufreq_disabled())
> return -ENODEV;
>
> - target_freq = __resolve_freq(policy, target_freq, policy->min,
> - policy->max, relation);
> + update_limits = cpufreq_test_and_clear_update_limits(policy);
> + cpufreq_read_policy_limits(policy, &min, &max);
> + target_freq = __resolve_freq(policy, target_freq, min, max, relation);
>
> pr_debug("CPU %u: cur %u kHz -> target %u kHz (req %u kHz, rel %u)\n",
> policy->cpu, policy->cur, target_freq, old_target_freq, relation);
> @@ -2384,11 +2433,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
> * calls.
> */
> if (target_freq == policy->cur) {
> - if (!(cpufreq_driver->flags & CPUFREQ_NEED_UPDATE_LIMITS) ||
> - !policy->update_limits)
> + if (!update_limits)
> return 0;
> -
> - policy->update_limits = false;
> }
>
> if (cpufreq_driver->target) {
> @@ -2399,13 +2445,19 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
> if (!policy->efficiencies_available)
> relation &= ~CPUFREQ_RELATION_E;
>
> - return cpufreq_driver->target(policy, target_freq, relation);
> + ret = cpufreq_driver->target(policy, target_freq, relation);
> + } else if (cpufreq_driver->target_index) {
> + ret = __target_index(policy, policy->cached_resolved_idx);
> + } else {
> + if (update_limits)
> + cpufreq_set_update_limits(policy);
> + return -EINVAL;
> }
>
> - if (!cpufreq_driver->target_index)
> - return -EINVAL;
> + if (ret && update_limits)
> + cpufreq_set_update_limits(policy);
>
> - return __target_index(policy, policy->cached_resolved_idx);
> + return ret;
> }
> EXPORT_SYMBOL_GPL(__cpufreq_driver_target);
>
> @@ -2681,7 +2733,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,
> CPUFREQ_RELATION_H);
> if (freq != policy->max) {
> WRITE_ONCE(policy->max, freq);
> - policy->update_limits = true;
> + cpufreq_set_update_limits(policy);
> }
>
> freq = __resolve_freq(policy, new_data.min, new_data.min, new_data.max,
> @@ -2689,7 +2741,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,
> freq = min(freq, policy->max);
> if (freq != policy->min) {
> WRITE_ONCE(policy->min, freq);
> - policy->update_limits = true;
> + cpufreq_set_update_limits(policy);
> }
>
> trace_cpu_frequency_limits(policy);
> diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
> index a0a7619d11fd..acd60c2b30f1 100644
> --- a/include/linux/cpufreq.h
> +++ b/include/linux/cpufreq.h
> @@ -499,6 +499,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data);
> void cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
>
> bool cpufreq_driver_test_flags(u16 flags);
> +bool cpufreq_driver_needs_limits_update(struct cpufreq_policy *policy);
> const char *cpufreq_get_current_driver(void);
> void *cpufreq_get_driver_data(void);
>
> diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
> index 49ccd6f1c185..43e448ee3c4c 100644
> --- a/kernel/sched/cpufreq_schedutil.c
> +++ b/kernel/sched/cpufreq_schedutil.c
> @@ -123,22 +123,11 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
> static bool sugov_update_next_freq(struct sugov_policy *sg_policy, u64 time,
> unsigned int next_freq)
> {
> - if (sg_policy->need_freq_update) {
> - sg_policy->need_freq_update = false;
> - /*
> - * The policy limits have changed, but if the return value of
> - * cpufreq_driver_resolve_freq() after applying the new limits
> - * is still equal to the previously selected frequency, the
> - * driver callback need not be invoked unless the driver
> - * specifically wants that to happen on every update of the
> - * policy limits.
> - */
> - if (sg_policy->next_freq == next_freq &&
> - !cpufreq_driver_test_flags(CPUFREQ_NEED_UPDATE_LIMITS))
> - return false;
> - } else if (sg_policy->next_freq == next_freq) {
> + sg_policy->need_freq_update = false;
> +
> + if (sg_policy->next_freq == next_freq &&
> + !cpufreq_driver_needs_limits_update(sg_policy->policy))
> return false;
> - }
>
> sg_policy->next_freq = next_freq;
> sg_policy->last_freq_update_time = time;
--
Thx and BRs,
Zhongqiu Han
next prev parent reply other threads:[~2026-10-02 12:46 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-29 10:29 [PATCH 0/3] cpufreq: Resolve CPPC frequencies to performance levels Christian Loehle
2026-09-29 10:29 ` [PATCH 1/3] cpufreq: Add a driver frequency resolution callback Christian Loehle
2026-10-02 10:20 ` Zhongqiu Han
2026-09-29 10:29 ` [PATCH 2/3] cpufreq: CPPC: Resolve frequencies to performance levels Christian Loehle
2026-10-02 12:29 ` Zhongqiu Han
2026-09-29 10:29 ` [PATCH 3/3] cpufreq: Skip updates for unchanged resolved limits Christian Loehle
2026-10-02 12:46 ` Zhongqiu Han [this message]
2026-09-30 20:06 ` [PATCH 0/3] cpufreq: Resolve CPPC frequencies to performance levels Mario Limonciello
2026-10-01 10:34 ` Peter Zijlstra
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4e218e26-19ac-4c05-94a8-c156952f4da0@oss.qualcomm.com \
--to=zhongqiu.han@oss.qualcomm.com \
--cc=beata.michalska@arm.com \
--cc=christian.loehle@arm.com \
--cc=corbet@lwn.net \
--cc=dietmar.eggemann@arm.com \
--cc=gautham.shenoy@amd.com \
--cc=ionela.voinescu@arm.com \
--cc=jeremy.linton@arm.com \
--cc=jonathanh@nvidia.com \
--cc=kprateek.nayak@amd.com \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lukasz.luba@arm.com \
--cc=mario.limonciello@amd.com \
--cc=perry.yuan@amd.com \
--cc=peterz@infradead.org \
--cc=pierre.gondois@arm.com \
--cc=rafael@kernel.org \
--cc=ray.huang@amd.com \
--cc=rdunlap@infradead.org \
--cc=sh@gentwo.org \
--cc=skhan@linuxfoundation.org \
--cc=sudeep.holla@kernel.org \
--cc=vanshikonda@os.amperecomputing.com \
--cc=vincent.guittot@linaro.org \
--cc=viresh.kumar@linaro.org \
--cc=zhanjie9@hisilicon.com \
--cc=zhenglifeng1@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®