aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Pitre <nicolas.pitre@linaro.org>2012-11-21 11:53:27 -0500
committerNicolas Pitre <nicolas.pitre@linaro.org>2013-05-13 19:02:05 -0400
commit8f4f5923eee935287e777a489c185e2517a89534 (patch)
tree8ca7d926531870c6c30d4804bfb103d88e6d96ed
parentfca7d749daa8a4331c59259831b7f8d3eb058367 (diff)
downloadvexpress-lsk-8f4f5923eee935287e777a489c185e2517a89534.tar.gz
ARM: bL_switcher: hot-unplug half of the available CPUs
With an MP kernel, all the CPUs are initially available. The switcher model always uses half of them at any time. Let's remove half of the available CPUs and make sure we still have a working switcher configuration. Signed-off-by: Nicolas Pitre <nico@linaro.org>
-rw-r--r--arch/arm/common/bL_switcher.c78
1 files changed, 77 insertions, 1 deletions
diff --git a/arch/arm/common/bL_switcher.c b/arch/arm/common/bL_switcher.c
index b59958e7e53..bcc1e06afc8 100644
--- a/arch/arm/common/bL_switcher.c
+++ b/arch/arm/common/bL_switcher.c
@@ -297,18 +297,94 @@ int bL_switch_request(unsigned int cpu, unsigned int new_cluster_id)
EXPORT_SYMBOL_GPL(bL_switch_request);
+/*
+ * Activation and configuration code.
+ */
+
+static cpumask_t bL_switcher_removed_logical_cpus;
+
+static void __init bL_switcher_restore_cpus(void)
+{
+ int i;
+
+ for_each_cpu(i, &bL_switcher_removed_logical_cpus)
+ cpu_up(i);
+}
+
+static int __init bL_switcher_halve_cpus(void)
+{
+ int cpu, cluster, i, ret;
+ cpumask_t cluster_mask[2], common_mask;
+
+ cpumask_clear(&bL_switcher_removed_logical_cpus);
+ cpumask_clear(&cluster_mask[0]);
+ cpumask_clear(&cluster_mask[1]);
+
+ for_each_online_cpu(i) {
+ cpu = cpu_logical_map(i) & 0xff;
+ cluster = (cpu_logical_map(i) >> 8) & 0xff;
+ if (cluster >= 2) {
+ pr_err("%s: only dual cluster systems are supported\n", __func__);
+ return -EINVAL;
+ }
+ cpumask_set_cpu(cpu, &cluster_mask[cluster]);
+ }
+
+ if (!cpumask_and(&common_mask, &cluster_mask[0], &cluster_mask[1])) {
+ pr_err("%s: no common set of CPUs\n", __func__);
+ return -EINVAL;
+ }
+
+ for_each_online_cpu(i) {
+ cpu = cpu_logical_map(i) & 0xff;
+ cluster = (cpu_logical_map(i) >> 8) & 0xff;
+
+ if (cpumask_test_cpu(cpu, &common_mask)) {
+ /*
+ * We keep only those logical CPUs which number
+ * is equal to their physical CPU number. This is
+ * not perfect but good enough in most cases.
+ */
+ if (cpu == i)
+ continue;
+ }
+
+ ret = cpu_down(i);
+ if (ret) {
+ bL_switcher_restore_cpus();
+ return ret;
+ }
+ cpumask_set_cpu(i, &bL_switcher_removed_logical_cpus);
+ }
+
+ return 0;
+}
+
static int __init bL_switcher_init(void)
{
- int cpu;
+ int cpu, ret;
pr_info("big.LITTLE switcher initializing\n");
+ if (MAX_NR_CLUSTERS != 2) {
+ pr_err("%s: only dual cluster systems are supported\n", __func__);
+ return -EINVAL;
+ }
+
+ cpu_hotplug_driver_lock();
+ ret = bL_switcher_halve_cpus();
+ if (ret) {
+ cpu_hotplug_driver_unlock();
+ return ret;
+ }
+
for_each_online_cpu(cpu) {
struct bL_thread *t = &bL_threads[cpu];
init_waitqueue_head(&t->wq);
t->wanted_cluster = -1;
t->task = bL_switcher_thread_create(cpu, t);
}
+ cpu_hotplug_driver_unlock();
pr_info("big.LITTLE switcher initialized\n");
return 0;