All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] arm64: topology: Implement basic CPU topology support
@ 2013-12-11 20:00 Mark Brown
  2013-12-11 20:00 ` [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Mark Brown @ 2013-12-11 20:00 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                |   8 +++
 arch/arm64/include/asm/topology.h |  42 +++++++++++++
 arch/arm64/kernel/Makefile        |   1 +
 arch/arm64/kernel/smp.c           |  12 ++++
 arch/arm64/kernel/topology.c      | 124 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 187 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..c0975fea9bfe 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -154,6 +154,14 @@ 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 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..611edefaeaf1
--- /dev/null
+++ b/arch/arm64/include/asm/topology.h
@@ -0,0 +1,42 @@
+#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);
+int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask);
+
+#else
+
+static inline void init_cpu_topology(void) { }
+static inline void store_cpu_topology(unsigned int cpuid) { }
+static inline int cluster_to_logical_mask(unsigned int socket_id,
+	cpumask_t *cluster_mask) { return -EINVAL; }
+
+#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..aae9d4d72328
--- /dev/null
+++ b/arch/arm64/kernel/topology.c
@@ -0,0 +1,124 @@
+/*
+ * 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);
+}
+
+
+/*
+ * cluster_to_logical_mask - return cpu logical mask of CPUs in a cluster
+ * @socket_id:		cluster HW identifier
+ * @cluster_mask:	the cpumask location to be initialized, modified by the
+ *			function only if return value == 0
+ *
+ * Return:
+ *
+ * 0 on success
+ * -EINVAL if cluster_mask is NULL or there is no record matching socket_id
+ */
+int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask)
+{
+	int cpu;
+
+	if (!cluster_mask)
+		return -EINVAL;
+
+	for_each_online_cpu(cpu) {
+		if (socket_id == topology_physical_package_id(cpu)) {
+			cpumask_copy(cluster_mask, topology_core_cpumask(cpu));
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+/*
+ * 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] 12+ messages in thread

* [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores
  2013-12-11 20:00 [PATCH 1/3] arm64: topology: Implement basic CPU topology support Mark Brown
@ 2013-12-11 20:00 ` Mark Brown
  2013-12-12 11:35   ` Morten Rasmussen
  2013-12-11 20:00 ` [PATCH 3/3] arm64: topology: Add support for topology DT bindings Mark Brown
  2013-12-12  7:13 ` [PATCH 1/3] arm64: topology: Implement basic CPU topology support Hanjun Guo
  2 siblings, 1 reply; 12+ messages in thread
From: Mark Brown @ 2013-12-11 20:00 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.

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.

Signed-off-by: Mark Brown <broonie@linaro.org>
---
 arch/arm64/kernel/topology.c | 164 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 164 insertions(+)

diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index aae9d4d72328..c88970b1b863 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>
 
@@ -26,6 +27,163 @@
 #include <asm/topology.h>
 
 /*
+ * cpu power scale management
+ */
+
+/*
+ * 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
+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[] = {
+	{ "arm,cortex-a57", 3891 },
+	{ "arm,cortex-a53", 2048 },
+	{ 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 = NULL;
+	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);
+
+	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
+
+/*
  * cpu topology table
  */
 struct cputopo_arm cpu_topology[NR_CPUS];
@@ -71,6 +229,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);
 }
 
 
@@ -119,6 +279,10 @@ 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);
 	}
 	smp_wmb();
+
+	parse_dt_topology();
 }
-- 
1.8.5.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 3/3] arm64: topology: Add support for topology DT bindings
  2013-12-11 20:00 [PATCH 1/3] arm64: topology: Implement basic CPU topology support Mark Brown
  2013-12-11 20:00 ` [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
@ 2013-12-11 20:00 ` Mark Brown
  2013-12-12  7:13 ` [PATCH 1/3] arm64: topology: Implement basic CPU topology support Hanjun Guo
  2 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2013-12-11 20:00 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 | 130 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 130 insertions(+)

diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index c88970b1b863..83d6919d4e0a 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -79,6 +79,121 @@ static unsigned long *__cpu_capacity;
 #define cpu_capacity(cpu)	__cpu_capacity[cpu]
 
 static unsigned long middle_capacity = 1;
+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++;
+}
 
 /*
  * Iterate all CPUs' descriptor in DT and compute the efficiency
@@ -100,6 +215,21 @@ static void __init parse_dt_topology(void)
 	alloc_size = nr_cpu_ids * sizeof(*__cpu_capacity);
 	__cpu_capacity = kzalloc(alloc_size, GFP_NOWAIT);
 
+	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);
+
 	for_each_possible_cpu(cpu) {
 		const u32 *rate;
 		int len;
-- 
1.8.5.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 1/3] arm64: topology: Implement basic CPU topology support
  2013-12-11 20:00 [PATCH 1/3] arm64: topology: Implement basic CPU topology support Mark Brown
  2013-12-11 20:00 ` [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
  2013-12-11 20:00 ` [PATCH 3/3] arm64: topology: Add support for topology DT bindings Mark Brown
@ 2013-12-12  7:13 ` Hanjun Guo
  2013-12-12 10:22   ` Mark Brown
  2 siblings, 1 reply; 12+ messages in thread
From: Hanjun Guo @ 2013-12-12  7:13 UTC (permalink / raw)
  To: linux-arm-kernel

On 2013-12-12 4:00, Mark Brown wrote:
> 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                |   8 +++
>  arch/arm64/include/asm/topology.h |  42 +++++++++++++
>  arch/arm64/kernel/Makefile        |   1 +
>  arch/arm64/kernel/smp.c           |  12 ++++
>  arch/arm64/kernel/topology.c      | 124 ++++++++++++++++++++++++++++++++++++++
>  5 files changed, 187 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..c0975fea9bfe 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -154,6 +154,14 @@ 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.
> +

The scheduler already used topology and cache sharing when
CONFIG_SCHED_MC and/or CONFIG_SCHED_SMT are enable. can you
add these configs for arm64 so the scheduler can use it?
something like:

+config SCHED_MC
+       bool "Multi-core scheduler support"
+       depends on ARM64_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 ARM64_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.
+

Thanks
Hanjun

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/3] arm64: topology: Implement basic CPU topology support
  2013-12-12  7:13 ` [PATCH 1/3] arm64: topology: Implement basic CPU topology support Hanjun Guo
@ 2013-12-12 10:22   ` Mark Brown
  0 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2013-12-12 10:22 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Dec 12, 2013 at 03:13:05PM +0800, Hanjun Guo wrote:

> The scheduler already used topology and cache sharing when
> CONFIG_SCHED_MC and/or CONFIG_SCHED_SMT are enable. can you
> add these configs for arm64 so the scheduler can use it?
> something like:

Right, yes - these should be added.  They got mistakenly included in the
patch for GTS which is obviously out of tree.
-------------- 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/20131212/bf0e1d68/attachment.sig>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores
  2013-12-11 20:00 ` [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
@ 2013-12-12 11:35   ` Morten Rasmussen
  2013-12-12 12:06     ` Mark Brown
  0 siblings, 1 reply; 12+ messages in thread
From: Morten Rasmussen @ 2013-12-12 11:35 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Dec 11, 2013 at 08:00:28PM +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.

Setting the relative performance through cpu_power in it current form
eads to sub-optimal scheduling on big.LITTLE in common scenarios.

I know that this is how it is currently done for ARMv7 and one could
argue that we should do the same for ARMv8 until we have a better
solution. I just want to highlight that setting cpu_power this way is
not generally the right thing to do for big.LITTLE. It will have to be
fixed eventually.

I fully agree that we need to pass relative performance information to
the scheduler, but it isn't really ready for it yet.

It doesn't harm when using the ARM big.LITTLE reference patches as they
ignore cpu_power for load-balancing. However, it leads to
under-utilization of little cpus with the mainline scheduler.

> 
> 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.

As said above, it needs to be fixed in the scheduler.

Morten

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores
  2013-12-12 11:35   ` Morten Rasmussen
@ 2013-12-12 12:06     ` Mark Brown
  2013-12-12 13:36       ` Morten Rasmussen
  0 siblings, 1 reply; 12+ messages in thread
From: Mark Brown @ 2013-12-12 12:06 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Dec 12, 2013 at 11:35:12AM +0000, Morten Rasmussen wrote:

> I know that this is how it is currently done for ARMv7 and one could
> argue that we should do the same for ARMv8 until we have a better
> solution. I just want to highlight that setting cpu_power this way is
> not generally the right thing to do for big.LITTLE. It will have to be
> fixed eventually.

Right, there's a good solid reason for all the work on the scheduler.  I
definitely think we ought to be following the same approach on both
ARMv7 and ARMv8 to avoid confusion between people based on the platform
they're working on.

If you're saying that the current ARMv7 code is always worse than doing
nothing then clearly we ought to be removing that code from ARMv7 rather
than hurting performance.  I'd been under the impression that what we
had there was not ideal but better than nothing in mainline rather than
actively harmful.
-------------- 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/20131212/853b289e/attachment.sig>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores
  2013-12-12 12:06     ` Mark Brown
@ 2013-12-12 13:36       ` Morten Rasmussen
  2013-12-12 17:39         ` Catalin Marinas
  0 siblings, 1 reply; 12+ messages in thread
From: Morten Rasmussen @ 2013-12-12 13:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Dec 12, 2013 at 12:06:49PM +0000, Mark Brown wrote:
> On Thu, Dec 12, 2013 at 11:35:12AM +0000, Morten Rasmussen wrote:
> 
> > I know that this is how it is currently done for ARMv7 and one could
> > argue that we should do the same for ARMv8 until we have a better
> > solution. I just want to highlight that setting cpu_power this way is
> > not generally the right thing to do for big.LITTLE. It will have to be
> > fixed eventually.
> 
> Right, there's a good solid reason for all the work on the scheduler.  I
> definitely think we ought to be following the same approach on both
> ARMv7 and ARMv8 to avoid confusion between people based on the platform
> they're working on.

That is fair enough.

> 
> If you're saying that the current ARMv7 code is always worse than doing
> nothing then clearly we ought to be removing that code from ARMv7 rather
> than hurting performance.  I'd been under the impression that what we
> had there was not ideal but better than nothing in mainline rather than
> actively harmful.

For some scenarios it might be better to set cpu_power to reflect the
relative performance, for others it is worse due to the way cpu_power
is currently used in the scheduler.

Setting cpu_power as it is done for v7 may bias the scheduler to put
heavier tasks on big cpus and will generally put more task on big cpus,
which is a good thing for some scenarios. However, if you have parallel
workloads that spawn a worker thread for each cpu and does dynamic work
distribution in user-space (OpenMP applications for example), then
setting cpu_power will put two worker threads on some big cpus and leave
some little cpus idle resulting in slower completion time. It happens on
TC2.

We need the code (or something very similar) later when the scheduler
has been fixed. For v7 it has been left in waiting for that fix, it
doesn't harm when using the reference big.LITTLE patches. We can do the
same for v8 to avoid confusion.

Morten

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores
  2013-12-12 13:36       ` Morten Rasmussen
@ 2013-12-12 17:39         ` Catalin Marinas
  2013-12-12 18:06           ` Mark Brown
  0 siblings, 1 reply; 12+ messages in thread
From: Catalin Marinas @ 2013-12-12 17:39 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Dec 12, 2013 at 01:36:43PM +0000, Morten Rasmussen wrote:
> On Thu, Dec 12, 2013 at 12:06:49PM +0000, Mark Brown wrote:
> > On Thu, Dec 12, 2013 at 11:35:12AM +0000, Morten Rasmussen wrote:
> > 
> > > I know that this is how it is currently done for ARMv7 and one could
> > > argue that we should do the same for ARMv8 until we have a better
> > > solution. I just want to highlight that setting cpu_power this way is
> > > not generally the right thing to do for big.LITTLE. It will have to be
> > > fixed eventually.
> > 
> > Right, there's a good solid reason for all the work on the scheduler.  I
> > definitely think we ought to be following the same approach on both
> > ARMv7 and ARMv8 to avoid confusion between people based on the platform
> > they're working on.
[...]
> > If you're saying that the current ARMv7 code is always worse than doing
> > nothing then clearly we ought to be removing that code from ARMv7 rather
> > than hurting performance.  I'd been under the impression that what we
> > had there was not ideal but better than nothing in mainline rather than
> > actively harmful.
> 
> For some scenarios it might be better to set cpu_power to reflect the
> relative performance, for others it is worse due to the way cpu_power
> is currently used in the scheduler.
> 
> Setting cpu_power as it is done for v7 may bias the scheduler to put
> heavier tasks on big cpus and will generally put more task on big cpus,
> which is a good thing for some scenarios. However, if you have parallel
> workloads that spawn a worker thread for each cpu and does dynamic work
> distribution in user-space (OpenMP applications for example), then
> setting cpu_power will put two worker threads on some big cpus and leave
> some little cpus idle resulting in slower completion time. It happens on
> TC2.
> 
> We need the code (or something very similar) later when the scheduler
> has been fixed. For v7 it has been left in waiting for that fix, it
> doesn't harm when using the reference big.LITTLE patches. We can do the
> same for v8 to avoid confusion.

If we can't really guarantee the effect of this patch, I would rather
keep it in the LSK kernel only until the scheduler is fixed (can this be
treated as a performance issue independent of the power-aware
scheduling? We could get it merged quicker).

-- 
Catalin

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores
  2013-12-12 17:39         ` Catalin Marinas
@ 2013-12-12 18:06           ` Mark Brown
  0 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2013-12-12 18:06 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Dec 12, 2013 at 05:39:12PM +0000, Catalin Marinas wrote:

> If we can't really guarantee the effect of this patch, I would rather
> keep it in the LSK kernel only until the scheduler is fixed (can this be
> treated as a performance issue independent of the power-aware
> scheduling? We could get it merged quicker).

My understanding is that the behaviour is reasonably well understood,
it's just not great in all situations (hence all the energy aware
scheduler work) but then the default scheduler behaviour isn't that good
either and possibly worse (hence all the energy aware scheduler work).

I think if we're that worried about problems that might be caused by
doing this we should remove the equivalent ARMv7 code to keep the two in
parity in terms of behaviour - it's going to be enough work getting
big.LITTLE working well without introducing software only differences
between ARMv7 and ARMv8 big.LITTLE.  If you're dead set on that then I
can do patches for that.
-------------- 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/20131212/dc55693b/attachment-0001.sig>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores
  2014-03-19 18:02 [PATCH 1/3] arm64: topology: Add support for topology DT bindings Mark Brown
@ 2014-03-19 18:02 ` Mark Brown
  0 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2014-03-19 18:02 UTC (permalink / raw)
  To: linux-arm-kernel

From: Mark Brown <broonie@linaro.org>

In heterogeneous systems like big.LITTLE systems the scheduler will be
able to make better use of the available cores if we provide power numbers
to it indicating their relative performance. Do this by parsing the CPU
nodes in the DT.

This code currently has no effect as no information on the relative
performance of the cores is provided.

Signed-off-by: Mark Brown <broonie@linaro.org>
---
 arch/arm64/kernel/topology.c | 146 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 145 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 548f04572e26..6713c7de4be3 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -19,9 +19,33 @@
 #include <linux/nodemask.h>
 #include <linux/of.h>
 #include <linux/sched.h>
+#include <linux/slab.h>
 
 #include <asm/topology.h>
 
+/*
+ * cpu power table
+ * This per cpu data structure describes the relative capacity of each core.
+ * On a heteregenous system, cores don't have the same computation capacity
+ * and we reflect that difference in the cpu_power field so the scheduler can
+ * take this difference into account during load balance. A per cpu structure
+ * is preferred because each CPU updates its own cpu_power field during the
+ * load balance except for idle cores. One idle core is selected to run the
+ * rebalance_domains for all idle cores and the cpu_power can be updated
+ * during this sequence.
+ */
+static DEFINE_PER_CPU(unsigned long, cpu_scale);
+
+unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu)
+{
+	return per_cpu(cpu_scale, cpu);
+}
+
+static void set_power_scale(unsigned int cpu, unsigned long power)
+{
+	per_cpu(cpu_scale, cpu) = power;
+}
+
 #ifdef CONFIG_OF
 static int __init get_cpu_for_node(struct device_node *node)
 {
@@ -150,9 +174,49 @@ static int __init parse_cluster(struct device_node *cluster, int depth)
 	return 0;
 }
 
+struct cpu_efficiency {
+	const char *compatible;
+	unsigned long efficiency;
+};
+
+/*
+ * Table of relative efficiency of each processors
+ * The efficiency value must fit in 20bit and the final
+ * cpu_scale value must be in the range
+ *   0 < cpu_scale < 3*SCHED_POWER_SCALE/2
+ * in order to return@most 1 when DIV_ROUND_CLOSEST
+ * is used to compute the capacity of a CPU.
+ * Processors that are not defined in the table,
+ * use the default SCHED_POWER_SCALE value for cpu_scale.
+ */
+static const struct cpu_efficiency table_efficiency[] = {
+	{ NULL, },
+};
+
+static unsigned long *__cpu_capacity;
+#define cpu_capacity(cpu)	__cpu_capacity[cpu]
+
+static unsigned long middle_capacity = 1;
+
+/*
+ * Iterate all CPUs' descriptor in DT and compute the efficiency
+ * (as per table_efficiency). Also calculate a middle efficiency
+ * as close as possible to  (max{eff_i} - min{eff_i}) / 2
+ * This is later used to scale the cpu_power field such that an
+ * 'average' CPU is of middle power. Also see the comments near
+ * table_efficiency[] and update_cpu_power().
+ */
 static int __init parse_dt_topology(void)
 {
+	const struct cpu_efficiency *cpu_eff;
 	struct device_node *cn;
+	unsigned long min_capacity = ULONG_MAX;
+	unsigned long max_capacity = 0;
+	unsigned long capacity = 0;
+	int cpu, ret;
+
+	__cpu_capacity = kcalloc(nr_cpu_ids, sizeof(*__cpu_capacity),
+				 GFP_NOWAIT);
 
 	cn = of_find_node_by_path("/cpus");
 	if (!cn) {
@@ -167,11 +231,88 @@ static int __init parse_dt_topology(void)
 	cn = of_get_child_by_name(cn, "cpu-map");
 	if (!cn)
 		return 0;
-	return parse_cluster(cn, 0);
+
+	ret = parse_cluster(cn, 0);
+	if (ret != 0)
+		return ret;
+
+	for_each_possible_cpu(cpu) {
+		const u32 *rate;
+		int len;
+
+		/* Too early to use cpu->of_node */
+		cn = of_get_cpu_node(cpu, NULL);
+		if (!cn) {
+			pr_err("Missing device node for CPU %d\n", cpu);
+			continue;
+		}
+
+		for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++)
+			if (of_device_is_compatible(cn, cpu_eff->compatible))
+				break;
+
+		if (cpu_eff->compatible == NULL) {
+			pr_warn("%s: Unknown CPU type\n", cn->full_name);
+			continue;
+		}
+
+		rate = of_get_property(cn, "clock-frequency", &len);
+		if (!rate || len != 4) {
+			pr_err("%s: Missing clock-frequency property\n",
+				cn->full_name);
+			continue;
+		}
+
+		capacity = ((be32_to_cpup(rate)) >> 20) * cpu_eff->efficiency;
+
+		/* Save min capacity of the system */
+		if (capacity < min_capacity)
+			min_capacity = capacity;
+
+		/* Save max capacity of the system */
+		if (capacity > max_capacity)
+			max_capacity = capacity;
+
+		cpu_capacity(cpu) = capacity;
+	}
+
+	/* If min and max capacities are equal we bypass the update of the
+	 * cpu_scale because all CPUs have the same capacity. Otherwise, we
+	 * compute a middle_capacity factor that will ensure that the capacity
+	 * of an 'average' CPU of the system will be as close as possible to
+	 * SCHED_POWER_SCALE, which is the default value, but with the
+	 * constraint explained near table_efficiency[].
+	 */
+	if (min_capacity == max_capacity)
+		return 0;
+	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 int parse_dt_topology(void) { return 0; }
+static inline void update_cpu_power(unsigned int cpuid) {}
 #endif
 
 /*
@@ -225,6 +366,7 @@ static void update_siblings_masks(unsigned int cpuid)
 void store_cpu_topology(unsigned int cpuid)
 {
 	update_siblings_masks(cpuid);
+	update_cpu_power(cpuid);
 }
 
 static void __init reset_cpu_topology(void)
@@ -239,6 +381,8 @@ static void __init reset_cpu_topology(void)
 		cpu_topo->cluster_id = -1;
 		cpumask_clear(&cpu_topo->core_sibling);
 		cpumask_clear(&cpu_topo->thread_sibling);
+
+		set_power_scale(cpu, SCHED_POWER_SCALE);
 	}
 }
 
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores
  2014-03-05  8:59 [PATCH 1/3] arm64: topology: Add support for topology DT bindings Mark Brown
@ 2014-03-05  8:59 ` Mark Brown
  0 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2014-03-05  8:59 UTC (permalink / raw)
  To: linux-arm-kernel

From: Mark Brown <broonie@linaro.org>

In heterogeneous systems like big.LITTLE systems the scheduler will be
able to make better use of the available cores if we provide power numbers
to it indicating their relative performance. Do this by parsing the CPU
nodes in the DT.

This code currently has no effect as no information on the relative
performance of the cores is provided.

Signed-off-by: Mark Brown <broonie@linaro.org>
---
 arch/arm64/kernel/topology.c | 146 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 145 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 8e0f29a..3a80979 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -19,9 +19,33 @@
 #include <linux/nodemask.h>
 #include <linux/of.h>
 #include <linux/sched.h>
+#include <linux/slab.h>
 
 #include <asm/topology.h>
 
+/*
+ * cpu power table
+ * This per cpu data structure describes the relative capacity of each core.
+ * On a heteregenous system, cores don't have the same computation capacity
+ * and we reflect that difference in the cpu_power field so the scheduler can
+ * take this difference into account during load balance. A per cpu structure
+ * is preferred because each CPU updates its own cpu_power field during the
+ * load balance except for idle cores. One idle core is selected to run the
+ * rebalance_domains for all idle cores and the cpu_power can be updated
+ * during this sequence.
+ */
+static DEFINE_PER_CPU(unsigned long, cpu_scale);
+
+unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu)
+{
+	return per_cpu(cpu_scale, cpu);
+}
+
+static void set_power_scale(unsigned int cpu, unsigned long power)
+{
+	per_cpu(cpu_scale, cpu) = power;
+}
+
 #ifdef CONFIG_OF
 static int __init get_cpu_for_node(struct device_node *node)
 {
@@ -148,9 +172,49 @@ static int __init parse_cluster(struct device_node *cluster, int depth)
 	return 0;
 }
 
+struct cpu_efficiency {
+	const char *compatible;
+	unsigned long efficiency;
+};
+
+/*
+ * Table of relative efficiency of each processors
+ * The efficiency value must fit in 20bit and the final
+ * cpu_scale value must be in the range
+ *   0 < cpu_scale < 3*SCHED_POWER_SCALE/2
+ * in order to return@most 1 when DIV_ROUND_CLOSEST
+ * is used to compute the capacity of a CPU.
+ * Processors that are not defined in the table,
+ * use the default SCHED_POWER_SCALE value for cpu_scale.
+ */
+static const struct cpu_efficiency table_efficiency[] = {
+	{ NULL, },
+};
+
+static unsigned long *__cpu_capacity;
+#define cpu_capacity(cpu)	__cpu_capacity[cpu]
+
+static unsigned long middle_capacity = 1;
+
+/*
+ * Iterate all CPUs' descriptor in DT and compute the efficiency
+ * (as per table_efficiency). Also calculate a middle efficiency
+ * as close as possible to  (max{eff_i} - min{eff_i}) / 2
+ * This is later used to scale the cpu_power field such that an
+ * 'average' CPU is of middle power. Also see the comments near
+ * table_efficiency[] and update_cpu_power().
+ */
 static int __init parse_dt_topology(void)
 {
+	const struct cpu_efficiency *cpu_eff;
 	struct device_node *cn;
+	unsigned long min_capacity = ULONG_MAX;
+	unsigned long max_capacity = 0;
+	unsigned long capacity = 0;
+	int cpu, ret;
+
+	__cpu_capacity = kcalloc(nr_cpu_ids, sizeof(*__cpu_capacity),
+				 GFP_NOWAIT);
 
 	cn = of_find_node_by_path("/cpus");
 	if (!cn) {
@@ -165,11 +229,88 @@ static int __init parse_dt_topology(void)
 	cn = of_get_child_by_name(cn, "cpu-map");
 	if (!cn)
 		return 0;
-	return parse_cluster(cn, 0);
+
+	ret = parse_cluster(cn, 0);
+	if (ret != 0)
+		return ret;
+
+	for_each_possible_cpu(cpu) {
+		const u32 *rate;
+		int len;
+
+		/* Too early to use cpu->of_node */
+		cn = of_get_cpu_node(cpu, NULL);
+		if (!cn) {
+			pr_err("Missing device node for CPU %d\n", cpu);
+			continue;
+		}
+
+		for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++)
+			if (of_device_is_compatible(cn, cpu_eff->compatible))
+				break;
+
+		if (cpu_eff->compatible == NULL) {
+			pr_warn("%s: Unknown CPU type\n", cn->full_name);
+			continue;
+		}
+
+		rate = of_get_property(cn, "clock-frequency", &len);
+		if (!rate || len != 4) {
+			pr_err("%s: Missing clock-frequency property\n",
+				cn->full_name);
+			continue;
+		}
+
+		capacity = ((be32_to_cpup(rate)) >> 20) * cpu_eff->efficiency;
+
+		/* Save min capacity of the system */
+		if (capacity < min_capacity)
+			min_capacity = capacity;
+
+		/* Save max capacity of the system */
+		if (capacity > max_capacity)
+			max_capacity = capacity;
+
+		cpu_capacity(cpu) = capacity;
+	}
+
+	/* If min and max capacities are equal we bypass the update of the
+	 * cpu_scale because all CPUs have the same capacity. Otherwise, we
+	 * compute a middle_capacity factor that will ensure that the capacity
+	 * of an 'average' CPU of the system will be as close as possible to
+	 * SCHED_POWER_SCALE, which is the default value, but with the
+	 * constraint explained near table_efficiency[].
+	 */
+	if (min_capacity == max_capacity)
+		return 0;
+	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 int parse_dt_topology(void) { return 0; }
+static inline void update_cpu_power(unsigned int cpuid) {}
 #endif
 
 /*
@@ -223,6 +364,7 @@ static void update_siblings_masks(unsigned int cpuid)
 void store_cpu_topology(unsigned int cpuid)
 {
 	update_siblings_masks(cpuid);
+	update_cpu_power(cpuid);
 }
 
 static void __init reset_cpu_topology(void)
@@ -238,6 +380,8 @@ static void __init reset_cpu_topology(void)
 		cpu_topo->cluster_id = -1;
 		cpumask_clear(&cpu_topo->core_sibling);
 		cpumask_clear(&cpu_topo->thread_sibling);
+
+		set_power_scale(cpu, SCHED_POWER_SCALE);
 	}
 }
 
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2014-03-19 18:02 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-12-11 20:00 [PATCH 1/3] arm64: topology: Implement basic CPU topology support Mark Brown
2013-12-11 20:00 ` [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
2013-12-12 11:35   ` Morten Rasmussen
2013-12-12 12:06     ` Mark Brown
2013-12-12 13:36       ` Morten Rasmussen
2013-12-12 17:39         ` Catalin Marinas
2013-12-12 18:06           ` Mark Brown
2013-12-11 20:00 ` [PATCH 3/3] arm64: topology: Add support for topology DT bindings Mark Brown
2013-12-12  7:13 ` [PATCH 1/3] arm64: topology: Implement basic CPU topology support Hanjun Guo
2013-12-12 10:22   ` Mark Brown
2014-03-05  8:59 [PATCH 1/3] arm64: topology: Add support for topology DT bindings Mark Brown
2014-03-05  8:59 ` [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown
2014-03-19 18:02 [PATCH 1/3] arm64: topology: Add support for topology DT bindings Mark Brown
2014-03-19 18:02 ` [PATCH 2/3] arm64: topology: Tell the scheduler about the relative power of cores Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.