Message ID | 20130418190923.GE14496@n2100.arm.linux.org.uk (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, Apr 18, 2013 at 2:09 PM, Russell King - ARM Linux <linux@arm.linux.org.uk> wrote: > On Thu, Apr 18, 2013 at 07:25:34PM +0100, Russell King - ARM Linux wrote: >> On Thu, Apr 18, 2013 at 06:18:01PM +0100, Russell King - ARM Linux wrote: >> > On Sat, Apr 06, 2013 at 03:37:04PM +0300, Kevin Bracey wrote: [...] >> Now, with this patch applied, we guarantee that we push out any data >> that matters from the dying CPU before platform_cpu_kill() is called. >> That should mean that shmobile can remove that whole cpu_dead thing. >> >> I've tested this on OMAP, and it appears to work from a simple test of >> CPU hotplug. This patch(set) also kills the cpu_disable() that tegra >> has which is just a copy of the generic version. > > Okay, last version for tonight, with additional comments too, and a > hole plugged if the powering down is done by the platform via > cpu_die(). > > arch/arm/kernel/smp.c | 42 +++++++++++++++++++++++++++++++--- > arch/arm/mach-exynos/hotplug.c | 1 - > arch/arm/mach-highbank/hotplug.c | 4 --- Looks like this just landed in -next and it breaks highbank build. Run-time will also be broken. See the last commit to this file in final 3.9 which will also conflict with this one. The problem is that highbank_set_cpu_jump calls outer cache functions which take a spinlock and pollute the L1 cache. This all goes away once PCSI support goes in, but for now we need another spot to clear the jump address or just drop the highbank change. Rob
On Wed, May 01, 2013 at 08:06:56AM -0500, Rob Herring wrote: > On Thu, Apr 18, 2013 at 2:09 PM, Russell King - ARM Linux > <linux@arm.linux.org.uk> wrote: > > On Thu, Apr 18, 2013 at 07:25:34PM +0100, Russell King - ARM Linux wrote: > >> On Thu, Apr 18, 2013 at 06:18:01PM +0100, Russell King - ARM Linux wrote: > >> > On Sat, Apr 06, 2013 at 03:37:04PM +0300, Kevin Bracey wrote: > > [...] > > >> Now, with this patch applied, we guarantee that we push out any data > >> that matters from the dying CPU before platform_cpu_kill() is called. > >> That should mean that shmobile can remove that whole cpu_dead thing. > >> > >> I've tested this on OMAP, and it appears to work from a simple test of > >> CPU hotplug. This patch(set) also kills the cpu_disable() that tegra > >> has which is just a copy of the generic version. > > > > Okay, last version for tonight, with additional comments too, and a > > hole plugged if the powering down is done by the platform via > > cpu_die(). > > > > arch/arm/kernel/smp.c | 42 +++++++++++++++++++++++++++++++--- > > arch/arm/mach-exynos/hotplug.c | 1 - > > arch/arm/mach-highbank/hotplug.c | 4 --- > > Looks like this just landed in -next and it breaks highbank build. > Run-time will also be broken. See the last commit to this file in > final 3.9 which will also conflict with this one. The problem is that > highbank_set_cpu_jump calls outer cache functions which take a > spinlock and pollute the L1 cache. This all goes away once PCSI > support goes in, but for now we need another spot to clear the jump > address or just drop the highbank change. If that's the case then what you currently have is dangerous. There is no guarantee that the results of touching those locks will get flushed out of the L1 cache of the going-down CPU - you _could_ end up with the result of the lock being flushed out but not the unlock, which would lead to a system deadlock. The only thing which has changed as far as highbank is concerned is that it will now use flush_cache_louis() rather than flush_cache_all() before calling highbank_set_cpu_jump(). So, I think this has merely uncovered a latent bug in your code. :)
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 1f2cccc..4231034 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -211,6 +211,13 @@ void __cpuinit __cpu_die(unsigned int cpu) } printk(KERN_NOTICE "CPU%u: shutdown\n", cpu); + /* + * platform_cpu_kill() is generally expected to do the powering off + * and/or cutting of clocks to the dying CPU. Optionally, this may + * be done by the CPU which is dying in preference to supporting + * this call, but that means there is _no_ synchronisation between + * the requesting CPU and the dying CPU actually losing power. + */ if (!platform_cpu_kill(cpu)) printk("CPU%u: unable to kill\n", cpu); } @@ -230,14 +237,41 @@ void __ref cpu_die(void) idle_task_exit(); local_irq_disable(); - mb(); - /* Tell __cpu_die() that this CPU is now safe to dispose of */ + /* + * Flush the data out of the L1 cache for this CPU. This must be + * before the completion to ensure that data is safely written out + * before platform_cpu_kill() gets called - which may disable + * *this* CPU and power down its cache. + */ + flush_cache_louis(); + + /* + * Tell __cpu_die() that this CPU is now safe to dispose of. Once + * this returns, power and/or clocks can be removed at any point + * from this CPU and its cache by platform_cpu_kill(). + */ RCU_NONIDLE(complete(&cpu_died)); /* - * actual CPU shutdown procedure is at least platform (if not - * CPU) specific. + * Ensure that the cache lines associated with that completion are + * written out. This covers the case where _this_ CPU is doing the + * powering down, to ensure that the completion is visible to the + * CPU waiting for this one. + */ + flush_cache_louis(); + + /* + * The actual CPU shutdown procedure is at least platform (if not + * CPU) specific. This may remove power, or it may simply spin. + * + * Platforms are generally expected *NOT* to return from this call, + * although there are some which do because they have no way to + * power down the CPU. These platforms are the _only_ reason we + * have a return path which uses the fragment of assembly below. + * + * The return path should not be used for platforms which can + * power off the CPU. */ if (smp_ops.cpu_die) smp_ops.cpu_die(cpu); diff --git a/arch/arm/mach-exynos/hotplug.c b/arch/arm/mach-exynos/hotplug.c index c3f825b..af90cfa 100644 --- a/arch/arm/mach-exynos/hotplug.c +++ b/arch/arm/mach-exynos/hotplug.c @@ -28,7 +28,6 @@ static inline void cpu_enter_lowpower_a9(void) { unsigned int v; - flush_cache_all(); asm volatile( " mcr p15, 0, %1, c7, c5, 0\n" " mcr p15, 0, %1, c7, c10, 4\n" diff --git a/arch/arm/mach-highbank/hotplug.c b/arch/arm/mach-highbank/hotplug.c index f30c528..35dd42e 100644 --- a/arch/arm/mach-highbank/hotplug.c +++ b/arch/arm/mach-highbank/hotplug.c @@ -15,8 +15,6 @@ */ #include <linux/kernel.h> -#include <asm/cacheflush.h> - #include "core.h" #include "sysregs.h" @@ -28,8 +26,6 @@ extern void secondary_startup(void); */ void __ref highbank_cpu_die(unsigned int cpu) { - flush_cache_all(); - highbank_set_cpu_jump(cpu, phys_to_virt(0)); highbank_set_core_pwr(); diff --git a/arch/arm/mach-imx/hotplug.c b/arch/arm/mach-imx/hotplug.c index 361a253..5e91112 100644 --- a/arch/arm/mach-imx/hotplug.c +++ b/arch/arm/mach-imx/hotplug.c @@ -11,7 +11,6 @@ */ #include <linux/errno.h> -#include <asm/cacheflush.h> #include <asm/cp15.h> #include "common.h" @@ -20,7 +19,6 @@ static inline void cpu_enter_lowpower(void) { unsigned int v; - flush_cache_all(); asm volatile( "mcr p15, 0, %1, c7, c5, 0\n" " mcr p15, 0, %1, c7, c10, 4\n" diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c index 750446f..326a872 100644 --- a/arch/arm/mach-msm/hotplug.c +++ b/arch/arm/mach-msm/hotplug.c @@ -10,16 +10,12 @@ #include <linux/errno.h> #include <linux/smp.h> -#include <asm/cacheflush.h> #include <asm/smp_plat.h> #include "common.h" static inline void cpu_enter_lowpower(void) { - /* Just flush the cache. Changing the coherency is not yet - * available on msm. */ - flush_cache_all(); } static inline void cpu_leave_lowpower(void) diff --git a/arch/arm/mach-omap2/omap-hotplug.c b/arch/arm/mach-omap2/omap-hotplug.c index e712d17..ceb30a5 100644 --- a/arch/arm/mach-omap2/omap-hotplug.c +++ b/arch/arm/mach-omap2/omap-hotplug.c @@ -35,9 +35,6 @@ void __ref omap4_cpu_die(unsigned int cpu) unsigned int boot_cpu = 0; void __iomem *base = omap_get_wakeupgen_base(); - flush_cache_all(); - dsb(); - /* * we're ready for shutdown now, so do it */ diff --git a/arch/arm/mach-prima2/hotplug.c b/arch/arm/mach-prima2/hotplug.c index f4b17cb..0ab2f8b 100644 --- a/arch/arm/mach-prima2/hotplug.c +++ b/arch/arm/mach-prima2/hotplug.c @@ -10,13 +10,10 @@ #include <linux/errno.h> #include <linux/smp.h> -#include <asm/cacheflush.h> #include <asm/smp_plat.h> static inline void platform_do_lowpower(unsigned int cpu) { - flush_cache_all(); - /* we put the platform to just WFI */ for (;;) { __asm__ __volatile__("dsb\n\t" "wfi\n\t" diff --git a/arch/arm/mach-realview/hotplug.c b/arch/arm/mach-realview/hotplug.c index 53818e5..ac22dd4 100644 --- a/arch/arm/mach-realview/hotplug.c +++ b/arch/arm/mach-realview/hotplug.c @@ -12,7 +12,6 @@ #include <linux/errno.h> #include <linux/smp.h> -#include <asm/cacheflush.h> #include <asm/cp15.h> #include <asm/smp_plat.h> @@ -20,7 +19,6 @@ static inline void cpu_enter_lowpower(void) { unsigned int v; - flush_cache_all(); asm volatile( " mcr p15, 0, %1, c7, c5, 0\n" " mcr p15, 0, %1, c7, c10, 4\n" diff --git a/arch/arm/mach-shmobile/smp-sh73a0.c b/arch/arm/mach-shmobile/smp-sh73a0.c index acb46a9..2f1ef1b 100644 --- a/arch/arm/mach-shmobile/smp-sh73a0.c +++ b/arch/arm/mach-shmobile/smp-sh73a0.c @@ -119,14 +119,6 @@ static int sh73a0_cpu_kill(unsigned int cpu) static void sh73a0_cpu_die(unsigned int cpu) { - /* - * The ARM MPcore does not issue a cache coherency request for the L1 - * cache when powering off single CPUs. We must take care of this and - * further caches. - */ - dsb(); - flush_cache_all(); - /* Set power off mode. This takes the CPU out of the MP cluster */ scu_power_mode(scu_base_addr(), SCU_PM_POWEROFF); diff --git a/arch/arm/mach-spear13xx/hotplug.c b/arch/arm/mach-spear13xx/hotplug.c index a7d2dd1..d97749c 100644 --- a/arch/arm/mach-spear13xx/hotplug.c +++ b/arch/arm/mach-spear13xx/hotplug.c @@ -13,7 +13,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/smp.h> -#include <asm/cacheflush.h> #include <asm/cp15.h> #include <asm/smp_plat.h> @@ -21,7 +20,6 @@ static inline void cpu_enter_lowpower(void) { unsigned int v; - flush_cache_all(); asm volatile( " mcr p15, 0, %1, c7, c5, 0\n" " dsb\n" diff --git a/arch/arm/mach-tegra/common.h b/arch/arm/mach-tegra/common.h index 32f8eb3..5900cc4 100644 --- a/arch/arm/mach-tegra/common.h +++ b/arch/arm/mach-tegra/common.h @@ -2,4 +2,3 @@ extern struct smp_operations tegra_smp_ops; extern int tegra_cpu_kill(unsigned int cpu); extern void tegra_cpu_die(unsigned int cpu); -extern int tegra_cpu_disable(unsigned int cpu); diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c index a599f6e..e8323bc 100644 --- a/arch/arm/mach-tegra/hotplug.c +++ b/arch/arm/mach-tegra/hotplug.c @@ -12,7 +12,6 @@ #include <linux/smp.h> #include <linux/clk/tegra.h> -#include <asm/cacheflush.h> #include <asm/smp_plat.h> #include "sleep.h" @@ -47,15 +46,6 @@ void __ref tegra_cpu_die(unsigned int cpu) BUG(); } -int tegra_cpu_disable(unsigned int cpu) -{ - /* - * we don't allow CPU 0 to be shutdown (it is still too special - * e.g. clock tick interrupts) - */ - return cpu == 0 ? -EPERM : 0; -} - #ifdef CONFIG_ARCH_TEGRA_2x_SOC extern void tegra20_hotplug_shutdown(void); void __init tegra20_hotplug_init(void) diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 2c6b3d5..ec33ec8 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -192,6 +192,5 @@ struct smp_operations tegra_smp_ops __initdata = { #ifdef CONFIG_HOTPLUG_CPU .cpu_kill = tegra_cpu_kill, .cpu_die = tegra_cpu_die, - .cpu_disable = tegra_cpu_disable, #endif }; diff --git a/arch/arm/mach-ux500/hotplug.c b/arch/arm/mach-ux500/hotplug.c index 2f6af25..1c55a55 100644 --- a/arch/arm/mach-ux500/hotplug.c +++ b/arch/arm/mach-ux500/hotplug.c @@ -12,7 +12,6 @@ #include <linux/errno.h> #include <linux/smp.h> -#include <asm/cacheflush.h> #include <asm/smp_plat.h> #include <mach/setup.h> @@ -24,8 +23,6 @@ */ void __ref ux500_cpu_die(unsigned int cpu) { - flush_cache_all(); - /* directly enter low power state, skipping secure registers */ for (;;) { __asm__ __volatile__("dsb\n\t" "wfi\n\t" diff --git a/arch/arm/mach-vexpress/hotplug.c b/arch/arm/mach-vexpress/hotplug.c index a141b98..f0ce6b8 100644 --- a/arch/arm/mach-vexpress/hotplug.c +++ b/arch/arm/mach-vexpress/hotplug.c @@ -12,7 +12,6 @@ #include <linux/errno.h> #include <linux/smp.h> -#include <asm/cacheflush.h> #include <asm/smp_plat.h> #include <asm/cp15.h> @@ -20,7 +19,6 @@ static inline void cpu_enter_lowpower(void) { unsigned int v; - flush_cache_all(); asm volatile( "mcr p15, 0, %1, c7, c5, 0\n" " mcr p15, 0, %1, c7, c10, 4\n"