From: Will Deacon <hidden> Date: 2011-06-15 17:23:11
Hello,
This is version 3 of the patches originally posted here:
v1: http://lists.infradead.org/pipermail/linux-arm-kernel/2011-June/052157.html
v2: http://lists.infradead.org/pipermail/linux-arm-kernel/2011-June/052559.html
Changes from v2 include:
* No longer remove locking from the L2 invalidation function. I had
previously misdiagnosed the problem here, so it's now fixed
correctly in the disabling code.
* The reserve stack is now aligned to PMD_SIZE instead of SZ_1M so
that it doesn't get clobbered by the identity mapping.
I've tested these patches with kexec on Versatile Express and
Realview-PBX without any problems. It would be nice to get any feedback
on how this can also be used by the hibernation code from Frank and
whether or not any changes are required.
Cheers,
Will
Will Deacon (9):
ARM: l2x0: fix disabling function to avoid deadlock
ARM: proc: add definition of cpu_reset for ARMv6 and ARMv7 cores
ARM: lib: add switch_stack function for safely changing stack
ARM: idmap: add header file for identity mapping functions
ARM: reset: allow kernelspace mappings to be flat mapped during reset
ARM: multi-cpu: remove arguments from CPU proc macros
ARM: reset: add reset functionality for jumping to a physical address
ARM: kexec: use arm_machine_reset for branching to the reboot buffer
ARM: stop: execute platform callback from cpu_stop code
arch/arm/Kconfig | 2 +-
arch/arm/include/asm/idmap.h | 23 +++++++++++++
arch/arm/include/asm/pgtable.h | 3 --
arch/arm/include/asm/proc-fns.h | 14 ++++----
arch/arm/include/asm/system.h | 1 +
arch/arm/kernel/machine_kexec.c | 14 +-------
arch/arm/kernel/process.c | 67 +++++++++++++++++++++++++++++++++------
arch/arm/kernel/smp.c | 5 +++
arch/arm/lib/Makefile | 3 +-
arch/arm/lib/switch_stack.S | 44 +++++++++++++++++++++++++
arch/arm/mm/cache-l2x0.c | 19 +++++++---
arch/arm/mm/idmap.c | 31 +++++++++++++-----
arch/arm/mm/proc-v6.S | 5 +++
arch/arm/mm/proc-v7.S | 7 ++++
14 files changed, 189 insertions(+), 49 deletions(-)
create mode 100644 arch/arm/include/asm/idmap.h
create mode 100644 arch/arm/lib/switch_stack.S
From: Will Deacon <hidden> Date: 2011-06-15 17:23:12
The l2x0_disable function attempts to writel with the l2x0_lock held.
This results in deadlock when the writel contains an outer_sync call
for the platform since the l2x0_lock is already held by the disable
function. A further problem is that disabling the L2 without flushing it
first can lead to the spin_lock operation becoming visible after the
spin_unlock, causing any subsequent L2 maintenance to deadlock.
This patch replaces the writel with a call to writel_relaxed in the
disabling code and adds a flush before disabling in the control
register, preventing livelock from occurring.
Signed-off-by: Will Deacon <redacted>
---
arch/arm/mm/cache-l2x0.c | 19 +++++++++++++------
1 files changed, 13 insertions(+), 6 deletions(-)
From: Will Deacon <hidden> Date: 2011-06-15 17:23:13
This patch adds simple definitions of cpu_reset for ARMv6 and ARMv7
cores, which disable the MMU via the SCTLR.
Signed-off-by: Will Deacon <redacted>
---
arch/arm/mm/proc-v6.S | 5 +++++
arch/arm/mm/proc-v7.S | 7 +++++++
2 files changed, 12 insertions(+), 0 deletions(-)
From: Will Deacon <hidden> Date: 2011-06-15 17:23:14
When disabling the MMU, it is necessary to take out a 1:1 identity map
of the reset code so that it can safely be executed with and without
the MMU active. To avoid the situation where the physical address of the
reset code aliases with the virtual address of the active stack (which
cannot be included in the 1:1 mapping), it is desirable to change to a
new stack at a location which is less likely to alias.
This code adds a new lib function, switch_stack:
void switch_stack(void (*fn)(void *), void *arg, void *sp);
which changes the stack to point at the sp parameter, before invoking
fn(arg) with the new stack selected.
Signed-off-by: Dave Martin <redacted>
Signed-off-by: Will Deacon <redacted>
---
arch/arm/lib/Makefile | 3 +-
arch/arm/lib/switch_stack.S | 44 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 46 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/lib/switch_stack.S
From: Will Deacon <hidden> Date: 2011-06-15 17:23:16
Currently, switch_mm_for_reboot only takes out a 1:1 mapping from 0x0
to TASK_SIZE during reboot. For situations where we actually want to
turn off the MMU (e.g. kexec, hibernate, CPU hotplug) we want to map
as much memory as possible using the identity mapping so that we
increase the chance of mapping our reset code.
This patch introduces a new reboot mode, 'k', which remaps all of memory
apart from the kernel (PAGE_OFFSET - _end) and an additional page
immediately following it, which can be used as a temporary stack if
valid memory is available there. Note that this change makes it
necessary to manipulate and switch to the swapper page tables rather
than hijack the current task.
Reviewed-by: Dave Martin <redacted>
Signed-off-by: Will Deacon <redacted>
---
arch/arm/include/asm/idmap.h | 7 +++++++
arch/arm/mm/idmap.c | 30 +++++++++++++++++++++---------
2 files changed, 28 insertions(+), 9 deletions(-)
From: Will Deacon <hidden> Date: 2011-06-15 17:23:17
The macros for invoking functions via the processor struct in the
MULTI_CPU case define the arguments as part of the macros, making it
impossible to take the address of those functions.
This patch removes the arguments from the macro definitions so that we
can take the address of these functions like we can for the !MULTI_CPU
case.
Reported-by: Frank Hofmann <redacted>
Signed-off-by: Will Deacon <redacted>
---
arch/arm/include/asm/proc-fns.h | 14 +++++++-------
1 files changed, 7 insertions(+), 7 deletions(-)
From: Will Deacon <hidden> Date: 2011-06-15 17:23:18
Tools such as kexec and CPU hotplug require a way to reset the processor
and branch to some code in physical space. This requires various bits of
jiggery pokery with the caches and MMU which, when it goes wrong, tends
to lock up the system.
This patch implements a new function, arm_machine_reset, for
consolidating this code in one place where it can be used by multiple
subsystems.
Signed-off-by: Dave Martin <redacted>
Signed-off-by: Will Deacon <redacted>
---
arch/arm/include/asm/system.h | 1 +
arch/arm/kernel/process.c | 64 +++++++++++++++++++++++++++++++++++-----
2 files changed, 57 insertions(+), 8 deletions(-)
@@ -90,12 +90,10 @@ static int __init hlt_setup(char *__unused)__setup("nohlt",nohlt_setup);__setup("hlt",hlt_setup);-voidarm_machine_restart(charmode,constchar*cmd)-{-/* Disable interrupts first */-local_irq_disable();-local_fiq_disable();+externvoidswitch_stack(void(*fn)(void*),void*arg,void*sp);+staticvoidprepare_for_reboot(charmode)+{/**Tellthemmsystemthatwearegoingtoreboot-*wemayneedittoinsertsome1:1mappingssothat
@@ -103,14 +101,20 @@ void arm_machine_restart(char mode, const char *cmd)*/setup_mm_for_reboot(mode);-/* Clean and invalidate caches */-flush_cache_all();-/* Turn off caching */cpu_proc_fin();/* Push out any further dirty data, and ensure cache is empty */flush_cache_all();+}++voidarm_machine_restart(charmode,constchar*cmd)+{+/* Disable interrupts first */+local_irq_disable();+local_fiq_disable();++prepare_for_reboot(mode);/**Nowcallthearchitecturespecificrebootcode.
@@ -126,6 +130,50 @@ void arm_machine_restart(char mode, const char *cmd)while(1);}+typedefvoid(*phys_reset_t)(unsignedlong);+void__arm_machine_reset(void*reset_code_phys)+{+phys_reset_tphys_reset;++prepare_for_reboot(MODE_REMAP_KERNEL);++/* Switch to the identity mapping. */+phys_reset=(phys_reset_t)virt_to_phys(cpu_reset);+phys_reset((unsignedlong)reset_code_phys);++/* Should never get here. */+BUG();+}++voidarm_machine_reset(unsignedlongreset_code_phys)+{+phys_addr_tcpu_reset_end_phys;+void*cpu_reset_end,*new_stack=(void*)RESERVE_STACK_PAGE;++cpu_reset_end=(void*)PAGE_ALIGN((unsignedlong)cpu_reset);+cpu_reset_end_phys=virt_to_phys(cpu_reset_end);++/* Check that we can safely identity map the reset code. */+BUG_ON(cpu_reset_end_phys>TASK_SIZE&&+cpu_reset_end_phys<=RESERVE_STACK_PAGE);++/* Check that the reserve stack page is valid memory. */+BUG_ON(!pfn_valid(__phys_to_pfn(virt_to_phys(new_stack-1))));++/* Disable interrupts first. */+local_irq_disable();+local_fiq_disable();++/* Disable the L2. */+outer_disable();++/* Change to the new stack and continue with the reset. */+switch_stack(__arm_machine_reset,(void*)reset_code_phys,new_stack);++/* Should never get here. */+BUG();+}+/**Functionpointerstooptionalmachinespecificfunctions*/
From: Will Deacon <hidden> Date: 2011-06-15 17:23:19
Now that there is a common way to reset the machine, let's use it
instead of reinventing the wheel in the kexec backend.
Signed-off-by: Will Deacon <redacted>
---
arch/arm/kernel/machine_kexec.c | 14 ++------------
1 files changed, 2 insertions(+), 12 deletions(-)
@@ -111,14 +109,6 @@ void machine_kexec(struct kimage *image)if(kexec_reinit)kexec_reinit();-local_irq_disable();-local_fiq_disable();-setup_mm_for_reboot(0);/* mode is not used, so just pass 0*/-flush_cache_all();-outer_flush_all();-outer_disable();-cpu_proc_fin();-outer_inv_all();-flush_cache_all();-cpu_reset(reboot_code_buffer_phys);++arm_machine_reset(reboot_code_buffer_phys);}
From: Will Deacon <hidden> Date: 2011-06-15 17:23:20
Sending IPI_CPU_STOP to a CPU causes it to execute a busy cpu_relax
loop forever. This makes it impossible to kexec successfully on an SMP
system since the secondary CPUs do not reset.
This patch adds a callback to platform_cpu_kill, defined when
CONFIG_HOTPLUG_CPU=y, from the ipi_cpu_stop handling code. This function
currently just returns 1 on all platforms that define it but allows them
to do something more sophisticated in the future.
Signed-off-by: Will Deacon <redacted>
---
arch/arm/Kconfig | 2 +-
arch/arm/kernel/smp.c | 4 ++++
2 files changed, 5 insertions(+), 1 deletions(-)
On Wed, Jun 15, 2011 at 06:23:12PM +0100, Will Deacon wrote:
The l2x0_disable function attempts to writel with the l2x0_lock held.
This results in deadlock when the writel contains an outer_sync call
for the platform since the l2x0_lock is already held by the disable
function. A further problem is that disabling the L2 without flushing it
first can lead to the spin_lock operation becoming visible after the
spin_unlock, causing any subsequent L2 maintenance to deadlock.
This patch replaces the writel with a call to writel_relaxed in the
disabling code and adds a flush before disabling in the control
register, preventing livelock from occurring.
Signed-off-by: Will Deacon <redacted>