Re: [PATCH 2/5] cpufreq:boost: Add support for software based CPU frequency boost

Lukasz Majewski
2013-06-06T11:49:18Z

Hi Viresh,

> Hi,
>
> On 6 June 2013 12:37, Lukasz Majewski <l.majewski@samsung.com> wrote:
> > This commit adds support for software based frequency boosting.
> > Exynos4 SoCs (e.g. 4x12) allow setting of frequency above its normal
> > condition limits. This can be done for some short time.
> >
> > Overclocking (boost) support came from cpufreq driver (which is
> > platform dependent). Hence the data structure describing it is
> > defined at its file.
> >
> > To allow support for either SW and HW (Intel) based boosting, the
> > cpufreq core code has been extended to support both solutions.
> >
> > The main boost switch has been put
> > at /sys/devices/system/cpu/cpufreq/boost.
>
> Log requires some better paragraphs but I am not concerned about it
> for now.
>
> > Signed-off-by: Lukasz Majewski <l.majewski@samsung.com>
> > Signed-off-by: Myungjoo Ham <myungjoo.ham@samsung.com>
> > ---
> > drivers/cpufreq/cpufreq.c | 156
> > ++++++++++++++++++++++++++++++++++++++++++
> > drivers/cpufreq/freq_table.c | 87 ++++++++++++++++++++++-
> > include/linux/cpufreq.h | 35 +++++++++- 3 files changed, 275
> > insertions(+), 3 deletions(-)
>
> My initial view on this patch is: "It is overly engineered and needs
> to get simplified"
>
> > diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> > index ca74e27..8cf9a92 100644
> > --- a/drivers/cpufreq/cpufreq.c
> > +++ b/drivers/cpufreq/cpufreq.c
> > @@ -133,6 +133,11 @@ bool have_governor_per_policy(void)
> > return cpufreq_driver->have_governor_per_policy;
> > }
> >
> > +/**
> > + * Global definition of cpufreq_boost structure
> > + */
> > +struct cpufreq_boost *cpufreq_boost;
>
> Probably just a 'static bool' here cpufreq_boost_enabled. Which takes
> care of selection from sysfs.

The pointer to struct cpufreq_boost is needed for further reference
(as it is now done with struct cpufreq_driver pointer - *cpufreq_driver
- defined at cpufreq.c file).

>
> > static struct cpufreq_governor *__find_governor(const char
> > *str_governor) {
> > @@ -761,6 +805,18 @@ static int cpufreq_add_dev_interface(unsigned
> > int cpu, if (cpufreq_set_drv_attr_files(policy,
> > cpufreq_driver->attr)) goto err_out_kobj_put;
> >
> > + if (cpufreq_driver->boost) {
> > + if (sysfs_create_file(cpufreq_global_kobject,
> > + &(global_boost.attr)))
>
> This will report error for systems where we have two policy
> structures. As we are creating something already present.
Good point, thanks.

>
> > + pr_warn("could not register global boost
> > sysfs file\n");
> > + else
> > + pr_debug("registered global boost sysfs
> > file\n");
>
> Please make all your prints to print function name too:
>
> pr_debug("%s: foo\n", __func__, foo);

OK.

>
> > + if (cpufreq_set_drv_attr_files(policy,
> > +
> > cpufreq_driver->boost->attr))
>
> Why is this required? Why do we want platforms to add some files
> in sysfs?

There are two kinds of attributes, which are exported by boost:

1. global boost (/sys/devices/system/cpu/cpufreq/boost)

2. attributes describing cpufreq abilities when boost is available
(/sys/devices/syste/cpu/cpu0/cpufreq/):
- scaling_boost_frequencies - which will show over clocked
frequencies
- the scaling_available_frequencies will also display boosted
frequency (when boost enabled)

Information from 2. is cpufreq_driver dependent. And that information
shouldn't been displayed when boost is not available

>
> > /*********************************************************************
> > + *
> > BOOST *
> > +
> > *********************************************************************/
> > +int cpufreq_boost_trigger_state(struct cpufreq_policy *policy, int
> > state) +{
> > + struct cpufreq_boost *boost;
> > + unsigned long flags;
> > + int ret = 0;
> > +
> > + if (!policy || !policy->boost
> > || !policy->boost->low_level_boost)
> > + return -ENODEV;
> > +
> > + boost = policy->boost;
> > + write_lock_irqsave(&cpufreq_driver_lock, flags);
> > +
> > + if (boost->status != state) {
> > + policy->boost->status = state;
> > + ret = boost->low_level_boost(policy, state);
> > + if (ret) {
> > + pr_err("BOOST cannot %sable low level code
> > (%d)\n",
> > + state ? "en" : "dis", ret);
> > + return ret;
> > + }
> > + }
> > +
> > + write_unlock_irqrestore(&cpufreq_driver_lock, flags);
> > +
> > + pr_debug("cpufreq BOOST %sabled\n", state ? "en" : "dis");
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * cpufreq_boost_notifier - notifier callback for cpufreq policy
> > change.
> > + * <at> nb: struct notifier_block * with callback info.
> > + * <at> event: value showing cpufreq event for which this
> > function invoked.
> > + * <at> data: callback-specific data
> > + */
> > +static int cpufreq_boost_notifier(struct notifier_block *nb,
> > + unsigned long event, void *data)
> > +{
> > + struct cpufreq_policy *policy = data;
> > +
> > + if (event == CPUFREQ_INCOMPATIBLE) {
> > + if (!policy || !policy->boost)
> > + return -ENODEV;
> > +
> > + if (policy->boost->status == CPUFREQ_BOOST_EN) {
> > + pr_info("NOTIFIER BOOST: MAX: %d e:%lu cpu:
> > %d\n",
> > + policy->max, event, policy->cpu);
> > + cpufreq_boost_trigger_state(policy, 0);
> > + }
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/* Notifier for cpufreq policy change */
> > +static struct notifier_block cpufreq_boost_notifier_block = {
> > + .notifier_call = cpufreq_boost_notifier,
> > +};
> > +
> > +int cpufreq_boost_init(struct cpufreq_policy *policy)
> > +{
> > + int ret = 0;
> > +
> > + if (!policy)
> > + return -EINVAL;
>
> Heh, policy can't be NULL here.

Extra precautions :-). I will remove it.

>
> > + if (!cpufreq_driver->boost) {
> > + pr_err("Boost mode not supported on this device\n");
>
> Wow!! You want to screw everybody else's logs with this message.
> Its not a crime if you don't have boost mode supported :)

Hmm, I've exaggerated a bit here.... :)

>
> Actually this routine must be called only if cpufreq_driver->boost
> is valid.
>
> > + return -ENODEV;
> > + }
> > +
> > + policy->boost = cpufreq_boost = cpufreq_driver->boost;
>
> Why are we copying same pointer to policy->boost? Driver is
> passing pointer to a single memory location, just save it globally.

This needs some explanation.

The sysfs entry presented at [1] doesn't bring any useful information
to reuse (like *policy). For this reason the global cpufreq_boost
pointer is needed.

However to efficiently manage the boost, it is necessary to keep per
policy pointer to the only struct cpufreq_boost instance (defined at
cpufreq_driver code).

>
> > + /* disable boost for newly created policy - as we e.g.
> > change
> > + governor */
> > + policy->boost->status = CPUFREQ_BOOST_DIS;
>
> Drivers supporting boost may want boost to be enabled by default,
> maybe without any sysfs calls.

This can be done by setting the struct cpufreq_boost status field to
CPUFREQ_BOOST_EN at cpufreq driver code (when boost structure is
defined)

>
> > + /* register policy notifier */
> > + ret =
> > cpufreq_register_notifier(&cpufreq_boost_notifier_block,
> > + CPUFREQ_POLICY_NOTIFIER);
> > + if (ret) {
> > + pr_err("CPUFREQ BOOST notifier not registered.\n");
> > + return ret;
> > + }
> > + /* add policy to policies list headed at struct
> > cpufreq_boost */
> > + list_add_tail(&policy->boost_list,
> > &cpufreq_boost->policies); +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(cpufreq_boost_init);
>
> Why do we need to maintain a list of boost here? notifiers? complex :(

Notifier is needed to disable boost when policy is changed (for
example when we change from ondemand/lab to performance governor).

I wanted to avoid the situation when boost stays enabled across
different governors.

The list of in system available policies is defined to allow boost
enable/disable for all policies available (by changing for example
policy->max field).

If we decide, that we will support only one policy (as it is now at
e.g. Exynos), the list is unnecessary here.

>
> > +/*********************************************************************
> > * REGISTER / UNREGISTER CPUFREQ
> > DRIVER *
> > *********************************************************************/
> >
> > @@ -1954,6 +2106,10 @@ int cpufreq_unregister_driver(struct
> > cpufreq_driver *driver) pr_debug("unregistering driver %s\n",
> > driver->name);
> >
> > subsys_interface_unregister(&cpufreq_interface);
> > +
> > + if (cpufreq_driver->boost)
> > + sysfs_remove_file(cpufreq_global_kobject,
> > &(global_boost.attr));
>
> You haven't removed this from policy. Memory leak.

Yes, you are right.

>
> > unregister_hotcpu_notifier(&cpufreq_cpu_notifier);
> >
> > write_lock_irqsave(&cpufreq_driver_lock, flags);
> > diff --git a/drivers/cpufreq/freq_table.c
> > b/drivers/cpufreq/freq_table.c index d7a7966..0e95524 100644
> > --- a/drivers/cpufreq/freq_table.c
> > +++ b/drivers/cpufreq/freq_table.c
> > @@ -3,6 +3,8 @@
> > *
> > * Copyright (C) 2002 - 2003 Dominik Brodowski
> > *
> > + * Copyright (C) 2013 Lukasz Majewski <l.majewski@samsung.com>
> > + *
>
> You shouldn't add it unless you did some major work on this file. You
> aren't maintaining this file in 2013.

OK, I will remove the entry.

>
> > +static int cpufreq_frequency_table_skip_boost(struct
> > cpufreq_policy *policy,
> > + unsigned int index)
> > +{
> > + if (index == CPUFREQ_BOOST)
> > + if (!policy->boost ||
>
> This shouldn't be true. If index has got CPUFREQ_BOOST, then driver
> has to support boost.

Correct me if I'm wrong here, but in my understanding the boost shall be
only supported when both CPUFREQ_BOOST index is defined in a freq_table
and boost.state = CPUFREQ_BOOST_EN is set.

Setting of CPUFREQ_BOOST shouldn't by default allow to use over
clocking frequency.

>
> > + policy->boost->status == CPUFREQ_BOOST_DIS)
> > + return 1;
> > +
> > + return 0;
> > +}
> > +
> > +unsigned int
> > +cpufreq_frequency_table_boost_max(struct cpufreq_frequency_table
> > *freq_table) +{
> > + int index, boost_freq_max;
> > +
> > + for (index = 0, boost_freq_max = 0;
> > + freq_table[index].frequency != CPUFREQ_TABLE_END;
> > index++)
> > + if (freq_table[index].index == CPUFREQ_BOOST) {
> > + if (freq_table[index].frequency >
> > boost_freq_max)
> > + boost_freq_max =
> > freq_table[index].frequency;
> > + }
> > +
> > + return boost_freq_max;
> > +}
> > +EXPORT_SYMBOL_GPL(cpufreq_frequency_table_boost_max);
>
> why do we need this?

To evaluate the maximal boost frequency from the frequency table. It is
then used as a delimiter (when LAB cooperates with thermal framework).

>
> > /*
> > * if you use these, you must assure that the frequency table is
> > valid
> > * all the time between get_attr and put_attr!
> > diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
> > index 037d36a..1294c8c 100644
> > --- a/include/linux/cpufreq.h
> > +++ b/include/linux/cpufreq.h
> > @@ -88,6 +88,25 @@ struct cpufreq_real_policy {
> > struct cpufreq_governor *governor; /* see below */
> > };
> >
> > +#define CPUFREQ_BOOST_DIS (0)
> > +#define CPUFREQ_BOOST_EN (1)
>
> You don't need these.. Just create variable as bool and 0 & 1 would
> be fine.

Yes, this seems to be a clearer solution.

>
> > +struct cpufreq_policy;
> > +struct cpufreq_boost {
> > + unsigned int max_boost_freq; /* maximum value of
> > + * boosted freq */
> > + unsigned int max_normal_freq; /* non boost max
> > freq */
> > + int status; /* status of boost */
> > +
> > + /* boost sysfs attributies */
> > + struct freq_attr **attr;
> > +
> > + /* low-level trigger for boost */
> > + int (*low_level_boost) (struct cpufreq_policy *policy, int
> > state); +
> > + struct list_head policies;
> > +};
>
> We don't need it. Just add two more fields to cpufreq_driver:
> - have_boost_freqs and low_level_boost (maybe a better name.
> What's its use?)

The separate struct cpufreq_boost was created to explicitly separate
boost fields from cpufreq_driver structure.

If in your opinion this structure is redundant, I can remove it and
extend cpufreq_driver structure.

>
> > struct cpufreq_policy {
> > /* CPUs sharing clock, require sw coordination */
> > cpumask_var_t cpus; /* Online CPUs only */
> > @@ -113,6 +132,9 @@ struct cpufreq_policy {
> >
> > struct cpufreq_real_policy user_policy;
> >
> > + struct cpufreq_boost *boost;
> > + struct list_head boost_list;
>
> We don't need both of these.

*boost pointer is necessary when one wants to enable/disable boost from
e.g governor code (which operates mostly on struct cpufreq_policy
*policy pointers).

The boost_list is necessary to connect policies in a list.

>
> > struct kobject kobj;
> > struct completion kobj_unregister;
> > };
>
> > @@ -277,7 +302,6 @@ struct cpufreq_driver {
> > int cpufreq_register_driver(struct cpufreq_driver *driver_data);
> > int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
> >
> > -
>
> ??
>
> > void cpufreq_notify_transition(struct cpufreq_policy *policy,
> > struct cpufreq_freqs *freqs, unsigned int state);
> >
> > @@ -403,6 +427,9 @@ extern struct cpufreq_governor
> > cpufreq_gov_conservative; #define CPUFREQ_ENTRY_INVALID ~0
> > #define CPUFREQ_TABLE_END ~1
> >
> > +/* Define index for boost frequency */
> > +#define CPUFREQ_BOOST ~2
>
> s/CPUFREQ_BOOST/CPUFREQ_BOOST_FREQ

Ok, will be changed to something more descriptive.

Thanks for thorough review :-)

--
Best regards,

Lukasz Majewski

Samsung R&D Institute Poland (SRPOL) | Linux Platform Group