mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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

  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®