Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 19ab428f authored by Stephen Warren's avatar Stephen Warren Committed by Russell King
Browse files

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: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: default avatarStephen Warren <swarren@nvidia.com>
Acked-by: default avatarWill Deacon <will.deacon@arm.com>
Tested-by: default avatarZhangfei Gao <zhangfei.gao@gmail.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 69f91ff8
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -2016,7 +2016,7 @@ config XIP_PHYS_ADDR

config KEXEC
	bool "Kexec system call (EXPERIMENTAL)"
	depends on (!SMP || HOTPLUG_CPU)
	depends on (!SMP || PM_SLEEP_SMP)
	help
	  kexec is a system call that implements the ability to shutdown your
	  current kernel, and to start another kernel.  It is like a reboot
+4 −0
Original line number Diff line number Diff line
@@ -134,6 +134,10 @@ void machine_kexec(struct kimage *image)
	unsigned long reboot_code_buffer_phys;
	void *reboot_code_buffer;

	if (num_online_cpus() > 1) {
		pr_err("kexec: error: multiple CPUs still online\n");
		return;
	}

	page_list = image->head & PAGE_MASK;

+37 −6
Original line number Diff line number Diff line
@@ -184,30 +184,61 @@ int __init reboot_setup(char *str)

__setup("reboot=", reboot_setup);

/*
 * Called by kexec, immediately prior to machine_kexec().
 *
 * This must completely disable all secondary CPUs; simply causing those CPUs
 * to execute e.g. a RAM-based pin loop is not sufficient. This allows the
 * kexec'd kernel to use any and all RAM as it sees fit, without having to
 * avoid any code or data used by any SW CPU pin loop. The CPU hotplug
 * functionality embodied in disable_nonboot_cpus() to achieve this.
 */
void machine_shutdown(void)
{
#ifdef CONFIG_SMP
	smp_send_stop();
#endif
	disable_nonboot_cpus();
}

/*
 * Halting simply requires that the secondary CPUs stop performing any
 * activity (executing tasks, handling interrupts). smp_send_stop()
 * achieves this.
 */
void machine_halt(void)
{
	machine_shutdown();
	smp_send_stop();

	local_irq_disable();
	while (1);
}

/*
 * Power-off simply requires that the secondary CPUs stop performing any
 * activity (executing tasks, handling interrupts). smp_send_stop()
 * achieves this. When the system power is turned off, it will take all CPUs
 * with it.
 */
void machine_power_off(void)
{
	machine_shutdown();
	smp_send_stop();

	if (pm_power_off)
		pm_power_off();
}

/*
 * Restart requires that the secondary CPUs stop performing any activity
 * while the primary CPU resets the system. Systems with a single CPU can
 * use soft_restart() as their machine descriptor's .restart hook, since that
 * will cause the only available CPU to reset. Systems with multiple CPUs must
 * provide a HW restart implementation, to ensure that all CPUs reset at once.
 * This is required so that any code running after reset on the primary CPU
 * doesn't have to co-ordinate with other CPUs to ensure they aren't still
 * executing pre-reset code, and using RAM that the primary CPU's code wishes
 * to use. Implementing such co-ordination would be essentially impossible.
 */
void machine_restart(char *cmd)
{
	machine_shutdown();
	smp_send_stop();

	arm_pm_restart(reboot_mode, cmd);

+0 −13
Original line number Diff line number Diff line
@@ -651,17 +651,6 @@ void smp_send_reschedule(int cpu)
	smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
}

#ifdef CONFIG_HOTPLUG_CPU
static void smp_kill_cpus(cpumask_t *mask)
{
	unsigned int cpu;
	for_each_cpu(cpu, mask)
		platform_cpu_kill(cpu);
}
#else
static void smp_kill_cpus(cpumask_t *mask) { }
#endif

void smp_send_stop(void)
{
	unsigned long timeout;
@@ -679,8 +668,6 @@ void smp_send_stop(void)

	if (num_online_cpus() > 1)
		pr_warning("SMP: failed to stop secondary CPUs\n");

	smp_kill_cpus(&mask);
}

/*