arm64: kernel: Add support for hibernate/suspend-to-disk
Subsystems (5)
v3 cover arm64: kernel: Add support for hibernate/suspend-to-disk
Hi all,
v2's "clean cached pages on architectures that require it" is wrong. do_copy_page() isn't used on the resume path for pages restored 'in place'. There is nowhere we can add the call to flush_icache_range(), instead patch 9 of this series adds a new 'pbe' list of pages restored in place. Architectures that need to clean these before they can be executed can walk this list. This preserves the status-quo a little better, as existing architectures won't get flush_icache_range() called on these pages.
The first four patches are borrowed from kexec v12, and are used to return el2 to the hyp stub, as its stack/vectors may be overwritten.
Patches 5 and 6 provide some cleanup to the cpu_suspend() API:
* allowing it to be used with a 'finisher' that needs to return success,
* and turn the MMU on early to allow sleep_save_sp to be accessed by VA.
The last patch adds hibernate support, following the x86 approach, it creates a temporary set of page tables and copies the hibernate_exit code. The implementation requires that exactly the same kernel is booted on the same hardware, and that the kernel is loaded at the same physical address.
This series is based on v4.4-rc2, and can be pulled from:git://linux-arm.org/linux-jm.git -b hibernate/v3
Changes from v2:
* Rewrote patch 9 - we can't clean pages in copy_page(), we need to publish a list for the architecture to clean
* Updated patch 10 following rewritten patch 9
* Added missing pgprot_val() in hibernate.c, spotted by STRICT_MM_TYPECHECKS
* Removed 'tcr_set_idmap_t0sz' from proc.S - I missed this when rebase-ing
* Re-imported the first four patches from kexec v12
* Rebased onto v4.4-rc2
* Changes from Pavel Machek's comments
Changes from v1:
* Removed for_each_process(){ for_each_vma() { } }; cache cleaning, replaced with icache_flush_range() call in core hibernate code
* Rebased onto conflicting tcr_ek1.t0sz bug-fix patch
[v2] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-October/376450.html
[v1] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-October/376450.html
AKASHI Takahiro (1):
arm64: kvm: allows kvm cpu hotplug
Geoff Levand (3):
arm64: Fold proc-macros.S into assembler.h
arm64: Convert hcalls to use HVC immediate value
arm64: Add new hcall HVC_CALL_FUNC
James Morse (6):
arm64: kernel: Rework finisher callback out of __cpu_suspend_enter().
arm64: Change cpu_resume() to enable mmu early then access sleep_sp by
va
arm64: kernel: Include _AC definition in page.h
arm64: Promote KERNEL_START/KERNEL_END definitions to a header file PM / Hibernate: Publish pages restored in-place to arch code
arm64: kernel: Add support for hibernate/suspend-to-disk.
arch/arm/include/asm/kvm_host.h | 10 +- arch/arm/include/asm/kvm_mmu.h | 1 + arch/arm/kvm/arm.c | 79 ++++---- arch/arm/kvm/mmu.c | 5 + arch/arm64/Kconfig | 3 + arch/arm64/include/asm/assembler.h | 48 ++++- arch/arm64/include/asm/kvm_host.h | 16 +- arch/arm64/include/asm/kvm_mmu.h | 1 + arch/arm64/include/asm/memory.h | 3 + arch/arm64/include/asm/page.h | 2 + arch/arm64/include/asm/suspend.h | 31 ++- arch/arm64/include/asm/virt.h | 49 +++++ arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/asm-offsets.c | 9 +- arch/arm64/kernel/head.S | 5 +- arch/arm64/kernel/hibernate-asm.S | 119 ++++++++++++ arch/arm64/kernel/hibernate.c | 376 +++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/hyp-stub.S | 43 +++-- arch/arm64/kernel/setup.c | 1 - arch/arm64/kernel/sleep.S | 148 +++++---------- arch/arm64/kernel/suspend.c | 103 ++++------ arch/arm64/kernel/vmlinux.lds.S | 15 ++ arch/arm64/kvm/hyp-init.S | 34 +++- arch/arm64/kvm/hyp.S | 44 ++++- arch/arm64/mm/cache.S | 2 - arch/arm64/mm/proc-macros.S | 64 ------- arch/arm64/mm/proc.S | 31 +-- include/linux/suspend.h | 1 + kernel/power/snapshot.c | 42 +++-- 29 files changed, 943 insertions(+), 343 deletions(-) create mode 100644 arch/arm64/kernel/hibernate-asm.S create mode 100644 arch/arm64/kernel/hibernate.c delete mode 100644 arch/arm64/mm/proc-macros.S
Signature · 1 line
2.6.2
PATCH v3 1/10 arm64: Fold proc-macros.S into assembler.h
From: Geoff Levand <[email protected]>
To allow the assembler macros defined in arch/arm64/mm/proc-macros.S to be used outside the mm code move the contents of proc-macros.S to asm/assembler.h. Also, delete proc-macros.S, and fix up all references to proc-macros.S.
--- arch/arm64/include/asm/assembler.h | 48 +++++++++++++++++++++++++++- arch/arm64/kvm/hyp-init.S | 1 - arch/arm64/mm/cache.S | 2 -- arch/arm64/mm/proc-macros.S | 64 -------------------------------------- arch/arm64/mm/proc.S | 3 -- 5 files changed, 47 insertions(+), 71 deletions(-) delete mode 100644 arch/arm64/mm/proc-macros.S
arch/arm64/include/asm/assembler.h +47 -1
@@ -1,5 +1,5 @@/* -* Based on arch/arm/include/asm/assembler.h +* Based on arch/arm/include/asm/assembler.h, arch/arm/mm/proc-macros.S * * Copyright (C) 1996-2000 Russell King * Copyright (C) 2012 ARM Ltd.
@@ -23,6 +23,8 @@#ifndef __ASM_ASSEMBLER_H#define __ASM_ASSEMBLER_H +#include <asm/asm-offsets.h> +#include <asm/pgtable-hwdef.h> #include <asm/ptrace.h> #include <asm/thread_info.h>
@@ -194,6 +196,50 @@ lr .req x30 // link register .endm /* + * vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm) + */ + .macro vma_vm_mm, rd, rn + ldr \rd, [\rn, #VMA_VM_MM] + .endm + +/* +* mmid - get context id from mm pointer (mm->context.id) +*/ + .macro mmid, rd, rn + ldr \rd, [\rn, #MM_CONTEXT_ID] + .endm + +/* +* dcache_line_size - get the minimum D-cache line size from the CTR register. +*/ + .macro dcache_line_size, reg, tmp + mrs \tmp, ctr_el0 // read CTR + ubfm \tmp, \tmp, #16, #19 // cache line size encoding + mov \reg, #4 // bytes per word + lsl \reg, \reg, \tmp // actual cache line size + .endm + +/* + * icache_line_size - get the minimum I-cache line size from the CTR register. + */ + .macro icache_line_size, reg, tmp + mrs \tmp, ctr_el0 // read CTR + and \tmp, \tmp, #0xf // cache line size encoding + mov \reg, #4 // bytes per word + lsl \reg, \reg, \tmp // actual cache line size + .endm + +/* + * tcr_set_idmap_t0sz - update TCR.T0SZ so that we can load the ID map + */ + .macro tcr_set_idmap_t0sz, valreg, tmpreg +#ifndef CONFIG_ARM64_VA_BITS_48 + ldr_l \tmpreg, idmap_t0sz + bfi \valreg, \tmpreg, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH +#endif + .endm + +/* * Annotate a function as position independent, i.e., safe to be called before * the kernel virtual mapping is activated. */
arch/arm64/kvm/hyp-init.S +0 -1
@@ -20,7 +20,6 @@ #include <asm/assembler.h> #include <asm/kvm_arm.h> #include <asm/kvm_mmu.h> -#include <asm/pgtable-hwdef.h> .text .pushsection .hyp.idmap.text, "ax"
arch/arm64/mm/cache.S +0 -2
@@ -24,8 +24,6 @@ #include <asm/cpufeature.h> #include <asm/alternative.h> -#include "proc-macros.S" - /* * flush_icache_range(start,end) *
arch/arm64/mm/proc-macros.S deleted +0 -64
64 lines not on this page · raw message
arch/arm64/mm/proc.S +0 -3
@@ -23,11 +23,8 @@ #include <asm/assembler.h> #include <asm/asm-offsets.h> #include <asm/hwcap.h> -#include <asm/pgtable-hwdef.h> #include <asm/pgtable.h> -#include "proc-macros.S" - #ifdef CONFIG_ARM64_64K_PAGES #define TCR_TG_FLAGS TCR_TG0_64K | TCR_TG1_64K #elif defined(CONFIG_ARM64_16K_PAGES)
Signature · 1 line
2.6.2
PATCH v3 1/10 arm64: Fold proc-macros.S into assembler.h
Quoted 8 lines · James Morse
From: Geoff Levand <[email protected]>
To allow the assembler macros defined in
arch/arm64/mm/proc-macros.Sto be used outside the mm code move the contents ofproc-macros.Stoasm/assembler.h. Also, deleteproc-macros.S, and fix up all references toproc-macros.S.Signed-off-by Geoff Levand <[email protected]>Signed-off-by James Morse <[email protected]>
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
PATCH v3 2/10 arm64: Convert hcalls to use HVC immediate value
In reply to James MorseFrom: Geoff Levand <[email protected]>
The existing arm64 hcall implementations are limited in that they only allow for two distinct hcalls; with the x0 register either zero or not zero. Also, the API of the hyp-stub exception vector routines and the KVM exception vector routines differ; hyp-stub uses a non-zero value in x0 to implement __hyp_set_vectors, whereas KVM uses it to implement kvm_call_hyp.
To allow for additional hcalls to be defined and to make the arm64 hcall API more consistent across exception vector routines, change the hcall implementations to use the 16 bit immediate value of the HVC instruction to specify the hcall type.
Define three new preprocessor macros HVC_CALL_HYP, HVC_GET_VECTORS, and HVC_SET_VECTORS to be used as hcall type specifiers and convert the existing __hyp_get_vectors(), __hyp_set_vectors() and kvm_call_hyp() routines to use these new macros when executing an HVC call. Also, change the corresponding hyp-stub and KVM el1_sync exception vector routines to use these new macros.
--- arch/arm64/include/asm/virt.h | 27 +++++++++++++++++++++++++++ arch/arm64/kernel/hyp-stub.S | 32 +++++++++++++++++++++----------- arch/arm64/kvm/hyp.S | 16 +++++++++------- 3 files changed, 57 insertions(+), 18 deletions(-)
arch/arm64/include/asm/virt.h +27 -0
@@ -18,6 +18,33 @@#ifndef __ASM__VIRT_H#define __ASM__VIRT_H +/* +* The arm64 hcall implementation uses the ISS field of the ESR_EL2 register to +* specify the hcall type. The exception handlers are allowed to use registers +* x17 and x18 in their implementation. Any routine issuing an hcall must not +* expect these registers to be preserved. +*/ + +/* +* HVC_CALL_HYP - Execute a hyp routine. +*/ + +#define HVC_CALL_HYP 0 + +/* +* HVC_GET_VECTORS - Return the value of the vbar_el2 register. +*/ + +#define HVC_GET_VECTORS 1 + +/* +* HVC_SET_VECTORS - Set the value of the vbar_el2 register. +* +* @x0: Physical address of the new vector table. +*/ + +#define HVC_SET_VECTORS 2 +#define BOOT_CPU_MODE_EL1 (0xe11)#define BOOT_CPU_MODE_EL2 (0xe12)
arch/arm64/kernel/hyp-stub.S +21 -11
@@ -22,6 +22,7 @@ #include <linux/irqchip/arm-gic-v3.h> #include <asm/assembler.h> +#include <asm/kvm_arm.h> #include <asm/ptrace.h> #include <asm/virt.h>
@@ -53,14 +54,22 @@ ENDPROC(__hyp_stub_vectors) .align 11 el1_sync: - mrs x1, esr_el2 - lsr x1, x1, #26 - cmp x1, #0x16 + mrs x18, esr_el2 + lsr x17, x18, #ESR_ELx_EC_SHIFT + and x18, x18, #ESR_ELx_ISS_MASK + + cmp x17, #ESR_ELx_EC_HVC64 b.ne 2f // Not an HVC trap - cbz x0, 1f - msr vbar_el2, x0 // Set vbar_el2 + + cmp x18, #HVC_GET_VECTORS + b.ne 1f + mrs x0, vbar_el2 b 2f -1: mrs x0, vbar_el2 // Return vbar_el2 + +1: cmp x18, #HVC_SET_VECTORS + b.ne 2f + msr vbar_el2, x0 + 2: eret ENDPROC(el1_sync)
@@ -100,11 +109,12 @@ ENDPROC(\label) * initialisation entry point. */ -ENTRY(__hyp_get_vectors) - mov x0, xzr - // fall through ENTRY(__hyp_set_vectors) - hvc #0 + hvc #HVC_SET_VECTORS ret -ENDPROC(__hyp_get_vectors) ENDPROC(__hyp_set_vectors) + +ENTRY(__hyp_get_vectors) + hvc #HVC_GET_VECTORS + ret +ENDPROC(__hyp_get_vectors)
arch/arm64/kvm/hyp.S +9 -7
@@ -29,6 +29,7 @@ #include <asm/kvm_asm.h> #include <asm/kvm_mmu.h> #include <asm/memory.h> +#include <asm/virt.h> #define CPU_GP_REG_OFFSET(x) (CPU_GP_REGS + x) #define CPU_XREG_OFFSET(x) CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x)
@@ -932,12 +933,9 @@ __hyp_panic_str: * in Hyp mode (see init_hyp_mode in arch/arm/kvm/arm.c). Return values are * passed in r0 and r1. * - * A function pointer with a value of 0 has a special meaning, and is - * used to implement __hyp_get_vectors in the same way as in - * arch/arm64/kernel/hyp_stub.S. */ ENTRY(kvm_call_hyp) - hvc #0 + hvc #HVC_CALL_HYP ret ENDPROC(kvm_call_hyp)
@@ -968,6 +966,7 @@ el1_sync: // Guest trapped into EL2 mrs x1, esr_el2 lsr x2, x1, #ESR_ELx_EC_SHIFT + and x0, x1, #ESR_ELx_ISS_MASK cmp x2, #ESR_ELx_EC_HVC64 b.ne el1_trap
@@ -976,15 +975,18 @@ el1_sync: // Guest trapped into EL2 cbnz x3, el1_trap // called HVC /* Here, we're pretty sure the host called HVC. */ + mov x18, x0 pop x2, x3 pop x0, x1 - /* Check for __hyp_get_vectors */ - cbnz x0, 1f + cmp x18, #HVC_GET_VECTORS + b.ne 1f mrs x0, vbar_el2 b 2f -1: push lr, xzr +1: /* Default to HVC_CALL_HYP. */ + + push lr, xzr /* * Compute the function address in EL2, and shuffle the parameters.
Signature · 1 line
2.6.2
PATCH v3 3/10 arm64: Add new hcall HVC_CALL_FUNC
In reply to James MorseFrom: Geoff Levand <[email protected]>
Add the new hcall HVC_CALL_FUNC that allows execution of a function at EL2. During CPU reset the CPU must be brought to the exception level it had on entry to the kernel. The HVC_CALL_FUNC hcall will provide the mechanism needed for this exception level switch.
To allow the HVC_CALL_FUNC exception vector to work without a stack, which is needed to support an hcall at CPU reset, this implementation uses register x18 to store the link register across the caller provided function. This dictates that the caller provided function must preserve the contents of register x18.
--- arch/arm64/include/asm/virt.h | 13 +++++++++++++ arch/arm64/kernel/hyp-stub.S | 13 ++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-)
arch/arm64/include/asm/virt.h +13 -0
@@ -45,6 +45,19 @@#define HVC_SET_VECTORS 2 +/* +* HVC_CALL_FUNC - Execute a function at EL2. +* +* @x0: Physical address of the function to be executed. +* @x1: Passed as the first argument to the function. +* @x2: Passed as the second argument to the function. +* @x3: Passed as the third argument to the function. +* +* The called function must preserve the contents of register x18. +*/ + +#define HVC_CALL_FUNC 3 +#define BOOT_CPU_MODE_EL1 (0xe11)#define BOOT_CPU_MODE_EL2 (0xe12)
arch/arm64/kernel/hyp-stub.S +12 -1
@@ -67,8 +67,19 @@ el1_sync: b 2f 1: cmp x18, #HVC_SET_VECTORS - b.ne 2f + b.ne 1f msr vbar_el2, x0 + b 2f + +1: cmp x18, #HVC_CALL_FUNC + b.ne 2f + mov x18, lr + mov lr, x0 + mov x0, x1 + mov x1, x2 + mov x2, x3 + blr lr + mov lr, x18 2: eret ENDPROC(el1_sync)
Signature · 1 line
2.6.2
PATCH v3 4/10 arm64: kvm: allows kvm cpu hotplug
In reply to James MorseFrom: AKASHI Takahiro <[email protected]>
The current kvm implementation on arm64 does cpu-specific initialization at system boot, and has no way to gracefully shutdown a core in terms of kvm. This prevents, especially, kexec from rebooting the system on a boot core in EL2.
This patch adds a cpu tear-down function and also puts an existing cpu-init code into a separate function, kvm_arch_hardware_disable() and kvm_arch_hardware_enable() respectively.
We don't need arm64-specific cpu hotplug hook any more.
Since this patch modifies common part of code between arm and arm64, one stub definition, __cpu_reset_hyp_mode(), is added on arm side to avoid compiling errors.
--- arch/arm/include/asm/kvm_host.h | 10 ++++- arch/arm/include/asm/kvm_mmu.h | 1 + arch/arm/kvm/arm.c | 79 ++++++++++++++++++--------------------- arch/arm/kvm/mmu.c | 5 +++ arch/arm64/include/asm/kvm_host.h | 16 +++++++- arch/arm64/include/asm/kvm_mmu.h | 1 + arch/arm64/include/asm/virt.h | 9 +++++ arch/arm64/kvm/hyp-init.S | 33 ++++++++++++++++ arch/arm64/kvm/hyp.S | 32 ++++++++++++++-- 9 files changed, 138 insertions(+), 48 deletions(-)
arch/arm/include/asm/kvm_host.h +9 -1
@@ -214,6 +214,15 @@ static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr,kvm_call_hyp ((void *)hyp_stack_ptr, vector_ptr, pgd_ptr); } +static inline void __cpu_reset_hyp_mode (phys_addr_t boot_pgd_ptr, +phys_addr_t phys_idmap_start) +{ +/* +* TODO +* kvm_call_reset(boot_pgd_ptr, phys_idmap_start); +*/ +} +static inline int kvm_arch_dev_ioctl_check_extension (long ext) {return 0 ;
@@ -226,7 +235,6 @@ void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot);struct kvm_vcpu *kvm_mpidr_to_vcpu (struct kvm *kvm,unsigned long mpidr); -static inline void kvm_arch_hardware_disable (void ) {}static inline void kvm_arch_hardware_unsetup (void ) {}static inline void kvm_arch_sync_events (struct kvm *kvm) {}static inline void kvm_arch_vcpu_uninit (struct kvm_vcpu *vcpu) {}
arch/arm/include/asm/kvm_mmu.h +1 -0
@@ -66,6 +66,7 @@ void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);phys_addr_t kvm_mmu_get_httbr (void );phys_addr_t kvm_mmu_get_boot_httbr (void );phys_addr_t kvm_get_idmap_vector (void ); +phys_addr_t kvm_get_idmap_start (void );int kvm_mmu_init (void );void kvm_clear_hyp_idmap (void );
arch/arm/kvm/arm.c +37 -42
149 lines not on this page · raw message
arch/arm/kvm/mmu.c +5 -0
@@ -1644,6 +1644,11 @@ phys_addr_t kvm_get_idmap_vector(void)return hyp_idmap_vector; } +phys_addr_t kvm_get_idmap_start (void ) +{ +return hyp_idmap_start; +} +int kvm_mmu_init (void ) {int err;
arch/arm64/include/asm/kvm_host.h +15 -1
@@ -223,6 +223,7 @@ struct kvm_vcpu *kvm_arm_get_running_vcpu(void);struct kvm_vcpu *__percpu *kvm_get_running_vcpus (void );u64 kvm_call_hyp (void *hypfn, ...); +void kvm_call_reset (phys_addr_t boot_pgd_ptr,phys_addr_t phys_idmap_start);void force_vm_exit (const cpumask_t *mask);void kvm_mmu_wp_memory_region (struct kvm *kvm,int slot);
@@ -247,7 +248,20 @@ static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr, hyp_stack_ptr, vector_ptr); } -static inline void kvm_arch_hardware_disable (void ) {} +static inline void __cpu_reset_hyp_mode (phys_addr_t boot_pgd_ptr, +phys_addr_t phys_idmap_start) +{ +/* +* Call reset code, and switch back to stub hyp vectors. +*/ +kvm_call_reset (boot_pgd_ptr, phys_idmap_start); +} + +struct vgic_sr_vectors { +void *save_vgic; +void *restore_vgic; +}; +static inline void kvm_arch_hardware_unsetup (void ) {}static inline void kvm_arch_sync_events (struct kvm *kvm) {}static inline void kvm_arch_vcpu_uninit (struct kvm_vcpu *vcpu) {}
arch/arm64/include/asm/kvm_mmu.h +1 -0
@@ -98,6 +98,7 @@ void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);phys_addr_t kvm_mmu_get_httbr (void );phys_addr_t kvm_mmu_get_boot_httbr (void );phys_addr_t kvm_get_idmap_vector (void ); +phys_addr_t kvm_get_idmap_start (void );int kvm_mmu_init (void );void kvm_clear_hyp_idmap (void );
arch/arm64/include/asm/virt.h +9 -0
@@ -58,9 +58,18 @@#define HVC_CALL_FUNC 3 +/* +* HVC_RESET_CPU - Reset cpu in EL2 to initial state. +* +* @x0: entry address in trampoline code in va +* @x1: identical mapping page table in pa +*/ +#define BOOT_CPU_MODE_EL1 (0xe11)#define BOOT_CPU_MODE_EL2 (0xe12) +#define HVC_RESET_CPU 4 +#ifndef __ASSEMBLY__/*
arch/arm64/kvm/hyp-init.S +33 -0
@@ -139,6 +139,39 @@ merged: eret ENDPROC(__kvm_hyp_init) + /* + * x0: HYP boot pgd + * x1: HYP phys_idmap_start + */ +ENTRY(__kvm_hyp_reset) + /* We're in trampoline code in VA, switch back to boot page tables */ + msr ttbr0_el2, x0 + isb + + /* Invalidate the old TLBs */ + tlbi alle2 + dsb sy + + /* Branch into PA space */ + adr x0, 1f + bfi x1, x0, #0, #PAGE_SHIFT + br x1 + + /* We're now in idmap, disable MMU */ +1: mrs x0, sctlr_el2 + ldr x1, =SCTLR_EL2_FLAGS + bic x0, x0, x1 // Clear SCTL_M and etc + msr sctlr_el2, x0 + isb + + /* Install stub vectors */ + adrp x0, __hyp_stub_vectors + add x0, x0, #:lo12:__hyp_stub_vectors + msr vbar_el2, x0 + + eret +ENDPROC(__kvm_hyp_reset) + .ltorg .popsection
arch/arm64/kvm/hyp.S +28 -4
@@ -939,6 +939,11 @@ ENTRY(kvm_call_hyp) ret ENDPROC(kvm_call_hyp) +ENTRY(kvm_call_reset) + hvc #HVC_RESET_CPU + ret +ENDPROC(kvm_call_reset) + .macro invalid_vector label, target .align 2 \label:
@@ -982,10 +987,27 @@ el1_sync: // Guest trapped into EL2 cmp x18, #HVC_GET_VECTORS b.ne 1f mrs x0, vbar_el2 - b 2f - -1: /* Default to HVC_CALL_HYP. */ + b do_eret + /* jump into trampoline code */ +1: cmp x18, #HVC_RESET_CPU + b.ne 2f + /* + * Entry point is: + * TRAMPOLINE_VA + * + (__kvm_hyp_reset - (__hyp_idmap_text_start & PAGE_MASK)) + */ + adrp x2, __kvm_hyp_reset + add x2, x2, #:lo12:__kvm_hyp_reset + adrp x3, __hyp_idmap_text_start + add x3, x3, #:lo12:__hyp_idmap_text_start + and x3, x3, PAGE_MASK + sub x2, x2, x3 + ldr x3, =TRAMPOLINE_VA + add x2, x2, x3 + br x2 // no return + +2: /* Default to HVC_CALL_HYP. */ push lr, xzr /*
@@ -999,7 +1021,9 @@ el1_sync: // Guest trapped into EL2 blr lr pop lr, xzr -2: eret + +do_eret: + eret el1_trap: /*
Signature · 1 line
2.6.2
PATCH v3 5/10 arm64: kernel: Rework finisher callback out of __cpu_suspend_enter().
In reply to James MorseHibernate could make use of the cpu_suspend() code to save/restore cpu state, however it needs to be able to return '0' from the 'finisher'.
Rework cpu_suspend() so that the finisher is called from C code, independently from the save/restore of cpu state. Space to save the context in is allocated in the caller's stack frame, and passed into __cpu_suspend_enter().
Hibernate's use of this API will look like a copy of the cpu_suspend() function.
--- arch/arm64/include/asm/suspend.h | 20 +++++++++ arch/arm64/kernel/asm-offsets.c | 2 + arch/arm64/kernel/sleep.S | 93 ++++++++++++++-------------------------- arch/arm64/kernel/suspend.c | 89 ++++++++++++++++++++++---------------- 4 files changed, 108 insertions(+), 96 deletions(-)
arch/arm64/include/asm/suspend.h +20 -0
@@ -2,6 +2,7 @@#define __ASM_SUSPEND_H#define NR_CTX_REGS 11 +#define NR_CALLEE_SAVED_REGS 12/* * struct cpu_suspend_ctx must be 16-byte aligned since it is allocated on
@@ -21,6 +22,25 @@ struct sleep_save_sp { phys_addr_t save_ptr_stash_phys; }; +/* +* Memory to save the cpu state is allocated on the stack by +* __cpu_suspend_enter()'s caller, and populated by __cpu_suspend_enter(). +* This data must survive until cpu_resume() is called. +* +* This struct desribes the size and the layout of the saved cpu state. +* The layout of the callee_saved_regs is defined by the implementation +* of __cpu_suspend_enter(), and cpu_resume(). This struct must be passed +* in by the caller as __cpu_suspend_enter()'s stack-frame is gone once it +* returns, and the data would be subsequently corrupted by the call to the +* finisher. +*/ +struct sleep_stack_data { +struct cpu_suspend_ctx system_regs; +unsigned long callee_saved_regs[NR_CALLEE_SAVED_REGS ]; +}; +extern int cpu_suspend (unsigned long arg,int (*fn)(unsigned long ));extern void cpu_resume (void ); +int __cpu_suspend_enter (struct sleep_stack_data *state); +void __cpu_suspend_exit (struct mm_struct *mm);#endif
arch/arm64/kernel/asm-offsets.c +2 -0
@@ -160,6 +160,8 @@ int main(void)DEFINE (SLEEP_SAVE_SP_SZ ,sizeof (struct sleep_save_sp ));DEFINE (SLEEP_SAVE_SP_PHYS , offsetof(struct sleep_save_sp , save_ptr_stash_phys));DEFINE (SLEEP_SAVE_SP_VIRT , offsetof(struct sleep_save_sp , save_ptr_stash)); +DEFINE (SLEEP_STACK_DATA_SYSTEM_REGS , offsetof(struct sleep_stack_data , system_regs)); +DEFINE (SLEEP_STACK_DATA_CALLEE_REGS , offsetof(struct sleep_stack_data , callee_saved_regs));#endif return 0 ; }
arch/arm64/kernel/sleep.S +33 -60
127 lines not on this page · raw message
arch/arm64/kernel/suspend.c +53 -36
129 lines not on this page · raw message
Signature · 1 line
2.6.2
PATCH v3 6/10 arm64: Change cpu_resume() to enable mmu early then access sleep_sp by va
In reply to James MorseBy enabling the MMU early in cpu_resume(), the sleep_save_sp and stack can be accessed by VA, which avoids the need to convert-addresses and clean to PoC on the suspend path.
MMU setup is shared with the boot path, meaning the swapper_pg_dir is restored directly: ttbr1_el1 is no longer saved/restored.
struct sleep_save_sp is removed, replacing it with a single array of pointers.
cpu_do_{suspend,resume} could be further reduced to not restore: cpacr_el1, mdscr_el1, tcr_el1, vbar_el1 and sctlr_el1, all of which are set by __cpu_setup(). However these values all contain res0 bits that may be used to enable future features.
--- arch/arm64/include/asm/suspend.h | 7 +---- arch/arm64/kernel/asm-offsets.c | 3 --- arch/arm64/kernel/head.S | 2 +- arch/arm64/kernel/setup.c | 1 - arch/arm64/kernel/sleep.S | 57 ++++++++++++++-------------------------- arch/arm64/kernel/suspend.c | 52 +++--------------------------------- arch/arm64/mm/proc.S | 28 +++++--------------- 7 files changed, 33 insertions(+), 117 deletions(-)
arch/arm64/include/asm/suspend.h +1 -6
@@ -1,7 +1,7 @@#ifndef __ASM_SUSPEND_H#define __ASM_SUSPEND_H -#define NR_CTX_REGS 11 +#define NR_CTX_REGS 10#define NR_CALLEE_SAVED_REGS 12/*
@@ -17,11 +17,6 @@ struct cpu_suspend_ctx {u64 sp; }__aligned(16) ; -struct sleep_save_sp { -phys_addr_t *save_ptr_stash; -phys_addr_t save_ptr_stash_phys; -}; -/* * Memory to save the cpu state is allocated on the stack by * __cpu_suspend_enter()'s caller, and populated by __cpu_suspend_enter().
arch/arm64/kernel/asm-offsets.c +0 -3
@@ -157,9 +157,6 @@ int main(void)DEFINE (CPU_CTX_SP , offsetof(struct cpu_suspend_ctx , sp));DEFINE (MPIDR_HASH_MASK , offsetof(struct mpidr_hash , mask));DEFINE (MPIDR_HASH_SHIFTS , offsetof(struct mpidr_hash , shift_aff)); -DEFINE (SLEEP_SAVE_SP_SZ ,sizeof (struct sleep_save_sp )); -DEFINE (SLEEP_SAVE_SP_PHYS , offsetof(struct sleep_save_sp , save_ptr_stash_phys)); -DEFINE (SLEEP_SAVE_SP_VIRT , offsetof(struct sleep_save_sp , save_ptr_stash));DEFINE (SLEEP_STACK_DATA_SYSTEM_REGS , offsetof(struct sleep_stack_data , system_regs));DEFINE (SLEEP_STACK_DATA_CALLEE_REGS , offsetof(struct sleep_stack_data , callee_saved_regs));#endif
arch/arm64/kernel/head.S +1 -1
@@ -622,7 +622,7 @@ ENDPROC(__secondary_switched) * If it isn't, park the CPU */ .section ".idmap.text", "ax" -__enable_mmu: +ENTRY(__enable_mmu) mrs x1, ID_AA64MMFR0_EL1 ubfx x2, x1, #ID_AA64MMFR0_TGRAN_SHIFT, 4 cmp x2, #ID_AA64MMFR0_TGRAN_SUPPORTED
arch/arm64/kernel/setup.c +0 -1
@@ -174,7 +174,6 @@ static void __init smp_build_mpidr_hash(void)*/ if (mpidr_hash_size () >4 *num_possible_cpus ())pr_warn ("Large number of MPIDR hash buckets detected\n" ); -__flush_dcache_area (&mpidr_hash,sizeof (struct mpidr_hash )); }static void __init setup_machine_fdt (phys_addr_t dt_phys)
arch/arm64/kernel/sleep.S +20 -37
94 lines not on this page · raw message
arch/arm64/kernel/suspend.c +4 -48
77 lines not on this page · raw message
arch/arm64/mm/proc.S +7 -21
@@ -61,20 +61,17 @@ ENTRY(cpu_do_suspend) mrs x2, tpidr_el0 mrs x3, tpidrro_el0 mrs x4, contextidr_el1 - mrs x5, mair_el1 mrs x6, cpacr_el1 - mrs x7, ttbr1_el1 mrs x8, tcr_el1 mrs x9, vbar_el1 mrs x10, mdscr_el1 mrs x11, oslsr_el1 mrs x12, sctlr_el1 stp x2, x3, [x0] - stp x4, x5, [x0, #16] - stp x6, x7, [x0, #32] - stp x8, x9, [x0, #48] - stp x10, x11, [x0, #64] - str x12, [x0, #80] + stp x4, xzr, [x0, #16] + stp x6, x8, [x0, #32] + stp x9, x10, [x0, #48] + stp x11, x12, [x0, #64] ret ENDPROC(cpu_do_suspend)
@@ -82,30 +79,20 @@ ENDPROC(cpu_do_suspend) * cpu_do_resume - restore CPU register context * * x0: Physical address of context pointer - * x1: ttbr0_el1 to be restored * * Returns: * sctlr_el1 value in x0 */ ENTRY(cpu_do_resume) - /* - * Invalidate local tlb entries before turning on MMU - */ - tlbi vmalle1 ldp x2, x3, [x0] ldp x4, x5, [x0, #16] - ldp x6, x7, [x0, #32] - ldp x8, x9, [x0, #48] - ldp x10, x11, [x0, #64] - ldr x12, [x0, #80] + ldp x6, x8, [x0, #32] + ldp x9, x10, [x0, #48] + ldp x11, x12, [x0, #64] msr tpidr_el0, x2 msr tpidrro_el0, x3 msr contextidr_el1, x4 - msr mair_el1, x5 msr cpacr_el1, x6 - msr ttbr0_el1, x1 - msr ttbr1_el1, x7 - tcr_set_idmap_t0sz x8, x7 msr tcr_el1, x8 msr vbar_el1, x9 msr mdscr_el1, x10
@@ -115,7 +102,6 @@ ENTRY(cpu_do_resume) ubfx x11, x11, #1, #1 msr oslar_el1, x11 mov x0, x12 - dsb nsh // Make sure local tlb invalidation completed isb ret ENDPROC(cpu_do_resume)
Signature · 1 line
2.6.2
PATCH v3 7/10 arm64: kernel: Include _AC definition in page.h
In reply to James Morsepage.h uses '_AC' in the definition of PAGE_SIZE, but doesn't include linux/const.h where this is defined. This produces build warnings when only asm/page.h is included by asm code.
--- arch/arm64/include/asm/page.h | 2 ++ 1 file changed, 2 insertions(+)
arch/arm64/include/asm/page.h +2 -0
@@ -19,6 +19,8 @@#ifndef __ASM_PAGE_H#define __ASM_PAGE_H +#include <linux/const.h> +/* PAGE_SHIFT determines the page size */ /* CONT_SHIFT determines the number of pages which can be tracked together */ #ifdef CONFIG_ARM64_64K_PAGES
Signature · 1 line
2.6.2
PATCH v3 7/10 arm64: kernel: Include _AC definition in page.h
Quoted 5 lines · James Morse
page.huses '_AC' in the definition ofPAGE_SIZE, but doesn't includelinux/const.hwhere this is defined. This produces build warnings when onlyasm/page.his included by asm code.Signed-off-by James Morse <[email protected]>
Quoted 17 lines
--- arch/arm64/include/asm/page.h | 2 ++ 1 file changed, 2 insertions(+)arch/arm64/include/asm/page.h +2
-0@@ -19,6 +19,8 @@#ifndef __ASM_PAGE_H#define __ASM_PAGE_H +#include <linux/const.h> +/* PAGE_SHIFT determines the page size */ /* CONT_SHIFT determines the number of pages which can be tracked together */ #ifdef CONFIG_ARM64_64K_PAGES
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
PATCH v3 8/10 arm64: Promote KERNEL_START/KERNEL_END definitions to a header file
In reply to James MorseKERNEL_START and KERNEL_END are useful outside head.S, move them to a header file.
--- arch/arm64/include/asm/memory.h | 3 +++ arch/arm64/kernel/head.S | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-)
arch/arm64/include/asm/memory.h +3 -0
@@ -70,6 +70,9 @@#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 4)) +#define KERNEL_START _text +#define KERNEL_END _end +/* * Physical vs virtual RAM address space conversion. These are * private definitions which should NOT be used outside memory.h
arch/arm64/kernel/head.S +0 -3
@@ -48,9 +48,6 @@ #error TEXT_OFFSET must be less than 2MB #endif -#define KERNEL_START _text -#define KERNEL_END _end - /* * Kernel startup entry point. * ---------------------------
Signature · 1 line
2.6.2
PATCH v3 8/10 arm64: Promote KERNEL_START/KERNEL_END definitions to a header file
Quoted 4 lines · James Morse
KERNEL_STARTandKERNEL_ENDare useful outsidehead.S, move them to a header file.Signed-off-by James Morse <[email protected]>
Quoted 28 lines
arch/arm64/include/asm/memory.h +3
-0@@ -70,6 +70,9 @@#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 4)) +#define KERNEL_START _text +#define KERNEL_END _end +/* * Physical vs virtual RAM address space conversion. These are * private definitions which should NOT be used outside memory.h arch/arm64/kernel/head.S +0
-3@@ -48,9 +48,6 @@ #error TEXT_OFFSET must be less than 2MB #endif -#define KERNEL_START _text -#define KERNEL_END _end - /* * Kernel startup entry point. * ---------------------------
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
PATCH v3 9/10 PM / Hibernate: Publish pages restored in-place to arch code
In reply to James MorseSome architectures require code written to memory as if it were data to be 'cleaned' from any data caches before the processor can fetch them as new instructions.
During resume from hibernate, the snapshot code copies some pages directly, meaning these architectures do not get a chance to perform their cache maintenance. Create a new list of pages that were restored in place, so that the arch code can perform this maintenance when necessary.
--- include/linux/suspend.h | 1 + kernel/power/snapshot.c | 42 ++++++++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 14 deletions(-)
include/linux/suspend.h +1 -0
@@ -384,6 +384,7 @@ extern bool system_entering_hibernation(void);extern bool hibernation_available (void ); asmlinkage int swsusp_save(void);extern struct pbe *restore_pblist; +extern struct pbe *restored_inplace_pblist;#else /* CONFIG_HIBERNATION */ static inline void register_nosave_region (unsigned long b,unsigned long e) {}static inline void register_nosave_region_late (unsigned long b,unsigned long e) {}
kernel/power/snapshot.c +28 -14
77 lines not on this page · raw message
Signature · 1 line
2.6.2
PATCH v3 9/10 PM / Hibernate: Publish pages restored in-place to arch code
On Thu, Nov 26, 2015 at 05:32:47PM +0000, James Morse wrote:
[...]
kernel/power/snapshot.c static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca) · line 2435
Quoted 3 lines
kernel/power/snapshot.c +0
-0@@ -2427,25 +2434,31 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)if (PageHighMem (page))return get_highmem_page_buffer (page, ca);
I know it is not a problem for arm64, but you should export the "restored" highmem pages list too because arch code may need to use it for the same reasons this patch was implemented.
Quoted 28 lines
kernel/power/snapshot.c +9
-14-if (swsusp_page_is_forbidden (page) &&swsusp_page_is_free (page)) -/* We have allocated the "original" page frame and we can -* use it directly to store the loaded page. -*/ -return page_address (page); - -/* The "original" page frame has not been allocated and we have to -* use a "safe" page frame to store the loaded page. -*/ pbe =chain_alloc (ca,sizeof (struct pbe ));if (!pbe) {swsusp_free ();return ERR_PTR (-ENOMEM ); } - pbe->orig_address =page_address (page); - pbe->address = safe_pages_list; - safe_pages_list = safe_pages_list->next; - pbe->next = restore_pblist; - restore_pblist = pbe; + +if (swsusp_page_is_forbidden (page) &&swsusp_page_is_free (page)) { +/* We have allocated the "original" page frame and we can +* use it directly to store the loaded page. +*/ + pbe->orig_address =NULL ; + pbe->address =page_address (page); + pbe->next = restored_inplace_pblist; + restored_inplace_pblist = pbe;
kernel/power/snapshot.c static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca) · line 2460
Quoted 10 lines
kernel/power/snapshot.c +10
-0+ }else { +/* The "original" page frame has not been allocated and we +* have to use a "safe" page frame to store the loaded page. +*/ + pbe->orig_address =page_address (page); + pbe->address = safe_pages_list; + safe_pages_list = safe_pages_list->next; + pbe->next = restore_pblist; + restore_pblist = pbe; + }
This makes sense to me, more so given that the pbe data is already pre-allocated regardless in prepare_image() (ie it should not change the current behaviour apart from calling chain_alloc() for every page we are restoring), you are just adding a pointer to stash that information, hence, if it is ok for Pavel and Rafael I think this patch can be considered for merging.
Feedback appreciated.
Thanks,
Lorenzo
Quoted 15 lines
kernel/power/snapshot.c +2
-0+return pbe->address; }@@ -2513,6 +2526,7 @@ int snapshot_write_next(struct snapshot_handle *handle)chain_init (&ca,GFP_ATOMIC ,PG_SAFE );memory_bm_position_reset (&orig_bm); restore_pblist =NULL ; + restored_inplace_pblist =NULL ; handle->buffer =get_buffer (&orig_bm, &ca); handle->sync_read =0 ;if (IS_ERR (handle->buffer))Signature · 1 line
2.6.2
PATCH v3 9/10 PM / Hibernate: Publish pages restored in-place to arch code
Hi Lorenzo,
Quoted 8 lines · Lorenzo Pieralisi
kernel/power/snapshot.c static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca) · line 2435
Quoted 3 lines · James Morse
kernel/power/snapshot.c +0
-0@@ -2427,25 +2434,31 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca)if (PageHighMem (page))return get_highmem_page_buffer (page, ca);I know it is not a problem for arm64, but you should export the "restored" highmem pages list too because arch code may need to use it for the same reasons this patch was implemented.
I'm not sure it can be for the same reasons:
kernel/power/snapshot.c:swap_two_pages_data() does the copy for highmem pages, and then kunmap-s them... if the page is to be used for execution, it needs to be re-mapped first, possibly at a different address. The place to do cache maintenance is in the kunmap/kmap calls.
Thanks,
James
PATCH v3 9/10 PM / Hibernate: Publish pages restored in-place to arch code
In reply to James MorseQuoted 8 lines · James Morse
Some architectures require code written to memory as if it were data to be 'cleaned' from any data caches before the processor can fetch them as new instructions.
During resume from hibernate, the snapshot code copies some pages directly, meaning these architectures do not get a chance to perform their cache maintenance. Create a new list of pages that were restored in place, so that the arch code can perform this maintenance when necessary.
Umm. Could the copy function be modified to do the neccessary flushing, instead?
Alternatively, can you just clean the whole cache before jumping to the new kernel?
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
PATCH v3 9/10 PM / Hibernate: Publish pages restored in-place to arch code
Hi Pavel,
Quoted 5 lines · Pavel Machek
Quoted 4 lines · James Morse
Some architectures require code written to memory as if it were data to be 'cleaned' from any data caches before the processor can fetch them as new instructions.
Quoted 7 lines
Quoted 4 lines
During resume from hibernate, the snapshot code copies some pages directly, meaning these architectures do not get a chance to perform their cache maintenance. Create a new list of pages that were restored in place, so that the arch code can perform this maintenance when necessary.
Umm. Could the copy function be modified to do the neccessary flushing, instead?
The copying is done by load_image_lzo() using memcpy() if you have compression enabled, and by load_image() using swap_read_page() if you don't.
I didn't do it here as it would clean every page copied, which was the worrying part of the previous approach. If there is an architecture where this cache-clean operation is expensive, it would slow down restore. I was trying to benchmark the impact of this on 32bit arm when I spotted it was broken.
This allocated-same-page code path doesn't happen very often, so we don't want this to have an impact on the 'normal' code path. On 32bit arm I saw ~20 of these allocations out of ~60,000 pages.
This new way allocates a few extra pages during restore, and doesn't assume that flush_cache_range() needs calling. It should have no impact on architectures that aren't using the new list.
Quoted 2 lines
Alternatively, can you just clean the whole cache before jumping to the new kernel?
On arm64, cleaning the whole cache means cleaning all of memory by virtual address, which would be a high price to pay when we only need to clean the pages we copied. The current implementation does clean all the page it copies, the problem is the ~0.03% that are copied behind its back. This patch publishes where those pages are.
Thanks!
James
PATCH v3 9/10 PM / Hibernate: Publish pages restored in-place to arch code
Hi!
Quoted 7 lines
Quoted 2 lines
Umm. Could the copy function be modified to do the neccessary flushing, instead?
The copying is done by
load_image_lzo()usingmemcpy()if you have compression enabled, and byload_image()usingswap_read_page()if you don't.
Quoted 5 lines
I didn't do it here as it would clean every page copied, which was the worrying part of the previous approach. If there is an architecture where this cache-clean operation is expensive, it would slow down restore. I was trying to benchmark the impact of this on 32bit arm when I spotted it was broken.
You have just loaded the page from slow storage (hard drive, MMC). Cleaning a page should be pretty fast compared to that.
Quoted 7 lines
This allocated-same-page code path doesn't happen very often, so we don't want this to have an impact on the 'normal' code path. On 32bit arm I saw ~20 of these allocations out of ~60,000 pages.
This new way allocates a few extra pages during restore, and doesn't assume that
flush_cache_range()needs calling. It should have no impact on architectures that aren't using the new list.
It is also complex.
Quoted 6 lines
Quoted 2 lines
Alternatively, can you just clean the whole cache before jumping to the new kernel?
On arm64, cleaning the whole cache means cleaning all of memory by virtual address, which would be a high price to pay when we only need to clean the pages we copied. The current implementation does clean all
How high price to pay? I mean, hibernation/restore takes _seconds_. Paying miliseconds to have cleaner code is acceptable price.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
PATCH v3 9/10 PM / Hibernate: Publish pages restored in-place to arch code
Hi Pavel,
Quoted 8 lines · Pavel Machek
Quoted 5 lines
I didn't do it here as it would clean every page copied, which was the worrying part of the previous approach. If there is an architecture where this cache-clean operation is expensive, it would slow down restore. I was trying to benchmark the impact of this on 32bit arm when I spotted it was broken.
You have just loaded the page from slow storage (hard drive, MMC). Cleaning a page should be pretty fast compared to that.
(One day I hope to own a laptop that hibernates to almost-memory speed nvram!)
Speed is one issue - another is I don't think its correct to assume that any architecture with a flush_icache_range() function can/should have that called on any page of data.
There is also the possibility that an architecture needs to do something other than flush_icache_range() on the pages that were copied. (I can see lots of s390 hooks for 'page keys', Intel's memory protection keys may want something similar...)
This patch is the general-purpose fix, matching the existing list of 'these pages need copying' with a 'these pages were already copied'.
Quoted 9 lines
Quoted 7 lines
This allocated-same-page code path doesn't happen very often, so we don't want this to have an impact on the 'normal' code path. On 32bit arm I saw ~20 of these allocations out of ~60,000 pages.
This new way allocates a few extra pages during restore, and doesn't assume that
flush_cache_range()needs calling. It should have no impact on architectures that aren't using the new list.It is also complex.
Its symmetric with the existing restore_pblist code, I think that this is the simplest way of doing it.
Quoted 10 lines
Quoted 6 lines
Quoted 2 lines
Alternatively, can you just clean the whole cache before jumping to the new kernel?
On arm64, cleaning the whole cache means cleaning all of memory by virtual address, which would be a high price to pay when we only need to clean the pages we copied. The current implementation does clean all
How high price to pay? I mean, hibernation/restore takes _seconds_. Paying miliseconds to have cleaner code is acceptable price.
I agree, but the code to clean all 8GB of memory on Juno takes ~3 seconds, and this will probably scale linearly. We only need to clean the ~250MB that was copied by hibernate, (and of that, only the executable pages). The sticking point is the few pages it copies, but doesn't tell us about.
I will put together the flush_icache_range()-during-decompression version of this patch... it looks like powerpc will suffer the most from this, from the comments, its flush_icache_range() code pushes data all the way out to memory...
Thanks,
James
PATCH v3 9/10 PM / Hibernate: Call flush_icache_range() on pages restored in-place
In reply to James MorseSome architectures require code written to memory as if it were data to be 'cleaned' from any data caches before the processor can fetch them as new instructions.
During resume from hibernate, the snapshot code copies some pages directly, meaning these architectures do not get a chance to perform their cache maintenance. Modify the read and decompress code to call flush_icache_range() on all pages that are restored, so that the restored in-place pages are guaranteed to be executable on these architectures.
---
Hi,
This is an alternative version of 9/10 [0] requested by Pavel. It isn't possible to know which pages are being restored in place, so this code cleans all the pages restored. This is unnecessary 99.95% of the time, as the pages will be copied (and cleaned again) by arch code walking restore_pblist.
I may be being over-cautions, but this looks like it will have the most impact on powerpc, where the cache will be cleaned to memory, a page at a time.
Patch 10/10 of the series won't build with this 9/10, I will repost the series if we chose to use this version.
James
[0] http://www.spinics.net/lists/arm-kernel/msg463600.html
kernel/power/swap.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
kernel/power/swap.c +18 -0
@@ -37,6 +37,14 @@#define HIBERNATE_SIG "S1SUSPEND"/* +* When reading an {un,}compressed image, we may restore pages in place, +* in which case some architectures need these pages cleaning before they +* can be executed. We don't know which pages these may be, so clean the lot. +*/ +bool clean_pages_on_read = false; +bool clean_pages_on_decompress = false; + +/* * The swap map is a data structure used for keeping track of each page * written to a swap partition. It consists of many swap_map_page * structures that contain each an array of MAP_PAGE_ENTRIES swap entries.
@@ -241,6 +249,9 @@ static void hib_end_io(struct bio *bio)if (bio_data_dir (bio) ==WRITE )put_page (page); +else if (clean_pages_on_read) +flush_icache_range ((unsigned long )page_address (page), + (unsigned long )page_address (page) +PAGE_SIZE );if (bio->bi_error && !hb->error) hb->error = bio->bi_error;
@@ -1049,6 +1060,7 @@ static int load_image(struct swap_map_handle *handle,hib_init_batch (&hb); + clean_pages_on_read = true;printk (KERN_INFO "PM: Loading image data pages (%u pages)...\n" , nr_to_read); m = nr_to_read /10 ;
@@ -1124,6 +1136,10 @@ static int lzo_decompress_threadfn(void *data) d->unc_len =LZO_UNC_SIZE ; d->ret =lzo1x_decompress_safe (d->cmp +LZO_HEADER , d->cmp_len, d->unc, &d->unc_len); +if (clean_pages_on_decompress) +flush_icache_range ((unsigned long )d->unc, + (unsigned long )d->unc + d->unc_len); +atomic_set (&d->stop,1 );wake_up (&d->done); }
@@ -1189,6 +1205,8 @@ static int load_image_lzo(struct swap_map_handle *handle, }memset (crc,0 , offsetof(struct crc_data , go)); + clean_pages_on_decompress = true; +/* * Start the decompression threads. */
Signature · 1 line
2.6.2
PATCH v3 10/10 arm64: kernel: Add support for hibernate/suspend-to-disk.
In reply to James MorseAdd support for hibernate/suspend-to-disk.
Suspend borrows code from cpu_suspend() to write cpu state onto the stack, before calling swsusp_save() to save the memory image.
Restore creates a set of temporary page tables, covering the kernel and the linear map, copies the restore code to a 'safe' page, then uses the copy to restore the memory image. It calls into cpu_resume(),
and then follows the normal cpu_suspend() path back into the suspend code.
The implementation assumes that exactly the same kernel is booted on the same hardware, and that the kernel is loaded at the same physical address.
--- arch/arm64/Kconfig | 3 + arch/arm64/include/asm/suspend.h | 8 + arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/asm-offsets.c | 4 + arch/arm64/kernel/hibernate-asm.S | 119 ++++++++++++ arch/arm64/kernel/hibernate.c | 376 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/vmlinux.lds.S | 15 ++ 7 files changed, 526 insertions(+) create mode 100644 arch/arm64/kernel/hibernate-asm.S create mode 100644 arch/arm64/kernel/hibernate.c
arch/arm64/Kconfig +3 -0
@@ -769,6 +769,9 @@ menu "Power management options" source "kernel/power/Kconfig" +config ARCH_HIBERNATION_POSSIBLE + def_bool y + config ARCH_SUSPEND_POSSIBLE def_bool y
arch/arm64/include/asm/suspend.h +8 -0
@@ -1,3 +1,5 @@ +#include <linux/suspend.h> +#ifndef __ASM_SUSPEND_H#define __ASM_SUSPEND_H
@@ -34,6 +36,12 @@ struct sleep_stack_data {unsigned long callee_saved_regs[NR_CALLEE_SAVED_REGS ]; }; +extern int swsusp_arch_suspend (void ); +extern int swsusp_arch_resume (void ); +int swsusp_arch_suspend_enter (struct cpu_suspend_ctx *ptr); +void __noreturn swsusp_arch_suspend_exit (phys_addr_t tmp_pg_dir, +phys_addr_t swapper_pg_dir, +void *kernel_start,void *kernel_end);extern int cpu_suspend (unsigned long arg,int (*fn)(unsigned long ));extern void cpu_resume (void );int __cpu_suspend_enter (struct sleep_stack_data *state);
arch/arm64/kernel/Makefile +1 -0
@@ -41,6 +41,7 @@ arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o arm64-obj-$(CONFIG_PCI) += pci.o arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o arm64-obj-$(CONFIG_ACPI) += acpi.o +arm64-obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o obj-y += $(arm64-obj-y) vdso/ obj-m += $(arm64-obj-m)
arch/arm64/kernel/asm-offsets.c +4 -0
@@ -22,6 +22,7 @@#include <linux/mm.h> #include <linux/dma-mapping.h> #include <linux/kvm_host.h> +#include <linux/suspend.h> #include <asm/thread_info.h> #include <asm/memory.h> #include <asm/smp_plat.h>
@@ -160,5 +161,8 @@ int main(void)DEFINE (SLEEP_STACK_DATA_SYSTEM_REGS , offsetof(struct sleep_stack_data , system_regs));DEFINE (SLEEP_STACK_DATA_CALLEE_REGS , offsetof(struct sleep_stack_data , callee_saved_regs));#endif +DEFINE (HIBERN_PBE_ORIG , offsetof(struct pbe , orig_address)); +DEFINE (HIBERN_PBE_ADDR , offsetof(struct pbe , address)); +DEFINE (HIBERN_PBE_NEXT , offsetof(struct pbe , next));return 0 ; }
arch/arm64/kernel/hibernate-asm.S new file +119 -0
119 lines not on this page · raw message
arch/arm64/kernel/hibernate.c new file +376 -0
376 lines not on this page · raw message
arch/arm64/kernel/vmlinux.lds.S +15 -0
@@ -45,6 +45,16 @@ jiffies = jiffies_64; *(.idmap.text) \ VMLINUX_SYMBOL(__idmap_text_end) = .; +#ifdef CONFIG_HIBERNATION +#define HIBERNATE_TEXT \ + . = ALIGN(SZ_4K); \ + VMLINUX_SYMBOL(__hibernate_exit_text_start) = .;\ + *(.hibernate_exit.text) \ + VMLINUX_SYMBOL(__hibernate_exit_text_end) = .; +#else +#define HIBERNATE_TEXT +#endif + /* * The size of the PE/COFF section that covers the kernel image, which * runs from stext to _edata, must be a round multiple of the PE/COFF
@@ -106,6 +116,7 @@ SECTIONS LOCK_TEXT HYPERVISOR_TEXT IDMAP_TEXT + HIBERNATE_TEXT *(.fixup) *(.gnu.warning) . = ALIGN(16);
@@ -185,6 +196,10 @@ ASSERT(__hyp_idmap_text_end - (__hyp_idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K, "HYP init code too big or misaligned") ASSERT(__idmap_text_end - (__idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K, "ID map text too big or misaligned") +#ifdef CONFIG_HIBERNATION +ASSERT(__hibernate_exit_text_end - (__hibernate_exit_text_start & ~(SZ_4K - 1)) + <= SZ_4K, "Hibernate exit text too big or misaligned") +#endif /* * If padding is applied before .head.text, virt<->phys conversions will fail.
Signature · 1 line
2.6.2
PATCH v3 10/10 arm64: kernel: Add support for hibernate/suspend-to-disk.
Hi!
Quoted 19 lines
Suspend borrows code from
cpu_suspend()to write cpu state onto the stack, before callingswsusp_save()to save the memory image.Restore creates a set of temporary page tables, covering the kernel and the linear map, copies the restore code to a 'safe' page, then uses the copy to restore the memory image. It calls into
cpu_resume(),
and then follows the normalcpu_suspend()path back into the suspend code.The implementation assumes that exactly the same kernel is booted on the same hardware, and that the kernel is loaded at the same physical address.
Signed-off-by James Morse <[email protected]>Acked-by Pavel Machek <[email protected]>arch/arm64/include/asm/suspend.h +0
-0
arch/arm64/include/asm/suspend.h line 5
Quoted 5 lines
arch/arm64/include/asm/suspend.h +2
-0@@ -1,3 +1,5 @@ +#include <linux/suspend.h> +#ifndef __ASM_SUSPEND_H#define __ASM_SUSPEND_H
Actually... even additional includes should go after the #ifdef guards.
Thanks,
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
PATCH v3 10/10 arm64: kernel: Add support for hibernate/suspend-to-disk.
Hi Pavel,
Quoted 5 lines · Pavel Machek
Quoted 5 lines
arch/arm64/include/asm/suspend.h +0
-0@@ -1,3 +1,5 @@
Quoted 8 lines
arch/arm64/include/asm/suspend.h line 5
Quoted 5 lines
arch/arm64/include/asm/suspend.h +2
-0+#include <linux/suspend.h> +#ifndef __ASM_SUSPEND_H#define __ASM_SUSPEND_HActually... even additional includes should go after the #ifdef guards.
Ooops - thanks for spotting that! I will send a fixup.
(I'm expecting to publish a new version if there are newer versions of the four patches shared with kexec.)
Thanks,
James
PATCH 1/1 fixup! arm64: kernel: Add support for hibernate/suspend-to-disk
In reply to James Morseadditional includes should go after the #ifdef guards
--- arch/arm64/include/asm/suspend.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/include/asm/suspend.h +2 -2
@@ -1,8 +1,8 @@ -#include <linux/suspend.h> -#ifndef __ASM_SUSPEND_H#define __ASM_SUSPEND_H +#include <linux/suspend.h> +#define NR_CTX_REGS 10#define NR_CALLEE_SAVED_REGS 12
Signature · 1 line
2.6.2
Catalin Marinas
maintainerPATCH v3 10/10 arm64: kernel: Add support for hibernate/suspend-to-disk.
In reply to James MorseQuoted 24 lines · James Morse
arch/arm64/kernel/hibernate.c new file +24
-0+static int copy_page_tables (pgd_t *new_pgd,unsigned long start_addr) +{ +int i; +int rc =0 ; +pud_t *new_pud; +pgd_t *old_pgd =pgd_offset_k (start_addr); + + new_pgd +=pgd_index (start_addr); + +for (i =pgd_index (start_addr); i <PTRS_PER_PGD ; + i++, start_addr +=PGDIR_SIZE , old_pgd++, new_pgd++) { +if (!pgd_val (*old_pgd)) +continue ; + +if (PTRS_PER_PUD !=1 ) { + new_pud = (pud_t *)get_safe_page (GFP_ATOMIC ); +if (!new_pud) { + rc = -ENOMEM ; +break ; + } + +set_pgd (new_pgd,__pgd (virt_to_phys (new_pud) + |PUD_TYPE_TABLE )); + }
arch/arm64/kernel/hibernate.c line 287
Quoted 10 lines
arch/arm64/kernel/hibernate.c new file +10
-0+ + rc =copy_pud (new_pgd, old_pgd, &start_addr); +if (rc) +break ; + + start_addr &=PGDIR_MASK ; + } + +return rc; +}
I think you could implement the above with fewer lines if you followed a common pattern for page table walking based on do...while(), pgd_addr_end() etc. See copy_page_range() as a (more complex) example.
Signature · 1 line
Catalin