* [PATCH 0/4] arm64 topology support
@ 2013-12-19 20:06 Mark Brown
2013-12-19 20:06 ` [PATCH 1/4] arm64: topology: Implement basic CPU " Mark Brown
` (5 more replies)
0 siblings, 6 replies; 42+ messages in thread
From: Mark Brown @ 2013-12-19 20:06 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
Another spin of the arm64 topology work - this should incorporate most
of the feedback from Lorenzo, there's a few things that were still under
discussion the main ones being:
- Should we have a smp_store_cpu_info(); like I say I like the errors
it generates for omitted cores and the reuse of the SMP enumeration
code (and cross-check with that I guess - make sure we don't get
confused about which CPUs are getting enabled).
- Should we update the binding to allow cores in the root cpu_map node
(since it's less effort in code and not a meaningful difference
semantically), warn if we find cores in the cpu_map node or actively
reject such DTs?
In both cases I don't much mind but I think what's there is reasonable
so I've left the code as-is pending further feedback. I also didn't
update the code to get more reuse of the iteration code, like I said I
did look at that when writing the code but couldn't find anything that
actually made things more pleasant but if someone has some ideas...
Everything else raised should be addressed I think.
Mark Brown (4):
arm64: topology: Implement basic CPU topology support
arm64: topology: Add support for topology DT bindings
arm64: topology: Tell the scheduler about the relative power of cores
arm64: topology: Provide relative power numbers for cores
arch/arm64/Kconfig | 24 +++
arch/arm64/include/asm/topology.h | 39 ++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/smp.c | 12 ++
arch/arm64/kernel/topology.c | 384 ++++++++++++++++++++++++++++++++++++++
5 files changed, 460 insertions(+)
create mode 100644 arch/arm64/include/asm/topology.h
create mode 100644 arch/arm64/kernel/topology.c
--
1.8.5.2
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/4] arm64: topology: Implement basic CPU topology support
2013-12-19 20:06 [PATCH 0/4] arm64 topology support Mark Brown
@ 2013-12-19 20:06 ` Mark Brown
2013-12-19 20:06 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
` (4 subsequent siblings)
5 siblings, 0 replies; 42+ 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 basic CPU topology support to arm64, based on the existing pre-v8
code and some work done by Mark Hambleton. This patch does not
implement any topology discovery support since that should be based on
information from firmware, it merely implements the scaffolding for
integration of topology support in the architecture.
The goal is to separate the architecture hookup for providing topology
information from the DT parsing in order to ease review and avoid
blocking the architecture code (which will be built on by other work)
with the DT code review by providing something something simple
and basic.
A following patch will implement support for parsing the DT topology
bindings for ARM, similar patches will be needed for ACPI.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/Kconfig | 24 ++++++++++
arch/arm64/include/asm/topology.h | 39 +++++++++++++++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/smp.c | 12 +++++
arch/arm64/kernel/topology.c | 92 +++++++++++++++++++++++++++++++++++++++
5 files changed, 168 insertions(+)
create mode 100644 arch/arm64/include/asm/topology.h
create mode 100644 arch/arm64/kernel/topology.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 6d4dd22ee4b7..00fcd490b3be 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -154,6 +154,30 @@ config SMP
If you don't know what to do here, say N.
+config ARM_CPU_TOPOLOGY
+ bool "Support CPU topology definition"
+ depends on SMP
+ default y
+ help
+ Support CPU topology definition, based on configuration
+ provided by the firmware.
+
+config SCHED_MC
+ bool "Multi-core scheduler support"
+ depends on ARM_CPU_TOPOLOGY
+ help
+ Multi-core scheduler support improves the CPU scheduler's decision
+ making when dealing with multi-core CPU chips at a cost of slightly
+ increased overhead in some places. If unsure say N here.
+
+config SCHED_SMT
+ bool "SMT scheduler support"
+ depends on ARM_CPU_TOPOLOGY
+ help
+ Improves the CPU scheduler's decision making when dealing with
+ MultiThreading at a cost of slightly increased overhead in some
+ places. If unsure say N here.
+
config NR_CPUS
int "Maximum number of CPUs (2-32)"
range 2 32
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
new file mode 100644
index 000000000000..58b8b84adcd2
--- /dev/null
+++ b/arch/arm64/include/asm/topology.h
@@ -0,0 +1,39 @@
+#ifndef _ASM_ARM_TOPOLOGY_H
+#define _ASM_ARM_TOPOLOGY_H
+
+#ifdef CONFIG_ARM_CPU_TOPOLOGY
+
+#include <linux/cpumask.h>
+
+struct cputopo_arm {
+ int thread_id;
+ int core_id;
+ int socket_id;
+ cpumask_t thread_sibling;
+ cpumask_t core_sibling;
+};
+
+extern struct cputopo_arm cpu_topology[NR_CPUS];
+
+#define topology_physical_package_id(cpu) (cpu_topology[cpu].socket_id)
+#define topology_core_id(cpu) (cpu_topology[cpu].core_id)
+#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling)
+#define topology_thread_cpumask(cpu) (&cpu_topology[cpu].thread_sibling)
+
+#define mc_capable() (cpu_topology[0].socket_id != -1)
+#define smt_capable() (cpu_topology[0].thread_id != -1)
+
+void init_cpu_topology(void);
+void store_cpu_topology(unsigned int cpuid);
+const struct cpumask *cpu_coregroup_mask(int cpu);
+
+#else
+
+static inline void init_cpu_topology(void) { }
+static inline void store_cpu_topology(unsigned int cpuid) { }
+
+#endif
+
+#include <asm-generic/topology.h>
+
+#endif /* _ASM_ARM_TOPOLOGY_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 5ba2fd43a75b..2d145e38ad49 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -18,6 +18,7 @@ arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+arm64-obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index a0c2ca602cf8..0271fbde5363 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -113,6 +113,11 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
return ret;
}
+static void __cpuinit smp_store_cpu_info(unsigned int cpuid)
+{
+ store_cpu_topology(cpuid);
+}
+
/*
* This is the secondary CPU boot entry. We're using this CPUs
* idle thread stack, but a set of temporary page tables.
@@ -150,6 +155,8 @@ asmlinkage void secondary_start_kernel(void)
*/
notify_cpu_starting(cpu);
+ smp_store_cpu_info(cpu);
+
/*
* OK, now it's safe to let the boot CPU continue. Wait for
* the CPU migration code to notice that the CPU is online
@@ -388,6 +395,11 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
int err;
unsigned int cpu, ncores = num_possible_cpus();
+ init_cpu_topology();
+
+ smp_store_cpu_info(smp_processor_id());
+
+
/*
* are we trying to boot more cores than exist?
*/
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
new file mode 100644
index 000000000000..853544f30a8b
--- /dev/null
+++ b/arch/arm64/kernel/topology.c
@@ -0,0 +1,92 @@
+/*
+ * arch/arm64/kernel/topology.c
+ *
+ * Copyright (C) 2011,2013 Linaro Limited.
+ *
+ * Based on the arm32 version written by Vincent Guittot in turn based on
+ * arch/sh/kernel/topology.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/node.h>
+#include <linux/nodemask.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <asm/topology.h>
+
+/*
+ * cpu topology table
+ */
+struct cputopo_arm cpu_topology[NR_CPUS];
+EXPORT_SYMBOL_GPL(cpu_topology);
+
+const struct cpumask *cpu_coregroup_mask(int cpu)
+{
+ return &cpu_topology[cpu].core_sibling;
+}
+
+static void update_siblings_masks(unsigned int cpuid)
+{
+ struct cputopo_arm *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
+ int cpu;
+
+ /* update core and thread sibling masks */
+ for_each_possible_cpu(cpu) {
+ cpu_topo = &cpu_topology[cpu];
+
+ if (cpuid_topo->socket_id != cpu_topo->socket_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
+
+ if (cpuid_topo->core_id != cpu_topo->core_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
+ }
+ smp_wmb();
+}
+
+void store_cpu_topology(unsigned int cpuid)
+{
+ struct cputopo_arm *cpuid_topo = &cpu_topology[cpuid];
+
+ /* DT should have been parsed by the time we get here */
+ if (cpuid_topo->core_id == -1)
+ pr_info("CPU%u: No topology information configured\n", cpuid);
+ else
+ 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)
+{
+ unsigned int cpu;
+
+ /* init core mask and power*/
+ for_each_possible_cpu(cpu) {
+ struct cputopo_arm *cpu_topo = &(cpu_topology[cpu]);
+
+ cpu_topo->thread_id = -1;
+ cpu_topo->core_id = -1;
+ cpu_topo->socket_id = -1;
+ cpumask_clear(&cpu_topo->core_sibling);
+ cpumask_clear(&cpu_topo->thread_sibling);
+ }
+ smp_wmb();
+}
--
1.8.5.2
^ permalink raw reply related [flat|nested] 42+ 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 ` [PATCH 1/4] arm64: topology: Implement basic CPU " Mark Brown
@ 2013-12-19 20:06 ` Mark Brown
2013-12-19 20:06 ` [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
` (3 subsequent siblings)
5 siblings, 0 replies; 42+ 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] 42+ messages in thread
* [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores
2013-12-19 20:06 [PATCH 0/4] arm64 topology support Mark Brown
2013-12-19 20:06 ` [PATCH 1/4] arm64: topology: Implement basic CPU " Mark Brown
2013-12-19 20:06 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
@ 2013-12-19 20:06 ` Mark Brown
2014-01-07 13:05 ` Lorenzo Pieralisi
2013-12-19 20:06 ` [PATCH 4/4] arm64: topology: Provide relative power numbers for cores Mark Brown
` (2 subsequent siblings)
5 siblings, 1 reply; 42+ messages in thread
From: Mark Brown @ 2013-12-19 20:06 UTC (permalink / raw)
To: linux-arm-kernel
From: Mark Brown <broonie@linaro.org>
In non-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>
---
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 5a2724b3d4b7..68ccf4f4f258 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -23,6 +23,29 @@
#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;
+}
+
#ifdef CONFIG_OF
static int cluster_id;
@@ -140,9 +163,49 @@ static void __init parse_cluster(struct device_node *cluster)
cluster_id++;
}
+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 void __init parse_dt_topology(void)
{
+ const struct cpu_efficiency *cpu_eff;
struct device_node *cn;
+ unsigned long min_capacity = (unsigned long)(-1);
+ unsigned long max_capacity = 0;
+ unsigned long capacity = 0;
+ int alloc_size, cpu;
+
+ alloc_size = nr_cpu_ids * sizeof(*__cpu_capacity);
+ __cpu_capacity = kzalloc(alloc_size, GFP_NOWAIT);
cn = of_find_node_by_path("/cpus");
if (!cn) {
@@ -158,10 +221,88 @@ static void __init parse_dt_topology(void)
if (!cn)
return;
parse_cluster(cn);
+
+ 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;
+ }
+
+ /* check if the cpu is marked as "disabled", if so ignore */
+ if (!of_device_is_available(cn))
+ 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));
}
#else
static inline void parse_dt_topology(void) {}
+static inline void update_cpu_power(unsigned int cpuid) {}
#endif
/*
@@ -210,6 +351,8 @@ void store_cpu_topology(unsigned int cpuid)
pr_info("CPU%u: No topology information configured\n", cpuid);
else
update_siblings_masks(cpuid);
+
+ update_cpu_power(cpuid);
}
/*
@@ -229,6 +372,8 @@ void __init init_cpu_topology(void)
cpu_topo->socket_id = -1;
cpumask_clear(&cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
+
+ set_power_scale(cpu, SCHED_POWER_SCALE);
}
parse_dt_topology();
--
1.8.5.2
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores
2013-12-19 20:06 ` [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
@ 2014-01-07 13:05 ` Lorenzo Pieralisi
2014-01-07 13:38 ` Mark Brown
0 siblings, 1 reply; 42+ messages in thread
From: Lorenzo Pieralisi @ 2014-01-07 13:05 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Dec 19, 2013 at 08:06:14PM +0000, Mark Brown wrote:
> From: Mark Brown <broonie@linaro.org>
>
> In non-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>
> ---
> 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
[...]
> +/*
> + * 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 void __init parse_dt_topology(void)
> {
> + const struct cpu_efficiency *cpu_eff;
> struct device_node *cn;
> + unsigned long min_capacity = (unsigned long)(-1);
ULONG_MAX ?
> + unsigned long max_capacity = 0;
> + unsigned long capacity = 0;
> + int alloc_size, cpu;
> +
> + alloc_size = nr_cpu_ids * sizeof(*__cpu_capacity);
> + __cpu_capacity = kzalloc(alloc_size, GFP_NOWAIT);
kcalloc ? BTW this patch should include slab.h not the previous ones,
because that's the only patch where memory allocation takes place unless
I am missing something.
> cn = of_find_node_by_path("/cpus");
> if (!cn) {
> @@ -158,10 +221,88 @@ static void __init parse_dt_topology(void)
> if (!cn)
> return;
> parse_cluster(cn);
> +
> + 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;
> + }
> +
> + /* check if the cpu is marked as "disabled", if so ignore */
> + if (!of_device_is_available(cn))
> + continue;
It is time we defined what a "disabled" CPU means in ARM world, I need to
have a proper look into this since this topic has been brought up before.
Lorenzo
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores
2014-01-07 13:05 ` Lorenzo Pieralisi
@ 2014-01-07 13:38 ` Mark Brown
2014-01-07 14:29 ` Lorenzo Pieralisi
0 siblings, 1 reply; 42+ messages in thread
From: Mark Brown @ 2014-01-07 13:38 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jan 07, 2014 at 01:05:40PM +0000, Lorenzo Pieralisi wrote:
> On Thu, Dec 19, 2013 at 08:06:14PM +0000, Mark Brown wrote:
> > + /* check if the cpu is marked as "disabled", if so ignore */
> > + if (!of_device_is_available(cn))
> > + continue;
> It is time we defined what a "disabled" CPU means in ARM world, I need to
> have a proper look into this since this topic has been brought up before.
What is the confusion here - why would there be something architecture
specific going on?
-------------- 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/20140107/c7d57827/attachment.sig>
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores
2014-01-07 13:38 ` Mark Brown
@ 2014-01-07 14:29 ` Lorenzo Pieralisi
2014-01-07 15:06 ` Mark Brown
0 siblings, 1 reply; 42+ messages in thread
From: Lorenzo Pieralisi @ 2014-01-07 14:29 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jan 07, 2014 at 01:38:29PM +0000, Mark Brown wrote:
> On Tue, Jan 07, 2014 at 01:05:40PM +0000, Lorenzo Pieralisi wrote:
> > On Thu, Dec 19, 2013 at 08:06:14PM +0000, Mark Brown wrote:
>
> > > + /* check if the cpu is marked as "disabled", if so ignore */
> > > + if (!of_device_is_available(cn))
> > > + continue;
>
> > It is time we defined what a "disabled" CPU means in ARM world, I need to
> > have a proper look into this since this topic has been brought up before.
>
> What is the confusion here - why would there be something architecture
> specific going on?
I think this check was added following this thread discussion:
http://lkml.indiana.edu/hypermail/linux/kernel/1306.0/03663.html
So my question is: what does "disabled" mean ? A CPU present in HW
that can't/must not be booted ?
ePAPR v1.1 page 43:
"disabled". The CPU is in a quiescent state. A quiescent CPU is in a state
where it cannot interfere with the normal operation of other CPUs, nor can
its state be affected by the normal operation of other running CPUs, except
by an explicit method for enabling or reenabling the quiescent CPU (see the
enable-method property).
This means that a "disabled" CPU can be booted with eg PSCI but that is
not what the thread in the link above wants to achieve. Furthermore, if
we add the check in topology.c, the check must be executed also when
building the cpu_logical_map, otherwise a "disabled" cpu would be marked
possible and then booted, am I wrong ?
Lorenzo
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores
2014-01-07 14:29 ` Lorenzo Pieralisi
@ 2014-01-07 15:06 ` Mark Brown
2014-01-07 17:56 ` Lorenzo Pieralisi
0 siblings, 1 reply; 42+ messages in thread
From: Mark Brown @ 2014-01-07 15:06 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jan 07, 2014 at 02:29:29PM +0000, Lorenzo Pieralisi wrote:
> On Tue, Jan 07, 2014 at 01:38:29PM +0000, Mark Brown wrote:
> > On Tue, Jan 07, 2014 at 01:05:40PM +0000, Lorenzo Pieralisi wrote:
> > > It is time we defined what a "disabled" CPU means in ARM world, I need to
> > > have a proper look into this since this topic has been brought up before.
> > What is the confusion here - why would there be something architecture
> > specific going on?
> I think this check was added following this thread discussion:
> http://lkml.indiana.edu/hypermail/linux/kernel/1306.0/03663.html
> So my question is: what does "disabled" mean ? A CPU present in HW
> that can't/must not be booted ?
Yes, that would seem to be the obvious meaning and consistent with ePAPR
(in so far as we're paying a blind bit of notice to ePAPR, see other
threads...).
> ePAPR v1.1 page 43:
> "disabled". The CPU is in a quiescent state. A quiescent CPU is in a state
> where it cannot interfere with the normal operation of other CPUs, nor can
> its state be affected by the normal operation of other running CPUs, except
> by an explicit method for enabling or reenabling the quiescent CPU (see the
> enable-method property).
> This means that a "disabled" CPU can be booted with eg PSCI but that is
> not what the thread in the link above wants to achieve. Furthermore, if
I think that's just another bit of ill considered wording from ePAPR
that doesn't really reflect reality; it seems like what they're trying
to shoot for there is administratively down.
At the very least that means hot unplugged, and it seems reasonable to
read that as being stronger than that. The current ARM implementation
is more conservative since it doesn't provide any way to put the core on
line but it does seem more likely to match what a system integrator
would be trying to achieve and it also matches the standard meaning of
disabled.
> we add the check in topology.c, the check must be executed also when
> building the cpu_logical_map, otherwise a "disabled" cpu would be marked
> possible and then booted, am I wrong ?
Right, this is a separate issue in the SMP enumeration code - it should
be paying attention to the property and at the very least defaulting the
core to being unplugged, though like I say I do find the ARM meaning
more sane.
In any case I don't vastly care, I guess I'll drop this for now.
-------------- 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/20140107/b9590c39/attachment-0001.sig>
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores
2014-01-07 15:06 ` Mark Brown
@ 2014-01-07 17:56 ` Lorenzo Pieralisi
2014-01-07 18:02 ` Mark Brown
0 siblings, 1 reply; 42+ messages in thread
From: Lorenzo Pieralisi @ 2014-01-07 17:56 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jan 07, 2014 at 03:06:31PM +0000, Mark Brown wrote:
> On Tue, Jan 07, 2014 at 02:29:29PM +0000, Lorenzo Pieralisi wrote:
> > On Tue, Jan 07, 2014 at 01:38:29PM +0000, Mark Brown wrote:
> > > On Tue, Jan 07, 2014 at 01:05:40PM +0000, Lorenzo Pieralisi wrote:
>
> > > > It is time we defined what a "disabled" CPU means in ARM world, I need to
> > > > have a proper look into this since this topic has been brought up before.
>
> > > What is the confusion here - why would there be something architecture
> > > specific going on?
>
> > I think this check was added following this thread discussion:
>
> > http://lkml.indiana.edu/hypermail/linux/kernel/1306.0/03663.html
>
> > So my question is: what does "disabled" mean ? A CPU present in HW
> > that can't/must not be booted ?
>
> Yes, that would seem to be the obvious meaning and consistent with ePAPR
> (in so far as we're paying a blind bit of notice to ePAPR, see other
> threads...).
Just playing devil's advocate and trying to reuse ePAPR bindings as much as
possible, provided they define what we need on ARM. In this case it seems they
do not.
> > ePAPR v1.1 page 43:
>
> > "disabled". The CPU is in a quiescent state. A quiescent CPU is in a state
> > where it cannot interfere with the normal operation of other CPUs, nor can
> > its state be affected by the normal operation of other running CPUs, except
> > by an explicit method for enabling or reenabling the quiescent CPU (see the
> > enable-method property).
>
> > This means that a "disabled" CPU can be booted with eg PSCI but that is
> > not what the thread in the link above wants to achieve. Furthermore, if
>
> I think that's just another bit of ill considered wording from ePAPR
> that doesn't really reflect reality; it seems like what they're trying
> to shoot for there is administratively down.
>
> At the very least that means hot unplugged, and it seems reasonable to
> read that as being stronger than that. The current ARM implementation
> is more conservative since it doesn't provide any way to put the core on
> line but it does seem more likely to match what a system integrator
> would be trying to achieve and it also matches the standard meaning of
> disabled.
What do you mean by ARM implementation ? The status property is currently
ignored on ARM. I'd agree with what you are saying but that should be
specified in DT bindings.
> > we add the check in topology.c, the check must be executed also when
> > building the cpu_logical_map, otherwise a "disabled" cpu would be marked
> > possible and then booted, am I wrong ?
>
> Right, this is a separate issue in the SMP enumeration code - it should
> be paying attention to the property and at the very least defaulting the
> core to being unplugged, though like I say I do find the ARM meaning
> more sane.
Again, I tend to agree, since this means that the CPU is there but
simply is not a "possible" one. To be debated.
> In any case I don't vastly care, I guess I'll drop this for now.
Yes, I think dropping the check is fine for now, we can add it if/when
we achieve consensus, that should not be a big deal.
Lorenzo
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores
2014-01-07 17:56 ` Lorenzo Pieralisi
@ 2014-01-07 18:02 ` Mark Brown
0 siblings, 0 replies; 42+ messages in thread
From: Mark Brown @ 2014-01-07 18:02 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jan 07, 2014 at 05:56:42PM +0000, Lorenzo Pieralisi wrote:
> On Tue, Jan 07, 2014 at 03:06:31PM +0000, Mark Brown wrote:
> > At the very least that means hot unplugged, and it seems reasonable to
> > read that as being stronger than that. The current ARM implementation
> > is more conservative since it doesn't provide any way to put the core on
> > line but it does seem more likely to match what a system integrator
> > would be trying to achieve and it also matches the standard meaning of
> > disabled.
> What do you mean by ARM implementation ? The status property is currently
> ignored on ARM. I'd agree with what you are saying but that should be
> specified in DT bindings.
The 32 bit ARM implementation. That code was supposed to be just
cut'n'pasted from there, though I see now it wasn't...
-------------- 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/20140107/59c818df/attachment.sig>
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 4/4] arm64: topology: Provide relative power numbers for cores
2013-12-19 20:06 [PATCH 0/4] arm64 topology support Mark Brown
` (2 preceding siblings ...)
2013-12-19 20:06 ` [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
@ 2013-12-19 20:06 ` Mark Brown
2013-12-20 18:07 ` [PATCH 0/4] arm64 topology support Catalin Marinas
2014-01-07 18:05 ` Lorenzo Pieralisi
5 siblings, 0 replies; 42+ messages in thread
From: Mark Brown @ 2013-12-19 20:06 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 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. 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. In both ARMv7 and ARMv8 cases the numbers were
based on the published DMIPS numbers.
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 68ccf4f4f258..67df4639d2b1 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -179,6 +179,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.8.5.2
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 0/4] arm64 topology support
2013-12-19 20:06 [PATCH 0/4] arm64 topology support Mark Brown
` (3 preceding siblings ...)
2013-12-19 20:06 ` [PATCH 4/4] arm64: topology: Provide relative power numbers for cores Mark Brown
@ 2013-12-20 18:07 ` Catalin Marinas
2014-01-07 18:05 ` Lorenzo Pieralisi
5 siblings, 0 replies; 42+ messages in thread
From: Catalin Marinas @ 2013-12-20 18:07 UTC (permalink / raw)
To: linux-arm-kernel
Hi Mark,
On Thu, Dec 19, 2013 at 08:06:11PM +0000, Mark Brown wrote:
> Another spin of the arm64 topology work - this should incorporate most
> of the feedback from Lorenzo, there's a few things that were still under
> discussion the main ones being:
We'll have to leave this patchset for early January, hopefully we have a
bit of time to review before the merging window. I (and Will, Lorenzo)
am on holiday until around 6th of January.
Have a Good Christmas!
--
Catalin
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 0/4] arm64 topology support
2013-12-19 20:06 [PATCH 0/4] arm64 topology support Mark Brown
` (4 preceding siblings ...)
2013-12-20 18:07 ` [PATCH 0/4] arm64 topology support Catalin Marinas
@ 2014-01-07 18:05 ` Lorenzo Pieralisi
2014-01-07 18:23 ` Mark Brown
5 siblings, 1 reply; 42+ messages in thread
From: Lorenzo Pieralisi @ 2014-01-07 18:05 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Dec 19, 2013 at 08:06:11PM +0000, Mark Brown wrote:
> From: Mark Brown <broonie@linaro.org>
>
> Another spin of the arm64 topology work - this should incorporate most
> of the feedback from Lorenzo, there's a few things that were still under
> discussion the main ones being:
>
> - Should we have a smp_store_cpu_info(); like I say I like the errors
> it generates for omitted cores and the reuse of the SMP enumeration
> code (and cross-check with that I guess - make sure we don't get
> confused about which CPUs are getting enabled).
I agree, we should keep it, it makes sense.
> - Should we update the binding to allow cores in the root cpu_map node
> (since it's less effort in code and not a meaningful difference
> semantically), warn if we find cores in the cpu_map node or actively
> reject such DTs?
I think cpu-map must only contain cluster nodes as descendant children. This
to prevent creative DTs with cluster and core nodes at top topology level.
Overall it makes sense, cores can only exist in a cluster container, might
seem churn but at least that's strict.
> In both cases I don't much mind but I think what's there is reasonable
> so I've left the code as-is pending further feedback. I also didn't
> update the code to get more reuse of the iteration code, like I said I
> did look at that when writing the code but couldn't find anything that
> actually made things more pleasant but if someone has some ideas...
I still think that most of the DT parsing code can and should be reused
also for other purposes (eg IRQ affinity). Comments on the patches
concerned.
Lorenzo
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 0/4] arm64 topology support
2014-01-07 18:05 ` Lorenzo Pieralisi
@ 2014-01-07 18:23 ` Mark Brown
2014-01-07 18:46 ` Mark Brown
2014-01-08 10:24 ` Lorenzo Pieralisi
0 siblings, 2 replies; 42+ messages in thread
From: Mark Brown @ 2014-01-07 18:23 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jan 07, 2014 at 06:05:45PM +0000, Lorenzo Pieralisi wrote:
> On Thu, Dec 19, 2013 at 08:06:11PM +0000, Mark Brown wrote:
> > - Should we update the binding to allow cores in the root cpu_map node
> > (since it's less effort in code and not a meaningful difference
> > semantically), warn if we find cores in the cpu_map node or actively
> > reject such DTs?
> I think cpu-map must only contain cluster nodes as descendant children. This
> to prevent creative DTs with cluster and core nodes at top topology level.
> Overall it makes sense, cores can only exist in a cluster container, might
> seem churn but at least that's strict.
That still leaves the question of what you want to happen with such
maps.
> > In both cases I don't much mind but I think what's there is reasonable
> > so I've left the code as-is pending further feedback. I also didn't
> > update the code to get more reuse of the iteration code, like I said I
> > did look at that when writing the code but couldn't find anything that
> > actually made things more pleasant but if someone has some ideas...
> I still think that most of the DT parsing code can and should be reused
> also for other purposes (eg IRQ affinity). Comments on the patches
> concerned.
I'm not seeing any mails there... Note that most of the code is there
because the binding took the decision to build the numbering for the
subnodes into the names which is very unusual for DT and hence not very
something the tooling works well with. Do these other bindings have the
same problem?
get_cpu_for_node() could probably be shifted into a header, perhaps when
there's other users though.
-------------- 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/20140107/02c474e9/attachment.sig>
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 0/4] arm64 topology support
2014-01-07 18:23 ` Mark Brown
@ 2014-01-07 18:46 ` Mark Brown
2014-01-08 10:15 ` Lorenzo Pieralisi
2014-01-08 10:24 ` Lorenzo Pieralisi
1 sibling, 1 reply; 42+ messages in thread
From: Mark Brown @ 2014-01-07 18:46 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jan 07, 2014 at 06:23:55PM +0000, Mark Brown wrote:
> On Tue, Jan 07, 2014 at 06:05:45PM +0000, Lorenzo Pieralisi wrote:
> > I think cpu-map must only contain cluster nodes as descendant children. This
> > to prevent creative DTs with cluster and core nodes at top topology level.
> > Overall it makes sense, cores can only exist in a cluster container, might
> > seem churn but at least that's strict.
> That still leaves the question of what you want to happen with such
> maps.
I've implemented a warning for this; it seems more constructive than
rejecting such DTs outright.
-------------- 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/20140107/d23d963f/attachment.sig>
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 0/4] arm64 topology support
2014-01-07 18:46 ` Mark Brown
@ 2014-01-08 10:15 ` Lorenzo Pieralisi
0 siblings, 0 replies; 42+ messages in thread
From: Lorenzo Pieralisi @ 2014-01-08 10:15 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jan 07, 2014 at 06:46:58PM +0000, Mark Brown wrote:
> On Tue, Jan 07, 2014 at 06:23:55PM +0000, Mark Brown wrote:
> > On Tue, Jan 07, 2014 at 06:05:45PM +0000, Lorenzo Pieralisi wrote:
>
> > > I think cpu-map must only contain cluster nodes as descendant children. This
> > > to prevent creative DTs with cluster and core nodes at top topology level.
> > > Overall it makes sense, cores can only exist in a cluster container, might
> > > seem churn but at least that's strict.
>
> > That still leaves the question of what you want to happen with such
> > maps.
>
> I've implemented a warning for this; it seems more constructive than
> rejecting such DTs outright.
At least we should reject the nodes that do not follow bindings rules
and warn on them. We should keep the valid nodes properties and build
the topology from the resulting valus, even though this is a slippery slope,
basically the topology is botched but kernel spits a warning on this so
I guess that's acceptable.
Lorenzo
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 0/4] arm64 topology support
2014-01-07 18:23 ` Mark Brown
2014-01-07 18:46 ` Mark Brown
@ 2014-01-08 10:24 ` Lorenzo Pieralisi
2014-01-08 11:58 ` Mark Brown
1 sibling, 1 reply; 42+ messages in thread
From: Lorenzo Pieralisi @ 2014-01-08 10:24 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, Jan 07, 2014 at 06:23:55PM +0000, Mark Brown wrote:
> On Tue, Jan 07, 2014 at 06:05:45PM +0000, Lorenzo Pieralisi wrote:
> > On Thu, Dec 19, 2013 at 08:06:11PM +0000, Mark Brown wrote:
>
> > > - Should we update the binding to allow cores in the root cpu_map node
> > > (since it's less effort in code and not a meaningful difference
> > > semantically), warn if we find cores in the cpu_map node or actively
> > > reject such DTs?
>
> > I think cpu-map must only contain cluster nodes as descendant children. This
> > to prevent creative DTs with cluster and core nodes at top topology level.
> > Overall it makes sense, cores can only exist in a cluster container, might
> > seem churn but at least that's strict.
>
> That still leaves the question of what you want to happen with such
> maps.
>
> > > In both cases I don't much mind but I think what's there is reasonable
> > > so I've left the code as-is pending further feedback. I also didn't
> > > update the code to get more reuse of the iteration code, like I said I
> > > did look at that when writing the code but couldn't find anything that
> > > actually made things more pleasant but if someone has some ideas...
>
> > I still think that most of the DT parsing code can and should be reused
> > also for other purposes (eg IRQ affinity). Comments on the patches
> > concerned.
>
> I'm not seeing any mails there... Note that most of the code is there
> because the binding took the decision to build the numbering for the
> subnodes into the names which is very unusual for DT and hence not very
> something the tooling works well with. Do these other bindings have the
> same problem?
Reviewing the parsing code now. I know the numbering is unusual and the
decision was not a simple one to make, it has been discussed and the reason
is simple: I do not want reg property in topology nodes, because they are
meaningless (we defined the topology to remove the dependency on the MPIDR,
so if we added back reg properties to eg cluster nodes, they might be thought
as cluster identifiers and that's wrong. Only cpu nodes reflect HW MPIDR
values).
No, as far as I can tell at present, IRQ affinity and C-state affinity
will work with phandles to cpu-map nodes, so the parsing you need for
the topology should not be reused for that purpose, but it is hard to
tell by just reviewing the code, I have to apply the patches and see how they
can be adapted for other purposes.
Nothing prevents us from merging code as it is and consolidate it when
needed, provided we will do that.
> get_cpu_for_node() could probably be shifted into a header, perhaps when
> there's other users though.
Right.
Thanks,
Lorenzo
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 1/4] arm64: topology: Initialise default topology state immediately
@ 2014-04-22 20:21 Mark Brown
2014-04-22 20:21 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
0 siblings, 1 reply; 42+ messages in thread
From: Mark Brown @ 2014-04-22 20:21 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.2
^ permalink raw reply related [flat|nested] 42+ 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; 42+ 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] 42+ 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; 42+ 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] 42+ 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
0 siblings, 1 reply; 42+ 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] 42+ 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
0 siblings, 1 reply; 42+ 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] 42+ 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; 42+ 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] 42+ 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; 42+ 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] 42+ 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; 42+ 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] 42+ messages in thread
* [PATCH 0/4] arm64: Topology
@ 2014-02-26 0:48 Mark Brown
2014-02-26 0:48 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
0 siblings, 1 reply; 42+ messages in thread
From: Mark Brown @ 2014-02-26 0:48 UTC (permalink / raw)
To: linux-arm-kernel
Use the scheduler documented default for unknown CPUs rather than
assigning them to a cluster.
Mark Brown (4):
arm64: topology: Implement basic CPU topology support
arm64: topology: Add support for topology DT bindings
arm64: topology: Tell the scheduler about the relative power of cores
arm64: topology: Provide relative power numbers for cores
arch/arm64/Kconfig | 24 +++
arch/arm64/include/asm/topology.h | 39 ++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/smp.c | 11 ++
arch/arm64/kernel/topology.c | 380 ++++++++++++++++++++++++++++++++++++++
5 files changed, 455 insertions(+)
^ permalink raw reply [flat|nested] 42+ 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; 42+ 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] 42+ messages in thread
* [PATCH 0/4] arm64: topology: CPU topology support
@ 2014-02-25 4:25 Mark Brown
2014-02-25 4:25 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
0 siblings, 1 reply; 42+ messages in thread
From: Mark Brown @ 2014-02-25 4:25 UTC (permalink / raw)
To: linux-arm-kernel
This revision of the series places any otherwise unmapped CPUs into a
cluster in order to avoid confusing the scheduler if no topology
information is provided. This means that we should never bring up a CPU
with no topology information so while the warning is still present and
has been upgraded it should never triger unless there is a genuine bug
(as opposed to missing information).
Mark Brown (4):
arm64: topology: Implement basic CPU topology support
arm64: topology: Add support for topology DT bindings
arm64: topology: Tell the scheduler about the relative power of cores
arm64: topology: Provide relative power numbers for cores
arch/arm64/Kconfig | 24 +++
arch/arm64/include/asm/topology.h | 39 ++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/smp.c | 11 ++
arch/arm64/kernel/topology.c | 388 ++++++++++++++++++++++++++++++++++++++
5 files changed, 463 insertions(+)
^ permalink raw reply [flat|nested] 42+ 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; 42+ 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] 42+ messages in thread
* [PATCH 1/4] arm64: topology: Implement basic CPU topology support
@ 2014-02-11 22:06 Mark Brown
2014-02-11 22:06 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
0 siblings, 1 reply; 42+ 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 basic CPU topology support to arm64, based on the existing pre-v8
code and some work done by Mark Hambleton. This patch does not
implement any topology discovery support since that should be based on
information from firmware, it merely implements the scaffolding for
integration of topology support in the architecture.
The goal is to separate the architecture hookup for providing topology
information from the DT parsing in order to ease review and avoid
blocking the architecture code (which will be built on by other work)
with the DT code review by providing something simple and basic.
A following patch will implement support for parsing the DT topology
bindings for ARM, similar patches will be needed for ACPI.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/Kconfig | 24 +++++++++++
arch/arm64/include/asm/topology.h | 39 +++++++++++++++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/smp.c | 11 +++++
arch/arm64/kernel/topology.c | 89 +++++++++++++++++++++++++++++++++++++++
5 files changed, 164 insertions(+)
create mode 100644 arch/arm64/include/asm/topology.h
create mode 100644 arch/arm64/kernel/topology.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 27bbcfc7202a..fea7b477676b 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -164,6 +164,30 @@ config SMP
If you don't know what to do here, say N.
+config CPU_TOPOLOGY
+ bool "Support CPU topology definition"
+ depends on SMP
+ default y
+ help
+ Support CPU topology definition, based on configuration
+ provided by the firmware.
+
+config SCHED_MC
+ bool "Multi-core scheduler support"
+ depends on CPU_TOPOLOGY
+ help
+ Multi-core scheduler support improves the CPU scheduler's decision
+ making when dealing with multi-core CPU chips at a cost of slightly
+ increased overhead in some places. If unsure say N here.
+
+config SCHED_SMT
+ bool "SMT scheduler support"
+ depends on CPU_TOPOLOGY
+ help
+ Improves the CPU scheduler's decision making when dealing with
+ MultiThreading at a cost of slightly increased overhead in some
+ places. If unsure say N here.
+
config NR_CPUS
int "Maximum number of CPUs (2-32)"
range 2 32
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
new file mode 100644
index 000000000000..c8a47e8f452b
--- /dev/null
+++ b/arch/arm64/include/asm/topology.h
@@ -0,0 +1,39 @@
+#ifndef __ASM_TOPOLOGY_H
+#define __ASM_TOPOLOGY_H
+
+#ifdef CONFIG_CPU_TOPOLOGY
+
+#include <linux/cpumask.h>
+
+struct cpu_topology {
+ int thread_id;
+ int core_id;
+ int cluster_id;
+ cpumask_t thread_sibling;
+ cpumask_t core_sibling;
+};
+
+extern struct cpu_topology cpu_topology[NR_CPUS];
+
+#define topology_physical_package_id(cpu) (cpu_topology[cpu].cluster_id)
+#define topology_core_id(cpu) (cpu_topology[cpu].core_id)
+#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling)
+#define topology_thread_cpumask(cpu) (&cpu_topology[cpu].thread_sibling)
+
+#define mc_capable() (cpu_topology[0].cluster_id != -1)
+#define smt_capable() (cpu_topology[0].thread_id != -1)
+
+void init_cpu_topology(void);
+void store_cpu_topology(unsigned int cpuid);
+const struct cpumask *cpu_coregroup_mask(int cpu);
+
+#else
+
+static inline void init_cpu_topology(void) { }
+static inline void store_cpu_topology(unsigned int cpuid) { }
+
+#endif
+
+#include <asm-generic/topology.h>
+
+#endif /* _ASM_ARM_TOPOLOGY_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 2d4554b13410..252b62181532 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -20,6 +20,7 @@ arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
+arm64-obj-$(CONFIG_CPU_TOPOLOGY) += topology.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 7cfb92a4ab66..9660750f34ba 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -114,6 +114,11 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
return ret;
}
+static void smp_store_cpu_info(unsigned int cpuid)
+{
+ store_cpu_topology(cpuid);
+}
+
/*
* This is the secondary CPU boot entry. We're using this CPUs
* idle thread stack, but a set of temporary page tables.
@@ -152,6 +157,8 @@ asmlinkage void secondary_start_kernel(void)
*/
notify_cpu_starting(cpu);
+ smp_store_cpu_info(cpu);
+
/*
* OK, now it's safe to let the boot CPU continue. Wait for
* the CPU migration code to notice that the CPU is online
@@ -390,6 +397,10 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
int err;
unsigned int cpu, ncores = num_possible_cpus();
+ init_cpu_topology();
+
+ smp_store_cpu_info(smp_processor_id());
+
/*
* are we trying to boot more cores than exist?
*/
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
new file mode 100644
index 000000000000..cbb8d432214e
--- /dev/null
+++ b/arch/arm64/kernel/topology.c
@@ -0,0 +1,89 @@
+/*
+ * arch/arm64/kernel/topology.c
+ *
+ * Copyright (C) 2011,2013 Linaro Limited.
+ *
+ * Based on the arm32 version written by Vincent Guittot in turn based on
+ * arch/sh/kernel/topology.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/node.h>
+#include <linux/nodemask.h>
+#include <linux/sched.h>
+
+#include <asm/topology.h>
+
+/*
+ * cpu topology table
+ */
+struct cpu_topology cpu_topology[NR_CPUS];
+EXPORT_SYMBOL_GPL(cpu_topology);
+
+const struct cpumask *cpu_coregroup_mask(int cpu)
+{
+ return &cpu_topology[cpu].core_sibling;
+}
+
+static void update_siblings_masks(unsigned int cpuid)
+{
+ struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
+ int cpu;
+
+ /* update core and thread sibling masks */
+ for_each_possible_cpu(cpu) {
+ cpu_topo = &cpu_topology[cpu];
+
+ if (cpuid_topo->cluster_id != cpu_topo->cluster_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
+
+ if (cpuid_topo->core_id != cpu_topo->core_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
+ }
+}
+
+void store_cpu_topology(unsigned int cpuid)
+{
+ struct cpu_topology *cpuid_topo = &cpu_topology[cpuid];
+
+ /* DT should have been parsed by the time we get here */
+ if (cpuid_topo->core_id == -1)
+ pr_info("CPU%u: No topology information configured\n", cpuid);
+ else
+ 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)
+{
+ unsigned int cpu;
+
+ /* init core mask and power*/
+ for_each_possible_cpu(cpu) {
+ struct cpu_topology *cpu_topo = &cpu_topology[cpu];
+
+ cpu_topo->thread_id = -1;
+ cpu_topo->core_id = -1;
+ cpu_topo->cluster_id = -1;
+ cpumask_clear(&cpu_topo->core_sibling);
+ cpumask_clear(&cpu_topo->thread_sibling);
+ }
+}
--
1.9.0.rc3
^ permalink raw reply related [flat|nested] 42+ 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; 42+ 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] 42+ messages in thread
* [PATCH 1/4] arm64: topology: Implement basic CPU topology support
@ 2014-02-10 13:02 Mark Brown
2014-02-10 13:02 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
0 siblings, 1 reply; 42+ 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 basic CPU topology support to arm64, based on the existing pre-v8
code and some work done by Mark Hambleton. This patch does not
implement any topology discovery support since that should be based on
information from firmware, it merely implements the scaffolding for
integration of topology support in the architecture.
The goal is to separate the architecture hookup for providing topology
information from the DT parsing in order to ease review and avoid
blocking the architecture code (which will be built on by other work)
with the DT code review by providing something simple and basic.
A following patch will implement support for parsing the DT topology
bindings for ARM, similar patches will be needed for ACPI.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/Kconfig | 24 +++++++++++
arch/arm64/include/asm/topology.h | 39 +++++++++++++++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/smp.c | 11 +++++
arch/arm64/kernel/topology.c | 91 +++++++++++++++++++++++++++++++++++++++
5 files changed, 166 insertions(+)
create mode 100644 arch/arm64/include/asm/topology.h
create mode 100644 arch/arm64/kernel/topology.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 27bbcfc7202a..fea7b477676b 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -164,6 +164,30 @@ config SMP
If you don't know what to do here, say N.
+config CPU_TOPOLOGY
+ bool "Support CPU topology definition"
+ depends on SMP
+ default y
+ help
+ Support CPU topology definition, based on configuration
+ provided by the firmware.
+
+config SCHED_MC
+ bool "Multi-core scheduler support"
+ depends on CPU_TOPOLOGY
+ help
+ Multi-core scheduler support improves the CPU scheduler's decision
+ making when dealing with multi-core CPU chips at a cost of slightly
+ increased overhead in some places. If unsure say N here.
+
+config SCHED_SMT
+ bool "SMT scheduler support"
+ depends on CPU_TOPOLOGY
+ help
+ Improves the CPU scheduler's decision making when dealing with
+ MultiThreading at a cost of slightly increased overhead in some
+ places. If unsure say N here.
+
config NR_CPUS
int "Maximum number of CPUs (2-32)"
range 2 32
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
new file mode 100644
index 000000000000..c8a47e8f452b
--- /dev/null
+++ b/arch/arm64/include/asm/topology.h
@@ -0,0 +1,39 @@
+#ifndef __ASM_TOPOLOGY_H
+#define __ASM_TOPOLOGY_H
+
+#ifdef CONFIG_CPU_TOPOLOGY
+
+#include <linux/cpumask.h>
+
+struct cpu_topology {
+ int thread_id;
+ int core_id;
+ int cluster_id;
+ cpumask_t thread_sibling;
+ cpumask_t core_sibling;
+};
+
+extern struct cpu_topology cpu_topology[NR_CPUS];
+
+#define topology_physical_package_id(cpu) (cpu_topology[cpu].cluster_id)
+#define topology_core_id(cpu) (cpu_topology[cpu].core_id)
+#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling)
+#define topology_thread_cpumask(cpu) (&cpu_topology[cpu].thread_sibling)
+
+#define mc_capable() (cpu_topology[0].cluster_id != -1)
+#define smt_capable() (cpu_topology[0].thread_id != -1)
+
+void init_cpu_topology(void);
+void store_cpu_topology(unsigned int cpuid);
+const struct cpumask *cpu_coregroup_mask(int cpu);
+
+#else
+
+static inline void init_cpu_topology(void) { }
+static inline void store_cpu_topology(unsigned int cpuid) { }
+
+#endif
+
+#include <asm-generic/topology.h>
+
+#endif /* _ASM_ARM_TOPOLOGY_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 2d4554b13410..252b62181532 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -20,6 +20,7 @@ arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
+arm64-obj-$(CONFIG_CPU_TOPOLOGY) += topology.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 7cfb92a4ab66..9660750f34ba 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -114,6 +114,11 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
return ret;
}
+static void smp_store_cpu_info(unsigned int cpuid)
+{
+ store_cpu_topology(cpuid);
+}
+
/*
* This is the secondary CPU boot entry. We're using this CPUs
* idle thread stack, but a set of temporary page tables.
@@ -152,6 +157,8 @@ asmlinkage void secondary_start_kernel(void)
*/
notify_cpu_starting(cpu);
+ smp_store_cpu_info(cpu);
+
/*
* OK, now it's safe to let the boot CPU continue. Wait for
* the CPU migration code to notice that the CPU is online
@@ -390,6 +397,10 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
int err;
unsigned int cpu, ncores = num_possible_cpus();
+ init_cpu_topology();
+
+ smp_store_cpu_info(smp_processor_id());
+
/*
* are we trying to boot more cores than exist?
*/
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
new file mode 100644
index 000000000000..a6d4ed2d69c0
--- /dev/null
+++ b/arch/arm64/kernel/topology.c
@@ -0,0 +1,91 @@
+/*
+ * arch/arm64/kernel/topology.c
+ *
+ * Copyright (C) 2011,2013 Linaro Limited.
+ *
+ * Based on the arm32 version written by Vincent Guittot in turn based on
+ * arch/sh/kernel/topology.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/node.h>
+#include <linux/nodemask.h>
+#include <linux/sched.h>
+
+#include <asm/topology.h>
+
+/*
+ * cpu topology table
+ */
+struct cpu_topology cpu_topology[NR_CPUS];
+EXPORT_SYMBOL_GPL(cpu_topology);
+
+const struct cpumask *cpu_coregroup_mask(int cpu)
+{
+ return &cpu_topology[cpu].core_sibling;
+}
+
+static void update_siblings_masks(unsigned int cpuid)
+{
+ struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
+ int cpu;
+
+ /* update core and thread sibling masks */
+ for_each_possible_cpu(cpu) {
+ cpu_topo = &cpu_topology[cpu];
+
+ if (cpuid_topo->cluster_id != cpu_topo->cluster_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
+
+ if (cpuid_topo->core_id != cpu_topo->core_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
+ }
+ smp_wmb();
+}
+
+void store_cpu_topology(unsigned int cpuid)
+{
+ struct cpu_topology *cpuid_topo = &cpu_topology[cpuid];
+
+ /* DT should have been parsed by the time we get here */
+ if (cpuid_topo->core_id == -1)
+ pr_info("CPU%u: No topology information configured\n", cpuid);
+ else
+ 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)
+{
+ unsigned int cpu;
+
+ /* init core mask and power*/
+ for_each_possible_cpu(cpu) {
+ struct cpu_topology *cpu_topo = &cpu_topology[cpu];
+
+ cpu_topo->thread_id = -1;
+ cpu_topo->core_id = -1;
+ cpu_topo->cluster_id = -1;
+ cpumask_clear(&cpu_topo->core_sibling);
+ cpumask_clear(&cpu_topo->thread_sibling);
+ }
+ smp_wmb();
+}
--
1.9.0.rc3
^ permalink raw reply related [flat|nested] 42+ 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; 42+ 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] 42+ messages in thread
* [PATCH v12 0/4] arm64 topology
@ 2014-01-15 11:38 Mark Brown
2014-01-15 11:38 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
0 siblings, 1 reply; 42+ messages in thread
From: Mark Brown @ 2014-01-15 11:38 UTC (permalink / raw)
To: linux-arm-kernel
Another round of cosmetic updates here, this should address everything
from the last round.
arm64: topology: Implement basic CPU topology support
arm64: topology: Add support for topology DT bindings
arm64: topology: Tell the scheduler about the relative
arm64: topology: Provide relative power numbers for cores
^ permalink raw reply [flat|nested] 42+ 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; 42+ 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] 42+ messages in thread
* [PATCH v11 0/4] ARMv8 cpu topology
@ 2014-01-12 19:20 Mark Brown
2014-01-12 19:20 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
0 siblings, 1 reply; 42+ messages in thread
From: Mark Brown @ 2014-01-12 19:20 UTC (permalink / raw)
To: linux-arm-kernel
- A few trivial renames requested by Catalin
- Pass cluster_id around by value outside of parse_cluster
arm64: topology: Implement basic CPU topology support
arm64: topology: Add support for topology DT bindings
arm64: topology: Tell the scheduler about the relative
arm64: topology: Provide relative power numbers for cores
^ permalink raw reply [flat|nested] 42+ 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; 42+ 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] 42+ 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; 42+ 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] 42+ 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; 42+ 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] 42+ messages in thread
* [PATCH 1/4] arm64: topology: Implement basic CPU topology support
@ 2014-01-08 19:12 Mark Brown
2014-01-08 19:12 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
0 siblings, 1 reply; 42+ 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 basic CPU topology support to arm64, based on the existing pre-v8
code and some work done by Mark Hambleton. This patch does not
implement any topology discovery support since that should be based on
information from firmware, it merely implements the scaffolding for
integration of topology support in the architecture.
The goal is to separate the architecture hookup for providing topology
information from the DT parsing in order to ease review and avoid
blocking the architecture code (which will be built on by other work)
with the DT code review by providing something something simple
and basic.
A following patch will implement support for parsing the DT topology
bindings for ARM, similar patches will be needed for ACPI.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/Kconfig | 24 +++++++++++
arch/arm64/include/asm/topology.h | 39 +++++++++++++++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/smp.c | 12 ++++++
arch/arm64/kernel/topology.c | 91 +++++++++++++++++++++++++++++++++++++++
5 files changed, 167 insertions(+)
create mode 100644 arch/arm64/include/asm/topology.h
create mode 100644 arch/arm64/kernel/topology.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 249acb9da4e3..a53514cdf39c 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -162,6 +162,30 @@ config SMP
If you don't know what to do here, say N.
+config ARM_CPU_TOPOLOGY
+ bool "Support CPU topology definition"
+ depends on SMP
+ default y
+ help
+ Support CPU topology definition, based on configuration
+ provided by the firmware.
+
+config SCHED_MC
+ bool "Multi-core scheduler support"
+ depends on ARM_CPU_TOPOLOGY
+ help
+ Multi-core scheduler support improves the CPU scheduler's decision
+ making when dealing with multi-core CPU chips at a cost of slightly
+ increased overhead in some places. If unsure say N here.
+
+config SCHED_SMT
+ bool "SMT scheduler support"
+ depends on ARM_CPU_TOPOLOGY
+ help
+ Improves the CPU scheduler's decision making when dealing with
+ MultiThreading at a cost of slightly increased overhead in some
+ places. If unsure say N here.
+
config NR_CPUS
int "Maximum number of CPUs (2-32)"
range 2 32
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
new file mode 100644
index 000000000000..58b8b84adcd2
--- /dev/null
+++ b/arch/arm64/include/asm/topology.h
@@ -0,0 +1,39 @@
+#ifndef _ASM_ARM_TOPOLOGY_H
+#define _ASM_ARM_TOPOLOGY_H
+
+#ifdef CONFIG_ARM_CPU_TOPOLOGY
+
+#include <linux/cpumask.h>
+
+struct cputopo_arm {
+ int thread_id;
+ int core_id;
+ int socket_id;
+ cpumask_t thread_sibling;
+ cpumask_t core_sibling;
+};
+
+extern struct cputopo_arm cpu_topology[NR_CPUS];
+
+#define topology_physical_package_id(cpu) (cpu_topology[cpu].socket_id)
+#define topology_core_id(cpu) (cpu_topology[cpu].core_id)
+#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling)
+#define topology_thread_cpumask(cpu) (&cpu_topology[cpu].thread_sibling)
+
+#define mc_capable() (cpu_topology[0].socket_id != -1)
+#define smt_capable() (cpu_topology[0].thread_id != -1)
+
+void init_cpu_topology(void);
+void store_cpu_topology(unsigned int cpuid);
+const struct cpumask *cpu_coregroup_mask(int cpu);
+
+#else
+
+static inline void init_cpu_topology(void) { }
+static inline void store_cpu_topology(unsigned int cpuid) { }
+
+#endif
+
+#include <asm-generic/topology.h>
+
+#endif /* _ASM_ARM_TOPOLOGY_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 1cd339d5037b..b2fe6ba97080 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -19,6 +19,7 @@ arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
+arm64-obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 1b7617ab499b..40e20efc13e6 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -114,6 +114,11 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
return ret;
}
+static void __cpuinit smp_store_cpu_info(unsigned int cpuid)
+{
+ store_cpu_topology(cpuid);
+}
+
/*
* This is the secondary CPU boot entry. We're using this CPUs
* idle thread stack, but a set of temporary page tables.
@@ -152,6 +157,8 @@ asmlinkage void secondary_start_kernel(void)
*/
notify_cpu_starting(cpu);
+ smp_store_cpu_info(cpu);
+
/*
* OK, now it's safe to let the boot CPU continue. Wait for
* the CPU migration code to notice that the CPU is online
@@ -391,6 +398,11 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
int err;
unsigned int cpu, ncores = num_possible_cpus();
+ init_cpu_topology();
+
+ smp_store_cpu_info(smp_processor_id());
+
+
/*
* are we trying to boot more cores than exist?
*/
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
new file mode 100644
index 000000000000..20eef01a4707
--- /dev/null
+++ b/arch/arm64/kernel/topology.c
@@ -0,0 +1,91 @@
+/*
+ * arch/arm64/kernel/topology.c
+ *
+ * Copyright (C) 2011,2013 Linaro Limited.
+ *
+ * Based on the arm32 version written by Vincent Guittot in turn based on
+ * arch/sh/kernel/topology.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/node.h>
+#include <linux/nodemask.h>
+#include <linux/sched.h>
+
+#include <asm/topology.h>
+
+/*
+ * cpu topology table
+ */
+struct cputopo_arm cpu_topology[NR_CPUS];
+EXPORT_SYMBOL_GPL(cpu_topology);
+
+const struct cpumask *cpu_coregroup_mask(int cpu)
+{
+ return &cpu_topology[cpu].core_sibling;
+}
+
+static void update_siblings_masks(unsigned int cpuid)
+{
+ struct cputopo_arm *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
+ int cpu;
+
+ /* update core and thread sibling masks */
+ for_each_possible_cpu(cpu) {
+ cpu_topo = &cpu_topology[cpu];
+
+ if (cpuid_topo->socket_id != cpu_topo->socket_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
+
+ if (cpuid_topo->core_id != cpu_topo->core_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
+ }
+ smp_wmb();
+}
+
+void store_cpu_topology(unsigned int cpuid)
+{
+ struct cputopo_arm *cpuid_topo = &cpu_topology[cpuid];
+
+ /* DT should have been parsed by the time we get here */
+ if (cpuid_topo->core_id == -1)
+ pr_info("CPU%u: No topology information configured\n", cpuid);
+ else
+ 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)
+{
+ unsigned int cpu;
+
+ /* init core mask and power*/
+ for_each_possible_cpu(cpu) {
+ struct cputopo_arm *cpu_topo = &(cpu_topology[cpu]);
+
+ cpu_topo->thread_id = -1;
+ cpu_topo->core_id = -1;
+ cpu_topo->socket_id = -1;
+ cpumask_clear(&cpu_topo->core_sibling);
+ cpumask_clear(&cpu_topo->thread_sibling);
+ }
+ smp_wmb();
+}
--
1.8.5.2
^ permalink raw reply related [flat|nested] 42+ 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; 42+ 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] 42+ 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; 42+ 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] 42+ 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; 42+ 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] 42+ messages in thread
* [PATCH 1/4] arm64: topology: Implement basic CPU topology support
@ 2014-01-08 17:10 Mark Brown
2014-01-08 17:10 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
0 siblings, 1 reply; 42+ 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 basic CPU topology support to arm64, based on the existing pre-v8
code and some work done by Mark Hambleton. This patch does not
implement any topology discovery support since that should be based on
information from firmware, it merely implements the scaffolding for
integration of topology support in the architecture.
The goal is to separate the architecture hookup for providing topology
information from the DT parsing in order to ease review and avoid
blocking the architecture code (which will be built on by other work)
with the DT code review by providing something something simple
and basic.
A following patch will implement support for parsing the DT topology
bindings for ARM, similar patches will be needed for ACPI.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/Kconfig | 24 ++++++++++
arch/arm64/include/asm/topology.h | 39 +++++++++++++++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/smp.c | 12 +++++
arch/arm64/kernel/topology.c | 92 +++++++++++++++++++++++++++++++++++++++
5 files changed, 168 insertions(+)
create mode 100644 arch/arm64/include/asm/topology.h
create mode 100644 arch/arm64/kernel/topology.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 249acb9da4e3..a53514cdf39c 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -162,6 +162,30 @@ config SMP
If you don't know what to do here, say N.
+config ARM_CPU_TOPOLOGY
+ bool "Support CPU topology definition"
+ depends on SMP
+ default y
+ help
+ Support CPU topology definition, based on configuration
+ provided by the firmware.
+
+config SCHED_MC
+ bool "Multi-core scheduler support"
+ depends on ARM_CPU_TOPOLOGY
+ help
+ Multi-core scheduler support improves the CPU scheduler's decision
+ making when dealing with multi-core CPU chips at a cost of slightly
+ increased overhead in some places. If unsure say N here.
+
+config SCHED_SMT
+ bool "SMT scheduler support"
+ depends on ARM_CPU_TOPOLOGY
+ help
+ Improves the CPU scheduler's decision making when dealing with
+ MultiThreading at a cost of slightly increased overhead in some
+ places. If unsure say N here.
+
config NR_CPUS
int "Maximum number of CPUs (2-32)"
range 2 32
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
new file mode 100644
index 000000000000..58b8b84adcd2
--- /dev/null
+++ b/arch/arm64/include/asm/topology.h
@@ -0,0 +1,39 @@
+#ifndef _ASM_ARM_TOPOLOGY_H
+#define _ASM_ARM_TOPOLOGY_H
+
+#ifdef CONFIG_ARM_CPU_TOPOLOGY
+
+#include <linux/cpumask.h>
+
+struct cputopo_arm {
+ int thread_id;
+ int core_id;
+ int socket_id;
+ cpumask_t thread_sibling;
+ cpumask_t core_sibling;
+};
+
+extern struct cputopo_arm cpu_topology[NR_CPUS];
+
+#define topology_physical_package_id(cpu) (cpu_topology[cpu].socket_id)
+#define topology_core_id(cpu) (cpu_topology[cpu].core_id)
+#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling)
+#define topology_thread_cpumask(cpu) (&cpu_topology[cpu].thread_sibling)
+
+#define mc_capable() (cpu_topology[0].socket_id != -1)
+#define smt_capable() (cpu_topology[0].thread_id != -1)
+
+void init_cpu_topology(void);
+void store_cpu_topology(unsigned int cpuid);
+const struct cpumask *cpu_coregroup_mask(int cpu);
+
+#else
+
+static inline void init_cpu_topology(void) { }
+static inline void store_cpu_topology(unsigned int cpuid) { }
+
+#endif
+
+#include <asm-generic/topology.h>
+
+#endif /* _ASM_ARM_TOPOLOGY_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 1cd339d5037b..b2fe6ba97080 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -19,6 +19,7 @@ arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
+arm64-obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 1b7617ab499b..40e20efc13e6 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -114,6 +114,11 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
return ret;
}
+static void __cpuinit smp_store_cpu_info(unsigned int cpuid)
+{
+ store_cpu_topology(cpuid);
+}
+
/*
* This is the secondary CPU boot entry. We're using this CPUs
* idle thread stack, but a set of temporary page tables.
@@ -152,6 +157,8 @@ asmlinkage void secondary_start_kernel(void)
*/
notify_cpu_starting(cpu);
+ smp_store_cpu_info(cpu);
+
/*
* OK, now it's safe to let the boot CPU continue. Wait for
* the CPU migration code to notice that the CPU is online
@@ -391,6 +398,11 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
int err;
unsigned int cpu, ncores = num_possible_cpus();
+ init_cpu_topology();
+
+ smp_store_cpu_info(smp_processor_id());
+
+
/*
* are we trying to boot more cores than exist?
*/
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
new file mode 100644
index 000000000000..853544f30a8b
--- /dev/null
+++ b/arch/arm64/kernel/topology.c
@@ -0,0 +1,92 @@
+/*
+ * arch/arm64/kernel/topology.c
+ *
+ * Copyright (C) 2011,2013 Linaro Limited.
+ *
+ * Based on the arm32 version written by Vincent Guittot in turn based on
+ * arch/sh/kernel/topology.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/node.h>
+#include <linux/nodemask.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <asm/topology.h>
+
+/*
+ * cpu topology table
+ */
+struct cputopo_arm cpu_topology[NR_CPUS];
+EXPORT_SYMBOL_GPL(cpu_topology);
+
+const struct cpumask *cpu_coregroup_mask(int cpu)
+{
+ return &cpu_topology[cpu].core_sibling;
+}
+
+static void update_siblings_masks(unsigned int cpuid)
+{
+ struct cputopo_arm *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
+ int cpu;
+
+ /* update core and thread sibling masks */
+ for_each_possible_cpu(cpu) {
+ cpu_topo = &cpu_topology[cpu];
+
+ if (cpuid_topo->socket_id != cpu_topo->socket_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
+
+ if (cpuid_topo->core_id != cpu_topo->core_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
+ }
+ smp_wmb();
+}
+
+void store_cpu_topology(unsigned int cpuid)
+{
+ struct cputopo_arm *cpuid_topo = &cpu_topology[cpuid];
+
+ /* DT should have been parsed by the time we get here */
+ if (cpuid_topo->core_id == -1)
+ pr_info("CPU%u: No topology information configured\n", cpuid);
+ else
+ 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)
+{
+ unsigned int cpu;
+
+ /* init core mask and power*/
+ for_each_possible_cpu(cpu) {
+ struct cputopo_arm *cpu_topo = &(cpu_topology[cpu]);
+
+ cpu_topo->thread_id = -1;
+ cpu_topo->core_id = -1;
+ cpu_topo->socket_id = -1;
+ cpumask_clear(&cpu_topo->core_sibling);
+ cpumask_clear(&cpu_topo->thread_sibling);
+ }
+ smp_wmb();
+}
--
1.8.5.2
^ permalink raw reply related [flat|nested] 42+ 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; 42+ 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] 42+ 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; 42+ 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] 42+ 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; 42+ 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] 42+ messages in thread
* [PATCH 1/4] arm64: topology: Implement basic CPU topology support
@ 2013-12-16 16:49 Mark Brown
2013-12-16 16:49 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
0 siblings, 1 reply; 42+ 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 basic CPU topology support to arm64, based on the existing pre-v8
code and some work done by Mark Hambleton. This patch does not
implement any topology discovery support since that should be based on
information from firmware, it merely implements the scaffolding for
integration of topology support in the architecture.
The goal is to separate the architecture hookup for providing topology
information from the DT parsing in order to ease review and avoid
blocking the architecture code (which will be built on by other work)
with the DT code review by providing something something simple
and basic.
A following patch will implement support for parsing the DT topology
bindings for ARM, similar patches will be needed for ACPI.
Signed-off-by: Mark Brown <broonie@linaro.org>
---
arch/arm64/Kconfig | 24 ++++++++++
arch/arm64/include/asm/topology.h | 39 ++++++++++++++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/smp.c | 12 +++++
arch/arm64/kernel/topology.c | 95 +++++++++++++++++++++++++++++++++++++++
5 files changed, 171 insertions(+)
create mode 100644 arch/arm64/include/asm/topology.h
create mode 100644 arch/arm64/kernel/topology.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 6d4dd22ee4b7..00fcd490b3be 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -154,6 +154,30 @@ config SMP
If you don't know what to do here, say N.
+config ARM_CPU_TOPOLOGY
+ bool "Support CPU topology definition"
+ depends on SMP
+ default y
+ help
+ Support CPU topology definition, based on configuration
+ provided by the firmware.
+
+config SCHED_MC
+ bool "Multi-core scheduler support"
+ depends on ARM_CPU_TOPOLOGY
+ help
+ Multi-core scheduler support improves the CPU scheduler's decision
+ making when dealing with multi-core CPU chips at a cost of slightly
+ increased overhead in some places. If unsure say N here.
+
+config SCHED_SMT
+ bool "SMT scheduler support"
+ depends on ARM_CPU_TOPOLOGY
+ help
+ Improves the CPU scheduler's decision making when dealing with
+ MultiThreading at a cost of slightly increased overhead in some
+ places. If unsure say N here.
+
config NR_CPUS
int "Maximum number of CPUs (2-32)"
range 2 32
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
new file mode 100644
index 000000000000..58b8b84adcd2
--- /dev/null
+++ b/arch/arm64/include/asm/topology.h
@@ -0,0 +1,39 @@
+#ifndef _ASM_ARM_TOPOLOGY_H
+#define _ASM_ARM_TOPOLOGY_H
+
+#ifdef CONFIG_ARM_CPU_TOPOLOGY
+
+#include <linux/cpumask.h>
+
+struct cputopo_arm {
+ int thread_id;
+ int core_id;
+ int socket_id;
+ cpumask_t thread_sibling;
+ cpumask_t core_sibling;
+};
+
+extern struct cputopo_arm cpu_topology[NR_CPUS];
+
+#define topology_physical_package_id(cpu) (cpu_topology[cpu].socket_id)
+#define topology_core_id(cpu) (cpu_topology[cpu].core_id)
+#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling)
+#define topology_thread_cpumask(cpu) (&cpu_topology[cpu].thread_sibling)
+
+#define mc_capable() (cpu_topology[0].socket_id != -1)
+#define smt_capable() (cpu_topology[0].thread_id != -1)
+
+void init_cpu_topology(void);
+void store_cpu_topology(unsigned int cpuid);
+const struct cpumask *cpu_coregroup_mask(int cpu);
+
+#else
+
+static inline void init_cpu_topology(void) { }
+static inline void store_cpu_topology(unsigned int cpuid) { }
+
+#endif
+
+#include <asm-generic/topology.h>
+
+#endif /* _ASM_ARM_TOPOLOGY_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 5ba2fd43a75b..2d145e38ad49 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -18,6 +18,7 @@ arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+arm64-obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index a0c2ca602cf8..0271fbde5363 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -113,6 +113,11 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
return ret;
}
+static void __cpuinit smp_store_cpu_info(unsigned int cpuid)
+{
+ store_cpu_topology(cpuid);
+}
+
/*
* This is the secondary CPU boot entry. We're using this CPUs
* idle thread stack, but a set of temporary page tables.
@@ -150,6 +155,8 @@ asmlinkage void secondary_start_kernel(void)
*/
notify_cpu_starting(cpu);
+ smp_store_cpu_info(cpu);
+
/*
* OK, now it's safe to let the boot CPU continue. Wait for
* the CPU migration code to notice that the CPU is online
@@ -388,6 +395,11 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
int err;
unsigned int cpu, ncores = num_possible_cpus();
+ init_cpu_topology();
+
+ smp_store_cpu_info(smp_processor_id());
+
+
/*
* are we trying to boot more cores than exist?
*/
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
new file mode 100644
index 000000000000..b078afa6958d
--- /dev/null
+++ b/arch/arm64/kernel/topology.c
@@ -0,0 +1,95 @@
+/*
+ * arch/arm64/kernel/topology.c
+ *
+ * Copyright (C) 2011,2013 Linaro Limited.
+ * Written by: Vincent Guittot
+ *
+ * based on arch/sh/kernel/topology.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/node.h>
+#include <linux/nodemask.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <asm/cputype.h>
+#include <asm/smp_plat.h>
+#include <asm/topology.h>
+
+/*
+ * cpu topology table
+ */
+struct cputopo_arm cpu_topology[NR_CPUS];
+EXPORT_SYMBOL_GPL(cpu_topology);
+
+const struct cpumask *cpu_coregroup_mask(int cpu)
+{
+ return &cpu_topology[cpu].core_sibling;
+}
+
+static void update_siblings_masks(unsigned int cpuid)
+{
+ struct cputopo_arm *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
+ int cpu;
+
+ /* update core and thread sibling masks */
+ for_each_possible_cpu(cpu) {
+ cpu_topo = &cpu_topology[cpu];
+
+ if (cpuid_topo->socket_id != cpu_topo->socket_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
+
+ if (cpuid_topo->core_id != cpu_topo->core_id)
+ continue;
+
+ cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling);
+ }
+ smp_wmb();
+}
+
+void store_cpu_topology(unsigned int cpuid)
+{
+ struct cputopo_arm *cpuid_topo = &cpu_topology[cpuid];
+
+ /* DT should have been parsed by the time we get here */
+ if (cpuid_topo->core_id == -1)
+ pr_info("CPU%u: No topology information configured\n", cpuid);
+ else
+ 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)
+{
+ unsigned int cpu;
+
+ /* init core mask and power*/
+ for_each_possible_cpu(cpu) {
+ struct cputopo_arm *cpu_topo = &(cpu_topology[cpu]);
+
+ cpu_topo->thread_id = -1;
+ cpu_topo->core_id = -1;
+ cpu_topo->socket_id = -1;
+ cpumask_clear(&cpu_topo->core_sibling);
+ cpumask_clear(&cpu_topo->thread_sibling);
+ }
+ smp_wmb();
+}
--
1.8.5.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [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; 42+ 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] 42+ 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; 42+ 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] 42+ 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; 42+ 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] 42+ messages in thread
end of thread, other threads:[~2014-04-24 14:48 UTC | newest]
Thread overview: 42+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-12-19 20:06 [PATCH 0/4] arm64 topology support Mark Brown
2013-12-19 20:06 ` [PATCH 1/4] arm64: topology: Implement basic CPU " Mark Brown
2013-12-19 20:06 ` [PATCH 2/4] arm64: topology: Add support for topology DT bindings Mark Brown
2013-12-19 20:06 ` [PATCH 3/4] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
2014-01-07 13:05 ` Lorenzo Pieralisi
2014-01-07 13:38 ` Mark Brown
2014-01-07 14:29 ` Lorenzo Pieralisi
2014-01-07 15:06 ` Mark Brown
2014-01-07 17:56 ` Lorenzo Pieralisi
2014-01-07 18:02 ` Mark Brown
2013-12-19 20:06 ` [PATCH 4/4] arm64: topology: Provide relative power numbers for cores Mark Brown
2013-12-20 18:07 ` [PATCH 0/4] arm64 topology support Catalin Marinas
2014-01-07 18:05 ` Lorenzo Pieralisi
2014-01-07 18:23 ` Mark Brown
2014-01-07 18:46 ` Mark Brown
2014-01-08 10:15 ` Lorenzo Pieralisi
2014-01-08 10:24 ` Lorenzo Pieralisi
2014-01-08 11:58 ` 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-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-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-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).