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

WARNING: multiple messages have this Message-ID (diff)
From: lorenzo.pieralisi@arm.com (Lorenzo Pieralisi)
To: linux-arm-kernel@lists.infradead.org
Subject: [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
> 

  reply	other threads:[~2015-05-05 15:56 UTC|newest]

Thread overview: 24+ 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 ` Lorenzo Pieralisi
2015-04-16 16:10 ` [RFC PATCH v2 1/1] drivers: cpuidle: cpuidle-arm: " Lorenzo Pieralisi
2015-04-16 16:10   ` Lorenzo Pieralisi
2015-05-04 13:19   ` Daniel Lezcano
2015-05-04 13:19     ` Daniel Lezcano
2015-05-05 15:56     ` Lorenzo Pieralisi [this message]
2015-05-05 15:56       ` Lorenzo Pieralisi
2015-05-12 13:03       ` Lorenzo Pieralisi
2015-05-12 13:03         ` Lorenzo Pieralisi
2015-07-14  4:52         ` Daniel Kurtz
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:04               ` Lorenzo Pieralisi
2015-07-14 10:23     ` 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-21 18:24   ` Kevin Hilman
2015-04-22  8:55   ` Lorenzo Pieralisi
2015-04-22  8:55     ` Lorenzo Pieralisi
2015-05-12 16:36     ` Lina Iyer
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
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.