Re: [RFC PATCH] Current status, suspend-to-disk support on ARM

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, 12 Apr 2011, Pavel Machek wrote:

Hi!

There's one ugly thing in this set - I've changed a generic kernel
header, <linux/suspend.h> to #define save/restore_processor_state()
on ARM so that it only does preempt_disable/enable(). It's
surprising that this isn't the default behaviour; all platforms need
swsusp_arch_suspend/resume anyway so why force the existance of
_two_ arch-specific hooks ?

Can you submit separate patch cleaning it up?

How about the attached ?

It's for discussion, and hence not tested (admittedly, I need an x86 test system ...).

The diff is against RMK's devel-stable tree, as of commit 5f183860d5007ec76ea36bfa6c36d66e37f0dbcf


There's a few hurdles here:

a) who knows all assembly calling conventions for all architectures
   supported by linux ?

   This applies to SH and S390; save/restore_processor_state on those
   are primitive and should be inlined within swsusp_arch_suspend/resume,
   just like the attached patch does for the powerpc variants.

   I've done a bounce call within S390 but don't know enough about SH3
   for arch/sh/kernel/cpu/sh3/swsusp.S - i.e. add a call in assembly to
   init_cpu(current), and then ditch save_processor_state.

b) some architectures do things _beyond_ register/state saving in those
   two functions, and hence rely on them being called paired.

c) CONFIG_KEXEC_JUMP (ab-)uses them. I don't know that subsystem at all,
   but this sort of re-use means the symbols can't just be ditched.
   They become arch-dependent though, no non-arch/ code calls them
   anymore.

What I've attached might break SH3 swsusp, as save_processor_state is no longer called there (it stores FPU state).




I've also been wondering:
The comments in kernel/power/hibernate.c mention the need to get preempt counts "right" as major motivator to call save/restore_processor_state.
effect" of save/restore_processor_state).

But then on all architectures in kernels as far back as 2.6.32 it doesn't look like save/restore_processor state are actually adjusting the preempt count; nowhere do they call preempt_enable/disable.

What is and what is not necessary, really ?


Finally, on things like e.g. the floating point context saves: Wouldn't this happen as part of freezing processes (in the early stages of suspend), and/or as part of device quiesce ? I wonder why e.g. powerpc does it explicitly; not doing so on ARM doesn't seem to cause problems.




@@ -195,6 +195,14 @@ config VECTORS_BASE
 	help
 	  The base address of exception vectors.

+config ARCH_HIBERNATION_POSSIBLE
+	bool
+	help
+	  If the machine architecture supports suspend-to-disk
+	  it should select this automatically for you.
+	  Otherwise, say 'Y' at your own peril.
+

Good for debugging, but not good for mainline.

How would this be done better ?

FrankH.


--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
 arch/mips/power/hibernate.S        |    8 ++++++-
 arch/powerpc/kernel/swsusp.c       |   39 ------------------------------------
 arch/powerpc/kernel/swsusp_32.S    |   18 ++++++++++++++++
 arch/powerpc/kernel/swsusp_asm64.S |   21 +++++++++++++++++++
 arch/powerpc/kernel/swsusp_booke.S |   19 +++++++++++++++++
 arch/s390/kernel/swsusp_asm64.S    |    6 +++++
 arch/sh/kernel/swsusp.c            |    5 ----
 arch/unicore32/kernel/hibernate.c  |    9 --------
 arch/x86/power/hibernate_asm_32.S  |    4 ++-
 arch/x86/power/hibernate_asm_64.S  |    2 +
 include/linux/suspend.h            |    2 +
 kernel/power/hibernate.c           |   10 ++++----
 12 files changed, 83 insertions(+), 60 deletions(-)

diff --git a/arch/mips/power/hibernate.S b/arch/mips/power/hibernate.S
index dbb5c7b..1b12ae1 100644
--- a/arch/mips/power/hibernate.S
+++ b/arch/mips/power/hibernate.S
@@ -27,6 +27,9 @@ LEAF(swsusp_arch_suspend)
 	PTR_S s5, PT_R21(t0)
 	PTR_S s6, PT_R22(t0)
 	PTR_S s7, PT_R23(t0)
+	jal save_processor_state
+	PTR_LA t0, saved_regs
+	PTR_L ra, PT_R31(t0)
 	j swsusp_save
 END(swsusp_arch_suspend)
 
@@ -45,7 +48,6 @@ LEAF(swsusp_arch_resume)
 	PTR_L t0, PBE_NEXT(t0)
 	bnez t0, 0b
 	PTR_LA t0, saved_regs
-	PTR_L ra, PT_R31(t0)
 	PTR_L sp, PT_R29(t0)
 	PTR_L fp, PT_R30(t0)
 	PTR_L gp, PT_R28(t0)
@@ -57,6 +59,10 @@ LEAF(swsusp_arch_resume)
 	PTR_L s5, PT_R21(t0)
 	PTR_L s6, PT_R22(t0)
 	PTR_L s7, PT_R23(t0)
+	jal restore_processor_state
+	PTR_LA t0, saved_regs
+	PTR_L ra, PT_R31(t0)
+
 	PTR_LI v0, 0x0
 	jr ra
 END(swsusp_arch_resume)
diff --git a/arch/powerpc/kernel/swsusp.c b/arch/powerpc/kernel/swsusp.c
deleted file mode 100644
index 560c961..0000000
--- a/arch/powerpc/kernel/swsusp.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Common powerpc suspend code for 32 and 64 bits
- *
- * Copyright 2007	Johannes Berg <johannes@xxxxxxxxxxxxxxxx>
- *
- * 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.
- */
-
-#include <linux/sched.h>
-#include <asm/suspend.h>
-#include <asm/system.h>
-#include <asm/current.h>
-#include <asm/mmu_context.h>
-
-void save_processor_state(void)
-{
-	/*
-	 * flush out all the special registers so we don't need
-	 * to save them in the snapshot
-	 */
-	flush_fp_to_thread(current);
-	flush_altivec_to_thread(current);
-	flush_spe_to_thread(current);
-
-#ifdef CONFIG_PPC64
-	hard_irq_disable();
-#endif
-
-}
-
-void restore_processor_state(void)
-{
-#ifdef CONFIG_PPC32
-	switch_mmu_context(NULL, current->active_mm);
-#endif
-}
diff --git a/arch/powerpc/kernel/swsusp_32.S b/arch/powerpc/kernel/swsusp_32.S
index b0754e2..08137c5 100644
--- a/arch/powerpc/kernel/swsusp_32.S
+++ b/arch/powerpc/kernel/swsusp_32.S
@@ -116,6 +116,16 @@ _GLOBAL(swsusp_arch_suspend)
 	/* Backup various CPU config stuffs */
 	bl	__save_cpu_setup
 #endif
+	/* flush out all the special registers so we don't need
+	 * to save them in the snapshot
+	 */
+	tophys(r4,r2);
+	bl	flush_fp_to_thread
+	tophys(r4,r2);
+	bl	flush_altivec_to_thread
+	tophys(r4,r2);
+	bl	flush_spe_to_thread
+
 	/* Call the low level suspend stuff (we should probably have made
 	 * a stackframe...
 	 */
@@ -323,6 +333,14 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_USE_HIGH_BATS)
 	li	r0,1
 	mtdec	r0
 
+	/* restore MMU context - switch_mmu_context(NULL, current->mm); */
+	li	r3,0
+	/* 'current->mm' needs to be in r4 */
+	tophys(r4, r2)
+	lwz     r4, MM(r4)
+	tophys(r4, r4)
+	bl      switch_mmu_context
+
 	/* Restore the callee-saved registers and return */
 	lwz	r0,SL_CR(r11)
 	mtcr	r0
diff --git a/arch/powerpc/kernel/swsusp_asm64.S b/arch/powerpc/kernel/swsusp_asm64.S
index 86ac1d9..e46786d 100644
--- a/arch/powerpc/kernel/swsusp_asm64.S
+++ b/arch/powerpc/kernel/swsusp_asm64.S
@@ -76,8 +76,29 @@ restore_pblist_ptr:
 	.section .text
 	.align  5
 _GLOBAL(swsusp_arch_suspend)
+	/* Save LR first so that we can make function calls */
 	ld	r11,swsusp_save_area_ptr@toc(r2)
 	SAVE_SPECIAL(LR)
+
+	/* flush out all the special registers so we don't need
+	 * to save them in the snapshot
+	 */
+	ld	r3,PACACURRENT(r13)
+	bl flush_fp_to_thread
+	ld	r3,PACACURRENT(r13)
+	bl flush_altivec_to_thread
+	ld	r3,PACACURRENT(r13)
+	bl flush_spe_to_thread
+	/* Disable interrupts now */
+	mfmsr	r4		/* Get current interrupt state */
+	rldicu	r3,r4,48,1	/* clear MSR_EE */
+	rotldi	r3,r3,16
+	mtmsrd	r3,1		/* Update machine state */
+	li	r3,0
+	stb	r3,PACASOFTIRQEN(r13)
+	stb	r3,PACAHARDIRQEN(r13)
+
+	ld	r11,swsusp_save_area_ptr@toc(r2)
 	SAVE_REGISTER(r1)
 	SAVE_SPECIAL(CR)
 	SAVE_SPECIAL(TB)
diff --git a/arch/powerpc/kernel/swsusp_booke.S b/arch/powerpc/kernel/swsusp_booke.S
index 11a3930..529e95b 100644
--- a/arch/powerpc/kernel/swsusp_booke.S
+++ b/arch/powerpc/kernel/swsusp_booke.S
@@ -50,8 +50,27 @@ _GLOBAL(swsusp_arch_suspend)
 	lis	r11,swsusp_save_area@h
 	ori	r11,r11,swsusp_save_area@l
 
+	/* save LR first so we can make function calls */
 	mflr	r0
 	stw	r0,SL_LR(r11)
+
+	/* flush out all the special registers so we don't need
+	 * to save them in the snapshot
+	 */
+	ld	r3,PACACURRENT(r13)
+	bl flush_fp_to_thread
+	ld	r3,PACACURRENT(r13)
+	bl flush_altivec_to_thread
+	ld	r3,PACACURRENT(r13)
+	bl flush_spe_to_thread
+	/* Disable interrupts now */
+	wrteei	0
+	li	r0,0
+	stb	r0,PACASOFTIRQEN(r13)
+	stb	r0,PACAHARDIRQEN(r13)
+
+	lis	r11,swsusp_save_area@h
+	ori	r11,r11,swsusp_save_area@l
 	mfcr	r0
 	stw	r0,SL_CR(r11)
 	stw	r1,SL_SP(r11)
diff --git a/arch/s390/kernel/swsusp_asm64.S b/arch/s390/kernel/swsusp_asm64.S
index 1f066e4..00cceff 100644
--- a/arch/s390/kernel/swsusp_asm64.S
+++ b/arch/s390/kernel/swsusp_asm64.S
@@ -25,6 +25,8 @@
 	.align	4
 	.globl swsusp_arch_suspend
 swsusp_arch_suspend:
+	brasl	%r14,save_processor_state	/* disables lowcore protection */
+
 	stmg	%r6,%r15,__SF_GPRS(%r15)
 	lgr	%r1,%r15
 	aghi	%r15,-STACK_FRAME_OVERHEAD
@@ -100,6 +102,8 @@ swsusp_arch_suspend:
 	/* Save image */
 	brasl	%r14,swsusp_save
 
+	brasl	%r14,restore_processor_state
+
 	/* Restore prefix register and return */
 	lghi	%r1,0x1000
 	spx	0x318(%r1)
@@ -259,6 +263,8 @@ restore_registers:
 	/* Reinitialize the channel subsystem */
 	brasl	%r14,channel_subsystem_reinit
 
+	/* reenable lowcore protection */
+	brasl	%r14,restore_processor_state
 	/* Return 0 */
 	lmg	%r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
 	lghi	%r2,0
diff --git a/arch/sh/kernel/swsusp.c b/arch/sh/kernel/swsusp.c
index 12b64a0..65a964e 100644
--- a/arch/sh/kernel/swsusp.c
+++ b/arch/sh/kernel/swsusp.c
@@ -31,8 +31,3 @@ void save_processor_state(void)
 {
 	init_fpu(current);
 }
-
-void restore_processor_state(void)
-{
-	local_flush_tlb_all();
-}
diff --git a/arch/unicore32/kernel/hibernate.c b/arch/unicore32/kernel/hibernate.c
index 7d0f0b7..aba4ad4 100644
--- a/arch/unicore32/kernel/hibernate.c
+++ b/arch/unicore32/kernel/hibernate.c
@@ -149,12 +149,3 @@ int pfn_is_nosave(unsigned long pfn)
 
 	return (pfn >= begin_pfn) && (pfn < end_pfn);
 }
-
-void save_processor_state(void)
-{
-}
-
-void restore_processor_state(void)
-{
-	local_flush_tlb_all();
-}
diff --git a/arch/x86/power/hibernate_asm_32.S b/arch/x86/power/hibernate_asm_32.S
index ad47dae..6827e1b 100644
--- a/arch/x86/power/hibernate_asm_32.S
+++ b/arch/x86/power/hibernate_asm_32.S
@@ -15,6 +15,7 @@
 .text
 
 ENTRY(swsusp_arch_suspend)
+	call save_processor_state
 	movl %esp, saved_context_esp
 	movl %ebx, saved_context_ebx
 	movl %ebp, saved_context_ebp
@@ -22,7 +23,6 @@ ENTRY(swsusp_arch_suspend)
 	movl %edi, saved_context_edi
 	pushfl
 	popl saved_context_eflags
-
 	call swsusp_save
 	ret
 
@@ -75,6 +75,8 @@ done:
 	pushl saved_context_eflags
 	popfl
 
+	call restore_processor_state
+
 	xorl	%eax, %eax
 
 	ret
diff --git a/arch/x86/power/hibernate_asm_64.S b/arch/x86/power/hibernate_asm_64.S
index 9356547..1318398 100644
--- a/arch/x86/power/hibernate_asm_64.S
+++ b/arch/x86/power/hibernate_asm_64.S
@@ -23,6 +23,7 @@
 #include <asm/processor-flags.h>
 
 ENTRY(swsusp_arch_suspend)
+	call save_processor_state
 	movq	$saved_context, %rax
 	movq	%rsp, pt_regs_sp(%rax)
 	movq	%rbp, pt_regs_bp(%rax)
@@ -139,6 +140,7 @@ ENTRY(restore_registers)
 	pushq	pt_regs_flags(%rax)
 	popfq
 
+	call restore_processor_state
 	xorq	%rax, %rax
 
 	/* tell the hibernation core that we've just restored the memory */
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 5a89e36..145d9a4 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -259,8 +259,10 @@ static inline bool system_entering_hibernation(void) { return false; }
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_KEXEC_JUMP
 void save_processor_state(void);
 void restore_processor_state(void);
+#endif
 
 /* kernel/power/main.c */
 extern int register_pm_notifier(struct notifier_block *nb);
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index aeabd26..554b741 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -285,13 +285,14 @@ static int create_image(int platform_mode)
 		goto Power_up;
 
 	in_suspend = 1;
-	save_processor_state();
+	preempt_disable();
 	error = swsusp_arch_suspend();
 	if (error)
 		printk(KERN_ERR "PM: Error %d creating hibernation image\n",
 			error);
 	/* Restore control flow magically appears here */
-	restore_processor_state();
+	flush_tlb_all();
+	preempt_enable();
 	if (!in_suspend) {
 		events_check_enabled = false;
 		platform_leave(platform_mode);
@@ -412,8 +413,7 @@ static int resume_target_kernel(bool platform_mode)
 	if (error)
 		goto Enable_irqs;
 
-	/* We'll ignore saved state, but this gets preempt count (etc) right */
-	save_processor_state();
+	preempt_disable();
 	error = restore_highmem();
 	if (!error) {
 		error = swsusp_arch_resume();
@@ -432,7 +432,7 @@ static int resume_target_kernel(bool platform_mode)
 	 * subsequent failures
 	 */
 	swsusp_free();
-	restore_processor_state();
+	preempt_enable();
 	touch_softlockup_watchdog();
 
 	syscore_resume();
_______________________________________________
linux-pm mailing list
linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

[Index of Archives]     [Linux ACPI]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [CPU Freq]     [Kernel Newbies]     [Fedora Kernel]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux