From: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
To: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Mark Rutland <Mark.Rutland@arm.com>,
"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
Howard Chen <howard.chen@linaro.org>,
Kevin Hilman <khilman@linaro.org>,
Mathieu Poirier <mathieu.poirier@linaro.org>,
"linux-pm@vger.kernel.org" <linux-pm@vger.kernel.org>,
Rob Herring <robh+dt@kernel.org>,
Lina Iyer <lina.iyer@linaro.org>,
Sudeep Holla <Sudeep.Holla@arm.com>,
"grant.likely@linaro.org" <grant.likely@linaro.org>,
"linux-arm-kernel@lists.infradead.org"
<linux-arm-kernel@lists.infradead.org>
Subject: Re: [RFC PATCH v2 1/1] drivers: cpuidle: cpuidle-arm: heterogeneous systems extension
Date: Tue, 5 May 2015 16:56:15 +0100 [thread overview]
Message-ID: <20150505155615.GA23106@red-moon> (raw)
In-Reply-To: <554771D3.5010701@linaro.org>
On Mon, May 04, 2015 at 02:19:15PM +0100, Daniel Lezcano wrote:
> On 04/16/2015 06:10 PM, Lorenzo Pieralisi wrote:
<snip>
> > /*
> > - * arm_idle_init
> > + * Compare idle states phandle properties
> > *
> > - * Registers the arm specific cpuidle driver with the cpuidle
> > - * framework. It relies on core code to parse the idle states
> > - * and initialize them using driver data structures accordingly.
> > + * Return true if properties are valid and equal, false otherwise
>
> Just a detail. Would be more consistent to return zero when valid and
> equal ?
Consistent with memcmp you mean ? Yes, I can change it.
> > */
> > -static int __init arm_idle_init(void)
> > +static bool __init idle_states_cmp(struct property *states1,
> > + struct property *states2)
> > +{
> > + /*
> > + * NB: Implemented through code from drivers/of/unittest.c
> > + * Function is generic and can be moved to generic OF code
> > + * if needed
> > + */
> > + return states1 && states2 &&
> > + (states1->length == states2->length) &&
> > + states1->value && states2->value &&
> > + !memcmp(states1->value, states2->value, states1->length);
> > +}
> > +
> > +static int __init arm_idle_init_driver(struct cpuidle_driver *drv)
> > {
> > - int cpu, ret;
> > - struct cpuidle_driver *drv = &arm_idle_driver;
> > + int ret, cpu;
> > struct cpuidle_device *dev;
> > + struct property *curr_idle_states, *idle_states = NULL;
> > + struct device_node *cpu_node;
> > +
> > + for_each_cpu(cpu, drv->cpumask) {
> > + cpu_node = of_cpu_device_node_get(cpu);
> > + curr_idle_states = of_find_property(cpu_node,
> > + "cpu-idle-states", NULL);
> > + of_node_put(cpu_node);
> > +
> > + /*
> > + * Stash the first valid idle states phandle in the cpumask.
> > + * If curr_idle_states is NULL assigning it to idle_states
> > + * is harmless and it is managed by idle states comparison
> > + * code. Keep track of first valid phandle so that
> > + * subsequent cpus can compare against it.
> > + */
> > + if (!idle_states)
> > + idle_states = curr_idle_states;
> > +
> > + /*
> > + * If idle states phandles are not equal, remove the
> > + * cpu from the driver mask since a CPUidle driver
> > + * is only capable of managing uniform idle states.
> > + *
> > + * Comparison works also when idle_states and
> > + * curr_idle_states are the same property, since
> > + * they can be == NULL so the cpu must be removed from
> > + * the driver mask in that case too (ie cpu has no idle
> > + * states).
> > + */
> > + if (!idle_states_cmp(idle_states, curr_idle_states))
> > + cpumask_clear_cpu(cpu, drv->cpumask);
> > + }
> > +
> > + /*
> > + * If there are no valid states for this driver we rely on arch
> > + * default idle behaviour, bail out
> > + */
> > + if (!idle_states)
> > + return -ENODEV;
> >
> > /*
> > * Initialize idle states data, starting at index 1.
> > @@ -117,7 +173,7 @@ static int __init arm_idle_init(void)
> > * Call arch CPU operations in order to initialize
> > * idle states suspend back-end specific data
> > */
> > - for_each_possible_cpu(cpu) {
> > + for_each_cpu(cpu, drv->cpumask) {
> > ret = arm_cpuidle_init(cpu);
> >
> > /*
> > @@ -157,7 +213,77 @@ out_fail:
> > }
> >
> > cpuidle_unregister_driver(drv);
> > + return ret;
> > +}
> > +
> > +/*
> > + * arm_idle_init
> > + *
> > + * Registers the arm specific cpuidle driver(s) with the cpuidle
> > + * framework. It relies on core code to parse the idle states
> > + * and initialize them using driver data structures accordingly.
> > + */
> > +static int __init arm_idle_init(void)
> > +{
> > + int i, ret = -ENODEV;
> > + struct cpuidle_driver *drv;
> > + cpumask_var_t tmpmask;
> > +
> > + /*
> > + * These drivers require DT idle states to be present.
> > + * If no idle states are detected there is no reason to
> > + * proceed any further hence we return early.
> > + */
> > + if (!of_find_node_by_name(NULL, "idle-states"))
> > + return -ENODEV;
> > +
> > + if (!alloc_cpumask_var(&tmpmask, GFP_KERNEL))
> > + return -ENOMEM;
> > +
> > + /*
> > + * We need to vet idle states to create CPUidle drivers
> > + * that share a common set of them. Create a tmp mask
> > + * that we use to keep track of initialized cpus.
> > + * Start off by initializing the mask with all possible
> > + * cpus, we clear it as we go, till either all cpus
> > + * have a CPUidle driver initialized or there are some
> > + * CPUs that have no idle states or a parsing error
> > + * occurs.
> > + */
> > + cpumask_copy(tmpmask, cpu_possible_mask);
> > +
> > + for (i = 0; !cpumask_empty(tmpmask); i++) {
> > + if (i == ARM_CPUIDLE_MAX_DRIVERS) {
> > + pr_warn("number of drivers exceeding static allocation\n");
> > + break;
> > + }
> > +
> > + drv = &arm_idle_drivers[i];
> > + drv->cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
> > + if (!drv->cpumask) {
> > + ret = -ENOMEM;
> > + break;
> > + }
> > + /*
> > + * Force driver mask, arm_idle_init_driver()
> > + * will tweak it by vetting idle states.
> > + */
> > + cpumask_copy(drv->cpumask, tmpmask);
>
> Why do you need to copy tmpmask ? Isn't simpler to have a zero-ed
> cpumask and let the arm_idle_init_driver function to set a bit instead
> of clearing it ?
No, because we need to keep track of logical cpus that have been
already parsed, we need a tmpmask to keep track of that, how could
we do it otherwise ? We can have more than two cpumask sets.
>
> > + ret = arm_idle_init_driver(drv);
> > + if (ret) {
> > + kfree(drv->cpumask);
> > + break;
> > + }
> > + /*
> > + * Remove the cpus that were part of the registered
> > + * driver from the mask of cpus to be initialized
> > + * and restart.
> > + */
> > + cpumask_andnot(tmpmask, tmpmask, drv->cpumask);
>
> If there is a DT definition with just a cluster with the cpuidle driver
> set and another one without any idle state, we will have always a
> pr_warn because i == ARM_CPUIDLE_MAX_DRIVERS due to tmpmask being never
> equal to a zero mask. Is it the purpose to detect such cases ?
Not really, because arm_idle_init_driver() would return -ENODEV when
it detects cpus with no idle states and the loop will break before warning.
If we had two cluster of cpus with an idle-states set per cluster plus
some cpus with no idle states the warning would be hit, because
in actual facts we have more cpuidle sets than drivers (I know, a cpu
set with no idle states falls back to default idle, but I think
that's a detail that is easy to sort out).
I can create idle drivers dynamically so that the ARM_CPUIDLE_MAX_DRIVERS
check can be removed or relaxed (but the code becomes slightly more complex).
Thanks for having a look, apart from these comments do we think it is
a reasonable approach ?
Lorenzo
>
> > + }
> >
> > + free_cpumask_var(tmpmask);
> > return ret;
> > }
> > device_initcall(arm_idle_init);
> >
>
>
> --
> <http://www.linaro.org/> Linaro.org ??? Open source software for ARM SoCs
>
> Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook |
> <http://twitter.com/#!/linaroorg> Twitter |
> <http://www.linaro.org/linaro-blog/> Blog
>
next prev parent reply other threads:[~2015-05-05 15:56 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-04-16 16:10 [RFC PATCH v2 0/1] ARM: cpuidle: heterogeneous systems extension Lorenzo Pieralisi
2015-04-16 16:10 ` [RFC PATCH v2 1/1] drivers: cpuidle: cpuidle-arm: " Lorenzo Pieralisi
2015-05-04 13:19 ` Daniel Lezcano
2015-05-05 15:56 ` Lorenzo Pieralisi [this message]
2015-05-12 13:03 ` Lorenzo Pieralisi
2015-07-14 4:52 ` Daniel Kurtz
[not found] ` <CAGS+omBtraNBH43qsFn_YgO3ePbFav9VtwgqDUinbOBMPwOGXA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-07-14 10:04 ` Lorenzo Pieralisi
2015-07-14 10:23 ` Lorenzo Pieralisi
2015-04-21 18:24 ` [RFC PATCH v2 0/1] ARM: cpuidle: " Kevin Hilman
2015-04-22 8:55 ` Lorenzo Pieralisi
2015-05-12 16:36 ` Lina Iyer
[not found] ` <1429200617-9546-1-git-send-email-lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org>
2015-04-30 15:51 ` Lorenzo Pieralisi
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=20150505155615.GA23106@red-moon \
--to=lorenzo.pieralisi@arm.com \
--cc=Mark.Rutland@arm.com \
--cc=Sudeep.Holla@arm.com \
--cc=daniel.lezcano@linaro.org \
--cc=devicetree@vger.kernel.org \
--cc=grant.likely@linaro.org \
--cc=howard.chen@linaro.org \
--cc=khilman@linaro.org \
--cc=lina.iyer@linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-pm@vger.kernel.org \
--cc=mathieu.poirier@linaro.org \
--cc=robh+dt@kernel.org \
/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;
as well as URLs for NNTP newsgroup(s).