* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2013-12-16 16:49 [PATCH 1/4] arm64: topology: Implement basic CPU topology support Mark Brown
@ 2013-12-16 16:49 ` Mark Brown
2013-12-17 17:40 ` Lorenzo Pieralisi
0 siblings, 1 reply; 28+ messages in thread
From: Mark Brown @ 2013-12-16 16:49 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 145 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 145 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index b078afa6958d..7fd473367e9b 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -18,6 +18,7 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -25,6 +26,147 @@
#include <asm/smp_plat.h>
#include <asm/topology.h>
+#ifdef CONFIG_OF
+static int cluster_id;
+
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node) {
+ pr_crit("%s: Unable to parse CPU phandle\n", node->full_name);
+ return -1;
+ }
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+ return -1;
+}
+
+static void __init parse_core(struct device_node *core, int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i, cpu;
+ struct device_node *t;
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu) {
+ pr_info("CPU%d: socket %d core %d thread %d\n",
+ cpu, cluster_id, core_id, i);
+ cpu_topology[cpu].socket_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ }
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return;
+ }
+
+ pr_info("CPU%d: socket %d core %d\n",
+ cpu, cluster_id, core_id);
+ cpu_topology[cpu].socket_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ }
+}
+
+static void __init parse_cluster(struct device_node *cluster)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ int core_id = 0;
+ int i;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ parse_cluster(c);
+ leaf = false;
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (leaf)
+ parse_core(c, core_id++);
+ else
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+}
+
+static void __init parse_dt_topology(void)
+{
+ struct device_node *cn;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return;
+ }
+
+ /*
+ * If topology is provided as a cpu-map it is essentially a
+ * root cluster.
+ */
+ cn = of_find_node_by_name(cn, "cpu-map");
+ if (!cn)
+ return;
+ parse_cluster(cn);
+}
+
+#else
+static inline void parse_dt_topology(void) {}
+#endif
+
/*
* cpu topology table
*/
@@ -91,5 +233,8 @@ void __init init_cpu_topology(void)
cpumask_clear(&cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
}
+
+ parse_dt_topology();
+
smp_wmb();
}
--
1.8.5.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2013-12-16 16:49 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
@ 2013-12-17 17:40 ` Lorenzo Pieralisi
2013-12-17 19:19 ` Mark Brown
0 siblings, 1 reply; 28+ messages in thread
From: Lorenzo Pieralisi @ 2013-12-17 17:40 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Dec 16, 2013 at 04:49:23PM +0000, Mark Brown wrote:
[...]
> +#ifdef CONFIG_OF
> +static int cluster_id;
> +
> +static int __init get_cpu_for_node(struct device_node *node)
> +{
> + struct device_node *cpu_node;
> + int cpu;
> +
> + cpu_node = of_parse_phandle(node, "cpu", 0);
> + if (!cpu_node) {
> + pr_crit("%s: Unable to parse CPU phandle\n", node->full_name);
> + return -1;
> + }
> +
> + for_each_possible_cpu(cpu) {
> + if (of_get_cpu_node(cpu, NULL) == cpu_node)
> + return cpu;
> + }
> +
> + pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
> + return -1;
> +}
> +
> +static void __init parse_core(struct device_node *core, int core_id)
> +{
> + char name[10];
> + bool leaf = true;
> + int i, cpu;
> + struct device_node *t;
> +
> + i = 0;
> + do {
> + snprintf(name, sizeof(name), "thread%d", i);
> + t = of_get_child_by_name(core, name);
> + if (t) {
> + leaf = false;
> + cpu = get_cpu_for_node(t);
> + if (cpu) {
I think that's wrong. If cpu == -1 that should be skipped.
> + pr_info("CPU%d: socket %d core %d thread %d\n",
> + cpu, cluster_id, core_id, i);
> + cpu_topology[cpu].socket_id = cluster_id;
> + cpu_topology[cpu].core_id = core_id;
> + cpu_topology[cpu].thread_id = i;
> + } else {
> + pr_err("%s: Can't get CPU for thread\n",
> + t->full_name);
> + }
> + }
> + i++;
> + } while (t);
> +
> + cpu = get_cpu_for_node(core);
> + if (cpu >= 0) {
> + if (!leaf) {
> + pr_err("%s: Core has both threads and CPU\n",
> + core->full_name);
> + return;
> + }
> +
> + pr_info("CPU%d: socket %d core %d\n",
> + cpu, cluster_id, core_id);
> + cpu_topology[cpu].socket_id = cluster_id;
> + cpu_topology[cpu].core_id = core_id;
> + } else if (leaf) {
> + pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
> + }
> +}
> +
> +static void __init parse_cluster(struct device_node *cluster)
> +{
> + char name[10];
> + bool leaf = true;
> + bool has_cores = false;
> + struct device_node *c;
> + int core_id = 0;
> + int i;
> +
> + /*
> + * First check for child clusters; we currently ignore any
> + * information about the nesting of clusters and present the
> + * scheduler with a flat list of them.
> + */
> + i = 0;
> + do {
> + snprintf(name, sizeof(name), "cluster%d", i);
> + c = of_get_child_by_name(cluster, name);
> + if (c) {
> + parse_cluster(c);
> + leaf = false;
> + }
> + i++;
> + } while (c);
> +
A cpu-map can only contain cluster nodes, this is not verified here, but
it has to be. Put it differently, a core node cannot be a cpu-map direct
child, a long winded way to say cpu-map cannot be parsed by this function
as it is.
> + /* Now check for cores */
> + i = 0;
> + do {
> + snprintf(name, sizeof(name), "core%d", i);
> + c = of_get_child_by_name(cluster, name);
> + if (c) {
> + has_cores = true;
> +
> + if (leaf)
> + parse_core(c, core_id++);
> + else
> + pr_err("%s: Non-leaf cluster with core %s\n",
> + cluster->full_name, name);
> + }
> + i++;
> + } while (c);
> +
> + if (leaf && !has_cores)
> + pr_warn("%s: empty cluster\n", cluster->full_name);
> +
> + if (leaf)
> + cluster_id++;
> +}
> +
> +static void __init parse_dt_topology(void)
> +{
> + struct device_node *cn;
> +
> + cn = of_find_node_by_path("/cpus");
> + if (!cn) {
> + pr_err("No CPU information found in DT\n");
> + return;
> + }
> +
> + /*
> + * If topology is provided as a cpu-map it is essentially a
> + * root cluster.
No, because it can't contain core nodes as direct children.
"a cpu-map's child nodes can be: one or more cluster nodes" the bindings
say :)
Apart from these minor remarks, I think we should aim for consolidating
these parsing functions, after all they are all pretty similar bar minor
corner cases, or at least factor out the parsing/enumeration loops.
What do you think ?
Thanks,
Lorenzo
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2013-12-17 17:40 ` Lorenzo Pieralisi
@ 2013-12-17 19:19 ` Mark Brown
0 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2013-12-17 19:19 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Dec 17, 2013 at 05:40:37PM +0000, Lorenzo Pieralisi wrote:
> On Mon, Dec 16, 2013 at 04:49:23PM +0000, Mark Brown wrote:
> > + i = 0;
> > + do {
> > + snprintf(name, sizeof(name), "thread%d", i);
> > + t = of_get_child_by_name(core, name);
> > + if (t) {
> > + leaf = false;
> > + cpu = get_cpu_for_node(t);
> > + if (cpu) {
> I think that's wrong. If cpu == -1 that should be skipped.
Yup, good spot.
> > + do {
> > + snprintf(name, sizeof(name), "cluster%d", i);
> > + c = of_get_child_by_name(cluster, name);
> > + if (c) {
> > + parse_cluster(c);
> > + leaf = false;
> > + }
> > + i++;
> > + } while (c);
> > +
> A cpu-map can only contain cluster nodes, this is not verified here, but
> it has to be. Put it differently, a core node cannot be a cpu-map direct
> child, a long winded way to say cpu-map cannot be parsed by this function
> as it is.
Well, it can be parsed totally happily but we're not as strict with the
validation as we might want be - we'll parse valid bindings successfully
but also accept out of spec bindings (but then they're using undefined
behaviour so anything could happen including the kernel trying to do
something sensible with what it was handed).
It might just make sense to change the binding here, saying the cpu_map
is a root cluster seems reasonable to me, it doesn't hurt to have the
extra level but it doesn't seem to buy us anything either. But we could
also add a validation check for unwanted properties, I'm not that fussed
between any of these options.
> Apart from these minor remarks, I think we should aim for consolidating
> these parsing functions, after all they are all pretty similar bar minor
> corner cases, or at least factor out the parsing/enumeration loops.
> What do you think ?
I thought about that and did poke at it but it didn't seem worth the
effort for the very small number of uses - adding a callback for the
action didn't seem to be doing anything for the readability and starting
to define macros didn't fill me with great joy. I didn't want to put
anything in of.h as bindings that can use the existing iterators are
generally more idiomatic.
It may be there's some nice way of writing the factoring out but I
didn't think of it.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20131217/a5c74b41/attachment.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2013-12-19 20:06 [PATCH 0/4] arm64 topology support Mark Brown
@ 2013-12-19 20:06 ` Mark Brown
0 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2013-12-19 20:06 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 145 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 145 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 853544f30a8b..5a2724b3d4b7 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -17,11 +17,153 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <asm/topology.h>
+#ifdef CONFIG_OF
+static int cluster_id;
+
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node) {
+ pr_crit("%s: Unable to parse CPU phandle\n", node->full_name);
+ return -1;
+ }
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+ return -1;
+}
+
+static void __init parse_core(struct device_node *core, int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i, cpu;
+ struct device_node *t;
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ pr_info("CPU%d: socket %d core %d thread %d\n",
+ cpu, cluster_id, core_id, i);
+ cpu_topology[cpu].socket_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ }
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return;
+ }
+
+ pr_info("CPU%d: socket %d core %d\n",
+ cpu, cluster_id, core_id);
+ cpu_topology[cpu].socket_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ }
+}
+
+static void __init parse_cluster(struct device_node *cluster)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ int core_id = 0;
+ int i;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ parse_cluster(c);
+ leaf = false;
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (leaf)
+ parse_core(c, core_id++);
+ else
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+}
+
+static void __init parse_dt_topology(void)
+{
+ struct device_node *cn;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return;
+ }
+
+ /*
+ * If topology is provided as a cpu-map it is essentially a
+ * root cluster.
+ */
+ cn = of_find_node_by_name(cn, "cpu-map");
+ if (!cn)
+ return;
+ parse_cluster(cn);
+}
+
+#else
+static inline void parse_dt_topology(void) {}
+#endif
+
/*
* cpu topology table
*/
@@ -88,5 +230,8 @@ void __init init_cpu_topology(void)
cpumask_clear(&cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
}
+
+ parse_dt_topology();
+
smp_wmb();
}
--
1.8.5.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-01-08 17:10 [PATCH 1/4] arm64: topology: Implement basic CPU topology support Mark Brown
@ 2014-01-08 17:10 ` Mark Brown
2014-01-08 18:23 ` Lorenzo Pieralisi
0 siblings, 1 reply; 28+ messages in thread
From: Mark Brown @ 2014-01-08 17:10 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
This doesn't actually ignore CPUs in the root cpu-map, merely warns
about them. After looking at ignoring them it seemed like a more
sensible thing to just shove them in a separate cluster for now -
giving the scheduler information about only some of the cores seemed
like it was asking for trouble and trying to do anything more active
seems like a lot of work to unsupport broken systems (if you see what
I mean). I would expect that the end result of putting them in a
cluster is going to be about the same as not providing information
anyway.
It seems like if this isn't enough then either disabling the affected
CPUs entirely or making the warnings louder is the way forwards.
arch/arm64/kernel/topology.c | 149 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 149 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 853544f30a8b..e77c6b0844be 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -17,11 +17,157 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <asm/topology.h>
+#ifdef CONFIG_OF
+static int cluster_id;
+
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node) {
+ pr_crit("%s: Unable to parse CPU phandle\n", node->full_name);
+ return -1;
+ }
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+ return -1;
+}
+
+static void __init parse_core(struct device_node *core, int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i, cpu;
+ struct device_node *t;
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ pr_info("CPU%d: socket %d core %d thread %d\n",
+ cpu, cluster_id, core_id, i);
+ cpu_topology[cpu].socket_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ }
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return;
+ }
+
+ pr_info("CPU%d: socket %d core %d\n",
+ cpu, cluster_id, core_id);
+ cpu_topology[cpu].socket_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ }
+}
+
+static void __init parse_cluster(struct device_node *cluster, int depth)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ int core_id = 0;
+ int i;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ parse_cluster(c, depth + 1);
+ leaf = false;
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (depth == 0)
+ pr_err("%s: cpu-map children should be clusters\n",
+ c->full_name);
+
+ if (leaf)
+ parse_core(c, core_id++);
+ else
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+}
+
+static void __init parse_dt_topology(void)
+{
+ struct device_node *cn;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return;
+ }
+
+ /*
+ * If topology is provided as a cpu-map it is essentially a
+ * root cluster.
+ */
+ cn = of_find_node_by_name(cn, "cpu-map");
+ if (!cn)
+ return;
+ parse_cluster(cn, 0);
+}
+
+#else
+static inline void parse_dt_topology(void) {}
+#endif
+
/*
* cpu topology table
*/
@@ -88,5 +234,8 @@ void __init init_cpu_topology(void)
cpumask_clear(&cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
}
+
+ parse_dt_topology();
+
smp_wmb();
}
--
1.8.5.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-01-08 17:10 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
@ 2014-01-08 18:23 ` Lorenzo Pieralisi
2014-01-08 18:32 ` Mark Brown
0 siblings, 1 reply; 28+ messages in thread
From: Lorenzo Pieralisi @ 2014-01-08 18:23 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Jan 08, 2014 at 05:10:11PM +0000, Mark Brown wrote:
> From: Mark Brown <broonie@linaro.org>
>
> Add support for parsing the explicit topology bindings to discover the
> topology of the system.
>
> Since it is not currently clear how to map multi-level clusters for the
> scheduler all leaf clusters are presented to the scheduler at the same
> level. This should be enough to provide good support for current systems.
>
> Signed-off-by: Mark Brown <broonie@linaro.org>
> ---
>
> This doesn't actually ignore CPUs in the root cpu-map, merely warns
> about them. After looking at ignoring them it seemed like a more
> sensible thing to just shove them in a separate cluster for now -
> giving the scheduler information about only some of the cores seemed
> like it was asking for trouble and trying to do anything more active
> seems like a lot of work to unsupport broken systems (if you see what
> I mean). I would expect that the end result of putting them in a
> cluster is going to be about the same as not providing information
> anyway.
>
> It seems like if this isn't enough then either disabling the affected
> CPUs entirely or making the warnings louder is the way forwards.
Well, there are so many corner cases (eg duplicated CPUs might be
another one - should we track that ? It is probably something worth
warning on, you check before initializing a cpu struct if it has already
been initialized) that they are hard to track. What you did makes sense
to me at first glance.
> arch/arm64/kernel/topology.c | 149 +++++++++++++++++++++++++++++++++++++++++++
> +static void __init parse_cluster(struct device_node *cluster, int depth)
Is it really worth adding a depth parameter just for cpu-map ? Can't we check
the node name ? Either way is fine by me, just flagging this up.
> +{
> + char name[10];
> + bool leaf = true;
> + bool has_cores = false;
> + struct device_node *c;
> + int core_id = 0;
> + int i;
> +
> + /*
> + * First check for child clusters; we currently ignore any
> + * information about the nesting of clusters and present the
> + * scheduler with a flat list of them.
> + */
> + i = 0;
> + do {
> + snprintf(name, sizeof(name), "cluster%d", i);
> + c = of_get_child_by_name(cluster, name);
> + if (c) {
> + parse_cluster(c, depth + 1);
> + leaf = false;
> + }
> + i++;
> + } while (c);
> +
> + /* Now check for cores */
> + i = 0;
> + do {
> + snprintf(name, sizeof(name), "core%d", i);
> + c = of_get_child_by_name(cluster, name);
> + if (c) {
> + has_cores = true;
> +
> + if (depth == 0)
> + pr_err("%s: cpu-map children should be clusters\n",
> + c->full_name);
> +
> + if (leaf)
> + parse_core(c, core_id++);
> + else
> + pr_err("%s: Non-leaf cluster with core %s\n",
> + cluster->full_name, name);
> + }
> + i++;
> + } while (c);
> +
> + if (leaf && !has_cores)
> + pr_warn("%s: empty cluster\n", cluster->full_name);
> +
> + if (leaf)
> + cluster_id++;
We increment cluster_id even for leaf clusters with no cores, it is
probably safe to do it given that cluster id are completely arbitrary
numbers, just wanted to mention that.
> +}
> +
> +static void __init parse_dt_topology(void)
> +{
> + struct device_node *cn;
> +
> + cn = of_find_node_by_path("/cpus");
> + if (!cn) {
> + pr_err("No CPU information found in DT\n");
> + return;
> + }
> +
> + /*
> + * If topology is provided as a cpu-map it is essentially a
> + * root cluster.
> + */
> + cn = of_find_node_by_name(cn, "cpu-map");
> + if (!cn)
> + return;
> + parse_cluster(cn, 0);
You could loop through cluster children here, but I understand you
want to avoid copying the sprintf variables and code here too just to
check for cluster children, so it is fine to leave code as it is
(probably we can remove the depth parameter too).
Lorenzo
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-01-08 18:23 ` Lorenzo Pieralisi
@ 2014-01-08 18:32 ` Mark Brown
0 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2014-01-08 18:32 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Jan 08, 2014 at 06:23:31PM +0000, Lorenzo Pieralisi wrote:
> > This doesn't actually ignore CPUs in the root cpu-map, merely warns
> > about them. After looking at ignoring them it seemed like a more
> > sensible thing to just shove them in a separate cluster for now -
> > giving the scheduler information about only some of the cores seemed
> > like it was asking for trouble and trying to do anything more active
> > seems like a lot of work to unsupport broken systems (if you see what
> > I mean). I would expect that the end result of putting them in a
> > cluster is going to be about the same as not providing information
> > anyway.
> > It seems like if this isn't enough then either disabling the affected
> > CPUs entirely or making the warnings louder is the way forwards.
> Well, there are so many corner cases (eg duplicated CPUs might be
> another one - should we track that ? It is probably something worth
> warning on, you check before initializing a cpu struct if it has already
> been initialized) that they are hard to track. What you did makes sense
> to me at first glance.
Yes, there's definitely scope for improving the robustness. On the
other hand I'm sure all device tree authors are dilligent and careful so
there's no need for us to worry! :P
> > arch/arm64/kernel/topology.c | 149 +++++++++++++++++++++++++++++++++++++++++++
> > +static void __init parse_cluster(struct device_node *cluster, int depth)
> Is it really worth adding a depth parameter just for cpu-map ? Can't we check
> the node name ? Either way is fine by me, just flagging this up.
It seemed more idiomatic and I suspect we may want the depth in future
if we try to tell the scheduler about clusters of clusters. It's
certainly easier to leave the code as is now I've written it this way if
nothing else.
> > + if (leaf)
> > + cluster_id++;
> We increment cluster_id even for leaf clusters with no cores, it is
> probably safe to do it given that cluster id are completely arbitrary
> numbers, just wanted to mention that.
Right, my thoughts exactly - this is the simplest thing I thought of.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140108/4ab73550/attachment.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-01-08 19:12 [PATCH 1/4] arm64: topology: Implement basic CPU topology support Mark Brown
@ 2014-01-08 19:12 ` Mark Brown
2014-01-09 12:50 ` Lorenzo Pieralisi
0 siblings, 1 reply; 28+ messages in thread
From: Mark Brown @ 2014-01-08 19:12 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 150 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 150 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 20eef01a4707..e77c6b0844be 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -17,10 +17,157 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
+#include <linux/slab.h>
#include <asm/topology.h>
+#ifdef CONFIG_OF
+static int cluster_id;
+
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node) {
+ pr_crit("%s: Unable to parse CPU phandle\n", node->full_name);
+ return -1;
+ }
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+ return -1;
+}
+
+static void __init parse_core(struct device_node *core, int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i, cpu;
+ struct device_node *t;
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ pr_info("CPU%d: socket %d core %d thread %d\n",
+ cpu, cluster_id, core_id, i);
+ cpu_topology[cpu].socket_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ }
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return;
+ }
+
+ pr_info("CPU%d: socket %d core %d\n",
+ cpu, cluster_id, core_id);
+ cpu_topology[cpu].socket_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ }
+}
+
+static void __init parse_cluster(struct device_node *cluster, int depth)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ int core_id = 0;
+ int i;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ parse_cluster(c, depth + 1);
+ leaf = false;
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (depth == 0)
+ pr_err("%s: cpu-map children should be clusters\n",
+ c->full_name);
+
+ if (leaf)
+ parse_core(c, core_id++);
+ else
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+}
+
+static void __init parse_dt_topology(void)
+{
+ struct device_node *cn;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return;
+ }
+
+ /*
+ * If topology is provided as a cpu-map it is essentially a
+ * root cluster.
+ */
+ cn = of_find_node_by_name(cn, "cpu-map");
+ if (!cn)
+ return;
+ parse_cluster(cn, 0);
+}
+
+#else
+static inline void parse_dt_topology(void) {}
+#endif
+
/*
* cpu topology table
*/
@@ -87,5 +234,8 @@ void __init init_cpu_topology(void)
cpumask_clear(&cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
}
+
+ parse_dt_topology();
+
smp_wmb();
}
--
1.8.5.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-01-08 19:12 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
@ 2014-01-09 12:50 ` Lorenzo Pieralisi
2014-01-09 13:26 ` Mark Brown
0 siblings, 1 reply; 28+ messages in thread
From: Lorenzo Pieralisi @ 2014-01-09 12:50 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Jan 08, 2014 at 07:12:08PM +0000, Mark Brown wrote:
> From: Mark Brown <broonie@linaro.org>
> diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
> index 20eef01a4707..e77c6b0844be 100644
> --- a/arch/arm64/kernel/topology.c
> +++ b/arch/arm64/kernel/topology.c
> @@ -17,10 +17,157 @@
> #include <linux/percpu.h>
> #include <linux/node.h>
> #include <linux/nodemask.h>
> +#include <linux/of.h>
> #include <linux/sched.h>
> +#include <linux/slab.h>
It does not belong here either.
> #include <asm/topology.h>
>
> +#ifdef CONFIG_OF
> +static int cluster_id;
This is __initdata. I think it is better to move it to parse_cluster
and pass it to parse_core as a parameter instead of using a global.
> +static int __init get_cpu_for_node(struct device_node *node)
> +{
> + struct device_node *cpu_node;
> + int cpu;
> +
> + cpu_node = of_parse_phandle(node, "cpu", 0);
> + if (!cpu_node) {
> + pr_crit("%s: Unable to parse CPU phandle\n", node->full_name);
This messages is spit out anytime you try to grab a cpu phandle and it
is not there. In particular, in parse_core if core is not a leaf but you
still try to grab a cpu phandle this message is spit out and that's not
nice at all. Either we remove the message or we do not check the phandle
in core nodes that are not leaves.
> + return -1;
> + }
> +
> + for_each_possible_cpu(cpu) {
> + if (of_get_cpu_node(cpu, NULL) == cpu_node)
> + return cpu;
> + }
> +
> + pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
> + return -1;
> +}
> +
> +static void __init parse_core(struct device_node *core, int core_id)
> +{
> + char name[10];
> + bool leaf = true;
> + int i, cpu;
> + struct device_node *t;
> +
> + i = 0;
> + do {
> + snprintf(name, sizeof(name), "thread%d", i);
> + t = of_get_child_by_name(core, name);
> + if (t) {
> + leaf = false;
> + cpu = get_cpu_for_node(t);
> + if (cpu >= 0) {
> + pr_info("CPU%d: socket %d core %d thread %d\n",
> + cpu, cluster_id, core_id, i);
Since all these variables are internal values that have no real meaning
I am not sure it is useful to print them out. Probably better to print
the resulting cpu masks on every cpu, problem is when to do that.
> + cpu_topology[cpu].socket_id = cluster_id;
> + cpu_topology[cpu].core_id = core_id;
> + cpu_topology[cpu].thread_id = i;
> + } else {
> + pr_err("%s: Can't get CPU for thread\n",
> + t->full_name);
> + }
> + }
> + i++;
> + } while (t);
> +
> + cpu = get_cpu_for_node(core);
This is what I am referring too above. For core nodes containing threads a
message is spit out and that must not be there. Either we avoid parsing
core nodes that are not leaves or we change get_cpu_core_for_node.
Lorenzo
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-01-09 12:50 ` Lorenzo Pieralisi
@ 2014-01-09 13:26 ` Mark Brown
0 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2014-01-09 13:26 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Jan 09, 2014 at 12:50:52PM +0000, Lorenzo Pieralisi wrote:
> On Wed, Jan 08, 2014 at 07:12:08PM +0000, Mark Brown wrote:
> > +#ifdef CONFIG_OF
> > +static int cluster_id;
> This is __initdata. I think it is better to move it to parse_cluster
> and pass it to parse_core as a parameter instead of using a global.
Should be __initdata, yes. I actually started using pointers but
passing the pointer around just seemed more ugly, it made the code in
the functions look more scary due to the dereferences.
> > + cpu_node = of_parse_phandle(node, "cpu", 0);
> > + if (!cpu_node) {
> > + pr_crit("%s: Unable to parse CPU phandle\n", node->full_name);
> This messages is spit out anytime you try to grab a cpu phandle and it
> is not there. In particular, in parse_core if core is not a leaf but you
> still try to grab a cpu phandle this message is spit out and that's not
> nice at all. Either we remove the message or we do not check the phandle
> in core nodes that are not leaves.
I'll just drop it.
> > + cpu = get_cpu_for_node(t);
> > + if (cpu >= 0) {
> > + pr_info("CPU%d: socket %d core %d thread %d\n",
> > + cpu, cluster_id, core_id, i);
> Since all these variables are internal values that have no real meaning
> I am not sure it is useful to print them out. Probably better to print
> the resulting cpu masks on every cpu, problem is when to do that.
I found it was useful for getting a quick overview of the topology from
the logs - the actual numbers don't matter but being able to see which
cores are grouped together seemed useful. I'm not sure the masks would
be an improvement for the human reader, I'd expect that presenting more
decoded information would be more helpful.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140109/64be5a90/attachment.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-01-12 19:20 [PATCH v11 0/4] ARMv8 cpu topology Mark Brown
@ 2014-01-12 19:20 ` Mark Brown
2014-01-14 11:43 ` Lorenzo Pieralisi
0 siblings, 1 reply; 28+ messages in thread
From: Mark Brown @ 2014-01-12 19:20 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 143 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 143 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 980019fefeff..7ef0d783ffff 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -17,10 +17,150 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <asm/topology.h>
+#ifdef CONFIG_OF
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node)
+ return -1;
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+ return -1;
+}
+
+static void __init parse_core(struct device_node *core, int cluster_id,
+ int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i, cpu;
+ struct device_node *t;
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ cpu_topology[cpu].socket_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ }
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return;
+ }
+
+ cpu_topology[cpu].socket_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ }
+}
+
+static void __init parse_cluster(struct device_node *cluster, int depth)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ static int cluster_id = 0;
+ int core_id = 0;
+ int i;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ parse_cluster(c, depth + 1);
+ leaf = false;
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (depth == 0)
+ pr_err("%s: cpu-map children should be clusters\n",
+ c->full_name);
+
+ if (leaf)
+ parse_core(c, cluster_id, core_id++);
+ else
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+}
+
+static void __init parse_dt_topology(void)
+{
+ struct device_node *cn;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return;
+ }
+
+ /*
+ * If topology is provided as a cpu-map it is essentially a
+ * root cluster.
+ */
+ cn = of_find_node_by_name(cn, "cpu-map");
+ if (!cn)
+ return;
+ parse_cluster(cn, 0);
+}
+
+#else
+static inline void parse_dt_topology(void) {}
+#endif
+
/*
* cpu topology table
*/
@@ -87,5 +227,8 @@ void __init init_cpu_topology(void)
cpumask_clear(&cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
}
+
+ parse_dt_topology();
+
smp_wmb();
}
--
1.8.5.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-01-12 19:20 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
@ 2014-01-14 11:43 ` Lorenzo Pieralisi
2014-01-14 12:36 ` Mark Brown
0 siblings, 1 reply; 28+ messages in thread
From: Lorenzo Pieralisi @ 2014-01-14 11:43 UTC (permalink / raw)
To: linux-arm-kernel
Hi Mark,
apart from a couple of minor nits and a question, it looks fine to me.
On Sun, Jan 12, 2014 at 07:20:39PM +0000, Mark Brown wrote:
> +static void __init parse_core(struct device_node *core, int cluster_id,
> + int core_id)
> +{
> + char name[10];
> + bool leaf = true;
> + int i, cpu;
> + struct device_node *t;
> +
> + i = 0;
You could initialize i at declaration, I can understand why you are doing that
explictly in parse_cluster (two loops, to make code clearer), but here
it does not make much sense to add a line for that.
> + do {
> + snprintf(name, sizeof(name), "thread%d", i);
If we wanted to be very picky, you need to copy "thread" just once (same
goes for other strings), but we'd better leave code as is IMHO.
> + t = of_get_child_by_name(core, name);
Should we check the MT bit in MPIDR_EL1 before validating threads as well ?
I do not like the idea because this means reliance on MPIDR_EL1 for MT
and DT for topology bits, but it might be a worthwhile check.
It is certainly odd to have a DT with threads and an MPIDR_EL1 with the MT
bit clear.
> + if (t) {
> + leaf = false;
> + cpu = get_cpu_for_node(t);
> + if (cpu >= 0) {
> + cpu_topology[cpu].socket_id = cluster_id;
> + cpu_topology[cpu].core_id = core_id;
> + cpu_topology[cpu].thread_id = i;
> + } else {
> + pr_err("%s: Can't get CPU for thread\n",
> + t->full_name);
> + }
> + }
> + i++;
> + } while (t);
> +
> + cpu = get_cpu_for_node(core);
> + if (cpu >= 0) {
> + if (!leaf) {
> + pr_err("%s: Core has both threads and CPU\n",
> + core->full_name);
> + return;
> + }
> +
> + cpu_topology[cpu].socket_id = cluster_id;
> + cpu_topology[cpu].core_id = core_id;
> + } else if (leaf) {
> + pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
> + }
> +}
> +
> +static void __init parse_cluster(struct device_node *cluster, int depth)
> +{
> + char name[10];
> + bool leaf = true;
> + bool has_cores = false;
> + struct device_node *c;
> + static int cluster_id = 0;
static int __initdata cluster_id;
[...]
> +static void __init parse_dt_topology(void)
> +{
> + struct device_node *cn;
> +
> + cn = of_find_node_by_path("/cpus");
> + if (!cn) {
> + pr_err("No CPU information found in DT\n");
> + return;
> + }
> +
> + /*
> + * If topology is provided as a cpu-map it is essentially a
> + * root cluster.
This comment is a bit misleading, because as you know, (1) topology
can only be provided with cpu-map, (2) cpu-map is not a root cluster.
> + */
> + cn = of_find_node_by_name(cn, "cpu-map");
> + if (!cn)
> + return;
> + parse_cluster(cn, 0);
> +}
> +
> +#else
> +static inline void parse_dt_topology(void) {}
> +#endif
> +
> /*
> * cpu topology table
> */
> @@ -87,5 +227,8 @@ void __init init_cpu_topology(void)
> cpumask_clear(&cpu_topo->core_sibling);
> cpumask_clear(&cpu_topo->thread_sibling);
> }
> +
> + parse_dt_topology();
> +
> smp_wmb();
> }
With the changes/comments above pending:
Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-01-14 11:43 ` Lorenzo Pieralisi
@ 2014-01-14 12:36 ` Mark Brown
0 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2014-01-14 12:36 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jan 14, 2014 at 11:43:37AM +0000, Lorenzo Pieralisi wrote:
> On Sun, Jan 12, 2014 at 07:20:39PM +0000, Mark Brown wrote:
> > +static void __init parse_core(struct device_node *core, int cluster_id,
> > + int core_id)
> > +{
> > + char name[10];
> > + bool leaf = true;
> > + int i, cpu;
> > + struct device_node *t;
> > +
> > + i = 0;
> You could initialize i at declaration, I can understand why you are doing that
> explictly in parse_cluster (two loops, to make code clearer), but here
> it does not make much sense to add a line for that.
I still find it clearer for do { } while loops to have the start
condition required for the loop to function right next to the loop.
Yes, you can save a line code but that's about it.
> > + do {
> > + snprintf(name, sizeof(name), "thread%d", i);
> If we wanted to be very picky, you need to copy "thread" just once (same
> goes for other strings), but we'd better leave code as is IMHO.
That would just make the code more complex, we need to handle tens of
cores so just doing i + '0' won't cut it.
> > + t = of_get_child_by_name(core, name);
> Should we check the MT bit in MPIDR_EL1 before validating threads as well ?
> I do not like the idea because this means reliance on MPIDR_EL1 for MT
> and DT for topology bits, but it might be a worthwhile check.
> It is certainly odd to have a DT with threads and an MPIDR_EL1 with the MT
> bit clear.
Checking seems counter to the idea of forcing everyone to provide this
information from the firmware in the first place - checking that one bit
and ignoring the rest of the information even if it's good would seem
perverse.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140114/8b118e09/attachment.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-01-15 11:38 [PATCH v12 0/4] arm64 topology Mark Brown
@ 2014-01-15 11:38 ` Mark Brown
0 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2014-01-15 11:38 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 143 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 143 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index a6d4ed2d69c0..061e3b813b9f 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -17,10 +17,150 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <asm/topology.h>
+#ifdef CONFIG_OF
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node)
+ return -1;
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+ return -1;
+}
+
+static void __init parse_core(struct device_node *core, int cluster_id,
+ int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i = 0;
+ int cpu;
+ struct device_node *t;
+
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ }
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return;
+ }
+
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ }
+}
+
+static void __init parse_cluster(struct device_node *cluster, int depth)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ static int __initdata cluster_id;
+ int core_id = 0;
+ int i;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ parse_cluster(c, depth + 1);
+ leaf = false;
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (depth == 0)
+ pr_err("%s: cpu-map children should be clusters\n",
+ c->full_name);
+
+ if (leaf)
+ parse_core(c, cluster_id, core_id++);
+ else
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+}
+
+static void __init parse_dt_topology(void)
+{
+ struct device_node *cn;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return;
+ }
+
+ /*
+ * When topology is provided cpu-map is essentially a root
+ * cluster with restricted subnodes.
+ */
+ cn = of_find_node_by_name(cn, "cpu-map");
+ if (!cn)
+ return;
+ parse_cluster(cn, 0);
+}
+
+#else
+static inline void parse_dt_topology(void) {}
+#endif
+
/*
* cpu topology table
*/
@@ -87,5 +227,8 @@ void __init init_cpu_topology(void)
cpumask_clear(&cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
}
+
+ parse_dt_topology();
+
smp_wmb();
}
--
1.8.5.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-02-10 13:02 [PATCH 1/4] arm64: topology: Implement basic CPU topology support Mark Brown
@ 2014-02-10 13:02 ` Mark Brown
0 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2014-02-10 13:02 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 143 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 143 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index a6d4ed2d69c0..061e3b813b9f 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -17,10 +17,150 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <asm/topology.h>
+#ifdef CONFIG_OF
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node)
+ return -1;
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+ return -1;
+}
+
+static void __init parse_core(struct device_node *core, int cluster_id,
+ int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i = 0;
+ int cpu;
+ struct device_node *t;
+
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ }
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return;
+ }
+
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ }
+}
+
+static void __init parse_cluster(struct device_node *cluster, int depth)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ static int __initdata cluster_id;
+ int core_id = 0;
+ int i;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ parse_cluster(c, depth + 1);
+ leaf = false;
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (depth == 0)
+ pr_err("%s: cpu-map children should be clusters\n",
+ c->full_name);
+
+ if (leaf)
+ parse_core(c, cluster_id, core_id++);
+ else
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+}
+
+static void __init parse_dt_topology(void)
+{
+ struct device_node *cn;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return;
+ }
+
+ /*
+ * When topology is provided cpu-map is essentially a root
+ * cluster with restricted subnodes.
+ */
+ cn = of_find_node_by_name(cn, "cpu-map");
+ if (!cn)
+ return;
+ parse_cluster(cn, 0);
+}
+
+#else
+static inline void parse_dt_topology(void) {}
+#endif
+
/*
* cpu topology table
*/
@@ -87,5 +227,8 @@ void __init init_cpu_topology(void)
cpumask_clear(&cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
}
+
+ parse_dt_topology();
+
smp_wmb();
}
--
1.9.0.rc3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-02-11 22:06 [PATCH 1/4] arm64: topology: Implement basic CPU topology support Mark Brown
@ 2014-02-11 22:06 ` Mark Brown
0 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2014-02-11 22:06 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 142 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 142 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index cbb8d432214e..a757297992a1 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -17,10 +17,150 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <asm/topology.h>
+#ifdef CONFIG_OF
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node)
+ return -1;
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+ return -1;
+}
+
+static void __init parse_core(struct device_node *core, int cluster_id,
+ int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i = 0;
+ int cpu;
+ struct device_node *t;
+
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ }
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return;
+ }
+
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ }
+}
+
+static void __init parse_cluster(struct device_node *cluster, int depth)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ static int __initdata cluster_id;
+ int core_id = 0;
+ int i;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ parse_cluster(c, depth + 1);
+ leaf = false;
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (depth == 0)
+ pr_err("%s: cpu-map children should be clusters\n",
+ c->full_name);
+
+ if (leaf)
+ parse_core(c, cluster_id, core_id++);
+ else
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+}
+
+static void __init parse_dt_topology(void)
+{
+ struct device_node *cn;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return;
+ }
+
+ /*
+ * When topology is provided cpu-map is essentially a root
+ * cluster with restricted subnodes.
+ */
+ cn = of_find_node_by_name(cn, "cpu-map");
+ if (!cn)
+ return;
+ parse_cluster(cn, 0);
+}
+
+#else
+static inline void parse_dt_topology(void) {}
+#endif
+
/*
* cpu topology table
*/
@@ -86,4 +226,6 @@ void __init init_cpu_topology(void)
cpumask_clear(&cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
}
+
+ parse_dt_topology();
}
--
1.9.0.rc3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-02-25 4:25 [PATCH 0/4] arm64: topology: CPU topology support Mark Brown
@ 2014-02-25 4:25 ` Mark Brown
0 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2014-02-25 4:25 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 142 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 142 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index c1e44d5..cda9a48 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -17,10 +17,150 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <asm/topology.h>
+#ifdef CONFIG_OF
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node)
+ return -1;
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+ return -1;
+}
+
+static void __init parse_core(struct device_node *core, int cluster_id,
+ int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i = 0;
+ int cpu;
+ struct device_node *t;
+
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ }
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return;
+ }
+
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ }
+}
+
+static void __init parse_cluster(struct device_node *cluster, int depth)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ static int __initdata cluster_id;
+ int core_id = 0;
+ int i;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ parse_cluster(c, depth + 1);
+ leaf = false;
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (depth == 0)
+ pr_err("%s: cpu-map children should be clusters\n",
+ c->full_name);
+
+ if (leaf)
+ parse_core(c, cluster_id, core_id++);
+ else
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+}
+
+static void __init parse_dt_topology(void)
+{
+ struct device_node *cn;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return;
+ }
+
+ /*
+ * When topology is provided cpu-map is essentially a root
+ * cluster with restricted subnodes.
+ */
+ cn = of_find_node_by_name(cn, "cpu-map");
+ if (!cn)
+ return;
+ parse_cluster(cn, 0);
+}
+
+#else
+static inline void parse_dt_topology(void) {}
+#endif
+
/*
* cpu topology table
*/
@@ -87,6 +227,8 @@ void __init init_cpu_topology(void)
cpumask_clear(&cpu_topo->thread_sibling);
}
+ parse_dt_topology();
+
/*
* Assign all remaining CPUs to a cluster so the scheduler
* doesn't get confused.
--
1.9.0.rc3
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-02-26 0:48 [PATCH 0/4] arm64: Topology Mark Brown
@ 2014-02-26 0:48 ` Mark Brown
0 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2014-02-26 0:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 142 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 142 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 0d94918..13ef616 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -17,10 +17,150 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <asm/topology.h>
+#ifdef CONFIG_OF
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node)
+ return -1;
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+ return -1;
+}
+
+static void __init parse_core(struct device_node *core, int cluster_id,
+ int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i = 0;
+ int cpu;
+ struct device_node *t;
+
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ }
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return;
+ }
+
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ }
+}
+
+static void __init parse_cluster(struct device_node *cluster, int depth)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ static int __initdata cluster_id;
+ int core_id = 0;
+ int i;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ parse_cluster(c, depth + 1);
+ leaf = false;
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (depth == 0)
+ pr_err("%s: cpu-map children should be clusters\n",
+ c->full_name);
+
+ if (leaf)
+ parse_core(c, cluster_id, core_id++);
+ else
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+}
+
+static void __init parse_dt_topology(void)
+{
+ struct device_node *cn;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return;
+ }
+
+ /*
+ * When topology is provided cpu-map is essentially a root
+ * cluster with restricted subnodes.
+ */
+ cn = of_find_node_by_name(cn, "cpu-map");
+ if (!cn)
+ return;
+ parse_cluster(cn, 0);
+}
+
+#else
+static inline void parse_dt_topology(void) {}
+#endif
+
/*
* cpu topology table
*/
@@ -92,4 +232,6 @@ void __init init_cpu_topology(void)
cpumask_clear(&cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
}
+
+ parse_dt_topology();
}
--
1.9.0
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 1/4] arm64: topology: Initialise default topology state immediately
@ 2014-03-21 17:27 Mark Brown
2014-03-21 17:27 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
` (2 more replies)
0 siblings, 3 replies; 28+ messages in thread
From: Mark Brown @ 2014-03-21 17:27 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
As a legacy of the way 32 bit ARM did things the topology code uses a null
topology map by default and then overwrites it by mapping cores with no
information to a cluster by themselves later. In order to make it simpler
to reset things as part of recovering from parse failures in firmware
information directly set this configuration on init. A core will always be
its own sibling so there should be no risk of confusion with firmware
provided information.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 3e06b0be4ec8..ff662b23af5f 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -43,9 +43,6 @@ static void update_siblings_masks(unsigned int cpuid)
* reset it to default behaviour
*/
pr_debug("CPU%u: No topology information configured\n", cpuid);
- cpuid_topo->core_id = 0;
- cpumask_set_cpu(cpuid, &cpuid_topo->core_sibling);
- cpumask_set_cpu(cpuid, &cpuid_topo->thread_sibling);
return;
}
@@ -87,9 +84,12 @@ void __init init_cpu_topology(void)
struct cpu_topology *cpu_topo = &cpu_topology[cpu];
cpu_topo->thread_id = -1;
- cpu_topo->core_id = -1;
+ cpu_topo->core_id = 0;
cpu_topo->cluster_id = -1;
+
cpumask_clear(&cpu_topo->core_sibling);
+ cpumask_set_cpu(cpu, &cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
+ cpumask_set_cpu(cpu, &cpu_topo->thread_sibling);
}
}
--
1.9.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-03-21 17:27 [PATCH 1/4] arm64: topology: Initialise default topology state immediately Mark Brown
@ 2014-03-21 17:27 ` Mark Brown
2014-03-24 15:36 ` Lorenzo Pieralisi
2014-03-21 17:28 ` [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
2014-03-21 17:28 ` [PATCH 4/4] arm64: topology: Provide relative power numbers for cores Mark Brown
2 siblings, 1 reply; 28+ messages in thread
From: Mark Brown @ 2014-03-21 17:27 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
- Discard the DT data if any CPUs are omitted from the topology.
- Call of_node_put() where required.
arch/arm64/kernel/topology.c | 192 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 186 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index ff662b23af5f..d0cb687fb7f5 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -17,10 +17,183 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <asm/topology.h>
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node)
+ return -1;
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == cpu_node)
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+ return -1;
+}
+
+static int __init parse_core(struct device_node *core, int cluster_id,
+ int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i = 0;
+ int cpu;
+ struct device_node *t;
+
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ return -EINVAL;
+ }
+ of_node_put(t);
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return -EINVAL;
+ }
+
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __init parse_cluster(struct device_node *cluster, int depth)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ static int cluster_id __initdata;
+ int core_id = 0;
+ int i, ret;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ ret = parse_cluster(c, depth + 1);
+ if (ret != 0)
+ return ret;
+ leaf = false;
+ of_node_put(c);
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (depth == 0)
+ pr_err("%s: cpu-map children should be clusters\n",
+ c->full_name);
+
+ if (leaf) {
+ ret = parse_core(c, cluster_id, core_id++);
+ if (ret != 0)
+ return ret;
+ } else {
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ return -EINVAL;
+ }
+ of_node_put(c);
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+
+ return 0;
+}
+
+static int __init parse_dt_topology(void)
+{
+ struct device_node *cn, *map;
+ int ret = 0;
+ int cpu;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return 0;
+ }
+
+ /*
+ * When topology is provided cpu-map is essentially a root
+ * cluster with restricted subnodes.
+ */
+ map = of_get_child_by_name(cn, "cpu-map");
+ if (!map)
+ goto out;
+
+ ret = parse_cluster(map, 0);
+ if (ret != 0)
+ goto out;
+
+ of_node_put(map);
+
+ /*
+ * Check that all cores are in the topology; the SMP code will
+ * only mark cores described in the DT as possible.
+ */
+ for_each_possible_cpu(cpu) {
+ if (cpu_topology[cpu].cluster_id == -1) {
+ pr_err("CPU%d: No topology information specified\n",
+ cpu);
+ ret = -EINVAL;
+ }
+ }
+
+out:
+ of_node_put(cn);
+ return ret;
+}
+
/*
* cpu topology table
*/
@@ -71,15 +244,10 @@ void store_cpu_topology(unsigned int cpuid)
update_siblings_masks(cpuid);
}
-/*
- * init_cpu_topology is called at boot when only one cpu is running
- * which prevent simultaneous write access to cpu_topology array
- */
-void __init init_cpu_topology(void)
+static void __init reset_cpu_topology(void)
{
unsigned int cpu;
- /* init core mask and power*/
for_each_possible_cpu(cpu) {
struct cpu_topology *cpu_topo = &cpu_topology[cpu];
@@ -93,3 +261,15 @@ void __init init_cpu_topology(void)
cpumask_set_cpu(cpu, &cpu_topo->thread_sibling);
}
}
+
+void __init init_cpu_topology(void)
+{
+ reset_cpu_topology();
+
+ /*
+ * Discard anything that was parsed if we hit an error so we
+ * don't use partial information.
+ */
+ if (parse_dt_topology())
+ reset_cpu_topology();
+}
--
1.9.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores
2014-03-21 17:27 [PATCH 1/4] arm64: topology: Initialise default topology state immediately Mark Brown
2014-03-21 17:27 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
@ 2014-03-21 17:28 ` Mark Brown
2014-03-21 17:28 ` [PATCH 4/4] arm64: topology: Provide relative power numbers for cores Mark Brown
2 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2014-03-21 17:28 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
In heterogeneous systems like big.LITTLE systems the scheduler will be
able to make better use of the available cores if we provide power numbers
to it indicating their relative performance. Do this by parsing the CPU
nodes in the DT.
This code currently has no effect as no information on the relative
performance of the cores is provided.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
- Split into a separate function from the rest of the DT parse since
the CPU performance information isn't directly part of the topology
binding.
arch/arm64/kernel/topology.c | 153 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 153 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index d0cb687fb7f5..516dd2bed7d6 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -19,9 +19,33 @@
#include <linux/nodemask.h>
#include <linux/of.h>
#include <linux/sched.h>
+#include <linux/slab.h>
#include <asm/topology.h>
+/*
+ * cpu power table
+ * This per cpu data structure describes the relative capacity of each core.
+ * On a heteregenous system, cores don't have the same computation capacity
+ * and we reflect that difference in the cpu_power field so the scheduler can
+ * take this difference into account during load balance. A per cpu structure
+ * is preferred because each CPU updates its own cpu_power field during the
+ * load balance except for idle cores. One idle core is selected to run the
+ * rebalance_domains for all idle cores and the cpu_power can be updated
+ * during this sequence.
+ */
+static DEFINE_PER_CPU(unsigned long, cpu_scale);
+
+unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu)
+{
+ return per_cpu(cpu_scale, cpu);
+}
+
+static void set_power_scale(unsigned int cpu, unsigned long power)
+{
+ per_cpu(cpu_scale, cpu) = power;
+}
+
static int __init get_cpu_for_node(struct device_node *node)
{
struct device_node *cpu_node;
@@ -151,6 +175,38 @@ static int __init parse_cluster(struct device_node *cluster, int depth)
return 0;
}
+struct cpu_efficiency {
+ const char *compatible;
+ unsigned long efficiency;
+};
+
+/*
+ * Table of relative efficiency of each processors
+ * The efficiency value must fit in 20bit and the final
+ * cpu_scale value must be in the range
+ * 0 < cpu_scale < 3*SCHED_POWER_SCALE/2
+ * in order to return@most 1 when DIV_ROUND_CLOSEST
+ * is used to compute the capacity of a CPU.
+ * Processors that are not defined in the table,
+ * use the default SCHED_POWER_SCALE value for cpu_scale.
+ */
+static const struct cpu_efficiency table_efficiency[] = {
+ { NULL, },
+};
+
+static unsigned long *__cpu_capacity;
+#define cpu_capacity(cpu) __cpu_capacity[cpu]
+
+static unsigned long middle_capacity = 1;
+
+/*
+ * Iterate all CPUs' descriptor in DT and compute the efficiency
+ * (as per table_efficiency). Also calculate a middle efficiency
+ * as close as possible to (max{eff_i} - min{eff_i}) / 2
+ * This is later used to scale the cpu_power field such that an
+ * 'average' CPU is of middle power. Also see the comments near
+ * table_efficiency[] and update_cpu_power().
+ */
static int __init parse_dt_topology(void)
{
struct device_node *cn, *map;
@@ -194,6 +250,91 @@ out:
return ret;
}
+static void __init parse_dt_cpu_power(void)
+{
+ const struct cpu_efficiency *cpu_eff;
+ struct device_node *cn;
+ unsigned long min_capacity = ULONG_MAX;
+ unsigned long max_capacity = 0;
+ unsigned long capacity = 0;
+ int cpu;
+
+ __cpu_capacity = kcalloc(nr_cpu_ids, sizeof(*__cpu_capacity),
+ GFP_NOWAIT);
+
+ for_each_possible_cpu(cpu) {
+ const u32 *rate;
+ int len;
+
+ /* Too early to use cpu->of_node */
+ cn = of_get_cpu_node(cpu, NULL);
+ if (!cn) {
+ pr_err("Missing device node for CPU %d\n", cpu);
+ continue;
+ }
+
+ for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++)
+ if (of_device_is_compatible(cn, cpu_eff->compatible))
+ break;
+
+ if (cpu_eff->compatible == NULL) {
+ pr_warn("%s: Unknown CPU type\n", cn->full_name);
+ continue;
+ }
+
+ rate = of_get_property(cn, "clock-frequency", &len);
+ if (!rate || len != 4) {
+ pr_err("%s: Missing clock-frequency property\n",
+ cn->full_name);
+ continue;
+ }
+
+ capacity = ((be32_to_cpup(rate)) >> 20) * cpu_eff->efficiency;
+
+ /* Save min capacity of the system */
+ if (capacity < min_capacity)
+ min_capacity = capacity;
+
+ /* Save max capacity of the system */
+ if (capacity > max_capacity)
+ max_capacity = capacity;
+
+ cpu_capacity(cpu) = capacity;
+ }
+
+ /* If min and max capacities are equal we bypass the update of the
+ * cpu_scale because all CPUs have the same capacity. Otherwise, we
+ * compute a middle_capacity factor that will ensure that the capacity
+ * of an 'average' CPU of the system will be as close as possible to
+ * SCHED_POWER_SCALE, which is the default value, but with the
+ * constraint explained near table_efficiency[].
+ */
+ if (min_capacity == max_capacity)
+ return;
+ else if (4 * max_capacity < (3 * (max_capacity + min_capacity)))
+ middle_capacity = (min_capacity + max_capacity)
+ >> (SCHED_POWER_SHIFT+1);
+ else
+ middle_capacity = ((max_capacity / 3)
+ >> (SCHED_POWER_SHIFT-1)) + 1;
+}
+
+/*
+ * Look for a customed capacity of a CPU in the cpu_topo_data table during the
+ * boot. The update of all CPUs is in O(n^2) for heteregeneous system but the
+ * function returns directly for SMP system.
+ */
+static void update_cpu_power(unsigned int cpu)
+{
+ if (!cpu_capacity(cpu))
+ return;
+
+ set_power_scale(cpu, cpu_capacity(cpu) / middle_capacity);
+
+ pr_info("CPU%u: update cpu_power %lu\n",
+ cpu, arch_scale_freq_power(NULL, cpu));
+}
+
/*
* cpu topology table
*/
@@ -242,6 +383,7 @@ static void update_siblings_masks(unsigned int cpuid)
void store_cpu_topology(unsigned int cpuid)
{
update_siblings_masks(cpuid);
+ update_cpu_power(cpuid);
}
static void __init reset_cpu_topology(void)
@@ -262,6 +404,14 @@ static void __init reset_cpu_topology(void)
}
}
+static void __init reset_cpu_power(void)
+{
+ unsigned int cpu;
+
+ for_each_possible_cpu(cpu)
+ set_power_scale(cpu, SCHED_POWER_SCALE);
+}
+
void __init init_cpu_topology(void)
{
reset_cpu_topology();
@@ -272,4 +422,7 @@ void __init init_cpu_topology(void)
*/
if (parse_dt_topology())
reset_cpu_topology();
+
+ reset_cpu_power();
+ parse_dt_cpu_power();
}
--
1.9.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 4/4] arm64: topology: Provide relative power numbers for cores
2014-03-21 17:27 [PATCH 1/4] arm64: topology: Initialise default topology state immediately Mark Brown
2014-03-21 17:27 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2014-03-21 17:28 ` [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
@ 2014-03-21 17:28 ` Mark Brown
2 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2014-03-21 17:28 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Provide performance numbers to the scheduler to help it fill the cores in
the system on big.LITTLE systems. With the current scheduler this may
perform poorly for applications that try to do OpenMP style work over all
cores but should help for more common workloads. The current 32 bit ARM
implementation provides a similar estimate so this helps ensure that
work to improve big.LITTLE systems on ARMv7 systems performs similarly
on ARMv8 systems.
The power numbers are the same as for ARMv7 since it seems that the
expected differential between the big and little cores is very similar on
both ARMv7 and ARMv8. In both ARMv7 and ARMv8 cases the numbers were
based on the published DMIPS numbers.
These numbers are just an initial and basic approximation for use with
the current scheduler, it is likely that both experience with silicon
and ongoing work on improving the scheduler will lead to further tuning
or will tune automatically at runtime and so make the specific choice of
numbers here less critical.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 516dd2bed7d6..870de6a9a885 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -191,6 +191,8 @@ struct cpu_efficiency {
* use the default SCHED_POWER_SCALE value for cpu_scale.
*/
static const struct cpu_efficiency table_efficiency[] = {
+ { "arm,cortex-a57", 3891 },
+ { "arm,cortex-a53", 2048 },
{ NULL, },
};
--
1.9.1
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-03-21 17:27 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
@ 2014-03-24 15:36 ` Lorenzo Pieralisi
2014-03-24 15:45 ` Mark Brown
0 siblings, 1 reply; 28+ messages in thread
From: Lorenzo Pieralisi @ 2014-03-24 15:36 UTC (permalink / raw)
To: linux-arm-kernel
Hi Mark,
On Fri, Mar 21, 2014 at 05:27:59PM +0000, Mark Brown wrote:
> From: Mark Brown <broonie@linaro.org>
>
> Add support for parsing the explicit topology bindings to discover the
> topology of the system.
>
> Since it is not currently clear how to map multi-level clusters for the
> scheduler all leaf clusters are presented to the scheduler at the same
> level. This should be enough to provide good support for current systems.
>
> Signed-off-by: Mark Brown <broonie@linaro.org>
Please apply the patches attached, that fix some stale comments and
add of_node_put in paths that could otherwise be broken by return
statements.
Patch "arm64: kernel: topology: removed stale comment" applies to patch
1 of this series.
Patch "arm64: kernel: topology updates" applies to patch 2 of this
series.
Both should be squashed into your original patches. Please have a final
look and test them, they work for me.
Having said that, on the updated patch 1 and 2 of this series:
Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> ---
>
> - Discard the DT data if any CPUs are omitted from the topology.
> - Call of_node_put() where required.
>
> arch/arm64/kernel/topology.c | 192 +++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 186 insertions(+), 6 deletions(-)
>
> diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
> index ff662b23af5f..d0cb687fb7f5 100644
> --- a/arch/arm64/kernel/topology.c
> +++ b/arch/arm64/kernel/topology.c
> @@ -17,10 +17,183 @@
> #include <linux/percpu.h>
> #include <linux/node.h>
> #include <linux/nodemask.h>
> +#include <linux/of.h>
> #include <linux/sched.h>
>
> #include <asm/topology.h>
>
> +static int __init get_cpu_for_node(struct device_node *node)
> +{
> + struct device_node *cpu_node;
> + int cpu;
> +
> + cpu_node = of_parse_phandle(node, "cpu", 0);
> + if (!cpu_node)
> + return -1;
> +
> + for_each_possible_cpu(cpu) {
> + if (of_get_cpu_node(cpu, NULL) == cpu_node)
> + return cpu;
> + }
> +
> + pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
> + return -1;
> +}
> +
> +static int __init parse_core(struct device_node *core, int cluster_id,
> + int core_id)
> +{
> + char name[10];
> + bool leaf = true;
> + int i = 0;
> + int cpu;
> + struct device_node *t;
> +
> + do {
> + snprintf(name, sizeof(name), "thread%d", i);
> + t = of_get_child_by_name(core, name);
> + if (t) {
> + leaf = false;
> + cpu = get_cpu_for_node(t);
> + if (cpu >= 0) {
> + cpu_topology[cpu].cluster_id = cluster_id;
> + cpu_topology[cpu].core_id = core_id;
> + cpu_topology[cpu].thread_id = i;
> + } else {
> + pr_err("%s: Can't get CPU for thread\n",
> + t->full_name);
> + return -EINVAL;
> + }
> + of_node_put(t);
> + }
> + i++;
> + } while (t);
> +
> + cpu = get_cpu_for_node(core);
> + if (cpu >= 0) {
> + if (!leaf) {
> + pr_err("%s: Core has both threads and CPU\n",
> + core->full_name);
> + return -EINVAL;
> + }
> +
> + cpu_topology[cpu].cluster_id = cluster_id;
> + cpu_topology[cpu].core_id = core_id;
> + } else if (leaf) {
> + pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int __init parse_cluster(struct device_node *cluster, int depth)
> +{
> + char name[10];
> + bool leaf = true;
> + bool has_cores = false;
> + struct device_node *c;
> + static int cluster_id __initdata;
> + int core_id = 0;
> + int i, ret;
> +
> + /*
> + * First check for child clusters; we currently ignore any
> + * information about the nesting of clusters and present the
> + * scheduler with a flat list of them.
> + */
> + i = 0;
> + do {
> + snprintf(name, sizeof(name), "cluster%d", i);
> + c = of_get_child_by_name(cluster, name);
> + if (c) {
> + ret = parse_cluster(c, depth + 1);
> + if (ret != 0)
> + return ret;
> + leaf = false;
> + of_node_put(c);
> + }
> + i++;
> + } while (c);
> +
> + /* Now check for cores */
> + i = 0;
> + do {
> + snprintf(name, sizeof(name), "core%d", i);
> + c = of_get_child_by_name(cluster, name);
> + if (c) {
> + has_cores = true;
> +
> + if (depth == 0)
> + pr_err("%s: cpu-map children should be clusters\n",
> + c->full_name);
> +
> + if (leaf) {
> + ret = parse_core(c, cluster_id, core_id++);
> + if (ret != 0)
> + return ret;
> + } else {
> + pr_err("%s: Non-leaf cluster with core %s\n",
> + cluster->full_name, name);
> + return -EINVAL;
> + }
> + of_node_put(c);
> + }
> + i++;
> + } while (c);
> +
> + if (leaf && !has_cores)
> + pr_warn("%s: empty cluster\n", cluster->full_name);
> +
> + if (leaf)
> + cluster_id++;
> +
> + return 0;
> +}
> +
> +static int __init parse_dt_topology(void)
> +{
> + struct device_node *cn, *map;
> + int ret = 0;
> + int cpu;
> +
> + cn = of_find_node_by_path("/cpus");
> + if (!cn) {
> + pr_err("No CPU information found in DT\n");
> + return 0;
> + }
> +
> + /*
> + * When topology is provided cpu-map is essentially a root
> + * cluster with restricted subnodes.
> + */
> + map = of_get_child_by_name(cn, "cpu-map");
> + if (!map)
> + goto out;
> +
> + ret = parse_cluster(map, 0);
> + if (ret != 0)
> + goto out;
> +
> + of_node_put(map);
> +
> + /*
> + * Check that all cores are in the topology; the SMP code will
> + * only mark cores described in the DT as possible.
> + */
> + for_each_possible_cpu(cpu) {
> + if (cpu_topology[cpu].cluster_id == -1) {
> + pr_err("CPU%d: No topology information specified\n",
> + cpu);
> + ret = -EINVAL;
> + }
> + }
> +
> +out:
> + of_node_put(cn);
> + return ret;
> +}
> +
> /*
> * cpu topology table
> */
> @@ -71,15 +244,10 @@ void store_cpu_topology(unsigned int cpuid)
> update_siblings_masks(cpuid);
> }
>
> -/*
> - * init_cpu_topology is called at boot when only one cpu is running
> - * which prevent simultaneous write access to cpu_topology array
> - */
> -void __init init_cpu_topology(void)
> +static void __init reset_cpu_topology(void)
> {
> unsigned int cpu;
>
> - /* init core mask and power*/
> for_each_possible_cpu(cpu) {
> struct cpu_topology *cpu_topo = &cpu_topology[cpu];
>
> @@ -93,3 +261,15 @@ void __init init_cpu_topology(void)
> cpumask_set_cpu(cpu, &cpu_topo->thread_sibling);
> }
> }
> +
> +void __init init_cpu_topology(void)
> +{
> + reset_cpu_topology();
> +
> + /*
> + * Discard anything that was parsed if we hit an error so we
> + * don't use partial information.
> + */
> + if (parse_dt_topology())
> + reset_cpu_topology();
> +}
> --
> 1.9.1
>
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-arm64-kernel-topology-removed-stale-comment.patch
Type: text/x-diff
Size: 936 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140324/6afd3bc2/attachment-0002.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0002-arm64-kernel-topology-updates.patch
Type: text/x-diff
Size: 2896 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140324/6afd3bc2/attachment-0003.bin>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-03-24 15:36 ` Lorenzo Pieralisi
@ 2014-03-24 15:45 ` Mark Brown
2014-03-24 16:02 ` Lorenzo Pieralisi
0 siblings, 1 reply; 28+ messages in thread
From: Mark Brown @ 2014-03-24 15:45 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Mar 24, 2014 at 03:36:11PM +0000, Lorenzo Pieralisi wrote:
> - for_each_possible_cpu(cpu) {
> - if (of_get_cpu_node(cpu, NULL) == cpu_node)
> + for_each_possible_cpu(cpu)
> + if (of_get_cpu_node(cpu, NULL) == cpu_node) {
> + of_node_put(cpu_node);
> return cpu;
> - }
> + }
of_get_cpu_node() doesn't visibly take a reference and isn't documented
as doing such?
> pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
> +
> + of_node_put(cpu_node);
I don't understand this one - the caller passed in cpu_node, we didn't
acquire a reference here so I'd expect the caller to be doing any frees
that are needed.
> @@ -55,6 +58,7 @@ static int __init parse_core(struct device_node *core, int cluster_id,
> if (t) {
> leaf = false;
> cpu = get_cpu_for_node(t);
> + of_node_put(t);
> if (cpu >= 0) {
> cpu_topology[cpu].cluster_id = cluster_id;
> cpu_topology[cpu].core_id = core_id;
> @@ -64,7 +68,6 @@ static int __init parse_core(struct device_node *core, int cluster_id,
> t->full_name);
> return -EINVAL;
> }
> - of_node_put(t);
This causes us to reference the just released t when displaying the
error message in the error case (t->full_name in the quoted context).
You're right that the reference is leaked in the error path but the free
needs to be inside the error handling case.
Of course all this refcounting does absolutely nothing for FDT but hey
ho.
> @@ -107,11 +110,11 @@ static int __init parse_cluster(struct device_node *cluster, int depth)
> snprintf(name, sizeof(name), "cluster%d", i);
> c = of_get_child_by_name(cluster, name);
> if (c) {
> + leaf = false;
> ret = parse_cluster(c, depth + 1);
> + of_node_put(c);
> if (ret != 0)
> return ret;
> - leaf = false;
> - of_node_put(c);
I don't understand why you moved the assignment of leaf?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140324/f7754c63/attachment-0001.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-03-24 15:45 ` Mark Brown
@ 2014-03-24 16:02 ` Lorenzo Pieralisi
2014-03-24 16:27 ` Mark Brown
0 siblings, 1 reply; 28+ messages in thread
From: Lorenzo Pieralisi @ 2014-03-24 16:02 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Mar 24, 2014 at 03:45:50PM +0000, Mark Brown wrote:
> On Mon, Mar 24, 2014 at 03:36:11PM +0000, Lorenzo Pieralisi wrote:
>
> > - for_each_possible_cpu(cpu) {
> > - if (of_get_cpu_node(cpu, NULL) == cpu_node)
> > + for_each_possible_cpu(cpu)
> > + if (of_get_cpu_node(cpu, NULL) == cpu_node) {
> > + of_node_put(cpu_node);
> > return cpu;
> > - }
> > + }
>
> of_get_cpu_node() doesn't visibly take a reference and isn't documented
> as doing such?
of_parse_phandle() does, am I right ?
> > pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
> > +
> > + of_node_put(cpu_node);
>
> I don't understand this one - the caller passed in cpu_node, we didn't
> acquire a reference here so I'd expect the caller to be doing any frees
> that are needed.
cpu_node has to be put, since it is obtained through of_parse_phandle().
> > @@ -55,6 +58,7 @@ static int __init parse_core(struct device_node *core, int cluster_id,
> > if (t) {
> > leaf = false;
> > cpu = get_cpu_for_node(t);
> > + of_node_put(t);
> > if (cpu >= 0) {
> > cpu_topology[cpu].cluster_id = cluster_id;
> > cpu_topology[cpu].core_id = core_id;
> > @@ -64,7 +68,6 @@ static int __init parse_core(struct device_node *core, int cluster_id,
> > t->full_name);
> > return -EINVAL;
> > }
> > - of_node_put(t);
>
> This causes us to reference the just released t when displaying the
> error message in the error case (t->full_name in the quoted context).
> You're right that the reference is leaked in the error path but the free
> needs to be inside the error handling case.
Gah, right. I will let you fix it please as you deem fit.
> Of course all this refcounting does absolutely nothing for FDT but hey
> ho.
We comply with the interface, so that at least from an API standpoint
code is complete. I understand your point.
> > @@ -107,11 +110,11 @@ static int __init parse_cluster(struct device_node *cluster, int depth)
> > snprintf(name, sizeof(name), "cluster%d", i);
> > c = of_get_child_by_name(cluster, name);
> > if (c) {
> > + leaf = false;
> > ret = parse_cluster(c, depth + 1);
> > + of_node_put(c);
> > if (ret != 0)
> > return ret;
> > - leaf = false;
> > - of_node_put(c);
>
> I don't understand why you moved the assignment of leaf?
It makes it tidier, but that's just cosmetic and my opinion (but thanks
for having a look since it is not a significant change). Again, squash it
in as you deem fit.
Thanks !
Lorenzo
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-03-24 16:02 ` Lorenzo Pieralisi
@ 2014-03-24 16:27 ` Mark Brown
0 siblings, 0 replies; 28+ messages in thread
From: Mark Brown @ 2014-03-24 16:27 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, Mar 24, 2014 at 04:02:36PM +0000, Lorenzo Pieralisi wrote:
> On Mon, Mar 24, 2014 at 03:45:50PM +0000, Mark Brown wrote:
> > of_get_cpu_node() doesn't visibly take a reference and isn't documented
> > as doing such?
> of_parse_phandle() does, am I right ?
Oh, yes. I was looking at the wrong thing.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140324/aa18d266/attachment.sig>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-04-22 20:21 [PATCH 1/4] arm64: topology: Initialise default topology state immediately Mark Brown
@ 2014-04-22 20:21 ` Mark Brown
2014-04-24 14:48 ` Lorenzo Pieralisi
0 siblings, 1 reply; 28+ messages in thread
From: Mark Brown @ 2014-04-22 20:21 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Add support for parsing the explicit topology bindings to discover the
topology of the system.
Since it is not currently clear how to map multi-level clusters for the
scheduler all leaf clusters are presented to the scheduler at the same
level. This should be enough to provide good support for current systems.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/kernel/topology.c | 203 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 195 insertions(+), 8 deletions(-)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index ff662b23af5f..a326f5b37546 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -17,10 +17,191 @@
#include <linux/percpu.h>
#include <linux/node.h>
#include <linux/nodemask.h>
+#include <linux/of.h>
#include <linux/sched.h>
#include <asm/topology.h>
+static int __init get_cpu_for_node(struct device_node *node)
+{
+ struct device_node *cpu_node;
+ int cpu;
+
+ cpu_node = of_parse_phandle(node, "cpu", 0);
+ if (!cpu_node)
+ return -1;
+
+ for_each_possible_cpu(cpu)
+ if (of_get_cpu_node(cpu, NULL) == cpu_node) {
+ of_node_put(cpu_node);
+ return cpu;
+ }
+
+ pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
+
+ of_node_put(cpu_node);
+ return -1;
+}
+
+static int __init parse_core(struct device_node *core, int cluster_id,
+ int core_id)
+{
+ char name[10];
+ bool leaf = true;
+ int i = 0;
+ int cpu;
+ struct device_node *t;
+
+ do {
+ snprintf(name, sizeof(name), "thread%d", i);
+ t = of_get_child_by_name(core, name);
+ if (t) {
+ leaf = false;
+ cpu = get_cpu_for_node(t);
+ if (cpu >= 0) {
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ cpu_topology[cpu].thread_id = i;
+ } else {
+ pr_err("%s: Can't get CPU for thread\n",
+ t->full_name);
+ of_node_put(t);
+ return -EINVAL;
+ }
+ of_node_put(t);
+ }
+ i++;
+ } while (t);
+
+ cpu = get_cpu_for_node(core);
+ if (cpu >= 0) {
+ if (!leaf) {
+ pr_err("%s: Core has both threads and CPU\n",
+ core->full_name);
+ return -EINVAL;
+ }
+
+ cpu_topology[cpu].cluster_id = cluster_id;
+ cpu_topology[cpu].core_id = core_id;
+ } else if (leaf) {
+ pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __init parse_cluster(struct device_node *cluster, int depth)
+{
+ char name[10];
+ bool leaf = true;
+ bool has_cores = false;
+ struct device_node *c;
+ static int cluster_id __initdata;
+ int core_id = 0;
+ int i, ret;
+
+ /*
+ * First check for child clusters; we currently ignore any
+ * information about the nesting of clusters and present the
+ * scheduler with a flat list of them.
+ */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "cluster%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ leaf = false;
+ ret = parse_cluster(c, depth + 1);
+ of_node_put(c);
+ if (ret != 0)
+ return ret;
+ }
+ i++;
+ } while (c);
+
+ /* Now check for cores */
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "core%d", i);
+ c = of_get_child_by_name(cluster, name);
+ if (c) {
+ has_cores = true;
+
+ if (depth == 0) {
+ pr_err("%s: cpu-map children should be clusters\n",
+ c->full_name);
+ of_node_put(c);
+ return -EINVAL;
+ }
+
+ if (leaf) {
+ ret = parse_core(c, cluster_id, core_id++);
+ } else {
+ pr_err("%s: Non-leaf cluster with core %s\n",
+ cluster->full_name, name);
+ ret = -EINVAL;
+ }
+
+ of_node_put(c);
+ if (ret != 0)
+ return ret;
+ }
+ i++;
+ } while (c);
+
+ if (leaf && !has_cores)
+ pr_warn("%s: empty cluster\n", cluster->full_name);
+
+ if (leaf)
+ cluster_id++;
+
+ return 0;
+}
+
+static int __init parse_dt_topology(void)
+{
+ struct device_node *cn, *map;
+ int ret = 0;
+ int cpu;
+
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return 0;
+ }
+
+ /*
+ * When topology is provided cpu-map is essentially a root
+ * cluster with restricted subnodes.
+ */
+ map = of_get_child_by_name(cn, "cpu-map");
+ if (!map)
+ goto out;
+
+ ret = parse_cluster(map, 0);
+ if (ret != 0)
+ goto out_map;
+
+ /*
+ * Check that all cores are in the topology; the SMP code will
+ * only mark cores described in the DT as possible.
+ */
+ for_each_possible_cpu(cpu) {
+ if (cpu_topology[cpu].cluster_id == -1) {
+ pr_err("CPU%d: No topology information specified\n",
+ cpu);
+ ret = -EINVAL;
+ }
+ }
+
+out_map:
+ of_node_put(map);
+out:
+ of_node_put(cn);
+ return ret;
+}
+
/*
* cpu topology table
*/
@@ -39,8 +220,7 @@ static void update_siblings_masks(unsigned int cpuid)
if (cpuid_topo->cluster_id == -1) {
/*
- * DT does not contain topology information for this cpu
- * reset it to default behaviour
+ * DT does not contain topology information for this cpu.
*/
pr_debug("CPU%u: No topology information configured\n", cpuid);
return;
@@ -71,15 +251,10 @@ void store_cpu_topology(unsigned int cpuid)
update_siblings_masks(cpuid);
}
-/*
- * init_cpu_topology is called at boot when only one cpu is running
- * which prevent simultaneous write access to cpu_topology array
- */
-void __init init_cpu_topology(void)
+static void __init reset_cpu_topology(void)
{
unsigned int cpu;
- /* init core mask and power*/
for_each_possible_cpu(cpu) {
struct cpu_topology *cpu_topo = &cpu_topology[cpu];
@@ -93,3 +268,15 @@ void __init init_cpu_topology(void)
cpumask_set_cpu(cpu, &cpu_topo->thread_sibling);
}
}
+
+void __init init_cpu_topology(void)
+{
+ reset_cpu_topology();
+
+ /*
+ * Discard anything that was parsed if we hit an error so we
+ * don't use partial information.
+ */
+ if (parse_dt_topology())
+ reset_cpu_topology();
+}
--
1.9.2
^ permalink raw reply related [flat|nested] 28+ messages in thread
* [PATCH 2/4] arm64: topology: Add support for topology DT bindings
2014-04-22 20:21 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
@ 2014-04-24 14:48 ` Lorenzo Pieralisi
0 siblings, 0 replies; 28+ messages in thread
From: Lorenzo Pieralisi @ 2014-04-24 14:48 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Apr 22, 2014 at 09:21:16PM +0100, Mark Brown wrote:
> From: Mark Brown <broonie@linaro.org>
>
> Add support for parsing the explicit topology bindings to discover the
> topology of the system.
>
> Since it is not currently clear how to map multi-level clusters for the
> scheduler all leaf clusters are presented to the scheduler at the same
> level. This should be enough to provide good support for current systems.
>
> Signed-off-by: Mark Brown <broonie@linaro.org>
> ---
> arch/arm64/kernel/topology.c | 203 +++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 195 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
> index ff662b23af5f..a326f5b37546 100644
> --- a/arch/arm64/kernel/topology.c
> +++ b/arch/arm64/kernel/topology.c
> @@ -17,10 +17,191 @@
> #include <linux/percpu.h>
> #include <linux/node.h>
> #include <linux/nodemask.h>
> +#include <linux/of.h>
> #include <linux/sched.h>
>
> #include <asm/topology.h>
>
> +static int __init get_cpu_for_node(struct device_node *node)
> +{
> + struct device_node *cpu_node;
> + int cpu;
> +
> + cpu_node = of_parse_phandle(node, "cpu", 0);
> + if (!cpu_node)
> + return -1;
> +
> + for_each_possible_cpu(cpu)
> + if (of_get_cpu_node(cpu, NULL) == cpu_node) {
> + of_node_put(cpu_node);
> + return cpu;
> + }
> +
> + pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name);
> +
> + of_node_put(cpu_node);
> + return -1;
> +}
> +
> +static int __init parse_core(struct device_node *core, int cluster_id,
> + int core_id)
> +{
> + char name[10];
> + bool leaf = true;
> + int i = 0;
> + int cpu;
> + struct device_node *t;
> +
> + do {
> + snprintf(name, sizeof(name), "thread%d", i);
> + t = of_get_child_by_name(core, name);
> + if (t) {
> + leaf = false;
> + cpu = get_cpu_for_node(t);
> + if (cpu >= 0) {
> + cpu_topology[cpu].cluster_id = cluster_id;
> + cpu_topology[cpu].core_id = core_id;
> + cpu_topology[cpu].thread_id = i;
> + } else {
> + pr_err("%s: Can't get CPU for thread\n",
> + t->full_name);
> + of_node_put(t);
> + return -EINVAL;
> + }
> + of_node_put(t);
> + }
> + i++;
> + } while (t);
> +
> + cpu = get_cpu_for_node(core);
> + if (cpu >= 0) {
> + if (!leaf) {
> + pr_err("%s: Core has both threads and CPU\n",
> + core->full_name);
> + return -EINVAL;
> + }
> +
> + cpu_topology[cpu].cluster_id = cluster_id;
> + cpu_topology[cpu].core_id = core_id;
> + } else if (leaf) {
> + pr_err("%s: Can't get CPU for leaf core\n", core->full_name);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int __init parse_cluster(struct device_node *cluster, int depth)
> +{
> + char name[10];
> + bool leaf = true;
> + bool has_cores = false;
> + struct device_node *c;
> + static int cluster_id __initdata;
> + int core_id = 0;
> + int i, ret;
> +
> + /*
> + * First check for child clusters; we currently ignore any
> + * information about the nesting of clusters and present the
> + * scheduler with a flat list of them.
> + */
> + i = 0;
> + do {
> + snprintf(name, sizeof(name), "cluster%d", i);
> + c = of_get_child_by_name(cluster, name);
> + if (c) {
> + leaf = false;
> + ret = parse_cluster(c, depth + 1);
> + of_node_put(c);
> + if (ret != 0)
> + return ret;
> + }
> + i++;
> + } while (c);
> +
> + /* Now check for cores */
> + i = 0;
> + do {
> + snprintf(name, sizeof(name), "core%d", i);
> + c = of_get_child_by_name(cluster, name);
> + if (c) {
> + has_cores = true;
> +
> + if (depth == 0) {
> + pr_err("%s: cpu-map children should be clusters\n",
> + c->full_name);
> + of_node_put(c);
> + return -EINVAL;
> + }
> +
> + if (leaf) {
> + ret = parse_core(c, cluster_id, core_id++);
> + } else {
> + pr_err("%s: Non-leaf cluster with core %s\n",
> + cluster->full_name, name);
> + ret = -EINVAL;
> + }
> +
> + of_node_put(c);
> + if (ret != 0)
> + return ret;
> + }
> + i++;
> + } while (c);
> +
> + if (leaf && !has_cores)
> + pr_warn("%s: empty cluster\n", cluster->full_name);
> +
> + if (leaf)
> + cluster_id++;
> +
> + return 0;
> +}
> +
> +static int __init parse_dt_topology(void)
> +{
> + struct device_node *cn, *map;
> + int ret = 0;
> + int cpu;
> +
> + cn = of_find_node_by_path("/cpus");
> + if (!cn) {
> + pr_err("No CPU information found in DT\n");
> + return 0;
> + }
> +
> + /*
> + * When topology is provided cpu-map is essentially a root
> + * cluster with restricted subnodes.
> + */
> + map = of_get_child_by_name(cn, "cpu-map");
> + if (!map)
> + goto out;
> +
> + ret = parse_cluster(map, 0);
> + if (ret != 0)
> + goto out_map;
> +
> + /*
> + * Check that all cores are in the topology; the SMP code will
> + * only mark cores described in the DT as possible.
> + */
> + for_each_possible_cpu(cpu) {
> + if (cpu_topology[cpu].cluster_id == -1) {
> + pr_err("CPU%d: No topology information specified\n",
> + cpu);
> + ret = -EINVAL;
> + }
> + }
> +
> +out_map:
> + of_node_put(map);
> +out:
> + of_node_put(cn);
> + return ret;
> +}
> +
> /*
> * cpu topology table
> */
> @@ -39,8 +220,7 @@ static void update_siblings_masks(unsigned int cpuid)
>
> if (cpuid_topo->cluster_id == -1) {
> /*
> - * DT does not contain topology information for this cpu
> - * reset it to default behaviour
> + * DT does not contain topology information for this cpu.
> */
> pr_debug("CPU%u: No topology information configured\n", cpuid);
> return;
> @@ -71,15 +251,10 @@ void store_cpu_topology(unsigned int cpuid)
> update_siblings_masks(cpuid);
> }
>
> -/*
> - * init_cpu_topology is called at boot when only one cpu is running
> - * which prevent simultaneous write access to cpu_topology array
> - */
> -void __init init_cpu_topology(void)
> +static void __init reset_cpu_topology(void)
> {
> unsigned int cpu;
>
> - /* init core mask and power*/
> for_each_possible_cpu(cpu) {
> struct cpu_topology *cpu_topo = &cpu_topology[cpu];
>
> @@ -93,3 +268,15 @@ void __init init_cpu_topology(void)
> cpumask_set_cpu(cpu, &cpu_topo->thread_sibling);
> }
> }
> +
> +void __init init_cpu_topology(void)
> +{
> + reset_cpu_topology();
> +
> + /*
> + * Discard anything that was parsed if we hit an error so we
> + * don't use partial information.
> + */
> + if (parse_dt_topology())
> + reset_cpu_topology();
> +}
I tested it and tried to clobber it a bit with some DT changes, it seems
fine.
Apart from an ultra-minor decoration change (attached):
Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-arm64-kernel-topology-minor-cosmetic-changes.patch
Type: text/x-diff
Size: 1008 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140424/75fec107/attachment.bin>
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2014-04-24 14:48 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-03-21 17:27 [PATCH 1/4] arm64: topology: Initialise default topology state immediately Mark Brown
2014-03-21 17:27 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2014-03-24 15:36 ` Lorenzo Pieralisi
2014-03-24 15:45 ` Mark Brown
2014-03-24 16:02 ` Lorenzo Pieralisi
2014-03-24 16:27 ` Mark Brown
2014-03-21 17:28 ` [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
2014-03-21 17:28 ` [PATCH 4/4] arm64: topology: Provide relative power numbers for cores Mark Brown
-- strict thread matches above, loose matches on Subject: below --
2014-04-22 20:21 [PATCH 1/4] arm64: topology: Initialise default topology state immediately Mark Brown
2014-04-22 20:21 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2014-04-24 14:48 ` Lorenzo Pieralisi
2014-02-26 0:48 [PATCH 0/4] arm64: Topology Mark Brown
2014-02-26 0:48 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2014-02-25 4:25 [PATCH 0/4] arm64: topology: CPU topology support Mark Brown
2014-02-25 4:25 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2014-02-11 22:06 [PATCH 1/4] arm64: topology: Implement basic CPU topology support Mark Brown
2014-02-11 22:06 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2014-02-10 13:02 [PATCH 1/4] arm64: topology: Implement basic CPU topology support Mark Brown
2014-02-10 13:02 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2014-01-15 11:38 [PATCH v12 0/4] arm64 topology Mark Brown
2014-01-15 11:38 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2014-01-12 19:20 [PATCH v11 0/4] ARMv8 cpu topology Mark Brown
2014-01-12 19:20 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2014-01-14 11:43 ` Lorenzo Pieralisi
2014-01-14 12:36 ` Mark Brown
2014-01-08 19:12 [PATCH 1/4] arm64: topology: Implement basic CPU topology support Mark Brown
2014-01-08 19:12 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2014-01-09 12:50 ` Lorenzo Pieralisi
2014-01-09 13:26 ` Mark Brown
2014-01-08 17:10 [PATCH 1/4] arm64: topology: Implement basic CPU topology support Mark Brown
2014-01-08 17:10 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2014-01-08 18:23 ` Lorenzo Pieralisi
2014-01-08 18:32 ` Mark Brown
2013-12-19 20:06 [PATCH 0/4] arm64 topology support Mark Brown
2013-12-19 20:06 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2013-12-16 16:49 [PATCH 1/4] arm64: topology: Implement basic CPU topology support Mark Brown
2013-12-16 16:49 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2013-12-17 17:40 ` Lorenzo Pieralisi
2013-12-17 19:19 ` Mark Brown
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).