From d4313b326c92200de2fc1f62cd8d0c06b98ccf5e Mon Sep 17 00:00:00 2001 From: Qais Yousef Date: Tue, 15 Sep 2020 12:47:10 +0100 Subject: [PATCH] ANDROID: arm64: Disallow offlining the last aarch32 cpu On asym aarch32 systems, bringing the last aarch32 cpu would cause any running 32bit application to crash. To protect against that, we ensure that we block the offlining operation of the last online cpu in aarch32_el0_mask. Suspend/hibernation and kexec operation should continue to work as intended. This is a different approach compared to the original one [1]. We should be able to convert this to a vendor hook if there's a desire to do so. [1] http://linux-arm.org/git?p=linux-power.git;a=commit;h=e6b567c1cc07dd1690e5d34b6a93ab9819ab2eeb Bug: 168847043 Reason: Needed for bringup. Revert when upstream patch is available Signed-off-by: Qais Yousef Change-Id: Ie8a2372d7b7db3c3580f19d724be183f988a5895 --- arch/arm64/Kconfig | 6 +++-- arch/arm64/kernel/smp.c | 54 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index f8a96aa5f794..330e5b8fd395 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1896,8 +1896,10 @@ config ASYMMETRIC_AARCH32 CPU configurations. Once the AArch32 EL0 support is detected on a CPU, the feature is made available to user space to allow the execution of 32-bit (compat) applications by migrating - them to the capable CPUs. Offlining such CPUs leads to 32-bit - applications being killed. + them to the capable CPUs. You will not be able to offline all + such CPUs to prevent any running 32-bit application from being + killed. I.e: we guarantee there will always be at least one + 32-bit capable CPU online. If unsure say N. diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 0e74a1552122..3209f7cadf24 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -95,6 +95,54 @@ static inline int op_cpu_kill(unsigned int cpu) } #endif +#ifdef CONFIG_ASYMMETRIC_AARCH32 +static int last_aarch32_cpu = -1; +static cpumask_t aarch32_online; + +static void asym_aarch32_online(void) +{ + /* + * Since we onlined another cpu, restore the hotpluggability of + * the last AAarch32 cpu if it was disabled. + */ + cpumask_and(&aarch32_online, &aarch32_el0_mask, cpu_online_mask); + + if (last_aarch32_cpu >= 0 && + cpumask_weight(&aarch32_online) > 1) { + + struct device *dev; + + dev = get_cpu_device(last_aarch32_cpu); + dev->offline_disabled = 0; + last_aarch32_cpu = -1; + } +} + +static void asym_aarch32_offline(void) +{ + /* Don't let the last AArch32-compatible CPU go down */ + if (!cpumask_empty(&aarch32_el0_mask)) { + + cpumask_and(&aarch32_online, &aarch32_el0_mask, cpu_online_mask); + + /* + * If we're left with only one AAarch32 cpu, prevent it from + * being offlined. + */ + if (cpumask_weight(&aarch32_online) == 1) { + struct device *dev; + + last_aarch32_cpu = cpumask_first(&aarch32_online); + dev = get_cpu_device(last_aarch32_cpu); + dev->offline_disabled = 1; + } + } +} +#else +static void asym_aarch32_online(void) {} +static void asym_aarch32_offline(void) {} +#endif + /* * Boot a secondary CPU, and assign it the specified idle task. @@ -139,8 +187,10 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle) */ wait_for_completion_timeout(&cpu_running, msecs_to_jiffies(5000)); - if (cpu_online(cpu)) + if (cpu_online(cpu)) { + asym_aarch32_online(); return 0; + } pr_crit("CPU%u: failed to come online\n", cpu); secondary_data.task = NULL; @@ -317,6 +367,8 @@ int __cpu_disable(void) set_cpu_online(cpu, false); ipi_teardown(cpu); + asym_aarch32_offline(); + /* * OK - migrate IRQs away from this CPU */