Signed-off-by: Vincent Guittot <vincent.guittot(a)linaro.org>
---
arch/arm/kernel/topology.c | 134 +++++++++++++++++++++++++++++++++++++++++---
1 files changed, 126 insertions(+), 8 deletions(-)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 2774c5d..a1b1f7f 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -21,6 +21,10 @@
#include <linux/cpumask.h>
#include <linux/cpuset.h>
+#ifdef CONFIG_CPU_FREQ
+#include <linux/cpufreq.h>
+#endif
+
#include <asm/cputype.h>
#include <asm/topology.h>
@@ -54,6 +58,7 @@ struct cputopo_arm cpu_topology[NR_CPUS];
* using its own cpu_power even it's not always true because of
* no_hz_idle_balance
*/
+
static DEFINE_PER_CPU(unsigned int, cpu_scale);
/*
@@ -65,17 +70,127 @@ unsigned int advanced_topology = 1;
static void normal_cpu_topology_mask(void);
static void (*set_cpu_topology_mask)(void) = normal_cpu_topology_mask;
-/* This table sets the cpu_power scale of a cpu according to the sched_mc mode.
- * The content of this table could be SoC specific so we should add a method to
- * overwrite this default table.
+#ifdef CONFIG_CPU_FREQ
+/*
+ * This struct describes parameters to compute cpu_power
+ */
+struct cputopo_power {
+ int id;
+ int max; /* max idx in the table */
+ unsigned int step; /* frequency step for the table */
+ unsigned int *table; /* table of cpu_power */
+};
+
+/* default table with one default cpu_power value */
+unsigned int table_default_power[1] = {
+ 1024
+};
+
+static struct cputopo_power default_cpu_power = {
+ .max = 1,
+ .step = 1,
+ .table = table_default_power,
+};
+
+/* CA-9 table with cpufreq modifying cpu_power */
+#define CPU_MAX_FREQ 10
+/* we use a 200Mhz step for scaling cpu power */
+#define CPU_TOPO_FREQ_STEP 200000
+/* This table sets the cpu_power scale of a cpu according to 2 inputs which are
+ * the frequency and the sched_mc mode. The content of this table could be SoC
+ * specific so we should add a method to overwrite this default table.
* TODO: Study how to use DT for setting this table
*/
+unsigned int table_ca9_power[CPU_MAX_FREQ] = {
+/* freq< 200 400 600 800 1000 1200 1400 1600 1800 other*/
+ 4096, 4096, 4096, 1024, 1024, 1024, 1024, 1024, 1024, 1024, /* Power save mode CA9 MP */
+};
+
+static struct cputopo_power CA9_cpu_power = {
+ .max = CPU_MAX_FREQ,
+ .step = CPU_TOPO_FREQ_STEP,
+ .table = table_ca9_power,
+};
+
#define ARM_CORTEX_A9_DEFAULT_SCALE 0
#define ARM_CORTEX_A9_POWER_SCALE 1
/* This table list all possible cpu power configuration */
-unsigned int table_config[2] = {
+struct cputopo_power *table_config[2] = {
+ &default_cpu_power,
+ &CA9_cpu_power,
+};
+
+struct cputopo_scale {
+ int id;
+ int freq;
+ struct cputopo_power *power;
+};
+
+/*
+ * The table will be mostly used by one cpu which will update the
+ * configuration for all cpu on a cpufreq notification
+ * or a sched_mc level change
+ */
+static struct cputopo_scale cpu_power[NR_CPUS];
+
+static void set_cpufreq_scale(unsigned int cpuid, unsigned int freq)
+{
+ unsigned int idx;
+
+ cpu_power[cpuid].freq = freq;
+
+ idx = freq / cpu_power[cpuid].power->step;
+ if (idx >= cpu_power[cpuid].power->max)
+ idx = cpu_power[cpuid].power->max - 1;
+
+ per_cpu(cpu_scale, cpuid) = cpu_power[cpuid].power->table[idx];
+ smp_wmb();
+}
+
+static void set_power_scale(unsigned int cpu, unsigned int idx)
+{
+ cpu_power[cpu].id = idx;
+ cpu_power[cpu].power = table_config[idx];
+
+ set_cpufreq_scale(cpu, cpu_power[cpu].freq);
+}
+
+static int topo_cpufreq_transition(struct notifier_block *nb,
+ unsigned long state, void *data)
+{
+ struct cpufreq_freqs *freqs = data;
+
+ if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE)
+ set_cpufreq_scale(freqs->cpu, freqs->new);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block topo_cpufreq_nb = {
+ .notifier_call = topo_cpufreq_transition,
+};
+
+static int topo_cpufreq_init(void)
+{
+ unsigned int cpu;
+
+ /* TODO set initial value according to current freq */
+
+ /* init core mask */
+ for_each_possible_cpu(cpu) {
+ cpu_power[cpu].freq = 0;
+ cpu_power[cpu].power = &default_cpu_power;
+ }
+
+ return cpufreq_register_notifier(&topo_cpufreq_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+#else
+#define ARM_CORTEX_A9_DEFAULT_SCALE 0
+#define ARM_CORTEX_A9_POWER_SCALE 0
+/* This table list all possible cpu power configuration */
+unsigned int table_config[1] = {
1024,
- 4096
};
static void set_power_scale(unsigned int cpu, unsigned int idx)
@@ -83,14 +198,17 @@ static void set_power_scale(unsigned int cpu, unsigned int idx)
per_cpu(cpu_scale, cpu) = table_config[idx];
}
+static inline int topo_cpufreq_init(void) {return 0; }
+#endif
+
static int init_cpu_power_scale(void)
{
+ /* register cpufreq notifer */
+ topo_cpufreq_init();
+
/* Do we need to change default config */
advanced_topology = 1;
- /* force topology update */
- arch_update_cpu_topology();
-
/* Force a cpu topology update */
rebuild_sched_domains();
--
1.7.4.1
Add an architecture specific function for setting cpu_power
Signed-off-by: Vincent Guittot <vincent.guittot(a)linaro.org>
---
arch/arm/kernel/topology.c | 22 ++++++++++++++++++++++
1 files changed, 22 insertions(+), 0 deletions(-)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index af1c3e6..9d80e22 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -45,12 +45,32 @@
struct cputopo_arm cpu_topology[NR_CPUS];
/*
+ * cpu power scale management
+ */
+
+/*
+ * a per cpu data structure should be better because each cpu is mainly
+ * using its own cpu_power even it's not always true because of
+ * no_hz_idle_balance
+ */
+static DEFINE_PER_CPU(unsigned int, cpu_scale);
+
+/*
* cpu topology mask management
*/
unsigned int advanced_topology = 1;
/*
+ * Update the cpu power
+ */
+
+unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu)
+{
+ return per_cpu(cpu_scale, cpu);
+}
+
+/*
* default topology function
*/
@@ -281,6 +301,8 @@ void init_cpu_topology(void)
cpu_topo->socket_id = -1;
cpumask_clear(&cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
+
+ per_cpu(cpu_scale, cpu) = SCHED_POWER_SCALE;
}
smp_wmb();
}
--
1.7.4.1
arch_update_cpu_topology function is called by the scheduler
before building its sched_domain hierarchy. Prepare the update
of the cpu topology masks in this function in addition to set
it in the the store_cpu_topology which is executed only once per cpu.
Signed-off-by: Vincent Guittot <vincent.guittot(a)linaro.org>
---
arch/arm/kernel/topology.c | 108 +++++++++++++++++++++++++++++++++++---------
1 files changed, 87 insertions(+), 21 deletions(-)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 8200dea..90352cb 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -43,12 +43,76 @@
struct cputopo_arm cpu_topology[NR_CPUS];
+/*
+ * cpu topology mask management
+ */
+
+unsigned int advanced_topology = 0;
+
+/*
+ * default topology function
+ */
+
const struct cpumask *cpu_coregroup_mask(int cpu)
{
return &cpu_topology[cpu].core_sibling;
}
/*
+ * clear cpu topology masks
+ */
+static void clear_cpu_topology_mask(void)
+{
+ unsigned int cpuid;
+ for_each_possible_cpu(cpuid) {
+ struct cputopo_arm *cpuid_topo = &(cpu_topology[cpuid]);
+ cpumask_clear(&cpuid_topo->core_sibling);
+ cpumask_clear(&cpuid_topo->thread_sibling);
+ }
+ smp_wmb();
+}
+
+/*
+ * default_cpu_topology_mask set the core and thread mask as described in the
+ * ARM ARM
+ */
+static inline void default_cpu_topology_mask(unsigned int cpuid)
+{
+ struct cputopo_arm *cpuid_topo = &cpu_topology[cpuid];
+ unsigned int cpu;
+
+ for_each_possible_cpu(cpu) {
+ struct cputopo_arm *cpu_topo = &cpu_topology[cpu];
+
+ if (cpuid_topo->socket_id == cpu_topo->socket_id) {
+ 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) {
+ cpumask_set_cpu(cpuid,
+ &cpu_topo->thread_sibling);
+ if (cpu != cpuid)
+ cpumask_set_cpu(cpu,
+ &cpuid_topo->thread_sibling);
+ }
+ }
+ }
+ smp_wmb();
+}
+
+static void normal_cpu_topology_mask(void)
+{
+ unsigned int cpuid;
+
+ for_each_possible_cpu(cpuid) {
+ default_cpu_topology_mask(cpuid);
+ }
+ smp_wmb();
+}
+
+/*
* store_cpu_topology is called at boot when only one cpu is running
* and with the mutex cpu_hotplug.lock locked, when several cpus have booted,
* which prevents simultaneous write access to cpu_topology array
@@ -57,7 +121,6 @@ void store_cpu_topology(unsigned int cpuid)
{
struct cputopo_arm *cpuid_topo = &cpu_topology[cpuid];
unsigned int mpidr;
- unsigned int cpu;
/* If the cpu topology has been already set, just return */
if (cpuid_topo->core_id != -1)
@@ -99,26 +162,11 @@ void store_cpu_topology(unsigned int cpuid)
cpuid_topo->socket_id = -1;
}
- /* update core and thread sibling masks */
- for_each_possible_cpu(cpu) {
- struct cputopo_arm *cpu_topo = &cpu_topology[cpu];
-
- if (cpuid_topo->socket_id == cpu_topo->socket_id) {
- 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) {
- cpumask_set_cpu(cpuid,
- &cpu_topo->thread_sibling);
- if (cpu != cpuid)
- cpumask_set_cpu(cpu,
- &cpuid_topo->thread_sibling);
- }
- }
- }
- smp_wmb();
+ /*
+ * The core and thread sibling masks can also be updated during the
+ * call of arch_update_cpu_topology
+ */
+ default_cpu_topology_mask(cpuid);
printk(KERN_INFO "CPU%u: thread %d, cpu %d, socket %d, mpidr %x\n",
cpuid, cpu_topology[cpuid].thread_id,
@@ -127,6 +175,24 @@ void store_cpu_topology(unsigned int cpuid)
}
/*
+ * arch_update_cpu_topology is called by the scheduler before building
+ * a new sched_domain hierarchy.
+ */
+int arch_update_cpu_topology(void)
+{
+ if (!advanced_topology)
+ return 0;
+
+ /* clear core mask */
+ clear_cpu_topology_mask();
+
+ /* update core and thread sibling masks */
+ normal_cpu_topology_mask();
+
+ return 1;
+}
+
+/*
* init_cpu_topology is called at boot when only one cpu is running
* which prevent simultaneous write access to cpu_topology array
*/
--
1.7.4.1
Just wanted to announce that the initial Linaro+Android-3.1 kernel for
11.11 is available.
I've tagged it as: linux-linaro-3.1-2011.11-0-android-0
>From the branch:
git://android.git.linaro.org/kernel/linaro-android.git linaro-android-3.1-agreen-rebase
Please remember Nico is freezing the 11.11 linaro kernel on Monday, and
I'll be tagging the 11.11 linaro-android kernel shortly there after.
thanks
-john