big.LITTLE low-level CPU and cluster power management
Subsystems (6)
cover big.LITTLE low-level CPU and cluster power management
This is the initial public posting of the initial support for big.LITTLE. Included here is the code required to safely power up and down CPUs in a b.L system, whether this is via CPU hotplug, a cpuidle driver or the Linaro b.L in-kernel switcher[*] on top of this. Only SMP secondary boot and CPU hotplug support is included at this time. Getting to this point already represents a significcant chunk of code as illustrated by the diffstat below.
This work was presented at Linaro Connect in Copenhagen by Dave Martin and myself. The presentation slides are available here:
http://www.linaro.org/documents/download/f3569407bb1fb8bde0d6da80e285b832508f92f57223c
The code is now stable on both Fast Models as well as Virtual Express TC2 and ready for public review.
Platform support is included for Fast Models implementing the Cortex-A15x4-A7x4 and Cortex-A15x1-A7x1 configurations. To allow successful compilation, I also included a preliminary version of the CCI400 driver from Lorenzo Pieralisi.
Support for actual hardware such as Vexpress TC2 should come later, once the basic infrastructure from this series is merged. A few DT bindings are used but not yet documented.
This series is made of the following parts:
Low-level support code:
[PATCH 01/16] ARM: b.L: secondary kernel entry code [PATCH 02/16] ARM: b.L: introduce the CPU/cluster power API [PATCH 03/16] ARM: b.L: introduce helpers for platform coherency [PATCH 04/16] ARM: b.L: Add baremetal voting mutexes [PATCH 05/16] ARM: bL_head: vlock-based first man election
Adaptation layer to hook with the generic kernel infrastructure:
[PATCH 06/16] ARM: b.L: generic SMP secondary bringup and hotplug [PATCH 07/16] ARM: bL_platsmp.c: close the kernel entry gate before [PATCH 08/16] ARM: bL_platsmp.c: make sure the GIC interface of a [PATCH 09/16] ARM: vexpress: Select the correct SMP operations at
Fast Models support:
[PATCH 10/16] ARM: vexpress: introduce DCSCB support [PATCH 11/16] ARM: vexpress/dcscb: add CPU use counts to the power [PATCH 12/16] ARM: vexpress/dcscb: do not hardcode number of CPUs [PATCH 13/16] drivers: misc: add ARM CCI support
[PATCH 14/16] ARM: TC2: ensure powerdown-time data is flushed from [PATCH 15/16] ARM: vexpress/dcscb: handle platform coherency [PATCH 16/16] ARM: vexpress/dcscb: probe via device tree
Here's the diffstat:
.../big.LITTLE/cluster-pm-race-avoidance.txt | 498 ++++++++++++++++++ Documentation/arm/big.LITTLE/vlocks.txt | 211 ++++++++ arch/arm/Kconfig | 6 + arch/arm/common/Makefile | 3 + arch/arm/common/bL_entry.c | 278 ++++++++++ arch/arm/common/bL_head.S | 232 ++++++++ arch/arm/common/bL_platsmp.c | 85 +++ arch/arm/common/gic.c | 6 + arch/arm/common/vlock.S | 108 ++++ arch/arm/common/vlock.h | 43 ++ arch/arm/include/asm/bL_entry.h | 189 +++++++ arch/arm/include/asm/hardware/gic.h | 2 + arch/arm/include/asm/mach/arch.h | 3 + arch/arm/kernel/setup.c | 5 +- arch/arm/mach-vexpress/Kconfig | 9 + arch/arm/mach-vexpress/Makefile | 1 + arch/arm/mach-vexpress/core.h | 2 + arch/arm/mach-vexpress/dcscb.c | 257 +++++++++ arch/arm/mach-vexpress/dcscb_setup.S | 77 +++ arch/arm/mach-vexpress/platsmp.c | 12 + arch/arm/mach-vexpress/v2m.c | 2 +- drivers/misc/Kconfig | 4 + drivers/misc/Makefile | 1 + drivers/misc/arm-cci.c | 124 +++++ include/linux/arm-cci.h | 30 ++ 25 files changed, 2186 insertions(+), 2 deletions(-)
Review comments are welcome!
[*] General design information on the b.L switcher can be found here:
http://lwn.net/Articles/481055/
However the code is only accessible to Linaro members for the time being.
PATCH 1/16 ARM: b.L: secondary kernel entry code
CPUs in a big.LITTLE systems have special needs when entering the kernel due to a hotplug event, or when resuming from a deep sleep mode.
This is vectorized so multiple CPUs can enter the kernel in parallel without serialization.
Only the basic structure is introduced here. This will be extended later.
TODO: MPIDR based indexing should eventually be made runtime adjusted.
--- arch/arm/Kconfig | 6 +++ arch/arm/common/Makefile | 3 ++ arch/arm/common/bL_entry.c | 30 +++++++++++++++ arch/arm/common/bL_head.S | 81 +++++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/bL_entry.h | 35 ++++++++++++++++++ 5 files changed, 155 insertions(+) create mode 100644 arch/arm/common/bL_entry.c create mode 100644 arch/arm/common/bL_head.S create mode 100644 arch/arm/include/asm/bL_entry.h
arch/arm/Kconfig +6 -0
@@ -1579,6 +1579,12 @@ config HAVE_ARM_TWD help This options enables support for the ARM timer and watchdog unit +config BIG_LITTLE + bool "big.LITTLE support (Experimental)" + depends on CPU_V7 && SMP && EXPERIMENTAL + help + This option enables support for the big.LITTLE architecture. + choice prompt "Memory split" default VMSPLIT_3G
arch/arm/common/Makefile +3 -0
@@ -13,3 +13,6 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o obj-$(CONFIG_SHARP_SCOOP) += scoop.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o +obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o +obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o +obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o
arch/arm/common/bL_entry.c new file +30 -0
@@ -0,0 +1,30 @@ +/* +* arch/arm/common/bL_entry.c -- big.LITTLE kernel re-entry point +* +* Created by: Nicolas Pitre, March 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bL_entry.h> +#include <asm/barrier.h> +#include <asm/proc-fns.h> +#include <asm/cacheflush.h> + +extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ]; + +void bL_set_entry_vector (unsigned cpu,unsigned cluster,void *ptr) +{ +unsigned long val = ptr ?virt_to_phys (ptr) :0 ; + bL_entry_vectors[cluster][cpu] = val; +smp_wmb (); +__cpuc_flush_dcache_area ((void *)&bL_entry_vectors[cluster][cpu],4 ); +outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]), +__pa (&bL_entry_vectors[cluster][cpu +1 ])); +}
arch/arm/common/bL_head.S new file +81 -0
81 lines not on this page · raw message
arch/arm/include/asm/bL_entry.h new file +35 -0
@@ -0,0 +1,35 @@ +/* +* arch/arm/include/asm/bL_entry.h +* +* Created by: Nicolas Pitre, April 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#ifndef BL_ENTRY_H +#define BL_ENTRY_H + +#define BL_CPUS_PER_CLUSTER 4 +#define BL_NR_CLUSTERS 2 + +#ifndef __ASSEMBLY__ + +/* +* Platform specific code should use this symbol to set up secondary +* entry location for processors to use when released from reset. +*/ +extern void bL_entry_point (void ); + +/* +* This is used to indicate where the given CPU from given cluster should +* branch once it is ready to re-enter the kernel using ptr, or NULL if it +* should be gated. A gated CPU is held in a WFE loop until its vector +* becomes non NULL. +*/ +void bL_set_entry_vector (unsigned cpu,unsigned cluster,void *ptr); + +#endif /* ! __ASSEMBLY__ */ +#endif
Signature · 1 line
1.8.0
PATCH 1/16 ARM: b.L: secondary kernel entry code
arch/arm/Kconfig config HAVE_ARM_TWD · line 1584
Quoted 11 lines · Nicolas Pitre
arch/arm/Kconfig +3
-0@@ -1579,6 +1579,12 @@ config HAVE_ARM_TWD help This options enables support for the ARM timer and watchdog unit +config BIG_LITTLE + bool "big.LITTLE support (Experimental)" + depends on CPU_V7 && SMP && EXPERIMENTAL
I thought EXPERIMENTAL was being phased out?
arch/arm/common/Makefile obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o · line 17
Quoted 10 lines
arch/arm/common/Makefile +2
-0@@ -13,3 +13,6 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o obj-$(CONFIG_SHARP_SCOOP) += scoop.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o +obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o +obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o
This looks like non-related stuff?
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH 1/16 ARM: b.L: secondary kernel entry code
Quoted 9 lines · Stephen Boyd
Quoted 8 lines · Nicolas Pitre
arch/arm/Kconfig +0
-0@@ -1579,6 +1579,12 @@ config HAVE_ARM_TWD help This options enables support for the ARM timer and watchdog unit
Quoted 5 lines
arch/arm/Kconfig config HAVE_ARM_TWD · line 1584
Quoted 3 lines
arch/arm/Kconfig +3
-0+config BIG_LITTLE + bool "big.LITTLE support (Experimental)" + depends on CPU_V7 && SMP && EXPERIMENTALI thought EXPERIMENTAL was being phased out?
True.
Quoted 4 lines
Quoted 4 lines
arch/arm/common/Makefile +0
-0
Quoted 8 lines
arch/arm/common/Makefile obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o · line 17
Quoted 6 lines
arch/arm/common/Makefile +2
-0@@ -13,3 +13,6 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o obj-$(CONFIG_SHARP_SCOOP) += scoop.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o +obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o +obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.oThis looks like non-related stuff?
Indeed. Rebase fallouts.
Thanks.
Nicolas
PATCH 1/16 ARM: b.L: secondary kernel entry code
In reply to Nicolas PitreHi Nico,
Quoted 2 lines · Nicolas Pitre
arch/arm/common/bL_entry.c new file +0
-0
...
Quoted 1 line
+extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS][BL_CPUS_PER_CLUSTER];
IMHO, we should keep this array linear and ignore the cluster grouping at this stage. This information could be added to latter patches that actually need to know about the b.L topology. This would also imply that we treat the MPIDR just as an ID without digging into its bit layout. But I haven't looked at the other patches yet to see how this would fit.
arch/arm/common/bL_entry.c line 29
Quoted 8 lines
arch/arm/common/bL_entry.c new file +8
-0+void bL_set_entry_vector (unsigned cpu,unsigned cluster,void *ptr) +{ +unsigned long val = ptr ?virt_to_phys (ptr) :0 ; + bL_entry_vectors[cluster][cpu] = val; +smp_wmb (); +__cpuc_flush_dcache_area ((void *)&bL_entry_vectors[cluster][cpu],4 ); +outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]), +__pa (&bL_entry_vectors[cluster][cpu +1 ]));
Why are you using the smp_wmb() here? We don't need any barrier since data cache ops by MVA are automatically ordered in relation to stores to the same MVA (as long as the MVA is in Normal Cacheable memory).
Quoted 2 lines
arch/arm/common/bL_head.S new file +0
-0
...
arch/arm/common/bL_head.S line 39
Quoted 7 lines
arch/arm/common/bL_head.S new file +7
-0+ENTRY(bL_entry_point) + + THUMB( adr r12, BSYM(1f) ) + THUMB( bx r12 ) + THUMB( .thumb ) +1: + mrc p15, 0, r0, c0, c0, 5
Minor thing, maybe a comment for this line like @ MPIDR.
Signature · 1 line
Catalin
PATCH 1/16 ARM: b.L: secondary kernel entry code
Quoted 11 lines · Catalin Marinas
Hi Nico,
Quoted 2 lines · Nicolas Pitre
arch/arm/common/bL_entry.c new file +0
-0...
Quoted 1 line
+extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS][BL_CPUS_PER_CLUSTER];
IMHO, we should keep this array linear and ignore the cluster grouping at this stage. This information could be added to latter patches that actually need to know about the b.L topology.
That's virtually all of them. Everything b.L related is always expressed in terms of a cpu,cluster tuple at the low level.
Quoted 2 lines
This would also imply that we treat the MPIDR just as an ID without digging into its bit layout.
That makes for too large an index space. We always end up needing to break the MPIDR into a cpu,cluster thing as the MPIDR bits are too sparse.
Quoted 4 lines
Quoted 4 lines
arch/arm/common/bL_entry.c new file +4
-0+void bL_set_entry_vector (unsigned cpu,unsigned cluster,void *ptr) +{ +unsigned long val = ptr ?virt_to_phys (ptr) :0 ; + bL_entry_vectors[cluster][cpu] = val;
Quoted 8 lines
arch/arm/common/bL_entry.c line 29
Quoted 4 lines
arch/arm/common/bL_entry.c new file +4
-0+smp_wmb (); +__cpuc_flush_dcache_area ((void *)&bL_entry_vectors[cluster][cpu],4 ); +outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]), +__pa (&bL_entry_vectors[cluster][cpu +1 ]));Why are you using the
smp_wmb()here? We don't need any barrier since data cache ops by MVA are automatically ordered in relation to stores to the same MVA (as long as the MVA is in Normal Cacheable memory).
That was the result of monkeying the write_pen_release() code. I'll remove that as the rest of the code added later doesn't use that anyway.
Quoted 12 lines
Quoted 2 lines
arch/arm/common/bL_head.S new file +0
-0...
arch/arm/common/bL_head.S line 39
Quoted 7 lines
arch/arm/common/bL_head.S new file +7
-0+ENTRY(bL_entry_point) + + THUMB( adr r12, BSYM(1f) ) + THUMB( bx r12 ) + THUMB( .thumb ) +1: + mrc p15, 0, r0, c0, c0, 5Minor thing, maybe a comment for this line like @ MPIDR.
ACK.
Nicolas
PATCH 1/16 ARM: b.L: secondary kernel entry code
Quoted 14 lines · Nicolas Pitre
Quoted 9 lines · Catalin Marinas
Quoted 2 lines · Nicolas Pitre
arch/arm/common/bL_entry.c new file +0
-0...
Quoted 1 line
+extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS][BL_CPUS_PER_CLUSTER];
IMHO, we should keep this array linear and ignore the cluster grouping at this stage. This information could be added to latter patches that actually need to know about the b.L topology.
That's virtually all of them. Everything b.L related is always expressed in terms of a cpu,cluster tuple at the low level.
Quoted 6 lines
Quoted 2 lines
This would also imply that we treat the MPIDR just as an ID without digging into its bit layout.
That makes for too large an index space. We always end up needing to break the MPIDR into a cpu,cluster thing as the MPIDR bits are too sparse.
You could find a way to compress this with some mask and shifts. We can look at this later if we are to generalise this to non-b.L systems.
Signature · 1 line
Catalin
PATCH 1/16 ARM: b.L: secondary kernel entry code
Quoted 18 lines · Catalin Marinas
Quoted 17 lines · Nicolas Pitre
Quoted 9 lines · Catalin Marinas
Quoted 2 lines · Nicolas Pitre
arch/arm/common/bL_entry.c new file +0
-0...
Quoted 1 line
+extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS][BL_CPUS_PER_CLUSTER];
IMHO, we should keep this array linear and ignore the cluster grouping at this stage. This information could be added to latter patches that actually need to know about the b.L topology.
That's virtually all of them. Everything b.L related is always expressed in terms of a cpu,cluster tuple at the low level.
Quoted 2 lines
This would also imply that we treat the MPIDR just as an ID without digging into its bit layout.
Quoted 6 lines
Quoted 3 lines
That makes for too large an index space. We always end up needing to break the MPIDR into a cpu,cluster thing as the MPIDR bits are too sparse.
You could find a way to compress this with some mask and shifts. We can look at this later if we are to generalise this to non-b.L systems.
The MPIDR linearization (a simple hash to convert it to a linear index) is planned anyway since code paths like cpu_{suspend/resume} do not work for multi-cluster systems as things stand.
Lorenzo
PATCH 1/16 ARM: b.L: secondary kernel entry code
In reply to Catalin MarinasQuoted 18 lines · Catalin Marinas
Quoted 17 lines · Nicolas Pitre
Quoted 9 lines · Catalin Marinas
Quoted 2 lines · Nicolas Pitre
arch/arm/common/bL_entry.c new file +0
-0...
Quoted 1 line
+extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS][BL_CPUS_PER_CLUSTER];
IMHO, we should keep this array linear and ignore the cluster grouping at this stage. This information could be added to latter patches that actually need to know about the b.L topology.
That's virtually all of them. Everything b.L related is always expressed in terms of a cpu,cluster tuple at the low level.
Quoted 2 lines
This would also imply that we treat the MPIDR just as an ID without digging into its bit layout.
Quoted 6 lines
Quoted 3 lines
That makes for too large an index space. We always end up needing to break the MPIDR into a cpu,cluster thing as the MPIDR bits are too sparse.
You could find a way to compress this with some mask and shifts. We can look at this later if we are to generalise this to non-b.L systems.
The b.L cluster handling code has multiple instances of this issue. We should either try to fix them all, or defer them all as being overkill for the foreseeable future.
For current platforms, the space saved in unlikely to be larger than the amount of code required to implement the optimisation.
I do think we need a good, generic way to map sparesely-populated, multidimensional topological node IDs to/from a linear space, but we should avoid reinventing that too many times.
I think Lorenzo was already potentially looking at this issue in relation to managing cpu_logical_map.
Cheers
---Dave
PATCH 1/16 ARM: b.L: secondary kernel entry code
In reply to Nicolas PitreQuoted 10 lines · Nicolas Pitre
CPUs in a big.LITTLE systems have special needs when entering the kernel due to a hotplug event, or when resuming from a deep sleep mode.
This is vectorized so multiple CPUs can enter the kernel in parallel without serialization.
Only the basic structure is introduced here. This will be extended later.
TODO: MPIDR based indexing should eventually be made runtime adjusted.
Agreed.
Quoted 16 lines
arch/arm/common/bL_entry.c new file +10
-0@@ -0,0 +1,30 @@ +/* +* arch/arm/common/bL_entry.c -- big.LITTLE kernel re-entry point +* +* Created by: Nicolas Pitre, March 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/
arch/arm/common/bL_entry.c line 20
Quoted 10 lines
arch/arm/common/bL_entry.c new file +10
-0+ +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bL_entry.h> +#include <asm/barrier.h> +#include <asm/proc-fns.h> +#include <asm/cacheflush.h> + +extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ];
Does this actually need to be volatile? I'd have thought a compiler barrier in place of the smp_wmb below would be enough (following on from Catalin's comments).
Quoted 56 lines
arch/arm/common/bL_entry.c new file +10
-0+ +void bL_set_entry_vector (unsigned cpu,unsigned cluster,void *ptr) +{ +unsigned long val = ptr ?virt_to_phys (ptr) :0 ; + bL_entry_vectors[cluster][cpu] = val; +smp_wmb (); +__cpuc_flush_dcache_area ((void *)&bL_entry_vectors[cluster][cpu],4 ); +outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]), +__pa (&bL_entry_vectors[cluster][cpu +1 ])); +}arch/arm/common/bL_head.S new file +40
-0@@ -0,0 +1,81 @@ +/* + * arch/arm/common/bL_head.S -- big.LITTLE kernel re-entry point + * + * Created by: Nicolas Pitre, March 2012 + * Copyright: (C) 2012 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/bL_entry.h> + + .macro pr_dbg cpu, string +#if defined(CONFIG_DEBUG_LL) && defined(DEBUG) + b 1901f +1902: .ascii "CPU 0: \0CPU 1: \0CPU 2: \0CPU 3: \0" + .ascii "CPU 4: \0CPU 5: \0CPU 6: \0CPU 7: \0" +1903: .asciz "\string" + .align +1901: adr r0, 1902b + add r0, r0, \cpu, lsl #3 + bl printascii + adr r0, 1903b + bl printascii +#endif + .endm + + .arm + .align + +ENTRY(bL_entry_point) + + THUMB( adr r12, BSYM(1f) ) + THUMB( bx r12 ) + THUMB( .thumb ) +1: + mrc p15, 0, r0, c0, c0, 5 + ubfx r9, r0, #0, #4 @ r9 = cpu
arch/arm/common/bL_head.S line 50
Quoted 10 lines
arch/arm/common/bL_head.S new file +10
-0+ ubfx r10, r0, #8, #4 @ r10 = cluster + mov r3, #BL_CPUS_PER_CLUSTER + mla r4, r3, r10, r9 @ r4 = canonical CPU index + cmp r4, #(BL_CPUS_PER_CLUSTER * BL_NR_CLUSTERS) + blo 2f + + /* We didn't expect this CPU. Try to make it quiet. */ +1: wfi + wfe + b 1b
I realise this CPU is stuck at this point, but you should have a dsb before a wfi instruction. This could be problematic with the CCI this early, so maybe just a comment saying that it doesn't matter because we don't care about this core?
Quoted 21 lines
arch/arm/common/bL_head.S new file +21
-0+ +2: pr_dbg r4, "kernel bL_entry_point\n" + + /* + * MMU is off so we need to get to bL_entry_vectors in a + * position independent way. + */ + adr r5, 3f + ldr r6, [r5] + add r6, r5, r6 @ r6 = bL_entry_vectors + +bL_entry_gated: + ldr r5, [r6, r4, lsl #2] @ r5 = CPU entry vector + cmp r5, #0 + wfeeq + beq bL_entry_gated + pr_dbg r4, "released\n" + bx r5 + + .align 2 +
arch/arm/common/bL_head.S line 81
Quoted 10 lines
arch/arm/common/bL_head.S new file +10
-0+3: .word bL_entry_vectors - . + +ENDPROC(bL_entry_point) + + .bss + .align 5 + + .type bL_entry_vectors, #object +ENTRY(bL_entry_vectors) + .space 4 * BL_NR_CLUSTERS * BL_CPUS_PER_CLUSTER
Is there a particular reason to put this in the bss?
Quoted 12 lines
arch/arm/include/asm/bL_entry.h new file +6
-0@@ -0,0 +1,35 @@ +/* +* arch/arm/include/asm/bL_entry.h +* +* Created by: Nicolas Pitre, April 2012 +* Copyright: (C) 2012 Linaro Limited +*
arch/arm/include/asm/bL_entry.h line 16
Quoted 10 lines
arch/arm/include/asm/bL_entry.h new file +10
-0+* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#ifndef BL_ENTRY_H +#define BL_ENTRY_H + +#define BL_CPUS_PER_CLUSTER 4 +#define BL_NR_CLUSTERS 2
Hmm, I see these have to be constant so you can allocate your space in the assembly file. In which case, I think it's worth changing their names to have MAX or LIMIT in them... maybe they could even be CONFIG options?
Will
PATCH 1/16 ARM: b.L: secondary kernel entry code
Quoted 36 lines · Will Deacon
Quoted 10 lines · Nicolas Pitre
CPUs in a big.LITTLE systems have special needs when entering the kernel due to a hotplug event, or when resuming from a deep sleep mode.
This is vectorized so multiple CPUs can enter the kernel in parallel without serialization.
Only the basic structure is introduced here. This will be extended later.
TODO: MPIDR based indexing should eventually be made runtime adjusted.
Agreed.
Quoted 22 lines
arch/arm/common/bL_entry.c new file +16
-0@@ -0,0 +1,30 @@ +/* +* arch/arm/common/bL_entry.c -- big.LITTLE kernel re-entry point +* +* Created by: Nicolas Pitre, March 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bL_entry.h> +#include <asm/barrier.h>
Quoted 8 lines
arch/arm/common/bL_entry.c line 20
Quoted 4 lines
arch/arm/common/bL_entry.c new file +4
-0+#include <asm/proc-fns.h> +#include <asm/cacheflush.h> + +extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ];Does this actually need to be volatile? I'd have thought a compiler barrier in place of the
smp_wmbbelow would be enough (following on from Catalin's comments).
Actually, I did the reverse i.e. I removed the smp_wmb() entirely. A compiler barrier forces the whole world to memory while here we only want this particular assignment to be pushed out.
Furthermore, I like the volatile as it flags that this is a special variable which in this case is also accessed from CPUs with no cache.
Quoted 62 lines
Quoted 62 lines
arch/arm/common/bL_entry.c new file +9
-0+void bL_set_entry_vector (unsigned cpu,unsigned cluster,void *ptr) +{ +unsigned long val = ptr ?virt_to_phys (ptr) :0 ; + bL_entry_vectors[cluster][cpu] = val; +smp_wmb (); +__cpuc_flush_dcache_area ((void *)&bL_entry_vectors[cluster][cpu],4 ); +outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]), +__pa (&bL_entry_vectors[cluster][cpu +1 ])); +}arch/arm/common/bL_head.S new file +47
-0@@ -0,0 +1,81 @@ +/* + * arch/arm/common/bL_head.S -- big.LITTLE kernel re-entry point + * + * Created by: Nicolas Pitre, March 2012 + * Copyright: (C) 2012 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/bL_entry.h> + + .macro pr_dbg cpu, string +#if defined(CONFIG_DEBUG_LL) && defined(DEBUG) + b 1901f +1902: .ascii "CPU 0: \0CPU 1: \0CPU 2: \0CPU 3: \0" + .ascii "CPU 4: \0CPU 5: \0CPU 6: \0CPU 7: \0" +1903: .asciz "\string" + .align +1901: adr r0, 1902b + add r0, r0, \cpu, lsl #3 + bl printascii + adr r0, 1903b + bl printascii +#endif + .endm + + .arm + .align + +ENTRY(bL_entry_point) + + THUMB( adr r12, BSYM(1f) ) + THUMB( bx r12 ) + THUMB( .thumb ) +1: + mrc p15, 0, r0, c0, c0, 5 + ubfx r9, r0, #0, #4 @ r9 = cpu + ubfx r10, r0, #8, #4 @ r10 = cluster + mov r3, #BL_CPUS_PER_CLUSTER + mla r4, r3, r10, r9 @ r4 = canonical CPU index + cmp r4, #(BL_CPUS_PER_CLUSTER * BL_NR_CLUSTERS) + blo 2f + + /* We didn't expect this CPU. Try to make it quiet. */
Quoted 8 lines
arch/arm/common/bL_head.S line 50
Quoted 3 lines
arch/arm/common/bL_head.S new file +3
-0+1: wfi + wfe + b 1bI realise this CPU is stuck at this point, but you should have a dsb before a wfi instruction. This could be problematic with the CCI this early, so maybe just a comment saying that it doesn't matter because we don't care about this core?
Why a dsb? No data was even touched at this point. And since this is meant to be a better "b ." kind of loop, I'd rather not try to make it more sophisticated than it already is. And of course it is meant to never be executed in practice.
Quoted 25 lines
Quoted 25 lines
arch/arm/common/bL_head.S new file +25
-0+ +2: pr_dbg r4, "kernel bL_entry_point\n" + + /* + * MMU is off so we need to get to bL_entry_vectors in a + * position independent way. + */ + adr r5, 3f + ldr r6, [r5] + add r6, r5, r6 @ r6 = bL_entry_vectors + +bL_entry_gated: + ldr r5, [r6, r4, lsl #2] @ r5 = CPU entry vector + cmp r5, #0 + wfeeq + beq bL_entry_gated + pr_dbg r4, "released\n" + bx r5 + + .align 2 + +3: .word bL_entry_vectors - . + +ENDPROC(bL_entry_point) +
Quoted 8 lines
arch/arm/common/bL_head.S line 81
Quoted 6 lines
arch/arm/common/bL_head.S new file +6
-0+ .bss + .align 5 + + .type bL_entry_vectors, #object +ENTRY(bL_entry_vectors) + .space 4 * BL_NR_CLUSTERS * BL_CPUS_PER_CLUSTERIs there a particular reason to put this in the bss?
Yes, to have it zero initialized without taking up binary space.
Quoted 18 lines
Quoted 18 lines
arch/arm/include/asm/bL_entry.h new file +12
-0@@ -0,0 +1,35 @@ +/* +* arch/arm/include/asm/bL_entry.h +* +* Created by: Nicolas Pitre, April 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#ifndef BL_ENTRY_H
Quoted 8 lines
arch/arm/include/asm/bL_entry.h line 16
Quoted 4 lines
arch/arm/include/asm/bL_entry.h new file +4
-0+#define BL_ENTRY_H + +#define BL_CPUS_PER_CLUSTER 4 +#define BL_NR_CLUSTERS 2Hmm, I see these have to be constant so you can allocate your space in the assembly file. In which case, I think it's worth changing their names to have MAX or LIMIT in them...
Yes, good point. I'll change them.
Quoted 1 line
maybe they could even be CONFIG options?
Nah. I prefer not adding new config options unless this is really necessary or useful. For the forseeable future, we'll see systems with at most 2 clusters and at most 4 CPUs per cluster. That could easily be revisited later if that becomes unsuitable for some new systems.
Initially I wanted all those things to be runtime sized in relation with the TODO item in the commit log. That too can come later.
Nicolas
PATCH 1/16 ARM: b.L: secondary kernel entry code
Quoted 9 lines · Nicolas Pitre
Quoted 7 lines · Will Deacon
Review
Quoted 2 lines · Nicolas Pitre
(quoted diff) +2
-0+ +extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS][BL_CPUS_PER_CLUSTER];Does this actually need to be volatile? I'd have thought a compiler barrier in place of the
smp_wmbbelow would be enough (following on from Catalin's comments).
Quoted 6 lines
Actually, I did the reverse i.e. I removed the
smp_wmb()entirely. A compiler barrier forces the whole world to memory while here we only want this particular assignment to be pushed out.Furthermore, I like the volatile as it flags that this is a special variable which in this case is also accessed from CPUs with no cache.
Ok, fair enough. Given that the smp_wmb isn't needed that sounds better.
Quoted 10 lines
Quoted 9 lines
arch/arm/common/bL_head.S line 50
Quoted 4 lines
arch/arm/common/bL_head.S new file +4
-0+ /* We didn't expect this CPU. Try to make it quiet. */ +1: wfi + wfe + b 1bI realise this CPU is stuck at this point, but you should have a dsb before a wfi instruction. This could be problematic with the CCI this early, so maybe just a comment saying that it doesn't matter because we don't care about this core?
Quoted 4 lines
Why a dsb? No data was even touched at this point. And since this is meant to be a better "b ." kind of loop, I'd rather not try to make it more sophisticated than it already is. And of course it is meant to never be executed in practice.
Sure, that's why I think just mentioning that we don't ever plan to boot this CPU is a good idea (so people don't add code here later on).
Quoted 27 lines
Quoted 18 lines
Quoted 18 lines
arch/arm/include/asm/bL_entry.h new file +12
-0@@ -0,0 +1,35 @@ +/* +* arch/arm/include/asm/bL_entry.h +* +* Created by: Nicolas Pitre, April 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#ifndef BL_ENTRY_H Quoted 8 lines
arch/arm/include/asm/bL_entry.h line 16
Quoted 4 lines
arch/arm/include/asm/bL_entry.h new file +4
-0+#define BL_ENTRY_H + +#define BL_CPUS_PER_CLUSTER 4 +#define BL_NR_CLUSTERS 2Hmm, I see these have to be constant so you can allocate your space in the assembly file. In which case, I think it's worth changing their names to have MAX or LIMIT in them...
Quoted 1 line
Yes, good point. I'll change them.
Thanks.
Quoted 6 lines
Quoted 1 line
maybe they could even be CONFIG options?
Nah. I prefer not adding new config options unless this is really necessary or useful. For the forseeable future, we'll see systems with at most 2 clusters and at most 4 CPUs per cluster. That could easily be revisited later if that becomes unsuitable for some new systems.
The current GIC is limited to 8 CPUs, so 4x2 is also a realistic possibility.
Quoted 2 lines
Initially I wanted all those things to be runtime sized in relation with the TODO item in the commit log. That too can come later.
Out of interest: how would you achieve that? I also thought about getting this information from the device tree, but I can't see how to plug that in with static storage.
Cheers,
Will
PATCH 1/16 ARM: b.L: secondary kernel entry code
Quoted 29 lines · Will Deacon
Quoted 9 lines · Nicolas Pitre
Quoted 7 lines · Will Deacon
Review
Quoted 2 lines · Nicolas Pitre
(quoted diff) +2
-0+ +extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS][BL_CPUS_PER_CLUSTER];Does this actually need to be volatile? I'd have thought a compiler barrier in place of the
smp_wmbbelow would be enough (following on from Catalin's comments).Quoted 6 lines
Actually, I did the reverse i.e. I removed the
smp_wmb()entirely. A compiler barrier forces the whole world to memory while here we only want this particular assignment to be pushed out.Furthermore, I like the volatile as it flags that this is a special variable which in this case is also accessed from CPUs with no cache.
Ok, fair enough. Given that the
smp_wmbisn't needed that sounds better.Quoted 10 lines
Quoted 9 lines
arch/arm/common/bL_head.S line 50
Quoted 4 lines
arch/arm/common/bL_head.S new file +4
-0+ /* We didn't expect this CPU. Try to make it quiet. */ +1: wfi + wfe + b 1bI realise this CPU is stuck at this point, but you should have a dsb before a wfi instruction. This could be problematic with the CCI this early, so maybe just a comment saying that it doesn't matter because we don't care about this core?
Quoted 7 lines
Quoted 4 lines
Why a dsb? No data was even touched at this point. And since this is meant to be a better "b ." kind of loop, I'd rather not try to make it more sophisticated than it already is. And of course it is meant to never be executed in practice.
Sure, that's why I think just mentioning that we don't ever plan to boot this CPU is a good idea (so people don't add code here later on).
I agree with the conclusions here.
Quoted 38 lines
Quoted 27 lines
Quoted 18 lines
Quoted 18 lines
arch/arm/include/asm/bL_entry.h new file +12
-0@@ -0,0 +1,35 @@ +/* +* arch/arm/include/asm/bL_entry.h +* +* Created by: Nicolas Pitre, April 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#ifndef BL_ENTRY_H Quoted 8 lines
arch/arm/include/asm/bL_entry.h line 16
Quoted 4 lines
arch/arm/include/asm/bL_entry.h new file +4
-0+#define BL_ENTRY_H + +#define BL_CPUS_PER_CLUSTER 4 +#define BL_NR_CLUSTERS 2Hmm, I see these have to be constant so you can allocate your space in the assembly file. In which case, I think it's worth changing their names to have MAX or LIMIT in them...
Quoted 1 line
Yes, good point. I'll change them.
Thanks.
Quoted 6 lines
Quoted 1 line
maybe they could even be CONFIG options?
Nah. I prefer not adding new config options unless this is really necessary or useful. For the forseeable future, we'll see systems with at most 2 clusters and at most 4 CPUs per cluster. That could easily be revisited later if that becomes unsuitable for some new systems.
Quoted 8 lines
The current GIC is limited to 8 CPUs, so 4x2 is also a realistic possibility.
Quoted 2 lines
Initially I wanted all those things to be runtime sized in relation with the TODO item in the commit log. That too can come later.
Out of interest: how would you achieve that? I also thought about getting this information from the device tree, but I can't see how to plug that in with static storage.
I think you would just have to bite the bullet and go dynamic in this case. But it's not a lot of data in total with the current limits, so this feels like overkill.
If we eventually need to go many-CPU with this code, it will need addressing, but there are no current plans for that that I know of.
Cheers
---Dave
PATCH 1/16 ARM: b.L: secondary kernel entry code
In reply to Nicolas PitreQuoted 13 lines · Nicolas Pitre
CPUs in a big.LITTLE systems have special needs when entering the kernel due to a hotplug event, or when resuming from a deep sleep mode.
This is vectorized so multiple CPUs can enter the kernel in parallel without serialization.
Only the basic structure is introduced here. This will be extended later.
TODO: MPIDR based indexing should eventually be made runtime adjusted.
Signed-off-by Nicolas Pitre <[email protected]>---
[..]
Quoted 17 lines
arch/arm/common/Makefile +3
-0@@ -13,3 +13,6 @@ obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o obj-$(CONFIG_SHARP_SCOOP) += scoop.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o +obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o +obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o +obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.oarch/arm/common/bL_entry.c new file +0
-0
arch/arm/common/bL_entry.c line 5
Quoted 5 lines
arch/arm/common/bL_entry.c new file +5
-0@@ -0,0 +1,30 @@ +/* +* arch/arm/common/bL_entry.c -- big.LITTLE kernel re-entry point +* +* Created by: Nicolas Pitre, March 2012 +* Copyright: (C) 2012 Linaro Limited
2013 now :-)
Looks like you need to update rest of the patches as well.
Quoted 15 lines
arch/arm/common/bL_entry.c new file +15
-0+* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/bL_entry.h> +#include <asm/barrier.h> +#include <asm/proc-fns.h> +#include <asm/cacheflush.h> + +extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ];
arch/arm/common/bL_entry.c line 30
Quoted 10 lines
arch/arm/common/bL_entry.c new file +10
-0+ +void bL_set_entry_vector (unsigned cpu,unsigned cluster,void *ptr) +{ +unsigned long val = ptr ?virt_to_phys (ptr) :0 ; + bL_entry_vectors[cluster][cpu] = val; +smp_wmb (); +__cpuc_flush_dcache_area ((void *)&bL_entry_vectors[cluster][cpu],4 ); +outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]), +__pa (&bL_entry_vectors[cluster][cpu +1 ])); +}
I had the same question about smp_wmb() as Catalin but after following rest of the comments, I understand it will be removed so thats good.
Quoted 45 lines
arch/arm/common/bL_head.S new file +39
-0@@ -0,0 +1,81 @@ +/* + * arch/arm/common/bL_head.S -- big.LITTLE kernel re-entry point + * + * Created by: Nicolas Pitre, March 2012 + * Copyright: (C) 2012 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/bL_entry.h> + + .macro pr_dbg cpu, string +#if defined(CONFIG_DEBUG_LL) && defined(DEBUG) + b 1901f +1902: .ascii "CPU 0: \0CPU 1: \0CPU 2: \0CPU 3: \0" + .ascii "CPU 4: \0CPU 5: \0CPU 6: \0CPU 7: \0" +1903: .asciz "\string" + .align +1901: adr r0, 1902b + add r0, r0, \cpu, lsl #3 + bl printascii + adr r0, 1903b + bl printascii +#endif + .endm + + .arm + .align + +ENTRY(bL_entry_point) + + THUMB( adr r12, BSYM(1f) ) + THUMB( bx r12 ) + THUMB( .thumb ) +1: + mrc p15, 0, r0, c0, c0, 5
arch/arm/common/bL_head.S line 49
Quoted 10 lines
arch/arm/common/bL_head.S new file +10
-0+ ubfx r9, r0, #0, #4 @ r9 = cpu + ubfx r10, r0, #8, #4 @ r10 = cluster + mov r3, #BL_CPUS_PER_CLUSTER + mla r4, r3, r10, r9 @ r4 = canonical CPU index + cmp r4, #(BL_CPUS_PER_CLUSTER * BL_NR_CLUSTERS) + blo 2f + + /* We didn't expect this CPU. Try to make it quiet. */ +1: wfi + wfe
Why do you need a wfe followed by wif ?
Just curious.
Regards
Santosh
PATCH 1/16 ARM: b.L: secondary kernel entry code
Quoted 13 lines · Santosh Shilimkar
Quoted 12 lines · Nicolas Pitre
arch/arm/common/bL_head.S new file +12
-0+ENTRY(bL_entry_point) + + THUMB( adr r12, BSYM(1f) ) + THUMB( bx r12 ) + THUMB( .thumb ) +1: + mrc p15, 0, r0, c0, c0, 5 + ubfx r9, r0, #0, #4 @ r9 = cpu + ubfx r10, r0, #8, #4 @ r10 = cluster + mov r3, #BL_CPUS_PER_CLUSTER + mla r4, r3, r10, r9 @ r4 = canonical CPU index + cmp r4, #(BL_CPUS_PER_CLUSTER * BL_NR_CLUSTERS)
Quoted 8 lines
arch/arm/common/bL_head.S line 49
Quoted 5 lines
arch/arm/common/bL_head.S new file +5
-0+ blo 2f + + /* We didn't expect this CPU. Try to make it quiet. */ +1: wfi + wfeWhy do you need a wfe followed by wif ?
Just curious.
If the WFI doesn't work because an interrupt is pending then the WFE might work better. But as I mentioned before, this is not intended to be used for other purposes than "we're really screwed so at least let's try to cheaply quieten this CPU" case.
Nicolas
PATCH 1/16 ARM: b.L: secondary kernel entry code
Quoted 24 lines · Nicolas Pitre
Quoted 13 lines · Santosh Shilimkar
Quoted 12 lines · Nicolas Pitre
arch/arm/common/bL_head.S new file +12
-0+ENTRY(bL_entry_point) + + THUMB( adr r12, BSYM(1f) ) + THUMB( bx r12 ) + THUMB( .thumb ) +1: + mrc p15, 0, r0, c0, c0, 5 + ubfx r9, r0, #0, #4 @ r9 = cpu + ubfx r10, r0, #8, #4 @ r10 = cluster + mov r3, #BL_CPUS_PER_CLUSTER + mla r4, r3, r10, r9 @ r4 = canonical CPU index + cmp r4, #(BL_CPUS_PER_CLUSTER * BL_NR_CLUSTERS)Quoted 8 lines
arch/arm/common/bL_head.S line 49
Quoted 5 lines
arch/arm/common/bL_head.S new file +5
-0+ blo 2f + + /* We didn't expect this CPU. Try to make it quiet. */ +1: wfi + wfeWhy do you need a wfe followed by wif ?
Just curious.
Quoted 5 lines
If the WFI doesn't work because an interrupt is pending then the WFE might work better. But as I mentioned before, this is not intended to be used for other purposes than "we're really screwed so at least let's try to cheaply quieten this CPU" case.
Thanks for clarification.
Regards
Santosh
PATCH 1/16 ARM: b.L: secondary kernel entry code
In reply to Nicolas PitreHi!
arch/arm/Kconfig config HAVE_ARM_TWD · line 1587
Quoted 12 lines
arch/arm/Kconfig +6
-0@@ -1579,6 +1579,12 @@ config HAVE_ARM_TWD help This options enables support for the ARM timer and watchdog unit +config BIG_LITTLE + bool "big.LITTLE support (Experimental)" + depends on CPU_V7 && SMP && EXPERIMENTAL + help + This option enables support for the big.LITTLE architecture. +
Perhaps few lines of what big.LITTLE is would be nice here?
It is that "few high-performance cores + few low-power cores" on chip, right?
BTW... is it possible/good idea to run all the cores at the same time for max performance? From descriptions I understood that is not normally done and I'd like to understand why....
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
PATCH 1/16 ARM: b.L: secondary kernel entry code
Quoted 8 lines · Pavel Machek
Hi!
Quoted 6 lines
arch/arm/Kconfig +0
-0@@ -1579,6 +1579,12 @@ config HAVE_ARM_TWD help This options enables support for the ARM timer and watchdog unit
Quoted 8 lines
arch/arm/Kconfig config HAVE_ARM_TWD · line 1587
Quoted 6 lines
arch/arm/Kconfig +6
-0+config BIG_LITTLE + bool "big.LITTLE support (Experimental)" + depends on CPU_V7 && SMP && EXPERIMENTAL + help + This option enables support for the big.LITTLE architecture. +Perhaps few lines of what big.LITTLE is would be nice here?
I'd invite you to look at the latest series posted on the list. This patch is obsolete.
Quoted 1 line
It is that "few high-performance cores + few low-power cores" on chip, right?
Right.
Quoted 3 lines
BTW... is it possible/good idea to run all the cores at the same time for max performance? From descriptions I understood that is not normally done and I'd like to understand why....
It can be done. But then the scheduler needs to be smarter about which task is put on which core.
Nicolas
PATCH 2/16 ARM: b.L: introduce the CPU/cluster power API
In reply to Nicolas PitreThis is the basic API used to handle the powering up/down of individual CPUs in a big.LITTLE system. The platform specific backend implementation has the responsibility to also handle the cluster level power as well when the first/last CPU in a cluster is brought up/down.
--- arch/arm/common/bL_entry.c | 88 +++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/bL_entry.h | 92 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+)
arch/arm/common/bL_entry.c +88 -0
@@ -11,11 +11,13 @@#include <linux/kernel.h> #include <linux/init.h> +#include <linux/irqflags.h> #include <asm/bL_entry.h> #include <asm/barrier.h> #include <asm/proc-fns.h> #include <asm/cacheflush.h> +#include <asm/idmap.h> extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ];
@@ -28,3 +30,89 @@ void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr)outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]),__pa (&bL_entry_vectors[cluster][cpu +1 ])); } + +static const struct bL_platform_power_ops *platform_ops; + +int __init bL_platform_power_register (const struct bL_platform_power_ops *ops) +{ +if (platform_ops) +return -EBUSY ; + platform_ops = ops; +return 0 ; +} + +int bL_cpu_power_up (unsigned int cpu,unsigned int cluster) +{ +if (!platform_ops) +return -EUNATCH ; +might_sleep (); +return platform_ops->power_up (cpu, cluster); +} + +typedef void (*phys_reset_t )(unsigned long ); + +void bL_cpu_power_down (void ) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* +* Do this before calling into the power_down method, +* as it might not always be safe to do afterwards. +*/ +setup_mm_for_reboot (); + + platform_ops->power_down (); + +/* +* It is possible for a power_up request to happen concurrently +* with a power_down request for the same CPU. In this case the +* power_down method might not be able to actually enter a +* powered down state with the WFI instruction if the power_up +* method has removed the required reset condition. The +* power_down method is then allowed to return. We must perform +* a re-entry in the kernel as if the power_up method just had +* deasserted reset on the CPU. +* +* To simplify race issues, the platform specific implementation +* must accommodate for the possibility of unordered calls to +* power_down and power_up with a usage count. Therefore, if a +* call to power_up is issued for a CPU that is not down, then +* the next call to power_down must not attempt a full shutdown +* but only do the minimum (normally disabling L1 cache and CPU +* coherency) and return just as if a concurrent power_up request +* had happened as described above. +*/ + + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); + +/* should never get here */ +BUG (); +} + +void bL_cpu_suspend (u64 expected_residency) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* Very similar to bL_cpu_power_down() */ +setup_mm_for_reboot (); + platform_ops->suspend (expected_residency); + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); +BUG (); +} + +int bL_cpu_powered_up (void ) +{ +if (!platform_ops) +return -EUNATCH ; +if (platform_ops->powered_up) + platform_ops->powered_up (); +return 0 ; +}
arch/arm/include/asm/bL_entry.h +92 -0
97 lines not on this page · raw message
Signature · 1 line
1.8.0
PATCH 2/16 ARM: b.L: introduce the CPU/cluster power API
Quoted 38 lines · Nicolas Pitre
This is the basic API used to handle the powering up/down of individual CPUs in a big.LITTLE system. The platform specific backend implementation has the responsibility to also handle the cluster level power as well when the first/last CPU in a cluster is brought up/down.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_entry.c | 88 +++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/bL_entry.h | 92 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+)arch/arm/common/bL_entry.c +7
-0@@ -11,11 +11,13 @@#include <linux/kernel.h> #include <linux/init.h> +#include <linux/irqflags.h> #include <asm/bL_entry.h> #include <asm/barrier.h> #include <asm/proc-fns.h> #include <asm/cacheflush.h> +#include <asm/idmap.h> extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ];@@ -28,3 +30,89 @@ void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr)outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]),__pa (&bL_entry_vectors[cluster][cpu +1 ])); } + +static const struct bL_platform_power_ops *platform_ops; + +int __init bL_platform_power_register (const struct bL_platform_power_ops *ops) +{
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 47
Quoted 10 lines
arch/arm/common/bL_entry.c +10
-0+if (platform_ops) +return -EBUSY ; + platform_ops = ops; +return 0 ; +} + +int bL_cpu_power_up (unsigned int cpu,unsigned int cluster) +{ +if (!platform_ops) +return -EUNATCH ;
Is this the right error code?
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 52
Quoted 5 lines
arch/arm/common/bL_entry.c +5
-0+might_sleep (); +return platform_ops->power_up (cpu, cluster); +} + +typedef void (*phys_reset_t )(unsigned long );
Maybe it's worth putting this typedef in a header file somewhere. It's also used by the soft reboot code.
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 58
Quoted 6 lines
arch/arm/common/bL_entry.c +6
-0+ +void bL_cpu_power_down (void ) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops);
Seems a bit overkill, or are we unrecoverable by this point?
Quoted 140 lines
arch/arm/common/bL_entry.c +60
-0+BUG_ON (!irqs_disabled ()); + +/* +* Do this before calling into the power_down method, +* as it might not always be safe to do afterwards. +*/ +setup_mm_for_reboot (); + + platform_ops->power_down (); + +/* +* It is possible for a power_up request to happen concurrently +* with a power_down request for the same CPU. In this case the +* power_down method might not be able to actually enter a +* powered down state with the WFI instruction if the power_up +* method has removed the required reset condition. The +* power_down method is then allowed to return. We must perform +* a re-entry in the kernel as if the power_up method just had +* deasserted reset on the CPU. +* +* To simplify race issues, the platform specific implementation +* must accommodate for the possibility of unordered calls to +* power_down and power_up with a usage count. Therefore, if a +* call to power_up is issued for a CPU that is not down, then +* the next call to power_down must not attempt a full shutdown +* but only do the minimum (normally disabling L1 cache and CPU +* coherency) and return just as if a concurrent power_up request +* had happened as described above. +*/ + + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); + +/* should never get here */ +BUG (); +} + +void bL_cpu_suspend (u64 expected_residency) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* Very similar to bL_cpu_power_down() */ +setup_mm_for_reboot (); + platform_ops->suspend (expected_residency); + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); +BUG (); +} + +int bL_cpu_powered_up (void ) +{ +if (!platform_ops) +return -EUNATCH ; +if (platform_ops->powered_up) + platform_ops->powered_up (); +return 0 ; +}arch/arm/include/asm/bL_entry.h +72
-0@@ -31,5 +31,97 @@ extern void bL_entry_point(void);*/ void bL_set_entry_vector (unsigned cpu,unsigned cluster,void *ptr); +/* +* CPU/cluster power operations API for higher subsystems to use. +*/ + +/** +* bL_cpu_power_up - make given CPU in given cluster runable +* +* @cpu: CPU number within given cluster +* @cluster: cluster number for the CPU +* +* The identified CPU is brought out of reset. If the cluster was powered +* down then it is brought up as well, taking care not to let the other CPUs +* in the cluster run, and ensuring appropriate cluster setup. +* +* Caller must ensure the appropriate entry vector is initialized with +* bL_set_entry_vector() prior to calling this. +* +* This must be called in a sleepable context. However, the implementation +* is strongly encouraged to return early and let the operation happen +* asynchronously, especially when significant delays are expected. +* +* If the operation cannot be performed then an error code is returned. +*/ +int bL_cpu_power_up (unsigned int cpu,unsigned int cluster); + +/** +* bL_cpu_power_down - power the calling CPU down +* +* The calling CPU is powered down. +* +* If this CPU is found to be the "last man standing" in the cluster +* then the cluster is prepared for power-down too. +* +* This must be called with interrupts disabled. +* +* This does not return. Re-entry in the kernel is expected via +* bL_entry_point. +*/ +void bL_cpu_power_down (void ); + +/** +* bL_cpu_suspend - bring the calling CPU in a suspended state +* +* @expected_residency: duration in microseconds the CPU is expected +* to remain suspended, or 0 if unknown/infinity. +* +* The calling CPU is suspended. The expected residency argument is used +* as a hint by the platform specific backend to implement the appropriate +* sleep state level according to the knowledge it has on wake-up latency +* for the given hardware. +* +* If this CPU is found to be the "last man standing" in the cluster +* then the cluster may be prepared for power-down too, if the expected +* residency makes it worthwhile. +* +* This must be called with interrupts disabled. +* +* This does not return. Re-entry in the kernel is expected via +* bL_entry_point. +*/ +void bL_cpu_suspend (u64 expected_residency); + +/** +* bL_cpu_powered_up - housekeeping workafter a CPU has been powered up +* +* This lets the platform specific backend code perform needed housekeeping +* work. This must be called by the newly activated CPU as soon as it is +* fully operational in kernel space, before it enables interrupts. +* +* If the operation cannot be performed then an error code is returned. +*/ +int bL_cpu_powered_up (void );
arch/arm/include/asm/bL_entry.h extern void bL_entry_point(void); · line 115
Quoted 10 lines
arch/arm/include/asm/bL_entry.h +10
-0+ +/* +* Platform specific methods used in the implementation of the above API. +*/ +struct bL_platform_power_ops { +int (*power_up)(unsigned int cpu,unsigned int cluster); +void (*power_down)(void ); +void (*suspend)(u64 ); +void (*powered_up)(void ); +};
It would be good if these prototypes matched the PSCI code, then platforms could just glue them together directly.
Will
PATCH 2/16 ARM: b.L: introduce the CPU/cluster power API
Quoted 43 lines · Will Deacon
Quoted 42 lines · Nicolas Pitre
This is the basic API used to handle the powering up/down of individual CPUs in a big.LITTLE system. The platform specific backend implementation has the responsibility to also handle the cluster level power as well when the first/last CPU in a cluster is brought up/down.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_entry.c | 88 +++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/bL_entry.h | 92 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+)arch/arm/common/bL_entry.c +11
-0@@ -11,11 +11,13 @@#include <linux/kernel.h> #include <linux/init.h> +#include <linux/irqflags.h> #include <asm/bL_entry.h> #include <asm/barrier.h> #include <asm/proc-fns.h> #include <asm/cacheflush.h> +#include <asm/idmap.h> extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ];@@ -28,3 +30,89 @@ void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr)outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]),__pa (&bL_entry_vectors[cluster][cpu +1 ])); } + +static const struct bL_platform_power_ops *platform_ops; + +int __init bL_platform_power_register (const struct bL_platform_power_ops *ops) +{ +if (platform_ops) +return -EBUSY ; + platform_ops = ops; +return 0 ;
Quoted 8 lines
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 47
Quoted 6 lines
arch/arm/common/bL_entry.c +6
-0+} + +int bL_cpu_power_up (unsigned int cpu,unsigned int cluster) +{ +if (!platform_ops) +return -EUNATCH ;Is this the right error code?
It is as good as any other, with some meaning to be distinguished from the traditional ones like -ENOMEM or -EINVAL that the platform backends could return.
Would you prefer another one?
Quoted 8 lines
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 52
Quoted 5 lines
arch/arm/common/bL_entry.c +5
-0+might_sleep (); +return platform_ops->power_up (cpu, cluster); +} + +typedef void (*phys_reset_t )(unsigned long );Maybe it's worth putting this typedef in a header file somewhere. It's also used by the soft reboot code.
Agreed. Maybe separately from this series though.
Quoted 8 lines
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 58
Quoted 6 lines
arch/arm/common/bL_entry.c +6
-0+ +void bL_cpu_power_down (void ) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops);Seems a bit overkill, or are we unrecoverable by this point?
We are. The upper layer expects this CPU to be dead and there is no easy recovery possible. This is a "should never happen" condition, and the kernel is badly configured otherwise.
Quoted 146 lines
Quoted 146 lines
arch/arm/common/bL_entry.c +60
-0+BUG_ON (!irqs_disabled ()); + +/* +* Do this before calling into the power_down method, +* as it might not always be safe to do afterwards. +*/ +setup_mm_for_reboot (); + + platform_ops->power_down (); + +/* +* It is possible for a power_up request to happen concurrently +* with a power_down request for the same CPU. In this case the +* power_down method might not be able to actually enter a +* powered down state with the WFI instruction if the power_up +* method has removed the required reset condition. The +* power_down method is then allowed to return. We must perform +* a re-entry in the kernel as if the power_up method just had +* deasserted reset on the CPU. +* +* To simplify race issues, the platform specific implementation +* must accommodate for the possibility of unordered calls to +* power_down and power_up with a usage count. Therefore, if a +* call to power_up is issued for a CPU that is not down, then +* the next call to power_down must not attempt a full shutdown +* but only do the minimum (normally disabling L1 cache and CPU +* coherency) and return just as if a concurrent power_up request +* had happened as described above. +*/ + + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); + +/* should never get here */ +BUG (); +} + +void bL_cpu_suspend (u64 expected_residency) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* Very similar to bL_cpu_power_down() */ +setup_mm_for_reboot (); + platform_ops->suspend (expected_residency); + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); +BUG (); +} + +int bL_cpu_powered_up (void ) +{ +if (!platform_ops) +return -EUNATCH ; +if (platform_ops->powered_up) + platform_ops->powered_up (); +return 0 ; +}arch/arm/include/asm/bL_entry.h +77
-0@@ -31,5 +31,97 @@ extern void bL_entry_point(void);*/ void bL_set_entry_vector (unsigned cpu,unsigned cluster,void *ptr); +/* +* CPU/cluster power operations API for higher subsystems to use. +*/ + +/** +* bL_cpu_power_up - make given CPU in given cluster runable +* +* @cpu: CPU number within given cluster +* @cluster: cluster number for the CPU +* +* The identified CPU is brought out of reset. If the cluster was powered +* down then it is brought up as well, taking care not to let the other CPUs +* in the cluster run, and ensuring appropriate cluster setup. +* +* Caller must ensure the appropriate entry vector is initialized with +* bL_set_entry_vector() prior to calling this. +* +* This must be called in a sleepable context. However, the implementation +* is strongly encouraged to return early and let the operation happen +* asynchronously, especially when significant delays are expected. +* +* If the operation cannot be performed then an error code is returned. +*/ +int bL_cpu_power_up (unsigned int cpu,unsigned int cluster); + +/** +* bL_cpu_power_down - power the calling CPU down +* +* The calling CPU is powered down. +* +* If this CPU is found to be the "last man standing" in the cluster +* then the cluster is prepared for power-down too. +* +* This must be called with interrupts disabled. +* +* This does not return. Re-entry in the kernel is expected via +* bL_entry_point. +*/ +void bL_cpu_power_down (void ); + +/** +* bL_cpu_suspend - bring the calling CPU in a suspended state +* +* @expected_residency: duration in microseconds the CPU is expected +* to remain suspended, or 0 if unknown/infinity. +* +* The calling CPU is suspended. The expected residency argument is used +* as a hint by the platform specific backend to implement the appropriate +* sleep state level according to the knowledge it has on wake-up latency +* for the given hardware. +* +* If this CPU is found to be the "last man standing" in the cluster +* then the cluster may be prepared for power-down too, if the expected +* residency makes it worthwhile. +* +* This must be called with interrupts disabled. +* +* This does not return. Re-entry in the kernel is expected via +* bL_entry_point. +*/ +void bL_cpu_suspend (u64 expected_residency); + +/** +* bL_cpu_powered_up - housekeeping workafter a CPU has been powered up +* +* This lets the platform specific backend code perform needed housekeeping +* work. This must be called by the newly activated CPU as soon as it is +* fully operational in kernel space, before it enables interrupts. +* +* If the operation cannot be performed then an error code is returned. +*/ +int bL_cpu_powered_up (void ); + +/* +* Platform specific methods used in the implementation of the above API. +*/ +struct bL_platform_power_ops {
Quoted 8 lines
arch/arm/include/asm/bL_entry.h extern void bL_entry_point(void); · line 115
Quoted 5 lines
arch/arm/include/asm/bL_entry.h +5
-0+int (*power_up)(unsigned int cpu,unsigned int cluster); +void (*power_down)(void ); +void (*suspend)(u64 ); +void (*powered_up)(void ); +};It would be good if these prototypes matched the PSCI code, then platforms could just glue them together directly.
No.
I discussed this at length with Charles (the PSCI spec author) already. Even in the PSCI case, a minimum PSCI backend is necessary to do some impedance matching between what the PSCI calls expect as arguments and what this kernel specific API needs to express. For example, the UP method needs to always be provided with the address for bL_entry, irrespective of where the user of this kernel API wants execution to be resumed. There might be some cases where the backend might decide to override the desired power saving state because of other kernel induced constraints (ongoing DMA operation for example) that PSCI doesn't (and should not) know about. And the best place to arbitrate between those platform specific constraints is in this platform specific shim or backend.
Because of that, and because one feature of Linux is to not have stable APIs in the kernel so to be free to adapt them to future needs, I think it is best not to even try matching the PSCI interface here.
Nicolas
PATCH 2/16 ARM: b.L: introduce the CPU/cluster power API
Quoted 9 lines · Nicolas Pitre
Quoted 7 lines · Will Deacon
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 47
Quoted 4 lines · Nicolas Pitre
arch/arm/common/bL_entry.c +4
-0+int bL_cpu_power_up (unsigned int cpu,unsigned int cluster) +{ +if (!platform_ops) +return -EUNATCH ;Is this the right error code?
Quoted 5 lines
It is as good as any other, with some meaning to be distinguished from the traditional ones like
-ENOMEMor-EINVALthat the platform backends could return.Would you prefer another one?
-ENODEV? Nothing to lose sleep over though.
Quoted 20 lines
Quoted 8 lines
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 52
Quoted 5 lines
arch/arm/common/bL_entry.c +5
-0+might_sleep (); +return platform_ops->power_up (cpu, cluster); +} + +typedef void (*phys_reset_t )(unsigned long );Maybe it's worth putting this typedef in a header file somewhere. It's also used by the soft reboot code.
Agreed. Maybe separately from this series though.
Quoted 8 lines
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 58
Quoted 6 lines
arch/arm/common/bL_entry.c +6
-0+ +void bL_cpu_power_down (void ) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops);Seems a bit overkill, or are we unrecoverable by this point?
Quoted 3 lines
We are. The upper layer expects this CPU to be dead and there is no easy recovery possible. This is a "should never happen" condition, and the kernel is badly configured otherwise.
Okey doke, that's what I feared. The BUG_ON makes sense then.
Quoted 15 lines
Quoted 4 lines
Quoted 4 lines
arch/arm/include/asm/bL_entry.h +4
-0+/* +* Platform specific methods used in the implementation of the above API. +*/ +struct bL_platform_power_ops {Quoted 8 lines
arch/arm/include/asm/bL_entry.h extern void bL_entry_point(void); · line 115
Quoted 5 lines
arch/arm/include/asm/bL_entry.h +5
-0+int (*power_up)(unsigned int cpu,unsigned int cluster); +void (*power_down)(void ); +void (*suspend)(u64 ); +void (*powered_up)(void ); +};It would be good if these prototypes matched the PSCI code, then platforms could just glue them together directly.
No.
Quoted 12 lines
I discussed this at length with Charles (the PSCI spec author) already. Even in the PSCI case, a minimum PSCI backend is necessary to do some impedance matching between what the PSCI calls expect as arguments and what this kernel specific API needs to express. For example, the UP method needs to always be provided with the address for
bL_entry, irrespective of where the user of this kernel API wants execution to be resumed. There might be some cases where the backend might decide to override the desired power saving state because of other kernel induced constraints (ongoing DMA operation for example) that PSCI doesn't (and should not) know about. And the best place to arbitrate between those platform specific constraints is in this platform specific shim or backend.
Yes, you're right. I was thinking we could convert cpu/cluster into cpuid automatically, but actually it's not guaranteed that the PSCI firmware will follow the MPIDR format so we even need platform-specific marshalling for that.
Thanks for the reply,
Will
PATCH 2/16 ARM: b.L: introduce the CPU/cluster power API
In reply to Nicolas PitreQuoted 80 lines · Nicolas Pitre
Quoted 43 lines · Will Deacon
Quoted 42 lines · Nicolas Pitre
This is the basic API used to handle the powering up/down of individual CPUs in a big.LITTLE system. The platform specific backend implementation has the responsibility to also handle the cluster level power as well when the first/last CPU in a cluster is brought up/down.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_entry.c | 88 +++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/bL_entry.h | 92 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+)arch/arm/common/bL_entry.c +11
-0@@ -11,11 +11,13 @@#include <linux/kernel.h> #include <linux/init.h> +#include <linux/irqflags.h> #include <asm/bL_entry.h> #include <asm/barrier.h> #include <asm/proc-fns.h> #include <asm/cacheflush.h> +#include <asm/idmap.h> extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ];@@ -28,3 +30,89 @@ void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr)outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]),__pa (&bL_entry_vectors[cluster][cpu +1 ])); } + +static const struct bL_platform_power_ops *platform_ops; + +int __init bL_platform_power_register (const struct bL_platform_power_ops *ops) +{ +if (platform_ops) +return -EBUSY ; + platform_ops = ops; +return 0 ;Quoted 8 lines
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 47
Quoted 6 lines
arch/arm/common/bL_entry.c +6
-0+} + +int bL_cpu_power_up (unsigned int cpu,unsigned int cluster) +{ +if (!platform_ops) +return -EUNATCH ;Is this the right error code?
It is as good as any other, with some meaning to be distinguished from the traditional ones like
-ENOMEMor-EINVALthat the platform backends could return.Would you prefer another one?
Quoted 8 lines
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 52
Quoted 5 lines
arch/arm/common/bL_entry.c +5
-0+might_sleep (); +return platform_ops->power_up (cpu, cluster); +} + +typedef void (*phys_reset_t )(unsigned long );Maybe it's worth putting this typedef in a header file somewhere. It's also used by the soft reboot code.
Agreed. Maybe separately from this series though.
Quoted 8 lines
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 58
Quoted 6 lines
arch/arm/common/bL_entry.c +6
-0+ +void bL_cpu_power_down (void ) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops);Seems a bit overkill, or are we unrecoverable by this point?
Quoted 3 lines
We are. The upper layer expects this CPU to be dead and there is no easy recovery possible. This is a "should never happen" condition, and the kernel is badly configured otherwise.
bL_cpu_power_down() is unconditional and does not fail. This means that calling this function means that:
a) a subsequent call to bL_cpu_power_up() on this CPU will cause it
to jump to bL_entry_point, in something resembling reset state;
b) for all, part (or, rarely, none) of the intervening period, the CPU may really be turned off.
Without this BUG_ON() we would need to implement a dummy mechanism to send the CPU to bL_entry_point at the right time. If this happens instantaneously (without waiting for bL_cpu_power_up()), then this will likely lead to a spin of some sort unless it only happens occasionally. Also, the whole purpose of this function is to power off the CPU, permitting power savings, so if no means has been registered too do that, a call to bL_power_off() is certainly buggy misuse.
Quoted 171 lines
Quoted 147 lines
Quoted 147 lines
arch/arm/common/bL_entry.c +60
-0+BUG_ON (!irqs_disabled ()); + +/* +* Do this before calling into the power_down method, +* as it might not always be safe to do afterwards. +*/ +setup_mm_for_reboot (); + + platform_ops->power_down (); + +/* +* It is possible for a power_up request to happen concurrently +* with a power_down request for the same CPU. In this case the +* power_down method might not be able to actually enter a +* powered down state with the WFI instruction if the power_up +* method has removed the required reset condition. The +* power_down method is then allowed to return. We must perform +* a re-entry in the kernel as if the power_up method just had +* deasserted reset on the CPU. +* +* To simplify race issues, the platform specific implementation +* must accommodate for the possibility of unordered calls to +* power_down and power_up with a usage count. Therefore, if a +* call to power_up is issued for a CPU that is not down, then +* the next call to power_down must not attempt a full shutdown +* but only do the minimum (normally disabling L1 cache and CPU +* coherency) and return just as if a concurrent power_up request +* had happened as described above. +*/ + + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); + +/* should never get here */ +BUG (); +} + +void bL_cpu_suspend (u64 expected_residency) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* Very similar to bL_cpu_power_down() */ +setup_mm_for_reboot (); + platform_ops->suspend (expected_residency); + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); +BUG (); +} + +int bL_cpu_powered_up (void ) +{ +if (!platform_ops) +return -EUNATCH ; +if (platform_ops->powered_up) + platform_ops->powered_up (); +return 0 ; +}arch/arm/include/asm/bL_entry.h +77
-0@@ -31,5 +31,97 @@ extern void bL_entry_point(void);*/ void bL_set_entry_vector (unsigned cpu,unsigned cluster,void *ptr); +/* +* CPU/cluster power operations API for higher subsystems to use. +*/ + +/** +* bL_cpu_power_up - make given CPU in given cluster runable +* +* @cpu: CPU number within given cluster +* @cluster: cluster number for the CPU +* +* The identified CPU is brought out of reset. If the cluster was powered +* down then it is brought up as well, taking care not to let the other CPUs +* in the cluster run, and ensuring appropriate cluster setup. +* +* Caller must ensure the appropriate entry vector is initialized with +* bL_set_entry_vector() prior to calling this. +* +* This must be called in a sleepable context. However, the implementation +* is strongly encouraged to return early and let the operation happen +* asynchronously, especially when significant delays are expected. +* +* If the operation cannot be performed then an error code is returned. +*/ +int bL_cpu_power_up (unsigned int cpu,unsigned int cluster); + +/** +* bL_cpu_power_down - power the calling CPU down +* +* The calling CPU is powered down. +* +* If this CPU is found to be the "last man standing" in the cluster +* then the cluster is prepared for power-down too. +* +* This must be called with interrupts disabled. +* +* This does not return. Re-entry in the kernel is expected via +* bL_entry_point. +*/ +void bL_cpu_power_down (void ); + +/** +* bL_cpu_suspend - bring the calling CPU in a suspended state +* +* @expected_residency: duration in microseconds the CPU is expected +* to remain suspended, or 0 if unknown/infinity. +* +* The calling CPU is suspended. The expected residency argument is used +* as a hint by the platform specific backend to implement the appropriate +* sleep state level according to the knowledge it has on wake-up latency +* for the given hardware. +* +* If this CPU is found to be the "last man standing" in the cluster +* then the cluster may be prepared for power-down too, if the expected +* residency makes it worthwhile. +* +* This must be called with interrupts disabled. +* +* This does not return. Re-entry in the kernel is expected via +* bL_entry_point. +*/ +void bL_cpu_suspend (u64 expected_residency); + +/** +* bL_cpu_powered_up - housekeeping workafter a CPU has been powered up +* +* This lets the platform specific backend code perform needed housekeeping +* work. This must be called by the newly activated CPU as soon as it is +* fully operational in kernel space, before it enables interrupts. +* +* If the operation cannot be performed then an error code is returned. +*/ +int bL_cpu_powered_up (void ); + +/* +* Platform specific methods used in the implementation of the above API. +*/ +struct bL_platform_power_ops {Quoted 8 lines
arch/arm/include/asm/bL_entry.h extern void bL_entry_point(void); · line 115
Quoted 5 lines
arch/arm/include/asm/bL_entry.h +5
-0+int (*power_up)(unsigned int cpu,unsigned int cluster); +void (*power_down)(void ); +void (*suspend)(u64 ); +void (*powered_up)(void ); +};It would be good if these prototypes matched the PSCI code, then platforms could just glue them together directly.
No.
I discussed this at length with Charles (the PSCI spec author) already. Even in the PSCI case, a minimum PSCI backend is necessary to do some impedance matching between what the PSCI calls expect as arguments and what this kernel specific API needs to express. For example, the UP method needs to always be provided with the address for
bL_entry, irrespective of where the user of this kernel API wants execution to be resumed. There might be some cases where the backend might decide to override the desired power saving state because of other kernel induced constraints (ongoing DMA operation for example) that PSCI doesn't (and should not) know about. And the best place to arbitrate between those platform specific constraints is in this platform specific shim or backend.
Quoted 3 lines
Because of that, and because one feature of Linux is to not have stable APIs in the kernel so to be free to adapt them to future needs, I think it is best not to even try matching the PSCI interface here.
The kernel may need to do stuff in these functions, even if the underlying backend is PSCI, so they wouldn't just be a pass-through ... or does the mach-virt experience convince us that there would be nothing
to do? I feel unsure about that, but I've not looked at the mach-virt
code yet.
mach-virt lacks most of the hardware nasties which we can't ignore at the host kernel / firmware interface.
Cheers
---Dave
PATCH 2/16 ARM: b.L: introduce the CPU/cluster power API
In reply to Nicolas PitreQuoted 100 lines · Nicolas Pitre
This is the basic API used to handle the powering up/down of individual CPUs in a big.LITTLE system. The platform specific backend implementation has the responsibility to also handle the cluster level power as well when the first/last CPU in a cluster is brought up/down.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_entry.c | 88 +++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/bL_entry.h | 92 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+)arch/arm/common/bL_entry.c +68
-0@@ -11,11 +11,13 @@#include <linux/kernel.h> #include <linux/init.h> +#include <linux/irqflags.h> #include <asm/bL_entry.h> #include <asm/barrier.h> #include <asm/proc-fns.h> #include <asm/cacheflush.h> +#include <asm/idmap.h> extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ];@@ -28,3 +30,89 @@ void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr)outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]),__pa (&bL_entry_vectors[cluster][cpu +1 ])); } + +static const struct bL_platform_power_ops *platform_ops; + +int __init bL_platform_power_register (const struct bL_platform_power_ops *ops) +{ +if (platform_ops) +return -EBUSY ; + platform_ops = ops; +return 0 ; +} + +int bL_cpu_power_up (unsigned int cpu,unsigned int cluster) +{ +if (!platform_ops) +return -EUNATCH ; +might_sleep (); +return platform_ops->power_up (cpu, cluster); +} + +typedef void (*phys_reset_t )(unsigned long ); + +void bL_cpu_power_down (void ) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* +* Do this before calling into the power_down method, +* as it might not always be safe to do afterwards. +*/ +setup_mm_for_reboot (); + + platform_ops->power_down (); + +/* +* It is possible for a power_up request to happen concurrently +* with a power_down request for the same CPU. In this case the +* power_down method might not be able to actually enter a +* powered down state with the WFI instruction if the power_up +* method has removed the required reset condition. The +* power_down method is then allowed to return. We must perform +* a re-entry in the kernel as if the power_up method just had +* deasserted reset on the CPU. +* +* To simplify race issues, the platform specific implementation +* must accommodate for the possibility of unordered calls to +* power_down and power_up with a usage count. Therefore, if a +* call to power_up is issued for a CPU that is not down, then +* the next call to power_down must not attempt a full shutdown +* but only do the minimum (normally disabling L1 cache and CPU +* coherency) and return just as if a concurrent power_up request +* had happened as described above. +*/ + + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); + +/* should never get here */ +BUG (); +} + +void bL_cpu_suspend (u64 expected_residency) +{ +phys_reset_t phys_reset;
arch/arm/common/bL_entry.c void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) · line 108
Quoted 10 lines
arch/arm/common/bL_entry.c +10
-0+ +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* Very similar to bL_cpu_power_down() */ +setup_mm_for_reboot (); + platform_ops->suspend (expected_residency); + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); +BUG ();
I might be missing all the rationales behind not having a recovery for CPUs entering suspend if they actualy come here because of some events. This is pretty much possible in many scenario's and hence letting CPU cpu come out of suspend should be possible. May be switcher code don't have such requirement but it appeared bit off to me.
Regards
Santosh
PATCH 2/16 ARM: b.L: introduce the CPU/cluster power API
Quoted 113 lines · Santosh Shilimkar
Quoted 112 lines · Nicolas Pitre
This is the basic API used to handle the powering up/down of individual CPUs in a big.LITTLE system. The platform specific backend implementation has the responsibility to also handle the cluster level power as well when the first/last CPU in a cluster is brought up/down.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_entry.c | 88 +++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/bL_entry.h | 92 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+)arch/arm/common/bL_entry.c +6
-0@@ -11,11 +11,13 @@#include <linux/kernel.h> #include <linux/init.h> +#include <linux/irqflags.h> #include <asm/bL_entry.h> #include <asm/barrier.h> #include <asm/proc-fns.h> #include <asm/cacheflush.h> +#include <asm/idmap.h> extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ];@@ -28,3 +30,89 @@ void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) outer_clean_range(__pa(&bL_entry_vectors[cluster][cpu]), __pa(&bL_entry_vectors[cluster][cpu + 1])); } + +static const struct bL_platform_power_ops *platform_ops; + +int __init bL_platform_power_register(const struct bL_platform_power_ops*ops)
arch/arm/common/bL_entry.c +70
-0+{ +if (platform_ops) +return -EBUSY ; + platform_ops = ops; +return 0 ; +} + +int bL_cpu_power_up (unsigned int cpu,unsigned int cluster) +{ +if (!platform_ops) +return -EUNATCH ; +might_sleep (); +return platform_ops->power_up (cpu, cluster); +} + +typedef void (*phys_reset_t )(unsigned long ); + +void bL_cpu_power_down (void ) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* +* Do this before calling into the power_down method, +* as it might not always be safe to do afterwards. +*/ +setup_mm_for_reboot (); + + platform_ops->power_down (); + +/* +* It is possible for a power_up request to happen concurrently +* with a power_down request for the same CPU. In this case the +* power_down method might not be able to actually enter a +* powered down state with the WFI instruction if the power_up +* method has removed the required reset condition. The +* power_down method is then allowed to return. We must perform +* a re-entry in the kernel as if the power_up method just had +* deasserted reset on the CPU. +* +* To simplify race issues, the platform specific implementation +* must accommodate for the possibility of unordered calls to +* power_down and power_up with a usage count. Therefore, if a +* call to power_up is issued for a CPU that is not down, then +* the next call to power_down must not attempt a full shutdown +* but only do the minimum (normally disabling L1 cache and CPU +* coherency) and return just as if a concurrent power_up request +* had happened as described above. +*/ + + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); + +/* should never get here */ +BUG (); +} + +void bL_cpu_suspend (u64 expected_residency) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* Very similar to bL_cpu_power_down() */ +setup_mm_for_reboot (); + platform_ops->suspend (expected_residency); + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset);
Quoted 8 lines
arch/arm/common/bL_entry.c
Quoted 3 lines
arch/arm/common/bL_entry.c +2
-0+phys_reset (virt_to_phys (bL_entry_point)); +BUG ();I might be missing all the rationales behind not having a recovery for CPUs entering suspend if they actualy come here because of some events. This is pretty much possible in many scenario's and hence letting CPU cpu come out of suspend should be possible. May be switcher code don't have such requirement but it appeared bit off to me.
There are two things to consider here:
1) The CPU is suspended. CPU state is lost. Next interrupt to wake up the CPU will make it restart from the reset vector and re-entry in the kernel will happen via bL_entry_point to deal with the various cluster issues, to eventually resume kernel code via cpu_resume. Obviously, the machine specific backend code would have set the bL_entry_point address in its machine specific reset vector in advance.
2) An interrupt comes along before the CPU is effectively suspended, say right before the backend code executes a WFI to shut the CPU down. The CPU and possibly cluster state was already set for being powered off. We cannot simply return at this point as caches are off, the CPU is not coherent with the rest of the system anymore, etc. So if the platform specific backend ever returns, say because the final WFI exited, then we have to go through the same arbitration process to restore the CPU and cluster state as if that was a hard reset. Hence the cpu_reset call to loop back into bL_entry_point.
In either cases, we simply cannot ever return from bL_cpu_suspend() directly. Of course, the caller is expected to have used bL_set_entry_vector() beforehand, most probably with cpu_resume as argument.
Nicolas
PATCH 2/16 ARM: b.L: introduce the CPU/cluster power API
Quoted 126 lines · Nicolas Pitre
Quoted 113 lines · Santosh Shilimkar
Quoted 112 lines · Nicolas Pitre
This is the basic API used to handle the powering up/down of individual CPUs in a big.LITTLE system. The platform specific backend implementation has the responsibility to also handle the cluster level power as well when the first/last CPU in a cluster is brought up/down.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_entry.c | 88 +++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/bL_entry.h | 92 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+)arch/arm/common/bL_entry.c +6
-0@@ -11,11 +11,13 @@#include <linux/kernel.h> #include <linux/init.h> +#include <linux/irqflags.h> #include <asm/bL_entry.h> #include <asm/barrier.h> #include <asm/proc-fns.h> #include <asm/cacheflush.h> +#include <asm/idmap.h> extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ];@@ -28,3 +30,89 @@ void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr) outer_clean_range(__pa(&bL_entry_vectors[cluster][cpu]), __pa(&bL_entry_vectors[cluster][cpu + 1])); } + +static const struct bL_platform_power_ops *platform_ops; + +int __init bL_platform_power_register(const struct bL_platform_power_ops*ops)
arch/arm/common/bL_entry.c +70
-0+{ +if (platform_ops) +return -EBUSY ; + platform_ops = ops; +return 0 ; +} + +int bL_cpu_power_up (unsigned int cpu,unsigned int cluster) +{ +if (!platform_ops) +return -EUNATCH ; +might_sleep (); +return platform_ops->power_up (cpu, cluster); +} + +typedef void (*phys_reset_t )(unsigned long ); + +void bL_cpu_power_down (void ) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* +* Do this before calling into the power_down method, +* as it might not always be safe to do afterwards. +*/ +setup_mm_for_reboot (); + + platform_ops->power_down (); + +/* +* It is possible for a power_up request to happen concurrently +* with a power_down request for the same CPU. In this case the +* power_down method might not be able to actually enter a +* powered down state with the WFI instruction if the power_up +* method has removed the required reset condition. The +* power_down method is then allowed to return. We must perform +* a re-entry in the kernel as if the power_up method just had +* deasserted reset on the CPU. +* +* To simplify race issues, the platform specific implementation +* must accommodate for the possibility of unordered calls to +* power_down and power_up with a usage count. Therefore, if a +* call to power_up is issued for a CPU that is not down, then +* the next call to power_down must not attempt a full shutdown +* but only do the minimum (normally disabling L1 cache and CPU +* coherency) and return just as if a concurrent power_up request +* had happened as described above. +*/ + + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); + +/* should never get here */ +BUG (); +} + +void bL_cpu_suspend (u64 expected_residency) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* Very similar to bL_cpu_power_down() */ +setup_mm_for_reboot (); + platform_ops->suspend (expected_residency); + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset);Quoted 8 lines
arch/arm/common/bL_entry.c
Quoted 3 lines
arch/arm/common/bL_entry.c +2
-0+phys_reset (virt_to_phys (bL_entry_point)); +BUG ();I might be missing all the rationales behind not having a recovery for CPUs entering suspend if they actualy come here because of some events. This is pretty much possible in many scenario's and hence letting CPU cpu come out of suspend should be possible. May be switcher code don't have such requirement but it appeared bit off to me.
There are two things to consider here:
Quoted 7 lines
1) The CPU is suspended. CPU state is lost. Next interrupt to wake up the CPU will make it restart from the reset vector and re-entry in the kernel will happen via
bL_entry_pointto deal with the various cluster issues, to eventually resume kernel code viacpu_resume. Obviously, the machine specific backend code would have set thebL_entry_pointaddress in its machine specific reset vector in advance.
This is the successful case and in that case you will anyway not hit the BUG.
Quoted 11 lines
2) An interrupt comes along before the CPU is effectively suspended, say right before the backend code executes a WFI to shut the CPU down. The CPU and possibly cluster state was already set for being powered off. We cannot simply return at this point as caches are off, the CPU is not coherent with the rest of the system anymore, etc. So if the platform specific backend ever returns, say because the final WFI exited, then we have to go through the same arbitration process to restore the CPU and cluster state as if that was a hard reset. Hence the
cpu_resetcall to loop back intobL_entry_point.
This is the one I was thinking. Enabling C bit and SMP bit should be enough for CPU to get back to right state since the CPU has not lost the context all the registers including SP is intact and CPU should be able to resume.
Quoted 5 lines
In either cases, we simply cannot ever return from
bL_cpu_suspend()directly. Of course, the caller is expected to have usedbL_set_entry_vector()beforehand, most probably withcpu_resumeas argument.
The above might get complicated if when above situation happens on last CPU where even CCI gets disabled and then adding the rever code for all of that may not be worth. You approach is much safer. Thanks for explaining it further.
Regards,
Santosh
PATCH 2/16 ARM: b.L: introduce the CPU/cluster power API
Quoted 151 lines · Santosh Shilimkar
Quoted 130 lines · Nicolas Pitre
Quoted 117 lines · Santosh Shilimkar
Quoted 116 lines · Nicolas Pitre
This is the basic API used to handle the powering up/down of individual CPUs in a big.LITTLE system. The platform specific backend implementation
has the responsibility to also handle the cluster level power as well when
the first/last CPU in a cluster is brought up/down.Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_entry.c | 88 +++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/bL_entry.h | 92 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+)arch/arm/common/bL_entry.c +6
-0@@ -11,11 +11,13 @@#include <linux/kernel.h> #include <linux/init.h> +#include <linux/irqflags.h> #include <asm/bL_entry.h> #include <asm/barrier.h> #include <asm/proc-fns.h> #include <asm/cacheflush.h> +#include <asm/idmap.h> extern volatile unsigned long bL_entry_vectors[BL_NR_CLUSTERS ][BL_CPUS_PER_CLUSTER ];@@ -28,3 +30,89 @@ void bL_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr)outer_clean_range (__pa (&bL_entry_vectors[cluster][cpu]),__pa (&bL_entry_vectors[cluster][cpu +1 ])); } + +static const struct bL_platform_power_ops *platform_ops; + +int __init bL_platform_power_register(const struct
bL_platform_power_ops
*ops)arch/arm/common/bL_entry.c +70
-0+{ +if (platform_ops) +return -EBUSY ; + platform_ops = ops; +return 0 ; +} + +int bL_cpu_power_up (unsigned int cpu,unsigned int cluster) +{ +if (!platform_ops) +return -EUNATCH ; +might_sleep (); +return platform_ops->power_up (cpu, cluster); +} + +typedef void (*phys_reset_t )(unsigned long ); + +void bL_cpu_power_down (void ) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* +* Do this before calling into the power_down method, +* as it might not always be safe to do afterwards. +*/ +setup_mm_for_reboot (); + + platform_ops->power_down (); + +/* +* It is possible for a power_up request to happen concurrently +* with a power_down request for the same CPU. In this case the +* power_down method might not be able to actually enter a +* powered down state with the WFI instruction if the power_up +* method has removed the required reset condition. The +* power_down method is then allowed to return. We must perform +* a re-entry in the kernel as if the power_up method just had +* deasserted reset on the CPU. +* +* To simplify race issues, the platform specific implementation +* must accommodate for the possibility of unordered calls to +* power_down and power_up with a usage count. Therefore, if a +* call to power_up is issued for a CPU that is not down, then +* the next call to power_down must not attempt a full shutdown +* but only do the minimum (normally disabling L1 cache and CPU +* coherency) and return just as if a concurrent power_up request +* had happened as described above. +*/ + + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset); +phys_reset (virt_to_phys (bL_entry_point)); + +/* should never get here */ +BUG (); +} + +void bL_cpu_suspend (u64 expected_residency) +{ +phys_reset_t phys_reset; + +BUG_ON (!platform_ops); +BUG_ON (!irqs_disabled ()); + +/* Very similar to bL_cpu_power_down() */ +setup_mm_for_reboot (); + platform_ops->suspend (expected_residency); + phys_reset = (phys_reset_t )(unsigned long )virt_to_phys (cpu_reset);Quoted 8 lines
arch/arm/common/bL_entry.c
Quoted 3 lines
arch/arm/common/bL_entry.c +2
-0+phys_reset (virt_to_phys (bL_entry_point)); +BUG ();I might be missing all the rationales behind not having a recovery for CPUs entering suspend if they actualy come here because of some events. This is pretty much possible in many scenario's and hence letting CPU cpu come out of suspend should be possible. May be switcher code don't have such requirement but it appeared bit off to me.
There are two things to consider here:
Quoted 7 lines
1) The CPU is suspended. CPU state is lost. Next interrupt to wake up the CPU will make it restart from the reset vector and re-entry in the kernel will happen via
bL_entry_pointto deal with the various cluster issues, to eventually resume kernel code viacpu_resume. Obviously, the machine specific backend code would have set thebL_entry_pointaddress in its machine specific reset vector in advance.This is the successful case and in that case you will anyway not hit the BUG.
Quoted 11 lines
2) An interrupt comes along before the CPU is effectively suspended, say right before the backend code executes a WFI to shut the CPU down. The CPU and possibly cluster state was already set for being powered off. We cannot simply return at this point as caches are off, the CPU is not coherent with the rest of the system anymore, etc. So if the platform specific backend ever returns, say because the final WFI exited, then we have to go through the same arbitration process to restore the CPU and cluster state as if that was a hard reset. Hence the
cpu_resetcall to loop back intobL_entry_point.
Quoted 4 lines
This is the one I was thinking. Enabling C bit and SMP bit should be enough for CPU to get back to right state since the CPU has not lost the context all the registers including SP is intact and CPU should be able to resume.
No. If we are the last man then the CCI is disabled and we cannot just enable the C and SMP bits anymore without turning the CCI back on. And even if we are not the last man, maybe another CPU is concurrently going through the same code and that one _is_ the last man, in which case it will have waited until we are done flushing our cache to turn off the CCI but we don't know about that. And yet another CPU might be coming up just at the same moment but this one will want to turn on the CCI and that has to be done in a controlled way, and that control is performed in the code from bL_entry_point. So, in short, we cannot just return from here.
Quoted 8 lines
Quoted 5 lines
In either cases, we simply cannot ever return from
bL_cpu_suspend()directly. Of course, the caller is expected to have usedbL_set_entry_vector()beforehand, most probably withcpu_resumeas argument.The above might get complicated if when above situation happens on last CPU where even CCI gets disabled and then adding the rever code for all of that may not be worth. You approach is much safer.
Indeed. Furthermore, that revert code does exist already: it is all in bL_head.S. Hence the cpu_reset(bL_entry_point) call.
Quoted 1 line
Thanks for explaining it further.
No problem. That literally took us months to get this code right so it might not be fully obvious to others after the first look.
Nicolas
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
In reply to Nicolas PitreFrom: Dave Martin <[email protected]>
This provides helper methods to coordinate between CPUs coming down and CPUs going up, as well as documentation on the used algorithms, so that cluster teardown and setup
operations are not done for a cluster simultaneously.
For use in the power_down() implementation:
* __bL_cpu_going_down(unsigned int cluster, unsigned int cpu)
* __bL_outbound_enter_critical(unsigned int cluster)
* __bL_outbound_leave_critical(unsigned int cluster)
* __bL_cpu_down(unsigned int cluster, unsigned int cpu)
The power_up_setup() helper should do platform-specific setup in preparation for turning the CPU on, such as invalidating local caches or entering coherency. It must be assembler for now, since it must run before the MMU can be switched on. It is passed the affinity level which should be initialized.
Because the bL_cluster_sync_struct content is looked-up and modified with the cache enabled or disabled depending on the code path, it is crucial to always ensure proper cache maintenance to update main memory right away. Therefore, any cached write must be followed by a cache clean operation and any cached read must be preceded by a cache invalidate operation on the accessed memory.
To avoid races where a reader would invalidate the cache and discard the latest update from a writer before that writer had a chance to clean it to RAM, we simply use cache flush (clean+invalidate) operations everywhere.
Also, in order to prevent a cached writer from interfering with an adjacent non-cached writer, we ensure each state variable is located to a separate cache line.
Thanks to Nicolas Pitre and Achin Gupta for the help with this patch.
--- .../arm/big.LITTLE/cluster-pm-race-avoidance.txt | 498 +++++++++++++++++++++ arch/arm/common/bL_entry.c | 160 +++++++ arch/arm/common/bL_head.S | 88 +++- arch/arm/include/asm/bL_entry.h | 62 +++ 4 files changed, 806 insertions(+), 2 deletions(-) create mode 100644 Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt new file +498 -0
@@ -0,0 +1,498 @@ +Big.LITTLE cluster Power-up/power-down race avoidance algorithm +=============================================================== + +This file documents the algorithm which is used to coordinate CPU and +cluster setup and teardown operations and to manage hardware coherency +controls safely. + +The section "Rationale" explains what the algorithm is for and why it is +needed. "Basic model" explains general concepts using a simplified view +of the system. The other sections explain the actual details of the +algorithm in use. + + +Rationale +--------- + +In a system containing multiple CPUs, it is desirable to have the +ability to turn off individual CPUs when the system is idle, reducing +power consumption and thermal dissipation. + +In a system containing multiple clusters of CPUs, it is also desirable +to have the ability to turn off entire clusters. + +Turning entire clusters off and on is a risky business, because it +involves performing potentially destructive operations affecting a group +of independently running CPUs, while the OS continues to run. This +means that we need some coordination in order to ensure that critical +cluster-level operations are only performed when it is truly safe to do +so. + +Simple locking may not be sufficient to solve this problem, because +mechanisms like Linux spinlocks may rely on coherency mechanisms which +are not immediately enabled when a cluster powers up. Since enabling or +disabling those mechanisms may itself be a non-atomic operation (such as +writing some hardware registers and invalidating large caches), other +methods of coordination are required in order to guarantee safe +power-down and power-up at the cluster level. + +The mechanism presented in this document describes a coherent memory +based protocol for performing the needed coordination. It aims to be as +lightweight as possible, while providing the required safety properties. + + +Basic model +----------- + +Each cluster and CPU is assigned a state, as follows: + + DOWN + COMING_UP + UP + GOING_DOWN + + +---------> UP ----------+ + | v + + COMING_UP GOING_DOWN + + ^ | + +--------- DOWN <--------+ + + +DOWN: The CPU or cluster is not coherent, and is either powered off or + suspended, or is ready to be powered off or suspended. + +COMING_UP: The CPU or cluster has committed to moving to the UP state. + It may be part way through the process of initialisation and + enabling coherency. + +UP: The CPU or cluster is active and coherent at the hardware + level. A CPU in this state is not necessarily being used + actively by the kernel. + +GOING_DOWN: The CPU or cluster has committed to moving to the DOWN + state. It may be part way through the process of teardown and + coherency exit. + + +Each CPU has one of these states assigned to it at any point in time. +The CPU states are described in the "CPU state" section, below. + +Each cluster is also assigned a state, but it is necessary to split the +state value into two parts (the "cluster" state and "inbound" state) and +to introduce additional states in order to avoid races between different +CPUs in the cluster simultaneously modifying the state. The cluster- +level states are described in the "Cluster state" section. + +To help distinguish the CPU states from cluster states in this +discussion, the state names are given a CPU_ prefix for the CPU states, +and a CLUSTER_ or INBOUND_ prefix for the cluster states. + + +CPU state +--------- + +In this algorithm, each individual core in a multi-core processor is +referred to as a "CPU". CPUs are assumed to be single-threaded: +therefore, a CPU can only be doing one thing@a single point in time. + +This means that CPUs fit the basic model closely. + +The algorithm defines the following states for each CPU in the system: + + CPU_DOWN + CPU_COMING_UP + CPU_UP + CPU_GOING_DOWN + + cluster setup and + CPU setup complete policy decision + +-----------> CPU_UP ------------+ + | v + + CPU_COMING_UP CPU_GOING_DOWN + + ^ | + +----------- CPU_DOWN <----------+ + policy decision CPU teardown complete + or hardware event + + +The definitions of the four states correspond closely to the states of +the basic model. + +Transitions between states occur as follows. + +A trigger event (spontaneous) means that the CPU can transition to the +next state as a result of making local progress only, with no +requirement for any external event to happen. + + +CPU_DOWN: + + A CPU reaches the CPU_DOWN state when it is ready for + power-down. On reaching this state, the CPU will typically + power itself down or suspend itself, via a WFI instruction or a + firmware call. + + Next state: CPU_COMING_UP + Conditions: none + + Trigger events: + + a) an explicit hardware power-up operation, resulting + from a policy decision on another CPU; + + b) a hardware event, such as an interrupt. + + +CPU_COMING_UP: + + A CPU cannot start participating in hardware coherency until the + cluster is set up and coherent. If the cluster is not ready, + then the CPU will wait in the CPU_COMING_UP state until the + cluster has been set up. + + Next state: CPU_UP + Conditions: The CPU's parent cluster must be in CLUSTER_UP. + Trigger events: Transition of the parent cluster to CLUSTER_UP. + + Refer to the "Cluster state" section for a description of the + CLUSTER_UP state. + + +CPU_UP: + When a CPU reaches the CPU_UP state, it is safe for the CPU to + start participating in local coherency. + + This is done by jumping to the kernel's CPU resume code. + + Note that the definition of this state is slightly different + from the basic model definition: CPU_UP does not mean that the + CPU is coherent yet, but it does mean that it is safe to resume + the kernel. The kernel handles the rest of the resume + procedure, so the remaining steps are not visible as part of the + race avoidance algorithm. + + The CPU remains in this state until an explicit policy decision + is made to shut down or suspend the CPU. + + Next state: CPU_GOING_DOWN + Conditions: none + Trigger events: explicit policy decision + + +CPU_GOING_DOWN: + + While in this state, the CPU exits coherency, including any + operations required to achieve this (such as cleaning data + caches). + + Next state: CPU_DOWN + Conditions: local CPU teardown complete + Trigger events: (spontaneous) + + +Cluster state +------------- + +A cluster is a group of connected CPUs with some common resources. +Because a cluster contains multiple CPUs, it can be doing multiple +things@the same time. This has some implications. In particular, a +CPU can start up while another CPU is tearing the cluster down. + +In this discussion, the "outbound side" is the view of the cluster state +as seen by a CPU tearing the cluster down. The "inbound side" is the +view of the cluster state as seen by a CPU setting the CPU up. + +In order to enable safe coordination in such situations, it is important +that a CPU which is setting up the cluster can advertise its state +independently of the CPU which is tearing down the cluster. For this +reason, the cluster state is split into two parts: + + "cluster" state: The global state of the cluster; or the state + on the outbound side: + + CLUSTER_DOWN + CLUSTER_UP + CLUSTER_GOING_DOWN + + "inbound" state: The state of the cluster on the inbound side. + + INBOUND_NOT_COMING_UP + INBOUND_COMING_UP + + + The different pairings of these states results in six possible + states for the cluster as a whole: + + CLUSTER_UP + +==========> INBOUND_NOT_COMING_UP -------------+ + # | + | + CLUSTER_UP <----+ | + INBOUND_COMING_UP | v + + ^ CLUSTER_GOING_DOWN CLUSTER_GOING_DOWN + # INBOUND_COMING_UP <=== INBOUND_NOT_COMING_UP + + CLUSTER_DOWN | | + INBOUND_COMING_UP <----+ | + | + ^ | + +=========== CLUSTER_DOWN <------------+ + INBOUND_NOT_COMING_UP + + Transitions -----> can only be made by the outbound CPU, and + only involve changes to the "cluster" state. + + Transitions ===##> can only be made by the inbound CPU, and only + involve changes to the "inbound" state, except where there is no + further transition possible on the outbound side (i.e., the + outbound CPU has put the cluster into the CLUSTER_DOWN state). + + The race avoidance algorithm does not provide a way to determine + which exact CPUs within the cluster play these roles. This must + be decided in advance by some other means. Refer to the section + "Last man and first man selection" for more explanation. + + + CLUSTER_DOWN/INBOUND_NOT_COMING_UP is the only state where the + cluster can actually be powered down. + + The parallelism of the inbound and outbound CPUs is observed by + the existence of two different paths from CLUSTER_GOING_DOWN/ + INBOUND_NOT_COMING_UP (corresponding to GOING_DOWN in the basic + model) to CLUSTER_DOWN/INBOUND_COMING_UP (corresponding to + COMING_UP in the basic model). The second path avoids cluster + teardown completely. + + CLUSTER_UP/INBOUND_COMING_UP is equivalent to UP in the basic + model. The final transition to CLUSTER_UP/INBOUND_NOT_COMING_UP + is trivial and merely resets the state machine ready for the + next cycle. + + Details of the allowable transitions follow. + + The next state in each case is notated + + <cluster state>/<inbound state> (<transitioner>) + + where the <transitioner> is the side on which the transition + can occur; either the inbound or the outbound side. + + +CLUSTER_DOWN/INBOUND_NOT_COMING_UP: + + Next state: CLUSTER_DOWN/INBOUND_COMING_UP (inbound) + Conditions: none + Trigger events: + + a) an explicit hardware power-up operation, resulting + from a policy decision on another CPU; + + b) a hardware event, such as an interrupt. + + +CLUSTER_DOWN/INBOUND_COMING_UP: + + In this state, an inbound CPU sets up the cluster, including + enabling of hardware coherency at the cluster level and any + other operations (such as cache invalidation) which are required + in order to achieve this. + + The purpose of this state is to do sufficient cluster-level + setup to enable other CPUs in the cluster to enter coherency + safely. + + Next state: CLUSTER_UP/INBOUND_COMING_UP (inbound) + Conditions: cluster-level setup and hardware coherency complete + Trigger events: (spontaneous) + + +CLUSTER_UP/INBOUND_COMING_UP: + + Cluster-level setup is complete and hardware coherency is + enabled for the cluster. Other CPUs in the cluster can safely + enter coherency. + + This is a transient state, leading immediately to + CLUSTER_UP/INBOUND_NOT_COMING_UP. All other CPUs on the cluster + should consider treat these two states as equivalent. + + Next state: CLUSTER_UP/INBOUND_NOT_COMING_UP (inbound) + Conditions: none + Trigger events: (spontaneous) + + +CLUSTER_UP/INBOUND_NOT_COMING_UP: + + Cluster-level setup is complete and hardware coherency is + enabled for the cluster. Other CPUs in the cluster can safely + enter coherency. + + The cluster will remain in this state until a policy decision is + made to power the cluster down. + + Next state: CLUSTER_GOING_DOWN/INBOUND_NOT_COMING_UP (outbound) + Conditions: none + Trigger events: policy decision to power down the cluster + + +CLUSTER_GOING_DOWN/INBOUND_NOT_COMING_UP: + + An outbound CPU is tearing the cluster down. The selected CPU + must wait in this state until all CPUs in the cluster are in the + CPU_DOWN state. + + When all CPUs are in the CPU_DOWN state, the cluster can be torn + down, for example by cleaning data caches and exiting + cluster-level coherency. + + To avoid wasteful unnecessary teardown operations, the outbound + should check the inbound cluster state for asynchronous + transitions to INBOUND_COMING_UP. Alternatively, individual + CPUs can be checked for entry into CPU_COMING_UP or CPU_UP. + + + Next states: + + CLUSTER_DOWN/INBOUND_NOT_COMING_UP (outbound) + Conditions: cluster torn down and ready to power off + Trigger events: (spontaneous) + + CLUSTER_GOING_DOWN/INBOUND_COMING_UP (inbound) + Conditions: none + Trigger events: + + a) an explicit hardware power-up operation, + resulting from a policy decision on another + CPU; + + b) a hardware event, such as an interrupt. + + +CLUSTER_GOING_DOWN/INBOUND_COMING_UP: + + The cluster is (or was) being torn down, but another CPU has + come online in the meantime and is trying to set up the cluster + again. + + If the outbound CPU observes this state, it has two choices: + + a) back out of teardown, restoring the cluster to the + CLUSTER_UP state; + + b) finish tearing the cluster down and put the cluster + in the CLUSTER_DOWN state; the inbound CPU will + set up the cluster again from there. + + Choice (a) permits the removal of some latency by avoiding + unnecessary teardown and setup operations in situations where + the cluster is not really going to be powered down. + + + Next states: + + CLUSTER_UP/INBOUND_COMING_UP (outbound) + Conditions: cluster-level setup and hardware + coherency complete + Trigger events: (spontaneous) + + CLUSTER_DOWN/INBOUND_COMING_UP (outbound) + Conditions: cluster torn down and ready to power off + Trigger events: (spontaneous) + + +Last man and First man selection +-------------------------------- + +The CPU which performs cluster tear-down operations on the outbound side +is commonly referred to as the "last man". + +The CPU which performs cluster setup on the inbound side is commonly +referred to as the "first man". + +The race avoidance algorithm documented above does not provide a +mechanism to choose which CPUs should play these roles. + + +Last man: + +When shutting down the cluster, all the CPUs involved are initially +executing Linux and hence coherent. Therefore, ordinary spinlocks can +be used to select a last man safely, before the CPUs become +non-coherent. + + +First man: + +Because CPUs may power up asynchronously in response to external wake-up +events, a dynamic mechanism is needed to make sure that only one CPU +attempts to play the first man role and do the cluster-level +initialisation: any other CPUs must wait for this to complete before +proceeding. + +Cluster-level initialisation may involve actions such as configuring +coherency controls in the bus fabric. + +The current implementation in bL_head.S uses a separate mutual exclusion +mechanism to do this arbitration. This mechanism is documented in +detail in vlocks.txt. + + +Features and Limitations +------------------------ + +Implementation: + + The current ARM-based implementation is split between + arch/arm/common/bL_head.S (low-level inbound CPU operations) and + arch/arm/common/bL_entry.c (everything else): + + __bL_cpu_going_down() signals the transition of a CPU to the + CPU_GOING_DOWN state. + + __bL_cpu_down() signals the transition of a CPU to the CPU_DOWN + state. + + A CPU transitions to CPU_COMING_UP and then to CPU_UP via the + low-level power-up code in bL_head.S. This could + involve CPU-specific setup code, but in the current + implementation it does not. + + __bL_outbound_enter_critical() and __bL_outbound_leave_critical() + handle transitions from CLUSTER_UP to CLUSTER_GOING_DOWN + and from there to CLUSTER_DOWN or back to CLUSTER_UP (in + the case of an aborted cluster power-down). + + These functions are more complex than the __bL_cpu_*() + functions due to the extra inter-CPU coordination which + is needed for safe transitions at the cluster level. + + A cluster transitions from CLUSTER_DOWN back to CLUSTER_UP via + the low-level power-up code in bL_head.S. This + typically involves platform-specific setup code, + provided by the platform-specific power_up_setup + function registered via bL_cluster_sync_init. + +Deep topologies: + + As currently described and implemented, the algorithm does not + support CPU topologies involving more than two levels (i.e., + clusters of clusters are not supported). The algorithm could be + extended by replicating the cluster-level states for the + additional topological levels, and modifying the transition + rules for the intermediate (non-outermost) cluster levels. + + +Colophon +-------- + +Originally created and documented by Dave Martin for Linaro Limited, in +collaboration with Nicolas Pitre and Achin Gupta. + +Copyright (C) 2012 Linaro Limited +Distributed under the terms of Version 2 of the GNU General Public +License, as defined in linux/COPYING.
arch/arm/common/bL_entry.c +160 -0
163 lines not on this page · raw message
arch/arm/common/bL_head.S +86 -2
118 lines not on this page · raw message
arch/arm/include/asm/bL_entry.h +62 -0
75 lines not on this page · raw message
Signature · 1 line
1.8.0
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 46 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
This provides helper methods to coordinate between CPUs coming down and CPUs going up, as well as documentation on the used algorithms, so that cluster teardown and setup
operations are not done for a cluster simultaneously.For use in the
power_down()implementation:
*__bL_cpu_going_down(unsigned int cluster, unsigned int cpu)
*__bL_outbound_enter_critical(unsigned int cluster)
*__bL_outbound_leave_critical(unsigned int cluster)
*__bL_cpu_down(unsigned int cluster, unsigned int cpu)The
power_up_setup()helper should do platform-specific setup in preparation for turning the CPU on, such as invalidating local caches or entering coherency. It must be assembler for now, since it must run before the MMU can be switched on. It is passed the affinity level which should be initialized.Because the
bL_cluster_sync_structcontent is looked-up and modified with the cache enabled or disabled depending on the code path, it is crucial to always ensure proper cache maintenance to update main memory right away. Therefore, any cached write must be followed by a cache clean operation and any cached read must be preceded by a cache invalidate operation on the accessed memory.To avoid races where a reader would invalidate the cache and discard the latest update from a writer before that writer had a chance to clean it to RAM, we simply use cache flush (clean+invalidate) operations everywhere.
Also, in order to prevent a cached writer from interfering with an adjacent non-cached writer, we ensure each state variable is located to a separate cache line.
Thanks to Nicolas Pitre and Achin Gupta for the help with this patch.
Signed-off-by Dave Martin <[email protected]>--- .../arm/big.LITTLE/cluster-pm-race-avoidance.txt | 498 +++++++++++++++++++++ arch/arm/common/bL_entry.c | 160 +++++++ arch/arm/common/bL_head.S | 88 +++- arch/arm/include/asm/bL_entry.h | 62 +++ 4 files changed, 806 insertions(+), 2 deletions(-) create mode 100644 Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt
[...]
Quoted 1 line
arch/arm/common/bL_entry.c +0
-0
[...]
Quoted 1 line
+int
__initbL_cluster_sync_init(void (*power_up_setup)(void))
The addition of the affinity level parameter for power_up_setup means that this prototype is not correct.
This is not a functional change, since that function is only called from assembler anyway, but it will help avoid confusion.
This could fixed by folding the following changes into the patch.
Cheers
---Dave
arch/arm/common/bL_entry.c +2 -1
@@ -245,7 +245,8 @@ int __bL_cluster_state(unsigned int cluster)extern unsigned long bL_power_up_setup_phys; -int __init bL_cluster_sync_init (void (*power_up_setup)(void )) +int __init bL_cluster_sync_init ( +void (*power_up_setup)(unsigned int affinity_level)) {unsigned int i, j, mpidr, this_cluster;
arch/arm/include/asm/bL_entry.h +2 -1
@@ -183,7 +183,8 @@ void __bL_outbound_leave_critical(unsigned int cluster, int state);bool __bL_outbound_enter_critical (unsigned int this_cpu,unsigned int cluster);int __bL_cluster_state (unsigned int cluster); -int __init bL_cluster_sync_init (void (*power_up_setup)(void )); +int __init bL_cluster_sync_init ( +void (*power_up_setup)(unsigned int affinity_level));#endif /* ! __ASSEMBLY__ */ #endif
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 4 lines · Dave Martin
Quoted 1 line
+int
__initbL_cluster_sync_init(void (*power_up_setup)(void))The addition of the affinity level parameter for
power_up_setupmeans that this prototype is not correct.
Indeed.
Quoted 2 lines
This is not a functional change, since that function is only called from assembler anyway, but it will help avoid confusion.
Fixed now, as well as the DCSCB usage.
Nicolas
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 9 lines · Nicolas Pitre
Quoted 4 lines · Dave Martin
Quoted 1 line
+int
__initbL_cluster_sync_init(void (*power_up_setup)(void))The addition of the affinity level parameter for
power_up_setupmeans that this prototype is not correct.Indeed.
Quoted 7 lines
Quoted 2 lines
This is not a functional change, since that function is only called from assembler anyway, but it will help avoid confusion.
Fixed now, as well as the DCSCB usage.
Nicolas
OK, thanks
---Dave
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
In reply to Nicolas PitreDocumentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt line 2
Quoted 5 lines · Nicolas Pitre
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt new file +2
-0@@ -0,0 +1,498 @@ +Big.LITTLE cluster Power-up/power-down race avoidance algorithm +===============================================================
Nice description and ascii art :).
Quoted 6 lines
arch/arm/common/bL_entry.c +0
-0@@ -116,3 +116,163 @@ int bL_cpu_powered_up(void) platform_ops->powered_up ();return 0 ; }
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 128
Quoted 10 lines
arch/arm/common/bL_entry.c +10
-0+ +struct bL_sync_struct bL_sync; + +static void __sync_range (volatile void *p,size_t size) +{ +char *_p = (char *)p; + +__cpuc_flush_dcache_area (_p, size); +outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync ();
The outer flush-range operations already contain a cache_sync, so an additional outer_sync() operation is not necessary.
You (well, Dave) said that you use the flush instead of clean/invalidate to avoid races with other CPUs writing the location. However, on the same CPU you can get a speculative load into L1 after the L1 flush but before the L2 flush, so the reader case can fail.
The sequence for readers is (note *L2* inval first):
L2 inval
L1 inval
The sequence for writers is:
L1 clean
L2 clean
The bi-directional sequence (that's what you need) is:
L1 clean
L2 clean+inval
L1 clean+inval
The last L1 op must be clean+inval in case another CPU writes to this location to avoid discarding the write.
If you don't have an L2, you just end up with two L1 clean ops, so you can probably put some checks.
Quoted 15 lines
arch/arm/common/bL_entry.c +15
-0+#define sync_mem (ptr) __sync_range(ptr, sizeof *(ptr)) + +/* +* __bL_cpu_going_down: Indicates that the cpu is being torn down. +* This must be called at the point of committing to teardown of a CPU. +* The CPU cache (SCTRL.C bit) is expected to still be active. +*/ +void __bL_cpu_going_down (unsigned int cpu,unsigned int cluster) +{ + bL_sync.clusters[cluster].cpus[cpu].cpu =CPU_GOING_DOWN ; +sync_mem (&bL_sync.clusters[cluster].cpus[cpu].cpu); +} + +/* +* __bL_cpu_down: Indicates that cpu teardown is complete and that the
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 155
Quoted 10 lines
arch/arm/common/bL_entry.c +10
-0+* cluster can be torn down without disrupting this CPU. +* To avoid deadlocks, this must be called before a CPU is powered down. +* The CPU cache (SCTRL.C bit) is expected to be off. +*/ +void __bL_cpu_down (unsigned int cpu,unsigned int cluster) +{ +dsb (); + bL_sync.clusters[cluster].cpus[cpu].cpu =CPU_DOWN ; +sync_mem (&bL_sync.clusters[cluster].cpus[cpu].cpu); +sev ();
For the sev() here (and other functions in this patch) you need a dsb() before. I'm not sure outer_sync() has one.
Signature · 1 line
Catalin
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 29 lines · Catalin Marinas
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt line 2
Quoted 5 lines · Nicolas Pitre
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt new file +2
-0@@ -0,0 +1,498 @@ +Big.LITTLE cluster Power-up/power-down race avoidance algorithm +===============================================================Nice description and ascii art :).
Quoted 6 lines
arch/arm/common/bL_entry.c +0
-0@@ -116,3 +116,163 @@ int bL_cpu_powered_up(void) platform_ops->powered_up ();return 0 ; }arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 128
Quoted 10 lines
arch/arm/common/bL_entry.c +10
-0+ +struct bL_sync_struct bL_sync; + +static void __sync_range (volatile void *p,size_t size) +{ +char *_p = (char *)p; + +__cpuc_flush_dcache_area (_p, size); +outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync ();The outer flush-range operations already contain a
cache_sync, so an additionalouter_sync()operation is not necessary.
Quoted 2 lines
You (well, Dave) said that you use the flush instead of clean/invalidate to avoid races with other CPUs writing the location.
Yes. To clarify for everyone, the issue here is that those state values are being written and/or read by different CPUs which may or may not have their cache enabled. And in some cases the L1 cache is disabled but L2 is still enabled.
So a cached reader must invalidate the cache to ensure it reads an up-to-date value from RAM since the last update might have come from a CPU with its cache disabled. But invalidating the cache might discard the newly updated state from a writer with an active cache before that writer had the chance to clean its cache to RAM. Therefore, using a cache flush rather than a cache invalidate before every reads solves this race.
Quoted 7 lines
However, on the same CPU you can get a speculative load into L1 after the L1 flush but before the L2 flush, so the reader case can fail.
The sequence for readers is (note *L2* inval first):
L2 inval
L1 inval
As you noted below and as I explained above, this can't be an inval operation as that could discard a concurrent writer's update.
Quoted 11 lines
The sequence for writers is:
L1 clean
L2 cleanThe bi-directional sequence (that's what you need) is:
L1 clean
L2 clean+inval
L1 clean+inval
Quoted 5 lines
The last L1 op must be clean+inval in case another CPU writes to this location to avoid discarding the write.
If you don't have an L2, you just end up with two L1 clean ops, so you can probably put some checks.
In fact, since this is only used on A7/A15 right now, there is no outer cache and the outer calls are effectively no-ops. I'm wondering if those should simply be removed until/unless there is some system showing up with a need for them.
Quoted 20 lines
Quoted 20 lines
arch/arm/common/bL_entry.c +20
-0+#define sync_mem (ptr) __sync_range(ptr, sizeof *(ptr)) + +/* +* __bL_cpu_going_down: Indicates that the cpu is being torn down. +* This must be called at the point of committing to teardown of a CPU. +* The CPU cache (SCTRL.C bit) is expected to still be active. +*/ +void __bL_cpu_going_down (unsigned int cpu,unsigned int cluster) +{ + bL_sync.clusters[cluster].cpus[cpu].cpu =CPU_GOING_DOWN ; +sync_mem (&bL_sync.clusters[cluster].cpus[cpu].cpu); +} + +/* +* __bL_cpu_down: Indicates that cpu teardown is complete and that the +* cluster can be torn down without disrupting this CPU. +* To avoid deadlocks, this must be called before a CPU is powered down. +* The CPU cache (SCTRL.C bit) is expected to be off. +*/ +void __bL_cpu_down (unsigned int cpu,unsigned int cluster)
Quoted 8 lines
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 155
Quoted 5 lines
arch/arm/common/bL_entry.c +5
-0+{ +dsb (); + bL_sync.clusters[cluster].cpus[cpu].cpu =CPU_DOWN ; +sync_mem (&bL_sync.clusters[cluster].cpus[cpu].cpu); +sev ();For the
sev()here (and other functions in this patch) you need adsb()before. I'm not sureouter_sync()has one.
__cpuc_flush_dcache_area() does though, via v7_flush_kern_dcache_area.
Nicolas
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 19 lines · Nicolas Pitre
Quoted 17 lines · Catalin Marinas
Quoted 16 lines · Nicolas Pitre
arch/arm/common/bL_entry.c +10
-0@@ -116,3 +116,163 @@ int bL_cpu_powered_up(void) platform_ops->powered_up ();return 0 ; } + +struct bL_sync_struct bL_sync; + +static void __sync_range (volatile void *p,size_t size) +{ +char *_p = (char *)p; + +__cpuc_flush_dcache_area (_p, size); +outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync ();
...
Quoted 25 lines
Quoted 7 lines
However, on the same CPU you can get a speculative load into L1 after the L1 flush but before the L2 flush, so the reader case can fail.
The sequence for readers is (note *L2* inval first):
L2 inval
L1 invalAs you noted below and as I explained above, this can't be an inval operation as that could discard a concurrent writer's update.
Quoted 14 lines
The sequence for writers is:
L1 clean
L2 cleanThe bi-directional sequence (that's what you need) is:
L1 clean
L2 clean+inval
L1 clean+invalThe last L1 op must be clean+inval in case another CPU writes to this location to avoid discarding the write.
Quoted 7 lines
Quoted 2 lines
If you don't have an L2, you just end up with two L1 clean ops, so you can probably put some checks.
In fact, since this is only used on A7/A15 right now, there is no outer cache and the outer calls are effectively no-ops. I'm wondering if those should simply be removed until/unless there is some system showing up with a need for them.
You could. I expect multi-cluster systems to have integrated L2 cache and avoid explicit outer cache maintenance. But is there a chance that your patches could be generalised to existing systems with A9 (not b.L configuration but just hotplug or cpuidle support)? I haven't finished reading all the patches, so maybe that's not the case at all.
Anyway, my point is that if L1 is inner and L2 outer, the correct bi-derectional flushing sequence is slightly different.
Signature · 1 line
Catalin
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 54 lines · Catalin Marinas
Quoted 19 lines · Nicolas Pitre
Quoted 17 lines · Catalin Marinas
Quoted 16 lines · Nicolas Pitre
arch/arm/common/bL_entry.c +10
-0@@ -116,3 +116,163 @@ int bL_cpu_powered_up(void) platform_ops->powered_up ();return 0 ; } + +struct bL_sync_struct bL_sync; + +static void __sync_range (volatile void *p,size_t size) +{ +char *_p = (char *)p; + +__cpuc_flush_dcache_area (_p, size); +outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync ();...
Quoted 25 lines
Quoted 7 lines
However, on the same CPU you can get a speculative load into L1 after the L1 flush but before the L2 flush, so the reader case can fail.
The sequence for readers is (note *L2* inval first):
L2 inval
L1 invalAs you noted below and as I explained above, this can't be an inval operation as that could discard a concurrent writer's update.
Quoted 14 lines
The sequence for writers is:
L1 clean
L2 cleanThe bi-directional sequence (that's what you need) is:
L1 clean
L2 clean+inval
L1 clean+invalThe last L1 op must be clean+inval in case another CPU writes to this location to avoid discarding the write.
Quoted 7 lines
Quoted 2 lines
If you don't have an L2, you just end up with two L1 clean ops, so you can probably put some checks.
In fact, since this is only used on A7/A15 right now, there is no outer cache and the outer calls are effectively no-ops. I'm wondering if those should simply be removed until/unless there is some system showing up with a need for them.
Quoted 5 lines
You could. I expect multi-cluster systems to have integrated L2 cache and avoid explicit outer cache maintenance. But is there a chance that your patches could be generalised to existing systems with A9 (not b.L configuration but just hotplug or cpuidle support)? I haven't finished reading all the patches, so maybe that's not the case at all.
I suppose it could, although the special requirements put on the first man / last man exist only for multi-cluster systems. OTOH, existing A9 systems are already served by far less complex code already, so it is really a matter of figuring out if the backend for those A9 systems needed by this cluster code would be simpler than the existing code, in which case that would certainly be beneficial.
Quoted 2 lines
Anyway, my point is that if L1 is inner and L2 outer, the correct bi-derectional flushing sequence is slightly different.
Agreed, I'll make sure to capture that in the code somehow.
Nicolas
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 36 lines · Nicolas Pitre
Quoted 34 lines · Catalin Marinas
Quoted 19 lines · Nicolas Pitre
Quoted 17 lines · Catalin Marinas
Quoted 16 lines · Nicolas Pitre
arch/arm/common/bL_entry.c +10
-0@@ -116,3 +116,163 @@ int bL_cpu_powered_up(void) platform_ops->powered_up ();return 0 ; } + +struct bL_sync_struct bL_sync; + +static void __sync_range (volatile void *p,size_t size) +{ +char *_p = (char *)p; + +__cpuc_flush_dcache_area (_p, size); +outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync ();...
Quoted 13 lines
Quoted 7 lines
However, on the same CPU you can get a speculative load into L1 after the L1 flush but before the L2 flush, so the reader case can fail.
The sequence for readers is (note *L2* inval first):
L2 inval
L1 invalAs you noted below and as I explained above, this can't be an inval operation as that could discard a concurrent writer's update.
Quoted 2 lines
The sequence for writers is:
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
L1 clean
L2 cleanThe bi-directional sequence (that's what you need) is:
L1 clean
L2 clean+inval
L1 clean+inval
Agreed. My bad, sorry... I was focusing on other aspects; plus we have no actual outer cache, so the mis-ordering is hidden in our testing.
This code has been through a few iterations, some of which had separate sequences for reads and writes, though possibly the ordering is still wrong.
If our cache is enabled, we may end up with the responsibility of writing out another CPU's dirty lines due to speculative migration, so for most or all of the flushes here, we do need the third sequence (inner clean; outer clean+invalidate; inner clean+invalidate)
Quoted 18 lines
Quoted 12 lines
Quoted 11 lines
Quoted 6 lines
The last L1 op must be clean+inval in case another CPU writes to this location to avoid discarding the write.
If you don't have an L2, you just end up with two L1 clean ops, so you can probably put some checks.
In fact, since this is only used on A7/A15 right now, there is no outer cache and the outer calls are effectively no-ops. I'm wondering if those should simply be removed until/unless there is some system showing up with a need for them.
Quoted 5 lines
You could. I expect multi-cluster systems to have integrated L2 cache and avoid explicit outer cache maintenance. But is there a chance that your patches could be generalised to existing systems with A9 (not b.L configuration but just hotplug or cpuidle support)? I haven't finished reading all the patches, so maybe that's not the case at all.
Quoted 6 lines
I suppose it could, although the special requirements put on the first man / last man exist only for multi-cluster systems. OTOH, existing A9 systems are already served by far less complex code already, so it is really a matter of figuring out if the backend for those A9 systems needed by this cluster code would be simpler than the existing code, in which case that would certainly be beneficial.
The outer operations just expand to nothing if there is no outer cache; the optimisation would be that instead of L1 clean; L1 clean+inval, we just need L1 clean+inval.
Quoted 4 lines
Quoted 2 lines
Anyway, my point is that if L1 is inner and L2 outer, the correct bi-derectional flushing sequence is slightly different.
Agreed, I'll make sure to capture that in the code somehow.
I'll have a go at this today (but I won't over-elaborate in case you've already done it...)
Cheers
---Dave
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
In reply to Catalin MarinasQuoted 8 lines · Catalin Marinas
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt line 2
Quoted 5 lines · Nicolas Pitre
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt new file +2
-0@@ -0,0 +1,498 @@ +Big.LITTLE cluster Power-up/power-down race avoidance algorithm +===============================================================Nice description and ascii art :).
Credits go to Dave Martin.
Nicolas
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
In reply to Nicolas PitreQuoted 6 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
This provides helper methods to coordinate between CPUs coming down and CPUs going up, as well as documentation on the used algorithms, so that cluster teardown and setup
operations are not done for a cluster simultaneously.
[...]
Quoted 8 lines
arch/arm/common/bL_entry.c +8
-0+int __init bL_cluster_sync_init (void (*power_up_setup)(void )) +{ +unsigned int i, j, mpidr, this_cluster; + +BUILD_BUG_ON (BL_SYNC_CLUSTER_SIZE *BL_NR_CLUSTERS !=sizeof bL_sync); +BUG_ON ((unsigned long )&bL_sync & (__CACHE_WRITEBACK_GRANULE -1 )); + +/*
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 265
Quoted 10 lines
arch/arm/common/bL_entry.c +10
-0+* Set initial CPU and cluster states. +* Only one cluster is assumed to be active at this point. +*/ +for (i =0 ; i <BL_NR_CLUSTERS ; i++) { + bL_sync.clusters[i].cluster =CLUSTER_DOWN ; + bL_sync.clusters[i].inbound =INBOUND_NOT_COMING_UP ; +for (j =0 ; j <BL_CPUS_PER_CLUSTER ; j++) + bL_sync.clusters[i].cpus[j].cpu =CPU_DOWN ; + } + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr));
We have a helper for this...
Quoted 1 line
+
this_cluster= (mpidr >> 8) &0xf;
... and also this, thanks to Lorenzo's recent patches.
Quoted 51 lines
arch/arm/common/bL_entry.c +12
-0+for_each_online_cpu (i) + bL_sync.clusters[this_cluster].cpus[i].cpu =CPU_UP ; + bL_sync.clusters[this_cluster].cluster =CLUSTER_UP ; +sync_mem (&bL_sync); + +if (power_up_setup) { + bL_power_up_setup_phys =virt_to_phys (power_up_setup); +sync_mem (&bL_power_up_setup_phys); + } + +return 0 ; +}arch/arm/common/bL_head.S +13
-2@@ -7,11 +7,19 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * + * Refer to Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt + * for details of the synchronisation algorithms used here. */ #include <linux/linkage.h> #include <asm/bL_entry.h> +.if BL_SYNC_CLUSTER_CPUS +.error "cpus must be the first member of struct bL_cluster_sync_struct" +.endif + .macro pr_dbg cpu, string #if defined(CONFIG_DEBUG_LL) && defined(DEBUG) b 1901f@@ -52,12 +60,82 @@ ENTRY(bL_entry_point) 2: pr_dbg r4, "kernel bL_entry_point\n" /* - * MMU is off so we need to get to bL_entry_vectors in a + * MMU is off so we need to get to various variables in a * position independent way. */ adr r5, 3f - ldr r6, [r5] + ldmia r5, {r6, r7, r8} add r6, r5, r6 @ r6 = bL_entry_vectors + ldr r7, [r5, r7] @ r7 = bL_power_up_setup_phys + add r8, r5, r8 @ r8 = bL_sync +
arch/arm/common/bL_head.S ENTRY(bL_entry_point) · line 81
Quoted 10 lines
arch/arm/common/bL_head.S +10
-0+ mov r0, #BL_SYNC_CLUSTER_SIZE + mla r8, r0, r10, r8 @ r8 = bL_sync cluster base + + @ Signal that this CPU is coming UP: + mov r0, #CPU_COMING_UP + mov r5, #BL_SYNC_CPU_SIZE + mla r5, r9, r5, r8 @ r5 = bL_sync cpu address + strb r0, [r5] + + dsb
Why is a dmb not enough here? In fact, the same goes for most of these other than the one preceeding the sev. Is there an interaction with the different mappings for the cluster data that I've missed?
Quoted 87 lines
arch/arm/common/bL_head.S +63
-0+ + @ At this point, the cluster cannot unexpectedly enter the GOING_DOWN + @ state, because there is at least one active CPU (this CPU). + + @ Check if the cluster has been set up yet: + ldrb r0, [r8, #BL_SYNC_CLUSTER_CLUSTER] + cmp r0, #CLUSTER_UP + beq cluster_already_up + + @ Signal that the cluster is being brought up: + mov r0, #INBOUND_COMING_UP + strb r0, [r8, #BL_SYNC_CLUSTER_INBOUND] + + dsb + + @ Any CPU trying to take the cluster into CLUSTER_GOING_DOWN from this + @ point onwards will observe INBOUND_COMING_UP and abort. + + @ Wait for any previously-pending cluster teardown operations to abort + @ or complete: +cluster_teardown_wait: + ldrb r0, [r8, #BL_SYNC_CLUSTER_CLUSTER] + cmp r0, #CLUSTER_GOING_DOWN + wfeeq + beq cluster_teardown_wait + + @ power_up_setup is responsible for setting up the cluster: + + cmp r7, #0 + mov r0, #1 @ second (cluster) affinity level + blxne r7 @ Call power_up_setup if defined + + @ Leave the cluster setup critical section: + + dsb + mov r0, #INBOUND_NOT_COMING_UP + strb r0, [r8, #BL_SYNC_CLUSTER_INBOUND] + mov r0, #CLUSTER_UP + strb r0, [r8, #BL_SYNC_CLUSTER_CLUSTER] + dsb + sev + +cluster_already_up: + @ If a platform-specific CPU setup hook is needed, it is + @ called from here. + + cmp r7, #0 + mov r0, #0 @ first (CPU) affinity level + blxne r7 @ Call power_up_setup if defined + + @ Mark the CPU as up: + + dsb + mov r0, #CPU_UP + strb r0, [r5] + dsb + sev bL_entry_gated: ldr r5, [r6, r4, lsl #2] @ r5 = CPU entry vector@@ -70,6 +148,8 @@ bL_entry_gated: .align 2 3: .word bL_entry_vectors - . + .word bL_power_up_setup_phys - 3b + .word bL_sync - 3b ENDPROC(bL_entry_point)@@ -79,3 +159,7 @@ ENDPROC(bL_entry_point) .type bL_entry_vectors, #object ENTRY(bL_entry_vectors) .space 4 * BL_NR_CLUSTERS * BL_CPUS_PER_CLUSTER + + .type bL_power_up_setup_phys, #object +ENTRY(bL_power_up_setup_phys) + .space 4 @ set by bL_cluster_sync_init()arch/arm/include/asm/bL_entry.h +2
-0@@ -15,8 +15,37 @@#define BL_CPUS_PER_CLUSTER 4#define BL_NR_CLUSTERS 2 +/* Definitions for bL_cluster_sync_struct */ +#define CPU_DOWN 0x11
arch/arm/include/asm/bL_entry.h line 29
Quoted 10 lines
arch/arm/include/asm/bL_entry.h +10
-0+#define CPU_COMING_UP 0x12 +#define CPU_UP 0x13 +#define CPU_GOING_DOWN 0x14 + +#define CLUSTER_DOWN 0x21 +#define CLUSTER_UP 0x22 +#define CLUSTER_GOING_DOWN 0x23 + +#define INBOUND_NOT_COMING_UP 0x31 +#define INBOUND_COMING_UP 0x32
Do these numbers signify anything? Why not 0, 1, 2 etc?
arch/arm/include/asm/bL_entry.h line 32
Quoted 3 lines
arch/arm/include/asm/bL_entry.h +3
-0+ +/* This is a complete guess. */ +#define __CACHE_WRITEBACK_ORDER 6
Is this CONFIG_ARM_L1_CACHE_SHIFT?
arch/arm/include/asm/bL_entry.h line 36
Quoted 4 lines
arch/arm/include/asm/bL_entry.h +4
-0+#define __CACHE_WRITEBACK_GRANULE (1 << __CACHE_WRITEBACK_ORDER) + +/* Offsets for the bL_cluster_sync_struct members, for use in asm: */ +#define BL_SYNC_CLUSTER_CPUS 0
Why not use asm-offsets.h for this?
arch/arm/include/asm/bL_entry.h line 44
Quoted 8 lines
arch/arm/include/asm/bL_entry.h +8
-0+#define BL_SYNC_CPU_SIZE __CACHE_WRITEBACK_GRANULE +#define BL_SYNC_CLUSTER_CLUSTER \ + (BL_SYNC_CLUSTER_CPUS + BL_SYNC_CPU_SIZE * BL_CPUS_PER_CLUSTER) +#define BL_SYNC_CLUSTER_INBOUND \ + (BL_SYNC_CLUSTER_CLUSTER + __CACHE_WRITEBACK_GRANULE) +#define BL_SYNC_CLUSTER_SIZE \ + (BL_SYNC_CLUSTER_INBOUND + __CACHE_WRITEBACK_GRANULE) +
Hmm, this looks pretty fragile to me but again, you need this stuff at compile time. Is there an architected maximum value for the writeback granule? Failing that, we may as well just use things like __cacheline_aligned if we're only using the L1 alignment anyway.
Will
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 31 lines · Will Deacon
Quoted 6 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
This provides helper methods to coordinate between CPUs coming down and CPUs going up, as well as documentation on the used algorithms, so that cluster teardown and setup
operations are not done for a cluster simultaneously.[...]
Quoted 8 lines
arch/arm/common/bL_entry.c +8
-0+int __init bL_cluster_sync_init (void (*power_up_setup)(void )) +{ +unsigned int i, j, mpidr, this_cluster; + +BUILD_BUG_ON (BL_SYNC_CLUSTER_SIZE *BL_NR_CLUSTERS !=sizeof bL_sync); +BUG_ON ((unsigned long )&bL_sync & (__CACHE_WRITEBACK_GRANULE -1 )); + +/* arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 265
Quoted 10 lines
arch/arm/common/bL_entry.c +10
-0+* Set initial CPU and cluster states. +* Only one cluster is assumed to be active at this point. +*/ +for (i =0 ; i <BL_NR_CLUSTERS ; i++) { + bL_sync.clusters[i].cluster =CLUSTER_DOWN ; + bL_sync.clusters[i].inbound =INBOUND_NOT_COMING_UP ; +for (j =0 ; j <BL_CPUS_PER_CLUSTER ; j++) + bL_sync.clusters[i].cpus[j].cpu =CPU_DOWN ; + } + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr));We have a helper for this...
Quoted 3 lines
Quoted 1 line
+
this_cluster= (mpidr >> 8) &0xf;... and also this, thanks to Lorenzo's recent patches.
Indeed, I'll have a closer look at them.
Quoted 57 lines
Quoted 57 lines
arch/arm/common/bL_entry.c +12
-0+for_each_online_cpu (i) + bL_sync.clusters[this_cluster].cpus[i].cpu =CPU_UP ; + bL_sync.clusters[this_cluster].cluster =CLUSTER_UP ; +sync_mem (&bL_sync); + +if (power_up_setup) { + bL_power_up_setup_phys =virt_to_phys (power_up_setup); +sync_mem (&bL_power_up_setup_phys); + } + +return 0 ; +}arch/arm/common/bL_head.S +19
-2@@ -7,11 +7,19 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * + * Refer to Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt + * for details of the synchronisation algorithms used here. */ #include <linux/linkage.h> #include <asm/bL_entry.h> +.if BL_SYNC_CLUSTER_CPUS +.error "cpus must be the first member of struct bL_cluster_sync_struct" +.endif + .macro pr_dbg cpu, string #if defined(CONFIG_DEBUG_LL) && defined(DEBUG) b 1901f@@ -52,12 +60,82 @@ ENTRY(bL_entry_point) 2: pr_dbg r4, "kernel bL_entry_point\n" /* - * MMU is off so we need to get to bL_entry_vectors in a + * MMU is off so we need to get to various variables in a * position independent way. */ adr r5, 3f - ldr r6, [r5] + ldmia r5, {r6, r7, r8} add r6, r5, r6 @ r6 = bL_entry_vectors + ldr r7, [r5, r7] @ r7 = bL_power_up_setup_phys + add r8, r5, r8 @ r8 = bL_sync + + mov r0, #BL_SYNC_CLUSTER_SIZE + mla r8, r0, r10, r8 @ r8 = bL_sync cluster base + + @ Signal that this CPU is coming UP: + mov r0, #CPU_COMING_UP + mov r5, #BL_SYNC_CPU_SIZE
Quoted 8 lines
arch/arm/common/bL_head.S ENTRY(bL_entry_point) · line 81
Quoted 4 lines
arch/arm/common/bL_head.S +4
-0+ mla r5, r9, r5, r8 @ r5 = bL_sync cpu address + strb r0, [r5] + + dsbWhy is a dmb not enough here? In fact, the same goes for most of these other than the one preceeding the sev. Is there an interaction with the different mappings for the cluster data that I've missed?
Probably Dave could comment more on this as this is his code, or Achin who also reviewed it. I don't know the level of discussion that happened inside ARM around those barriers.
When the TC2 firmware didn't properly handle the ACP snoops, the dsb's couldn't be used at this point. The replacement for a dsb was a read back followed by a dmb in that case, and then the general sentiment was that this was an A15 specific workaround which wasn't architecturally guaranteed on all ARMv7 compliant implementations, or something along those lines.
Given that the TC2 firmware properly handles the snoops now, and that the dsb apparently doesn't require a readback, we just decided to revert to having simple dsb's.
Quoted 85 lines
Quoted 85 lines
arch/arm/common/bL_head.S +63
-0+ + @ At this point, the cluster cannot unexpectedly enter the GOING_DOWN + @ state, because there is at least one active CPU (this CPU). + + @ Check if the cluster has been set up yet: + ldrb r0, [r8, #BL_SYNC_CLUSTER_CLUSTER] + cmp r0, #CLUSTER_UP + beq cluster_already_up + + @ Signal that the cluster is being brought up: + mov r0, #INBOUND_COMING_UP + strb r0, [r8, #BL_SYNC_CLUSTER_INBOUND] + + dsb + + @ Any CPU trying to take the cluster into CLUSTER_GOING_DOWN from this + @ point onwards will observe INBOUND_COMING_UP and abort. + + @ Wait for any previously-pending cluster teardown operations to abort + @ or complete: +cluster_teardown_wait: + ldrb r0, [r8, #BL_SYNC_CLUSTER_CLUSTER] + cmp r0, #CLUSTER_GOING_DOWN + wfeeq + beq cluster_teardown_wait + + @ power_up_setup is responsible for setting up the cluster: + + cmp r7, #0 + mov r0, #1 @ second (cluster) affinity level + blxne r7 @ Call power_up_setup if defined + + @ Leave the cluster setup critical section: + + dsb + mov r0, #INBOUND_NOT_COMING_UP + strb r0, [r8, #BL_SYNC_CLUSTER_INBOUND] + mov r0, #CLUSTER_UP + strb r0, [r8, #BL_SYNC_CLUSTER_CLUSTER] + dsb + sev + +cluster_already_up: + @ If a platform-specific CPU setup hook is needed, it is + @ called from here. + + cmp r7, #0 + mov r0, #0 @ first (CPU) affinity level + blxne r7 @ Call power_up_setup if defined + + @ Mark the CPU as up: + + dsb + mov r0, #CPU_UP + strb r0, [r5] + dsb + sev bL_entry_gated: ldr r5, [r6, r4, lsl #2] @ r5 = CPU entry vector@@ -70,6 +148,8 @@ bL_entry_gated: .align 2 3: .word bL_entry_vectors - . + .word bL_power_up_setup_phys - 3b + .word bL_sync - 3b ENDPROC(bL_entry_point)@@ -79,3 +159,7 @@ ENDPROC(bL_entry_point) .type bL_entry_vectors, #object ENTRY(bL_entry_vectors) .space 4 * BL_NR_CLUSTERS * BL_CPUS_PER_CLUSTER + + .type bL_power_up_setup_phys, #object +ENTRY(bL_power_up_setup_phys) + .space 4 @ set by bL_cluster_sync_init()arch/arm/include/asm/bL_entry.h +0
-0@@ -15,8 +15,37 @@#define BL_CPUS_PER_CLUSTER 4#define BL_NR_CLUSTERS 2
Quoted 14 lines
arch/arm/include/asm/bL_entry.h line 29
Quoted 12 lines
arch/arm/include/asm/bL_entry.h +12
-0+/* Definitions for bL_cluster_sync_struct */ +#define CPU_DOWN 0x11 +#define CPU_COMING_UP 0x12 +#define CPU_UP 0x13 +#define CPU_GOING_DOWN 0x14 + +#define CLUSTER_DOWN 0x21 +#define CLUSTER_UP 0x22 +#define CLUSTER_GOING_DOWN 0x23 + +#define INBOUND_NOT_COMING_UP 0x31 +#define INBOUND_COMING_UP 0x32Do these numbers signify anything? Why not 0, 1, 2 etc?
Initially that's what they were. But durring debugging (as we faced a few cache coherency issues here) it was more useful to use numbers with an easily distinguishable signature. For example, a 0 may come from anywhere and could mean anything so that is about the worst choice. Other than that, those numbers have no particular significance.
Quoted 5 lines
arch/arm/include/asm/bL_entry.h line 32
Quoted 3 lines
arch/arm/include/asm/bL_entry.h +3
-0+ +/* This is a complete guess. */ +#define __CACHE_WRITEBACK_ORDER 6Is this
CONFIG_ARM_L1_CACHE_SHIFT?
No. That has to cover L2 as well.
Quoted 6 lines
arch/arm/include/asm/bL_entry.h line 36
Quoted 4 lines
arch/arm/include/asm/bL_entry.h +4
-0+#define __CACHE_WRITEBACK_GRANULE (1 << __CACHE_WRITEBACK_ORDER) + +/* Offsets for the bL_cluster_sync_struct members, for use in asm: */ +#define BL_SYNC_CLUSTER_CPUS 0Why not use
asm-offsets.hfor this?
That's how that was done initially. But that ended up cluttering asm-offsets.h for stuff that actually is really a local implementation detail which doesn't need kernel wide scope. In other words, the end result looked worse.
One could argue that they are still exposed too much as the only files that need to know about those defines are bL_head.S and bL_entry.c.
Quoted 11 lines
arch/arm/include/asm/bL_entry.h line 44
Quoted 8 lines
arch/arm/include/asm/bL_entry.h +8
-0+#define BL_SYNC_CPU_SIZE __CACHE_WRITEBACK_GRANULE +#define BL_SYNC_CLUSTER_CLUSTER \ + (BL_SYNC_CLUSTER_CPUS + BL_SYNC_CPU_SIZE * BL_CPUS_PER_CLUSTER) +#define BL_SYNC_CLUSTER_INBOUND \ + (BL_SYNC_CLUSTER_CLUSTER + __CACHE_WRITEBACK_GRANULE) +#define BL_SYNC_CLUSTER_SIZE \ + (BL_SYNC_CLUSTER_INBOUND + __CACHE_WRITEBACK_GRANULE) +Hmm, this looks pretty fragile to me but again, you need this stuff at compile time.
There are compile time and run time assertions in bL_entry.c to ensure those offsets and the corresponding C structure don't get out of sync.
Quoted 3 lines
Is there an architected maximum value for the writeback granule? Failing that, we may as well just use things like
__cacheline_alignedif we're only using the L1 alignment anyway.
See above -- we need L2 alignment.
Nicolas
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 24 lines · Nicolas Pitre
Quoted 22 lines · Will Deacon
Quoted 6 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
This provides helper methods to coordinate between CPUs coming down and CPUs going up, as well as documentation on the used algorithms, so that cluster teardown and setup
operations are not done for a cluster simultaneously.[...]
Quoted 12 lines
arch/arm/common/bL_entry.c +12
-0+int __init bL_cluster_sync_init (void (*power_up_setup)(void )) +{ +unsigned int i, j, mpidr, this_cluster; + +BUILD_BUG_ON (BL_SYNC_CLUSTER_SIZE *BL_NR_CLUSTERS !=sizeof bL_sync); +BUG_ON ((unsigned long )&bL_sync & (__CACHE_WRITEBACK_GRANULE -1 )); + +/* +* Set initial CPU and cluster states. +* Only one cluster is assumed to be active at this point. +*/ +for (i =0 ; i <BL_NR_CLUSTERS ; i++) {
Quoted 8 lines
Quoted 8 lines
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 265
Quoted 6 lines
arch/arm/common/bL_entry.c +6
-0+ bL_sync.clusters[i].cluster =CLUSTER_DOWN ; + bL_sync.clusters[i].inbound =INBOUND_NOT_COMING_UP ; +for (j =0 ; j <BL_CPUS_PER_CLUSTER ; j++) + bL_sync.clusters[i].cpus[j].cpu =CPU_DOWN ; + } + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr));We have a helper for this...
Agreed, we would ideally use a single definition for that.
Quoted 84 lines
Quoted 4 lines
Quoted 2 lines
+
this_cluster= (mpidr >> 8) &0xf;... and also this, thanks to Lorenzo's recent patches.
Indeed, I'll have a closer look at them.
Quoted 57 lines
Quoted 57 lines
arch/arm/common/bL_entry.c +12
-0+for_each_online_cpu (i) + bL_sync.clusters[this_cluster].cpus[i].cpu =CPU_UP ; + bL_sync.clusters[this_cluster].cluster =CLUSTER_UP ; +sync_mem (&bL_sync); + +if (power_up_setup) { + bL_power_up_setup_phys =virt_to_phys (power_up_setup); +sync_mem (&bL_power_up_setup_phys); + } + +return 0 ; +}arch/arm/common/bL_head.S +19
-2@@ -7,11 +7,19 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * + * Refer to Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt + * for details of the synchronisation algorithms used here. */ #include <linux/linkage.h> #include <asm/bL_entry.h> +.if BL_SYNC_CLUSTER_CPUS +.error "cpus must be the first member of struct bL_cluster_sync_struct" +.endif + .macro pr_dbg cpu, string #if defined(CONFIG_DEBUG_LL) && defined(DEBUG) b 1901f@@ -52,12 +60,82 @@ ENTRY(bL_entry_point) 2: pr_dbg r4, "kernel bL_entry_point\n" /* - * MMU is off so we need to get to bL_entry_vectors in a + * MMU is off so we need to get to various variables in a * position independent way. */ adr r5, 3f - ldr r6, [r5] + ldmia r5, {r6, r7, r8} add r6, r5, r6 @ r6 = bL_entry_vectors + ldr r7, [r5, r7] @ r7 = bL_power_up_setup_phys + add r8, r5, r8 @ r8 = bL_sync + + mov r0, #BL_SYNC_CLUSTER_SIZE + mla r8, r0, r10, r8 @ r8 = bL_sync cluster base + + @ Signal that this CPU is coming UP: + mov r0, #CPU_COMING_UP + mov r5, #BL_SYNC_CPU_SIZEQuoted 8 lines
arch/arm/common/bL_head.S ENTRY(bL_entry_point) · line 81
Quoted 4 lines
arch/arm/common/bL_head.S +4
-0+ mla r5, r9, r5, r8 @ r5 = bL_sync cpu address + strb r0, [r5] + + dsbWhy is a dmb not enough here? In fact, the same goes for most of these other than the one preceeding the sev. Is there an interaction with the different mappings for the cluster data that I've missed?
Probably Dave could comment more on this as this is his code, or Achin who also reviewed it. I don't know the level of discussion that happened inside ARM around those barriers.
When the TC2 firmware didn't properly handle the ACP snoops, the dsb's couldn't be used at this point. The replacement for a dsb was a read back followed by a dmb in that case, and then the general sentiment was that this was an A15 specific workaround which wasn't architecturally guaranteed on all ARMv7 compliant implementations, or something along those lines.
Quoted 3 lines
Given that the TC2 firmware properly handles the snoops now, and that the dsb apparently doesn't require a readback, we just decided to revert to having simple dsb's.
I'll take another look at the code and think about this again. This code was initially a bit conservative. Because we are S-O at this point, most of your potential dmbs should actually require no barrier at all (as in the vlock code). I was cautious about that, but we've now seen the principle work successfully with the vlock code (which postdates the cluster state handling code here).
The one exception is before SEV. Also, before WFE (opinions differ, but since we are about to wait anyway the extra time cost of the dsb is not really a concern here).
Quoted 107 lines
Quoted 86 lines
Quoted 86 lines
arch/arm/common/bL_head.S +63
-0+ + @ At this point, the cluster cannot unexpectedly enter the GOING_DOWN + @ state, because there is at least one active CPU (this CPU). + + @ Check if the cluster has been set up yet: + ldrb r0, [r8, #BL_SYNC_CLUSTER_CLUSTER] + cmp r0, #CLUSTER_UP + beq cluster_already_up + + @ Signal that the cluster is being brought up: + mov r0, #INBOUND_COMING_UP + strb r0, [r8, #BL_SYNC_CLUSTER_INBOUND] + + dsb + + @ Any CPU trying to take the cluster into CLUSTER_GOING_DOWN from this + @ point onwards will observe INBOUND_COMING_UP and abort. + + @ Wait for any previously-pending cluster teardown operations to abort + @ or complete: +cluster_teardown_wait: + ldrb r0, [r8, #BL_SYNC_CLUSTER_CLUSTER] + cmp r0, #CLUSTER_GOING_DOWN + wfeeq + beq cluster_teardown_wait + + @ power_up_setup is responsible for setting up the cluster: + + cmp r7, #0 + mov r0, #1 @ second (cluster) affinity level + blxne r7 @ Call power_up_setup if defined + + @ Leave the cluster setup critical section: + + dsb + mov r0, #INBOUND_NOT_COMING_UP + strb r0, [r8, #BL_SYNC_CLUSTER_INBOUND] + mov r0, #CLUSTER_UP + strb r0, [r8, #BL_SYNC_CLUSTER_CLUSTER] + dsb + sev + +cluster_already_up: + @ If a platform-specific CPU setup hook is needed, it is + @ called from here. + + cmp r7, #0 + mov r0, #0 @ first (CPU) affinity level + blxne r7 @ Call power_up_setup if defined + + @ Mark the CPU as up: + + dsb + mov r0, #CPU_UP + strb r0, [r5] + dsb + sev bL_entry_gated: ldr r5, [r6, r4, lsl #2] @ r5 = CPU entry vector@@ -70,6 +148,8 @@ bL_entry_gated: .align 2 3: .word bL_entry_vectors - . + .word bL_power_up_setup_phys - 3b + .word bL_sync - 3b ENDPROC(bL_entry_point)@@ -79,3 +159,7 @@ ENDPROC(bL_entry_point) .type bL_entry_vectors, #object ENTRY(bL_entry_vectors) .space 4 * BL_NR_CLUSTERS * BL_CPUS_PER_CLUSTER + + .type bL_power_up_setup_phys, #object +ENTRY(bL_power_up_setup_phys) + .space 4 @ set by bL_cluster_sync_init()arch/arm/include/asm/bL_entry.h +0
-0@@ -15,8 +15,37 @@#define BL_CPUS_PER_CLUSTER 4#define BL_NR_CLUSTERS 2Quoted 14 lines
arch/arm/include/asm/bL_entry.h line 29
Quoted 12 lines
arch/arm/include/asm/bL_entry.h +12
-0+/* Definitions for bL_cluster_sync_struct */ +#define CPU_DOWN 0x11 +#define CPU_COMING_UP 0x12 +#define CPU_UP 0x13 +#define CPU_GOING_DOWN 0x14 + +#define CLUSTER_DOWN 0x21 +#define CLUSTER_UP 0x22 +#define CLUSTER_GOING_DOWN 0x23 + +#define INBOUND_NOT_COMING_UP 0x31 +#define INBOUND_COMING_UP 0x32Do these numbers signify anything? Why not 0, 1, 2 etc?
Initially that's what they were. But durring debugging (as we faced a few cache coherency issues here) it was more useful to use numbers with an easily distinguishable signature. For example, a 0 may come from anywhere and could mean anything so that is about the worst choice. Other than that, those numbers have no particular significance.
Quoted 7 lines
Quoted 5 lines
arch/arm/include/asm/bL_entry.h line 32
Quoted 3 lines
arch/arm/include/asm/bL_entry.h +3
-0+ +/* This is a complete guess. */ +#define __CACHE_WRITEBACK_ORDER 6Is this
CONFIG_ARM_L1_CACHE_SHIFT?No. That has to cover L2 as well.
Of course, I seem to remember that there are assumptions elsewhere in the kernel that 1 << CONFIG_ARM_L1_CACHE_SHIFT is (at least) the cache writeback granule.
I prefer not to use a macro with a wholly misleading name, but I would like a "proper" way to get this value, if there is one ... ?
One reason for adding a #define here was to document the fact that the value used really is a guess and that we have no correct way to discover it.
Quoted 28 lines
Quoted 7 lines
arch/arm/include/asm/bL_entry.h line 36
Quoted 5 lines
arch/arm/include/asm/bL_entry.h +4
-0+#define __CACHE_WRITEBACK_GRANULE (1 << __CACHE_WRITEBACK_ORDER) + +/* Offsets for the bL_cluster_sync_struct members, for use in asm: */ +#define BL_SYNC_CLUSTER_CPUS 0Why not use
asm-offsets.hfor this?That's how that was done initially. But that ended up cluttering
asm-offsets.hfor stuff that actually is really a local implementation detail which doesn't need kernel wide scope. In other words, the end result looked worse.One could argue that they are still exposed too much as the only files that need to know about those defines are
bL_head.SandbL_entry.c.Quoted 11 lines
arch/arm/include/asm/bL_entry.h line 44
Quoted 8 lines
arch/arm/include/asm/bL_entry.h +8
-0+#define BL_SYNC_CPU_SIZE __CACHE_WRITEBACK_GRANULE +#define BL_SYNC_CLUSTER_CLUSTER \ + (BL_SYNC_CLUSTER_CPUS + BL_SYNC_CPU_SIZE * BL_CPUS_PER_CLUSTER) +#define BL_SYNC_CLUSTER_INBOUND \ + (BL_SYNC_CLUSTER_CLUSTER + __CACHE_WRITEBACK_GRANULE) +#define BL_SYNC_CLUSTER_SIZE \ + (BL_SYNC_CLUSTER_INBOUND + __CACHE_WRITEBACK_GRANULE) +Hmm, this looks pretty fragile to me but again, you need this stuff at compile time.
Quoted 5 lines
There are compile time and run time assertions in
bL_entry.cto ensure those offsets and the corresponding C structure don't get out of sync.Quoted 2 lines
Is there an architected maximum value for the writeback granule? Failing that, we may as well just use things like
There is an architectural maximum, bit it is 2K (which although "safe" feels a bit excessive for our purposes. A 2+3 CPU system would require at least 22K for the synchronisation data with this assumption, rising to 28K for 4+4. Not the end of the world for .bss data on modern hardware with GB of DRAM, but it still feels wasteful.
Does anyone have a view on how much we care?
If there is no outer cache, the actual granule size can be determined via CP15 at run-time; if there is an outer cache, we would also need to find out its granule somehow.
Quoted 3 lines
Quoted 1 line
__cacheline_alignedif we're only using the L1 alignment anyway.See above -- we need L2 alignment.
This partly depends on whether __cacheline_aligned is supposed to guarantee cache writeback granule alignment. Is it? At best I was highly uncertain about this.
Cheers
---Dave
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
In reply to Nicolas PitreQuoted 538 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
This provides helper methods to coordinate between CPUs coming down and CPUs going up, as well as documentation on the used algorithms, so that cluster teardown and setup
operations are not done for a cluster simultaneously.For use in the
power_down()implementation:
*__bL_cpu_going_down(unsigned int cluster, unsigned int cpu)
*__bL_outbound_enter_critical(unsigned int cluster)
*__bL_outbound_leave_critical(unsigned int cluster)
*__bL_cpu_down(unsigned int cluster, unsigned int cpu)The
power_up_setup()helper should do platform-specific setup in preparation for turning the CPU on, such as invalidating local caches or entering coherency. It must be assembler for now, since it must run before the MMU can be switched on. It is passed the affinity level which should be initialized.Because the
bL_cluster_sync_structcontent is looked-up and modified with the cache enabled or disabled depending on the code path, it is crucial to always ensure proper cache maintenance to update main memory right away. Therefore, any cached write must be followed by a cache clean operation and any cached read must be preceded by a cache invalidate operation on the accessed memory.To avoid races where a reader would invalidate the cache and discard the latest update from a writer before that writer had a chance to clean it to RAM, we simply use cache flush (clean+invalidate) operations everywhere.
Also, in order to prevent a cached writer from interfering with an adjacent non-cached writer, we ensure each state variable is located to a separate cache line.
Thanks to Nicolas Pitre and Achin Gupta for the help with this patch.
Signed-off-by Dave Martin <[email protected]>--- .../arm/big.LITTLE/cluster-pm-race-avoidance.txt | 498 +++++++++++++++++++++ arch/arm/common/bL_entry.c | 160 +++++++ arch/arm/common/bL_head.S | 88 +++- arch/arm/include/asm/bL_entry.h | 62 +++ 4 files changed, 806 insertions(+), 2 deletions(-)create mode 100644
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txtDocumentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt new file +485
-0@@ -0,0 +1,498 @@ +Big.LITTLE cluster Power-up/power-down race avoidance algorithm +=============================================================== + +This file documents the algorithm which is used to coordinate CPU and +cluster setup and teardown operations and to manage hardware coherency +controls safely. + +The section "Rationale" explains what the algorithm is for and why it is +needed. "Basic model" explains general concepts using a simplified view +of the system. The other sections explain the actual details of the +algorithm in use. + + +Rationale +--------- + +In a system containing multiple CPUs, it is desirable to have the +ability to turn off individual CPUs when the system is idle, reducing +power consumption and thermal dissipation. + +In a system containing multiple clusters of CPUs, it is also desirable +to have the ability to turn off entire clusters. + +Turning entire clusters off and on is a risky business, because it +involves performing potentially destructive operations affecting a group +of independently running CPUs, while the OS continues to run. This +means that we need some coordination in order to ensure that critical +cluster-level operations are only performed when it is truly safe to do +so. + +Simple locking may not be sufficient to solve this problem, because +mechanisms like Linux spinlocks may rely on coherency mechanisms which +are not immediately enabled when a cluster powers up. Since enabling or +disabling those mechanisms may itself be a non-atomic operation (such as +writing some hardware registers and invalidating large caches), other +methods of coordination are required in order to guarantee safe +power-down and power-up at the cluster level. + +The mechanism presented in this document describes a coherent memory +based protocol for performing the needed coordination. It aims to be as +lightweight as possible, while providing the required safety properties. + + +Basic model +----------- + +Each cluster and CPU is assigned a state, as follows: + + DOWN + COMING_UP + UP + GOING_DOWN + + +---------> UP ----------+ + | v + + COMING_UP GOING_DOWN + + ^ | + +--------- DOWN <--------+ + + +DOWN: The CPU or cluster is not coherent, and is either powered off or + suspended, or is ready to be powered off or suspended. + +COMING_UP: The CPU or cluster has committed to moving to the UP state. + It may be part way through the process of initialisation and + enabling coherency. + +UP: The CPU or cluster is active and coherent at the hardware + level. A CPU in this state is not necessarily being used + actively by the kernel. + +GOING_DOWN: The CPU or cluster has committed to moving to the DOWN + state. It may be part way through the process of teardown and + coherency exit. + + +Each CPU has one of these states assigned to it at any point in time. +The CPU states are described in the "CPU state" section, below. + +Each cluster is also assigned a state, but it is necessary to split the +state value into two parts (the "cluster" state and "inbound" state) and +to introduce additional states in order to avoid races between different +CPUs in the cluster simultaneously modifying the state. The cluster- +level states are described in the "Cluster state" section. + +To help distinguish the CPU states from cluster states in this +discussion, the state names are given a CPU_ prefix for the CPU states, +and a CLUSTER_ or INBOUND_ prefix for the cluster states. + + +CPU state +--------- + +In this algorithm, each individual core in a multi-core processor is +referred to as a "CPU". CPUs are assumed to be single-threaded: +therefore, a CPU can only be doing one thing at a single point in time. + +This means that CPUs fit the basic model closely. + +The algorithm defines the following states for each CPU in the system: + + CPU_DOWN + CPU_COMING_UP + CPU_UP + CPU_GOING_DOWN + + cluster setup and + CPU setup complete policy decision + +-----------> CPU_UP ------------+ + | v + + CPU_COMING_UP CPU_GOING_DOWN + + ^ | + +----------- CPU_DOWN <----------+ + policy decision CPU teardown complete + or hardware event + + +The definitions of the four states correspond closely to the states of +the basic model. + +Transitions between states occur as follows. + +A trigger event (spontaneous) means that the CPU can transition to the +next state as a result of making local progress only, with no +requirement for any external event to happen. + + +CPU_DOWN: + + A CPU reaches the CPU_DOWN state when it is ready for + power-down. On reaching this state, the CPU will typically + power itself down or suspend itself, via a WFI instruction or a + firmware call. + + Next state: CPU_COMING_UP + Conditions: none + + Trigger events: + + a) an explicit hardware power-up operation, resulting + from a policy decision on another CPU; + + b) a hardware event, such as an interrupt. + + +CPU_COMING_UP: + + A CPU cannot start participating in hardware coherency until the + cluster is set up and coherent. If the cluster is not ready, + then the CPU will wait in the CPU_COMING_UP state until the + cluster has been set up. + + Next state: CPU_UP + Conditions: The CPU's parent cluster must be in CLUSTER_UP. + Trigger events: Transition of the parent cluster to CLUSTER_UP. + + Refer to the "Cluster state" section for a description of the + CLUSTER_UP state. + + +CPU_UP: + When a CPU reaches the CPU_UP state, it is safe for the CPU to + start participating in local coherency. + + This is done by jumping to the kernel's CPU resume code. + + Note that the definition of this state is slightly different + from the basic model definition: CPU_UP does not mean that the + CPU is coherent yet, but it does mean that it is safe to resume + the kernel. The kernel handles the rest of the resume + procedure, so the remaining steps are not visible as part of the + race avoidance algorithm. + + The CPU remains in this state until an explicit policy decision + is made to shut down or suspend the CPU. + + Next state: CPU_GOING_DOWN + Conditions: none + Trigger events: explicit policy decision + + +CPU_GOING_DOWN: + + While in this state, the CPU exits coherency, including any + operations required to achieve this (such as cleaning data + caches). + + Next state: CPU_DOWN + Conditions: local CPU teardown complete + Trigger events: (spontaneous) + + +Cluster state +------------- + +A cluster is a group of connected CPUs with some common resources. +Because a cluster contains multiple CPUs, it can be doing multiple +things at the same time. This has some implications. In particular, a +CPU can start up while another CPU is tearing the cluster down. + +In this discussion, the "outbound side" is the view of the cluster state +as seen by a CPU tearing the cluster down. The "inbound side" is the +view of the cluster state as seen by a CPU setting the CPU up. + +In order to enable safe coordination in such situations, it is important +that a CPU which is setting up the cluster can advertise its state +independently of the CPU which is tearing down the cluster. For this +reason, the cluster state is split into two parts: + + "cluster" state: The global state of the cluster; or the state + on the outbound side: + + CLUSTER_DOWN + CLUSTER_UP + CLUSTER_GOING_DOWN + + "inbound" state: The state of the cluster on the inbound side. + + INBOUND_NOT_COMING_UP + INBOUND_COMING_UP + + + The different pairings of these states results in six possible + states for the cluster as a whole: + + CLUSTER_UP + +==========> INBOUND_NOT_COMING_UP -------------+ + # | + | + CLUSTER_UP <----+ | + INBOUND_COMING_UP | v + + ^ CLUSTER_GOING_DOWN CLUSTER_GOING_DOWN + # INBOUND_COMING_UP <=== INBOUND_NOT_COMING_UP + + CLUSTER_DOWN | | + INBOUND_COMING_UP <----+ | + | + ^ | + +=========== CLUSTER_DOWN <------------+ + INBOUND_NOT_COMING_UP + + Transitions -----> can only be made by the outbound CPU, and + only involve changes to the "cluster" state. + + Transitions ===##> can only be made by the inbound CPU, and only + involve changes to the "inbound" state, except where there is no + further transition possible on the outbound side (i.e., the + outbound CPU has put the cluster into the CLUSTER_DOWN state). + + The race avoidance algorithm does not provide a way to determine + which exact CPUs within the cluster play these roles. This must + be decided in advance by some other means. Refer to the section + "Last man and first man selection" for more explanation. + + + CLUSTER_DOWN/INBOUND_NOT_COMING_UP is the only state where the + cluster can actually be powered down. + + The parallelism of the inbound and outbound CPUs is observed by + the existence of two different paths from CLUSTER_GOING_DOWN/ + INBOUND_NOT_COMING_UP (corresponding to GOING_DOWN in the basic + model) to CLUSTER_DOWN/INBOUND_COMING_UP (corresponding to + COMING_UP in the basic model). The second path avoids cluster + teardown completely. + + CLUSTER_UP/INBOUND_COMING_UP is equivalent to UP in the basic + model. The final transition to CLUSTER_UP/INBOUND_NOT_COMING_UP + is trivial and merely resets the state machine ready for the + next cycle. + + Details of the allowable transitions follow. + + The next state in each case is notated + + <cluster state>/<inbound state> (<transitioner>) + + where the <transitioner> is the side on which the transition + can occur; either the inbound or the outbound side. + + +CLUSTER_DOWN/INBOUND_NOT_COMING_UP: + + Next state: CLUSTER_DOWN/INBOUND_COMING_UP (inbound) + Conditions: none + Trigger events: + + a) an explicit hardware power-up operation, resulting + from a policy decision on another CPU; + + b) a hardware event, such as an interrupt. + + +CLUSTER_DOWN/INBOUND_COMING_UP: + + In this state, an inbound CPU sets up the cluster, including + enabling of hardware coherency at the cluster level and any + other operations (such as cache invalidation) which are required + in order to achieve this. + + The purpose of this state is to do sufficient cluster-level + setup to enable other CPUs in the cluster to enter coherency + safely. + + Next state: CLUSTER_UP/INBOUND_COMING_UP (inbound) + Conditions: cluster-level setup and hardware coherency complete + Trigger events: (spontaneous) + + +CLUSTER_UP/INBOUND_COMING_UP: + + Cluster-level setup is complete and hardware coherency is + enabled for the cluster. Other CPUs in the cluster can safely + enter coherency. + + This is a transient state, leading immediately to + CLUSTER_UP/INBOUND_NOT_COMING_UP. All other CPUs on the cluster + should consider treat these two states as equivalent. + + Next state: CLUSTER_UP/INBOUND_NOT_COMING_UP (inbound) + Conditions: none + Trigger events: (spontaneous) + + +CLUSTER_UP/INBOUND_NOT_COMING_UP: + + Cluster-level setup is complete and hardware coherency is + enabled for the cluster. Other CPUs in the cluster can safely + enter coherency. + + The cluster will remain in this state until a policy decision is + made to power the cluster down. + + Next state: CLUSTER_GOING_DOWN/INBOUND_NOT_COMING_UP (outbound) + Conditions: none + Trigger events: policy decision to power down the cluster + + +CLUSTER_GOING_DOWN/INBOUND_NOT_COMING_UP: + + An outbound CPU is tearing the cluster down. The selected CPU + must wait in this state until all CPUs in the cluster are in the + CPU_DOWN state. + + When all CPUs are in the CPU_DOWN state, the cluster can be torn + down, for example by cleaning data caches and exiting + cluster-level coherency. + + To avoid wasteful unnecessary teardown operations, the outbound + should check the inbound cluster state for asynchronous + transitions to INBOUND_COMING_UP. Alternatively, individual + CPUs can be checked for entry into CPU_COMING_UP or CPU_UP. + + + Next states: + + CLUSTER_DOWN/INBOUND_NOT_COMING_UP (outbound) + Conditions: cluster torn down and ready to power off + Trigger events: (spontaneous) + + CLUSTER_GOING_DOWN/INBOUND_COMING_UP (inbound) + Conditions: none + Trigger events: + + a) an explicit hardware power-up operation, + resulting from a policy decision on another + CPU; + + b) a hardware event, such as an interrupt. + + +CLUSTER_GOING_DOWN/INBOUND_COMING_UP: + + The cluster is (or was) being torn down, but another CPU has + come online in the meantime and is trying to set up the cluster + again. + + If the outbound CPU observes this state, it has two choices: + + a) back out of teardown, restoring the cluster to the + CLUSTER_UP state; + + b) finish tearing the cluster down and put the cluster + in the CLUSTER_DOWN state; the inbound CPU will + set up the cluster again from there. + + Choice (a) permits the removal of some latency by avoiding + unnecessary teardown and setup operations in situations where + the cluster is not really going to be powered down. + + + Next states: + + CLUSTER_UP/INBOUND_COMING_UP (outbound) + Conditions: cluster-level setup and hardware + coherency complete + Trigger events: (spontaneous) + + CLUSTER_DOWN/INBOUND_COMING_UP (outbound) + Conditions: cluster torn down and ready to power off + Trigger events: (spontaneous) + + +Last man and First man selection +-------------------------------- + +The CPU which performs cluster tear-down operations on the outbound side +is commonly referred to as the "last man". + +The CPU which performs cluster setup on the inbound side is commonly +referred to as the "first man". + +The race avoidance algorithm documented above does not provide a +mechanism to choose which CPUs should play these roles. + + +Last man: + +When shutting down the cluster, all the CPUs involved are initially +executing Linux and hence coherent. Therefore, ordinary spinlocks can +be used to select a last man safely, before the CPUs become +non-coherent. + + +First man: + +Because CPUs may power up asynchronously in response to external wake-up +events, a dynamic mechanism is needed to make sure that only one CPU +attempts to play the first man role and do the cluster-level +initialisation: any other CPUs must wait for this to complete before +proceeding. + +Cluster-level initialisation may involve actions such as configuring +coherency controls in the bus fabric. + +The current implementation in bL_head.S uses a separate mutual exclusion +mechanism to do this arbitration. This mechanism is documented in +detail in vlocks.txt. + + +Features and Limitations +------------------------ + +Implementation: + + The current ARM-based implementation is split between + arch/arm/common/bL_head.S (low-level inbound CPU operations) and + arch/arm/common/bL_entry.c (everything else): + + __bL_cpu_going_down() signals the transition of a CPU to the + CPU_GOING_DOWN state. + + __bL_cpu_down() signals the transition of a CPU to the CPU_DOWN + state. + + A CPU transitions to CPU_COMING_UP and then to CPU_UP via the + low-level power-up code in bL_head.S. This could + involve CPU-specific setup code, but in the current + implementation it does not. + + __bL_outbound_enter_critical() and __bL_outbound_leave_critical() + handle transitions from CLUSTER_UP to CLUSTER_GOING_DOWN + and from there to CLUSTER_DOWN or back to CLUSTER_UP (in + the case of an aborted cluster power-down). + + These functions are more complex than the __bL_cpu_*() + functions due to the extra inter-CPU coordination which + is needed for safe transitions at the cluster level. + + A cluster transitions from CLUSTER_DOWN back to CLUSTER_UP via + the low-level power-up code in bL_head.S. This + typically involves platform-specific setup code, + provided by the platform-specific power_up_setup + function registered via bL_cluster_sync_init. + +Deep topologies: + + As currently described and implemented, the algorithm does not + support CPU topologies involving more than two levels (i.e., + clusters of clusters are not supported). The algorithm could be + extended by replicating the cluster-level states for the
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt line 495
Quoted 10 lines
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt new file +10
-0+ additional topological levels, and modifying the transition + rules for the intermediate (non-outermost) cluster levels. + + +Colophon +-------- + +Originally created and documented by Dave Martin for Linaro Limited, in +collaboration with Nicolas Pitre and Achin Gupta. +
Great write-up Dave!! I might have to do couple of more passes on it to get overall idea, but surely this documentation is good start for anybody reading/reviewing the big.LITTLE switcher code.
Quoted 16 lines
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt new file +3
-0+Copyright (C) 2012 Linaro Limited +Distributed under the terms of Version 2 of the GNU General Public +License, as defined in linux/COPYING.arch/arm/common/bL_entry.c +5
-0@@ -116,3 +116,163 @@ int bL_cpu_powered_up(void) platform_ops->powered_up ();return 0 ; } + +struct bL_sync_struct bL_sync; + +static void __sync_range (volatile void *p,size_t size) +{
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 133
Quoted 10 lines
arch/arm/common/bL_entry.c +10
-0+char *_p = (char *)p; + +__cpuc_flush_dcache_area (_p, size); +outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync (); +} + +#define sync_mem (ptr) __sync_range(ptr, sizeof *(ptr)) + +/*
/** as per kerneldoc.
Quoted 10 lines
arch/arm/common/bL_entry.c +10
-0+* __bL_cpu_going_down: Indicates that the cpu is being torn down. +* This must be called at the point of committing to teardown of a CPU. +* The CPU cache (SCTRL.C bit) is expected to still be active. +*/ +void __bL_cpu_going_down (unsigned int cpu,unsigned int cluster) +{ + bL_sync.clusters[cluster].cpus[cpu].cpu =CPU_GOING_DOWN ; +sync_mem (&bL_sync.clusters[cluster].cpus[cpu].cpu); +} +
[..]
Quoted 39 lines
arch/arm/common/bL_head.S +13
-2@@ -7,11 +7,19 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * + * Refer to Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt + * for details of the synchronisation algorithms used here. */ #include <linux/linkage.h> #include <asm/bL_entry.h> +.if BL_SYNC_CLUSTER_CPUS +.error "cpus must be the first member of struct bL_cluster_sync_struct" +.endif + .macro pr_dbg cpu, string #if defined(CONFIG_DEBUG_LL) && defined(DEBUG) b 1901f@@ -52,12 +60,82 @@ ENTRY(bL_entry_point) 2: pr_dbg r4, "kernel bL_entry_point\n" /* - * MMU is off so we need to get to bL_entry_vectors in a + * MMU is off so we need to get to various variables in a * position independent way. */ adr r5, 3f - ldr r6, [r5] + ldmia r5, {r6, r7, r8} add r6, r5, r6 @ r6 = bL_entry_vectors + ldr r7, [r5, r7] @ r7 = bL_power_up_setup_phys + add r8, r5, r8 @ r8 = bL_sync +
arch/arm/common/bL_head.S ENTRY(bL_entry_point) · line 81
Quoted 10 lines
arch/arm/common/bL_head.S +10
-0+ mov r0, #BL_SYNC_CLUSTER_SIZE + mla r8, r0, r10, r8 @ r8 = bL_sync cluster base + + @ Signal that this CPU is coming UP: + mov r0, #CPU_COMING_UP + mov r5, #BL_SYNC_CPU_SIZE + mla r5, r9, r5, r8 @ r5 = bL_sync cpu address + strb r0, [r5] + + dsb
Do you really need above dsb(). With MMU off, the the store should any way make it to the main memory, No ?
Quoted 4 lines
arch/arm/common/bL_head.S +4
-0+ + @ At this point, the cluster cannot unexpectedly enter the GOING_DOWN + @ state, because there is at least one active CPU (this CPU). +
arch/arm/common/bL_head.S ENTRY(bL_entry_point) · line 95
Quoted 10 lines
arch/arm/common/bL_head.S +10
-0+ @ Check if the cluster has been set up yet: + ldrb r0, [r8, #BL_SYNC_CLUSTER_CLUSTER] + cmp r0, #CLUSTER_UP + beq cluster_already_up + + @ Signal that the cluster is being brought up: + mov r0, #INBOUND_COMING_UP + strb r0, [r8, #BL_SYNC_CLUSTER_INBOUND] + + dsb
Same comment.
Regards,
Santosh
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
On Fri, Jan 11, 2013 at 11:16:18PM +0530, Santosh Shilimkar wrote:
[...]
Quoted 6 lines
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt line 495
Quoted 3 lines
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt new file +3
-0+Originally created and documented by Dave Martin for Linaro Limited, in +collaboration with Nicolas Pitre and Achin Gupta. +Great write-up Dave!! I might have to do couple of more passes on it to get overall idea, but surely this documentation is good start for anybody reading/reviewing the big.LITTLE switcher code.
Thanks for reading through it. Partly, this was insurance against me forgetting how the code worked in between writing and posting it... but this is all quite subtle code, so it felt important to document it thoroughly.
Quoted 20 lines
Quoted 20 lines
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt new file +3
-0+Copyright (C) 2012 Linaro Limited +Distributed under the terms of Version 2 of the GNU General Public +License, as defined in linux/COPYING.arch/arm/common/bL_entry.c +8
-0@@ -116,3 +116,163 @@ int bL_cpu_powered_up(void) platform_ops->powered_up ();return 0 ; } + +struct bL_sync_struct bL_sync; + +static void __sync_range (volatile void *p,size_t size) +{ +char *_p = (char *)p; + +__cpuc_flush_dcache_area (_p, size);
Quoted 8 lines
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 133
Quoted 7 lines
arch/arm/common/bL_entry.c +7
-0+outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync (); +} + +#define sync_mem (ptr) __sync_range(ptr, sizeof *(ptr)) + +/* /** as per kerneldoc.
Does kerneldoc not require the comment to be specially formatted?
I haven't played with that, so far.
Quoted 56 lines
Quoted 11 lines
arch/arm/common/bL_entry.c +10
-0+* __bL_cpu_going_down: Indicates that the cpu is being torn down. +* This must be called at the point of committing to teardown of a CPU. +* The CPU cache (SCTRL.C bit) is expected to still be active. +*/ +void __bL_cpu_going_down (unsigned int cpu,unsigned int cluster) +{ + bL_sync.clusters[cluster].cpus[cpu].cpu =CPU_GOING_DOWN ; +sync_mem (&bL_sync.clusters[cluster].cpus[cpu].cpu); +} +[..]
Quoted 42 lines
arch/arm/common/bL_head.S +16
-2@@ -7,11 +7,19 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * + * Refer to Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt + * for details of the synchronisation algorithms used here. */ #include <linux/linkage.h> #include <asm/bL_entry.h> +.if BL_SYNC_CLUSTER_CPUS +.error "cpus must be the first member of struct bL_cluster_sync_struct" +.endif + .macro pr_dbg cpu, string #if defined(CONFIG_DEBUG_LL) && defined(DEBUG) b 1901f@@ -52,12 +60,82 @@ ENTRY(bL_entry_point) 2: pr_dbg r4, "kernel bL_entry_point\n" /* - * MMU is off so we need to get to bL_entry_vectors in a + * MMU is off so we need to get to various variables in a * position independent way. */ adr r5, 3f - ldr r6, [r5] + ldmia r5, {r6, r7, r8} add r6, r5, r6 @ r6 = bL_entry_vectors + ldr r7, [r5, r7] @ r7 = bL_power_up_setup_phys + add r8, r5, r8 @ r8 = bL_sync + + mov r0, #BL_SYNC_CLUSTER_SIZE + mla r8, r0, r10, r8 @ r8 = bL_sync cluster base +
Quoted 8 lines
arch/arm/common/bL_head.S ENTRY(bL_entry_point) · line 81
Quoted 7 lines
arch/arm/common/bL_head.S +7
-0+ @ Signal that this CPU is coming UP: + mov r0, #CPU_COMING_UP + mov r5, #BL_SYNC_CPU_SIZE + mla r5, r9, r5, r8 @ r5 = bL_sync cpu address + strb r0, [r5] + + dsbDo you really need above
dsb(). With MMU off, the the store should
The short answer is "maybe not". Some of the barriers can be eliminated; some can be demoted to DSBs. Others may be required but unnecessarily duplicated e.g., between bL_head.S and vlock.S.
Quoted 1 line
any way make it to the main memory, No ?
Yes, but this raises issues about precisely what the architecture guarantees about memory ordering in these scenarios. The only obvious thing about that is that it's non-obvious.
Strongly-Ordered memory is not quite the same as having explicit barriers everywhere.
I need to have a careful think, but it should be possible to optimise a bit here.
Quoted 16 lines
Quoted 5 lines
arch/arm/common/bL_head.S +4
-0+ + @ At this point, the cluster cannot unexpectedly enter the GOING_DOWN + @ state, because there is at least one active CPU (this CPU). +arch/arm/common/bL_head.S ENTRY(bL_entry_point) · line 95
Quoted 10 lines
arch/arm/common/bL_head.S +10
-0+ @ Check if the cluster has been set up yet: + ldrb r0, [r8, #BL_SYNC_CLUSTER_CLUSTER] + cmp r0, #CLUSTER_UP + beq cluster_already_up + + @ Signal that the cluster is being brought up: + mov r0, #INBOUND_COMING_UP + strb r0, [r8, #BL_SYNC_CLUSTER_INBOUND] + + dsbSame comment.
Same answer... for now
Cheers
---Dave
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 118 lines · Dave Martin
On Fri, Jan 11, 2013 at 11:16:18PM +0530, Santosh Shilimkar wrote:
[...]
Quoted 6 lines
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt line 495
Quoted 3 lines
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt new file +3
-0+Originally created and documented by Dave Martin for Linaro Limited, in +collaboration with Nicolas Pitre and Achin Gupta. +Great write-up Dave!! I might have to do couple of more passes on it to get overall idea, but surely this documentation is good start for anybody reading/reviewing the big.LITTLE switcher code.
Thanks for reading through it. Partly, this was insurance against me forgetting how the code worked in between writing and posting it... but this is all quite subtle code, so it felt important to document it thoroughly.
Quoted 20 lines
Quoted 20 lines
Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt new file +3
-0+Copyright (C) 2012 Linaro Limited +Distributed under the terms of Version 2 of the GNU General Public +License, as defined in linux/COPYING.arch/arm/common/bL_entry.c +8
-0@@ -116,3 +116,163 @@ int bL_cpu_powered_up(void) platform_ops->powered_up ();return 0 ; } + +struct bL_sync_struct bL_sync; + +static void __sync_range (volatile void *p,size_t size) +{ +char *_p = (char *)p; + +__cpuc_flush_dcache_area (_p, size);Quoted 8 lines
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 133
Quoted 7 lines
arch/arm/common/bL_entry.c +7
-0+outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync (); +} + +#define sync_mem (ptr) __sync_range(ptr, sizeof *(ptr)) + +/* /** as per kerneldoc.
Does kerneldoc not require the comment to be specially formatted?
I haven't played with that, so far.
Quoted 56 lines
Quoted 11 lines
arch/arm/common/bL_entry.c +10
-0+* __bL_cpu_going_down: Indicates that the cpu is being torn down. +* This must be called at the point of committing to teardown of a CPU. +* The CPU cache (SCTRL.C bit) is expected to still be active. +*/ +void __bL_cpu_going_down (unsigned int cpu,unsigned int cluster) +{ + bL_sync.clusters[cluster].cpus[cpu].cpu =CPU_GOING_DOWN ; +sync_mem (&bL_sync.clusters[cluster].cpus[cpu].cpu); +} +[..]
Quoted 42 lines
arch/arm/common/bL_head.S +16
-2@@ -7,11 +7,19 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * + * Refer to Documentation/arm/big.LITTLE/cluster-pm-race-avoidance.txt + * for details of the synchronisation algorithms used here. */ #include <linux/linkage.h> #include <asm/bL_entry.h> +.if BL_SYNC_CLUSTER_CPUS +.error "cpus must be the first member of struct bL_cluster_sync_struct" +.endif + .macro pr_dbg cpu, string #if defined(CONFIG_DEBUG_LL) && defined(DEBUG) b 1901f@@ -52,12 +60,82 @@ ENTRY(bL_entry_point) 2: pr_dbg r4, "kernel bL_entry_point\n" /* - * MMU is off so we need to get to bL_entry_vectors in a + * MMU is off so we need to get to various variables in a * position independent way. */ adr r5, 3f - ldr r6, [r5] + ldmia r5, {r6, r7, r8} add r6, r5, r6 @ r6 = bL_entry_vectors + ldr r7, [r5, r7] @ r7 = bL_power_up_setup_phys + add r8, r5, r8 @ r8 = bL_sync + + mov r0, #BL_SYNC_CLUSTER_SIZE + mla r8, r0, r10, r8 @ r8 = bL_sync cluster base +Quoted 8 lines
arch/arm/common/bL_head.S ENTRY(bL_entry_point) · line 81
Quoted 7 lines
arch/arm/common/bL_head.S +7
-0+ @ Signal that this CPU is coming UP: + mov r0, #CPU_COMING_UP + mov r5, #BL_SYNC_CPU_SIZE + mla r5, r9, r5, r8 @ r5 = bL_sync cpu address + strb r0, [r5] + + dsbDo you really need above
dsb(). With MMU off, the the store shouldThe short answer is "maybe not". Some of the barriers can be eliminated; some can be demoted to DSBs. Others may be required but unnecessarily duplicated e.g., between
bL_head.Sandvlock.S.
Quoted 6 lines
Quoted 1 line
any way make it to the main memory, No ?
Yes, but this raises issues about precisely what the architecture guarantees about memory ordering in these scenarios. The only obvious thing about that is that it's non-obvious.
Well at least ARM documents clearly says the memory accesses will be treated as strongly ordered with MMU OFF and that means they expect to make it to main memory.
Quoted 6 lines
Strongly-Ordered memory is not quite the same as having explicit barriers everywhere.
I need to have a careful think, but it should be possible to optimise a bit here.
If the CCI comes in between that rule and if it needs a barrier to let it flush is WB to main memory then thats a different story.
Anyway thanks for the answer.
Regards
Santosh
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
In reply to Nicolas PitreQuoted 6 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
This provides helper methods to coordinate between CPUs coming down and CPUs going up, as well as documentation on the used algorithms, so that cluster teardown and setup
operations are not done for a cluster simultaneously.
[...]
In response to the incorrectness of the outer cache handling, here's a supplementary patch:
Quoted 1 line
From
b64f305c90e7ea585992df2d710f62ec6a7b5395Mon Sep 17 00:00:00 2001
From: Dave Martin <[email protected]>
Date: Mon, 14 Jan 2013 16:25:47 +0000
Subject: [PATCH] ARM: b.L: Fix outer cache handling for coherency setup/exit helpers
This patch addresses the following issues:
* When invalidating stale data from the cache before a read, outer caches must be invalidated _before_ inner caches, not after, otherwise stale data may be re-filled from outer to inner after the inner cache is flushed.
We still retain an inner clean before touching the outer cache, to avoid stale data being rewritten from there into the outer cache after the outer cache is flushed.
* All the sync_mem() calls synchronise either reads or writes, but not both. This patch splits sync_mem() into separate functions for reads and writes, to avoid excessive inner flushes in the write case.
The two functions are different from the original sync_mem(), to fix the above issues.
---
NOTE: This patch is build-tested only.
arch/arm/common/bL_entry.c | 57 ++++++++++++++++++++++++++++++++++---------- 1 files changed, 44 insertions(+), 13 deletions(-)
arch/arm/common/bL_entry.c +44 -13
@@ -119,16 +119,47 @@ int bL_cpu_powered_up(void)struct bL_sync_struct bL_sync; -static void __sync_range (volatile void *p,size_t size) +/* +* Ensure preceding writes to *p by this CPU are visible to +* subsequent reads by other CPUs: +*/ +static void __sync_range_w (volatile void *p,size_t size) {char *_p = (char *)p;__cpuc_flush_dcache_area (_p, size); -outer_flush_range (__pa (_p),__pa (_p + size)); +outer_clean_range (__pa (_p),__pa (_p + size));outer_sync (); } -#define sync_mem (ptr) __sync_range(ptr, sizeof *(ptr)) +/* +* Ensure preceding writes to *p by other CPUs are visible to +* subsequent reads by this CPU: +*/ +static void __sync_range_r (volatile void *p,size_t size) +{ +char *_p = (char *)p; + +#ifdef CONFIG_OUTER_CACHE +if (outer_cache.flush_range) { +/* +* Ensure ditry data migrated from other CPUs into our cache +* are cleaned out safely before the outer cache is cleaned: +*/ +__cpuc_flush_dcache_area (_p, size); + +/* Clean and invalidate stale data for *p from outer ... */ +outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync (); + } +#endif + +/* ... and inner cache: */ +__cpuc_flush_dcache_area (_p, size); +} + +#define sync_w (ptr) __sync_range_w(ptr, sizeof *(ptr)) +#define sync_r (ptr) __sync_range_r(ptr, sizeof *(ptr))/* * __bL_cpu_going_down: Indicates that the cpu is being torn down.
@@ -138,7 +169,7 @@ static void __sync_range(volatile void *p, size_t size)void __bL_cpu_going_down (unsigned int cpu,unsigned int cluster) { bL_sync.clusters[cluster].cpus[cpu].cpu =CPU_GOING_DOWN ; -sync_mem (&bL_sync.clusters[cluster].cpus[cpu].cpu); +sync_w (&bL_sync.clusters[cluster].cpus[cpu].cpu); }/*
@@ -151,7 +182,7 @@ void __bL_cpu_down(unsigned int cpu, unsigned int cluster) {dsb (); bL_sync.clusters[cluster].cpus[cpu].cpu =CPU_DOWN ; -sync_mem (&bL_sync.clusters[cluster].cpus[cpu].cpu); +sync_w (&bL_sync.clusters[cluster].cpus[cpu].cpu);sev (); }
@@ -167,7 +198,7 @@ void __bL_outbound_leave_critical(unsigned int cluster, int state) {dsb (); bL_sync.clusters[cluster].cluster = state; -sync_mem (&bL_sync.clusters[cluster].cluster); +sync_w (&bL_sync.clusters[cluster].cluster);sev (); }
@@ -189,10 +220,10 @@ bool __bL_outbound_enter_critical(unsigned int cpu, unsigned int cluster)/* Warn inbound CPUs that the cluster is being torn down: */ c->cluster =CLUSTER_GOING_DOWN ; -sync_mem (&c->cluster); +sync_w (&c->cluster);/* Back out if the inbound cluster is already in the critical region: */ -sync_mem (&c->inbound); +sync_r (&c->inbound);if (c->inbound ==INBOUND_COMING_UP ) goto abort;
@@ -203,7 +234,7 @@ bool __bL_outbound_enter_critical(unsigned int cpu, unsigned int cluster)* If any CPU has been woken up again from the DOWN state, then we * shouldn't be taking the cluster down at all: abort in that case. */ -sync_mem (&c->cpus); +sync_r (&c->cpus);for (i =0 ; i <BL_CPUS_PER_CLUSTER ; i++) {int cpustate;
@@ -216,7 +247,7 @@ bool __bL_outbound_enter_critical(unsigned int cpu, unsigned int cluster)break ;wfe (); -sync_mem (&c->cpus[i].cpu); +sync_r (&c->cpus[i].cpu); }switch (cpustate) {
@@ -239,7 +270,7 @@ abort:int __bL_cluster_state (unsigned int cluster) { -sync_mem (&bL_sync.clusters[cluster].cluster); +sync_r (&bL_sync.clusters[cluster].cluster);return bL_sync.clusters[cluster].cluster; }
@@ -267,11 +298,11 @@ int __init bL_cluster_sync_init(void (*power_up_setup)(void))for_each_online_cpu (i) bL_sync.clusters[this_cluster].cpus[i].cpu =CPU_UP ; bL_sync.clusters[this_cluster].cluster =CLUSTER_UP ; -sync_mem (&bL_sync); +sync_w (&bL_sync);if (power_up_setup) { bL_power_up_setup_phys =virt_to_phys (power_up_setup); -sync_mem (&bL_power_up_setup_phys); +sync_w (&bL_power_up_setup_phys); }return 0 ;
Signature · 1 line
1.7.4.1
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 42 lines · Dave Martin
From
b64f305c90e7ea585992df2d710f62ec6a7b5395Mon Sep 17 00:00:00 2001
From: Dave Martin <[email protected]>
Date: Mon, 14 Jan 2013 16:25:47 +0000
Subject: [PATCH] ARM: b.L: Fix outer cache handling for coherency setup/exit helpersThis patch addresses the following issues:
* When invalidating stale data from the cache before a read, outer caches must be invalidated _before_ inner caches, not after, otherwise stale data may be re-filled from outer to inner after the inner cache is flushed.
We still retain an inner clean before touching the outer cache, to avoid stale data being rewritten from there into the outer cache after the outer cache is flushed.
* All the
sync_mem()calls synchronise either reads or writes, but not both. This patch splitssync_mem()into separate functions for reads and writes, to avoid excessive inner flushes in the write case.The two functions are different from the original
sync_mem(), to fix the above issues.Signed-off-by Dave Martin <[email protected]>---
NOTE: This patch is build-tested only.arch/arm/common/bL_entry.c | 57 ++++++++++++++++++++++++++++++++++---------- 1 files changed, 44 insertions(+), 13 deletions(-)arch/arm/common/bL_entry.c +2
-1@@ -119,16 +119,47 @@ int bL_cpu_powered_up(void)struct bL_sync_struct bL_sync; -static void __sync_range (volatile void *p,size_t size) +/* +* Ensure preceding writes to *p by this CPU are visible to
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 132
Quoted 10 lines
arch/arm/common/bL_entry.c +4
-1+* subsequent reads by other CPUs: +*/ +static void __sync_range_w (volatile void *p,size_t size) {char *_p = (char *)p;__cpuc_flush_dcache_area (_p, size); -outer_flush_range (__pa (_p),__pa (_p + size)); +outer_clean_range (__pa (_p),__pa (_p + size));outer_sync ();
It's not part of your patch but I thought about commenting here. The outer_clean_range() already has a cache_sync() operation, so no need for the additional outer_sync().
Quoted 12 lines
arch/arm/common/bL_entry.c +9
-1} -#define sync_mem (ptr) __sync_range(ptr, sizeof *(ptr)) +/* +* Ensure preceding writes to *p by other CPUs are visible to +* subsequent reads by this CPU: +*/ +static void __sync_range_r (volatile void *p,size_t size) +{ +char *_p = (char *)p; + +#ifdef CONFIG_OUTER_CACHE
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 153
Quoted 10 lines
arch/arm/common/bL_entry.c +10
-0+if (outer_cache.flush_range) { +/* +* Ensure ditry data migrated from other CPUs into our cache +* are cleaned out safely before the outer cache is cleaned: +*/ +__cpuc_flush_dcache_area (_p, size); + +/* Clean and invalidate stale data for *p from outer ... */ +outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync ();
Same here.
Signature · 1 line
Catalin
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 74 lines · Catalin Marinas
Quoted 42 lines · Dave Martin
From
b64f305c90e7ea585992df2d710f62ec6a7b5395Mon Sep 17 00:00:00 2001
From: Dave Martin <[email protected]>
Date: Mon, 14 Jan 2013 16:25:47 +0000
Subject: [PATCH] ARM: b.L: Fix outer cache handling for coherency setup/exit helpersThis patch addresses the following issues:
* When invalidating stale data from the cache before a read, outer caches must be invalidated _before_ inner caches, not after, otherwise stale data may be re-filled from outer to inner after the inner cache is flushed.
We still retain an inner clean before touching the outer cache, to avoid stale data being rewritten from there into the outer cache after the outer cache is flushed.
* All the
sync_mem()calls synchronise either reads or writes, but not both. This patch splitssync_mem()into separate functions for reads and writes, to avoid excessive inner flushes in the write case.The two functions are different from the original
sync_mem(), to fix the above issues.Signed-off-by Dave Martin <[email protected]>---
NOTE: This patch is build-tested only.arch/arm/common/bL_entry.c | 57 ++++++++++++++++++++++++++++++++++---------- 1 files changed, 44 insertions(+), 13 deletions(-)arch/arm/common/bL_entry.c +2
-1@@ -119,16 +119,47 @@ int bL_cpu_powered_up(void)struct bL_sync_struct bL_sync; -static void __sync_range (volatile void *p,size_t size) +/* +* Ensure preceding writes to *p by this CPU are visible to arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 132
Quoted 10 lines
arch/arm/common/bL_entry.c +4
-1+* subsequent reads by other CPUs: +*/ +static void __sync_range_w (volatile void *p,size_t size) {char *_p = (char *)p;__cpuc_flush_dcache_area (_p, size); -outer_flush_range (__pa (_p),__pa (_p + size)); +outer_clean_range (__pa (_p),__pa (_p + size));outer_sync ();It's not part of your patch but I thought about commenting here. The
outer_clean_range()already has acache_sync()operation, so no need for the additionalouter_sync().Quoted 16 lines
arch/arm/common/bL_entry.c +13
-1} -#define sync_mem (ptr) __sync_range(ptr, sizeof *(ptr)) +/* +* Ensure preceding writes to *p by other CPUs are visible to +* subsequent reads by this CPU: +*/ +static void __sync_range_r (volatile void *p,size_t size) +{ +char *_p = (char *)p; + +#ifdef CONFIG_OUTER_CACHE +if (outer_cache.flush_range) { +/* +* Ensure ditry data migrated from other CPUs into our cache +* are cleaned out safely before the outer cache is cleaned:
Quoted 8 lines
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 153
Quoted 6 lines
arch/arm/common/bL_entry.c +6
-0+*/ +__cpuc_flush_dcache_area (_p, size); + +/* Clean and invalidate stale data for *p from outer ... */ +outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync ();Same here.
Ah, right. I've seen code do this in various places, and just copy- pasted it under the assumption that it is needed. Our discussion abouto ensuring that outer_sync() really does guarantee completion of its effects on return still applies.
Are there any situations when outer_sync() should be called explicitly?
Cheers
---Dave
PATCH 3/16 ARM: b.L: introduce helpers for platform coherency exit/setup
Quoted 84 lines · Dave Martin
Quoted 74 lines · Catalin Marinas
Quoted 42 lines · Dave Martin
From
b64f305c90e7ea585992df2d710f62ec6a7b5395Mon Sep 17 00:00:00 2001
From: Dave Martin <[email protected]>
Date: Mon, 14 Jan 2013 16:25:47 +0000
Subject: [PATCH] ARM: b.L: Fix outer cache handling for coherency setup/exit helpersThis patch addresses the following issues:
* When invalidating stale data from the cache before a read, outer caches must be invalidated _before_ inner caches, not after, otherwise stale data may be re-filled from outer to inner after the inner cache is flushed.
We still retain an inner clean before touching the outer cache, to avoid stale data being rewritten from there into the outer cache after the outer cache is flushed.
* All the
sync_mem()calls synchronise either reads or writes, but not both. This patch splitssync_mem()into separate functions for reads and writes, to avoid excessive inner flushes in the write case.The two functions are different from the original
sync_mem(), to fix the above issues.Signed-off-by Dave Martin <[email protected]>---
NOTE: This patch is build-tested only.arch/arm/common/bL_entry.c | 57 ++++++++++++++++++++++++++++++++++---------- 1 files changed, 44 insertions(+), 13 deletions(-)arch/arm/common/bL_entry.c +2
-1@@ -119,16 +119,47 @@ int bL_cpu_powered_up(void)struct bL_sync_struct bL_sync; -static void __sync_range (volatile void *p,size_t size) +/* +* Ensure preceding writes to *p by this CPU are visible to arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 132
Quoted 10 lines
arch/arm/common/bL_entry.c +4
-1+* subsequent reads by other CPUs: +*/ +static void __sync_range_w (volatile void *p,size_t size) {char *_p = (char *)p;__cpuc_flush_dcache_area (_p, size); -outer_flush_range (__pa (_p),__pa (_p + size)); +outer_clean_range (__pa (_p),__pa (_p + size));outer_sync ();It's not part of your patch but I thought about commenting here. The
outer_clean_range()already has acache_sync()operation, so no need for the additionalouter_sync().Quoted 16 lines
arch/arm/common/bL_entry.c +13
-1} -#define sync_mem (ptr) __sync_range(ptr, sizeof *(ptr)) +/* +* Ensure preceding writes to *p by other CPUs are visible to +* subsequent reads by this CPU: +*/ +static void __sync_range_r (volatile void *p,size_t size) +{ +char *_p = (char *)p; + +#ifdef CONFIG_OUTER_CACHE +if (outer_cache.flush_range) { +/* +* Ensure ditry data migrated from other CPUs into our cache +* are cleaned out safely before the outer cache is cleaned: Quoted 8 lines
arch/arm/common/bL_entry.c int bL_cpu_powered_up(void) · line 153
Quoted 6 lines
arch/arm/common/bL_entry.c +6
-0+*/ +__cpuc_flush_dcache_area (_p, size); + +/* Clean and invalidate stale data for *p from outer ... */ +outer_flush_range (__pa (_p),__pa (_p + size)); +outer_sync ();Same here.
Quoted 6 lines
Ah, right. I've seen code do this in various places, and just copy- pasted it under the assumption that it is needed. Our discussion abouto ensuring that
outer_sync()really does guarantee completion of its effects on return still applies.Are there any situations when
outer_sync()should be called explicitly?
outer_sync() on its own ensures the draining of the PL310 write buffer. DSB drains the CPU write buffers but PL310 doesn't detect it, so a separate outer_sync() is needed. In general this is required when you write a Normal Non-cacheable buffer (but bufferable, e.g. DMA coherent) and you want to ensure data visibility (DSB+outer_sync(), that's what the mb() macro does).
Signature · 1 line
Catalin
PATCH 4/16 ARM: b.L: Add baremetal voting mutexes
In reply to Nicolas PitreFrom: Dave Martin <[email protected]>
This patch adds a simple low-level voting mutex implementation to be used to arbitrate during first man selection when no load/store exclusive instructions are usable.
For want of a better name, these are called "vlocks". (I was tempted to call them ballot locks, but "block" is way too confusing an abbreviation...)
There is no function to wait for the lock to be released, and no vlock_lock() function since we don't need these at the moment. These could straightforwardly be added if vlocks get used for other purposes.
--- Documentation/arm/big.LITTLE/vlocks.txt | 211 ++++++++++++++++++++++++++++++++ arch/arm/common/vlock.S | 108 ++++++++++++++++ arch/arm/common/vlock.h | 43 +++++++ 3 files changed, 362 insertions(+) create mode 100644 Documentation/arm/big.LITTLE/vlocks.txt create mode 100644 arch/arm/common/vlock.S create mode 100644 arch/arm/common/vlock.h
Documentation/arm/big.LITTLE/vlocks.txt new file +211 -0
@@ -0,0 +1,211 @@ +vlocks for Bare-Metal Mutual Exclusion +====================================== + +Voting Locks, or "vlocks" provide a simple low-level mutual exclusion +mechanism, with reasonable but minimal requirements on the memory +system. + +These are intended to be used to coordinate critical activity among CPUs +which are otherwise non-coherent, in situations where the hardware +provides no other mechanism to support this and ordinary spinlocks +cannot be used. + + +vlocks make use of the atomicity provided by the memory system for +writes to a single memory location. To arbitrate, every CPU "votes for +itself", by storing a unique number to a common memory location. The +final value seen in that memory location when all the votes have been +cast identifies the winner. + +In order to make sure that the election produces an unambiguous result +in finite time, a CPU will only enter the election in the first place if +no winner has been chosen and the election does not appear to have +started yet. + + +Algorithm +--------- + +The easiest way to explain the vlocks algorithm is with some pseudo-code: + + + int currently_voting[NR_CPUS] = { 0, }; + int last_vote = -1; /* no votes yet */ + + bool vlock_trylock(int this_cpu) + { + /* signal our desire to vote */ + currently_voting[this_cpu] = 1; + if (last_vote != -1) { + /* someone already volunteered himself */ + currently_voting[this_cpu] = 0; + return false; /* not ourself */ + } + + /* let's suggest ourself */ + last_vote = this_cpu; + currently_voting[this_cpu] = 0; + + /* then wait until everyone else is done voting */ + for_each_cpu(i) { + while (currently_voting[i] != 0) + /* wait */; + } + + /* result */ + if (last_vote == this_cpu) + return true; /* we won */ + return false; + } + + bool vlock_unlock(void) + { + last_vote = -1; + } + + +The currently_voting[] array provides a way for the CPUs to determine +whether an election is in progress, and plays a role analogous to the +"entering" array in Lamport's bakery algorithm [1]. + +However, once the election has started, the underlying memory system +atomicity is used to pick the winner. This avoids the need for a static +priority rule to act as a tie-breaker, or any counters which could +overflow. + +As long as the last_vote variable is globally visible to all CPUs, it +will contain only one value that won't change once every CPU has cleared +its currently_voting flag. + + +Features and limitations +------------------------ + + * vlocks are not intended to be fair. In the contended case, it is the + _last_ CPU which attempts to get the lock which will be most likely + to win. + + vlocks are therefore best suited to situations where it is necessary + to pick a unique winner, but it does not matter which CPU actually + wins. + + * Like other similar mechanisms, vlocks will not scale well to a large + number of CPUs. + + vlocks can be cascaded in a voting hierarchy to permit better scaling + if necessary, as in the following hypothetical example for 4096 CPUs: + + /* first level: local election */ + my_town = towns[(this_cpu >> 4) & 0xf]; + I_won = vlock_trylock(my_town, this_cpu & 0xf); + if (I_won) { + /* we won the town election, let's go for the state */ + my_state = states[(this_cpu >> 8) & 0xf]; + I_won = vlock_lock(my_state, this_cpu & 0xf)); + if (I_won) { + /* and so on */ + I_won = vlock_lock(the_whole_country, this_cpu & 0xf]; + if (I_won) { + /* ... */ + } + vlock_unlock(the_whole_country); + } + vlock_unlock(my_state); + } + vlock_unlock(my_town); + + +ARM implementation +------------------ + +The current ARM implementation [2] contains a some optimisations beyond +the basic algorithm: + + * By packing the members of the currently_voting array close together, + we can read the whole array in one transaction (providing the number + of CPUs potentially contending the lock is small enough). This + reduces the number of round-trips required to external memory. + + In the ARM implementation, this means that we can use a single load + and comparison: + + LDR Rt, [Rn] + CMP Rt, #0 + + ...in place of code equivalent to: + + LDRB Rt, [Rn] + CMP Rt, #0 + LDRBEQ Rt, [Rn, #1] + CMPEQ Rt, #0 + LDRBEQ Rt, [Rn, #2] + CMPEQ Rt, #0 + LDRBEQ Rt, [Rn, #3] + CMPEQ Rt, #0 + + This cuts down on the fast-path latency, as well as potentially + reducing bus contention in contended cases. + + The optimisation relies on the fact that the ARM memory system + guarantees coherency between overlapping memory accesses of + different sizes, similarly to many other architectures. Note that + we do not care which element of currently_voting appears in which + bits of Rt, so there is no need to worry about endianness in this + optimisation. + + If there are too many CPUs to read the currently_voting array in + one transaction then multiple transations are still required. The + implementation uses a simple loop of word-sized loads for this + case. The number of transactions is still fewer than would be + required if bytes were loaded individually. + + + In principle, we could aggregate further by using LDRD or LDM, but + to keep the code simple this was not attempted in the initial + implementation. + + + * vlocks are currently only used to coordinate between CPUs which are + unable to enable their caches yet. This means that the + implementation removes many of the barriers which would be required + when executing the algorithm in cached memory. + + packing of the currently_voting array does not work with cached + memory unless all CPUs contending the lock are cache-coherent, due + to cache writebacks from one CPU clobbering values written by other + CPUs. (Though if all the CPUs are cache-coherent, you should be + probably be using proper spinlocks instead anyway). + + + * The "no votes yet" value used for the last_vote variable is 0 (not + -1 as in the pseudocode). This allows statically-allocated vlocks + to be implicitly initialised to an unlocked state simply by putting + them in .bss. + + An offset is added to each CPU's ID for the purpose of setting this + variable, so that no CPU uses the value 0 for its ID. + + +Colophon +-------- + +Originally created and documented by Dave Martin for Linaro Limited, for +use in ARM-based big.LITTLE platforms, with review and input gratefully +received from Nicolas Pitre and Achin Gupta. Thanks to Nicolas for +grabbing most of this text out of the relevant mail thread and writing +up the pseudocode. + +Copyright (C) 2012 Linaro Limited +Distributed under the terms of Version 2 of the GNU General Public +License, as defined in linux/COPYING. + + +References +---------- + +[1] Lamport, L. "A New Solution of Dijkstra's Concurrent Programming + Problem", Communications of the ACM 17, 8 (August 1974), 453-455. + + http://en.wikipedia.org/wiki/Lamport%27s_bakery_algorithm + +[2] linux/arch/arm/common/vlock.S, www.kernel.org.
arch/arm/common/vlock.S new file +108 -0
108 lines not on this page · raw message
arch/arm/common/vlock.h new file +43 -0
43 lines not on this page · raw message
Signature · 1 line
1.8.0
PATCH 4/16 ARM: b.L: Add baremetal voting mutexes
Quoted 14 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
This patch adds a simple low-level voting mutex implementation to be used to arbitrate during first man selection when no load/store exclusive instructions are usable.
For want of a better name, these are called "vlocks". (I was tempted to call them ballot locks, but "block" is way too confusing an abbreviation...)
There is no function to wait for the lock to be released, and no
vlock_lock()function since we don't need these at the moment. These could straightforwardly be added if vlocks get used for other purposes.
[...]
Quoted 8 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +2
-0@@ -0,0 +1,211 @@ +vlocks for Bare-Metal Mutual Exclusion +======================================
[...]
Documentation/arm/big.LITTLE/vlocks.txt line 121
Quoted 4 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +4
-0+ARM implementation +------------------ + +The current ARM implementation [2] contains a some optimisations beyond
-a
Quoted 40 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +40
-0+the basic algorithm: + + * By packing the members of the currently_voting array close together, + we can read the whole array in one transaction (providing the number + of CPUs potentially contending the lock is small enough). This + reduces the number of round-trips required to external memory. + + In the ARM implementation, this means that we can use a single load + and comparison: + + LDR Rt, [Rn] + CMP Rt, #0 + + ...in place of code equivalent to: + + LDRB Rt, [Rn] + CMP Rt, #0 + LDRBEQ Rt, [Rn, #1] + CMPEQ Rt, #0 + LDRBEQ Rt, [Rn, #2] + CMPEQ Rt, #0 + LDRBEQ Rt, [Rn, #3] + CMPEQ Rt, #0 + + This cuts down on the fast-path latency, as well as potentially + reducing bus contention in contended cases. + + The optimisation relies on the fact that the ARM memory system + guarantees coherency between overlapping memory accesses of + different sizes, similarly to many other architectures. Note that + we do not care which element of currently_voting appears in which + bits of Rt, so there is no need to worry about endianness in this + optimisation. + + If there are too many CPUs to read the currently_voting array in + one transaction then multiple transations are still required. The + implementation uses a simple loop of word-sized loads for this + case. The number of transactions is still fewer than would be + required if bytes were loaded individually. +
Documentation/arm/big.LITTLE/vlocks.txt line 171
Quoted 10 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +10
-0+ + In principle, we could aggregate further by using LDRD or LDM, but + to keep the code simple this was not attempted in the initial + implementation. + + + * vlocks are currently only used to coordinate between CPUs which are + unable to enable their caches yet. This means that the + implementation removes many of the barriers which would be required + when executing the algorithm in cached memory.
I think you need to elaborate on this and clearly identify the requirements of the memory behaviour. In reality, these locks are hardly ever usable so we don't want them cropping up in driver code and the like!
Documentation/arm/big.LITTLE/vlocks.txt line 183
Quoted 12 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +12
-0+ + packing of the currently_voting array does not work with cached + memory unless all CPUs contending the lock are cache-coherent, due + to cache writebacks from one CPU clobbering values written by other + CPUs. (Though if all the CPUs are cache-coherent, you should be + probably be using proper spinlocks instead anyway). + + + * The "no votes yet" value used for the last_vote variable is 0 (not + -1 as in the pseudocode). This allows statically-allocated vlocks + to be implicitly initialised to an unlocked state simply by putting + them in .bss.
You could also put them in their own section and initialise them to -1 there.
Quoted 34 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +28
-0+ + An offset is added to each CPU's ID for the purpose of setting this + variable, so that no CPU uses the value 0 for its ID. + + +Colophon +-------- + +Originally created and documented by Dave Martin for Linaro Limited, for +use in ARM-based big.LITTLE platforms, with review and input gratefully +received from Nicolas Pitre and Achin Gupta. Thanks to Nicolas for +grabbing most of this text out of the relevant mail thread and writing +up the pseudocode. + +Copyright (C) 2012 Linaro Limited +Distributed under the terms of Version 2 of the GNU General Public +License, as defined in linux/COPYING. + + +References +---------- + +[1] Lamport, L. "A New Solution of Dijkstra's Concurrent Programming + Problem", Communications of the ACM 17, 8 (August 1974), 453-455. + + http://en.wikipedia.org/wiki/Lamport%27s_bakery_algorithm + +[2] linux/arch/arm/common/vlock.S, www.kernel.org.arch/arm/common/vlock.S new file +0
-0
arch/arm/common/vlock.S line 10
Quoted 10 lines
arch/arm/common/vlock.S new file +10
-0@@ -0,0 +1,108 @@ +/* + * vlock.S - simple voting lock implementation for ARM + * + * Created by: Dave Martin, 2012-08-16 + * Copyright: (C) 2012 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version.
Your documentation is strictly GPLv2, so there's a strange discrepancy here.
Quoted 9 lines
arch/arm/common/vlock.S new file +9
-0+ * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
arch/arm/common/vlock.S line 29
Quoted 10 lines
arch/arm/common/vlock.S new file +10
-0+ * + * + * This algorithm is described in more detail in + * Documentation/arm/big.LITTLE/vlocks.txt. + */ + +#include <linux/linkage.h> +#include "vlock.h" + +#if VLOCK_VOTING_SIZE > 4
4? Maybe a CONFIG option or a #define in an arch vlock.h?
Quoted 43 lines
arch/arm/common/vlock.S new file +43
-0+#define FEW(x...) +#define MANY(x...) x +#else +#define FEW(x...) x +#define MANY(x...) +#endif + +@ voting lock for first-man coordination + +.macro voting_begin rbase:req, rcpu:req, rscratch:req + mov \rscratch, #1 + strb \rscratch, [\rbase, \rcpu] + dsb +.endm + +.macro voting_end rbase:req, rcpu:req, rscratch:req + mov \rscratch, #0 + strb \rscratch, [\rbase, \rcpu] + dsb + sev +.endm + +/* + * The vlock structure must reside in Strongly-Ordered or Device memory. + * This implementation deliberately eliminates most of the barriers which + * would be required for other memory types, and assumes that independent + * writes to neighbouring locations within a cacheline do not interfere + * with one another. + */ + +@ r0: lock structure base +@ r1: CPU ID (0-based index within cluster) +ENTRY(vlock_trylock) + add r1, r1, #VLOCK_VOTING_OFFSET + + voting_begin r0, r1, r2 + + ldrb r2, [r0, #VLOCK_OWNER_OFFSET] @ check whether lock is held + cmp r2, #VLOCK_OWNER_NONE + bne trylock_fail @ fail if so + + strb r1, [r0, #VLOCK_OWNER_OFFSET] @ submit my vote +
arch/arm/common/vlock.S line 82
Quoted 10 lines
arch/arm/common/vlock.S new file +10
-0+ voting_end r0, r1, r2 + + @ Wait for the current round of voting to finish: + + MANY( mov r3, #VLOCK_VOTING_OFFSET ) +0: + MANY( ldr r2, [r0, r3] ) + FEW( ldr r2, [r0, #VLOCK_VOTING_OFFSET] ) + cmp r2, #0 + wfene
Is there a race here? I wonder if you can end up in a situation where everybody enters wfe and then there is nobody left to signal an event via voting_end (if, for example the last voter sent the sev when everybody else was simultaneously doing the cmp before the wfe)...
... actually, that's ok as long as VLOCK_VOTING_OFFSET isn't speculated, which it shouldn't be from strongly-ordered memory. Fair enough!
Quoted 50 lines
arch/arm/common/vlock.S new file +26
-0+ bne 0b + MANY( add r3, r3, #4 ) + MANY( cmp r3, #VLOCK_VOTING_OFFSET + VLOCK_VOTING_SIZE ) + MANY( bne 0b ) + + @ Check who won: + + ldrb r2, [r0, #VLOCK_OWNER_OFFSET] + eor r0, r1, r2 @ zero if I won, else nonzero + bx lr + +trylock_fail: + voting_end r0, r1, r2 + mov r0, #1 @ nonzero indicates that I lost + bx lr +ENDPROC(vlock_trylock) + +@ r0: lock structure base +ENTRY(vlock_unlock) + mov r1, #VLOCK_OWNER_NONE + dsb + strb r1, [r0, #VLOCK_OWNER_OFFSET] + dsb + sev + bx lr +ENDPROC(vlock_unlock)arch/arm/common/vlock.h new file +18
-0@@ -0,0 +1,43 @@ +/* +* vlock.h - simple voting lock implementation +* +* Created by: Dave Martin, 2012-08-16 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc.,
arch/arm/common/vlock.h line 28
Quoted 10 lines
arch/arm/common/vlock.h new file +10
-0+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef __VLOCK_H +#define __VLOCK_H + +#include <asm/bL_entry.h> + +#define VLOCK_OWNER_OFFSET 0 +#define VLOCK_VOTING_OFFSET 4
asm-offsets again?
arch/arm/common/vlock.h line 29
Quoted 1 line
arch/arm/common/vlock.h new file +1
-0+#define VLOCK_VOTING_SIZE ((BL_CPUS_PER_CLUSTER + 3) / 4 * 4)
Huh?
arch/arm/common/vlock.h line 37
Quoted 8 lines
arch/arm/common/vlock.h new file +8
-0+#define VLOCK_SIZE (VLOCK_VOTING_OFFSET + VLOCK_VOTING_SIZE) +#define VLOCK_OWNER_NONE 0 + +#ifndef __ASSEMBLY__ + +struct vlock { +char data[VLOCK_SIZE ]; +};
Does this mean the struct is only single byte aligned? You do word accesses to it in your vlock code and rely on atomicity, so I'd feel safer if it was aligned to 4 bytes, especially since this isn't being accessed via a normal mapping.
Will
PATCH 4/16 ARM: b.L: Add baremetal voting mutexes
Quoted 29 lines · Will Deacon
Quoted 14 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
This patch adds a simple low-level voting mutex implementation to be used to arbitrate during first man selection when no load/store exclusive instructions are usable.
For want of a better name, these are called "vlocks". (I was tempted to call them ballot locks, but "block" is way too confusing an abbreviation...)
There is no function to wait for the lock to be released, and no
vlock_lock()function since we don't need these at the moment. These could straightforwardly be added if vlocks get used for other purposes.[...]
Quoted 8 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +2
-0@@ -0,0 +1,211 @@ +vlocks for Bare-Metal Mutual Exclusion +======================================[...]
Quoted 6 lines
Documentation/arm/big.LITTLE/vlocks.txt line 121
Quoted 4 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +4
-0+ARM implementation +------------------ + +The current ARM implementation [2] contains a some optimisations beyond-a
Fixed.
Quoted 48 lines
Quoted 48 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +47
-0+the basic algorithm: + + * By packing the members of the currently_voting array close together, + we can read the whole array in one transaction (providing the number + of CPUs potentially contending the lock is small enough). This + reduces the number of round-trips required to external memory. + + In the ARM implementation, this means that we can use a single load + and comparison: + + LDR Rt, [Rn] + CMP Rt, #0 + + ...in place of code equivalent to: + + LDRB Rt, [Rn] + CMP Rt, #0 + LDRBEQ Rt, [Rn, #1] + CMPEQ Rt, #0 + LDRBEQ Rt, [Rn, #2] + CMPEQ Rt, #0 + LDRBEQ Rt, [Rn, #3] + CMPEQ Rt, #0 + + This cuts down on the fast-path latency, as well as potentially + reducing bus contention in contended cases. + + The optimisation relies on the fact that the ARM memory system + guarantees coherency between overlapping memory accesses of + different sizes, similarly to many other architectures. Note that + we do not care which element of currently_voting appears in which + bits of Rt, so there is no need to worry about endianness in this + optimisation. + + If there are too many CPUs to read the currently_voting array in + one transaction then multiple transations are still required. The + implementation uses a simple loop of word-sized loads for this + case. The number of transactions is still fewer than would be + required if bytes were loaded individually. + + + In principle, we could aggregate further by using LDRD or LDM, but + to keep the code simple this was not attempted in the initial + implementation. + + + * vlocks are currently only used to coordinate between CPUs which are
Quoted 8 lines
Documentation/arm/big.LITTLE/vlocks.txt line 171
Quoted 3 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +3
-0+ unable to enable their caches yet. This means that the + implementation removes many of the barriers which would be required + when executing the algorithm in cached memory.I think you need to elaborate on this and clearly identify the requirements of the memory behaviour. In reality, these locks are hardly ever usable so we don't want them cropping up in driver code and the like!
Doesn't the following paragraph make that clear enough?
Maybe we should rip out the C interface to avoid such abuses. I think that was initially added when we weren't sure if the C code had to be involved.
Quoted 6 lines
Quoted 6 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +6
-0+ packing of the currently_voting array does not work with cached + memory unless all CPUs contending the lock are cache-coherent, due + to cache writebacks from one CPU clobbering values written by other + CPUs. (Though if all the CPUs are cache-coherent, you should be + probably be using proper spinlocks instead anyway). +
Quoted 8 lines
Documentation/arm/big.LITTLE/vlocks.txt line 183
Quoted 5 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +5
-0+ + * The "no votes yet" value used for the last_vote variable is 0 (not + -1 as in the pseudocode). This allows statically-allocated vlocks + to be implicitly initialised to an unlocked state simply by putting + them in .bss.You could also put them in their own section and initialise them to -1 there.
Same argument as for bL_vectors: That is less efficient than using .bss which takes no image space. Plus the transformation for CPU 0 to work with this is basically free.
Quoted 38 lines
Quoted 38 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +27
-0+ An offset is added to each CPU's ID for the purpose of setting this + variable, so that no CPU uses the value 0 for its ID. + + +Colophon +-------- + +Originally created and documented by Dave Martin for Linaro Limited, for +use in ARM-based big.LITTLE platforms, with review and input gratefully +received from Nicolas Pitre and Achin Gupta. Thanks to Nicolas for +grabbing most of this text out of the relevant mail thread and writing +up the pseudocode. + +Copyright (C) 2012 Linaro Limited +Distributed under the terms of Version 2 of the GNU General Public +License, as defined in linux/COPYING. + + +References +---------- + +[1] Lamport, L. "A New Solution of Dijkstra's Concurrent Programming + Problem", Communications of the ACM 17, 8 (August 1974), 453-455. + + http://en.wikipedia.org/wiki/Lamport%27s_bakery_algorithm + +[2] linux/arch/arm/common/vlock.S, www.kernel.org.arch/arm/common/vlock.S new file +5
-0@@ -0,0 +1,108 @@ +/* + * vlock.S - simple voting lock implementation for ARM + * + * Created by: Dave Martin, 2012-08-16 + * Copyright: (C) 2012 Linaro Limited
Quoted 8 lines
arch/arm/common/vlock.S line 10
Quoted 5 lines
arch/arm/common/vlock.S new file +5
-0+ * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version.Your documentation is strictly GPLv2, so there's a strange discrepancy here.
Indeed.
@Dave: your call.
Quoted 13 lines
Quoted 13 lines
arch/arm/common/vlock.S new file +13
-0+ * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * + * This algorithm is described in more detail in + * Documentation/arm/big.LITTLE/vlocks.txt.
Quoted 8 lines
arch/arm/common/vlock.S line 29
Quoted 6 lines
arch/arm/common/vlock.S new file +6
-0+ */ + +#include <linux/linkage.h> +#include "vlock.h" + +#if VLOCK_VOTING_SIZE > 44? Maybe a CONFIG option or a #define in an arch
vlock.h?
The 4 here is actually related to the number of bytes in a word, to decide whether or not a loop is needed for voters enumeration. That is not configurable.
Quoted 116 lines
Quoted 43 lines
arch/arm/common/vlock.S new file +43
-0+#define FEW(x...) +#define MANY(x...) x +#else +#define FEW(x...) x +#define MANY(x...) +#endif + +@ voting lock for first-man coordination + +.macro voting_begin rbase:req, rcpu:req, rscratch:req + mov \rscratch, #1 + strb \rscratch, [\rbase, \rcpu] + dsb +.endm + +.macro voting_end rbase:req, rcpu:req, rscratch:req + mov \rscratch, #0 + strb \rscratch, [\rbase, \rcpu] + dsb + sev +.endm + +/* + * The vlock structure must reside in Strongly-Ordered or Device memory. + * This implementation deliberately eliminates most of the barriers which + * would be required for other memory types, and assumes that independent + * writes to neighbouring locations within a cacheline do not interfere + * with one another. + */ + +@ r0: lock structure base +@ r1: CPU ID (0-based index within cluster) +ENTRY(vlock_trylock) + add r1, r1, #VLOCK_VOTING_OFFSET + + voting_begin r0, r1, r2 + + ldrb r2, [r0, #VLOCK_OWNER_OFFSET] @ check whether lock is held + cmp r2, #VLOCK_OWNER_NONE + bne trylock_fail @ fail if so + + strb r1, [r0, #VLOCK_OWNER_OFFSET] @ submit my vote +arch/arm/common/vlock.S line 82
Quoted 10 lines
arch/arm/common/vlock.S new file +10
-0+ voting_end r0, r1, r2 + + @ Wait for the current round of voting to finish: + + MANY( mov r3, #VLOCK_VOTING_OFFSET ) +0: + MANY( ldr r2, [r0, r3] ) + FEW( ldr r2, [r0, #VLOCK_VOTING_OFFSET] ) + cmp r2, #0 + wfeneIs there a race here? I wonder if you can end up in a situation where everybody enters wfe and then there is nobody left to signal an event via
voting_end(if, for example the last voter sent the sev when everybody else was simultaneously doing the cmp before the wfe)...... actually, that's ok as long as
VLOCK_VOTING_OFFSETisn't speculated, which it shouldn't be from strongly-ordered memory. Fair enough!Quoted 54 lines
arch/arm/common/vlock.S new file +26
-0+ bne 0b + MANY( add r3, r3, #4 ) + MANY( cmp r3, #VLOCK_VOTING_OFFSET + VLOCK_VOTING_SIZE ) + MANY( bne 0b ) + + @ Check who won: + + ldrb r2, [r0, #VLOCK_OWNER_OFFSET] + eor r0, r1, r2 @ zero if I won, else nonzero + bx lr + +trylock_fail: + voting_end r0, r1, r2 + mov r0, #1 @ nonzero indicates that I lost + bx lr +ENDPROC(vlock_trylock) + +@ r0: lock structure base +ENTRY(vlock_unlock) + mov r1, #VLOCK_OWNER_NONE + dsb + strb r1, [r0, #VLOCK_OWNER_OFFSET] + dsb + sev + bx lr +ENDPROC(vlock_unlock)arch/arm/common/vlock.h new file +22
-0@@ -0,0 +1,43 @@ +/* +* vlock.h - simple voting lock implementation +* +* Created by: Dave Martin, 2012-08-16 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef __VLOCK_H
Quoted 8 lines
arch/arm/common/vlock.h line 28
Quoted 6 lines
arch/arm/common/vlock.h new file +6
-0+#define __VLOCK_H + +#include <asm/bL_entry.h> + +#define VLOCK_OWNER_OFFSET 0 +#define VLOCK_VOTING_OFFSET 4asm-offsets again?
Same answer.
Quoted 3 lines
arch/arm/common/vlock.h line 29
Quoted 1 line
arch/arm/common/vlock.h new file +1
-0+#define VLOCK_VOTING_SIZE ((BL_CPUS_PER_CLUSTER + 3) / 4 * 4)Huh?
Each ballot is one byte, and we pack them into words. So this is the size of the required words to hold all ballots.
Quoted 5 lines
Quoted 5 lines
arch/arm/common/vlock.h new file +5
-0+#define VLOCK_SIZE (VLOCK_VOTING_OFFSET + VLOCK_VOTING_SIZE) +#define VLOCK_OWNER_NONE 0 + +#ifndef __ASSEMBLY__ +
Quoted 8 lines
arch/arm/common/vlock.h line 37
Quoted 3 lines
arch/arm/common/vlock.h new file +3
-0+struct vlock { +char data[VLOCK_SIZE ]; +};Does this mean the struct is only single byte aligned? You do word accesses to it in your vlock code and rely on atomicity, so I'd feel safer if it was aligned to 4 bytes, especially since this isn't being accessed via a normal mapping.
The structure size is always a multiple of 4 bytes. Its alignment is actually much larger than 4 as it needs to span a whole cache line not to be overwritten by dirty line writeback.
As I mentioned before, given that this structure is allocated and accessed only by assembly code, we could simply remove all those unused C definitions to avoid potential confusion and misuse.
Nicolas
PATCH 4/16 ARM: b.L: Add baremetal voting mutexes
Quoted 7 lines · Nicolas Pitre
Quoted 6 lines · Will Deacon
Quoted 4 lines · Nicolas Pitre
+ * vlocks are currently only used to coordinate between CPUs which are
Documentation/arm/big.LITTLE/vlocks.txt new file +3
-0+ unable to enable their caches yet. This means that the + implementation removes many of the barriers which would be required + when executing the algorithm in cached memory.
Quoted 6 lines
Quoted 4 lines
I think you need to elaborate on this and clearly identify the requirements of the memory behaviour. In reality, these locks are hardly ever usable so we don't want them cropping up in driver code and the like!
Doesn't the following paragraph make that clear enough?
I think it misses a lot out (e.g. we require single-copy atomicity guarantees for byte access, we require no speculation etc). Essentially, it's an imprecise definition of strongly-ordered memory. However, see below (the bit about removing the C implementation)...
Quoted 3 lines
Maybe we should rip out the C interface to avoid such abuses. I think that was initially added when we weren't sure if the C code had to be involved.
[...]
Quoted 10 lines
Quoted 6 lines
arch/arm/common/vlock.S line 29
Quoted 4 lines
arch/arm/common/vlock.S new file +4
-0+#include <linux/linkage.h> +#include "vlock.h" + +#if VLOCK_VOTING_SIZE > 44? Maybe a CONFIG option or a #define in an arch
vlock.h?The 4 here is actually related to the number of bytes in a word, to decide whether or not a loop is needed for voters enumeration. That is not configurable.
BYTES_PER_LONG then (or suitable shifting of BITS_PER_LONG)?
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
+#define
VLOCK_VOTING_SIZE((BL_CPUS_PER_CLUSTER+ 3) / 4 * 4)Huh?
Each ballot is one byte, and we pack them into words. So this is the size of the required words to hold all ballots.
Ok, so this could make use of BYTES_PER_LONG too, just to make the reasoning more clear.
Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
arch/arm/common/vlock.h new file +5
-0+#define VLOCK_SIZE (VLOCK_VOTING_OFFSET + VLOCK_VOTING_SIZE) +#define VLOCK_OWNER_NONE 0 + +#ifndef __ASSEMBLY__ +Quoted 8 lines
arch/arm/common/vlock.h line 37
Quoted 3 lines
arch/arm/common/vlock.h new file +3
-0+struct vlock { +char data[VLOCK_SIZE ]; +};Does this mean the struct is only single byte aligned? You do word accesses to it in your vlock code and rely on atomicity, so I'd feel safer if it was aligned to 4 bytes, especially since this isn't being accessed via a normal mapping.
Quoted 3 lines
The structure size is always a multiple of 4 bytes. Its alignment is actually much larger than 4 as it needs to span a whole cache line not to be overwritten by dirty line writeback.
That's not implied from the structure definition.
Quoted 3 lines
As I mentioned before, given that this structure is allocated and accessed only by assembly code, we could simply remove all those unused C definitions to avoid potential confusion and misuse.
Yes, I think removing the C definitions is a great idea. Then, we have a pure-asm implementation which is, as such, tied to ARM. In that case, the documentation can just refer to ARM memory types instead of loosely defining the required characteristics (i.e. state that device or strongly-ordered memory is required).
Cheers,
Will
PATCH 4/16 ARM: b.L: Add baremetal voting mutexes
In reply to Nicolas PitreQuoted 169 lines · Nicolas Pitre
Quoted 29 lines · Will Deacon
Quoted 14 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
This patch adds a simple low-level voting mutex implementation to be used to arbitrate during first man selection when no load/store exclusive instructions are usable.
For want of a better name, these are called "vlocks". (I was tempted to call them ballot locks, but "block" is way too confusing an abbreviation...)
There is no function to wait for the lock to be released, and no
vlock_lock()function since we don't need these at the moment. These could straightforwardly be added if vlocks get used for other purposes.[...]
Quoted 8 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +2
-0@@ -0,0 +1,211 @@ +vlocks for Bare-Metal Mutual Exclusion +======================================[...]
Quoted 6 lines
Documentation/arm/big.LITTLE/vlocks.txt line 121
Quoted 4 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +4
-0+ARM implementation +------------------ + +The current ARM implementation [2] contains a some optimisations beyond-a
Fixed.
Quoted 48 lines
Quoted 48 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +47
-0+the basic algorithm: + + * By packing the members of the currently_voting array close together, + we can read the whole array in one transaction (providing the number + of CPUs potentially contending the lock is small enough). This + reduces the number of round-trips required to external memory. + + In the ARM implementation, this means that we can use a single load + and comparison: + + LDR Rt, [Rn] + CMP Rt, #0 + + ...in place of code equivalent to: + + LDRB Rt, [Rn] + CMP Rt, #0 + LDRBEQ Rt, [Rn, #1] + CMPEQ Rt, #0 + LDRBEQ Rt, [Rn, #2] + CMPEQ Rt, #0 + LDRBEQ Rt, [Rn, #3] + CMPEQ Rt, #0 + + This cuts down on the fast-path latency, as well as potentially + reducing bus contention in contended cases. + + The optimisation relies on the fact that the ARM memory system + guarantees coherency between overlapping memory accesses of + different sizes, similarly to many other architectures. Note that + we do not care which element of currently_voting appears in which + bits of Rt, so there is no need to worry about endianness in this + optimisation. + + If there are too many CPUs to read the currently_voting array in + one transaction then multiple transations are still required. The + implementation uses a simple loop of word-sized loads for this + case. The number of transactions is still fewer than would be + required if bytes were loaded individually. + + + In principle, we could aggregate further by using LDRD or LDM, but + to keep the code simple this was not attempted in the initial + implementation. + + + * vlocks are currently only used to coordinate between CPUs which areQuoted 8 lines
Documentation/arm/big.LITTLE/vlocks.txt line 171
Quoted 3 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +3
-0+ unable to enable their caches yet. This means that the + implementation removes many of the barriers which would be required + when executing the algorithm in cached memory.I think you need to elaborate on this and clearly identify the requirements of the memory behaviour. In reality, these locks are hardly ever usable so we don't want them cropping up in driver code and the like!
Doesn't the following paragraph make that clear enough?
Maybe we should rip out the C interface to avoid such abuses. I think that was initially added when we weren't sure if the C code had to be involved.
Quoted 6 lines
Quoted 6 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +6
-0+ packing of the currently_voting array does not work with cached + memory unless all CPUs contending the lock are cache-coherent, due + to cache writebacks from one CPU clobbering values written by other + CPUs. (Though if all the CPUs are cache-coherent, you should be + probably be using proper spinlocks instead anyway). +Quoted 8 lines
Documentation/arm/big.LITTLE/vlocks.txt line 183
Quoted 5 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +5
-0+ + * The "no votes yet" value used for the last_vote variable is 0 (not + -1 as in the pseudocode). This allows statically-allocated vlocks + to be implicitly initialised to an unlocked state simply by putting + them in .bss.You could also put them in their own section and initialise them to -1 there.
Same argument as for
bL_vectors: That is less efficient than using .bss which takes no image space. Plus the transformation for CPU 0 to work with this is basically free.Quoted 38 lines
Quoted 38 lines
Documentation/arm/big.LITTLE/vlocks.txt new file +27
-0+ An offset is added to each CPU's ID for the purpose of setting this + variable, so that no CPU uses the value 0 for its ID. + + +Colophon +-------- + +Originally created and documented by Dave Martin for Linaro Limited, for +use in ARM-based big.LITTLE platforms, with review and input gratefully +received from Nicolas Pitre and Achin Gupta. Thanks to Nicolas for +grabbing most of this text out of the relevant mail thread and writing +up the pseudocode. + +Copyright (C) 2012 Linaro Limited +Distributed under the terms of Version 2 of the GNU General Public +License, as defined in linux/COPYING. + + +References +---------- + +[1] Lamport, L. "A New Solution of Dijkstra's Concurrent Programming + Problem", Communications of the ACM 17, 8 (August 1974), 453-455. + + http://en.wikipedia.org/wiki/Lamport%27s_bakery_algorithm + +[2] linux/arch/arm/common/vlock.S, www.kernel.org.arch/arm/common/vlock.S new file +5
-0@@ -0,0 +1,108 @@ +/* + * vlock.S - simple voting lock implementation for ARM + * + * Created by: Dave Martin, 2012-08-16 + * Copyright: (C) 2012 Linaro LimitedQuoted 8 lines
arch/arm/common/vlock.S line 10
Quoted 5 lines
arch/arm/common/vlock.S new file +5
-0+ * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version.Your documentation is strictly GPLv2, so there's a strange discrepancy here.
Quoted 3 lines
Indeed.
@Dave: your call.
This can all be strict v2. The discrepancy was unintentional.
Quoted 23 lines
Quoted 14 lines
Quoted 14 lines
arch/arm/common/vlock.S new file +13
-0+ * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * + * This algorithm is described in more detail in + * Documentation/arm/big.LITTLE/vlocks.txt.Quoted 8 lines
arch/arm/common/vlock.S line 29
Quoted 6 lines
arch/arm/common/vlock.S new file +6
-0+ */ + +#include <linux/linkage.h> +#include "vlock.h" + +#if VLOCK_VOTING_SIZE > 44? Maybe a CONFIG option or a #define in an arch
vlock.h?
Quoted 3 lines
The 4 here is actually related to the number of bytes in a word, to decide whether or not a loop is needed for voters enumeration. That is not configurable.
This is arch-specific assembler, and the 4-bytes-per-word proprty is a fixed property of the architecture.
We could have a comment maybe:
/*
* Each voting occupies a byte, so if there are 4 or fewer, the whole
* set of voting flags can be accessed with a single word access. */
Quoted 60 lines
Quoted 60 lines
Quoted 44 lines
arch/arm/common/vlock.S new file +43
-0+#define FEW(x...) +#define MANY(x...) x +#else +#define FEW(x...) x +#define MANY(x...) +#endif + +@ voting lock for first-man coordination + +.macro voting_begin rbase:req, rcpu:req, rscratch:req + mov \rscratch, #1 + strb \rscratch, [\rbase, \rcpu] + dsb +.endm + +.macro voting_end rbase:req, rcpu:req, rscratch:req + mov \rscratch, #0 + strb \rscratch, [\rbase, \rcpu] + dsb + sev +.endm + +/* + * The vlock structure must reside in Strongly-Ordered or Device memory. + * This implementation deliberately eliminates most of the barriers which + * would be required for other memory types, and assumes that independent + * writes to neighbouring locations within a cacheline do not interfere + * with one another. + */ + +@ r0: lock structure base +@ r1: CPU ID (0-based index within cluster) +ENTRY(vlock_trylock) + add r1, r1, #VLOCK_VOTING_OFFSET + + voting_begin r0, r1, r2 + + ldrb r2, [r0, #VLOCK_OWNER_OFFSET] @ check whether lock is held + cmp r2, #VLOCK_OWNER_NONE + bne trylock_fail @ fail if so + + strb r1, [r0, #VLOCK_OWNER_OFFSET] @ submit my vote +arch/arm/common/vlock.S line 82
Quoted 10 lines
arch/arm/common/vlock.S new file +10
-0+ voting_end r0, r1, r2 + + @ Wait for the current round of voting to finish: + + MANY( mov r3, #VLOCK_VOTING_OFFSET ) +0: + MANY( ldr r2, [r0, r3] ) + FEW( ldr r2, [r0, #VLOCK_VOTING_OFFSET] ) + cmp r2, #0 + wfeneIs there a race here? I wonder if you can end up in a situation where everybody enters wfe and then there is nobody left to signal an event via
voting_end(if, for example the last voter sent the sev when everybody else was simultaneously doing the cmp before the wfe)...
Quoted 2 lines
Quoted 2 lines
... actually, that's ok as long as
VLOCK_VOTING_OFFSETisn't speculated, which it shouldn't be from strongly-ordered memory. Fair enough!
Indeed. The order of accesses to the voting flags is guaranteed by strongly-ordered-ness. The ordering between the strb and sev in voting_end required a dsb, which we have. The ordering between the external load and wfe in the waiting code is guaranteed so S-O-ness and a control dependency.
Quoted 64 lines
Quoted 55 lines
Quoted 55 lines
arch/arm/common/vlock.S new file +26
-0+ bne 0b + MANY( add r3, r3, #4 ) + MANY( cmp r3, #VLOCK_VOTING_OFFSET + VLOCK_VOTING_SIZE ) + MANY( bne 0b ) + + @ Check who won: + + ldrb r2, [r0, #VLOCK_OWNER_OFFSET] + eor r0, r1, r2 @ zero if I won, else nonzero + bx lr + +trylock_fail: + voting_end r0, r1, r2 + mov r0, #1 @ nonzero indicates that I lost + bx lr +ENDPROC(vlock_trylock) + +@ r0: lock structure base +ENTRY(vlock_unlock) + mov r1, #VLOCK_OWNER_NONE + dsb + strb r1, [r0, #VLOCK_OWNER_OFFSET] + dsb + sev + bx lr +ENDPROC(vlock_unlock)arch/arm/common/vlock.h new file +22
-0@@ -0,0 +1,43 @@ +/* +* vlock.h - simple voting lock implementation +* +* Created by: Dave Martin, 2012-08-16 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef __VLOCK_HQuoted 8 lines
arch/arm/common/vlock.h line 28
Quoted 6 lines
arch/arm/common/vlock.h new file +6
-0+#define __VLOCK_H + +#include <asm/bL_entry.h> + +#define VLOCK_OWNER_OFFSET 0 +#define VLOCK_VOTING_OFFSET 4asm-offsets again?
Quoted 1 line
Same answer.
I did start out by adding stuff to asm-offsets, but it just ende up looking like cruft.
asm-offsets is primarily for synchronising C structures with asm. The vlock structure is not accessed from C, though.
Quoted 7 lines
Quoted 4 lines
arch/arm/common/vlock.h line 29
Quoted 2 lines
arch/arm/common/vlock.h new file +1
-0+#define VLOCK_VOTING_SIZE ((BL_CPUS_PER_CLUSTER + 3) / 4 * 4)Huh?
Each ballot is one byte, and we pack them into words. So this is the size of the required words to hold all ballots.
Hopefully we don't need a comment? I hoped this was straightforward.
Quoted 15 lines
Quoted 14 lines
arch/arm/common/vlock.h line 37
Quoted 9 lines
arch/arm/common/vlock.h new file +8
-0+#define VLOCK_SIZE (VLOCK_VOTING_OFFSET + VLOCK_VOTING_SIZE) +#define VLOCK_OWNER_NONE 0 + +#ifndef __ASSEMBLY__ + +struct vlock { +char data[VLOCK_SIZE ]; +};Does this mean the struct is only single byte aligned? You do word accesses to it in your vlock code and rely on atomicity, so I'd feel safer if it was aligned to 4 bytes, especially since this isn't being accessed via a normal mapping.
Quoted 7 lines
The structure size is always a multiple of 4 bytes. Its alignment is actually much larger than 4 as it needs to span a whole cache line not to be overwritten by dirty line writeback.
As I mentioned before, given that this structure is allocated and accessed only by assembly code, we could simply remove all those unused C definitions to avoid potential confusion and misuse.
Agreed. Originally I anticipated this stuff being usable from C, but this is so tenuous that providing C declarations may just confuse people.
Cheers
---Dave
PATCH 5/16 ARM: bL_head: vlock-based first man election
In reply to Nicolas PitreFrom: Dave Martin <[email protected]>
Instead of requiring the first man to be elected in advance (which can be suboptimal in some situations), this patch uses a per- cluster mutex to co-ordinate selection of the first man.
This should also make it more feasible to reuse this code path for asynchronous cluster resume (as in CPUidle scenarios).
--- arch/arm/common/Makefile | 2 +- arch/arm/common/bL_head.S | 91 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 80 insertions(+), 13 deletions(-)
arch/arm/common/Makefile +1 -1
@@ -15,4 +15,4 @@ obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o -obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o +obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o vlock.o
arch/arm/common/bL_head.S +79 -12
143 lines not on this page · raw message
Signature · 1 line
1.8.0
PATCH 6/16 ARM: b.L: generic SMP secondary bringup and hotplug support
In reply to Nicolas PitreNow that the b.L power API is in place, we can use it for SMP secondary bringup and CPU hotplug in a generic fashion.
--- arch/arm/common/Makefile | 2 +- arch/arm/common/bL_platsmp.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 arch/arm/common/bL_platsmp.c
arch/arm/common/Makefile +1 -1
@@ -15,4 +15,4 @@ obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o -obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o vlock.o +obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o bL_platsmp.o vlock.o
arch/arm/common/bL_platsmp.c new file +79 -0
79 lines not on this page · raw message
Signature · 1 line
1.8.0
PATCH 6/16 ARM: b.L: generic SMP secondary bringup and hotplug support
Quoted 58 lines · Nicolas Pitre
Now that the b.L power API is in place, we can use it for SMP secondary bringup and CPU hotplug in a generic fashion.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/Makefile | 2 +- arch/arm/common/bL_platsmp.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-)create mode 100644
arch/arm/common/bL_platsmp.carch/arm/common/Makefile +1
-1@@ -15,4 +15,4 @@ obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o -obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o vlock.o +obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o bL_platsmp.o vlock.oarch/arm/common/bL_platsmp.c new file +32
-0@@ -0,0 +1,79 @@ +/* +* linux/arch/arm/mach-vexpress/bL_platsmp.c +* +* Created by: Nicolas Pitre, November 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* Code to handle secondary CPU bringup and hotplug for the bL power API. +*/ + +#include <linux/init.h> +#include <linux/smp.h> + +#include <asm/bL_entry.h> +#include <asm/smp_plat.h> +#include <asm/hardware/gic.h> + +static void __init simple_smp_init_cpus (void ) +{ +set_smp_cross_call (gic_raise_softirq); +} + +static int __cpuinit bL_boot_secondary (unsigned int cpu,struct task_struct *idle) +{ +unsigned int pcpu, pcluster, ret; +extern void secondary_startup (void ); + + pcpu =cpu_logical_map (cpu) &0xff ; + pcluster = (cpu_logical_map (cpu) >>8 ) &0xff ;
arch/arm/common/bL_platsmp.c line 42
Quoted 10 lines
arch/arm/common/bL_platsmp.c new file +10
-0+pr_debug ("%s: logical CPU %d is physical CPU %d cluster %d\n" , + __func__, cpu, pcpu, pcluster); + +bL_set_entry_vector (pcpu, pcluster,NULL ); + ret =bL_cpu_power_up (pcpu, pcluster); +if (ret) +return ret; +bL_set_entry_vector (pcpu, pcluster, secondary_startup); +gic_raise_softirq (cpumask_of (cpu),0 ); +sev ();
softirq() should be enough to break a CPU if it is in standby with wfe state. Is that additional sev() needed here ?
Regards,
Santosh
PATCH 6/16 ARM: b.L: generic SMP secondary bringup and hotplug support
Hi Santosh,
Quoted 68 lines · Santosh Shilimkar
Quoted 67 lines · Nicolas Pitre
Now that the b.L power API is in place, we can use it for SMP secondary bringup and CPU hotplug in a generic fashion.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/Makefile | 2 +- arch/arm/common/bL_platsmp.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-)create mode 100644
arch/arm/common/bL_platsmp.carch/arm/common/Makefile +1
-1@@ -15,4 +15,4 @@ obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o -obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o vlock.o +obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o bL_platsmp.ovlock.o
arch/arm/common/bL_platsmp.c new file +26
-0@@ -0,0 +1,79 @@ +/* +* linux/arch/arm/mach-vexpress/bL_platsmp.c +* +* Created by: Nicolas Pitre, November 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* Code to handle secondary CPU bringup and hotplug for the bL power API. +*/ + +#include <linux/init.h> +#include <linux/smp.h> + +#include <asm/bL_entry.h> +#include <asm/smp_plat.h> +#include <asm/hardware/gic.h> + +static void __init simple_smp_init_cpus (void ) +{ +set_smp_cross_call (gic_raise_softirq); +} + +static int __cpuinit bL_boot_secondary(unsigned int cpu, struct
task_struct*idle)arch/arm/common/bL_platsmp.c new file +11
-0+{ +unsigned int pcpu, pcluster, ret; +extern void secondary_startup (void ); + + pcpu =cpu_logical_map (cpu) &0xff ; + pcluster = (cpu_logical_map (cpu) >>8 ) &0xff ; +pr_debug ("%s: logical CPU %d is physical CPU %d cluster %d\n" , + __func__, cpu, pcpu, pcluster); + +bL_set_entry_vector (pcpu, pcluster,NULL ); + ret =bL_cpu_power_up (pcpu, pcluster);
Quoted 8 lines
arch/arm/common/bL_platsmp.c line 42
Quoted 5 lines
arch/arm/common/bL_platsmp.c new file +5
-0+if (ret) +return ret; +bL_set_entry_vector (pcpu, pcluster, secondary_startup); +gic_raise_softirq (cpumask_of (cpu),0 ); +sev ();
softirq()should be enough to break a CPU if it is in standby with wfe state. Is that additionalsev()needed here ?
Not if the target cpu has its I & F bits disabled and that would be the case with a secondary waiting to be woken up
thanks,
Achin
Quoted 4 lines
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH 6/16 ARM: b.L: generic SMP secondary bringup and hotplug support
Quoted 81 lines · Achin Gupta
Hi Santosh,
Quoted 68 lines · Santosh Shilimkar
Quoted 67 lines · Nicolas Pitre
Now that the b.L power API is in place, we can use it for SMP secondary bringup and CPU hotplug in a generic fashion.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/Makefile | 2 +- arch/arm/common/bL_platsmp.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-)create mode 100644
arch/arm/common/bL_platsmp.carch/arm/common/Makefile +1
-1@@ -15,4 +15,4 @@ obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o -obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o vlock.o +obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o bL_platsmp.ovlock.o
arch/arm/common/bL_platsmp.c new file +26
-0@@ -0,0 +1,79 @@ +/* +* linux/arch/arm/mach-vexpress/bL_platsmp.c +* +* Created by: Nicolas Pitre, November 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* Code to handle secondary CPU bringup and hotplug for the bL power API. +*/ + +#include <linux/init.h> +#include <linux/smp.h> + +#include <asm/bL_entry.h> +#include <asm/smp_plat.h> +#include <asm/hardware/gic.h> + +static void __init simple_smp_init_cpus (void ) +{ +set_smp_cross_call (gic_raise_softirq); +} + +static int __cpuinit bL_boot_secondary(unsigned int cpu, struct
task_struct*idle)arch/arm/common/bL_platsmp.c new file +11
-0+{ +unsigned int pcpu, pcluster, ret; +extern void secondary_startup (void ); + + pcpu =cpu_logical_map (cpu) &0xff ; + pcluster = (cpu_logical_map (cpu) >>8 ) &0xff ; +pr_debug ("%s: logical CPU %d is physical CPU %d cluster %d\n" , + __func__, cpu, pcpu, pcluster); + +bL_set_entry_vector (pcpu, pcluster,NULL ); + ret =bL_cpu_power_up (pcpu, pcluster);Quoted 8 lines
arch/arm/common/bL_platsmp.c line 42
Quoted 5 lines
arch/arm/common/bL_platsmp.c new file +5
-0+if (ret) +return ret; +bL_set_entry_vector (pcpu, pcluster, secondary_startup); +gic_raise_softirq (cpumask_of (cpu),0 ); +sev ();
softirq()should be enough to break a CPU if it is in standby with wfe state. Is that additionalsev()needed here ?
Quoted 3 lines
Not if the target cpu has its I & F bits disabled and that would be the case with a secondary waiting to be woken up
This is interesting since CPU is actually in standby state and this was not my understanding so far. Your statement at least contradicts the ARM ARM (B1.8.12 Wait For Interrupt)
-----------------------
The processor can remain in the WFI low-power state until it is reset, or it detects one of the following WFI wake-up
events:
? a physical IRQ interrupt, regardless of the value of the CPSR.I bit ? a physical FIQ interrupt, regardless of the value of the CPSR.F bit
----------------------------------
Are you referring to some new behavior on latest ARMv7 CPUs ?
Regards,
Santosh
PATCH 6/16 ARM: b.L: generic SMP secondary bringup and hotplug support
Hi Santosh,
Quoted 102 lines · Santosh Shilimkar
Quoted 87 lines · Achin Gupta
Hi Santosh,
Quoted 72 lines · Santosh Shilimkar
Quoted 70 lines · Nicolas Pitre
Now that the b.L power API is in place, we can use it for SMP secondary bringup and CPU hotplug in a generic fashion.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/Makefile | 2 +- arch/arm/common/bL_platsmp.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-)create mode 100644
arch/arm/common/bL_platsmp.carch/arm/common/Makefile +1
-1@@ -15,4 +15,4 @@ obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o -obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o vlock.o +obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o bL_platsmp.ovlock.o
arch/arm/common/bL_platsmp.c new file +11
-0@@ -0,0 +1,79 @@ +/* +* linux/arch/arm/mach-vexpress/bL_platsmp.c +* +* Created by: Nicolas Pitre, November 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* Code to handle secondary CPU bringup and hotplug for the bL power API.
+ */arch/arm/common/bL_platsmp.c new file +14
-0+ +#include <linux/init.h> +#include <linux/smp.h> + +#include <asm/bL_entry.h> +#include <asm/smp_plat.h> +#include <asm/hardware/gic.h> + +static void __init simple_smp_init_cpus (void ) +{ +set_smp_cross_call (gic_raise_softirq); +} + +static int __cpuinit bL_boot_secondary(unsigned int cpu, struct
task_struct*idle)arch/arm/common/bL_platsmp.c new file +12
-0+{ +unsigned int pcpu, pcluster, ret; +extern void secondary_startup (void ); + + pcpu =cpu_logical_map (cpu) &0xff ; + pcluster = (cpu_logical_map (cpu) >>8 ) &0xff ; +pr_debug ("%s: logical CPU %d is physical CPU %d cluster %d\n" , + __func__, cpu, pcpu, pcluster); + +bL_set_entry_vector (pcpu, pcluster,NULL ); + ret =bL_cpu_power_up (pcpu, pcluster); +if (ret)Quoted 8 lines
arch/arm/common/bL_platsmp.c line 42
Quoted 4 lines
arch/arm/common/bL_platsmp.c new file +4
-0+return ret; +bL_set_entry_vector (pcpu, pcluster, secondary_startup); +gic_raise_softirq (cpumask_of (cpu),0 ); +sev ();
softirq()should be enough to break a CPU if it is in standby with wfe state. Is that additionalsev()needed here ?Quoted 3 lines
Not if the target cpu has its I & F bits disabled and that would be the case with a secondary waiting to be woken up
This is interesting since CPU is actually in standby state and this was not my understanding so far. Your statement at least contradicts the ARM ARM (B1.8.12 Wait For Interrupt)
-----------------------
The processor can remain in the WFI low-power state until it is reset, or it detects one of the following WFI wake-up
events:
? a physical IRQ interrupt, regardless of the value of the CPSR.I bit ? a physical FIQ interrupt, regardless of the value of the CPSR.F bit
----------------------------------
Quoted 1 line
Are you referring to some new behavior on latest ARMv7 CPUs ?
You are abs right about the 'wfi' behaviour. I was talking about the effect of interrupts on a cpu thats in 'wfe'.
The power up process takes place in two steps. The first step involves sending an ipi which will either:
a. cause the power controller to bring the processor out of reset b. cause the processor to exit from wfi (most probably in the bootloader code)
The cpu then enters Linux (bL_entry_point) and after doing any cluster setup waits in 'wfe' if its 'bL_entry_vector' has not been set as yet. The 'sev' is meant
to poke the cpu once this has been done.
Its not required in this case as we have already set 'bL_entry_vector' , issued a barrier & flushed the cache line. So if the incoming cpu sees a 0 in its vector
then that would be a symptom of a different problem.
Thanks,
Achin
Quoted 4 lines
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH 6/16 ARM: b.L: generic SMP secondary bringup and hotplug support
Quoted 114 lines · Achin Gupta
Hi Santosh,
Quoted 102 lines · Santosh Shilimkar
Quoted 87 lines · Achin Gupta
Hi Santosh,
Quoted 72 lines · Santosh Shilimkar
Quoted 70 lines · Nicolas Pitre
Now that the b.L power API is in place, we can use it for SMP secondary bringup and CPU hotplug in a generic fashion.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/Makefile | 2 +- arch/arm/common/bL_platsmp.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-)create mode 100644
arch/arm/common/bL_platsmp.carch/arm/common/Makefile +1
-1@@ -15,4 +15,4 @@ obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o -obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o vlock.o +obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o bL_platsmp.ovlock.o
arch/arm/common/bL_platsmp.c new file +11
-0@@ -0,0 +1,79 @@ +/* +* linux/arch/arm/mach-vexpress/bL_platsmp.c +* +* Created by: Nicolas Pitre, November 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* Code to handle secondary CPU bringup and hotplug for the bL power API.
+ */arch/arm/common/bL_platsmp.c new file +14
-0+ +#include <linux/init.h> +#include <linux/smp.h> + +#include <asm/bL_entry.h> +#include <asm/smp_plat.h> +#include <asm/hardware/gic.h> + +static void __init simple_smp_init_cpus (void ) +{ +set_smp_cross_call (gic_raise_softirq); +} + +static int __cpuinit bL_boot_secondary(unsigned int cpu, struct
task_struct*idle)arch/arm/common/bL_platsmp.c new file +12
-0+{ +unsigned int pcpu, pcluster, ret; +extern void secondary_startup (void ); + + pcpu =cpu_logical_map (cpu) &0xff ; + pcluster = (cpu_logical_map (cpu) >>8 ) &0xff ; +pr_debug ("%s: logical CPU %d is physical CPU %d cluster %d\n" , + __func__, cpu, pcpu, pcluster); + +bL_set_entry_vector (pcpu, pcluster,NULL ); + ret =bL_cpu_power_up (pcpu, pcluster); +if (ret)Quoted 8 lines
arch/arm/common/bL_platsmp.c line 42
Quoted 4 lines
arch/arm/common/bL_platsmp.c new file +4
-0+return ret; +bL_set_entry_vector (pcpu, pcluster, secondary_startup); +gic_raise_softirq (cpumask_of (cpu),0 ); +sev ();
softirq()should be enough to break a CPU if it is in standby with wfe state. Is that additionalsev()needed here ?Quoted 3 lines
Not if the target cpu has its I & F bits disabled and that would be the case with a secondary waiting to be woken up
This is interesting since CPU is actually in standby state and this was not my understanding so far. Your statement at least contradicts the ARM ARM (B1.8.12 Wait For Interrupt)
-----------------------
The processor can remain in the WFI low-power state until it is reset, or it detects one of the following WFI wake-up
events:
? a physical IRQ interrupt, regardless of the value of the CPSR.I bit ? a physical FIQ interrupt, regardless of the value of the CPSR.F bit
----------------------------------Quoted 1 line
Are you referring to some new behavior on latest ARMv7 CPUs ?
You are abs right about the 'wfi' behaviour. I was talking about the effect of interrupts on a cpu thats in 'wfe'.
The power up process takes place in two steps. The first step involves sending an ipi which will either:
Quoted 8 lines
a. cause the power controller to bring the processor out of reset b. cause the processor to exit from wfi (most probably in the bootloader code)
The cpu then enters Linux (
bL_entry_point) and after doing any cluster setup waits in 'wfe' if its 'bL_entry_vector' has not been set as yet. The 'sev' is meant
to poke the cpu once this has been done.
Thanks for additional information. Its clear to me now.
Regards
Santosh
PATCH 6/16 ARM: b.L: generic SMP secondary bringup and hotplug support
In reply to Achin GuptaQuoted 122 lines · Achin Gupta
Hi Santosh,
Quoted 102 lines · Santosh Shilimkar
Quoted 87 lines · Achin Gupta
Hi Santosh,
Quoted 72 lines · Santosh Shilimkar
Quoted 70 lines · Nicolas Pitre
Now that the b.L power API is in place, we can use it for SMP secondary bringup and CPU hotplug in a generic fashion.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/Makefile | 2 +- arch/arm/common/bL_platsmp.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-)create mode 100644
arch/arm/common/bL_platsmp.carch/arm/common/Makefile +1
-1@@ -15,4 +15,4 @@ obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o -obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o vlock.o +obj-$(CONFIG_BIG_LITTLE) += bL_head.o bL_entry.o bL_platsmp.ovlock.o
arch/arm/common/bL_platsmp.c new file +11
-0@@ -0,0 +1,79 @@ +/* +* linux/arch/arm/mach-vexpress/bL_platsmp.c +* +* Created by: Nicolas Pitre, November 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* Code to handle secondary CPU bringup and hotplug for the bL power API.
+ */arch/arm/common/bL_platsmp.c new file +14
-0+ +#include <linux/init.h> +#include <linux/smp.h> + +#include <asm/bL_entry.h> +#include <asm/smp_plat.h> +#include <asm/hardware/gic.h> + +static void __init simple_smp_init_cpus (void ) +{ +set_smp_cross_call (gic_raise_softirq); +} + +static int __cpuinit bL_boot_secondary(unsigned int cpu, struct
task_struct*idle)arch/arm/common/bL_platsmp.c new file +12
-0+{ +unsigned int pcpu, pcluster, ret; +extern void secondary_startup (void ); + + pcpu =cpu_logical_map (cpu) &0xff ; + pcluster = (cpu_logical_map (cpu) >>8 ) &0xff ; +pr_debug ("%s: logical CPU %d is physical CPU %d cluster %d\n" , + __func__, cpu, pcpu, pcluster); + +bL_set_entry_vector (pcpu, pcluster,NULL ); + ret =bL_cpu_power_up (pcpu, pcluster); +if (ret)Quoted 8 lines
arch/arm/common/bL_platsmp.c line 42
Quoted 4 lines
arch/arm/common/bL_platsmp.c new file +4
-0+return ret; +bL_set_entry_vector (pcpu, pcluster, secondary_startup); +gic_raise_softirq (cpumask_of (cpu),0 ); +sev ();
softirq()should be enough to break a CPU if it is in standby with wfe state. Is that additionalsev()needed here ?Quoted 3 lines
Not if the target cpu has its I & F bits disabled and that would be the case with a secondary waiting to be woken up
This is interesting since CPU is actually in standby state and this was not my understanding so far. Your statement at least contradicts the ARM ARM (B1.8.12 Wait For Interrupt)
-----------------------
The processor can remain in the WFI low-power state until it is reset, or it detects one of the following WFI wake-up
events:
? a physical IRQ interrupt, regardless of the value of the CPSR.I bit ? a physical FIQ interrupt, regardless of the value of the CPSR.F bit
----------------------------------Quoted 1 line
Are you referring to some new behavior on latest ARMv7 CPUs ?
You are abs right about the 'wfi' behaviour. I was talking about the effect of interrupts on a cpu thats in 'wfe'.
The power up process takes place in two steps. The first step involves sending an ipi which will either:
a. cause the power controller to bring the processor out of reset b. cause the processor to exit from wfi (most probably in the bootloader code)
The cpu then enters Linux (
bL_entry_point) and after doing any cluster setup waits in 'wfe' if its 'bL_entry_vector' has not been set as yet. The 'sev' is meant
to poke the cpu once this has been done.
Quoted 4 lines
Its not required in this case as we have already set '
bL_entry_vector' , issued a barrier & flushed the cache line. So if the incoming cpu sees a 0 in its vector
then that would be a symptom of a different problem.
Perhaps this could be made a bit clearer by defining two helpers, bL_entry_close_gate() and bL_entry_open_gate(). The sev() is only applicable for opening the gate, and could be buried in bL_entry_open_gate().
This is pretty specialised low-level code though, so I'm not sure that the abstraction is worth it.
Cheers
---Dave
PATCH 6/16 ARM: b.L: generic SMP secondary bringup and hotplug support
In reply to Nicolas PitreQuoted 2 lines · Nicolas Pitre
Now that the b.L power API is in place, we can use it for SMP secondary bringup and CPU hotplug in a generic fashion.
[...]
Quoted 28 lines
arch/arm/common/bL_platsmp.c new file +22
-0@@ -0,0 +1,79 @@ +/* +* linux/arch/arm/mach-vexpress/bL_platsmp.c +* +* Created by: Nicolas Pitre, November 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* Code to handle secondary CPU bringup and hotplug for the bL power API. +*/ + +#include <linux/init.h> +#include <linux/smp.h> + +#include <asm/bL_entry.h> +#include <asm/smp_plat.h> +#include <asm/hardware/gic.h> + +static void __init simple_smp_init_cpus (void ) +{
arch/arm/common/bL_platsmp.c line 32
Quoted 10 lines
arch/arm/common/bL_platsmp.c new file +10
-0+set_smp_cross_call (gic_raise_softirq); +} + +static int __cpuinit bL_boot_secondary (unsigned int cpu,struct task_struct *idle) +{ +unsigned int pcpu, pcluster, ret; +extern void secondary_startup (void ); + + pcpu =cpu_logical_map (cpu) &0xff ; + pcluster = (cpu_logical_map (cpu) >>8 ) &0xff ;
Again, you can probably use Lorenzo's helpers here.
arch/arm/common/bL_platsmp.c line 36
Quoted 4 lines
arch/arm/common/bL_platsmp.c new file +4
-0+pr_debug ("%s: logical CPU %d is physical CPU %d cluster %d\n" , + __func__, cpu, pcpu, pcluster); + +bL_set_entry_vector (pcpu, pcluster,NULL );
Now that you don't have a barrier in this function, you need one here.
arch/arm/common/bL_platsmp.c line 39
Quoted 3 lines
arch/arm/common/bL_platsmp.c new file +3
-0+ ret =bL_cpu_power_up (pcpu, pcluster); +if (ret) +return ret;
and here, although I confess to not understanding why you write NULL the first time.
arch/arm/common/bL_platsmp.c line 42
Quoted 3 lines
arch/arm/common/bL_platsmp.c new file +3
-0+bL_set_entry_vector (pcpu, pcluster, secondary_startup); +gic_raise_softirq (cpumask_of (cpu),0 ); +sev ();
This relise on the event register being able to be set if the target is in a low-power (wfi) state. I'd feel safer with a dsb before the sev...
Will
PATCH 6/16 ARM: b.L: generic SMP secondary bringup and hotplug support
Quoted 38 lines · Will Deacon
Quoted 2 lines · Nicolas Pitre
Now that the b.L power API is in place, we can use it for SMP secondary bringup and CPU hotplug in a generic fashion.
[...]
Quoted 32 lines
arch/arm/common/bL_platsmp.c new file +26
-0@@ -0,0 +1,79 @@ +/* +* linux/arch/arm/mach-vexpress/bL_platsmp.c +* +* Created by: Nicolas Pitre, November 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* Code to handle secondary CPU bringup and hotplug for the bL power API. +*/ + +#include <linux/init.h> +#include <linux/smp.h> + +#include <asm/bL_entry.h> +#include <asm/smp_plat.h> +#include <asm/hardware/gic.h> + +static void __init simple_smp_init_cpus (void ) +{ +set_smp_cross_call (gic_raise_softirq); +} + +static int __cpuinit bL_boot_secondary (unsigned int cpu,struct task_struct *idle)
Quoted 8 lines
arch/arm/common/bL_platsmp.c line 32
Quoted 6 lines
arch/arm/common/bL_platsmp.c new file +6
-0+{ +unsigned int pcpu, pcluster, ret; +extern void secondary_startup (void ); + + pcpu =cpu_logical_map (cpu) &0xff ; + pcluster = (cpu_logical_map (cpu) >>8 ) &0xff ;Again, you can probably use Lorenzo's helpers here.
Yes, that goes for the whole series.
Quoted 6 lines
arch/arm/common/bL_platsmp.c line 36
Quoted 4 lines
arch/arm/common/bL_platsmp.c new file +4
-0+pr_debug ("%s: logical CPU %d is physical CPU %d cluster %d\n" , + __func__, cpu, pcpu, pcluster); + +bL_set_entry_vector (pcpu, pcluster,NULL );Now that you don't have a barrier in this function, you need one here.
Hmmm... Why?
Quoted 6 lines
arch/arm/common/bL_platsmp.c line 39
Quoted 3 lines
arch/arm/common/bL_platsmp.c new file +3
-0+ ret =bL_cpu_power_up (pcpu, pcluster); +if (ret) +return ret;and here, although I confess to not understanding why you write NULL the first time.
If for some reasons the bL_cpu_power_up() call fails, I don't want this CPU to suddenly decide to enter the kernel if it wakes up at a later time when secondary_startup is not ready to deal with it anymore.
Quoted 6 lines
arch/arm/common/bL_platsmp.c line 42
Quoted 3 lines
arch/arm/common/bL_platsmp.c new file +3
-0+bL_set_entry_vector (pcpu, pcluster, secondary_startup); +gic_raise_softirq (cpumask_of (cpu),0 ); +sev ();This relise on the event register being able to be set if the target is in a low-power (wfi) state. I'd feel safer with a dsb before the sev...
Sure.
Nicolas
PATCH 6/16 ARM: b.L: generic SMP secondary bringup and hotplug support
Quoted 51 lines · Nicolas Pitre
Quoted 38 lines · Will Deacon
Quoted 2 lines · Nicolas Pitre
Now that the b.L power API is in place, we can use it for SMP secondary bringup and CPU hotplug in a generic fashion.
[...]
Quoted 32 lines
arch/arm/common/bL_platsmp.c new file +26
-0@@ -0,0 +1,79 @@ +/* +* linux/arch/arm/mach-vexpress/bL_platsmp.c +* +* Created by: Nicolas Pitre, November 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* Code to handle secondary CPU bringup and hotplug for the bL power API. +*/ + +#include <linux/init.h> +#include <linux/smp.h> + +#include <asm/bL_entry.h> +#include <asm/smp_plat.h> +#include <asm/hardware/gic.h> + +static void __init simple_smp_init_cpus (void ) +{ +set_smp_cross_call (gic_raise_softirq); +} + +static int __cpuinit bL_boot_secondary (unsigned int cpu,struct task_struct *idle)Quoted 8 lines
arch/arm/common/bL_platsmp.c line 32
Quoted 6 lines
arch/arm/common/bL_platsmp.c new file +6
-0+{ +unsigned int pcpu, pcluster, ret; +extern void secondary_startup (void ); + + pcpu =cpu_logical_map (cpu) &0xff ; + pcluster = (cpu_logical_map (cpu) >>8 ) &0xff ;Again, you can probably use Lorenzo's helpers here.
Yes, that goes for the whole series.
Quoted 8 lines
Quoted 6 lines
arch/arm/common/bL_platsmp.c line 36
Quoted 4 lines
arch/arm/common/bL_platsmp.c new file +4
-0+pr_debug ("%s: logical CPU %d is physical CPU %d cluster %d\n" , + __func__, cpu, pcpu, pcluster); + +bL_set_entry_vector (pcpu, pcluster,NULL );Now that you don't have a barrier in this function, you need one here.
Hmmm... Why?
In effect, we are entering a critical section here: that's precisely why we close the gate.
We need a barrier after bL_set_entry_vector() to make sure that no operations from the critical section leak outside from the perspective of the target CPU.
Similarly, we need a barrier before bL_set_entry_vector() when opening the gate.
The corresponding barrier required in bL_head.S at bL_entry_gated is added by my recent barriers and tidyups series.
Closing and opening the gate are a bit like taking and releasing a lock -- which is one reason for having simple wrapper functions to make these roles more obvious.
Quoted 8 lines
Quoted 7 lines
arch/arm/common/bL_platsmp.c line 39
Quoted 4 lines
arch/arm/common/bL_platsmp.c new file +3
-0+ ret =bL_cpu_power_up (pcpu, pcluster); +if (ret) +return ret;and here, although I confess to not understanding why you write NULL the first time.
Quoted 10 lines
If for some reasons the
bL_cpu_power_up()call fails, I don't want this CPU to suddenly decide to enter the kernel if it wakes up at a later time whensecondary_startupis not ready to deal with it anymore.Quoted 6 lines
arch/arm/common/bL_platsmp.c line 42
Quoted 3 lines
arch/arm/common/bL_platsmp.c new file +3
-0+bL_set_entry_vector (pcpu, pcluster, secondary_startup); +gic_raise_softirq (cpumask_of (cpu),0 ); +sev ();This relise on the event register being able to be set if the target is in a low-power (wfi) state. I'd feel safer with a dsb before the sev...
The sev() signals the update to the entry vector, which has already been DSB'd by the flushing in bL_set_entry_vector().
Also, the relative order of gic_raise_softirq() and sev() here is not important, provided they both follow bL_set_entry_vector().
There are no circumstances under which we could know whether the IRQ or SEV arrives first at the destination CPU anyway. A DSB is insufficient since the store may still not have arrived at the GIC; but even doing a readback from the GIC isn't enough, because the relationships and relative speed of the underlying interrupt and SEV signalling mechanisms are not architecturally visible.
The crucial thing is that the SEV does not arrive before the destination CPU has observed the modification to the entry vector -- that could cause the target CPU to stall in WFE.
If the CPU powers up after missing the SEV, that's fine, because the observability of the non-NULL entry vector is guaranteed by the flushing which precedes gic_raise_softirq(). SEV is only important if the CPU powers up early, observes a NULL entry vector and goes into WFE.
Because this code only applies with the multiprocessing extensions, we know that the writes associated with gic_raise_softirq() will drain and take effect eventually, but we don't care when. We can't know the results for sure until the target CPU re-enters the kernel.
Cheers
---Dave
PATCH 7/16 ARM: bL_platsmp.c: close the kernel entry gate before hot-unplugging a CPU
In reply to Nicolas PitreIf for whatever reason a CPU is unexpectedly awaken, it shouldn't re-enter the kernel by using whatever entry vector that might have been set by a previous operation.
--- arch/arm/common/bL_platsmp.c | 5 +++++ 1 file changed, 5 insertions(+)
arch/arm/common/bL_platsmp.c +5 -0
@@ -63,6 +63,11 @@ static int bL_cpu_disable(unsigned int cpu)static void __ref bL_cpu_die (unsigned int cpu) { +unsigned int mpidr, pcpu, pcluster; + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); + pcpu = mpidr &0xff ; + pcluster = (mpidr >>8 ) &0xff ; +bL_set_entry_vector (pcpu, pcluster,NULL );bL_cpu_power_down (); }
Signature · 1 line
1.8.0
PATCH 7/16 ARM: bL_platsmp.c: close the kernel entry gate before hot-unplugging a CPU
Quoted 14 lines · Nicolas Pitre
If for whatever reason a CPU is unexpectedly awaken, it shouldn't re-enter the kernel by using whatever entry vector that might have been set by a previous operation.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 5 +++++ 1 file changed, 5 insertions(+)arch/arm/common/bL_platsmp.c +0
-0
arch/arm/common/bL_platsmp.c static int bL_cpu_disable(unsigned int cpu) · line 69
Quoted 7 lines
arch/arm/common/bL_platsmp.c +4
-0@@ -63,6 +63,11 @@ static int bL_cpu_disable(unsigned int cpu)static void __ref bL_cpu_die (unsigned int cpu) { +unsigned int mpidr, pcpu, pcluster; + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); + pcpu = mpidr &0xff ; + pcluster = (mpidr >>8 ) &0xff ;
Usual comment about helper functions :)
arch/arm/common/bL_platsmp.c static int bL_cpu_disable(unsigned int cpu) · line 70
Quoted 1 line
arch/arm/common/bL_platsmp.c +1
-0+bL_set_entry_vector (pcpu, pcluster,NULL );
Similar to the power_on story, you need a barrier here (unless you change your platform_ops API to require barriers).
arch/arm/common/bL_platsmp.c static int bL_cpu_disable(unsigned int cpu) · line 71
Quoted 1 line
arch/arm/common/bL_platsmp.c +0
-0bL_cpu_power_down ();
Will
PATCH 7/16 ARM: bL_platsmp.c: close the kernel entry gate before hot-unplugging a CPU
Quoted 25 lines · Will Deacon
Quoted 14 lines · Nicolas Pitre
If for whatever reason a CPU is unexpectedly awaken, it shouldn't re-enter the kernel by using whatever entry vector that might have been set by a previous operation.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 5 +++++ 1 file changed, 5 insertions(+)arch/arm/common/bL_platsmp.c +0
-0arch/arm/common/bL_platsmp.c static int bL_cpu_disable(unsigned int cpu) · line 69
Quoted 7 lines
arch/arm/common/bL_platsmp.c +4
-0@@ -63,6 +63,11 @@ static int bL_cpu_disable(unsigned int cpu)static void __ref bL_cpu_die (unsigned int cpu) { +unsigned int mpidr, pcpu, pcluster; + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); + pcpu = mpidr &0xff ; + pcluster = (mpidr >>8 ) &0xff ;Usual comment about helper functions :)
Quoted 4 lines
arch/arm/common/bL_platsmp.c static int bL_cpu_disable(unsigned int cpu) · line 70
Quoted 1 line
arch/arm/common/bL_platsmp.c +1
-0+bL_set_entry_vector (pcpu, pcluster,NULL );Similar to the
power_onstory, you need a barrier here (unless you change yourplatform_opsAPI to require barriers).
The bL_set_entry_vector() includes a cache flush which itself has a DSB. Hence my previous interrogation.
Nicolas
PATCH 7/16 ARM: bL_platsmp.c: close the kernel entry gate before hot-unplugging a CPU
Quoted 27 lines · Nicolas Pitre
Quoted 25 lines · Will Deacon
Quoted 14 lines · Nicolas Pitre
If for whatever reason a CPU is unexpectedly awaken, it shouldn't re-enter the kernel by using whatever entry vector that might have been set by a previous operation.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 5 +++++ 1 file changed, 5 insertions(+)arch/arm/common/bL_platsmp.c +0
-0arch/arm/common/bL_platsmp.c static int bL_cpu_disable(unsigned int cpu) · line 69
Quoted 7 lines
arch/arm/common/bL_platsmp.c +4
-0@@ -63,6 +63,11 @@ static int bL_cpu_disable(unsigned int cpu)static void __ref bL_cpu_die (unsigned int cpu) { +unsigned int mpidr, pcpu, pcluster; + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); + pcpu = mpidr &0xff ; + pcluster = (mpidr >>8 ) &0xff ;Usual comment about helper functions :)
Quoted 7 lines
Quoted 4 lines
arch/arm/common/bL_platsmp.c static int bL_cpu_disable(unsigned int cpu) · line 70
Quoted 1 line
arch/arm/common/bL_platsmp.c +1
-0+bL_set_entry_vector (pcpu, pcluster,NULL );Similar to the
power_onstory, you need a barrier here (unless you change yourplatform_opsAPI to require barriers).The
bL_set_entry_vector()includes a cache flush which itself has a DSB. Hence my previous interrogation.
For L1, sure, we always have the dsb for v7. However, for the outer-cache we only have a dsb by virtue of a spin_unlock in l2x0.c... it seems a bit risky to rely on that for ordering your entry_vector write with the power_on.
I think the best bet is to put a barrier in power_on, before invoking the platform_ops function and similarly for power_off.
What do you reckon?
Will
PATCH 7/16 ARM: bL_platsmp.c: close the kernel entry gate before hot-unplugging a CPU
Quoted 33 lines · Will Deacon
Quoted 32 lines · Nicolas Pitre
Quoted 29 lines · Will Deacon
Quoted 14 lines · Nicolas Pitre
If for whatever reason a CPU is unexpectedly awaken, it shouldn't re-enter the kernel by using whatever entry vector that might have been set by a previous operation.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 5 +++++ 1 file changed, 5 insertions(+)arch/arm/common/bL_platsmp.c +0
-0arch/arm/common/bL_platsmp.c static int bL_cpu_disable(unsigned int cpu) · line 69
Quoted 7 lines
arch/arm/common/bL_platsmp.c +4
-0@@ -63,6 +63,11 @@ static int bL_cpu_disable(unsigned int cpu)static void __ref bL_cpu_die (unsigned int cpu) { +unsigned int mpidr, pcpu, pcluster; + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); + pcpu = mpidr &0xff ; + pcluster = (mpidr >>8 ) &0xff ;Usual comment about helper functions :)
arch/arm/common/bL_platsmp.c static int bL_cpu_disable(unsigned int cpu) · line 70
Quoted 1 line
arch/arm/common/bL_platsmp.c +1
-0+bL_set_entry_vector (pcpu, pcluster,NULL );Similar to the
power_onstory, you need a barrier here (unless you change yourplatform_opsAPI to require barriers).
Quoted 6 lines
Quoted 2 lines
The
bL_set_entry_vector()includes a cache flush which itself has a DSB. Hence my previous interrogation.For L1, sure, we always have the dsb for v7. However, for the outer-cache we only have a dsb by virtue of a
spin_unlockinl2x0.c... it seems a bit risky to rely on that for ordering yourentry_vectorwrite with thepower_on.
I was discussing this with Dave earlier, I think we need to fix the outer-cache functions even for the UP case to include a barrier (for PL310 actually we may need to read a register as the cache_wait is a no-op). We assume that cache functions (both inner and outer) fully complete the operation before returning and there is no additional need for barriers.
Signature · 1 line
Catalin
PATCH 7/16 ARM: bL_platsmp.c: close the kernel entry gate before hot-unplugging a CPU
In reply to Will DeaconQuoted 36 lines · Will Deacon
Quoted 27 lines · Nicolas Pitre
Quoted 25 lines · Will Deacon
Quoted 14 lines · Nicolas Pitre
If for whatever reason a CPU is unexpectedly awaken, it shouldn't re-enter the kernel by using whatever entry vector that might have been set by a previous operation.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 5 +++++ 1 file changed, 5 insertions(+)arch/arm/common/bL_platsmp.c +0
-0arch/arm/common/bL_platsmp.c static int bL_cpu_disable(unsigned int cpu) · line 69
Quoted 7 lines
arch/arm/common/bL_platsmp.c +4
-0@@ -63,6 +63,11 @@ static int bL_cpu_disable(unsigned int cpu)static void __ref bL_cpu_die (unsigned int cpu) { +unsigned int mpidr, pcpu, pcluster; + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); + pcpu = mpidr &0xff ; + pcluster = (mpidr >>8 ) &0xff ;Usual comment about helper functions :)
Quoted 7 lines
Quoted 4 lines
arch/arm/common/bL_platsmp.c static int bL_cpu_disable(unsigned int cpu) · line 70
Quoted 1 line
arch/arm/common/bL_platsmp.c +1
-0+bL_set_entry_vector (pcpu, pcluster,NULL );Similar to the
power_onstory, you need a barrier here (unless you change yourplatform_opsAPI to require barriers).The
bL_set_entry_vector()includes a cache flush which itself has a DSB. Hence my previous interrogation.
Quoted 8 lines
For L1, sure, we always have the dsb for v7. However, for the outer-cache we only have a dsb by virtue of a
spin_unlockinl2x0.c... it seems a bit risky to rely on that for ordering yourentry_vectorwrite with thepower_on.I think the best bet is to put a barrier in
power_on, before invoking theplatform_opsfunction and similarly forpower_off.What do you reckon?
I much prefer adding barriers inside the API when they are needed for proper execution of the API intent. So if I call bL_set_entry_vector(), I trust that by the time it returns the vector is indeed set and ready for use by other processors.
The same could be said about the outer cache ops. If a DSB is needed for their intent to be valid, then why isn't this DSB always implied by the corresponding cache op calls? And as you say, there is already one implied by the spinlock used there, so that is not if things would change much in practice.
Nicolas
PATCH 7/16 ARM: bL_platsmp.c: close the kernel entry gate before hot-unplugging a CPU
Quoted 19 lines · Nicolas Pitre
Quoted 4 lines · Will Deacon
Quoted 2 lines · Nicolas Pitre
The
bL_set_entry_vector()includes a cache flush which itself has a DSB. Hence my previous interrogation.Quoted 8 lines
For L1, sure, we always have the dsb for v7. However, for the outer-cache we only have a dsb by virtue of a
spin_unlockinl2x0.c... it seems a bit risky to rely on that for ordering yourentry_vectorwrite with thepower_on.I think the best bet is to put a barrier in
power_on, before invoking theplatform_opsfunction and similarly forpower_off.What do you reckon?
I much prefer adding barriers inside the API when they are needed for proper execution of the API intent. So if I call
bL_set_entry_vector(), I trust that by the time it returns the vector is indeed set and ready for use by other processors.
Quoted 5 lines
The same could be said about the outer cache ops. If a DSB is needed for their intent to be valid, then why isn't this DSB always implied by the corresponding cache op calls? And as you say, there is already one implied by the spinlock used there, so that is not if things would change much in practice.
Ok, so we can fix the outer_cache functions as suggested by Catalin. That still leaves the GIC CPU interface problem in the later patch, which uses a writel_relaxed to disable the CPU interface, so I suppose we can just put a dsb at the end of gic_cpu_if_down().
Will
PATCH 7/16 ARM: bL_platsmp.c: close the kernel entry gate before hot-unplugging a CPU
In reply to Nicolas PitreQuoted 3 lines · Nicolas Pitre
The same could be said about the outer cache ops. If a DSB is needed for their intent to be valid, then why isn't this DSB always implied by the corresponding cache op calls?
Hmm, just been thinking about this.
The L2x0 calls do contain a DSB but it's not obvious. They hold a raw spinlock, and when that spinlock is dropped, we issue a dsb and sev instruction.
Whether the other L2 implementations do this or not I'm not sure - but the above is a requirement of the spinlock implementation, and it just happens to provide the right behaviour for L2x0.
But... we _probably_ don't want to impose that down at the L2 cache level of things - at least not for DMA ops, particular for the sanity of the scatter-list operating operations. We really want to avoid doing one DSB per scatterlist entry, doing one DSB per scatterlist operation instead.
That does affect how the L2 cache API gets used - maybe we want to separate out the DMA stuff from the other users so that we can have dsbs in that path for non-DMA users.
Thoughts?
PATCH 7/16 ARM: bL_platsmp.c: close the kernel entry gate before hot-unplugging a CPU
Quoted 21 lines · Russell King - ARM Linux
Quoted 3 lines · Nicolas Pitre
The same could be said about the outer cache ops. If a DSB is needed for their intent to be valid, then why isn't this DSB always implied by the corresponding cache op calls?
Hmm, just been thinking about this.
The L2x0 calls do contain a DSB but it's not obvious. They hold a raw spinlock, and when that spinlock is dropped, we issue a dsb and sev instruction.
Whether the other L2 implementations do this or not I'm not sure - but the above is a requirement of the spinlock implementation, and it just happens to provide the right behaviour for L2x0.
But... we _probably_ don't want to impose that down at the L2 cache level of things - at least not for DMA ops, particular for the sanity of the scatter-list operating operations. We really want to avoid doing one DSB per scatterlist entry, doing one DSB per scatterlist operation instead.
Quoted 5 lines
That does affect how the L2 cache API gets used - maybe we want to separate out the DMA stuff from the other users so that we can have dsbs in that path for non-DMA users.
Thoughts?
The dsb or its intended effect could be confined to outer_sync() and then cache_sync() removed from l2x0_flush_range(). That would allow the sync to be applied when appropriate. However that suffers the same API intent mismatch I was talking about.
Maybe adding some asynchronous methods to outer_cache (that could default to the synchronous calls) where the name of the function clearly implies a posted operation would be a better solution. In that case the effect of the operation would be assumed complete only after a terminating outer_sync().
Nicolas
PATCH 7/16 ARM: bL_platsmp.c: close the kernel entry gate before hot-unplugging a CPU
In reply to Russell King - ARM LinuxQuoted 21 lines · Russell King - ARM Linux
Quoted 3 lines · Nicolas Pitre
The same could be said about the outer cache ops. If a DSB is needed for their intent to be valid, then why isn't this DSB always implied by the corresponding cache op calls?
Hmm, just been thinking about this.
The L2x0 calls do contain a DSB but it's not obvious. They hold a raw spinlock, and when that spinlock is dropped, we issue a dsb and sev instruction.
Whether the other L2 implementations do this or not I'm not sure - but the above is a requirement of the spinlock implementation, and it just happens to provide the right behaviour for L2x0.
But... we _probably_ don't want to impose that down at the L2 cache level of things - at least not for DMA ops, particular for the sanity of the scatter-list operating operations. We really want to avoid doing one DSB per scatterlist entry, doing one DSB per scatterlist operation instead.
Quoted 5 lines
That does affect how the L2 cache API gets used - maybe we want to separate out the DMA stuff from the other users so that we can have dsbs in that path for non-DMA users.
Thoughts?
Perhaps the existing functions could be renamed to things like:
outer_XXX_flush_range()outer_XXX_sync()
Where XXX is something like "batch" or "background". Optionally these could be declared somewhere separate to discourage non-DMA code from using them. Other code could still want to do batches of outer cache operations efficiently, but I guess DMA is the main user.
Then we could provide simple non-background wrappers which also do the appropriate CPU-side synchronisation, and provide the familiar interface for that.
It might be less invasive to rename the new functions instead of the old ones. It partly depends on what proportion of existing uses of these functions are incorrect (i.e., assume full synchronisation).
Cheers
---Dave
PATCH 7/16 ARM: bL_platsmp.c: close the kernel entry gate before hot-unplugging a CPU
Quoted 30 lines · Dave Martin
Quoted 21 lines · Russell King - ARM Linux
Quoted 3 lines · Nicolas Pitre
The same could be said about the outer cache ops. If a DSB is needed for their intent to be valid, then why isn't this DSB always implied by the corresponding cache op calls?
Hmm, just been thinking about this.
The L2x0 calls do contain a DSB but it's not obvious. They hold a raw spinlock, and when that spinlock is dropped, we issue a dsb and sev instruction.
Whether the other L2 implementations do this or not I'm not sure - but the above is a requirement of the spinlock implementation, and it just happens to provide the right behaviour for L2x0.
But... we _probably_ don't want to impose that down at the L2 cache level of things - at least not for DMA ops, particular for the sanity of the scatter-list operating operations. We really want to avoid doing one DSB per scatterlist entry, doing one DSB per scatterlist operation instead.
Quoted 5 lines
That does affect how the L2 cache API gets used - maybe we want to separate out the DMA stuff from the other users so that we can have dsbs in that path for non-DMA users.
Thoughts?
Perhaps the existing functions could be renamed to things like:
Quoted 7 lines
outer_XXX_flush_range()outer_XXX_sync()Where XXX is something like "batch" or "background". Optionally these could be declared somewhere separate to discourage non-DMA code from using them. Other code could still want to do batches of outer cache operations efficiently, but I guess DMA is the main user.
There can be some confusion with using 'background' name because both PL310 and L2X0 have background operations (the former only for the set/way ops). But in software you need to ensure the completion of such operations otherwise the L2 controller behaviour can be unpredictable.
So you just want to drop the barriers (outer_sync and dsb), maybe could use the 'relaxed' suffix which matches the I/O accessors.
Signature · 1 line
Catalin
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
In reply to Nicolas PitreOtherwise there might be some interrupts or IPIs becoming pending and the CPU will not enter low power mode when doing a WFI. The effect of this is a CPU that loops back into the kernel, go through the first man election, signals itself as alive, and prevent the cluster from being shut down.
This could benefit from a better solution.
--- arch/arm/common/bL_platsmp.c | 1 + arch/arm/common/gic.c | 6 ++++++ arch/arm/include/asm/hardware/gic.h | 2 ++ 3 files changed, 9 insertions(+)
arch/arm/common/bL_platsmp.c +1 -0
@@ -68,6 +68,7 @@ static void __ref bL_cpu_die(unsigned int cpu) pcpu = mpidr &0xff ; pcluster = (mpidr >>8 ) &0xff ;bL_set_entry_vector (pcpu, pcluster,NULL ); +gic_cpu_if_down ();bL_cpu_power_down (); }
arch/arm/common/gic.c +6 -0
@@ -428,6 +428,12 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)writel_relaxed (1 , base +GIC_CPU_CTRL ); } +void gic_cpu_if_down (void ) +{ +void __iomem *cpu_base =gic_data_cpu_base (&gic_data[0 ]); +writel_relaxed (0 , cpu_base +GIC_CPU_CTRL ); +} +#ifdef CONFIG_CPU_PM /* * Saves the GIC distributor registers during suspend or idle. Must be called
arch/arm/include/asm/hardware/gic.h +2 -0
@@ -46,6 +46,8 @@ void gic_handle_irq(struct pt_regs *regs);void gic_cascade_irq (unsigned int gic_nr,unsigned int irq);void gic_raise_softirq (const struct cpumask *mask,unsigned int irq); +void gic_cpu_if_down (void ); +static inline void gic_init (unsigned int nr,int start,void __iomem *dist ,void __iomem *cpu) {
Signature · 1 line
1.8.0
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
Quoted 20 lines · Nicolas Pitre
Otherwise there might be some interrupts or IPIs becoming pending and the CPU will not enter low power mode when doing a WFI. The effect of this is a CPU that loops back into the kernel, go through the first man election, signals itself as alive, and prevent the cluster from being shut down.
This could benefit from a better solution.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 1 + arch/arm/common/gic.c | 6 ++++++ arch/arm/include/asm/hardware/gic.h | 2 ++ 3 files changed, 9 insertions(+)arch/arm/common/bL_platsmp.c +0
-0
arch/arm/common/bL_platsmp.c static void __ref bL_cpu_die(unsigned int cpu) · line 71
Quoted 4 lines
arch/arm/common/bL_platsmp.c +1
-0@@ -68,6 +68,7 @@ static void __ref bL_cpu_die(unsigned int cpu) pcpu = mpidr &0xff ; pcluster = (mpidr >>8 ) &0xff ;bL_set_entry_vector (pcpu, pcluster,NULL ); +gic_cpu_if_down ();
So for a case where CPU still don't power down for some reason even after CPU interface is disabled, can not listen to and SGI or PPI. Not sure if this happens on big.LITTLE but i have seen one such issue on Cortex-A9 based SOC.
Regards
Santosh
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
Quoted 16 lines · Santosh Shilimkar
Quoted 15 lines · Nicolas Pitre
Otherwise there might be some interrupts or IPIs becoming pending and the CPU will not enter low power mode when doing a WFI. The effect of this is a CPU that loops back into the kernel, go through the first man election, signals itself as alive, and prevent the cluster from being shut down.
This could benefit from a better solution.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 1 + arch/arm/common/gic.c | 6 ++++++ arch/arm/include/asm/hardware/gic.h | 2 ++ 3 files changed, 9 insertions(+)
Quoted 14 lines
arch/arm/common/bL_platsmp.c static void __ref bL_cpu_die(unsigned int cpu) · line 71
Quoted 9 lines
arch/arm/common/bL_platsmp.c +1
-0@@ -68,6 +68,7 @@ static void __ref bL_cpu_die(unsigned int cpu) pcpu = mpidr &0xff ; pcluster = (mpidr >>8 ) &0xff ;bL_set_entry_vector (pcpu, pcluster,NULL ); +gic_cpu_if_down ();So for a case where CPU still don't power down for some reason even after CPU interface is disabled, can not listen to and SGI or PPI. Not sure if this happens on big.LITTLE but i have seen one such issue on Cortex-A9 based SOC.
Here the problem was the reverse i.e. a CPU wouldn't go down because some pending SGIs prevented that.
Nicolas
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
Quoted 33 lines · Nicolas Pitre
Quoted 16 lines · Santosh Shilimkar
Quoted 15 lines · Nicolas Pitre
Otherwise there might be some interrupts or IPIs becoming pending and the CPU will not enter low power mode when doing a WFI. The effect of this is a CPU that loops back into the kernel, go through the first man election, signals itself as alive, and prevent the cluster from being shut down.
This could benefit from a better solution.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 1 + arch/arm/common/gic.c | 6 ++++++ arch/arm/include/asm/hardware/gic.h | 2 ++ 3 files changed, 9 insertions(+)Quoted 14 lines
arch/arm/common/bL_platsmp.c static void __ref bL_cpu_die(unsigned int cpu) · line 71
Quoted 9 lines
arch/arm/common/bL_platsmp.c +1
-0@@ -68,6 +68,7 @@ static void __ref bL_cpu_die(unsigned int cpu) pcpu = mpidr &0xff ; pcluster = (mpidr >>8 ) &0xff ;bL_set_entry_vector (pcpu, pcluster,NULL ); +gic_cpu_if_down ();So for a case where CPU still don't power down for some reason even after CPU interface is disabled, can not listen to and SGI or PPI. Not sure if this happens on big.LITTLE but i have seen one such issue on Cortex-A9 based SOC.
Quoted 3 lines
Here the problem was the reverse i.e. a CPU wouldn't go down because some pending SGIs prevented that.
I understood that part. What I was saying is, with CPU IF disabled and if CPU doesn't enter into the intended low power state and if the wakeup mechanism on that CPU is SGI/SPI, CPU may never wakeup and can lead to dead lock. I have seen this scenario on OMAP especially in CPUidle path. It may not be relevant for switcher considering, you almost force CPU to enter to low power state :-)
Regards,
Santosh
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
Quoted 35 lines · Santosh Shilimkar
Quoted 34 lines · Nicolas Pitre
Quoted 31 lines · Santosh Shilimkar
Quoted 21 lines · Nicolas Pitre
Otherwise there might be some interrupts or IPIs becoming pending and the
CPU will not enter low power mode when doing a WFI. The effect of this is a CPU that loops back into the kernel, go through the first man election, signals itself as alive, and prevent the cluster from being shut down.This could benefit from a better solution.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 1 + arch/arm/common/gic.c | 6 ++++++ arch/arm/include/asm/hardware/gic.h | 2 ++ 3 files changed, 9 insertions(+)arch/arm/common/bL_platsmp.c +0
-0arch/arm/common/bL_platsmp.c static void __ref bL_cpu_die(unsigned int cpu) · line 71
Quoted 4 lines
arch/arm/common/bL_platsmp.c +1
-0@@ -68,6 +68,7 @@ static void __ref bL_cpu_die(unsigned int cpu) pcpu = mpidr &0xff ; pcluster = (mpidr >>8 ) &0xff ;bL_set_entry_vector (pcpu, pcluster,NULL ); +gic_cpu_if_down ();So for a case where CPU still don't power down for some reason even after CPU interface is disabled, can not listen to and SGI or PPI. Not sure if this happens on big.LITTLE but i have seen one such issue on Cortex-A9 based SOC.
Quoted 7 lines
Quoted 3 lines
Here the problem was the reverse i.e. a CPU wouldn't go down because some pending SGIs prevented that.
I understood that part. What I was saying is, with CPU IF disabled and if CPU doesn't enter into the intended low power state and if the wakeup mechanism on that CPU is SGI/SPI, CPU may never wakeup and can lead to dead lock. I have seen this scenario on OMAP especially in CPUidle path.
Obviously, on the CPU idle path, you should not turn off the GIC interface as this might lose the ability to wake the CPU up with a pending interrupt, if your system is so configured.
Here this is the CPU hot unplug path and we don't want the CPU to be awaken at all until we explicitly do something to wake it back up.
However, in theory, all interrupts should have been migrated away from this CPU, so there shouldn't be any need for this. I should revisit the test that led me to create this patch.
Quoted 2 lines
It may not be relevant for switcher considering, you almost force CPU to enter to low power state :-)
The switcher doesn't use cpu_die() but calls into bL_cpu_power_down() directly.
Nicolas
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
Quoted 45 lines · Nicolas Pitre
Quoted 35 lines · Santosh Shilimkar
Quoted 34 lines · Nicolas Pitre
Quoted 31 lines · Santosh Shilimkar
Quoted 21 lines · Nicolas Pitre
Otherwise there might be some interrupts or IPIs becoming pending and the
CPU will not enter low power mode when doing a WFI. The effect of this is a CPU that loops back into the kernel, go through the first man election, signals itself as alive, and prevent the cluster from being shut down.This could benefit from a better solution.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 1 + arch/arm/common/gic.c | 6 ++++++ arch/arm/include/asm/hardware/gic.h | 2 ++ 3 files changed, 9 insertions(+)arch/arm/common/bL_platsmp.c +0
-0arch/arm/common/bL_platsmp.c static void __ref bL_cpu_die(unsigned int cpu) · line 71
Quoted 4 lines
arch/arm/common/bL_platsmp.c +1
-0@@ -68,6 +68,7 @@ static void __ref bL_cpu_die(unsigned int cpu) pcpu = mpidr &0xff ; pcluster = (mpidr >>8 ) &0xff ;bL_set_entry_vector (pcpu, pcluster,NULL ); +gic_cpu_if_down ();So for a case where CPU still don't power down for some reason even after CPU interface is disabled, can not listen to and SGI or PPI. Not sure if this happens on big.LITTLE but i have seen one such issue on Cortex-A9 based SOC.
Quoted 7 lines
Quoted 3 lines
Here the problem was the reverse i.e. a CPU wouldn't go down because some pending SGIs prevented that.
I understood that part. What I was saying is, with CPU IF disabled and if CPU doesn't enter into the intended low power state and if the wakeup mechanism on that CPU is SGI/SPI, CPU may never wakeup and can lead to dead lock. I have seen this scenario on OMAP especially in CPUidle path.
Quoted 7 lines
Obviously, on the CPU idle path, you should not turn off the GIC interface as this might lose the ability to wake the CPU up with a pending interrupt, if your system is so configured.
Here this is the CPU hot unplug path and we don't want the CPU to be awaken at all until we explicitly do something to wake it back up.
I see.
Quoted 4 lines
However, in theory, all interrupts should have been migrated away from this CPU, so there shouldn't be any need for this. I should revisit the test that led me to create this patch.
Thats right from hot-plug path and SPI are concerned. But SGI/PPI can still wakeup CPU and there is no migration as such since they are local to that
Quoted 6 lines
Quoted 2 lines
It may not be relevant for switcher considering, you almost force CPU to enter to low power state :-)
The switcher doesn't use
cpu_die()but calls intobL_cpu_power_down()directly.
Good to know that.
Regards,
Santosh
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
In reply to Nicolas PitreQuoted 40 lines · Nicolas Pitre
Quoted 38 lines · Santosh Shilimkar
Quoted 37 lines · Nicolas Pitre
Quoted 17 lines · Santosh Shilimkar
Quoted 16 lines · Nicolas Pitre
Otherwise there might be some interrupts or IPIs becoming pending and the
CPU will not enter low power mode when doing a WFI. The effect of this is a CPU that loops back into the kernel, go through the first man election, signals itself as alive, and prevent the cluster from being shut down.This could benefit from a better solution.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 1 + arch/arm/common/gic.c | 6 ++++++ arch/arm/include/asm/hardware/gic.h | 2 ++ 3 files changed, 9 insertions(+)Quoted 14 lines
arch/arm/common/bL_platsmp.c static void __ref bL_cpu_die(unsigned int cpu) · line 71
Quoted 9 lines
arch/arm/common/bL_platsmp.c +1
-0@@ -68,6 +68,7 @@ static void __ref bL_cpu_die(unsigned int cpu) pcpu = mpidr &0xff ; pcluster = (mpidr >>8 ) &0xff ;bL_set_entry_vector (pcpu, pcluster,NULL ); +gic_cpu_if_down ();So for a case where CPU still don't power down for some reason even after CPU interface is disabled, can not listen to and SGI or PPI. Not sure if this happens on big.LITTLE but i have seen one such issue on Cortex-A9 based SOC.
Here the problem was the reverse i.e. a CPU wouldn't go down because some pending SGIs prevented that.
Quoted 8 lines
Quoted 4 lines
I understood that part. What I was saying is, with CPU IF disabled and if CPU doesn't enter into the intended low power state and if the wakeup mechanism on that CPU is SGI/SPI, CPU may never wakeup and can lead to dead lock. I have seen this scenario on OMAP especially in CPUidle path.
Obviously, on the CPU idle path, you should not turn off the GIC interface as this might lose the ability to wake the CPU up with a pending interrupt, if your system is so configured.
That's platform specific. On TC2 turning GIC CPU IF off is pivotal otherwise the CPU receiving an IRQ can complete wfi and be reset by firmware when executing in the middle of nowhere, leading to a system lock-up.
Disabling the GIC CPU IF must not be added to the gic_cpu_save() code, but we do need a helper function to disable the CPU IF for platforms that need this to happen to function properly (eg TC2).
Lorenzo
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
In reply to Nicolas PitreQuoted 20 lines · Nicolas Pitre
Otherwise there might be some interrupts or IPIs becoming pending and the CPU will not enter low power mode when doing a WFI. The effect of this is a CPU that loops back into the kernel, go through the first man election, signals itself as alive, and prevent the cluster from being shut down.
This could benefit from a better solution.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 1 + arch/arm/common/gic.c | 6 ++++++ arch/arm/include/asm/hardware/gic.h | 2 ++ 3 files changed, 9 insertions(+)arch/arm/common/bL_platsmp.c +0
-0
arch/arm/common/bL_platsmp.c static void __ref bL_cpu_die(unsigned int cpu) · line 71
Quoted 4 lines
arch/arm/common/bL_platsmp.c +1
-0@@ -68,6 +68,7 @@ static void __ref bL_cpu_die(unsigned int cpu) pcpu = mpidr &0xff ; pcluster = (mpidr >>8 ) &0xff ;bL_set_entry_vector (pcpu, pcluster,NULL ); +gic_cpu_if_down ();
I'm starting to sound like a stuck record (and not a very tuneful one at that) but... I think you need a barrier here.
arch/arm/common/bL_platsmp.c static void __ref bL_cpu_die(unsigned int cpu) · line 72
Quoted 1 line
arch/arm/common/bL_platsmp.c +0
-0bL_cpu_power_down ();
Will
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
Quoted 16 lines · Will Deacon
Quoted 15 lines · Nicolas Pitre
Otherwise there might be some interrupts or IPIs becoming pending and the CPU will not enter low power mode when doing a WFI. The effect of this is a CPU that loops back into the kernel, go through the first man election, signals itself as alive, and prevent the cluster from being shut down.
This could benefit from a better solution.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 1 + arch/arm/common/gic.c | 6 ++++++ arch/arm/include/asm/hardware/gic.h | 2 ++ 3 files changed, 9 insertions(+)
Quoted 12 lines
arch/arm/common/bL_platsmp.c static void __ref bL_cpu_die(unsigned int cpu) · line 71
Quoted 9 lines
arch/arm/common/bL_platsmp.c +1
-0@@ -68,6 +68,7 @@ static void __ref bL_cpu_die(unsigned int cpu) pcpu = mpidr &0xff ; pcluster = (mpidr >>8 ) &0xff ;bL_set_entry_vector (pcpu, pcluster,NULL ); +gic_cpu_if_down ();I'm starting to sound like a stuck record (and not a very tuneful one at that) but... I think you need a barrier here.
And I'm getting puzzled at the repetition. ;-)
Nicolas
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
Quoted 31 lines · Nicolas Pitre
Quoted 16 lines · Will Deacon
Quoted 15 lines · Nicolas Pitre
Otherwise there might be some interrupts or IPIs becoming pending and the CPU will not enter low power mode when doing a WFI. The effect of this is a CPU that loops back into the kernel, go through the first man election, signals itself as alive, and prevent the cluster from being shut down.
This could benefit from a better solution.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 1 + arch/arm/common/gic.c | 6 ++++++ arch/arm/include/asm/hardware/gic.h | 2 ++ 3 files changed, 9 insertions(+)Quoted 12 lines
arch/arm/common/bL_platsmp.c static void __ref bL_cpu_die(unsigned int cpu) · line 71
Quoted 9 lines
arch/arm/common/bL_platsmp.c +1
-0@@ -68,6 +68,7 @@ static void __ref bL_cpu_die(unsigned int cpu) pcpu = mpidr &0xff ; pcluster = (mpidr >>8 ) &0xff ;bL_set_entry_vector (pcpu, pcluster,NULL ); +gic_cpu_if_down ();I'm starting to sound like a stuck record (and not a very tuneful one at that) but... I think you need a barrier here.
Quoted 1 line
And I'm getting puzzled at the repetition. ;-)
Sorry! This case is more interesting though, because you also want to order the cpu_if_down GIC write so that it completes before we do the power_off.
Will
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
Quoted 32 lines · Will Deacon
Quoted 31 lines · Nicolas Pitre
Quoted 28 lines · Will Deacon
Quoted 20 lines · Nicolas Pitre
Otherwise there might be some interrupts or IPIs becoming pending and the CPU will not enter low power mode when doing a WFI. The effect of this is a CPU that loops back into the kernel, go through the first man election, signals itself as alive, and prevent the cluster from being shut down.
This could benefit from a better solution.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/common/bL_platsmp.c | 1 + arch/arm/common/gic.c | 6 ++++++ arch/arm/include/asm/hardware/gic.h | 2 ++ 3 files changed, 9 insertions(+)arch/arm/common/bL_platsmp.c +0
-0arch/arm/common/bL_platsmp.c static void __ref bL_cpu_die(unsigned int cpu) · line 71
Quoted 4 lines
arch/arm/common/bL_platsmp.c +1
-0@@ -68,6 +68,7 @@ static void __ref bL_cpu_die(unsigned int cpu) pcpu = mpidr &0xff ; pcluster = (mpidr >>8 ) &0xff ;bL_set_entry_vector (pcpu, pcluster,NULL ); +gic_cpu_if_down ();I'm starting to sound like a stuck record (and not a very tuneful one at that) but... I think you need a barrier here.
Quoted 4 lines
Quoted 1 line
And I'm getting puzzled at the repetition. ;-)
Sorry! This case is more interesting though, because you also want to order the
cpu_if_downGIC write so that it completes before we do thepower_off.
In this case I'm leaning toward removing that gic_cpu_if_down() entirely. I'm not convinced it is necessary, and if it is then we probably have a bug somewhere else.
Nicolas
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
Quoted 7 lines · Nicolas Pitre
Quoted 2 lines · Will Deacon
Sorry! This case is more interesting though, because you also want to order the
cpu_if_downGIC write so that it completes before we do thepower_off.In this case I'm leaning toward removing that
gic_cpu_if_down()entirely. I'm not convinced it is necessary, and if it is then we probably have a bug somewhere else.
Or that :)
Will
PATCH 8/16 ARM: bL_platsmp.c: make sure the GIC interface of a dying CPU is disabled
Quoted 10 lines · Will Deacon
Quoted 7 lines · Nicolas Pitre
Quoted 2 lines · Will Deacon
Sorry! This case is more interesting though, because you also want to order the
cpu_if_downGIC write so that it completes before we do thepower_off.In this case I'm leaning toward removing that
gic_cpu_if_down()entirely. I'm not convinced it is necessary, and if it is then we probably have a bug somewhere else.Or that :)
In the CPU idle code path (cpu_suspend) we do need to turn off the GIC CPU IF, hence we will have to cross that bridge when we come to it. Very soon.
Lorenzo
PATCH 9/16 ARM: vexpress: Select the correct SMP operations at run-time
In reply to Nicolas PitreFrom: Jon Medhurst <[email protected]>
--- arch/arm/include/asm/mach/arch.h | 3 +++ arch/arm/kernel/setup.c | 5 ++++- arch/arm/mach-vexpress/core.h | 2 ++ arch/arm/mach-vexpress/platsmp.c | 12 ++++++++++++ arch/arm/mach-vexpress/v2m.c | 2 +- 5 files changed, 22 insertions(+), 2 deletions(-)
arch/arm/include/asm/mach/arch.h +3 -0
@@ -17,8 +17,10 @@ struct pt_regs;struct smp_operations ;#ifdef CONFIG_SMP #define smp_ops (ops) (&(ops)) +#define smp_init_ops (ops) (&(ops))#else #define smp_ops (ops) (struct smp_operations *)NULL +#define smp_init_ops (ops) (void (*)(void))NULL#endif struct machine_desc {
@@ -42,6 +44,7 @@ struct machine_desc { unsigned char reserve_lp2 :1; /* never has lp2 */char restart_mode;/* default restart mode */ struct smp_operations *smp;/* SMP operations */ +void (*smp_init)(void );void (*fixup)(struct tag *,char **,struct meminfo *);void (*reserve)(void );/* reserve mem blocks */
arch/arm/kernel/setup.c +4 -1
@@ -768,7 +768,10 @@ void __init setup_arch(char **cmdline_p)arm_dt_init_cpu_maps ();#ifdef CONFIG_SMP if (is_smp ()) { -smp_set_ops (mdesc->smp); +if (mdesc->smp_init) + (*mdesc->smp_init)(); +else +smp_set_ops (mdesc->smp);smp_init_cpus (); }#endif
arch/arm/mach-vexpress/core.h +2 -0
@@ -6,6 +6,8 @@void vexpress_dt_smp_map_io (void ); +void vexpress_smp_init_ops (void ); +extern struct smp_operations vexpress_smp_ops;extern void vexpress_cpu_die (unsigned int cpu);
arch/arm/mach-vexpress/platsmp.c +12 -0
@@ -12,6 +12,7 @@#include <linux/errno.h> #include <linux/smp.h> #include <linux/io.h> +#include <linux/of.h> #include <linux/of_fdt.h> #include <linux/vexpress.h>
@@ -206,3 +207,14 @@ struct smp_operations __initdata vexpress_smp_ops = { .cpu_die = vexpress_cpu_die,#endif }; + +void __init vexpress_smp_init_ops (void ) +{ +struct smp_operations *ops = &vexpress_smp_ops; +#ifdef CONFIG_BIG_LITTLE +extern struct smp_operations bL_smp_ops; +if (of_find_compatible_node (NULL ,NULL ,"arm,cci" )) + ops = &bL_smp_ops; +#endif +smp_set_ops (ops); +}
arch/arm/mach-vexpress/v2m.c +1 -1
@@ -494,7 +494,7 @@ static const char * const v2m_dt_match[] __initconst = {DT_MACHINE_START (VEXPRESS_DT ,"ARM-Versatile Express" ) .dt_compat = v2m_dt_match, - .smp = smp_ops(vexpress_smp_ops), + .smp_init = smp_init_ops(vexpress_smp_init_ops), .map_io = v2m_dt_map_io, .init_early = v2m_dt_init_early, .init_irq = v2m_dt_init_irq,
Signature · 1 line
1.8.0
PATCH 10/16 ARM: vexpress: introduce DCSCB support
In reply to Nicolas PitreThis adds basic CPU and cluster reset controls on RTSM for the A15x4-A7x4 model configuration using the Dual Cluster System Configuration Block (DCSCB).
The cache coherency interconnect (CCI) is not handled yet.
--- arch/arm/mach-vexpress/Kconfig | 8 ++ arch/arm/mach-vexpress/Makefile | 1 + arch/arm/mach-vexpress/dcscb.c | 160 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 arch/arm/mach-vexpress/dcscb.c
arch/arm/mach-vexpress/Kconfig +8 -0
@@ -53,4 +53,12 @@ config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA config ARCH_VEXPRESS_CA9X4 bool "Versatile Express Cortex-A9x4 tile" +config ARCH_VEXPRESS_DCSCB + bool "Dual Cluster System Control Block (DCSCB) support" + depends on BIG_LITTLE + help + Support for the Dual Cluster System Configuration Block (DCSCB). + This is needed to provide CPU and cluster power management + on RTSM. + endmenu
arch/arm/mach-vexpress/Makefile +1 -0
@@ -6,5 +6,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ obj-y := v2m.o reset.o obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o +obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
arch/arm/mach-vexpress/dcscb.c new file +160 -0
160 lines not on this page · raw message
Signature · 1 line
1.8.0
PATCH 10/16 ARM: vexpress: introduce DCSCB support
Quoted 140 lines · Nicolas Pitre
This adds basic CPU and cluster reset controls on RTSM for the A15x4-A7x4 model configuration using the Dual Cluster System Configuration Block (DCSCB).
The cache coherency interconnect (CCI) is not handled yet.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/mach-vexpress/Kconfig | 8 ++ arch/arm/mach-vexpress/Makefile | 1 + arch/arm/mach-vexpress/dcscb.c | 160 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+)create mode 100644
arch/arm/mach-vexpress/dcscb.carch/arm/mach-vexpress/Kconfig +8
-0@@ -53,4 +53,12 @@ config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA config ARCH_VEXPRESS_CA9X4 bool "Versatile Express Cortex-A9x4 tile" +config ARCH_VEXPRESS_DCSCB + bool "Dual Cluster System Control Block (DCSCB) support" + depends on BIG_LITTLE + help + Support for the Dual Cluster System Configuration Block (DCSCB). + This is needed to provide CPU and cluster power management + on RTSM. + endmenuarch/arm/mach-vexpress/Makefile +1
-0@@ -6,5 +6,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ obj-y := v2m.o reset.o obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o +obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.oarch/arm/mach-vexpress/dcscb.c new file +92
-0@@ -0,0 +1,160 @@ +/* +* arch/arm/mach-vexpress/dcscb.c - Dual Cluster System Control Block +* +* Created by: Nicolas Pitre, May 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/vexpress.h> + +#include <asm/bL_entry.h> +#include <asm/proc-fns.h> +#include <asm/cacheflush.h> + + +#define DCSCB_PHYS_BASE 0x60000000 + +#define RST_HOLD0 0x0 +#define RST_HOLD1 0x4 +#define SYS_SWRESET 0x8 +#define RST_STAT0 0xc +#define RST_STAT1 0x10 +#define EAG_CFG_R 0x20 +#define EAG_CFG_W 0x24 +#define KFC_CFG_R 0x28 +#define KFC_CFG_W 0x2c +#define DCS_CFG_R 0x30 + +/* +* We can't use regular spinlocks. In the switcher case, it is possible +* for an outbound CPU to call power_down() after its inbound counterpart +* is already live using the same logical CPU number which trips lockdep +* debugging. +*/ +static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED; + +static void __iomem *dcscb_base; + +static int dcscb_power_up (unsigned int cpu,unsigned int cluster) +{ +unsigned int rst_hold, cpumask = (1 << cpu); + +pr_debug ("%s: cpu %u cluster %u\n" , __func__, cpu, cluster); +if (cpu >=4 || cluster >=2 ) +return -EINVAL ; + +/* +* Since this is called with IRQs enabled, and no arch_spin_lock_irq +* variant exists, we need to disable IRQs manually here. +*/ +local_irq_disable (); +arch_spin_lock (&dcscb_lock); + + rst_hold =readl_relaxed (dcscb_base +RST_HOLD0 + cluster *4 ); +if (rst_hold & (1 <<8 )) { +/* remove cluster reset and add individual CPU's reset */ + rst_hold &= ~(1 <<8 ); + rst_hold |=0xf ; + } + rst_hold &= ~(cpumask | (cpumask <<4 )); +writel (rst_hold, dcscb_base +RST_HOLD0 + cluster *4 ); + +arch_spin_unlock (&dcscb_lock); +local_irq_enable (); + +return 0 ; +} + +static void dcscb_power_down (void ) +{ +unsigned int mpidr, cpu, cluster, rst_hold, cpumask, last_man; + + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); + cpu = mpidr &0xff ; + cluster = (mpidr >>8 ) &0xff ; + cpumask = (1 << cpu); + +pr_debug ("%s: cpu %u cluster %u\n" , __func__, cpu, cluster); +BUG_ON (cpu >=4 || cluster >=2 ); + +arch_spin_lock (&dcscb_lock); + rst_hold =readl_relaxed (dcscb_base +RST_HOLD0 + cluster *4 ); + rst_hold |= cpumask; +if (((rst_hold | (rst_hold >>4 )) &0xf ) ==0xf )
arch/arm/mach-vexpress/dcscb.c line 102
Quoted 10 lines
arch/arm/mach-vexpress/dcscb.c new file +10
-0+ rst_hold |= (1 <<8 ); +writel (rst_hold, dcscb_base +RST_HOLD0 + cluster *4 ); +arch_spin_unlock (&dcscb_lock); + last_man = (rst_hold & (1 <<8 )); + +/* +* Now let's clean our L1 cache and shut ourself down. +* If we're the last CPU in this cluster then clean L2 too. +*/ +
Do you wanted to have C bit clear code here ?
Quoted 14 lines
arch/arm/mach-vexpress/dcscb.c new file +14
-0+/* +* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need +* a preliminary flush here for those CPUs. At least, that's +* the theory -- without the extra flush, Linux explodes on +* RTSM (maybe not needed anymore, to be investigated).. +*/ +flush_cache_louis (); +cpu_proc_fin (); + +if (!last_man) { +flush_cache_louis (); + }else { +flush_cache_all (); +outer_flush_all ();
arch/arm/mach-vexpress/dcscb.c line 126
Quoted 10 lines
arch/arm/mach-vexpress/dcscb.c new file +10
-0+ } + +/* Disable local coherency by clearing the ACTLR "SMP" bit: */ + asmvolatile ( +"mrc p15, 0, ip, c1, c0, 1 \n\t" +"bic ip, ip, #(1 << 6) @ clear SMP bit \n\t" +"mcr p15, 0, ip, c1, c0, 1" + : : :"ip" ); + +/* Now we are prepared for power-down, do it: */
You need dsb here, right ?
arch/arm/mach-vexpress/dcscb.c line 131
Quoted 5 lines
arch/arm/mach-vexpress/dcscb.c new file +5
-0+wfi (); + +/* Not dead at this point? Let our caller cope. */ +} +
Regards
Santosh
PATCH 10/16 ARM: vexpress: introduce DCSCB support
Quoted 147 lines · Santosh Shilimkar
Quoted 146 lines · Nicolas Pitre
This adds basic CPU and cluster reset controls on RTSM for the A15x4-A7x4 model configuration using the Dual Cluster System Configuration Block (DCSCB).
The cache coherency interconnect (CCI) is not handled yet.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/mach-vexpress/Kconfig | 8 ++ arch/arm/mach-vexpress/Makefile | 1 + arch/arm/mach-vexpress/dcscb.c | 160 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+)create mode 100644
arch/arm/mach-vexpress/dcscb.carch/arm/mach-vexpress/Kconfig +8
-0@@ -53,4 +53,12 @@ config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA config ARCH_VEXPRESS_CA9X4 bool "Versatile Express Cortex-A9x4 tile" +config ARCH_VEXPRESS_DCSCB + bool "Dual Cluster System Control Block (DCSCB) support" + depends on BIG_LITTLE + help + Support for the Dual Cluster System Configuration Block (DCSCB). + This is needed to provide CPU and cluster power management + on RTSM. + endmenuarch/arm/mach-vexpress/Makefile +1
-1@@ -6,5 +6,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ obj-y := v2m.o reset.o obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o +obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.oarch/arm/mach-vexpress/dcscb.c new file +95
-0@@ -0,0 +1,160 @@ +/* +* arch/arm/mach-vexpress/dcscb.c - Dual Cluster System Control Block +* +* Created by: Nicolas Pitre, May 2012 +* Copyright: (C) 2012 Linaro Limited +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/vexpress.h> + +#include <asm/bL_entry.h> +#include <asm/proc-fns.h> +#include <asm/cacheflush.h> + + +#define DCSCB_PHYS_BASE 0x60000000 + +#define RST_HOLD0 0x0 +#define RST_HOLD1 0x4 +#define SYS_SWRESET 0x8 +#define RST_STAT0 0xc +#define RST_STAT1 0x10 +#define EAG_CFG_R 0x20 +#define EAG_CFG_W 0x24 +#define KFC_CFG_R 0x28 +#define KFC_CFG_W 0x2c +#define DCS_CFG_R 0x30 + +/* +* We can't use regular spinlocks. In the switcher case, it is possible +* for an outbound CPU to call power_down() after its inbound counterpart +* is already live using the same logical CPU number which trips lockdep +* debugging. +*/ +static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED; + +static void __iomem *dcscb_base; + +static int dcscb_power_up (unsigned int cpu,unsigned int cluster) +{ +unsigned int rst_hold, cpumask = (1 << cpu); + +pr_debug ("%s: cpu %u cluster %u\n" , __func__, cpu, cluster); +if (cpu >=4 || cluster >=2 ) +return -EINVAL ; + +/* +* Since this is called with IRQs enabled, and no arch_spin_lock_irq +* variant exists, we need to disable IRQs manually here. +*/ +local_irq_disable (); +arch_spin_lock (&dcscb_lock); + + rst_hold =readl_relaxed (dcscb_base +RST_HOLD0 + cluster *4 ); +if (rst_hold & (1 <<8 )) { +/* remove cluster reset and add individual CPU's reset */ + rst_hold &= ~(1 <<8 ); + rst_hold |=0xf ; + } + rst_hold &= ~(cpumask | (cpumask <<4 )); +writel (rst_hold, dcscb_base +RST_HOLD0 + cluster *4 ); + +arch_spin_unlock (&dcscb_lock); +local_irq_enable (); + +return 0 ; +} + +static void dcscb_power_down (void ) +{ +unsigned int mpidr, cpu, cluster, rst_hold, cpumask, last_man; + + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); + cpu = mpidr &0xff ; + cluster = (mpidr >>8 ) &0xff ; + cpumask = (1 << cpu); + +pr_debug ("%s: cpu %u cluster %u\n" , __func__, cpu, cluster); +BUG_ON (cpu >=4 || cluster >=2 ); + +arch_spin_lock (&dcscb_lock); + rst_hold =readl_relaxed (dcscb_base +RST_HOLD0 + cluster *4 ); + rst_hold |= cpumask; +if (((rst_hold | (rst_hold >>4 )) &0xf ) ==0xf ) + rst_hold |= (1 <<8 ); +writel (rst_hold, dcscb_base +RST_HOLD0 + cluster *4 ); +arch_spin_unlock (&dcscb_lock);
Quoted 8 lines
arch/arm/mach-vexpress/dcscb.c line 102
Quoted 7 lines
arch/arm/mach-vexpress/dcscb.c new file +7
-0+ last_man = (rst_hold & (1 <<8 )); + +/* +* Now let's clean our L1 cache and shut ourself down. +* If we're the last CPU in this cluster then clean L2 too. +*/ +Do you wanted to have C bit clear code here ?
cpu_proc_fin() does it.
Quoted 17 lines
Quoted 17 lines
arch/arm/mach-vexpress/dcscb.c new file +17
-0+/* +* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need +* a preliminary flush here for those CPUs. At least, that's +* the theory -- without the extra flush, Linux explodes on +* RTSM (maybe not needed anymore, to be investigated).. +*/ +flush_cache_louis (); +cpu_proc_fin (); + +if (!last_man) { +flush_cache_louis (); + }else { +flush_cache_all (); +outer_flush_all (); + } + +/* Disable local coherency by clearing the ACTLR "SMP" bit: */
Quoted 8 lines
arch/arm/mach-vexpress/dcscb.c line 126
Quoted 7 lines
arch/arm/mach-vexpress/dcscb.c new file +7
-0+ asmvolatile ( +"mrc p15, 0, ip, c1, c0, 1 \n\t" +"bic ip, ip, #(1 << 6) @ clear SMP bit \n\t" +"mcr p15, 0, ip, c1, c0, 1" + : : :"ip" ); + +/* Now we are prepared for power-down, do it: */ You need dsb here, right ?
Probably. However this code is being refactored significantly with subsequent patches. This intermediate step was kept not to introduce too many concepts at once.
Quoted 9 lines
arch/arm/mach-vexpress/dcscb.c line 131
Quoted 5 lines
arch/arm/mach-vexpress/dcscb.c new file +5
-0+wfi (); + +/* Not dead at this point? Let our caller cope. */ +} +Regards
Santosh
Nicolas
PATCH 10/16 ARM: vexpress: introduce DCSCB support
Quoted 18 lines · Nicolas Pitre
Quoted 16 lines · Santosh Shilimkar
Quoted 15 lines · Nicolas Pitre
This adds basic CPU and cluster reset controls on RTSM for the A15x4-A7x4 model configuration using the Dual Cluster System Configuration Block (DCSCB).
The cache coherency interconnect (CCI) is not handled yet.
Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/mach-vexpress/Kconfig | 8 ++ arch/arm/mach-vexpress/Makefile | 1 + arch/arm/mach-vexpress/dcscb.c | 160 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+)create mode 100644
arch/arm/mach-vexpress/dcscb.c
[..]
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
arch/arm/mach-vexpress/dcscb.c new file +0
-0
[..]
Quoted 28 lines
Quoted 19 lines
Quoted 19 lines
arch/arm/mach-vexpress/dcscb.c new file +19
-0+static void dcscb_power_down (void ) +{ +unsigned int mpidr, cpu, cluster, rst_hold, cpumask, last_man; + + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); + cpu = mpidr &0xff ; + cluster = (mpidr >>8 ) &0xff ; + cpumask = (1 << cpu); + +pr_debug ("%s: cpu %u cluster %u\n" , __func__, cpu, cluster); +BUG_ON (cpu >=4 || cluster >=2 ); + +arch_spin_lock (&dcscb_lock); + rst_hold =readl_relaxed (dcscb_base +RST_HOLD0 + cluster *4 ); + rst_hold |= cpumask; +if (((rst_hold | (rst_hold >>4 )) &0xf ) ==0xf ) + rst_hold |= (1 <<8 ); +writel (rst_hold, dcscb_base +RST_HOLD0 + cluster *4 ); +arch_spin_unlock (&dcscb_lock);Quoted 8 lines
arch/arm/mach-vexpress/dcscb.c line 102
Quoted 7 lines
arch/arm/mach-vexpress/dcscb.c new file +7
-0+ last_man = (rst_hold & (1 <<8 )); + +/* +* Now let's clean our L1 cache and shut ourself down. +* If we're the last CPU in this cluster then clean L2 too. +*/ +Do you wanted to have C bit clear code here ?
Quoted 2 lines
cpu_proc_fin()does it.
Yep. I noticed that in next patch when read the comment.
Quoted 26 lines
Quoted 17 lines
Quoted 17 lines
arch/arm/mach-vexpress/dcscb.c new file +17
-0+/* +* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need +* a preliminary flush here for those CPUs. At least, that's +* the theory -- without the extra flush, Linux explodes on +* RTSM (maybe not needed anymore, to be investigated).. +*/ +flush_cache_louis (); +cpu_proc_fin (); + +if (!last_man) { +flush_cache_louis (); + }else { +flush_cache_all (); +outer_flush_all (); + } + +/* Disable local coherency by clearing the ACTLR "SMP" bit: */ Quoted 8 lines
arch/arm/mach-vexpress/dcscb.c line 126
Quoted 7 lines
arch/arm/mach-vexpress/dcscb.c new file +7
-0+ asmvolatile ( +"mrc p15, 0, ip, c1, c0, 1 \n\t" +"bic ip, ip, #(1 << 6) @ clear SMP bit \n\t" +"mcr p15, 0, ip, c1, c0, 1" + : : :"ip" ); + +/* Now we are prepared for power-down, do it: */ You need dsb here, right ?
Quoted 4 lines
Probably. However this code is being refactored significantly with subsequent patches. This intermediate step was kept not to introduce too many concepts at once.
Yes. I do see updates in subsequent patch.
Regards
Santosh
PATCH 11/16 ARM: vexpress/dcscb: add CPU use counts to the power up/down API implementation
In reply to Nicolas PitreIt is possible for a CPU to be told to power up before it managed to power itself down. Solve this race with a usage count as mandated by the API definition.
--- arch/arm/mach-vexpress/dcscb.c | 74 +++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 15 deletions(-)
arch/arm/mach-vexpress/dcscb.c +59 -15
@@ -43,6 +43,7 @@static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED;static void __iomem *dcscb_base; +static int dcscb_use_count[4 ][2 ];static int dcscb_power_up (unsigned int cpu,unsigned int cluster) {
@@ -59,14 +60,27 @@ static int dcscb_power_up(unsigned int cpu, unsigned int cluster)local_irq_disable ();arch_spin_lock (&dcscb_lock); - rst_hold =readl_relaxed (dcscb_base +RST_HOLD0 + cluster *4 ); -if (rst_hold & (1 <<8 )) { -/* remove cluster reset and add individual CPU's reset */ - rst_hold &= ~(1 <<8 ); - rst_hold |=0xf ; + dcscb_use_count[cpu][cluster]++; +if (dcscb_use_count[cpu][cluster] ==1 ) { + rst_hold =readl_relaxed (dcscb_base +RST_HOLD0 + cluster *4 ); +if (rst_hold & (1 <<8 )) { +/* remove cluster reset and add individual CPU's reset */ + rst_hold &= ~(1 <<8 ); + rst_hold |=0xf ; + } + rst_hold &= ~(cpumask | (cpumask <<4 )); +writel (rst_hold, dcscb_base +RST_HOLD0 + cluster *4 ); + }else if (dcscb_use_count[cpu][cluster] !=2 ) { +/* +* The only possible values are: +* 0 = CPU down +* 1 = CPU (still) up +* 2 = CPU requested to be up before it had a chance +* to actually make itself down. +* Any other value is a bug. +*/ +BUG (); } - rst_hold &= ~(cpumask | (cpumask <<4 )); -writel (rst_hold, dcscb_base +RST_HOLD0 + cluster *4 );arch_spin_unlock (&dcscb_lock);local_irq_enable ();
@@ -76,7 +90,8 @@ static int dcscb_power_up(unsigned int cpu, unsigned int cluster)static void dcscb_power_down (void ) { -unsigned int mpidr, cpu, cluster, rst_hold, cpumask, last_man; +unsigned int mpidr, cpu, cluster, rst_hold, cpumask; +bool last_man = false, skip_wfi = false; asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); cpu = mpidr &0xff ;
@@ -87,13 +102,26 @@ static void dcscb_power_down(void)BUG_ON (cpu >=4 || cluster >=2 );arch_spin_lock (&dcscb_lock); - rst_hold =readl_relaxed (dcscb_base +RST_HOLD0 + cluster *4 ); - rst_hold |= cpumask; -if (((rst_hold | (rst_hold >>4 )) &0xf ) ==0xf ) - rst_hold |= (1 <<8 ); -writel (rst_hold, dcscb_base +RST_HOLD0 + cluster *4 ); + dcscb_use_count[cpu][cluster]--; +if (dcscb_use_count[cpu][cluster] ==0 ) { + rst_hold =readl_relaxed (dcscb_base +RST_HOLD0 + cluster *4 ); + rst_hold |= cpumask; +if (((rst_hold | (rst_hold >>4 )) &0xf ) ==0xf ) { + rst_hold |= (1 <<8 ); + last_man = true; + } +writel (rst_hold, dcscb_base +RST_HOLD0 + cluster *4 ); + }else if (dcscb_use_count[cpu][cluster] ==1 ) { +/* +* A power_up request went ahead of us. +* Even if we do not want to shut this CPU down, +* the caller expects a certain state as if the WFI +* was aborted. So let's continue with cache cleaning. +*/ + skip_wfi = true; + }else +BUG ();arch_spin_unlock (&dcscb_lock); - last_man = (rst_hold & (1 <<8 ));/* * Now let's clean our L1 cache and shut ourself down.
@@ -124,7 +152,8 @@ static void dcscb_power_down(void) : : : "ip" );/* Now we are prepared for power-down, do it: */ -wfi (); +if (!skip_wfi) +wfi ();/* Not dead@this point? Let our caller cope. */ }
@@ -134,6 +163,19 @@ static const struct bL_platform_power_ops dcscb_power_ops = { .power_down = dcscb_power_down, }; +static void __init dcscb_usage_count_init (void ) +{ +unsigned int mpidr, cpu, cluster; + + asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); + cpu = mpidr &0xff ; + cluster = (mpidr >>8 ) &0xff ; + +pr_debug ("%s: cpu %u cluster %u\n" , __func__, cpu, cluster); +BUG_ON (cpu >=4 || cluster >=2 ); + dcscb_use_count[cpu][cluster] =1 ; +} +static int __init dcscb_init (void ) {int ret;
@@ -142,6 +184,8 @@ static int __init dcscb_init(void)if (!dcscb_base)return -ENOMEM ; +dcscb_usage_count_init (); + ret =bL_platform_power_register (&dcscb_power_ops);if (ret) {iounmap (dcscb_base);
Signature · 1 line
1.8.0
PATCH 12/16 ARM: vexpress/dcscb: do not hardcode number of CPUs per cluster
In reply to Nicolas PitreIf 4 CPUs are assumed, the A15x1-A7x1 model configuration would never shut down the initial cluster as the 0xf reset bit mask will never be observed. Let's construct this mask based on the provided information in the DCSCB config register for the number of CPUs per cluster.
--- arch/arm/mach-vexpress/dcscb.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
arch/arm/mach-vexpress/dcscb.c +10 -4
@@ -44,10 +44,12 @@ static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED;static void __iomem *dcscb_base;static int dcscb_use_count[4 ][2 ]; +static int dcscb_cluster_cpu_mask[2 ];static int dcscb_power_up (unsigned int cpu,unsigned int cluster) {unsigned int rst_hold, cpumask = (1 << cpu); +unsigned int cluster_mask = dcscb_cluster_cpu_mask[cluster];pr_debug ("%s: cpu %u cluster %u\n" , __func__, cpu, cluster);if (cpu >=4 || cluster >=2 )
@@ -66,7 +68,7 @@ static int dcscb_power_up(unsigned int cpu, unsigned int cluster)if (rst_hold & (1 <<8 )) {/* remove cluster reset and add individual CPU's reset */ rst_hold &= ~(1 <<8 ); - rst_hold |=0xf ; + rst_hold |= cluster_mask; } rst_hold &= ~(cpumask | (cpumask <<4 ));writel (rst_hold, dcscb_base +RST_HOLD0 + cluster *4 );
@@ -90,13 +92,14 @@ static int dcscb_power_up(unsigned int cpu, unsigned int cluster)static void dcscb_power_down (void ) { -unsigned int mpidr, cpu, cluster, rst_hold, cpumask; +unsigned int mpidr, cpu, cluster, rst_hold, cpumask, cluster_mask;bool last_man = false, skip_wfi = false; asm ("mrc p15, 0, %0, c0, c0, 5" :"=r" (mpidr)); cpu = mpidr &0xff ; cluster = (mpidr >>8 ) &0xff ; cpumask = (1 << cpu); + cluster_mask = dcscb_cluster_cpu_mask[cluster];pr_debug ("%s: cpu %u cluster %u\n" , __func__, cpu, cluster);BUG_ON (cpu >=4 || cluster >=2 );
@@ -106,7 +109,7 @@ static void dcscb_power_down(void)if (dcscb_use_count[cpu][cluster] ==0 ) { rst_hold =readl_relaxed (dcscb_base +RST_HOLD0 + cluster *4 ); rst_hold |= cpumask; -if (((rst_hold | (rst_hold >>4 )) &0xf ) ==0xf ) { +if (((rst_hold | (rst_hold >>4 )) & cluster_mask) == cluster_mask) { rst_hold |= (1 <<8 ); last_man = true; }
@@ -178,12 +181,15 @@ static void __init dcscb_usage_count_init(void)static int __init dcscb_init (void ) { +unsigned int cfg;int ret; dcscb_base =ioremap (DCSCB_PHYS_BASE ,0x1000 );if (!dcscb_base)return -ENOMEM ; - + cfg =readl_relaxed (dcscb_base +DCS_CFG_R ); + dcscb_cluster_cpu_mask[0 ] = (1 << (((cfg >>16 ) >> (0 <<2 )) &0xf )) -1 ; + dcscb_cluster_cpu_mask[1 ] = (1 << (((cfg >>16 ) >> (1 <<2 )) &0xf )) -1 ;dcscb_usage_count_init (); ret =bL_platform_power_register (&dcscb_power_ops);
Signature · 1 line
1.8.0
PATCH 13/16 drivers: misc: add ARM CCI support
In reply to Nicolas PitreFrom: Lorenzo Pieralisi <[email protected]>
On ARM multi-cluster systems coherency between cores running on different clusters is managed by the cache-coherent interconnect (CCI). It allows broadcasting of TLB invalidates and memory barriers and it guarantees cache coherency at system level.
This patch enables the basic infrastructure required in Linux to handle and programme the CCI component. The first implementation is based on a platform device, its relative DT compatible property and a simple programming interface.
--- drivers/misc/Kconfig | 3 ++ drivers/misc/Makefile | 1 + drivers/misc/arm-cci.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/arm-cci.h | 30 ++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 drivers/misc/arm-cci.c create mode 100644 include/linux/arm-cci.h
drivers/misc/Kconfig +3 -0
@@ -499,6 +499,9 @@ config USB_SWITCH_FSA9480 stereo and mono audio, video, microphone and UART data to use a common connector port. +config ARM_CCI + bool "ARM CCI driver support" + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig"
drivers/misc/Makefile +1 -0
@@ -49,3 +49,4 @@ obj-y += carma/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_INTEL_MEI) += mei/ +obj-$(CONFIG_ARM_CCI) += arm-cci.o
drivers/misc/arm-cci.c new file +107 -0
107 lines not on this page · raw message
include/linux/arm-cci.h new file +30 -0
@@ -0,0 +1,30 @@ +/* +* CCI support +* +* Copyright (C) 2012 ARM Ltd. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LINUX_ARM_CCI_H +#define __LINUX_ARM_CCI_H + +#ifdef CONFIG_ARM_CCI +extern void disable_cci (int cluster); +#else +static inline void disable_cci (int cluster) { } +#endif + +#endif
Signature · 1 line
1.8.0
PATCH 13/16 drivers: misc: add ARM CCI support
Quoted 12 lines · Nicolas Pitre
From: Lorenzo Pieralisi <[email protected]>
On ARM multi-cluster systems coherency between cores running on different clusters is managed by the cache-coherent interconnect (CCI). It allows broadcasting of TLB invalidates and memory barriers and it guarantees cache coherency at system level.
This patch enables the basic infrastructure required in Linux to handle and programme the CCI component. The first implementation is based on a platform device, its relative DT compatible property and a simple programming interface.
Quoted 6 lines
Signed-off-by Nicolas Pitre <[email protected]>--- drivers/misc/Kconfig | 3 ++ drivers/misc/Makefile | 1 + drivers/misc/arm-cci.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/arm-cci.h | 30 ++++++++++++++
How about 'drivers/bus/' considering CCI is an interconnect bus (though for coherency)
drivers/misc/Kconfig config USB_SWITCH_FSA9480 · line 502
Quoted 13 lines
4 files changed, 141 insertions(+)create mode 100644
drivers/misc/arm-cci.c
create mode 100644include/linux/arm-cci.hdrivers/misc/Kconfig +1
-0@@ -499,6 +499,9 @@ config USB_SWITCH_FSA9480 stereo and mono audio, video, microphone and UART data to use a common connector port. +config ARM_CCI
You might want add depends on ARM big.LITTTLE otherwise it will break build for other arch's with random configurations.
[..]
Quoted 40 lines
drivers/misc/arm-cci.c new file +34
-0@@ -0,0 +1,107 @@ +/* +* CCI support +* +* Copyright (C) 2012 ARM Ltd. +* Author: Lorenzo Pieralisi <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed "as is" WITHOUT ANY WARRANTY of any +* kind, whether express or implied; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/arm-cci.h> + +#define CCI400_EAG_OFFSET 0x4000 +#define CCI400_KF_OFFSET 0x5000 + +#define DRIVER_NAME "CCI" +struct cci_drvdata { +void __iomem *baseaddr; +spinlock_t lock; +}; + +static struct cci_drvdata *info; +
drivers/misc/arm-cci.c line 44
Quoted 10 lines
drivers/misc/arm-cci.c new file +10
-0+void disable_cci (int cluster) +{ +u32 cci_reg = cluster ?CCI400_KF_OFFSET :CCI400_EAG_OFFSET ; +writel_relaxed (0x0 , info->baseaddr + cci_reg); + +while (readl_relaxed (info->baseaddr +0xc ) &0x1 ) + ; +} +EXPORT_SYMBOL_GPL (disable_cci); +
Is more functionality going to be added for CCI driver. Having this much of driver code for just a disable_cci() functions seems like overkill.
Regards
Santosh
PATCH 13/16 drivers: misc: add ARM CCI support
Quoted 20 lines · Santosh Shilimkar
Quoted 12 lines · Nicolas Pitre
From: Lorenzo Pieralisi <[email protected]>
On ARM multi-cluster systems coherency between cores running on different clusters is managed by the cache-coherent interconnect (CCI). It allows broadcasting of TLB invalidates and memory barriers and it guarantees cache coherency at system level.
This patch enables the basic infrastructure required in Linux to handle and programme the CCI component. The first implementation is based on a platform device, its relative DT compatible property and a simple programming interface.
Quoted 7 lines
Signed-off-by Nicolas Pitre <[email protected]>--- drivers/misc/Kconfig | 3 ++ drivers/misc/Makefile | 1 + drivers/misc/arm-cci.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/arm-cci.h | 30 ++++++++++++++
Quoted 2 lines
How about '
drivers/bus/' considering CCI is an interconnect bus (though for coherency)
Yes, I like that better.
Quoted 12 lines
Quoted 12 lines
4 files changed, 141 insertions(+)create mode 100644
drivers/misc/arm-cci.c
create mode 100644include/linux/arm-cci.hdrivers/misc/Kconfig +0
-0@@ -499,6 +499,9 @@ config USB_SWITCH_FSA9480 stereo and mono audio, video, microphone and UART data to use a common connector port.
Quoted 3 lines
drivers/misc/Kconfig config USB_SWITCH_FSA9480 · line 502
Quoted 1 line
drivers/misc/Kconfig +1
-0+config ARM_CCIYou might want add depends on ARM big.LITTTLE otherwise it will break build for other arch's with random configurations.
As far as this patch goes, this is buildable on other architectures too. The next patch changes that though.
Quoted 47 lines
[..]
Quoted 45 lines
drivers/misc/arm-cci.c new file +39
-0@@ -0,0 +1,107 @@ +/* +* CCI support +* +* Copyright (C) 2012 ARM Ltd. +* Author: Lorenzo Pieralisi <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed "as is" WITHOUT ANY WARRANTY of any +* kind, whether express or implied; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/arm-cci.h> + +#define CCI400_EAG_OFFSET 0x4000 +#define CCI400_KF_OFFSET 0x5000 + +#define DRIVER_NAME "CCI" +struct cci_drvdata { +void __iomem *baseaddr; +spinlock_t lock; +}; + +static struct cci_drvdata *info; + +void disable_cci (int cluster) +{ +u32 cci_reg = cluster ?CCI400_KF_OFFSET :CCI400_EAG_OFFSET ; +writel_relaxed (0x0 , info->baseaddr + cci_reg); +
Quoted 8 lines
drivers/misc/arm-cci.c line 44
Quoted 5 lines
drivers/misc/arm-cci.c new file +5
-0+while (readl_relaxed (info->baseaddr +0xc ) &0x1 ) + ; +} +EXPORT_SYMBOL_GPL (disable_cci); +Is more functionality going to be added for CCI driver. Having this much of driver code for just a
disable_cci()functions seems like overkill.
Yes. More code will appear here to provide pmu functionalities, etc.
Nicolas
PATCH 13/16 drivers: misc: add ARM CCI support
Quoted 22 lines · Nicolas Pitre
Quoted 20 lines · Santosh Shilimkar
Quoted 19 lines · Nicolas Pitre
From: Lorenzo Pieralisi <[email protected]>
On ARM multi-cluster systems coherency between cores running on different clusters is managed by the cache-coherent interconnect (CCI). It allows broadcasting of TLB invalidates and memory barriers and it guarantees cache coherency at system level.
This patch enables the basic infrastructure required in Linux to handle and programme the CCI component. The first implementation is based on a platform device, its relative DT compatible property and a simple programming interface.
Signed-off-by Nicolas Pitre <[email protected]>--- drivers/misc/Kconfig | 3 ++ drivers/misc/Makefile | 1 + drivers/misc/arm-cci.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/arm-cci.h | 30 ++++++++++++++
Quoted 5 lines
Quoted 2 lines
How about '
drivers/bus/' considering CCI is an interconnect bus (though for coherency)Yes, I like that better.
Great.
Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
4 files changed, 141 insertions(+)create mode 100644
drivers/misc/arm-cci.c
create mode 100644include/linux/arm-cci.hdrivers/misc/Kconfig +0
-0@@ -499,6 +499,9 @@ config USB_SWITCH_FSA9480 stereo and mono audio, video, microphone and UART data to use a common connector port.
Quoted 7 lines
Quoted 3 lines
drivers/misc/Kconfig config USB_SWITCH_FSA9480 · line 502
Quoted 1 line
drivers/misc/Kconfig +1
-0+config ARM_CCIYou might want add depends on ARM big.LITTTLE otherwise it will break build for other arch's with random configurations.
As far as this patch goes, this is buildable on other architectures too. The next patch changes that though.
Thanks.
Quoted 56 lines
Quoted 47 lines
[..]
Quoted 45 lines
drivers/misc/arm-cci.c new file +39
-0@@ -0,0 +1,107 @@ +/* +* CCI support +* +* Copyright (C) 2012 ARM Ltd. +* Author: Lorenzo Pieralisi <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed "as is" WITHOUT ANY WARRANTY of any +* kind, whether express or implied; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/arm-cci.h> + +#define CCI400_EAG_OFFSET 0x4000 +#define CCI400_KF_OFFSET 0x5000 + +#define DRIVER_NAME "CCI" +struct cci_drvdata { +void __iomem *baseaddr; +spinlock_t lock; +}; + +static struct cci_drvdata *info; + +void disable_cci (int cluster) +{ +u32 cci_reg = cluster ?CCI400_KF_OFFSET :CCI400_EAG_OFFSET ; +writel_relaxed (0x0 , info->baseaddr + cci_reg); +Quoted 8 lines
drivers/misc/arm-cci.c line 44
Quoted 5 lines
drivers/misc/arm-cci.c new file +5
-0+while (readl_relaxed (info->baseaddr +0xc ) &0x1 ) + ; +} +EXPORT_SYMBOL_GPL (disable_cci); +Is more functionality going to be added for CCI driver. Having this much of driver code for just a
disable_cci()functions seems like overkill.
Quoted 2 lines
Yes. More code will appear here to provide pmu functionalities, etc.
Good to know.
Regards,
Santosh
PATCH 13/16 drivers: misc: add ARM CCI support
In reply to Nicolas PitreQuoted 102 lines · Nicolas Pitre
Quoted 20 lines · Santosh Shilimkar
Quoted 12 lines · Nicolas Pitre
From: Lorenzo Pieralisi <[email protected]>
On ARM multi-cluster systems coherency between cores running on different clusters is managed by the cache-coherent interconnect (CCI). It allows broadcasting of TLB invalidates and memory barriers and it guarantees cache coherency at system level.
This patch enables the basic infrastructure required in Linux to handle and programme the CCI component. The first implementation is based on a platform device, its relative DT compatible property and a simple programming interface.
Quoted 7 lines
Signed-off-by Nicolas Pitre <[email protected]>--- drivers/misc/Kconfig | 3 ++ drivers/misc/Makefile | 1 + drivers/misc/arm-cci.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/arm-cci.h | 30 ++++++++++++++Quoted 2 lines
How about '
drivers/bus/' considering CCI is an interconnect bus (though for coherency)Yes, I like that better.
Quoted 12 lines
Quoted 12 lines
4 files changed, 141 insertions(+)create mode 100644
drivers/misc/arm-cci.c
create mode 100644include/linux/arm-cci.hdrivers/misc/Kconfig +0
-0@@ -499,6 +499,9 @@ config USB_SWITCH_FSA9480 stereo and mono audio, video, microphone and UART data to use a common connector port.Quoted 3 lines
drivers/misc/Kconfig config USB_SWITCH_FSA9480 · line 502
Quoted 1 line
drivers/misc/Kconfig +1
-0+config ARM_CCIYou might want add depends on ARM big.LITTTLE otherwise it will break build for other arch's with random configurations.
As far as this patch goes, this is buildable on other architectures too. The next patch changes that though.
Quoted 47 lines
[..]
Quoted 45 lines
drivers/misc/arm-cci.c new file +39
-0@@ -0,0 +1,107 @@ +/* +* CCI support +* +* Copyright (C) 2012 ARM Ltd. +* Author: Lorenzo Pieralisi <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed "as is" WITHOUT ANY WARRANTY of any +* kind, whether express or implied; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/arm-cci.h> + +#define CCI400_EAG_OFFSET 0x4000 +#define CCI400_KF_OFFSET 0x5000 + +#define DRIVER_NAME "CCI" +struct cci_drvdata { +void __iomem *baseaddr; +spinlock_t lock; +}; + +static struct cci_drvdata *info; + +void disable_cci (int cluster) +{ +u32 cci_reg = cluster ?CCI400_KF_OFFSET :CCI400_EAG_OFFSET ; +writel_relaxed (0x0 , info->baseaddr + cci_reg); +Quoted 8 lines
drivers/misc/arm-cci.c line 44
Quoted 5 lines
drivers/misc/arm-cci.c new file +5
-0+while (readl_relaxed (info->baseaddr +0xc ) &0x1 ) + ; +} +EXPORT_SYMBOL_GPL (disable_cci); +Is more functionality going to be added for CCI driver. Having this much of driver code for just a
disable_cci()functions seems like overkill.
Quoted 1 line
Yes. More code will appear here to provide pmu functionalities, etc.
There's also a load of QoS configuration and other stuff which we could control, in principle. Normally the optimum settings are a property of the hardware, so maybe we would always just rely on the firmware to set up a sensible configuration.
Cheers
---Dave
PATCH 14/16 ARM: TC2: ensure powerdown-time data is flushed from cache
In reply to Nicolas PitreFrom: Dave Martin <[email protected]>
Non-local variables used by the CCI management function called after disabling the cache must be flushed out to main memory in advance, otherwise incoherency of those values may occur if they are sitting in the cache of some other CPU when cci_disable() executes.
This patch adds the appropriate flushing to the CCI driver to ensure that the relevant data is available in RAM ahead of time.
Because this creates a dependency on arch-specific cacheflushing functions, this patch also makes ARM_CCI depend on ARM.
--- drivers/misc/Kconfig | 1 + drivers/misc/arm-cci.c | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-)
drivers/misc/Kconfig +1 -0
@@ -501,6 +501,7 @@ config USB_SWITCH_FSA9480 config ARM_CCI bool "ARM CCI driver support" + depends on ARM source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig"
drivers/misc/arm-cci.c +19 -2
@@ -21,8 +21,16 @@#include <linux/slab.h> #include <linux/arm-cci.h> -#define CCI400_EAG_OFFSET 0x4000 -#define CCI400_KF_OFFSET 0x5000 +#include <asm/cacheflush.h> +#include <asm/memory.h> +#include <asm/outercache.h> + +#include <asm/irq_regs.h> +#include <asm/pmu.h> + +#define CCI400_PMCR 0x0100 +#define CCI400_EAG_OFFSET 0x4000 +#define CCI400_KF_OFFSET 0x5000#define DRIVER_NAME "CCI"struct cci_drvdata {
@@ -73,6 +81,15 @@ static int __devinit cci_driver_probe(struct platform_device *pdev) goto ioremap_err; } +/* +* Multi-cluster systems may need this data when non-coherent, during +* cluster power-up/power-down. Make sure it reaches main memory: +*/ +__cpuc_flush_dcache_area (info,sizeof *info); +__cpuc_flush_dcache_area (&info,sizeof info); +outer_clean_range (virt_to_phys (info),virt_to_phys (info +1 )); +outer_clean_range (virt_to_phys (&info),virt_to_phys (&info +1 )); +platform_set_drvdata (pdev, info);pr_info ("CCI loaded at %p\n" , info->baseaddr);
Signature · 1 line
1.8.0
PATCH 14/16 ARM: TC2: ensure powerdown-time data is flushed from cache
Quoted 1 line · Nicolas Pitre
From: Dave Martin <[email protected]>
To avoid confusion, the prefix in the subject line should be "CCI", not "TC2". Any platform which calls disable_cci() after turning caches off and/or disabling the SMP bit may fall foul of this otherwise ... i.e., any platform which has CCI.
Let me know if you want me to send you a modified patch.
Cheers
---Daave
Quoted 77 lines
Non-local variables used by the CCI management function called after disabling the cache must be flushed out to main memory in advance, otherwise incoherency of those values may occur if they are sitting in the cache of some other CPU when
cci_disable()executes.This patch adds the appropriate flushing to the CCI driver to ensure that the relevant data is available in RAM ahead of time.
Because this creates a dependency on arch-specific cacheflushing functions, this patch also makes
ARM_CCIdepend on ARM.Signed-off-by Dave Martin <[email protected]>Signed-off-by Nicolas Pitre <[email protected]>--- drivers/misc/Kconfig | 1 + drivers/misc/arm-cci.c | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-)drivers/misc/Kconfig +1
-0@@ -501,6 +501,7 @@ config USB_SWITCH_FSA9480 config ARM_CCI bool "ARM CCI driver support" + depends on ARM source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig"drivers/misc/arm-cci.c +19
-2@@ -21,8 +21,16 @@#include <linux/slab.h> #include <linux/arm-cci.h> -#define CCI400_EAG_OFFSET 0x4000 -#define CCI400_KF_OFFSET 0x5000 +#include <asm/cacheflush.h> +#include <asm/memory.h> +#include <asm/outercache.h> + +#include <asm/irq_regs.h> +#include <asm/pmu.h> + +#define CCI400_PMCR 0x0100 +#define CCI400_EAG_OFFSET 0x4000 +#define CCI400_KF_OFFSET 0x5000#define DRIVER_NAME "CCI"struct cci_drvdata {@@ -73,6 +81,15 @@ static int __devinit cci_driver_probe(struct platform_device *pdev) goto ioremap_err; } +/* +* Multi-cluster systems may need this data when non-coherent, during +* cluster power-up/power-down. Make sure it reaches main memory: +*/ +__cpuc_flush_dcache_area (info,sizeof *info); +__cpuc_flush_dcache_area (&info,sizeof info); +outer_clean_range (virt_to_phys (info),virt_to_phys (info +1 )); +outer_clean_range (virt_to_phys (&info),virt_to_phys (&info +1 )); +platform_set_drvdata (pdev, info);pr_info ("CCI loaded at %p\n" , info->baseaddr);Signature · 7 lines
1.8.0 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH 14/16 ARM: TC2: ensure powerdown-time data is flushed from cache
Quoted 5 lines · Dave Martin
Quoted 1 line · Nicolas Pitre
From: Dave Martin <[email protected]>
To avoid confusion, the prefix in the subject line should be "CCI", not "TC2".
Absolutely. This is my mistake as I removed the TC2 changes from your original patch to only keep the CCI ones, but forgot to update the patch title.
Nicolas
PATCH 14/16 ARM: TC2: ensure powerdown-time data is flushed from cache
Quoted 11 lines · Nicolas Pitre
Quoted 5 lines · Dave Martin
Quoted 1 line · Nicolas Pitre
From: Dave Martin <[email protected]>
To avoid confusion, the prefix in the subject line should be "CCI", not "TC2".
Absolutely. This is my mistake as I removed the TC2 changes from your original patch to only keep the CCI ones, but forgot to update the patch title.
Oh right. I was quite happy to believe it was my mistake :)
It makes sense to have two separate patches anyway, though.
Cheers
---Dave
PATCH 15/16 ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI
In reply to Nicolas PitreFrom: Dave Martin <[email protected]>
Add the required code to properly handle race free platform coherency exit to the DCSCB power down method.
The power_up_setup callback is used to enable the CCI interface for the cluster being brought up. This must be done in assembly before the kernel environment is entered.
Thanks to Achin Gupta and Nicolas Pitre for their help and contributions.
--- arch/arm/mach-vexpress/Kconfig | 1 + arch/arm/mach-vexpress/Makefile | 2 +- arch/arm/mach-vexpress/dcscb.c | 90 +++++++++++++++++++++++++++--------- arch/arm/mach-vexpress/dcscb_setup.S | 77 ++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 24 deletions(-) create mode 100644 arch/arm/mach-vexpress/dcscb_setup.S
arch/arm/mach-vexpress/Kconfig +1 -0
@@ -56,6 +56,7 @@ config ARCH_VEXPRESS_CA9X4 config ARCH_VEXPRESS_DCSCB bool "Dual Cluster System Control Block (DCSCB) support" depends on BIG_LITTLE + select ARM_CCI help Support for the Dual Cluster System Configuration Block (DCSCB). This is needed to provide CPU and cluster power management
arch/arm/mach-vexpress/Makefile +1 -1
@@ -6,6 +6,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ obj-y := v2m.o reset.o obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o -obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o +obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
arch/arm/mach-vexpress/dcscb.c +67 -23
132 lines not on this page · raw message
arch/arm/mach-vexpress/dcscb_setup.S new file +77 -0
77 lines not on this page · raw message
Signature · 1 line
1.8.0
PATCH 15/16 ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI
Quoted 21 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
Add the required code to properly handle race free platform coherency exit to the DCSCB power down method.
The
power_up_setupcallback is used to enable the CCI interface for the cluster being brought up. This must be done in assembly before the kernel environment is entered.Thanks to Achin Gupta and Nicolas Pitre for their help and contributions.
Signed-off-by Dave Martin <[email protected]>Signed-off-by Nicolas Pitre <[email protected]>--- arch/arm/mach-vexpress/Kconfig | 1 + arch/arm/mach-vexpress/Makefile | 2 +- arch/arm/mach-vexpress/dcscb.c | 90 +++++++++++++++++++++++++++--------- arch/arm/mach-vexpress/dcscb_setup.S | 77 ++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 24 deletions(-) create mode 100644 arch/arm/mach-vexpress/dcscb_setup.S
[...]
Quoted 1 line
arch/arm/mach-vexpress/dcscb.c +0
-0
[...]
arch/arm/mach-vexpress/dcscb.c static void __init dcscb_usage_count_init(void) · line 222
Quoted 5 lines
arch/arm/mach-vexpress/dcscb.c +1
-0@@ -179,6 +219,8 @@ static void __init dcscb_usage_count_init(void) dcscb_use_count[cpu][cluster] =1 ; } +extern void dcscb_power_up_setup (void );
The following change can be folded in to match the prototype to the underlying function.
Cheers
---Dave
arch/arm/mach-vexpress/dcscb.c +1 -1
@@ -219,7 +219,7 @@ static void __init dcscb_usage_count_init(void) dcscb_use_count[cpu][cluster] =1 ; } -extern void dcscb_power_up_setup (void ); +extern void dcscb_power_up_setup (unsigned int affinity_level);static int __init dcscb_init (void ) {
PATCH 15/16 ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI
In reply to Nicolas PitreQuoted 15 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
Add the required code to properly handle race free platform coherency exit to the DCSCB power down method.
The
power_up_setupcallback is used to enable the CCI interface for the cluster being brought up. This must be done in assembly before the kernel environment is entered.Thanks to Achin Gupta and Nicolas Pitre for their help and contributions.
Signed-off-by Dave Martin <[email protected]>Signed-off-by Nicolas Pitre <[email protected]>---
[..]
Quoted 54 lines
arch/arm/mach-vexpress/dcscb.c +6
-17@@ -15,6 +15,7 @@#include <linux/spinlock.h> #include <linux/errno.h> #include <linux/vexpress.h> +#include <linux/arm-cci.h> #include <asm/bL_entry.h> #include <asm/proc-fns.h> @@ -104,6 +105,8 @@ static void dcscb_power_down(void)pr_debug ("%s: cpu %u cluster %u\n" , __func__, cpu, cluster);BUG_ON (cpu >=4 || cluster >=2 ); +__bL_cpu_going_down (cpu, cluster); +arch_spin_lock (&dcscb_lock); dcscb_use_count[cpu][cluster]--;if (dcscb_use_count[cpu][cluster] ==0 ) {@@ -111,6 +114,7 @@ static void dcscb_power_down(void) rst_hold |= cpumask;if (((rst_hold | (rst_hold >>4 )) & cluster_mask) == cluster_mask) { rst_hold |= (1 <<8 ); +BUG_ON (__bL_cluster_state (cluster) !=CLUSTER_UP ); last_man = true; }writel (rst_hold, dcscb_base +RST_HOLD0 + cluster *4 );@@ -124,35 +128,71 @@ static void dcscb_power_down(void) skip_wfi = true; }else BUG (); -arch_spin_unlock (&dcscb_lock); -/* -* Now let's clean our L1 cache and shut ourself down. -* If we're the last CPU in this cluster then clean L2 too. -*/ - -/* -* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need -* a preliminary flush here for those CPUs. At least, that's -* the theory -- without the extra flush, Linux explodes on -* RTSM (maybe not needed anymore, to be investigated).. -*/ -flush_cache_louis (); -cpu_proc_fin (); +if (last_man &&__bL_outbound_enter_critical (cpu, cluster)) { +arch_spin_unlock (&dcscb_lock); -if (!last_man) { -flush_cache_louis (); - }else {
arch/arm/mach-vexpress/dcscb.c static void dcscb_power_down(void) · line 144
Quoted 10 lines
arch/arm/mach-vexpress/dcscb.c +9
-0+/* +* Flush all cache levels for this cluster. +* +* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need +* a preliminary flush here for those CPUs. At least, that's +* the theory -- without the extra flush, Linux explodes on +* RTSM (maybe not needed anymore, to be investigated). +*/ flush_cache_all (); +cpu_proc_fin ();/* disable allocation into internal caches*/
I see now. In previous patch I missed the cpu_proc_fin() which clears C bit
Quoted 26 lines
arch/arm/mach-vexpress/dcscb.c +25
-0+flush_cache_all (); + +/* +* This is a harmless no-op. On platforms with a real +* outer cache this might either be needed or not, +* depending on where the outer cache sits. +*/ outer_flush_all (); + +/* Disable local coherency by clearing the ACTLR "SMP" bit: */ + asmvolatile ( +"mrc p15, 0, ip, c1, c0, 1 \n\t" +"bic ip, ip, #(1 << 6) @ clear SMP bit \n\t" +"mcr p15, 0, ip, c1, c0, 1 \n\t" +"isb \n\t" +"dsb" + : : :"ip" ); + +/* +* Disable cluster-level coherency by masking +* incoming snoops and DVM messages: +*/ +disable_cci (cluster); + +__bL_outbound_leave_critical (cluster,CLUSTER_DOWN ); + }else {
arch/arm/mach-vexpress/dcscb.c static void dcscb_power_down(void) · line 180
Quoted 10 lines
arch/arm/mach-vexpress/dcscb.c +10
-0+arch_spin_unlock (&dcscb_lock); + +/* +* Flush the local CPU cache. +* +* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need +* a preliminary flush here for those CPUs. At least, that's +* the theory -- without the extra flush, Linux explodes on +* RTSM (maybe not needed anymore, to be investigated). +*/
This is expected if the entire code is not in one stack frame and the additional flush is needed to avoid possible stack corruption. This issue has been discussed in past on the list.
Quoted 14 lines
arch/arm/mach-vexpress/dcscb.c +12
-0+flush_cache_louis (); +cpu_proc_fin ();/* disable allocation into internal caches*/ +flush_cache_louis (); + +/* Disable local coherency by clearing the ACTLR "SMP" bit: */ + asmvolatile ( +"mrc p15, 0, ip, c1, c0, 1 \n\t" +"bic ip, ip, #(1 << 6) @ clear SMP bit \n\t" +"mcr p15, 0, ip, c1, c0, 1 \n\t" +"isb \n\t" +"dsb" + : : :"ip" ); }
arch/arm/mach-vexpress/dcscb.c static void dcscb_power_down(void) · line 198
Quoted 10 lines
arch/arm/mach-vexpress/dcscb.c +1
-6-/* Disable local coherency by clearing the ACTLR "SMP" bit: */ - asmvolatile ( -"mrc p15, 0, ip, c1, c0, 1 \n\t" -"bic ip, ip, #(1 << 6) @ clear SMP bit \n\t" -"mcr p15, 0, ip, c1, c0, 1" - : : :"ip" ); +__bL_cpu_down (cpu, cluster);/* Now we are prepared for power-down, do it: */ if (!skip_wfi)
Regards,
Santosh
PATCH 15/16 ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI
Quoted 14 lines · Santosh Shilimkar
arch/arm/mach-vexpress/dcscb.c static void dcscb_power_down(void) · line 180
Quoted 10 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
arch/arm/mach-vexpress/dcscb.c +8
-0+/* +* Flush the local CPU cache. +* +* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need +* a preliminary flush here for those CPUs. At least, that's +* the theory -- without the extra flush, Linux explodes on +* RTSM (maybe not needed anymore, to be investigated). +*/ This is expected if the entire code is not in one stack frame and the additional flush is needed to avoid possible stack corruption. This issue has been discussed in past on the list.
I missed that. Do you have a reference or pointer handy?
What is strange is that this is 100% reproducible on RTSM while this apparently is not an issue on real hardware so far.
Nicolas
PATCH 15/16 ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI
Quoted 17 lines · Nicolas Pitre
Quoted 14 lines · Santosh Shilimkar
arch/arm/mach-vexpress/dcscb.c static void dcscb_power_down(void) · line 180
Quoted 10 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
arch/arm/mach-vexpress/dcscb.c +8
-0+/* +* Flush the local CPU cache. +* +* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need +* a preliminary flush here for those CPUs. At least, that's +* the theory -- without the extra flush, Linux explodes on +* RTSM (maybe not needed anymore, to be investigated). +*/ This is expected if the entire code is not in one stack frame and the additional flush is needed to avoid possible stack corruption. This issue has been discussed in past on the list.
Quoted 5 lines
I missed that. Do you have a reference or pointer handy?
What is strange is that this is 100% reproducible on RTSM while this apparently is not an issue on real hardware so far.
I tried searching archives and realized the discussion was in private email thread. There are some bits and pieces on list but not all the information.
The main issue RMK pointed out is- An additional L1 flush needed to avoid the effective change of view of memory when the C bit is turned off, and the cache is no longer searched for local CPU accesses.
In your case dcscb_power_down() has updated the stack which can be hit in cache line and hence cache is dirty now. Then cpu_proc_fin() clears the C-bit and hence for sub sequent calls the L1 cache won't be searched. You then call flush_cache_all() which again updates the stack but avoids searching the L1 cache. So it overwrites previous saved stack frame. This seems to be an issue in your case as well.
Regards,
Santosh
PATCH 15/16 ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI
Quoted 31 lines · Santosh Shilimkar
Quoted 17 lines · Nicolas Pitre
Quoted 14 lines · Santosh Shilimkar
arch/arm/mach-vexpress/dcscb.c static void dcscb_power_down(void) · line 180
Quoted 10 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
arch/arm/mach-vexpress/dcscb.c +8
-0+/* +* Flush the local CPU cache. +* +* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need +* a preliminary flush here for those CPUs. At least, that's +* the theory -- without the extra flush, Linux explodes on +* RTSM (maybe not needed anymore, to be investigated). +*/ This is expected if the entire code is not in one stack frame and the additional flush is needed to avoid possible stack corruption. This issue has been discussed in past on the list.
Quoted 5 lines
I missed that. Do you have a reference or pointer handy?
What is strange is that this is 100% reproducible on RTSM while this apparently is not an issue on real hardware so far.
I tried searching archives and realized the discussion was in private email thread. There are some bits and pieces on list but not all the information.
The main issue RMK pointed out is- An additional L1 flush needed to avoid the effective change of view of memory when the C bit is turned off, and the cache is no longer searched for local CPU accesses.
Quoted 6 lines
In your case
dcscb_power_down()has updated the stack which can be hit in cache line and hence cache is dirty now. Thencpu_proc_fin()clears the C-bit and hence for sub sequent calls the L1 cache won't be searched. You then callflush_cache_all()which again updates the stack but avoids searching the L1 cache. So it overwrites previous saved stack frame. This seems to be an issue in your case as well.
On A15/A7 even with the C bit cleared the D-cache is searched, the situation above cannot happen and if it does we are facing a HW/model bug. If this code is run on A9 then we have a problem since there, when the C bit is cleared D-cache is not searched (and that's why the sequence above should be written in assembly with no data access whatsoever), but on A15/A7 we do not.
I have been running this code on TC2 for hours on end with nary a problem.
The sequence:
- clear C bit
- clean D-cache
- exit SMP
must be written in assembly with no data access whatsoever to make it portable across v7 implementations. I think I will write some docs and add them to the kernel to avoid further discussion on this topic.
FYI, the thread Santosh mentioned:
http://lists.infradead.org/pipermail/linux-arm-kernel/2012-May/099791.html
Lorenzo
PATCH 15/16 ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI
Quoted 39 lines · Lorenzo Pieralisi
Quoted 31 lines · Santosh Shilimkar
Quoted 17 lines · Nicolas Pitre
Quoted 14 lines · Santosh Shilimkar
arch/arm/mach-vexpress/dcscb.c static void dcscb_power_down(void) · line 180
Quoted 10 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
arch/arm/mach-vexpress/dcscb.c +8
-0+/* +* Flush the local CPU cache. +* +* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need +* a preliminary flush here for those CPUs. At least, that's +* the theory -- without the extra flush, Linux explodes on +* RTSM (maybe not needed anymore, to be investigated). +*/ This is expected if the entire code is not in one stack frame and the additional flush is needed to avoid possible stack corruption. This issue has been discussed in past on the list.
Quoted 5 lines
I missed that. Do you have a reference or pointer handy?
What is strange is that this is 100% reproducible on RTSM while this apparently is not an issue on real hardware so far.
I tried searching archives and realized the discussion was in private email thread. There are some bits and pieces on list but not all the information.
The main issue RMK pointed out is- An additional L1 flush needed to avoid the effective change of view of memory when the C bit is turned off, and the cache is no longer searched for local CPU accesses.
Quoted 6 lines
In your case
dcscb_power_down()has updated the stack which can be hit in cache line and hence cache is dirty now. Thencpu_proc_fin()clears the C-bit and hence for sub sequent calls the L1 cache won't be searched. You then callflush_cache_all()which again updates the stack but avoids searching the L1 cache. So it overwrites previous saved stack frame. This seems to be an issue in your case as well.
Quoted 7 lines
On A15/A7 even with the C bit cleared the D-cache is searched, the situation above cannot happen and if it does we are facing a HW/model bug. If this code is run on A9 then we have a problem since there, when the C bit is cleared D-cache is not searched (and that's why the sequence above should be written in assembly with no data access whatsoever), but on A15/A7 we do not.
Good point. May be model has modeled A9 and not A15 but in either case, lets be consistent for all ARMv7 machines at least to avoid people debugging similar issues. Many machines share code for ARMv7 processors so the best things is to stick to the sequence which works across all ARMv7 processors.
Quoted 2 lines
I have been running this code on TC2 for hours on end with nary a problem.
Thanks for the additional information.
Quoted 10 lines
The sequence:
- clear C bit
- clean D-cache
- exit SMPmust be written in assembly with no data access whatsoever to make it portable across v7 implementations. I think I will write some docs and add them to the kernel to avoid further discussion on this topic.
Best thing is to update the ARM Architecture Reference Manual because thats is what most of the time gets referred by many OS vendors.
Quoted 4 lines
FYI, the thread Santosh mentioned:
http://lists.infradead.org/pipermail/linux-arm-kernel/2012-May/099791.html
Yes. This is one of the relevant thread. Thanks.
Regards,
Santosh
PATCH 15/16 ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI
Quoted 47 lines · Santosh Shilimkar
Quoted 39 lines · Lorenzo Pieralisi
Quoted 31 lines · Santosh Shilimkar
Quoted 17 lines · Nicolas Pitre
Quoted 14 lines · Santosh Shilimkar
arch/arm/mach-vexpress/dcscb.c static void dcscb_power_down(void) · line 180
Quoted 10 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
arch/arm/mach-vexpress/dcscb.c +8
-0+/* +* Flush the local CPU cache. +* +* A15/A7 can hit in the cache with SCTLR.C=0, so we don't need +* a preliminary flush here for those CPUs. At least, that's +* the theory -- without the extra flush, Linux explodes on +* RTSM (maybe not needed anymore, to be investigated). +*/ This is expected if the entire code is not in one stack frame and the additional flush is needed to avoid possible stack corruption. This issue has been discussed in past on the list.
Quoted 5 lines
I missed that. Do you have a reference or pointer handy?
What is strange is that this is 100% reproducible on RTSM while this apparently is not an issue on real hardware so far.
I tried searching archives and realized the discussion was in private email thread. There are some bits and pieces on list but not all the information.
The main issue RMK pointed out is- An additional L1 flush needed to avoid the effective change of view of memory when the C bit is turned off, and the cache is no longer searched for local CPU accesses.
Quoted 6 lines
In your case
dcscb_power_down()has updated the stack which can be hit in cache line and hence cache is dirty now. Thencpu_proc_fin()clears the C-bit and hence for sub sequent calls the L1 cache won't be searched. You then callflush_cache_all()which again updates the stack but avoids searching the L1 cache. So it overwrites previous saved stack frame. This seems to be an issue in your case as well.Quoted 7 lines
On A15/A7 even with the C bit cleared the D-cache is searched, the situation above cannot happen and if it does we are facing a HW/model bug. If this code is run on A9 then we have a problem since there, when the C bit is cleared D-cache is not searched (and that's why the sequence above should be written in assembly with no data access whatsoever), but on A15/A7 we do not.
Quoted 5 lines
Good point. May be model has modeled A9 and not A15 but in either case, lets be consistent for all ARMv7 machines at least to avoid people debugging similar issues. Many machines share code for ARMv7 processors so the best things is to stick to the sequence which works across all ARMv7 processors.
Is it sufficient to clarify the comment to indicate that the code is not directly reusable for other CPU combinations?
DCSCB is incredibly platform-specific, and we would not expect to see it in other platforms.
Or do we consider the risk of people copying this code verbatim (including the "do not copy this code" comment) too high?
Cheers
---Dave
PATCH 15/16 ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI
Quoted 48 lines · Dave Martin
Quoted 47 lines · Santosh Shilimkar
Quoted 46 lines · Lorenzo Pieralisi
Quoted 31 lines · Santosh Shilimkar
Quoted 17 lines · Nicolas Pitre
Quoted 14 lines · Santosh Shilimkar
Review
Quoted 10 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
(quoted diff) +8
-0+ /* + * Flush the local CPU cache. + * + * A15/A7 can hit in the cache with SCTLR.C=0, so we don't need + * a preliminary flush here for those CPUs. At least, that's + * the theory -- without the extra flush, Linux explodes on + * RTSM (maybe not needed anymore, to be investigated). + */This is expected if the entire code is not in one stack frame and the additional flush is needed to avoid possible stack corruption. This issue has been discussed in past on the list.
Quoted 5 lines
I missed that. Do you have a reference or pointer handy?
What is strange is that this is 100% reproducible on RTSM while this apparently is not an issue on real hardware so far.
I tried searching archives and realized the discussion was in private email thread. There are some bits and pieces on list but not all the information.
The main issue RMK pointed out is- An additional L1 flush needed to avoid the effective change of view of memory when the C bit is turned off, and the cache is no longer searched for local CPU accesses.
Quoted 6 lines
In your case
dcscb_power_down()has updated the stack which can be hit in cache line and hence cache is dirty now. Thencpu_proc_fin()clears the C-bit and hence for sub sequent calls the L1 cache won't be searched. You then callflush_cache_all()which again updates the stack but avoids searching the L1 cache. So it overwrites previous saved stack frame. This seems to be an issue in your case as well.On A15/A7 even with the C bit cleared the D-cache is searched, the situation above cannot happen and if it does we are facing a HW/model bug. If this code is run on A9 then we have a problem since there, when the C bit is cleared D-cache is not searched (and that's why the sequence above should be written in assembly with no data access whatsoever), but on A15/A7 we do not.
Quoted 9 lines
Quoted 5 lines
Good point. May be model has modeled A9 and not A15 but in either case, lets be consistent for all ARMv7 machines at least to avoid people debugging similar issues. Many machines share code for ARMv7 processors so the best things is to stick to the sequence which works across all ARMv7 processors.
Is it sufficient to clarify the comment to indicate that the code is not directly reusable for other CPU combinations?
Thats not what I mean. CPU power down sequence is as per the ARM specs so there shouldn't be an issue in case people find it useful for other purposes. Thats other topc though.
Quoted 6 lines
DCSCB is incredibly platform-specific, and we would not expect to see it in other platforms.
Or do we consider the risk of people copying this code verbatim (including the "do not copy this code" comment) too high?
I am not sure what exactly you mean. We are discussing the sequence here on the basis of additional L1 cache flush. As mentioned clearly the documentation is the ARM ARM(which is generic for all ARMv7) missing to capture the need of the power
down code and stack usage which at least creates an issue on A9. Documenting that in code and mainly in ARM specs would avoid any further confusions.
Regards,
Santosh
PATCH 15/16 ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI
Quoted 55 lines · Santosh Shilimkar
Quoted 54 lines · Dave Martin
Quoted 52 lines · Santosh Shilimkar
Quoted 39 lines · Lorenzo Pieralisi
Quoted 31 lines · Santosh Shilimkar
Quoted 17 lines · Nicolas Pitre
Quoted 14 lines · Santosh Shilimkar
Review
Quoted 10 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
(quoted diff) +8
-0+ /* + * Flush the local CPU cache. + * + * A15/A7 can hit in the cache with SCTLR.C=0, so we don't need + * a preliminary flush here for those CPUs. At least, that's + * the theory -- without the extra flush, Linux explodes on + * RTSM (maybe not needed anymore, to be investigated). + */This is expected if the entire code is not in one stack frame and the additional flush is needed to avoid possible stack corruption. This issue has been discussed in past on the list.
Quoted 5 lines
I missed that. Do you have a reference or pointer handy?
What is strange is that this is 100% reproducible on RTSM while this apparently is not an issue on real hardware so far.
I tried searching archives and realized the discussion was in private email thread. There are some bits and pieces on list but not all the information.
The main issue RMK pointed out is- An additional L1 flush needed to avoid the effective change of view of memory when the C bit is turned off, and the cache is no longer searched for local CPU accesses.
Quoted 6 lines
In your case
dcscb_power_down()has updated the stack which can be hit in cache line and hence cache is dirty now. Thencpu_proc_fin()clears the C-bit and hence for sub sequent calls the L1 cache won't be searched. You then callflush_cache_all()which again updates the stack but avoids searching the L1 cache. So it overwrites previous saved stack frame. This seems to be an issue in your case as well.Quoted 7 lines
On A15/A7 even with the C bit cleared the D-cache is searched, the situation above cannot happen and if it does we are facing a HW/model bug. If this code is run on A9 then we have a problem since there, when the C bit is cleared D-cache is not searched (and that's why the sequence above should be written in assembly with no data access whatsoever), but on A15/A7 we do not.
Good point. May be model has modeled A9 and not A15 but in either case, lets be consistent for all ARMv7 machines at least to avoid people debugging similar issues. Many machines share code for ARMv7 processors so the best things is to stick to the sequence which works across all ARMv7 processors.
Quoted 6 lines
Quoted 3 lines
Is it sufficient to clarify the comment to indicate that the code is not directly reusable for other CPU combinations?
Thats not what I mean. CPU power down sequence is as per the ARM specs so there shouldn't be an issue in case people find it useful for other purposes. Thats other topc though.
If they run it on an A9 there is an issue and as hotplug code for vexpress proved, copy'n'paste is a real danger.
Quoted 3 lines
Quoted 3 lines
DCSCB is incredibly platform-specific, and we would not expect to see it in other platforms.
Agreed, but it is also the first example of power API implementation. Stubbing out this code in an assembly file valid for all v7 implementations is simple, provided we consider that worthwhile. I do. Or at least I can write the sequence up in /Documentation, how it should be done to be generic and describe the pitfalls.
Quoted 11 lines
Quoted 4 lines
Or do we consider the risk of people copying this code verbatim (including the "do not copy this code" comment) too high?
I am not sure what exactly you mean. We are discussing the sequence here on the basis of additional L1 cache flush. As mentioned clearly the documentation is the ARM ARM(which is generic for all ARMv7) missing to capture the need of the power
down code and stack usage which at least creates an issue on A9. Documenting that in code and mainly in ARM specs would avoid any further confusions.
Power down sequence is defined explicitly in A15 and A7 TRMs. I do not think they should write "do not use the stack or cacheable memory that can result in dirty lines" in betweeen the power down steps. Once you know the C bit behaviour coding follows. True, they might add this for A9, and I asked that, to no avail, for internal reasons.
Documenting it in the kernel won't hurt either. And to answer Dave, I think that copy'n'paste verbatim is a risk we should not run, unless we are willing to be on the lookout for bugs. I can write up some documentation that we can merge along with the power API.
Lorenzo
PATCH 15/16 ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI
Quoted 88 lines · Lorenzo Pieralisi
Quoted 55 lines · Santosh Shilimkar
Quoted 54 lines · Dave Martin
Quoted 52 lines · Santosh Shilimkar
Quoted 39 lines · Lorenzo Pieralisi
Quoted 31 lines · Santosh Shilimkar
Quoted 17 lines · Nicolas Pitre
Quoted 14 lines · Santosh Shilimkar
Review
Quoted 10 lines · Nicolas Pitre
From: Dave Martin <[email protected]>
(quoted diff) +8
-0+ /* + * Flush the local CPU cache. + * + * A15/A7 can hit in the cache with SCTLR.C=0, so we don't need + * a preliminary flush here for those CPUs. At least, that's + * the theory -- without the extra flush, Linux explodes on + * RTSM (maybe not needed anymore, to be investigated). + */This is expected if the entire code is not in one stack frame and the additional flush is needed to avoid possible stack corruption. This issue has been discussed in past on the list.
Quoted 5 lines
I missed that. Do you have a reference or pointer handy?
What is strange is that this is 100% reproducible on RTSM while this apparently is not an issue on real hardware so far.
I tried searching archives and realized the discussion was in private email thread. There are some bits and pieces on list but not all the information.
The main issue RMK pointed out is- An additional L1 flush needed to avoid the effective change of view of memory when the C bit is turned off, and the cache is no longer searched for local CPU accesses.
Quoted 6 lines
In your case
dcscb_power_down()has updated the stack which can be hit in cache line and hence cache is dirty now. Thencpu_proc_fin()clears the C-bit and hence for sub sequent calls the L1 cache won't be searched. You then callflush_cache_all()which again updates the stack but avoids searching the L1 cache. So it overwrites previous saved stack frame. This seems to be an issue in your case as well.Quoted 7 lines
On A15/A7 even with the C bit cleared the D-cache is searched, the situation above cannot happen and if it does we are facing a HW/model bug. If this code is run on A9 then we have a problem since there, when the C bit is cleared D-cache is not searched (and that's why the sequence above should be written in assembly with no data access whatsoever), but on A15/A7 we do not.
Good point. May be model has modeled A9 and not A15 but in either case, lets be consistent for all ARMv7 machines at least to avoid people debugging similar issues. Many machines share code for ARMv7 processors so the best things is to stick to the sequence which works across all ARMv7 processors.
Quoted 6 lines
Quoted 3 lines
Is it sufficient to clarify the comment to indicate that the code is not directly reusable for other CPU combinations?
Thats not what I mean. CPU power down sequence is as per the ARM specs so there shouldn't be an issue in case people find it useful for other purposes. Thats other topc though.
If they run it on an A9 there is an issue and as hotplug code for vexpress proved, copy'n'paste is a real danger.
Quoted 3 lines
Quoted 3 lines
DCSCB is incredibly platform-specific, and we would not expect to see it in other platforms.
Agreed, but it is also the first example of power API implementation. Stubbing out this code in an assembly file valid for all v7 implementations is simple, provided we consider that worthwhile. I do. Or at least I can write the sequence up in /Documentation, how it should be done to be generic and describe the pitfalls.
Quoted 11 lines
Quoted 4 lines
Or do we consider the risk of people copying this code verbatim (including the "do not copy this code" comment) too high?
I am not sure what exactly you mean. We are discussing the sequence here on the basis of additional L1 cache flush. As mentioned clearly the documentation is the ARM ARM(which is generic for all ARMv7) missing to capture the need of the power
down code and stack usage which at least creates an issue on A9. Documenting that in code and mainly in ARM specs would avoid any further confusions.
Quoted 6 lines
Power down sequence is defined explicitly in A15 and A7 TRMs. I do not think they should write "do not use the stack or cacheable memory that can result in dirty lines" in betweeen the power down steps. Once you know the C bit behaviour coding follows. True, they might add this for A9, and I asked that, to no avail, for internal reasons.
Fair enough.
Quoted 5 lines
Documenting it in the kernel won't hurt either. And to answer Dave, I think that copy'n'paste verbatim is a risk we should not run, unless we are willing to be on the lookout for bugs. I can write up some documentation that we can merge along with the power API.
+1
Regards,
Santosh
PATCH 16/16 ARM: vexpress/dcscb: probe via device tree
In reply to Nicolas PitreThis allows for the DCSCB support to be compiled in and selected at run time.
--- arch/arm/mach-vexpress/dcscb.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
arch/arm/mach-vexpress/dcscb.c +7 -4
@@ -14,6 +14,7 @@#include <linux/io.h> #include <linux/spinlock.h> #include <linux/errno.h> +#include <linux/of_address.h> #include <linux/vexpress.h> #include <linux/arm-cci.h>
@@ -22,8 +23,6 @@#include <asm/cacheflush.h> -#define DCSCB_PHYS_BASE 0x60000000 -#define RST_HOLD0 0x0#define RST_HOLD1 0x4#define SYS_SWRESET 0x8
@@ -223,12 +222,16 @@ extern void dcscb_power_up_setup(void);static int __init dcscb_init (void ) { +struct device_node *node;unsigned int cfg;int ret; - dcscb_base =ioremap (DCSCB_PHYS_BASE ,0x1000 ); + node =of_find_compatible_node (NULL ,NULL ,"arm,dcscb" ); +if (!node) +return -ENODEV ; + dcscb_base=of_iomap (node,0 );if (!dcscb_base) -return -ENOMEM ; +return -EINVAL ; cfg =readl_relaxed (dcscb_base +DCS_CFG_R ); dcscb_cluster_cpu_mask[0 ] = (1 << (((cfg >>16 ) >> (0 <<2 )) &0xf )) -1 ; dcscb_cluster_cpu_mask[1 ] = (1 << (((cfg >>16 ) >> (1 <<2 )) &0xf )) -1 ;
Signature · 1 line
1.8.0
cover big.LITTLE low-level CPU and cluster power management
In reply to Nicolas PitreQuoted 25 lines · Nicolas Pitre
This is the initial public posting of the initial support for big.LITTLE. Included here is the code required to safely power up and down CPUs in a b.L system, whether this is via CPU hotplug, a cpuidle driver or the Linaro b.L in-kernel switcher[*] on top of this. Only SMP secondary boot and CPU hotplug support is included at this time. Getting to this point already represents a significcant chunk of code as illustrated by the diffstat below.
This work was presented at Linaro Connect in Copenhagen by Dave Martin and myself. The presentation slides are available here:
http://www.linaro.org/documents/download/f3569407bb1fb8bde0d6da80e285b832508f92f57223c
The code is now stable on both Fast Models as well as Virtual Express TC2 and ready for public review.
Platform support is included for Fast Models implementing the Cortex-A15x4-A7x4 and Cortex-A15x1-A7x1 configurations. To allow successful compilation, I also included a preliminary version of the CCI400 driver from Lorenzo Pieralisi.
Support for actual hardware such as Vexpress TC2 should come later, once the basic infrastructure from this series is merged. A few DT bindings are used but not yet documented.
Quoted 8 lines
This series is made of the following parts:
Low-level support code:
[PATCH 01/16] ARM: b.L: secondary kernel entry code [PATCH 02/16] ARM: b.L: introduce the CPU/cluster power API [PATCH 03/16] ARM: b.L: introduce helpers for platform coherency [PATCH 04/16] ARM: b.L: Add baremetal voting mutexes [PATCH 05/16] ARM:bL_head: vlock-based first man election
After a quick scan, I have a basic question. How are any of these specific to big.LITTLE? Isn't all this just general multi-cluster support?
Rob
Quoted 58 lines
Adaptation layer to hook with the generic kernel infrastructure:
[PATCH 06/16] ARM: b.L: generic SMP secondary bringup and hotplug [PATCH 07/16] ARM:bL_platsmp.c: close the kernel entry gate before [PATCH 08/16] ARM:bL_platsmp.c: make sure the GIC interface of a [PATCH 09/16] ARM: vexpress: Select the correct SMP operations atFast Models support:
[PATCH 10/16] ARM: vexpress: introduce DCSCB support [PATCH 11/16] ARM: vexpress/dcscb: add CPU use counts to the power [PATCH 12/16] ARM: vexpress/dcscb: do not hardcode number of CPUs [PATCH 13/16] drivers: misc: add ARM CCI support
[PATCH 14/16] ARM: TC2: ensure powerdown-time data is flushed from [PATCH 15/16] ARM: vexpress/dcscb: handle platform coherency [PATCH 16/16] ARM: vexpress/dcscb: probe via device treeHere's the diffstat:
.../big.LITTLE/cluster-pm-race-avoidance.txt | 498 ++++++++++++++++++ Documentation/arm/big.LITTLE/vlocks.txt | 211 ++++++++ arch/arm/Kconfig | 6 + arch/arm/common/Makefile | 3 + arch/arm/common/bL_entry.c | 278 ++++++++++ arch/arm/common/bL_head.S | 232 ++++++++ arch/arm/common/bL_platsmp.c | 85 +++ arch/arm/common/gic.c | 6 + arch/arm/common/vlock.S | 108 ++++ arch/arm/common/vlock.h | 43 ++ arch/arm/include/asm/bL_entry.h | 189 +++++++ arch/arm/include/asm/hardware/gic.h | 2 + arch/arm/include/asm/mach/arch.h | 3 + arch/arm/kernel/setup.c | 5 +- arch/arm/mach-vexpress/Kconfig | 9 + arch/arm/mach-vexpress/Makefile | 1 + arch/arm/mach-vexpress/core.h | 2 + arch/arm/mach-vexpress/dcscb.c | 257 +++++++++ arch/arm/mach-vexpress/dcscb_setup.S | 77 +++ arch/arm/mach-vexpress/platsmp.c | 12 + arch/arm/mach-vexpress/v2m.c | 2 +- drivers/misc/Kconfig | 4 + drivers/misc/Makefile | 1 + drivers/misc/arm-cci.c | 124 +++++ include/linux/arm-cci.h | 30 ++ 25 files changed, 2186 insertions(+), 2 deletions(-)Review comments are welcome!
[*] General design information on the b.L switcher can be found here:
http://lwn.net/Articles/481055/
However the code is only accessible to Linaro members for the time being._______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
cover big.LITTLE low-level CPU and cluster power management
Quoted 35 lines · Rob Herring
Quoted 25 lines · Nicolas Pitre
This is the initial public posting of the initial support for big.LITTLE. Included here is the code required to safely power up and down CPUs in a b.L system, whether this is via CPU hotplug, a cpuidle driver or the Linaro b.L in-kernel switcher[*] on top of this. Only SMP secondary boot and CPU hotplug support is included at this time. Getting to this point already represents a significcant chunk of code as illustrated by the diffstat below.
This work was presented at Linaro Connect in Copenhagen by Dave Martin and myself. The presentation slides are available here:
http://www.linaro.org/documents/download/f3569407bb1fb8bde0d6da80e285b832508f92f57223c
The code is now stable on both Fast Models as well as Virtual Express TC2 and ready for public review.
Platform support is included for Fast Models implementing the Cortex-A15x4-A7x4 and Cortex-A15x1-A7x1 configurations. To allow successful compilation, I also included a preliminary version of the CCI400 driver from Lorenzo Pieralisi.
Support for actual hardware such as Vexpress TC2 should come later, once the basic infrastructure from this series is merged. A few DT bindings are used but not yet documented.
Quoted 8 lines
This series is made of the following parts:
Low-level support code:
[PATCH 01/16] ARM: b.L: secondary kernel entry code [PATCH 02/16] ARM: b.L: introduce the CPU/cluster power API [PATCH 03/16] ARM: b.L: introduce helpers for platform coherency [PATCH 04/16] ARM: b.L: Add baremetal voting mutexes [PATCH 05/16] ARM:bL_head: vlock-based first man election
Quoted 2 lines
After a quick scan, I have a basic question. How are any of these specific to big.LITTLE? Isn't all this just general multi-cluster support?
It is. However, b.L is what brought the need for this multi-cluster support, and being first it gave the name.
I've been pondering about naming this code differently so not to imply b.L but I couldn't come up with a good name/prefix. The bL_ prefix is really hard to beat! So I decided to put it off until there is actually some non b.L users for this code, at which point we can trivially rename it.
Nicolas
cover big.LITTLE low-level CPU and cluster power management
In reply to Nicolas PitreHi Nicolas,
Quoted 7 lines · Nicolas Pitre
This is the initial public posting of the initial support for big.LITTLE. Included here is the code required to safely power up and down CPUs in a b.L system, whether this is via CPU hotplug, a cpuidle driver or the Linaro b.L in-kernel switcher[*] on top of this. Only SMP secondary boot and CPU hotplug support is included at this time. Getting to this point already represents a significcant chunk of code as illustrated by the diffstat below.
I've just started going through this, so I have some comments on the first few patches. I'll try and get through the rest of it soon (but Christoffer is kicking me to look at kvm again too :)
Quoted 6 lines
Low-level support code:
[PATCH 01/16] ARM: b.L: secondary kernel entry code [PATCH 02/16] ARM: b.L: introduce the CPU/cluster power API [PATCH 03/16] ARM: b.L: introduce helpers for platform coherency [PATCH 04/16] ARM: b.L: Add baremetal voting mutexes [PATCH 05/16] ARM:bL_head: vlock-based first man election
I got this far, so I'll send my comments as replies to those.
Quoted 6 lines
Adaptation layer to hook with the generic kernel infrastructure:
[PATCH 06/16] ARM: b.L: generic SMP secondary bringup and hotplug [PATCH 07/16] ARM:bL_platsmp.c: close the kernel entry gate before [PATCH 08/16] ARM:bL_platsmp.c: make sure the GIC interface of a [PATCH 09/16] ARM: vexpress: Select the correct SMP operations at
Quoted 8 lines
Fast Models support:
[PATCH 10/16] ARM: vexpress: introduce DCSCB support [PATCH 11/16] ARM: vexpress/dcscb: add CPU use counts to the power [PATCH 12/16] ARM: vexpress/dcscb: do not hardcode number of CPUs [PATCH 13/16] drivers: misc: add ARM CCI support
[PATCH 14/16] ARM: TC2: ensure powerdown-time data is flushed from [PATCH 15/16] ARM: vexpress/dcscb: handle platform coherency [PATCH 16/16] ARM: vexpress/dcscb: probe via device tree
These last six really need Pawel Moll on CC. He's on holiday at the moment, but he'll be back at the end of the month so I suggest pinging him then so they don't get lost.
Will
cover big.LITTLE low-level CPU and cluster power management
In reply to Nicolas PitreHi Nicolas,
Quoted 40 lines · Nicolas Pitre
This is the initial public posting of the initial support for big.LITTLE. Included here is the code required to safely power up and down CPUs in a b.L system, whether this is via CPU hotplug, a cpuidle driver or the Linaro b.L in-kernel switcher[*] on top of this. Only SMP secondary boot and CPU hotplug support is included at this time. Getting to this point already represents a significcant chunk of code as illustrated by the diffstat below.
This work was presented at Linaro Connect in Copenhagen by Dave Martin and myself. The presentation slides are available here:
http://www.linaro.org/documents/download/f3569407bb1fb8bde0d6da80e285b832508f92f57223c
The code is now stable on both Fast Models as well as Virtual Express TC2 and ready for public review.
Platform support is included for Fast Models implementing the Cortex-A15x4-A7x4 and Cortex-A15x1-A7x1 configurations. To allow successful compilation, I also included a preliminary version of the CCI400 driver from Lorenzo Pieralisi.
Support for actual hardware such as Vexpress TC2 should come later, once the basic infrastructure from this series is merged. A few DT bindings are used but not yet documented.
This series is made of the following parts:
Low-level support code:
[PATCH 01/16] ARM: b.L: secondary kernel entry code [PATCH 02/16] ARM: b.L: introduce the CPU/cluster power API [PATCH 03/16] ARM: b.L: introduce helpers for platform coherency [PATCH 04/16] ARM: b.L: Add baremetal voting mutexes [PATCH 05/16] ARM:bL_head: vlock-based first man electionAdaptation layer to hook with the generic kernel infrastructure:
[PATCH 06/16] ARM: b.L: generic SMP secondary bringup and hotplug [PATCH 07/16] ARM:bL_platsmp.c: close the kernel entry gate before [PATCH 08/16] ARM:bL_platsmp.c: make sure the GIC interface of a [PATCH 09/16] ARM: vexpress: Select the correct SMP operations at
Quoted 9 lines
Fast Models support:
[PATCH 10/16] ARM: vexpress: introduce DCSCB support [PATCH 11/16] ARM: vexpress/dcscb: add CPU use counts to the power [PATCH 12/16] ARM: vexpress/dcscb: do not hardcode number of CPUs [PATCH 13/16] drivers: misc: add ARM CCI support
[PATCH 14/16] ARM: TC2: ensure powerdown-time data is flushed from [PATCH 15/16] ARM: vexpress/dcscb: handle platform coherency [PATCH 16/16] ARM: vexpress/dcscb: probe via device tree
Thanks for introducing this series.
I am taking a look at this series. It introduced an algorithm for syncing and avoid racing when syncing the power status of clusters and CPUs. Do you think these codes could have a chance to become a generic framework?
The Tegra chip series had a similar design for CPU clusters and it had limitation that the CPU0 always needs to be the last CPU to be shut down before cluster power down as well. I believe it can also get benefits of this works. We indeed need a similar algorithm to sync CPUs power status before cluster power down and switching.
The "bL_entry.c", "bL_entry.S", "bL_entry.h", "vlock.h" and "vlock.S" looks have a chance to be a common framework for ARM platform even if it just support one cluster. Because some systems had the limitations for cluster power down. That's why the coupled cpuidle been introduced. And this framework could be enabled automatically if platform dependent or by menuconfig.
For ex,
select CPUS_CLUSTERS_POWER_SYNC_FRAMEWORK if SMP && CPU_PM
How do you think of this suggestion?
BTW, some questions...
1. The "bL_entry_point" looks like a first run function when CPUs just power up, then jumping to original reset vector that it should be called. Do you think this should be a function and be called by reset handler? Or in your design, this should be called as soon as possible when the CPU power be resumed?
2. Does the Last_man mechanism should implement in platform specific code to check something like cpu_online_status and if there is a limitation for the specific last CPU to be powered down?
Thanks,
Joseph
cover big.LITTLE low-level CPU and cluster power management
Quoted 53 lines · Joseph Lo
Hi Nicolas,
Quoted 40 lines · Nicolas Pitre
This is the initial public posting of the initial support for big.LITTLE. Included here is the code required to safely power up and down CPUs in a b.L system, whether this is via CPU hotplug, a cpuidle driver or the Linaro b.L in-kernel switcher[*] on top of this. Only SMP secondary boot and CPU hotplug support is included at this time. Getting to this point already represents a significcant chunk of code as illustrated by the diffstat below.
This work was presented at Linaro Connect in Copenhagen by Dave Martin and myself. The presentation slides are available here:
http://www.linaro.org/documents/download/f3569407bb1fb8bde0d6da80e285b832508f92f57223c
The code is now stable on both Fast Models as well as Virtual Express TC2 and ready for public review.
Platform support is included for Fast Models implementing the Cortex-A15x4-A7x4 and Cortex-A15x1-A7x1 configurations. To allow successful compilation, I also included a preliminary version of the CCI400 driver from Lorenzo Pieralisi.
Support for actual hardware such as Vexpress TC2 should come later, once the basic infrastructure from this series is merged. A few DT bindings are used but not yet documented.
This series is made of the following parts:
Low-level support code:
[PATCH 01/16] ARM: b.L: secondary kernel entry code [PATCH 02/16] ARM: b.L: introduce the CPU/cluster power API [PATCH 03/16] ARM: b.L: introduce helpers for platform coherency [PATCH 04/16] ARM: b.L: Add baremetal voting mutexes [PATCH 05/16] ARM:bL_head: vlock-based first man electionAdaptation layer to hook with the generic kernel infrastructure:
[PATCH 06/16] ARM: b.L: generic SMP secondary bringup and hotplug [PATCH 07/16] ARM:bL_platsmp.c: close the kernel entry gate before [PATCH 08/16] ARM:bL_platsmp.c: make sure the GIC interface of a [PATCH 09/16] ARM: vexpress: Select the correct SMP operations atQuoted 9 lines
Fast Models support:
[PATCH 10/16] ARM: vexpress: introduce DCSCB support [PATCH 11/16] ARM: vexpress/dcscb: add CPU use counts to the power [PATCH 12/16] ARM: vexpress/dcscb: do not hardcode number of CPUs [PATCH 13/16] drivers: misc: add ARM CCI support
[PATCH 14/16] ARM: TC2: ensure powerdown-time data is flushed from [PATCH 15/16] ARM: vexpress/dcscb: handle platform coherency [PATCH 16/16] ARM: vexpress/dcscb: probe via device tree
Quoted 5 lines
Thanks for introducing this series.
I am taking a look at this series. It introduced an algorithm for syncing and avoid racing when syncing the power status of clusters and CPUs. Do you think these codes could have a chance to become a generic framework?
Yes. As I mentioned before, the bL_ prefix is implied only by the fact that big.LITTLE was the motivation for creating this code.
Quoted 1 line
The Tegra chip series had a similar design for CPU clusters and it
had
Quoted 11 lines
limitation that the CPU0 always needs to be the last CPU to be shut down before cluster power down as well. I believe it can also get benefits of this works. We indeed need a similar algorithm to sync CPUs power status before cluster power down and switching.
The "
bL_entry.c", "bL_entry.S", "bL_entry.h", "vlock.h" and "vlock.S" looks have a chance to be a common framework for ARM platform even if it just support one cluster. Because some systems had the limitations for cluster power down. That's why the coupled cpuidle been introduced. And this framework could be enabled automatically if platform dependent or by menuconfig.
Absolutely.
Quoted 4 lines
For ex,
select CPUS_CLUSTERS_POWER_SYNC_FRAMEWORK if SMP && CPU_PMHow do you think of this suggestion?
I'd prefer a more concise name though.
Quoted 6 lines
BTW, some questions...
1. The "bL_entry_point" looks like a first run function when CPUs just power up, then jumping to original reset vector that it should be called. Do you think this should be a function and be called by reset handler? Or in your design, this should be called as soon as possible when the CPU power be resumed?
This should be called as soon as possible.
Quoted 3 lines
2. Does the
Last_manmechanism should implement in platform specific code to check something likecpu_online_statusand if there is a limitation for the specific last CPU to be powered down?
The selection of the last man is accomplished using a platform specific mechanism. By the time this has to be done, the CPU is already dead as far as the Linux kernel is concerned, and therefore the generic CPU map is not reliable. In the DCSCB case we simply look at the hardware reset register being modified to directly determine the last man. On TC2 (not yet posted) we have to keep a local map of online CPUs.
In your case, the selection of the last man would simply be forced on CPU0.
Nicolas
cover big.LITTLE low-level CPU and cluster power management
Quoted 31 lines · Nicolas Pitre
Quoted 13 lines · Joseph Lo
Hi Nicolas,
Quoted 9 lines · Nicolas Pitre
This is the initial public posting of the initial support for big.LITTLE. Included here is the code required to safely power up and down CPUs in a b.L system, whether this is via CPU hotplug, a cpuidle driver or the Linaro b.L in-kernel switcher[*] on top of this. Only SMP secondary boot and CPU hotplug support is included at this time. Getting to this point already represents a significcant chunk of code as illustrated by the diffstat below.
Quoted 5 lines
Thanks for introducing this series.
I am taking a look at this series. It introduced an algorithm for syncing and avoid racing when syncing the power status of clusters and CPUs. Do you think these codes could have a chance to become a generic framework?Yes. As I mentioned before, the
bL_prefix is implied only by the fact that big.LITTLE was the motivation for creating this code.Quoted 1 line
The Tegra chip series had a similar design for CPU clusters and it
had
Quoted 5 lines
limitation that the CPU0 always needs to be the last CPU to be shut down before cluster power down as well. I believe it can also get benefits of this works. We indeed need a similar algorithm to sync CPUs power status before cluster power down and switching.
Quoted 9 lines
Quoted 6 lines
The "
bL_entry.c", "bL_entry.S", "bL_entry.h", "vlock.h" and "vlock.S" looks have a chance to be a common framework for ARM platform even if it just support one cluster. Because some systems had the limitations for cluster power down. That's why the coupled cpuidle been introduced. And this framework could be enabled automatically if platform dependent or by menuconfig.Absolutely.
So do you have a plan to make it become a generic framework in this series or later work?
(And I will add some common power sync wrapper functions that based on this framework for all Tegra series.)
Quoted 8 lines
Quoted 5 lines
For ex,
select CPUS_CLUSTERS_POWER_SYNC_FRAMEWORK if SMP && CPU_PMHow do you think of this suggestion?
I'd prefer a more concise name though.
Sure. :-)
Thanks,
Joseph
cover big.LITTLE low-level CPU and cluster power management
Quoted 39 lines · Joseph Lo
Quoted 38 lines · Nicolas Pitre
Quoted 13 lines · Joseph Lo
Hi Nicolas,
Quoted 9 lines · Nicolas Pitre
This is the initial public posting of the initial support for big.LITTLE. Included here is the code required to safely power up and down CPUs in a b.L system, whether this is via CPU hotplug, a cpuidle driver or the Linaro b.L in-kernel switcher[*] on top of this. Only SMP secondary boot and CPU hotplug support is included at this time. Getting to this point already represents a significcant chunk of code as illustrated by the diffstat below.
Quoted 5 lines
Thanks for introducing this series.
I am taking a look at this series. It introduced an algorithm for syncing and avoid racing when syncing the power status of clusters and CPUs. Do you think these codes could have a chance to become a generic framework?Yes. As I mentioned before, the
bL_prefix is implied only by the fact that big.LITTLE was the motivation for creating this code.Quoted 1 line
The Tegra chip series had a similar design for CPU clusters and it
had
Quoted 11 lines
limitation that the CPU0 always needs to be the last CPU to be shut down before cluster power down as well. I believe it can also get benefits of this works. We indeed need a similar algorithm to sync CPUs power status before cluster power down and switching.
The "
bL_entry.c", "bL_entry.S", "bL_entry.h", "vlock.h" and "vlock.S" looks have a chance to be a common framework for ARM platform even if it just support one cluster. Because some systems had the limitations for cluster power down. That's why the coupled cpuidle been introduced. And this framework could be enabled automatically if platform dependent or by menuconfig.
Quoted 4 lines
Quoted 2 lines
Absolutely.
So do you have a plan to make it become a generic framework in this series or later work?
It is already generic, except for the name. In other words, you could start using this code already.
I'm still debating a good substitute for the bL_ prefix in this series to give it the appearance of generic code.
Quoted 2 lines
(And I will add some common power sync wrapper functions that based on this framework for all Tegra series.)
Great!
Nicolas
cover big.LITTLE low-level CPU and cluster power management
Quoted 9 lines · Nicolas Pitre
Quoted 2 lines · Joseph Lo
So do you have a plan to make it become a generic framework in this series or later work?
It is already generic, except for the name. In other words, you could start using this code already.
I'm still debating a good substitute for the
bL_prefix in this series to give it the appearance of generic code.
mc_?
Signature · 1 line
Catalin
cover big.LITTLE low-level CPU and cluster power management
Quoted 5 lines · Catalin Marinas
Quoted 4 lines · Nicolas Pitre
Quoted 2 lines · Joseph Lo
So do you have a plan to make it become a generic framework in this series or later work?
Quoted 7 lines
Quoted 5 lines
It is already generic, except for the name. In other words, you could start using this code already.
I'm still debating a good substitute for the
bL_prefix in this series to give it the appearance of generic code.
mc_?
Bah. :-/
Nicolas
cover big.LITTLE low-level CPU and cluster power management
Quoted 10 lines · Nicolas Pitre
Quoted 8 lines · Catalin Marinas
Quoted 7 lines · Nicolas Pitre
Quoted 2 lines · Joseph Lo
So do you have a plan to make it become a generic framework in this series or later work?
It is already generic, except for the name. In other words, you could start using this code already.
Quoted 6 lines
Quoted 4 lines
Quoted 2 lines
I'm still debating a good substitute for the
bL_prefix in this series to give it the appearance of generic code.
mc_?Bah. :-/
Like in multi-cluster (not "Hammer time" ;). But if they are not even cluster dependent, a simple mp_ would also do.
Signature · 1 line
Catalin
cover big.LITTLE low-level CPU and cluster power management
In reply to Nicolas PitreQuoted 88 lines · Nicolas Pitre
Quoted 53 lines · Joseph Lo
Hi Nicolas,
Quoted 40 lines · Nicolas Pitre
This is the initial public posting of the initial support for big.LITTLE. Included here is the code required to safely power up and down CPUs in a b.L system, whether this is via CPU hotplug, a cpuidle driver or the Linaro b.L in-kernel switcher[*] on top of this. Only SMP secondary boot and CPU hotplug support is included at this time. Getting to this point already represents a significcant chunk of code as illustrated by the diffstat below.
This work was presented at Linaro Connect in Copenhagen by Dave Martin and myself. The presentation slides are available here:
http://www.linaro.org/documents/download/f3569407bb1fb8bde0d6da80e285b832508f92f57223c
The code is now stable on both Fast Models as well as Virtual Express TC2 and ready for public review.
Platform support is included for Fast Models implementing the Cortex-A15x4-A7x4 and Cortex-A15x1-A7x1 configurations. To allow successful compilation, I also included a preliminary version of the CCI400 driver from Lorenzo Pieralisi.
Support for actual hardware such as Vexpress TC2 should come later, once the basic infrastructure from this series is merged. A few DT bindings are used but not yet documented.
This series is made of the following parts:
Low-level support code:
[PATCH 01/16] ARM: b.L: secondary kernel entry code [PATCH 02/16] ARM: b.L: introduce the CPU/cluster power API [PATCH 03/16] ARM: b.L: introduce helpers for platform coherency [PATCH 04/16] ARM: b.L: Add baremetal voting mutexes [PATCH 05/16] ARM:bL_head: vlock-based first man electionAdaptation layer to hook with the generic kernel infrastructure:
[PATCH 06/16] ARM: b.L: generic SMP secondary bringup and hotplug [PATCH 07/16] ARM:bL_platsmp.c: close the kernel entry gate before [PATCH 08/16] ARM:bL_platsmp.c: make sure the GIC interface of a [PATCH 09/16] ARM: vexpress: Select the correct SMP operations atQuoted 9 lines
Fast Models support:
[PATCH 10/16] ARM: vexpress: introduce DCSCB support [PATCH 11/16] ARM: vexpress/dcscb: add CPU use counts to the power [PATCH 12/16] ARM: vexpress/dcscb: do not hardcode number of CPUs [PATCH 13/16] drivers: misc: add ARM CCI support
[PATCH 14/16] ARM: TC2: ensure powerdown-time data is flushed from [PATCH 15/16] ARM: vexpress/dcscb: handle platform coherency [PATCH 16/16] ARM: vexpress/dcscb: probe via device treeQuoted 5 lines
Thanks for introducing this series.
I am taking a look at this series. It introduced an algorithm for syncing and avoid racing when syncing the power status of clusters and CPUs. Do you think these codes could have a chance to become a generic framework?Yes. As I mentioned before, the
bL_prefix is implied only by the fact that big.LITTLE was the motivation for creating this code.Quoted 1 line
The Tegra chip series had a similar design for CPU clusters and it
had
Quoted 11 lines
limitation that the CPU0 always needs to be the last CPU to be shut down before cluster power down as well. I believe it can also get benefits of this works. We indeed need a similar algorithm to sync CPUs power status before cluster power down and switching.
The "
bL_entry.c", "bL_entry.S", "bL_entry.h", "vlock.h" and "vlock.S" looks have a chance to be a common framework for ARM platform even if it just support one cluster. Because some systems had the limitations for cluster power down. That's why the coupled cpuidle been introduced. And this framework could be enabled automatically if platform dependent or by menuconfig.Absolutely.
Quoted 4 lines
For ex,
select CPUS_CLUSTERS_POWER_SYNC_FRAMEWORK if SMP && CPU_PMHow do you think of this suggestion?
I'd prefer a more concise name though.
Quoted 8 lines
Quoted 6 lines
BTW, some questions...
1. The "bL_entry_point" looks like a first run function when CPUs just power up, then jumping to original reset vector that it should be called. Do you think this should be a function and be called by reset handler? Or in your design, this should be called as soon as possible when the CPU power be resumed?This should be called as soon as possible.
For one thing, you can't safely turn on the MMU or do anything which may affect any other CPU, until the code at bL_entry_point has run.
On most real hardware, the first thing to run on a powered-up CPU will be some boot ROM or firmware, but we expect bL_entry_point to be the initial entry point into Linux in these scenarios.
Quoted 11 lines
Quoted 3 lines
2. Does the
Last_manmechanism should implement in platform specific code to check something likecpu_online_statusand if there is a limitation for the specific last CPU to be powered down?The selection of the last man is accomplished using a platform specific mechanism. By the time this has to be done, the CPU is already dead as far as the Linux kernel is concerned, and therefore the generic CPU map is not reliable. In the DCSCB case we simply look at the hardware reset register being modified to directly determine the last man. On TC2 (not yet posted) we have to keep a local map of online CPUs.
Quoted 2 lines
In your case, the selection of the last man would simply be forced on CPU0.
Things are actually simpler in your scenario, because there is only one CPU that can possibly become the last man. However, the algorithm could still be re-used: it doesn't matter that it is "too safe" for your situation, and some aspects remain important, such as checking for CPUs unexpectedly powering up while a cluster power- down is pending, for example.
Cheers
---Dave
cover big.LITTLE low-level CPU and cluster power management
In reply to Nicolas PitreHi!
Quoted 6 lines
Review comments are welcome!
[*] General design information on the b.L switcher can be found here:
http://lwn.net/Articles/481055/
However the code is only accessible to Linaro members for the time being.
Assuming the lwn article is accurate:
Hmm. So we have hw capable of running 8 cores, and then we introduce strange switching code, because "scheduler is not ready". Sounds like a bad idea.
I'd say:
* expose all 8 cores.
* as long as scheduler is not ready, you can offline "the other" set of cores...
/sys/.../cpu/..../online
That way, I suspect scheduler will be fixed rather quickly.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
cover big.LITTLE low-level CPU and cluster power management
Quoted 9 lines · Pavel Machek
Hi!
Quoted 6 lines
Review comments are welcome!
[*] General design information on the b.L switcher can be found here:
http://lwn.net/Articles/481055/
However the code is only accessible to Linaro members for the time being.
Quoted 8 lines
Assuming the lwn article is accurate:
Hmm. So we have hw capable of running 8 cores, and then we introduce strange switching code, because "scheduler is not ready". Sounds like a bad idea.
I'd say:
* expose all 8 cores.
You may do that now. However the resulting power efficiency is far from optimal.
Quoted 2 lines
* as long as scheduler is not ready, you can offline "the other" set of cores...
/sys/.../cpu/..../online
You may do that now also. But system performance is far from optimal.
Quoted 1 line
That way, I suspect scheduler will be fixed rather quickly.
If we could fix the scheduler quickly, we would have done that instead. But if you have a good idea for fixing it quickly, then please join the people who have been working on that problem for over a year already.
Niicolas
cover big.LITTLE low-level CPU and cluster power management
Hi!
Quoted 12 lines
Quoted 12 lines
Quoted 6 lines
Review comments are welcome!
[*] General design information on the b.L switcher can be found here:
http://lwn.net/Articles/481055/
However the code is only accessible to Linaro members for the time being.Assuming the lwn article is accurate:
Hmm. So we have hw capable of running 8 cores, and then we introduce strange switching code, because "scheduler is not ready". Sounds like a bad idea.
Quoted 6 lines
Quoted 3 lines
I'd say:
* expose all 8 cores.
You may do that now. However the resulting power efficiency is far from optimal.
Really? Assuming the lwn article is accurate, it will be exactly the same.
I propose exposing all 8 cores, then keeping 4 of them offline in normal operation. This should have characteristics of your solution, except that cpu does not change unexpectedly under the pinned tasks.
Quoted 4 lines
Quoted 2 lines
* as long as scheduler is not ready, you can offline "the other" set of cores...
/sys/.../cpu/..../onlineYou may do that now also. But system performance is far from optimal.
Really? Do the offlining from the cpufreq code, and solution will be equivalent to yours, except the hacks.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
cover big.LITTLE low-level CPU and cluster power management
Quoted 28 lines · Pavel Machek
Hi!
Quoted 12 lines
Quoted 12 lines
Quoted 6 lines
Review comments are welcome!
[*] General design information on the b.L switcher can be found here:
http://lwn.net/Articles/481055/
However the code is only accessible to Linaro members for the time being.Assuming the lwn article is accurate:
Hmm. So we have hw capable of running 8 cores, and then we introduce strange switching code, because "scheduler is not ready". Sounds like a bad idea.
Quoted 6 lines
Quoted 3 lines
I'd say:
* expose all 8 cores.
You may do that now. However the resulting power efficiency is far from optimal.
Really? Assuming the lwn article is accurate, it will be exactly the same.
I propose exposing all 8 cores, then keeping 4 of them offline in normal operation. This should have characteristics of your solution, except that cpu does not change unexpectedly under the pinned tasks.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
* as long as scheduler is not ready, you can offline "the other" set of cores...
/sys/.../cpu/..../onlineYou may do that now also. But system performance is far from optimal.
Really? Do the offlining from the cpufreq code, and solution will be equivalent to yours, except the hacks.
No it is not equivalent. Offlining a CPU is a fairly heavy operation in Linux and you don't want to do that multiple times per second for example. Whereas the switcher "hack" is similar to a context switch in terms of software complexity.
Nicolas
cover big.LITTLE low-level CPU and cluster power management
Hi!
Quoted 19 lines
Quoted 19 lines
Quoted 6 lines
Quoted 3 lines
I'd say:
* expose all 8 cores.
You may do that now. However the resulting power efficiency is far from optimal.
Really? Assuming the lwn article is accurate, it will be exactly the same.
I propose exposing all 8 cores, then keeping 4 of them offline in normal operation. This should have characteristics of your solution, except that cpu does not change unexpectedly under the pinned tasks.
Quoted 4 lines
Quoted 2 lines
* as long as scheduler is not ready, you can offline "the other" set of cores...
/sys/.../cpu/..../onlineYou may do that now also. But system performance is far from optimal.
Quoted 7 lines
Quoted 2 lines
Really? Do the offlining from the cpufreq code, and solution will be equivalent to yours, except the hacks.
No it is not equivalent. Offlining a CPU is a fairly heavy operation in Linux and you don't want to do that multiple times per second for example. Whereas the switcher "hack" is similar to a context switch in terms of software complexity.
Well, why not. Logically, you _are_ saving state, offlining, onlining, and restoring state, multiple times per second, already. Fast offline/online benefits everyone...
As for the hacks... what does /proc/cpuinfo show on your system? What happens when process requests binding to specific cpu? I fear that switching hack is easy now, but will have unnice impact on unexpected places.
BTW I guess this should be discussed on linux-kernel, not on the arm list.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
cover big.LITTLE low-level CPU and cluster power management
Quoted 24 lines · Pavel Machek
Hi!
Quoted 22 lines
Quoted 21 lines
Quoted 6 lines
Quoted 3 lines
I'd say:
* expose all 8 cores.
You may do that now. However the resulting power efficiency is far from optimal.
Really? Assuming the lwn article is accurate, it will be exactly the same.
I propose exposing all 8 cores, then keeping 4 of them offline in normal operation. This should have characteristics of your solution, except that cpu does not change unexpectedly under the pinned tasks.
Quoted 4 lines
Quoted 2 lines
* as long as scheduler is not ready, you can offline "the other" set of cores...
/sys/.../cpu/..../onlineYou may do that now also. But system performance is far from optimal.
Really? Do the offlining from the cpufreq code, and solution will be equivalent to yours, except the hacks.
Quoted 7 lines
Quoted 4 lines
No it is not equivalent. Offlining a CPU is a fairly heavy operation in Linux and you don't want to do that multiple times per second for example. Whereas the switcher "hack" is similar to a context switch in terms of software complexity.
Well, why not. Logically, you _are_ saving state, offlining, onlining, and restoring state, multiple times per second, already.
Maybe "logically", but in practice there is no actual offlining/onlining but rather a migration.
Quoted 1 line
Fast offline/online benefits everyone...
Absolutely! You could ask tglx and cie where they are with their attempt at making hotplug less costly?
Quoted 7 lines
As for the hacks... what does
/proc/cpuinfoshow on your system? What happens when process requests binding to specific cpu? I fear that switching hack is easy now, but will have unnice impact on unexpected places.BTW I guess this should be discussed on linux-kernel, not on the arm list.
There is nothing to discuss until I'm ready to show you the code.
Nicolas