diff mbox series

[1/2] cpufreq_schedutil: Refactor sugov_cpu_is_busy()

Message ID 20240619031250.2936087-2-tj@kernel.org (mailing list archive)
State Handled Elsewhere, archived
Headers show
Series [1/2] cpufreq_schedutil: Refactor sugov_cpu_is_busy() | expand

Commit Message

Tejun Heo June 19, 2024, 3:12 a.m. UTC
sugov_cpu_is_busy() is used to avoid decreasing performance level while the
CPU is busy and called by sugov_update_single_freq() and
sugov_update_single_perf(). Both callers repeat the same pattern to first
test for uclamp and then the business. Let's refactor so that the tests
aren't repeated.

The new helper is named sugov_hold_freq() and tests both the uclamp
exception and CPU business. No functional changes. This will make adding
more exception conditions easier.

Signed-off-by: Tejun Heo <tj@kernel.org>
Reviewed-by: David Vernet <dvernet@meta.com>
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
---
 kernel/sched/cpufreq_schedutil.c | 38 +++++++++++++++-----------------
 1 file changed, 18 insertions(+), 20 deletions(-)

Comments

Christian Loehle June 19, 2024, 2:07 p.m. UTC | #1
On 6/19/24 04:12, Tejun Heo wrote:
> sugov_cpu_is_busy() is used to avoid decreasing performance level while the
> CPU is busy and called by sugov_update_single_freq() and
> sugov_update_single_perf(). Both callers repeat the same pattern to first
> test for uclamp and then the business. Let's refactor so that the tests
> aren't repeated.
> 
> The new helper is named sugov_hold_freq() and tests both the uclamp
> exception and CPU business. No functional changes. This will make adding
> more exception conditions easier.
> 
> Signed-off-by: Tejun Heo <tj@kernel.org>
> Reviewed-by: David Vernet <dvernet@meta.com>
> Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> Cc: Viresh Kumar <viresh.kumar@linaro.org>
> ---
>  kernel/sched/cpufreq_schedutil.c | 38 +++++++++++++++-----------------
>  1 file changed, 18 insertions(+), 20 deletions(-)
> 
> diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
> index eece6244f9d2..972b7dd65af2 100644
> --- a/kernel/sched/cpufreq_schedutil.c
> +++ b/kernel/sched/cpufreq_schedutil.c
> @@ -325,16 +325,27 @@ static unsigned long sugov_iowait_apply(struct sugov_cpu *sg_cpu, u64 time,
>  }
>  
>  #ifdef CONFIG_NO_HZ_COMMON
> -static bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu)
> +static bool sugov_hold_freq(struct sugov_cpu *sg_cpu)
>  {
> -	unsigned long idle_calls = tick_nohz_get_idle_calls_cpu(sg_cpu->cpu);
> -	bool ret = idle_calls == sg_cpu->saved_idle_calls;
> +	unsigned long idle_calls;
> +	bool ret;
> +
> +	/* if capped by uclamp_max, always update to be in compliance */
> +	if (uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)))
> +		return false;
> +
> +	/*
> +	 * Maintain the frequency if the CPU has not been idle recently, as
> +	 * reduction is likely to be premature.
> +	 */
> +	idle_calls = tick_nohz_get_idle_calls_cpu(sg_cpu->cpu);
> +	ret = idle_calls == sg_cpu->saved_idle_calls;
>  
>  	sg_cpu->saved_idle_calls = idle_calls;
>  	return ret;
>  }
>  #else
> -static inline bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu) { return false; }
> +static inline bool sugov_hold_freq(struct sugov_cpu *sg_cpu) { return false; }
>  #endif /* CONFIG_NO_HZ_COMMON */
>  
>  /*
> @@ -382,14 +393,8 @@ static void sugov_update_single_freq(struct update_util_data *hook, u64 time,
>  		return;
>  
>  	next_f = get_next_freq(sg_policy, sg_cpu->util, max_cap);
> -	/*
> -	 * Do not reduce the frequency if the CPU has not been idle
> -	 * recently, as the reduction is likely to be premature then.
> -	 *
> -	 * Except when the rq is capped by uclamp_max.
> -	 */
> -	if (!uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)) &&
> -	    sugov_cpu_is_busy(sg_cpu) && next_f < sg_policy->next_freq &&
> +
> +	if (sugov_hold_freq(sg_cpu) && next_f < sg_policy->next_freq &&
>  	    !sg_policy->need_freq_update) {
>  		next_f = sg_policy->next_freq;
>  

Not necessarily related to your changes, but in case you're touching this
again, maybe sugov_hold_freq() could be the last condition?
And do we want something like
#ifdef CONFIG_NO_HZ_COMMON                                                      
else
	sg_cpu->saved_idle_calls = tick_nohz_get_idle_calls_cpu(sg_cpu->cpu);
#endif
here?

> @@ -436,14 +441,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time,
>  	if (!sugov_update_single_common(sg_cpu, time, max_cap, flags))
>  		return;
>  
> -	/*
> -	 * Do not reduce the target performance level if the CPU has not been
> -	 * idle recently, as the reduction is likely to be premature then.
> -	 *
> -	 * Except when the rq is capped by uclamp_max.
> -	 */
> -	if (!uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)) &&
> -	    sugov_cpu_is_busy(sg_cpu) && sg_cpu->util < prev_util)
> +	if (sugov_hold_freq(sg_cpu) && sg_cpu->util < prev_util)
>  		sg_cpu->util = prev_util;
>  
>  	cpufreq_driver_adjust_perf(sg_cpu->cpu, sg_cpu->bw_min,

FWIW
Reviewed-by: Christian Loehle <christian.loehle@arm.com>
Rafael J. Wysocki June 19, 2024, 6:45 p.m. UTC | #2
On Wed, Jun 19, 2024 at 5:13 AM Tejun Heo <tj@kernel.org> wrote:
>
> sugov_cpu_is_busy() is used to avoid decreasing performance level while the
> CPU is busy and called by sugov_update_single_freq() and
> sugov_update_single_perf(). Both callers repeat the same pattern to first
> test for uclamp and then the business. Let's refactor so that the tests
> aren't repeated.
>
> The new helper is named sugov_hold_freq() and tests both the uclamp
> exception and CPU business. No functional changes. This will make adding
> more exception conditions easier.
>
> Signed-off-by: Tejun Heo <tj@kernel.org>
> Reviewed-by: David Vernet <dvernet@meta.com>
> Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> Cc: Viresh Kumar <viresh.kumar@linaro.org>

Acked-by: Rafael J. Wysocki <rafael@kernel.org>

for this particular change.

> ---
>  kernel/sched/cpufreq_schedutil.c | 38 +++++++++++++++-----------------
>  1 file changed, 18 insertions(+), 20 deletions(-)
>
> diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
> index eece6244f9d2..972b7dd65af2 100644
> --- a/kernel/sched/cpufreq_schedutil.c
> +++ b/kernel/sched/cpufreq_schedutil.c
> @@ -325,16 +325,27 @@ static unsigned long sugov_iowait_apply(struct sugov_cpu *sg_cpu, u64 time,
>  }
>
>  #ifdef CONFIG_NO_HZ_COMMON
> -static bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu)
> +static bool sugov_hold_freq(struct sugov_cpu *sg_cpu)
>  {
> -       unsigned long idle_calls = tick_nohz_get_idle_calls_cpu(sg_cpu->cpu);
> -       bool ret = idle_calls == sg_cpu->saved_idle_calls;
> +       unsigned long idle_calls;
> +       bool ret;
> +
> +       /* if capped by uclamp_max, always update to be in compliance */
> +       if (uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)))
> +               return false;
> +
> +       /*
> +        * Maintain the frequency if the CPU has not been idle recently, as
> +        * reduction is likely to be premature.
> +        */
> +       idle_calls = tick_nohz_get_idle_calls_cpu(sg_cpu->cpu);
> +       ret = idle_calls == sg_cpu->saved_idle_calls;
>
>         sg_cpu->saved_idle_calls = idle_calls;
>         return ret;
>  }
>  #else
> -static inline bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu) { return false; }
> +static inline bool sugov_hold_freq(struct sugov_cpu *sg_cpu) { return false; }
>  #endif /* CONFIG_NO_HZ_COMMON */
>
>  /*
> @@ -382,14 +393,8 @@ static void sugov_update_single_freq(struct update_util_data *hook, u64 time,
>                 return;
>
>         next_f = get_next_freq(sg_policy, sg_cpu->util, max_cap);
> -       /*
> -        * Do not reduce the frequency if the CPU has not been idle
> -        * recently, as the reduction is likely to be premature then.
> -        *
> -        * Except when the rq is capped by uclamp_max.
> -        */
> -       if (!uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)) &&
> -           sugov_cpu_is_busy(sg_cpu) && next_f < sg_policy->next_freq &&
> +
> +       if (sugov_hold_freq(sg_cpu) && next_f < sg_policy->next_freq &&
>             !sg_policy->need_freq_update) {
>                 next_f = sg_policy->next_freq;
>
> @@ -436,14 +441,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time,
>         if (!sugov_update_single_common(sg_cpu, time, max_cap, flags))
>                 return;
>
> -       /*
> -        * Do not reduce the target performance level if the CPU has not been
> -        * idle recently, as the reduction is likely to be premature then.
> -        *
> -        * Except when the rq is capped by uclamp_max.
> -        */
> -       if (!uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)) &&
> -           sugov_cpu_is_busy(sg_cpu) && sg_cpu->util < prev_util)
> +       if (sugov_hold_freq(sg_cpu) && sg_cpu->util < prev_util)
>                 sg_cpu->util = prev_util;
>
>         cpufreq_driver_adjust_perf(sg_cpu->cpu, sg_cpu->bw_min,
> --
> 2.45.2
>
>
Tejun Heo June 19, 2024, 6:57 p.m. UTC | #3
Hello, Christian.

On Wed, Jun 19, 2024 at 03:07:32PM +0100, Christian Loehle wrote:
> > +	if (sugov_hold_freq(sg_cpu) && next_f < sg_policy->next_freq &&
> >  	    !sg_policy->need_freq_update) {
> >  		next_f = sg_policy->next_freq;
> >  
> 
> Not necessarily related to your changes, but in case you're touching this
> again, maybe sugov_hold_freq() could be the last condition?

I'll update the patch so that sugov_hold_freq() is the last condition.

> And do we want something like
> #ifdef CONFIG_NO_HZ_COMMON                                                      
> else
> 	sg_cpu->saved_idle_calls = tick_nohz_get_idle_calls_cpu(sg_cpu->cpu);
> #endif
> here?

I have no idea but if something like the above is necessary, it'd probably
fit better in the #else definition of sugof_hold_freq() or just move the
#ifdef inside the function body so that the common part is outside?

Thanks.
Tejun Heo June 19, 2024, 7:07 p.m. UTC | #4
On Wed, Jun 19, 2024 at 08:57:56AM -1000, Tejun Heo wrote:
> Hello, Christian.
> 
> On Wed, Jun 19, 2024 at 03:07:32PM +0100, Christian Loehle wrote:
> > > +	if (sugov_hold_freq(sg_cpu) && next_f < sg_policy->next_freq &&
> > >  	    !sg_policy->need_freq_update) {
> > >  		next_f = sg_policy->next_freq;
> > >  
> > 
> > Not necessarily related to your changes, but in case you're touching this
> > again, maybe sugov_hold_freq() could be the last condition?
> 
> I'll update the patch so that sugov_hold_freq() is the last condition.

Oh, looking at the code again, this would lead to behavior change, right? It
changes the period over which non-idleness is measured. Maybe that's okay
but seems out-of-scope for a refactoring patch. I'll leave it as-is.

> > And do we want something like
> > #ifdef CONFIG_NO_HZ_COMMON                                                      
> > else
> > 	sg_cpu->saved_idle_calls = tick_nohz_get_idle_calls_cpu(sg_cpu->cpu);
> > #endif
> > here?
> 
> I have no idea but if something like the above is necessary, it'd probably
> fit better in the #else definition of sugof_hold_freq() or just move the
> #ifdef inside the function body so that the common part is outside?

and ->saved_idle_calls isn't even defined if !NO_HZ_COMMON and is only used
to determine whether to hold frequency, so the above doesn't seem necessary
either.

Thanks.
Tejun Heo June 19, 2024, 7:53 p.m. UTC | #5
Hello, Rafael.

On Wed, Jun 19, 2024 at 08:45:42PM +0200, Rafael J. Wysocki wrote:
> On Wed, Jun 19, 2024 at 5:13 AM Tejun Heo <tj@kernel.org> wrote:
> >
> > sugov_cpu_is_busy() is used to avoid decreasing performance level while the
> > CPU is busy and called by sugov_update_single_freq() and
> > sugov_update_single_perf(). Both callers repeat the same pattern to first
> > test for uclamp and then the business. Let's refactor so that the tests
> > aren't repeated.
> >
> > The new helper is named sugov_hold_freq() and tests both the uclamp
> > exception and CPU business. No functional changes. This will make adding
> > more exception conditions easier.
> >
> > Signed-off-by: Tejun Heo <tj@kernel.org>
> > Reviewed-by: David Vernet <dvernet@meta.com>
> > Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> > Cc: Viresh Kumar <viresh.kumar@linaro.org>
> 
> Acked-by: Rafael J. Wysocki <rafael@kernel.org>
> 
> for this particular change.

If the cpufreq_schedutil part of the second patch looks good to you, would
it be okay to route together with this patch through the sched_ext tree?

Thanks.
Christian Loehle June 20, 2024, 9:40 a.m. UTC | #6
On 6/19/24 20:07, Tejun Heo wrote:
> On Wed, Jun 19, 2024 at 08:57:56AM -1000, Tejun Heo wrote:
>> Hello, Christian.
>>
>> On Wed, Jun 19, 2024 at 03:07:32PM +0100, Christian Loehle wrote:
>>>> +	if (sugov_hold_freq(sg_cpu) && next_f < sg_policy->next_freq &&
>>>>  	    !sg_policy->need_freq_update) {
>>>>  		next_f = sg_policy->next_freq;
>>>>  
>>>
>>> Not necessarily related to your changes, but in case you're touching this
>>> again, maybe sugov_hold_freq() could be the last condition?
>>
>> I'll update the patch so that sugov_hold_freq() is the last condition.
> 
> Oh, looking at the code again, this would lead to behavior change, right? It
> changes the period over which non-idleness is measured. Maybe that's okay
> but seems out-of-scope for a refactoring patch. I'll leave it as-is.

It does prevent idle_calls being updated in some cases, but see below I don't
think they are that deliberate anyway.

> 
>>> And do we want something like
>>> #ifdef CONFIG_NO_HZ_COMMON                                                      
>>> else
>>> 	sg_cpu->saved_idle_calls = tick_nohz_get_idle_calls_cpu(sg_cpu->cpu);
>>> #endif
>>> here?
>>
>> I have no idea but if something like the above is necessary, it'd probably
>> fit better in the #else definition of sugof_hold_freq() or just move the
>> #ifdef inside the function body so that the common part is outside?
> 
> and ->saved_idle_calls isn't even defined if !NO_HZ_COMMON and is only used
> to determine whether to hold frequency, so the above doesn't seem necessary
> either.

When reading that code again it seems like the right thing to do.
Anyway feel free to ignore, I might pick it up myself.
I'll think it through some more, the question mark was more like an open
question to anyone reading this.

Kind Regards,
Christian
Rafael J. Wysocki June 20, 2024, 5:57 p.m. UTC | #7
Hi Tejun,

On Wed, Jun 19, 2024 at 9:53 PM Tejun Heo <tj@kernel.org> wrote:
>
> Hello, Rafael.
>
> On Wed, Jun 19, 2024 at 08:45:42PM +0200, Rafael J. Wysocki wrote:
> > On Wed, Jun 19, 2024 at 5:13 AM Tejun Heo <tj@kernel.org> wrote:
> > >
> > > sugov_cpu_is_busy() is used to avoid decreasing performance level while the
> > > CPU is busy and called by sugov_update_single_freq() and
> > > sugov_update_single_perf(). Both callers repeat the same pattern to first
> > > test for uclamp and then the business. Let's refactor so that the tests
> > > aren't repeated.
> > >
> > > The new helper is named sugov_hold_freq() and tests both the uclamp
> > > exception and CPU business. No functional changes. This will make adding
> > > more exception conditions easier.
> > >
> > > Signed-off-by: Tejun Heo <tj@kernel.org>
> > > Reviewed-by: David Vernet <dvernet@meta.com>
> > > Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> > > Cc: Viresh Kumar <viresh.kumar@linaro.org>
> >
> > Acked-by: Rafael J. Wysocki <rafael@kernel.org>
> >
> > for this particular change.
>
> If the cpufreq_schedutil part of the second patch looks good to you, would
> it be okay to route together with this patch through the sched_ext tree?

Please feel free to pick up the $subject patch (with my ACK).

As for the [2/2], I don't think I'm sufficiently familiar with the
scx_* stuff to make any comments on it, either way.

Cheers, Rafael
Tejun Heo June 20, 2024, 6:08 p.m. UTC | #8
Hello, Rafael.

On Thu, Jun 20, 2024 at 07:57:47PM +0200, Rafael J. Wysocki wrote:
> Please feel free to pick up the $subject patch (with my ACK).

Will do. Thanks.

> As for the [2/2], I don't think I'm sufficiently familiar with the
> scx_* stuff to make any comments on it, either way.

Yeah, as long as the cpufreq part isn't objectionable to you, it's all good.

Thank you.
Tejun Heo June 21, 2024, 10:38 p.m. UTC | #9
On Tue, Jun 18, 2024 at 05:12:02PM -1000, Tejun Heo wrote:
> sugov_cpu_is_busy() is used to avoid decreasing performance level while the
> CPU is busy and called by sugov_update_single_freq() and
> sugov_update_single_perf(). Both callers repeat the same pattern to first
> test for uclamp and then the business. Let's refactor so that the tests
> aren't repeated.
> 
> The new helper is named sugov_hold_freq() and tests both the uclamp
> exception and CPU business. No functional changes. This will make adding
> more exception conditions easier.
> 
> Signed-off-by: Tejun Heo <tj@kernel.org>
> Reviewed-by: David Vernet <dvernet@meta.com>
> Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> Cc: Viresh Kumar <viresh.kumar@linaro.org>

Applied to sched_ext/for-6.11.

Thanks.
diff mbox series

Patch

diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index eece6244f9d2..972b7dd65af2 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -325,16 +325,27 @@  static unsigned long sugov_iowait_apply(struct sugov_cpu *sg_cpu, u64 time,
 }
 
 #ifdef CONFIG_NO_HZ_COMMON
-static bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu)
+static bool sugov_hold_freq(struct sugov_cpu *sg_cpu)
 {
-	unsigned long idle_calls = tick_nohz_get_idle_calls_cpu(sg_cpu->cpu);
-	bool ret = idle_calls == sg_cpu->saved_idle_calls;
+	unsigned long idle_calls;
+	bool ret;
+
+	/* if capped by uclamp_max, always update to be in compliance */
+	if (uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)))
+		return false;
+
+	/*
+	 * Maintain the frequency if the CPU has not been idle recently, as
+	 * reduction is likely to be premature.
+	 */
+	idle_calls = tick_nohz_get_idle_calls_cpu(sg_cpu->cpu);
+	ret = idle_calls == sg_cpu->saved_idle_calls;
 
 	sg_cpu->saved_idle_calls = idle_calls;
 	return ret;
 }
 #else
-static inline bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu) { return false; }
+static inline bool sugov_hold_freq(struct sugov_cpu *sg_cpu) { return false; }
 #endif /* CONFIG_NO_HZ_COMMON */
 
 /*
@@ -382,14 +393,8 @@  static void sugov_update_single_freq(struct update_util_data *hook, u64 time,
 		return;
 
 	next_f = get_next_freq(sg_policy, sg_cpu->util, max_cap);
-	/*
-	 * Do not reduce the frequency if the CPU has not been idle
-	 * recently, as the reduction is likely to be premature then.
-	 *
-	 * Except when the rq is capped by uclamp_max.
-	 */
-	if (!uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)) &&
-	    sugov_cpu_is_busy(sg_cpu) && next_f < sg_policy->next_freq &&
+
+	if (sugov_hold_freq(sg_cpu) && next_f < sg_policy->next_freq &&
 	    !sg_policy->need_freq_update) {
 		next_f = sg_policy->next_freq;
 
@@ -436,14 +441,7 @@  static void sugov_update_single_perf(struct update_util_data *hook, u64 time,
 	if (!sugov_update_single_common(sg_cpu, time, max_cap, flags))
 		return;
 
-	/*
-	 * Do not reduce the target performance level if the CPU has not been
-	 * idle recently, as the reduction is likely to be premature then.
-	 *
-	 * Except when the rq is capped by uclamp_max.
-	 */
-	if (!uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)) &&
-	    sugov_cpu_is_busy(sg_cpu) && sg_cpu->util < prev_util)
+	if (sugov_hold_freq(sg_cpu) && sg_cpu->util < prev_util)
 		sg_cpu->util = prev_util;
 
 	cpufreq_driver_adjust_perf(sg_cpu->cpu, sg_cpu->bw_min,