diff options
author | Stephen Warren <swarren@nvidia.com> | 2013-06-14 11:14:14 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-06-17 16:35:25 -0400 |
commit | 19ab428f4b7988ef3ac727c680efc193ef53ce14 (patch) | |
tree | 7cdce88e40ca449db2fea719b88a423f884e1157 /arch/arm | |
parent | 69f91ff8c93c778cb65b71d9b2d95ff62956354f (diff) |
ARM: 7759/1: decouple CPU offlining from reboot/shutdown
Add comments to machine_shutdown()/halt()/power_off()/restart() that
describe their purpose and/or requirements re: CPUs being active/not.
In machine_shutdown(), replace the call to smp_send_stop() with a call to
disable_nonboot_cpus(). This completely disables all but one CPU, thus
satisfying the requirement that only a single CPU be active for kexec.
Adjust Kconfig dependencies for this change.
In machine_halt()/power_off()/restart(), call smp_send_stop() directly,
rather than via machine_shutdown(); these functions don't need to
completely de-activate all CPUs using hotplug, but rather just quiesce
them.
Remove smp_kill_cpus(), and its call from smp_send_stop().
smp_kill_cpus() was indirectly calling smp_ops.cpu_kill() without calling
smp_ops.cpu_die() on the target CPUs first. At least some implementations
of smp_ops had issues with this; it caused cpu_kill() to hang on Tegra,
for example. Since smp_send_stop() is only used for shutdown, halt, and
power-off, there is no need to attempt any kind of CPU hotplug here.
Adjust Kconfig to reflect that machine_shutdown() (and hence kexec)
relies upon disable_nonboot_cpus(). However, this alone doesn't guarantee
that hotplug will work, or even that hotplug is implemented for a
particular piece of HW that a multi-platform zImage runs on. Hence, add
error-checking to machine_kexec() to determine whether it did work.
Suggested-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Will Deacon <will.deacon@arm.com>
Tested-by: Zhangfei Gao <zhangfei.gao@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/kernel/machine_kexec.c | 4 | ||||
-rw-r--r-- | arch/arm/kernel/process.c | 43 | ||||
-rw-r--r-- | arch/arm/kernel/smp.c | 13 |
4 files changed, 42 insertions, 20 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 239fa96c12bb..2651b1da1c56 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -2016,7 +2016,7 @@ config XIP_PHYS_ADDR | |||
2016 | 2016 | ||
2017 | config KEXEC | 2017 | config KEXEC |
2018 | bool "Kexec system call (EXPERIMENTAL)" | 2018 | bool "Kexec system call (EXPERIMENTAL)" |
2019 | depends on (!SMP || HOTPLUG_CPU) | 2019 | depends on (!SMP || PM_SLEEP_SMP) |
2020 | help | 2020 | help |
2021 | kexec is a system call that implements the ability to shutdown your | 2021 | kexec is a system call that implements the ability to shutdown your |
2022 | current kernel, and to start another kernel. It is like a reboot | 2022 | current kernel, and to start another kernel. It is like a reboot |
diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index 8ef8c9337809..4fb074c446bf 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c | |||
@@ -134,6 +134,10 @@ void machine_kexec(struct kimage *image) | |||
134 | unsigned long reboot_code_buffer_phys; | 134 | unsigned long reboot_code_buffer_phys; |
135 | void *reboot_code_buffer; | 135 | void *reboot_code_buffer; |
136 | 136 | ||
137 | if (num_online_cpus() > 1) { | ||
138 | pr_err("kexec: error: multiple CPUs still online\n"); | ||
139 | return; | ||
140 | } | ||
137 | 141 | ||
138 | page_list = image->head & PAGE_MASK; | 142 | page_list = image->head & PAGE_MASK; |
139 | 143 | ||
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 282de4826abb..6e8931ccf13e 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
@@ -184,30 +184,61 @@ int __init reboot_setup(char *str) | |||
184 | 184 | ||
185 | __setup("reboot=", reboot_setup); | 185 | __setup("reboot=", reboot_setup); |
186 | 186 | ||
187 | /* | ||
188 | * Called by kexec, immediately prior to machine_kexec(). | ||
189 | * | ||
190 | * This must completely disable all secondary CPUs; simply causing those CPUs | ||
191 | * to execute e.g. a RAM-based pin loop is not sufficient. This allows the | ||
192 | * kexec'd kernel to use any and all RAM as it sees fit, without having to | ||
193 | * avoid any code or data used by any SW CPU pin loop. The CPU hotplug | ||
194 | * functionality embodied in disable_nonboot_cpus() to achieve this. | ||
195 | */ | ||
187 | void machine_shutdown(void) | 196 | void machine_shutdown(void) |
188 | { | 197 | { |
189 | #ifdef CONFIG_SMP | 198 | disable_nonboot_cpus(); |
190 | smp_send_stop(); | ||
191 | #endif | ||
192 | } | 199 | } |
193 | 200 | ||
201 | /* | ||
202 | * Halting simply requires that the secondary CPUs stop performing any | ||
203 | * activity (executing tasks, handling interrupts). smp_send_stop() | ||
204 | * achieves this. | ||
205 | */ | ||
194 | void machine_halt(void) | 206 | void machine_halt(void) |
195 | { | 207 | { |
196 | machine_shutdown(); | 208 | smp_send_stop(); |
209 | |||
197 | local_irq_disable(); | 210 | local_irq_disable(); |
198 | while (1); | 211 | while (1); |
199 | } | 212 | } |
200 | 213 | ||
214 | /* | ||
215 | * Power-off simply requires that the secondary CPUs stop performing any | ||
216 | * activity (executing tasks, handling interrupts). smp_send_stop() | ||
217 | * achieves this. When the system power is turned off, it will take all CPUs | ||
218 | * with it. | ||
219 | */ | ||
201 | void machine_power_off(void) | 220 | void machine_power_off(void) |
202 | { | 221 | { |
203 | machine_shutdown(); | 222 | smp_send_stop(); |
223 | |||
204 | if (pm_power_off) | 224 | if (pm_power_off) |
205 | pm_power_off(); | 225 | pm_power_off(); |
206 | } | 226 | } |
207 | 227 | ||
228 | /* | ||
229 | * Restart requires that the secondary CPUs stop performing any activity | ||
230 | * while the primary CPU resets the system. Systems with a single CPU can | ||
231 | * use soft_restart() as their machine descriptor's .restart hook, since that | ||
232 | * will cause the only available CPU to reset. Systems with multiple CPUs must | ||
233 | * provide a HW restart implementation, to ensure that all CPUs reset at once. | ||
234 | * This is required so that any code running after reset on the primary CPU | ||
235 | * doesn't have to co-ordinate with other CPUs to ensure they aren't still | ||
236 | * executing pre-reset code, and using RAM that the primary CPU's code wishes | ||
237 | * to use. Implementing such co-ordination would be essentially impossible. | ||
238 | */ | ||
208 | void machine_restart(char *cmd) | 239 | void machine_restart(char *cmd) |
209 | { | 240 | { |
210 | machine_shutdown(); | 241 | smp_send_stop(); |
211 | 242 | ||
212 | arm_pm_restart(reboot_mode, cmd); | 243 | arm_pm_restart(reboot_mode, cmd); |
213 | 244 | ||
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 550d63cef68e..5919eb451bb9 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c | |||
@@ -651,17 +651,6 @@ void smp_send_reschedule(int cpu) | |||
651 | smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE); | 651 | smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE); |
652 | } | 652 | } |
653 | 653 | ||
654 | #ifdef CONFIG_HOTPLUG_CPU | ||
655 | static void smp_kill_cpus(cpumask_t *mask) | ||
656 | { | ||
657 | unsigned int cpu; | ||
658 | for_each_cpu(cpu, mask) | ||
659 | platform_cpu_kill(cpu); | ||
660 | } | ||
661 | #else | ||
662 | static void smp_kill_cpus(cpumask_t *mask) { } | ||
663 | #endif | ||
664 | |||
665 | void smp_send_stop(void) | 654 | void smp_send_stop(void) |
666 | { | 655 | { |
667 | unsigned long timeout; | 656 | unsigned long timeout; |
@@ -679,8 +668,6 @@ void smp_send_stop(void) | |||
679 | 668 | ||
680 | if (num_online_cpus() > 1) | 669 | if (num_online_cpus() > 1) |
681 | pr_warning("SMP: failed to stop secondary CPUs\n"); | 670 | pr_warning("SMP: failed to stop secondary CPUs\n"); |
682 | |||
683 | smp_kill_cpus(&mask); | ||
684 | } | 671 | } |
685 | 672 | ||
686 | /* | 673 | /* |