diff mbox series

[v2,4/7] cpufreq: report whether cpufreq supports Frequency Invariance (FI)

Message ID 20200722093732.14297-5-ionela.voinescu@arm.com (mailing list archive)
State Changes Requested, archived
Headers show
Series cpufreq: improve frequency invariance support | expand

Commit Message

Ionela Voinescu July 22, 2020, 9:37 a.m. UTC
Now that the update of the FI scale factor is done in cpufreq core for
selected functions - target(), target_index() and fast_switch(),
we can provide feedback to the task scheduler and architecture code
on whether cpufreq supports FI.

For this purpose, provide error and debug messages, together with an
external function to expose whether the cpufreq drivers support FI, by
using a static key.

The logic behind the enablement of cpufreq-based invariance is as
follows:
 - cpufreq-based invariance is disabled by default
 - cpufreq-based invariance is enabled if any of the callbacks
   above is implemented while the unsupported setpolicy() is not
 - if enabled, cpufreq-based invariance will be disabled during the
   call of the default arch_set_freq_scale() function which does
   not set a scale factor.

Signed-off-by: Ionela Voinescu <ionela.voinescu@arm.com>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
---
 drivers/cpufreq/cpufreq.c | 28 ++++++++++++++++++++++++++++
 include/linux/cpufreq.h   |  5 +++++
 2 files changed, 33 insertions(+)

Comments

Rafael J. Wysocki July 27, 2020, 2:02 p.m. UTC | #1
On Wed, Jul 22, 2020 at 11:38 AM Ionela Voinescu
<ionela.voinescu@arm.com> wrote:
>
> Now that the update of the FI scale factor is done in cpufreq core for
> selected functions - target(), target_index() and fast_switch(),
> we can provide feedback to the task scheduler and architecture code
> on whether cpufreq supports FI.
>
> For this purpose, provide error and debug messages, together with an
> external function to expose whether the cpufreq drivers support FI, by
> using a static key.
>
> The logic behind the enablement of cpufreq-based invariance is as
> follows:
>  - cpufreq-based invariance is disabled by default
>  - cpufreq-based invariance is enabled if any of the callbacks
>    above is implemented while the unsupported setpolicy() is not
>  - if enabled, cpufreq-based invariance will be disabled during the
>    call of the default arch_set_freq_scale() function which does
>    not set a scale factor.
>
> Signed-off-by: Ionela Voinescu <ionela.voinescu@arm.com>
> Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
> Cc: Viresh Kumar <viresh.kumar@linaro.org>
> ---
>  drivers/cpufreq/cpufreq.c | 28 ++++++++++++++++++++++++++++
>  include/linux/cpufreq.h   |  5 +++++
>  2 files changed, 33 insertions(+)
>
> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> index 3497c1cd6818..1d0b046fe8e9 100644
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -61,6 +61,9 @@ static struct cpufreq_driver *cpufreq_driver;
>  static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
>  static DEFINE_RWLOCK(cpufreq_driver_lock);
>
> +/* Mark support for the scheduler's frequency invariance engine */
> +static DEFINE_STATIC_KEY_FALSE(cpufreq_set_freq_scale);
> +
>  /* Flag to suspend/resume CPUFreq governors */
>  static bool cpufreq_suspended;
>
> @@ -69,6 +72,25 @@ static inline bool has_target(void)
>         return cpufreq_driver->target_index || cpufreq_driver->target;
>  }
>
> +static inline
> +void enable_cpufreq_freq_invariance(struct cpufreq_driver *driver)
> +{
> +       if ((driver->target || driver->target_index || driver->fast_switch) &&
> +           !driver->setpolicy) {
> +
> +               static_branch_enable_cpuslocked(&cpufreq_set_freq_scale);
> +               pr_debug("%s: Driver %s can provide frequency invariance.",
> +                        __func__, driver->name);
> +       } else
> +               pr_err("%s: Driver %s cannot provide frequency invariance.",
> +               __func__, driver->name);

This doesn't follow the kernel coding style (the braces around the
pr_err() statement are missing).

Besides, IMO on architectures where arch_set_freq_scale() is empty,
this should be empty as well.

> +}
> +
> +bool cpufreq_sets_freq_scale(void)
> +{
> +       return static_branch_likely(&cpufreq_set_freq_scale);
> +}
> +
>  /* internal prototypes */
>  static unsigned int __cpufreq_get(struct cpufreq_policy *policy);
>  static int cpufreq_init_governor(struct cpufreq_policy *policy);
> @@ -159,6 +181,9 @@ EXPORT_SYMBOL_GPL(get_cpu_idle_time);
>  __weak void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq,
>                 unsigned long max_freq)
>  {
> +       if (cpufreq_sets_freq_scale())
> +               static_branch_disable_cpuslocked(&cpufreq_set_freq_scale);
> +
>  }
>  EXPORT_SYMBOL_GPL(arch_set_freq_scale);
>
> @@ -2722,6 +2747,8 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
>         cpufreq_driver = driver_data;
>         write_unlock_irqrestore(&cpufreq_driver_lock, flags);
>
> +       enable_cpufreq_freq_invariance(cpufreq_driver);
> +
>         if (driver_data->setpolicy)
>                 driver_data->flags |= CPUFREQ_CONST_LOOPS;
>
> @@ -2791,6 +2818,7 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
>         cpus_read_lock();
>         subsys_interface_unregister(&cpufreq_interface);
>         remove_boost_sysfs_file();
> +       static_branch_disable_cpuslocked(&cpufreq_set_freq_scale);
>         cpuhp_remove_state_nocalls_cpuslocked(hp_online);
>
>         write_lock_irqsave(&cpufreq_driver_lock, flags);
> diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
> index e62b022cb07e..f81215ad76f1 100644
> --- a/include/linux/cpufreq.h
> +++ b/include/linux/cpufreq.h
> @@ -217,6 +217,7 @@ void refresh_frequency_limits(struct cpufreq_policy *policy);
>  void cpufreq_update_policy(unsigned int cpu);
>  void cpufreq_update_limits(unsigned int cpu);
>  bool have_governor_per_policy(void);
> +bool cpufreq_sets_freq_scale(void);
>  struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy);
>  void cpufreq_enable_fast_switch(struct cpufreq_policy *policy);
>  void cpufreq_disable_fast_switch(struct cpufreq_policy *policy);
> @@ -237,6 +238,10 @@ static inline unsigned int cpufreq_get_hw_max_freq(unsigned int cpu)
>  {
>         return 0;
>  }
> +static inline bool cpufreq_sets_freq_scale(void)
> +{
> +       return false;
> +}
>  static inline void disable_cpufreq(void) { }
>  #endif
>
> --
> 2.17.1
>
Ionela Voinescu July 29, 2020, 2:39 p.m. UTC | #2
Hi,

On Monday 27 Jul 2020 at 16:02:18 (+0200), Rafael J. Wysocki wrote:
> On Wed, Jul 22, 2020 at 11:38 AM Ionela Voinescu
> <ionela.voinescu@arm.com> wrote:
[..]
> > +static inline
> > +void enable_cpufreq_freq_invariance(struct cpufreq_driver *driver)
> > +{
> > +       if ((driver->target || driver->target_index || driver->fast_switch) &&
> > +           !driver->setpolicy) {
> > +
> > +               static_branch_enable_cpuslocked(&cpufreq_set_freq_scale);
> > +               pr_debug("%s: Driver %s can provide frequency invariance.",
> > +                        __func__, driver->name);
> > +       } else
> > +               pr_err("%s: Driver %s cannot provide frequency invariance.",
> > +               __func__, driver->name);
> 
> This doesn't follow the kernel coding style (the braces around the
> pr_err() statement are missing).
> 

I'll fix this.

Also, depending on the result of the discussion below, it might be best
for this to be a warning, not an error.

> Besides, IMO on architectures where arch_set_freq_scale() is empty,
> this should be empty as well.
>

Yes, you are right, there are two aspects here:
 - (1) Whether a driver *can* provide frequency invariance. IOW, whether
   it implements the callbacks that result in the call to
   arch_set_freq_scale().

 - (2) Whether cpufreq/driver *does* provide frequency invariance. IOW,
   whether the call to arch_set_freq_scale() actually results in the
   setting of the scale factor.

Even when creating this v2 I was going back and forth between the options
for this:

(a) cpufreq should report whether it *can* provide frequency invariance
    (as described at (1)). If we go for this, for clarity I should change

    s/cpufreq_set_freq_scale/cpufreq_can_set_freq_scale_key
    s/cpufreq_sets_freq_scale()/cpufreq_can_set_freq_scale()

    Through this, cpufreq only reports that it calls
    arch_set_freq_scale(), independent on whether that call results in a
    scale factor being set. Then it would be up to the caller to ensure
    this information is used with a proper definition of
    arch_set_freq_scale().

(b) cpufreq should report whether it *does* provide frequency invariance

    A way of doing this is to use a arch_set_freq_scale define (as done
    for other arch functions, for example arch_scale_freq_tick()) and
    guard this enable_cpufreq_freq_invariance() function based on that
    definition.
    Therefore, cpufreq_sets_freq_scale() would report whether
    enable_cpufreq_freq_invariance() was successful and there is an
    external definition of arch_set_freq_scale() that sets the scale
    factor.


The current version is somewhat a combination of (a) and (b):
cpufreq_set_freq_scale would initially be enabled if the proper callbacks
are implemented (a), but later if the weak version of
arch_set_freq_scale() is called, we disabled it (b) (as can be seen below).

[..]
> >  __weak void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq,
> >                 unsigned long max_freq)
> >  {
> > +       if (cpufreq_sets_freq_scale())
> > +               static_branch_disable_cpuslocked(&cpufreq_set_freq_scale);
> > +
> >  }
> >  EXPORT_SYMBOL_GPL(arch_set_freq_scale);

I suppose a clear (a) or (b) solution might be better here.

IMO, given that (b) cannot actually guarantee that a scale factor is set
through arch_set_freq_scale() given cpufreq information about current and
maximum frequencies, for me (a) is preferred as it conveys the only
information that cpufreq can convey - the fact that it *can* set the scale
factor, not that it *does*.

Can you please confirm whether you still prefer (b), given the details
above?

Thank you,
Ionela.
Viresh Kumar July 30, 2020, 4:43 a.m. UTC | #3
On 22-07-20, 10:37, Ionela Voinescu wrote:
> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> index 3497c1cd6818..1d0b046fe8e9 100644
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -61,6 +61,9 @@ static struct cpufreq_driver *cpufreq_driver;
>  static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
>  static DEFINE_RWLOCK(cpufreq_driver_lock);
>  
> +/* Mark support for the scheduler's frequency invariance engine */
> +static DEFINE_STATIC_KEY_FALSE(cpufreq_set_freq_scale);
> +
>  /* Flag to suspend/resume CPUFreq governors */
>  static bool cpufreq_suspended;
>  
> @@ -69,6 +72,25 @@ static inline bool has_target(void)
>  	return cpufreq_driver->target_index || cpufreq_driver->target;
>  }
>  
> +static inline
> +void enable_cpufreq_freq_invariance(struct cpufreq_driver *driver)
> +{
> +	if ((driver->target || driver->target_index || driver->fast_switch) &&
> +	    !driver->setpolicy) {

Just checking for !driver->setpolicy should be enough here.

> +		static_branch_enable_cpuslocked(&cpufreq_set_freq_scale);
> +		pr_debug("%s: Driver %s can provide frequency invariance.",
> +			 __func__, driver->name);

I think a simpler print will work well too.

                pr_debug("Freq invariance enabled");

__func__ isn't really required as this is the only print with that
kind of info in cpufreq.c.

> +	} else
> +		pr_err("%s: Driver %s cannot provide frequency invariance.",
> +		__func__, driver->name);

Why not supporting freq-invariance an error ? I will just drop this
message completely.
Ionela Voinescu Aug. 3, 2020, 3:24 p.m. UTC | #4
Hi Viresh,

On Thursday 30 Jul 2020 at 10:13:46 (+0530), Viresh Kumar wrote:
> On 22-07-20, 10:37, Ionela Voinescu wrote:
> > diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> > index 3497c1cd6818..1d0b046fe8e9 100644
> > --- a/drivers/cpufreq/cpufreq.c
> > +++ b/drivers/cpufreq/cpufreq.c
> > @@ -61,6 +61,9 @@ static struct cpufreq_driver *cpufreq_driver;
> >  static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
> >  static DEFINE_RWLOCK(cpufreq_driver_lock);
> >  
> > +/* Mark support for the scheduler's frequency invariance engine */
> > +static DEFINE_STATIC_KEY_FALSE(cpufreq_set_freq_scale);
> > +
> >  /* Flag to suspend/resume CPUFreq governors */
> >  static bool cpufreq_suspended;
> >  
> > @@ -69,6 +72,25 @@ static inline bool has_target(void)
> >  	return cpufreq_driver->target_index || cpufreq_driver->target;
> >  }
> >  
> > +static inline
> > +void enable_cpufreq_freq_invariance(struct cpufreq_driver *driver)
> > +{
> > +	if ((driver->target || driver->target_index || driver->fast_switch) &&
> > +	    !driver->setpolicy) {
> 
> Just checking for !driver->setpolicy should be enough here.
> 

Right, cpufreq_register_driver() should check that at least one of them
is present (although currently cpufreq_register_driver() will return
-EINVAL if .fast_switch() alone is present - something to be fixed).

Will do, on both accounts.


> > +		static_branch_enable_cpuslocked(&cpufreq_set_freq_scale);
> > +		pr_debug("%s: Driver %s can provide frequency invariance.",
> > +			 __func__, driver->name);
> 
> I think a simpler print will work well too.
> 
>                 pr_debug("Freq invariance enabled");
> 

I think the right way of reporting this support is important here.

By enabling the static key here, we're not actually enabling frequency
invariance. So printing "Freq invariance enabled" would be very
misleading. Frequency invariance (scheduler concept) being truly enabled
depends on three things:
 - Having a source of information for current frequency and maximum
   frequency (cpufreq, counters)
 - Having arch support for using that information to set the frequency
   scale factor: arch_set_freq_scale(), arch_scale_freq_tick()
 - Having arch support for passing the set frequency scale factor to the
   scheduler and reporting support for frequency invariance:
   arch_scale_freq_capacity(), arch_scale_freq_invariant().

Therefore, cpufreq can only report that the current driver can be a
source of information for frequency invariance "Driver %s can provide
frequency invariance", but it can't guarantee that the other conditions
are accomplished.

So I would recommend to keep this original debug message.

> __func__ isn't really required as this is the only print with that
> kind of info in cpufreq.c.
>

Makes sense!

> > +	} else
> > +		pr_err("%s: Driver %s cannot provide frequency invariance.",
> > +		__func__, driver->name);
> 
> Why not supporting freq-invariance an error ? I will just drop this
> message completely.

Yes, an error does not make sense here. I was thinking to demote it to a
warning instead in my previous reply to Rafael, but removing it
altogether might be better.

Many thanks for the thorough review,
Ionela.

> 
> -- 
> viresh
Viresh Kumar Aug. 4, 2020, 6:46 a.m. UTC | #5
On 03-08-20, 16:24, Ionela Voinescu wrote:
> Right, cpufreq_register_driver() should check that at least one of them
> is present

> (although currently cpufreq_register_driver() will return
> -EINVAL if .fast_switch() alone is present - something to be fixed).

I think it is fine as there is no guarantee from cpufreq core if
.fast_switch() will get called and so target/target_index must be
present. We can't do fast-switch today without schedutil (as only that
enables it) and if a notifier gets registered before the driver, then
we are gone again.

> Will do, on both accounts.
> 
> 
> > > +		static_branch_enable_cpuslocked(&cpufreq_set_freq_scale);
> > > +		pr_debug("%s: Driver %s can provide frequency invariance.",
> > > +			 __func__, driver->name);
> > 
> > I think a simpler print will work well too.
> > 
> >                 pr_debug("Freq invariance enabled");
> > 
> 
> I think the right way of reporting this support is important here.

Yeah, we can't say it is enabled as you explained, though I meant
something else here then, i.e. getting rid of driver name and
unimportant stuff. What about this now:

pr_debug("supports frequency invariance");

This shall get printed as this finally:

cpufreq: supports frequency invariance
Ionela Voinescu Aug. 5, 2020, 10:35 a.m. UTC | #6
On Tuesday 04 Aug 2020 at 12:16:56 (+0530), Viresh Kumar wrote:
> On 03-08-20, 16:24, Ionela Voinescu wrote:
> > Right, cpufreq_register_driver() should check that at least one of them
> > is present
> 
> > (although currently cpufreq_register_driver() will return
> > -EINVAL if .fast_switch() alone is present - something to be fixed).
> 
> I think it is fine as there is no guarantee from cpufreq core if
> .fast_switch() will get called and so target/target_index must be
> present. We can't do fast-switch today without schedutil (as only that
> enables it) and if a notifier gets registered before the driver, then
> we are gone again.
> 
> > Will do, on both accounts.
> > 
> > 
> > > > +		static_branch_enable_cpuslocked(&cpufreq_set_freq_scale);
> > > > +		pr_debug("%s: Driver %s can provide frequency invariance.",
> > > > +			 __func__, driver->name);
> > > 
> > > I think a simpler print will work well too.
> > > 
> > >                 pr_debug("Freq invariance enabled");
> > > 
> > 
> > I think the right way of reporting this support is important here.
> 
> Yeah, we can't say it is enabled as you explained, though I meant
> something else here then, i.e. getting rid of driver name and
> unimportant stuff. What about this now:
> 
> pr_debug("supports frequency invariance");
> 
> This shall get printed as this finally:
> 
> cpufreq: supports frequency invariance
> 

Will do!

Thanks,
Ionela.

> -- 
> viresh
diff mbox series

Patch

diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 3497c1cd6818..1d0b046fe8e9 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -61,6 +61,9 @@  static struct cpufreq_driver *cpufreq_driver;
 static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
 static DEFINE_RWLOCK(cpufreq_driver_lock);
 
+/* Mark support for the scheduler's frequency invariance engine */
+static DEFINE_STATIC_KEY_FALSE(cpufreq_set_freq_scale);
+
 /* Flag to suspend/resume CPUFreq governors */
 static bool cpufreq_suspended;
 
@@ -69,6 +72,25 @@  static inline bool has_target(void)
 	return cpufreq_driver->target_index || cpufreq_driver->target;
 }
 
+static inline
+void enable_cpufreq_freq_invariance(struct cpufreq_driver *driver)
+{
+	if ((driver->target || driver->target_index || driver->fast_switch) &&
+	    !driver->setpolicy) {
+
+		static_branch_enable_cpuslocked(&cpufreq_set_freq_scale);
+		pr_debug("%s: Driver %s can provide frequency invariance.",
+			 __func__, driver->name);
+	} else
+		pr_err("%s: Driver %s cannot provide frequency invariance.",
+		__func__, driver->name);
+}
+
+bool cpufreq_sets_freq_scale(void)
+{
+	return static_branch_likely(&cpufreq_set_freq_scale);
+}
+
 /* internal prototypes */
 static unsigned int __cpufreq_get(struct cpufreq_policy *policy);
 static int cpufreq_init_governor(struct cpufreq_policy *policy);
@@ -159,6 +181,9 @@  EXPORT_SYMBOL_GPL(get_cpu_idle_time);
 __weak void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq,
 		unsigned long max_freq)
 {
+	if (cpufreq_sets_freq_scale())
+		static_branch_disable_cpuslocked(&cpufreq_set_freq_scale);
+
 }
 EXPORT_SYMBOL_GPL(arch_set_freq_scale);
 
@@ -2722,6 +2747,8 @@  int cpufreq_register_driver(struct cpufreq_driver *driver_data)
 	cpufreq_driver = driver_data;
 	write_unlock_irqrestore(&cpufreq_driver_lock, flags);
 
+	enable_cpufreq_freq_invariance(cpufreq_driver);
+
 	if (driver_data->setpolicy)
 		driver_data->flags |= CPUFREQ_CONST_LOOPS;
 
@@ -2791,6 +2818,7 @@  int cpufreq_unregister_driver(struct cpufreq_driver *driver)
 	cpus_read_lock();
 	subsys_interface_unregister(&cpufreq_interface);
 	remove_boost_sysfs_file();
+	static_branch_disable_cpuslocked(&cpufreq_set_freq_scale);
 	cpuhp_remove_state_nocalls_cpuslocked(hp_online);
 
 	write_lock_irqsave(&cpufreq_driver_lock, flags);
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index e62b022cb07e..f81215ad76f1 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -217,6 +217,7 @@  void refresh_frequency_limits(struct cpufreq_policy *policy);
 void cpufreq_update_policy(unsigned int cpu);
 void cpufreq_update_limits(unsigned int cpu);
 bool have_governor_per_policy(void);
+bool cpufreq_sets_freq_scale(void);
 struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy);
 void cpufreq_enable_fast_switch(struct cpufreq_policy *policy);
 void cpufreq_disable_fast_switch(struct cpufreq_policy *policy);
@@ -237,6 +238,10 @@  static inline unsigned int cpufreq_get_hw_max_freq(unsigned int cpu)
 {
 	return 0;
 }
+static inline bool cpufreq_sets_freq_scale(void)
+{
+	return false;
+}
 static inline void disable_cpufreq(void) { }
 #endif