aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64
diff options
context:
space:
mode:
authorAshwin Chaugule <ashwin.chaugule@linaro.org>2014-05-07 10:18:36 -0400
committerAshwin Chaugule <ashwin.chaugule@linaro.org>2014-05-15 10:16:30 -0400
commitc814ca029e1015bb0ecec312f4bb9751ba1a711a (patch)
tree75ff2a914c476700b4a2272c523416d7d4cb3328 /arch/arm64
parente1cd3b361a4f1dd323252543f40d43509d80affa (diff)
ARM: Check if a CPU has gone offline
PSCIv0.2 adds a new function called AFFINITY_INFO, which can be used to query if a specified CPU has actually gone offline. Calling this function via cpu_kill ensures that a CPU has quiesced after a call to cpu_die. This helps prevent the CPU from doing arbitrary bad things when data or instructions are clobbered (as happens with kexec) in the window between a CPU announcing that it is dead and said CPU leaving the kernel. Signed-off-by: Ashwin Chaugule <ashwin.chaugule@linaro.org> Signed-off-by: Mark Rutland <mark.rutland@arm.com> Reviewed-by: Rob Herring <robh@kernel.org> Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'arch/arm64')
-rw-r--r--arch/arm64/include/asm/cpu_ops.h2
-rw-r--r--arch/arm64/kernel/psci.c31
-rw-r--r--arch/arm64/kernel/smp.c22
3 files changed, 55 insertions, 0 deletions
diff --git a/arch/arm64/include/asm/cpu_ops.h b/arch/arm64/include/asm/cpu_ops.h
index 152413076503..d7b4b38a8e86 100644
--- a/arch/arm64/include/asm/cpu_ops.h
+++ b/arch/arm64/include/asm/cpu_ops.h
@@ -39,6 +39,7 @@ struct device_node;
39 * from the cpu to be killed. 39 * from the cpu to be killed.
40 * @cpu_die: Makes a cpu leave the kernel. Must not fail. Called from the 40 * @cpu_die: Makes a cpu leave the kernel. Must not fail. Called from the
41 * cpu being killed. 41 * cpu being killed.
42 * @cpu_kill: Ensures a cpu has left the kernel. Called from another cpu.
42 * @cpu_suspend: Suspends a cpu and saves the required context. May fail owing 43 * @cpu_suspend: Suspends a cpu and saves the required context. May fail owing
43 * to wrong parameters or error conditions. Called from the 44 * to wrong parameters or error conditions. Called from the
44 * CPU being suspended. Must be called with IRQs disabled. 45 * CPU being suspended. Must be called with IRQs disabled.
@@ -52,6 +53,7 @@ struct cpu_operations {
52#ifdef CONFIG_HOTPLUG_CPU 53#ifdef CONFIG_HOTPLUG_CPU
53 int (*cpu_disable)(unsigned int cpu); 54 int (*cpu_disable)(unsigned int cpu);
54 void (*cpu_die)(unsigned int cpu); 55 void (*cpu_die)(unsigned int cpu);
56 int (*cpu_kill)(unsigned int cpu);
55#endif 57#endif
56#ifdef CONFIG_ARM64_CPU_SUSPEND 58#ifdef CONFIG_ARM64_CPU_SUSPEND
57 int (*cpu_suspend)(unsigned long); 59 int (*cpu_suspend)(unsigned long);
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
index 90df6e641227..9e9798f91172 100644
--- a/arch/arm64/kernel/psci.c
+++ b/arch/arm64/kernel/psci.c
@@ -20,6 +20,7 @@
20#include <linux/smp.h> 20#include <linux/smp.h>
21#include <linux/reboot.h> 21#include <linux/reboot.h>
22#include <linux/pm.h> 22#include <linux/pm.h>
23#include <linux/delay.h>
23#include <uapi/linux/psci.h> 24#include <uapi/linux/psci.h>
24 25
25#include <asm/compiler.h> 26#include <asm/compiler.h>
@@ -403,6 +404,35 @@ static void cpu_psci_cpu_die(unsigned int cpu)
403 404
404 pr_crit("unable to power off CPU%u (%d)\n", cpu, ret); 405 pr_crit("unable to power off CPU%u (%d)\n", cpu, ret);
405} 406}
407
408static int cpu_psci_cpu_kill(unsigned int cpu)
409{
410 int err, i;
411
412 if (!psci_ops.affinity_info)
413 return 1;
414 /*
415 * cpu_kill could race with cpu_die and we can
416 * potentially end up declaring this cpu undead
417 * while it is dying. So, try again a few times.
418 */
419
420 for (i = 0; i < 10; i++) {
421 err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
422 if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
423 pr_info("CPU%d killed.\n", cpu);
424 return 1;
425 }
426
427 msleep(10);
428 pr_info("Retrying again to check for CPU kill\n");
429 }
430
431 pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
432 cpu, err);
433 /* Make op_cpu_kill() fail. */
434 return 0;
435}
406#endif 436#endif
407 437
408const struct cpu_operations cpu_psci_ops = { 438const struct cpu_operations cpu_psci_ops = {
@@ -413,6 +443,7 @@ const struct cpu_operations cpu_psci_ops = {
413#ifdef CONFIG_HOTPLUG_CPU 443#ifdef CONFIG_HOTPLUG_CPU
414 .cpu_disable = cpu_psci_cpu_disable, 444 .cpu_disable = cpu_psci_cpu_disable,
415 .cpu_die = cpu_psci_cpu_die, 445 .cpu_die = cpu_psci_cpu_die,
446 .cpu_kill = cpu_psci_cpu_kill,
416#endif 447#endif
417}; 448};
418 449
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index f0a141dd5655..c3cb160edc69 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -228,6 +228,19 @@ int __cpu_disable(void)
228 return 0; 228 return 0;
229} 229}
230 230
231static int op_cpu_kill(unsigned int cpu)
232{
233 /*
234 * If we have no means of synchronising with the dying CPU, then assume
235 * that it is really dead. We can only wait for an arbitrary length of
236 * time and hope that it's dead, so let's skip the wait and just hope.
237 */
238 if (!cpu_ops[cpu]->cpu_kill)
239 return 1;
240
241 return cpu_ops[cpu]->cpu_kill(cpu);
242}
243
231static DECLARE_COMPLETION(cpu_died); 244static DECLARE_COMPLETION(cpu_died);
232 245
233/* 246/*
@@ -241,6 +254,15 @@ void __cpu_die(unsigned int cpu)
241 return; 254 return;
242 } 255 }
243 pr_notice("CPU%u: shutdown\n", cpu); 256 pr_notice("CPU%u: shutdown\n", cpu);
257
258 /*
259 * Now that the dying CPU is beyond the point of no return w.r.t.
260 * in-kernel synchronisation, try to get the firwmare to help us to
261 * verify that it has really left the kernel before we consider
262 * clobbering anything it might still be using.
263 */
264 if (!op_cpu_kill(cpu))
265 pr_warn("CPU%d may not have shut down cleanly\n", cpu);
244} 266}
245 267
246/* 268/*