[RFC PATCH 2/3] ARM: mcpm: Implement cpu_kill() to synchronise on powerdown
Nicolas Pitre
nicolas.pitre at linaro.org
Mon Sep 30 13:00:00 EDT 2013
On Mon, 30 Sep 2013, Dave Martin wrote:
> CPU hotplug and kexec rely on smp_ops.cpu_kill(), which is supposed
> to wait for the CPU to park or power down, and perform the last
> rites (such as disabling clocks etc., where the platform doesn't do
> this automatically).
>
> kexec in particular is unsafe without performing this
> synchronisation to park secondaries. Without it, the secondaries
> might not be parked when kexec trashes the kernel.
>
> There is no generic way to do this synchronisation, so a new mcpm
> platform_ops method power_down_finish() is added by this patch.
>
> The new method is mandatory. A platform which provides no way to
> detect when CPUs are parked is likely broken.
>
> Signed-off-by: Dave Martin <Dave.Martin at arm.com>
> ---
> arch/arm/common/mcpm_entry.c | 5 +++++
> arch/arm/common/mcpm_platsmp.c | 10 ++++++++++
> arch/arm/include/asm/mcpm.h | 31 +++++++++++++++++++++++++++++++
> 3 files changed, 46 insertions(+)
>
> diff --git a/arch/arm/common/mcpm_entry.c b/arch/arm/common/mcpm_entry.c
> index 370236d..b722027 100644
> --- a/arch/arm/common/mcpm_entry.c
> +++ b/arch/arm/common/mcpm_entry.c
> @@ -89,6 +89,11 @@ void mcpm_cpu_power_down(void)
> BUG();
> }
>
> +int mcpm_cpu_power_down_finish(unsigned int cpu, unsigned int cluster)
> +{
> + return platform_ops->power_down_finish(cpu, cluster);
> +}
Please make it return an error (non-zero return value) if platform_ops
or platform_ops->power_down_finish is NULL. Otherwise this would oops
the kernel.
There is a patch already handling the other cases in RMK's patch system.
> +
> void mcpm_cpu_suspend(u64 expected_residency)
> {
> phys_reset_t phys_reset;
> diff --git a/arch/arm/common/mcpm_platsmp.c b/arch/arm/common/mcpm_platsmp.c
> index c0c3cd7..ac48cc7 100644
> --- a/arch/arm/common/mcpm_platsmp.c
> +++ b/arch/arm/common/mcpm_platsmp.c
> @@ -56,6 +56,15 @@ static void mcpm_secondary_init(unsigned int cpu)
>
> #ifdef CONFIG_HOTPLUG_CPU
>
> +static int mcpm_cpu_kill(unsigned int cpu)
> +{
> + unsigned int pcpu, pcluster;
> +
> + cpu_to_pcpu(cpu, &pcpu, &pcluster);
> +
> + return mcpm_cpu_power_down_finish(pcpu, pcluster);
> +}
> +
> static int mcpm_cpu_disable(unsigned int cpu)
> {
> /*
> @@ -82,6 +91,7 @@ static struct smp_operations __initdata mcpm_smp_ops = {
> .smp_boot_secondary = mcpm_boot_secondary,
> .smp_secondary_init = mcpm_secondary_init,
> #ifdef CONFIG_HOTPLUG_CPU
> + .cpu_kill = mcpm_cpu_kill,
> .cpu_disable = mcpm_cpu_disable,
> .cpu_die = mcpm_cpu_die,
> #endif
> diff --git a/arch/arm/include/asm/mcpm.h b/arch/arm/include/asm/mcpm.h
> index 0f7b762..22cdc6f 100644
> --- a/arch/arm/include/asm/mcpm.h
> +++ b/arch/arm/include/asm/mcpm.h
> @@ -78,10 +78,40 @@ int mcpm_cpu_power_up(unsigned int cpu, unsigned int cluster);
> *
> * This does not return. Re-entry in the kernel is expected via
> * mcpm_entry_point.
> + *
> + * The CPU is not necessarily truly halted until
> + * mcpu_cpu_power_down_finish() subsequently returns non-zero for the
s/mcpu/mcpm/
> + * specified cpu. Until then, other CPUs should make sure they do not
> + * trash memory the target CPU might be executing/accessing.
> */
> void mcpm_cpu_power_down(void);
>
> /**
> + * mcpm_cpu_power_down_finish - wait for a specified CPU to halt, and
> + * make sure it is powered off
> + *
> + * @cpu: CPU number within given cluster
> + * @cluster: cluster number for the CPU
> + *
> + * Call this function to ensure that a pending powerdown has taken
> + * effect and the CPU is safely parked before performing non-mcpm
> + * operations that may affect the CPU (such as kexec trashing the
> + * kernel text).
> + *
> + * It is *not* necessary to call this function if you only need to
> + * serialise a pending powerdown with mcpu_cpu_power_up() or a wakeup
Ditto.
> + * event.
> + *
> + * Do not call this function unless the specified CPU has already
> + * called mcpu_cpu_power_down() or has committed to doing so.
Ditto.
> + *
> + * @return:
> + * - zero if the CPU is in a safely parked state
> + * - nonzero otherwise (e.g., timeout)
> + */
> +int mcpm_cpu_power_down_finish(unsigned int cpu, unsigned int cluster);
> +
> +/**
> * mcpm_cpu_suspend - bring the calling CPU in a suspended state
> *
> * @expected_residency: duration in microseconds the CPU is expected
> @@ -120,6 +150,7 @@ int mcpm_cpu_powered_up(void);
> struct mcpm_platform_ops {
> int (*power_up)(unsigned int cpu, unsigned int cluster);
> void (*power_down)(void);
> + int (*power_down_finish)(unsigned int cpu, unsigned int cluster);
> void (*suspend)(u64);
> void (*powered_up)(void);
> };
> --
> 1.7.9.5
>
More information about the linux-arm-kernel
mailing list