/dev/null | 235 --------
25-akpm/arch/i386/Kconfig | 44 -
25-akpm/arch/i386/Makefile | 1
25-akpm/arch/i386/kernel/Makefile | 2
25-akpm/arch/i386/power/Makefile | 3
25-akpm/arch/i386/power/cpu.c | 141 +++++
25-akpm/arch/i386/power/pmdisk.S | 94 +++
25-akpm/arch/i386/power/swsusp.S | 94 +++
25-akpm/drivers/acpi/sleep/main.c | 53 +
25-akpm/drivers/acpi/sleep/proc.c | 73 ++
25-akpm/drivers/acpi/sleep/sleep.h | 3
25-akpm/drivers/base/core.c | 33 -
25-akpm/drivers/base/power/main.c | 13
25-akpm/drivers/base/power/power.h | 3
25-akpm/drivers/base/power/resume.c | 21
25-akpm/drivers/base/power/suspend.c | 10
25-akpm/include/asm-i386/suspend.h | 7
25-akpm/include/linux/suspend.h | 13
25-akpm/kernel/power/Kconfig | 92 +++
25-akpm/kernel/power/Makefile | 1
25-akpm/kernel/power/console.c | 2
25-akpm/kernel/power/disk.c | 335 ++++++++++++
25-akpm/kernel/power/main.c | 391 ++------------
25-akpm/kernel/power/pmdisk.c | 942 +++++++++++++++++++++++++++++++++++
25-akpm/kernel/power/power.h | 41 -
25-akpm/kernel/power/swsusp.c | 405 +++++++++++----
25-akpm/kernel/sched.c | 4
25-akpm/kernel/sys.c | 7
28 files changed, 2249 insertions(+), 814 deletions(-)
diff -puN arch/i386/Kconfig~test5-pm2 arch/i386/Kconfig
--- 25/arch/i386/Kconfig~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/arch/i386/Kconfig Wed Sep 17 12:33:38 2003
@@ -815,49 +815,7 @@ endmenu
menu "Power management options (ACPI, APM)"
depends on !X86_VOYAGER
-config PM
- bool "Power Management support"
- ---help---
- "Power Management" means that parts of your computer are shut
- off or put into a power conserving "sleep" mode if they are not
- being used. There are two competing standards for doing this: APM
- and ACPI. If you want to use either one, say Y here and then also
- to the requisite support below.
-
- Power Management is most important for battery powered laptop
- computers; if you have a laptop, check out the Linux Laptop home
- page on the WWW at
- and the
- Battery Powered Linux mini-HOWTO, available from
- .
-
- Note that, even if you say N here, Linux on the x86 architecture
- will issue the hlt instruction if nothing is to be done, thereby
- sending the processor to sleep and saving power.
-
-config SOFTWARE_SUSPEND
- bool "Software Suspend (EXPERIMENTAL)"
- depends on EXPERIMENTAL && PM && SWAP
- ---help---
- Enable the possibilty of suspendig machine. It doesn't need APM.
- You may suspend your machine by 'swsusp' or 'shutdown -z '
- (patch for sysvinit needed).
-
- It creates an image which is saved in your active swaps. By the next
- booting the, pass 'resume=/dev/swappartition' and kernel will
- detect the saved image, restore the memory from
- it and then it continues to run as before you've suspended.
- If you don't want the previous state to continue use the 'noresume'
- kernel option. However note that your partitions will be fsck'd and
- you must re-mkswap your swap partitions. It does not work with swap
- files.
-
- Right now you may boot without resuming and then later resume but
- in meantime you cannot use those swap partitions/files which were
- involved in suspending. Also in this case there is a risk that buffers
- on disk won't match with saved ones.
-
- For more information take a look at Documentation/swsusp.txt.
+source kernel/power/Kconfig
source "drivers/acpi/Kconfig"
diff -puN arch/i386/kernel/Makefile~test5-pm2 arch/i386/kernel/Makefile
--- 25/arch/i386/kernel/Makefile~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/arch/i386/kernel/Makefile Wed Sep 17 12:33:38 2003
@@ -18,9 +18,7 @@ obj-$(CONFIG_KGDB) += kgdb_stub.o
obj-$(CONFIG_X86_MSR) += msr.o
obj-$(CONFIG_X86_CPUID) += cpuid.o
obj-$(CONFIG_MICROCODE) += microcode.o
-obj-$(CONFIG_PM) += suspend.o
obj-$(CONFIG_APM) += apm.o
-obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend_asm.o
obj-$(CONFIG_X86_SMP) += smp.o smpboot.o
obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
obj-$(CONFIG_X86_MPPARSE) += mpparse.o
diff -puN -L arch/i386/kernel/suspend_asm.S arch/i386/kernel/suspend_asm.S~test5-pm2 /dev/null
--- 25/arch/i386/kernel/suspend_asm.S
+++ /dev/null Thu Apr 11 07:25:15 2002
@@ -1,94 +0,0 @@
-.text
-
-/* Originally gcc generated, modified by hand */
-
-#include
-#include
-#include
-
- .text
-
-ENTRY(do_magic)
- pushl %ebx
- cmpl $0,8(%esp)
- jne .L1450
- call do_magic_suspend_1
- call save_processor_state
-
- movl %esp, saved_context_esp
- movl %eax, saved_context_eax
- movl %ebx, saved_context_ebx
- movl %ecx, saved_context_ecx
- movl %edx, saved_context_edx
- movl %ebp, saved_context_ebp
- movl %esi, saved_context_esi
- movl %edi, saved_context_edi
- pushfl ; popl saved_context_eflags
-
- call do_magic_suspend_2
- jmp .L1449
- .p2align 4,,7
-.L1450:
- movl $swapper_pg_dir-__PAGE_OFFSET,%ecx
- movl %ecx,%cr3
-
- call do_magic_resume_1
- movl $0,loop
- cmpl $0,nr_copy_pages
- je .L1453
- .p2align 4,,7
-.L1455:
- movl $0,loop2
- .p2align 4,,7
-.L1459:
- movl pagedir_nosave,%ecx
- movl loop,%eax
- movl loop2,%edx
- sall $4,%eax
- movl 4(%ecx,%eax),%ebx
- movl (%ecx,%eax),%eax
- movb (%edx,%eax),%al
- movb %al,(%edx,%ebx)
- movl %cr3, %eax;
- movl %eax, %cr3; # flush TLB
-
- movl loop2,%eax
- leal 1(%eax),%edx
- movl %edx,loop2
- movl %edx,%eax
- cmpl $4095,%eax
- jbe .L1459
- movl loop,%eax
- leal 1(%eax),%edx
- movl %edx,loop
- movl %edx,%eax
- cmpl nr_copy_pages,%eax
- jb .L1455
- .p2align 4,,7
-.L1453:
- movl $__USER_DS,%eax
-
- movw %ax, %ds
- movw %ax, %es
- movl saved_context_esp, %esp
- movl saved_context_ebp, %ebp
- movl saved_context_eax, %eax
- movl saved_context_ebx, %ebx
- movl saved_context_ecx, %ecx
- movl saved_context_edx, %edx
- movl saved_context_esi, %esi
- movl saved_context_edi, %edi
- call restore_processor_state
- pushl saved_context_eflags ; popfl
- call do_magic_resume_2
-.L1449:
- popl %ebx
- ret
-
- .section .data.nosave
-loop:
- .quad 0
-loop2:
- .quad 0
- .previous
-
\ No newline at end of file
diff -puN -L arch/i386/kernel/suspend.c arch/i386/kernel/suspend.c~test5-pm2 /dev/null
--- 25/arch/i386/kernel/suspend.c
+++ /dev/null Thu Apr 11 07:25:15 2002
@@ -1,141 +0,0 @@
-/*
- * Suspend support specific for i386.
- *
- * Distribute under GPLv2
- *
- * Copyright (c) 2002 Pavel Machek
- * Copyright (c) 2001 Patrick Mochel
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-static struct saved_context saved_context;
-static void fix_processor_context(void);
-
-unsigned long saved_context_eax, saved_context_ebx;
-unsigned long saved_context_ecx, saved_context_edx;
-unsigned long saved_context_esp, saved_context_ebp;
-unsigned long saved_context_esi, saved_context_edi;
-unsigned long saved_context_eflags;
-
-extern void enable_sep_cpu(void *);
-
-void save_processor_state(void)
-{
- kernel_fpu_begin();
-
- /*
- * descriptor tables
- */
- asm volatile ("sgdt %0" : "=m" (saved_context.gdt_limit));
- asm volatile ("sidt %0" : "=m" (saved_context.idt_limit));
- asm volatile ("sldt %0" : "=m" (saved_context.ldt));
- asm volatile ("str %0" : "=m" (saved_context.tr));
-
- /*
- * segment registers
- */
- asm volatile ("movw %%es, %0" : "=m" (saved_context.es));
- asm volatile ("movw %%fs, %0" : "=m" (saved_context.fs));
- asm volatile ("movw %%gs, %0" : "=m" (saved_context.gs));
- asm volatile ("movw %%ss, %0" : "=m" (saved_context.ss));
-
- /*
- * control registers
- */
- asm volatile ("movl %%cr0, %0" : "=r" (saved_context.cr0));
- asm volatile ("movl %%cr2, %0" : "=r" (saved_context.cr2));
- asm volatile ("movl %%cr3, %0" : "=r" (saved_context.cr3));
- asm volatile ("movl %%cr4, %0" : "=r" (saved_context.cr4));
-}
-
-static void
-do_fpu_end(void)
-{
- /* restore FPU regs if necessary */
- /* Do it out of line so that gcc does not move cr0 load to some stupid place */
- kernel_fpu_end();
-}
-
-void restore_processor_state(void)
-{
-
- /*
- * control registers
- */
- asm volatile ("movl %0, %%cr4" :: "r" (saved_context.cr4));
- asm volatile ("movl %0, %%cr3" :: "r" (saved_context.cr3));
- asm volatile ("movl %0, %%cr2" :: "r" (saved_context.cr2));
- asm volatile ("movl %0, %%cr0" :: "r" (saved_context.cr0));
-
- /*
- * segment registers
- */
- asm volatile ("movw %0, %%es" :: "r" (saved_context.es));
- asm volatile ("movw %0, %%fs" :: "r" (saved_context.fs));
- asm volatile ("movw %0, %%gs" :: "r" (saved_context.gs));
- asm volatile ("movw %0, %%ss" :: "r" (saved_context.ss));
-
- /*
- * now restore the descriptor tables to their proper values
- * ltr is done i fix_processor_context().
- */
- asm volatile ("lgdt %0" :: "m" (saved_context.gdt_limit));
- asm volatile ("lidt %0" :: "m" (saved_context.idt_limit));
- asm volatile ("lldt %0" :: "m" (saved_context.ldt));
-
- /*
- * sysenter MSRs
- */
- if (boot_cpu_has(X86_FEATURE_SEP))
- enable_sep_cpu(NULL);
-
- fix_processor_context();
- do_fpu_end();
-}
-
-static void fix_processor_context(void)
-{
- int cpu = smp_processor_id();
- struct tss_struct * t = init_tss + cpu;
-
- set_tss_desc(cpu,t); /* This just modifies memory; should not be necessary. But... This is necessary, because 386 hardware has concept of busy TSS or some similar stupidity. */
- cpu_gdt_table[cpu][GDT_ENTRY_TSS].b &= 0xfffffdff;
-
- load_TR_desc(); /* This does ltr */
- load_LDT(¤t->active_mm->context); /* This does lldt */
-
- /*
- * Now maybe reload the debug registers
- */
- if (current->thread.debugreg[7]){
- loaddebug(¤t->thread, 0);
- loaddebug(¤t->thread, 1);
- loaddebug(¤t->thread, 2);
- loaddebug(¤t->thread, 3);
- /* no 4 and 5 */
- loaddebug(¤t->thread, 6);
- loaddebug(¤t->thread, 7);
- }
-
-}
-
-EXPORT_SYMBOL(save_processor_state);
-EXPORT_SYMBOL(restore_processor_state);
diff -puN arch/i386/Makefile~test5-pm2 arch/i386/Makefile
--- 25/arch/i386/Makefile~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/arch/i386/Makefile Wed Sep 17 12:33:38 2003
@@ -103,6 +103,7 @@ drivers-$(CONFIG_MATH_EMULATION) += arch
drivers-$(CONFIG_PCI) += arch/i386/pci/
# must be linked after kernel/
drivers-$(CONFIG_OPROFILE) += arch/i386/oprofile/
+drivers-$(CONFIG_PM) += arch/i386/power/
CFLAGS += $(mflags-y)
AFLAGS += $(mflags-y)
diff -puN /dev/null arch/i386/power/cpu.c
--- /dev/null Thu Apr 11 07:25:15 2002
+++ 25-akpm/arch/i386/power/cpu.c Wed Sep 17 12:33:38 2003
@@ -0,0 +1,141 @@
+/*
+ * Suspend support specific for i386.
+ *
+ * Distribute under GPLv2
+ *
+ * Copyright (c) 2002 Pavel Machek
+ * Copyright (c) 2001 Patrick Mochel
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static struct saved_context saved_context;
+static void fix_processor_context(void);
+
+unsigned long saved_context_eax, saved_context_ebx;
+unsigned long saved_context_ecx, saved_context_edx;
+unsigned long saved_context_esp, saved_context_ebp;
+unsigned long saved_context_esi, saved_context_edi;
+unsigned long saved_context_eflags;
+
+extern void enable_sep_cpu(void *);
+
+void save_processor_state(void)
+{
+ kernel_fpu_begin();
+
+ /*
+ * descriptor tables
+ */
+ asm volatile ("sgdt %0" : "=m" (saved_context.gdt_limit));
+ asm volatile ("sidt %0" : "=m" (saved_context.idt_limit));
+ asm volatile ("sldt %0" : "=m" (saved_context.ldt));
+ asm volatile ("str %0" : "=m" (saved_context.tr));
+
+ /*
+ * segment registers
+ */
+ asm volatile ("movw %%es, %0" : "=m" (saved_context.es));
+ asm volatile ("movw %%fs, %0" : "=m" (saved_context.fs));
+ asm volatile ("movw %%gs, %0" : "=m" (saved_context.gs));
+ asm volatile ("movw %%ss, %0" : "=m" (saved_context.ss));
+
+ /*
+ * control registers
+ */
+ asm volatile ("movl %%cr0, %0" : "=r" (saved_context.cr0));
+ asm volatile ("movl %%cr2, %0" : "=r" (saved_context.cr2));
+ asm volatile ("movl %%cr3, %0" : "=r" (saved_context.cr3));
+ asm volatile ("movl %%cr4, %0" : "=r" (saved_context.cr4));
+}
+
+static void
+do_fpu_end(void)
+{
+ /* restore FPU regs if necessary */
+ /* Do it out of line so that gcc does not move cr0 load to some stupid place */
+ kernel_fpu_end();
+}
+
+void restore_processor_state(void)
+{
+
+ /*
+ * control registers
+ */
+ asm volatile ("movl %0, %%cr4" :: "r" (saved_context.cr4));
+ asm volatile ("movl %0, %%cr3" :: "r" (saved_context.cr3));
+ asm volatile ("movl %0, %%cr2" :: "r" (saved_context.cr2));
+ asm volatile ("movl %0, %%cr0" :: "r" (saved_context.cr0));
+
+ /*
+ * segment registers
+ */
+ asm volatile ("movw %0, %%es" :: "r" (saved_context.es));
+ asm volatile ("movw %0, %%fs" :: "r" (saved_context.fs));
+ asm volatile ("movw %0, %%gs" :: "r" (saved_context.gs));
+ asm volatile ("movw %0, %%ss" :: "r" (saved_context.ss));
+
+ /*
+ * now restore the descriptor tables to their proper values
+ * ltr is done i fix_processor_context().
+ */
+ asm volatile ("lgdt %0" :: "m" (saved_context.gdt_limit));
+ asm volatile ("lidt %0" :: "m" (saved_context.idt_limit));
+ asm volatile ("lldt %0" :: "m" (saved_context.ldt));
+
+ /*
+ * sysenter MSRs
+ */
+ if (boot_cpu_has(X86_FEATURE_SEP))
+ enable_sep_cpu(NULL);
+
+ fix_processor_context();
+ do_fpu_end();
+}
+
+static void fix_processor_context(void)
+{
+ int cpu = smp_processor_id();
+ struct tss_struct * t = init_tss + cpu;
+
+ set_tss_desc(cpu,t); /* This just modifies memory; should not be necessary. But... This is necessary, because 386 hardware has concept of busy TSS or some similar stupidity. */
+ cpu_gdt_table[cpu][GDT_ENTRY_TSS].b &= 0xfffffdff;
+
+ load_TR_desc(); /* This does ltr */
+ load_LDT(¤t->active_mm->context); /* This does lldt */
+
+ /*
+ * Now maybe reload the debug registers
+ */
+ if (current->thread.debugreg[7]){
+ loaddebug(¤t->thread, 0);
+ loaddebug(¤t->thread, 1);
+ loaddebug(¤t->thread, 2);
+ loaddebug(¤t->thread, 3);
+ /* no 4 and 5 */
+ loaddebug(¤t->thread, 6);
+ loaddebug(¤t->thread, 7);
+ }
+
+}
+
+EXPORT_SYMBOL(save_processor_state);
+EXPORT_SYMBOL(restore_processor_state);
diff -puN /dev/null arch/i386/power/Makefile
--- /dev/null Thu Apr 11 07:25:15 2002
+++ 25-akpm/arch/i386/power/Makefile Wed Sep 17 12:33:38 2003
@@ -0,0 +1,3 @@
+obj-$(CONFIG_PM) += cpu.o
+obj-$(CONFIG_PM_DISK) += pmdisk.o
+obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
diff -puN /dev/null arch/i386/power/pmdisk.S
--- /dev/null Thu Apr 11 07:25:15 2002
+++ 25-akpm/arch/i386/power/pmdisk.S Wed Sep 17 12:33:38 2003
@@ -0,0 +1,94 @@
+.text
+
+/* Originally gcc generated, modified by hand */
+
+#include
+#include
+#include
+
+ .text
+
+ENTRY(pmdisk_arch_suspend)
+ pushl %ebx
+ cmpl $0,8(%esp)
+ jne .L1450
+ call save_processor_state
+
+ movl %esp, saved_context_esp
+ movl %eax, saved_context_eax
+ movl %ebx, saved_context_ebx
+ movl %ecx, saved_context_ecx
+ movl %edx, saved_context_edx
+ movl %ebp, saved_context_ebp
+ movl %esi, saved_context_esi
+ movl %edi, saved_context_edi
+ pushfl ; popl saved_context_eflags
+
+ call pmdisk_suspend
+ jmp .L1449
+ .p2align 4,,7
+.L1450:
+ movl $swapper_pg_dir-__PAGE_OFFSET,%ecx
+ movl %ecx,%cr3
+
+ movl $0,loop
+ cmpl $0,pmdisk_pages
+ je .L1453
+ .p2align 4,,7
+.L1455:
+ movl $0,loop2
+ .p2align 4,,7
+.L1459:
+ movl pm_pagedir_nosave,%ecx
+ movl loop,%eax
+ movl loop2,%edx
+ sall $4,%eax
+ movl 4(%ecx,%eax),%ebx
+ movl (%ecx,%eax),%eax
+ movb (%edx,%eax),%al
+ movb %al,(%edx,%ebx)
+ movl %cr3, %eax;
+ movl %eax, %cr3; # flush TLB
+
+ movl loop2,%eax
+ leal 1(%eax),%edx
+ movl %edx,loop2
+ movl %edx,%eax
+ cmpl $4095,%eax
+ jbe .L1459
+ movl loop,%eax
+ leal 1(%eax),%edx
+ movl %edx,loop
+ movl %edx,%eax
+ cmpl pmdisk_pages,%eax
+ jb .L1455
+ .p2align 4,,7
+.L1453:
+ movl $__USER_DS,%eax
+
+ movw %ax, %ds
+ movw %ax, %es
+ movl saved_context_esp, %esp
+ movl saved_context_ebp, %ebp
+ movl saved_context_eax, %eax
+ movl saved_context_ebx, %ebx
+ movl saved_context_ecx, %ecx
+ movl saved_context_edx, %edx
+ movl saved_context_esi, %esi
+ movl saved_context_edi, %edi
+ pushl saved_context_eflags ; popfl
+ call pmdisk_resume
+.L1449:
+ popl %ebx
+ pushl %eax
+ call restore_processor_state
+ popl %eax
+ ret
+
+ .section .data.nosave
+loop:
+ .quad 0
+loop2:
+ .quad 0
+ .previous
+
diff -puN /dev/null arch/i386/power/swsusp.S
--- /dev/null Thu Apr 11 07:25:15 2002
+++ 25-akpm/arch/i386/power/swsusp.S Wed Sep 17 12:33:38 2003
@@ -0,0 +1,94 @@
+.text
+
+/* Originally gcc generated, modified by hand */
+
+#include
+#include
+#include
+
+ .text
+
+ENTRY(do_magic)
+ pushl %ebx
+ cmpl $0,8(%esp)
+ jne .L1450
+ call do_magic_suspend_1
+ call save_processor_state
+
+ movl %esp, saved_context_esp
+ movl %eax, saved_context_eax
+ movl %ebx, saved_context_ebx
+ movl %ecx, saved_context_ecx
+ movl %edx, saved_context_edx
+ movl %ebp, saved_context_ebp
+ movl %esi, saved_context_esi
+ movl %edi, saved_context_edi
+ pushfl ; popl saved_context_eflags
+
+ call do_magic_suspend_2
+ jmp .L1449
+ .p2align 4,,7
+.L1450:
+ movl $swapper_pg_dir-__PAGE_OFFSET,%ecx
+ movl %ecx,%cr3
+
+ call do_magic_resume_1
+ movl $0,loop
+ cmpl $0,nr_copy_pages
+ je .L1453
+ .p2align 4,,7
+.L1455:
+ movl $0,loop2
+ .p2align 4,,7
+.L1459:
+ movl pagedir_nosave,%ecx
+ movl loop,%eax
+ movl loop2,%edx
+ sall $4,%eax
+ movl 4(%ecx,%eax),%ebx
+ movl (%ecx,%eax),%eax
+ movb (%edx,%eax),%al
+ movb %al,(%edx,%ebx)
+ movl %cr3, %eax;
+ movl %eax, %cr3; # flush TLB
+
+ movl loop2,%eax
+ leal 1(%eax),%edx
+ movl %edx,loop2
+ movl %edx,%eax
+ cmpl $4095,%eax
+ jbe .L1459
+ movl loop,%eax
+ leal 1(%eax),%edx
+ movl %edx,loop
+ movl %edx,%eax
+ cmpl nr_copy_pages,%eax
+ jb .L1455
+ .p2align 4,,7
+.L1453:
+ movl $__USER_DS,%eax
+
+ movw %ax, %ds
+ movw %ax, %es
+ movl saved_context_esp, %esp
+ movl saved_context_ebp, %ebp
+ movl saved_context_eax, %eax
+ movl saved_context_ebx, %ebx
+ movl saved_context_ecx, %ecx
+ movl saved_context_edx, %edx
+ movl saved_context_esi, %esi
+ movl saved_context_edi, %edi
+ call restore_processor_state
+ pushl saved_context_eflags ; popfl
+ call do_magic_resume_2
+.L1449:
+ popl %ebx
+ ret
+
+ .section .data.nosave
+loop:
+ .quad 0
+loop2:
+ .quad 0
+ .previous
+
\ No newline at end of file
diff -puN drivers/acpi/sleep/main.c~test5-pm2 drivers/acpi/sleep/main.c
--- 25/drivers/acpi/sleep/main.c~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/drivers/acpi/sleep/main.c Wed Sep 17 12:33:38 2003
@@ -41,7 +41,6 @@ static u32 acpi_suspend_states[] = {
static int acpi_pm_prepare(u32 state)
{
- int error = 0;
u32 acpi_state = acpi_suspend_states[state];
if (!sleep_states[acpi_state])
@@ -56,21 +55,9 @@ static int acpi_pm_prepare(u32 state)
acpi_set_firmware_waking_vector(
(acpi_physical_address) acpi_wakeup_address);
}
-
ACPI_FLUSH_CPU_CACHE();
-
- /* Do arch specific saving of state. */
- if (state > PM_SUSPEND_STANDBY) {
- if ((error = acpi_save_state_mem()))
- goto Err;
- }
-
acpi_enter_sleep_state_prep(acpi_state);
-
return 0;
- Err:
- acpi_set_firmware_waking_vector(0);
- return error;
}
@@ -90,6 +77,15 @@ static int acpi_pm_enter(u32 state)
u32 acpi_state = acpi_suspend_states[state];
ACPI_FLUSH_CPU_CACHE();
+
+ /* Do arch specific saving of state. */
+ if (state > PM_SUSPEND_STANDBY) {
+ int error = acpi_save_state_mem();
+ if (error)
+ return error;
+ }
+
+
local_irq_save(flags);
switch (state)
{
@@ -114,6 +110,15 @@ static int acpi_pm_enter(u32 state)
local_irq_restore(flags);
printk(KERN_DEBUG "Back to C!\n");
+ /* restore processor state
+ * We should only be here if we're coming back from STR or STD.
+ * And, in the case of the latter, the memory image should have already
+ * been loaded from disk.
+ */
+ if (state > PM_SUSPEND_STANDBY)
+ acpi_restore_state_mem();
+
+
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
}
@@ -130,14 +135,6 @@ static int acpi_pm_finish(u32 state)
{
acpi_leave_sleep_state(state);
- /* restore processor state
- * We should only be here if we're coming back from STR or STD.
- * And, in the case of the latter, the memory image should have already
- * been loaded from disk.
- */
- if (state > ACPI_STATE_S1)
- acpi_restore_state_mem();
-
/* reset firmware waking vector */
acpi_set_firmware_waking_vector((acpi_physical_address) 0);
@@ -149,6 +146,20 @@ static int acpi_pm_finish(u32 state)
}
+int acpi_suspend(u32 acpi_state)
+{
+ u32 states[] = {
+ [1] = PM_SUSPEND_STANDBY,
+ [3] = PM_SUSPEND_MEM,
+ [4] = PM_SUSPEND_DISK,
+ };
+
+ if (acpi_state <= 4 && states[acpi_state])
+ return pm_suspend(states[acpi_state]);
+ return -EINVAL;
+}
+
+
static struct pm_ops acpi_pm_ops = {
.prepare = acpi_pm_prepare,
.enter = acpi_pm_enter,
diff -puN drivers/acpi/sleep/proc.c~test5-pm2 drivers/acpi/sleep/proc.c
--- 25/drivers/acpi/sleep/proc.c~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/drivers/acpi/sleep/proc.c Wed Sep 17 12:33:38 2003
@@ -13,12 +13,71 @@
#include "sleep.h"
+#define ACPI_SYSTEM_FILE_SLEEP "sleep"
#define ACPI_SYSTEM_FILE_ALARM "alarm"
#define _COMPONENT ACPI_SYSTEM_COMPONENT
ACPI_MODULE_NAME ("sleep")
+static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset)
+{
+ int i;
+
+ ACPI_FUNCTION_TRACE("acpi_system_sleep_seq_show");
+
+ for (i = 0; i <= ACPI_STATE_S5; i++) {
+ if (sleep_states[i]) {
+ seq_printf(seq,"S%d ", i);
+ if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f)
+ seq_printf(seq, "S4bios ");
+ }
+ }
+
+ seq_puts(seq, "\n");
+
+ return 0;
+}
+
+static int acpi_system_sleep_open_fs(struct inode *inode, struct file *file)
+{
+ return single_open(file, acpi_system_sleep_seq_show, PDE(inode)->data);
+}
+
+static int
+acpi_system_write_sleep (
+ struct file *file,
+ const char *buffer,
+ size_t count,
+ loff_t *ppos)
+{
+ char str[12];
+ u32 state = 0;
+ int error = 0;
+
+ if (count > sizeof(str) - 1)
+ goto Done;
+ memset(str,0,sizeof(str));
+ if (copy_from_user(str, buffer, count))
+ return -EFAULT;
+
+ /* Check for S4 bios request */
+ if (!strcmp(str,"4b")) {
+ error = acpi_suspend(4);
+ goto Done;
+ }
+ state = simple_strtoul(str, NULL, 0);
+#ifdef CONFIG_SOFTWARE_SUSPEND
+ if (state == 4) {
+ error = software_suspend();
+ goto Done;
+ }
+#endif
+ error = acpi_suspend(state);
+ Done:
+ return error ? error : count;
+}
+
static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset)
{
u32 sec, min, hr;
@@ -294,6 +353,14 @@ end:
}
+static struct file_operations acpi_system_sleep_fops = {
+ .open = acpi_system_sleep_open_fs,
+ .read = seq_read,
+ .write = acpi_system_write_sleep,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static struct file_operations acpi_system_alarm_fops = {
.open = acpi_system_alarm_open_fs,
.read = seq_read,
@@ -307,6 +374,12 @@ static int acpi_sleep_proc_init(void)
{
struct proc_dir_entry *entry = NULL;
+ /* 'sleep' [R/W]*/
+ entry = create_proc_entry(ACPI_SYSTEM_FILE_SLEEP,
+ S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir);
+ if (entry)
+ entry->proc_fops = &acpi_system_sleep_fops;
+
/* 'alarm' [R/W] */
entry = create_proc_entry(ACPI_SYSTEM_FILE_ALARM,
S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir);
diff -puN drivers/acpi/sleep/sleep.h~test5-pm2 drivers/acpi/sleep/sleep.h
--- 25/drivers/acpi/sleep/sleep.h~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/drivers/acpi/sleep/sleep.h Wed Sep 17 12:33:38 2003
@@ -1,5 +1,4 @@
extern u8 sleep_states[];
-
-extern acpi_status acpi_suspend (u32 state);
+extern int acpi_suspend (u32 state);
diff -puN drivers/base/core.c~test5-pm2 drivers/base/core.c
--- 25/drivers/base/core.c~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/drivers/base/core.c Wed Sep 17 12:33:38 2003
@@ -225,28 +225,30 @@ int device_add(struct device *dev)
dev->kobj.parent = &parent->kobj;
if ((error = kobject_add(&dev->kobj)))
- goto register_done;
-
- /* now take care of our own registration */
-
+ goto Error;
+ if ((error = device_pm_add(dev)))
+ goto PMError;
+ if ((error = bus_add_device(dev)))
+ goto BusError;
down_write(&devices_subsys.rwsem);
if (parent)
list_add_tail(&dev->node,&parent->children);
up_write(&devices_subsys.rwsem);
- bus_add_device(dev);
-
- device_pm_add(dev);
-
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
-
- register_done:
- if (error && parent)
- put_device(parent);
+ Done:
put_device(dev);
return error;
+ BusError:
+ device_pm_remove(dev);
+ PMError:
+ kobject_unregister(&dev->kobj);
+ Error:
+ if (parent)
+ put_device(parent);
+ goto Done;
}
@@ -312,8 +314,6 @@ void device_del(struct device * dev)
{
struct device * parent = dev->parent;
- device_pm_remove(dev);
-
down_write(&devices_subsys.rwsem);
if (parent)
list_del_init(&dev->node);
@@ -324,14 +324,11 @@ void device_del(struct device * dev)
*/
if (platform_notify_remove)
platform_notify_remove(dev);
-
bus_remove_device(dev);
-
+ device_pm_remove(dev);
kobject_del(&dev->kobj);
-
if (parent)
put_device(parent);
-
}
/**
diff -puN drivers/base/power/main.c~test5-pm2 drivers/base/power/main.c
--- 25/drivers/base/power/main.c~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/drivers/base/power/main.c Wed Sep 17 12:33:38 2003
@@ -36,12 +36,14 @@ DECLARE_MUTEX(dpm_sem);
static inline void device_pm_hold(struct device * dev)
{
- atomic_inc(&dev->power.pm_users);
+ if (dev)
+ atomic_inc(&dev->power.pm_users);
}
static inline void device_pm_release(struct device * dev)
{
- atomic_inc(&dev->power.pm_users);
+ if (dev)
+ atomic_dec(&dev->power.pm_users);
}
@@ -61,11 +63,9 @@ static inline void device_pm_release(str
void device_pm_set_parent(struct device * dev, struct device * parent)
{
struct device * old_parent = dev->power.pm_parent;
- if (old_parent)
- device_pm_release(old_parent);
+ device_pm_release(old_parent);
dev->power.pm_parent = parent;
- if (parent)
- device_pm_hold(parent);
+ device_pm_hold(parent);
}
EXPORT_SYMBOL(device_pm_set_parent);
@@ -91,6 +91,7 @@ void device_pm_remove(struct device * de
dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
down(&dpm_sem);
dpm_sysfs_remove(dev);
+ device_pm_release(dev->power.pm_parent);
list_del(&dev->power.entry);
up(&dpm_sem);
}
diff -puN drivers/base/power/power.h~test5-pm2 drivers/base/power/power.h
--- 25/drivers/base/power/power.h~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/drivers/base/power/power.h Wed Sep 17 12:33:38 2003
@@ -58,7 +58,8 @@ extern void dpm_sysfs_remove(struct devi
/*
* resume.c
*/
-extern int dpm_resume(void);
+
+extern void dpm_resume(void);
extern void dpm_power_up(void);
extern int resume_device(struct device *);
diff -puN drivers/base/power/resume.c~test5-pm2 drivers/base/power/resume.c
--- 25/drivers/base/power/resume.c~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/drivers/base/power/resume.c Wed Sep 17 12:33:38 2003
@@ -28,6 +28,19 @@ int resume_device(struct device * dev)
}
+
+void dpm_resume(void)
+{
+ while(!list_empty(&dpm_off)) {
+ struct list_head * entry = dpm_off.next;
+ struct device * dev = to_device(entry);
+ list_del_init(entry);
+ resume_device(dev);
+ list_add_tail(entry,&dpm_active);
+ }
+}
+
+
/**
* device_resume - Restore state of each device in system.
*
@@ -38,13 +51,7 @@ int resume_device(struct device * dev)
void device_resume(void)
{
down(&dpm_sem);
- while(!list_empty(&dpm_off)) {
- struct list_head * entry = dpm_off.next;
- struct device * dev = to_device(entry);
- list_del_init(entry);
- resume_device(dev);
- list_add_tail(entry,&dpm_active);
- }
+ dpm_resume();
up(&dpm_sem);
}
diff -puN drivers/base/power/suspend.c~test5-pm2 drivers/base/power/suspend.c
--- 25/drivers/base/power/suspend.c~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/drivers/base/power/suspend.c Wed Sep 17 12:33:38 2003
@@ -81,14 +81,18 @@ int device_suspend(u32 state)
while(!list_empty(&dpm_active)) {
struct list_head * entry = dpm_active.prev;
struct device * dev = to_device(entry);
- if ((error = suspend_device(dev,state)))
- goto Error;
+ if ((error = suspend_device(dev,state))) {
+ if (error != -EAGAIN)
+ goto Error;
+ else
+ error = 0;
+ }
}
Done:
up(&dpm_sem);
return error;
Error:
- device_resume();
+ dpm_resume();
goto Done;
}
diff -puN include/asm-i386/suspend.h~test5-pm2 include/asm-i386/suspend.h
--- 25/include/asm-i386/suspend.h~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/include/asm-i386/suspend.h Wed Sep 17 12:33:38 2003
@@ -6,11 +6,12 @@
#include
#include
-static inline void
+static inline int
arch_prepare_suspend(void)
{
if (!cpu_has_pse)
- panic("pse required");
+ return -EPERM;
+ return 0;
}
/* image of the saved processor state */
@@ -38,8 +39,6 @@ struct saved_context {
extern void save_processor_state(void);
extern void restore_processor_state(void);
-extern int do_magic(int resume);
-
#ifdef CONFIG_ACPI_SLEEP
extern unsigned long saved_eip;
extern unsigned long saved_esp;
diff -puN include/linux/suspend.h~test5-pm2 include/linux/suspend.h
--- 25/include/linux/suspend.h~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/include/linux/suspend.h Wed Sep 17 12:33:38 2003
@@ -10,7 +10,7 @@
#include
#include
-#ifdef CONFIG_SOFTWARE_SUSPEND
+#ifdef CONFIG_PM
/* page backup entry */
typedef struct pbe {
unsigned long address; /* address of the copy */
@@ -53,10 +53,17 @@ extern suspend_pagedir_t *pagedir_nosave
extern void do_suspend_lowlevel(int resume);
extern void do_suspend_lowlevel_s4bios(int resume);
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_SOFTWARE_SUSPEND
+
+extern unsigned char software_suspend_enabled;
+
+extern void software_suspend(void);
#else /* CONFIG_SOFTWARE_SUSPEND */
-static inline int software_suspend(void)
+static inline void software_suspend(void)
{
- return -EPERM;
+ printk("Warning: fake suspend called\n");
}
#endif /* CONFIG_SOFTWARE_SUSPEND */
diff -puN kernel/power/console.c~test5-pm2 kernel/power/console.c
--- 25/kernel/power/console.c~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/kernel/power/console.c Wed Sep 17 12:33:38 2003
@@ -8,7 +8,7 @@
#include
#include "power.h"
-static int new_loglevel = 7;
+static int new_loglevel = 10;
static int orig_loglevel;
static int orig_fgconsole, orig_kmsg;
diff -puN /dev/null kernel/power/disk.c
--- /dev/null Thu Apr 11 07:25:15 2002
+++ 25-akpm/kernel/power/disk.c Wed Sep 17 12:33:38 2003
@@ -0,0 +1,335 @@
+/*
+ * kernel/power/disk.c - Suspend-to-disk support.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ *
+ * This file is release under the GPLv2
+ *
+ */
+
+#define DEBUG
+
+
+#include
+#include
+#include
+#include
+#include
+#include "power.h"
+
+
+extern u32 pm_disk_mode;
+extern struct pm_ops * pm_ops;
+
+extern int pmdisk_save(void);
+extern int pmdisk_write(void);
+extern int pmdisk_read(void);
+extern int pmdisk_restore(void);
+extern int pmdisk_free(void);
+
+extern long sys_sync(void);
+
+
+/**
+ * power_down - Shut machine down for hibernate.
+ * @mode: Suspend-to-disk mode
+ *
+ * Use the platform driver, if configured so, and return gracefully if it
+ * fails.
+ * Otherwise, try to power off and reboot. If they fail, halt the machine,
+ * there ain't no turning back.
+ */
+
+static int power_down(u32 mode)
+{
+ unsigned long flags;
+ int error = 0;
+
+ local_irq_save(flags);
+ device_power_down(PM_SUSPEND_DISK);
+ switch(mode) {
+ case PM_DISK_PLATFORM:
+ error = pm_ops->enter(PM_SUSPEND_DISK);
+ break;
+ case PM_DISK_SHUTDOWN:
+ printk("Powering off system\n");
+ machine_power_off();
+ break;
+ case PM_DISK_REBOOT:
+ machine_restart(NULL);
+ break;
+ }
+ machine_halt();
+ device_power_up();
+ local_irq_restore(flags);
+ return 0;
+}
+
+
+static int in_suspend __nosavedata = 0;
+
+
+/**
+ * free_some_memory - Try to free as much memory as possible
+ *
+ * ... but do not OOM-kill anyone
+ *
+ * Notice: all userland should be stopped at this point, or
+ * livelock is possible.
+ */
+
+static void free_some_memory(void)
+{
+ printk("Freeing memory: ");
+ while (shrink_all_memory(10000))
+ printk(".");
+ printk("|\n");
+ blk_run_queues();
+}
+
+
+static inline void platform_finish(void)
+{
+ if (pm_disk_mode == PM_DISK_PLATFORM) {
+ if (pm_ops && pm_ops->finish)
+ pm_ops->finish(PM_SUSPEND_DISK);
+ }
+}
+
+static void finish(void)
+{
+ device_resume();
+ platform_finish();
+ thaw_processes();
+ pm_restore_console();
+}
+
+
+static int prepare(void)
+{
+ int error;
+
+ pm_prepare_console();
+
+ sys_sync();
+ if (freeze_processes()) {
+ error = -EBUSY;
+ goto Thaw;
+ }
+
+ if (pm_disk_mode == PM_DISK_PLATFORM) {
+ if (pm_ops && pm_ops->prepare) {
+ if ((error = pm_ops->prepare(PM_SUSPEND_DISK)))
+ goto Thaw;
+ }
+ }
+
+ /* Free memory before shutting down devices. */
+ free_some_memory();
+
+ if ((error = device_suspend(PM_SUSPEND_DISK)))
+ goto Finish;
+
+ return 0;
+ Finish:
+ platform_finish();
+ Thaw:
+ thaw_processes();
+ pm_restore_console();
+ return error;
+}
+
+
+/**
+ * pm_suspend_disk - The granpappy of power management.
+ *
+ * If we're going through the firmware, then get it over with quickly.
+ *
+ * If not, then call pmdis to do it's thing, then figure out how
+ * to power down the system.
+ */
+
+int pm_suspend_disk(void)
+{
+ int error;
+
+ if ((error = prepare()))
+ return error;
+
+ pr_debug("PM: Attempting to suspend to disk.\n");
+ if (pm_disk_mode == PM_DISK_FIRMWARE)
+ return pm_ops->enter(PM_SUSPEND_DISK);
+
+ pr_debug("PM: snapshotting memory.\n");
+ in_suspend = 1;
+ if ((error = pmdisk_save()))
+ goto Done;
+
+ if (in_suspend) {
+ pr_debug("PM: writing image.\n");
+
+ /*
+ * FIXME: Leftover from swsusp. Are they necessary?
+ */
+ mb();
+ barrier();
+
+ error = pmdisk_write();
+ if (!error) {
+ error = power_down(pm_disk_mode);
+ pr_debug("PM: Power down failed.\n");
+ }
+ } else
+ pr_debug("PM: Image restored successfully.\n");
+ pmdisk_free();
+ Done:
+ finish();
+ return error;
+}
+
+
+/**
+ * pm_resume - Resume from a saved image.
+ *
+ * Called as a late_initcall (so all devices are discovered and
+ * initialized), we call pmdisk to see if we have a saved image or not.
+ * If so, we quiesce devices, the restore the saved image. We will
+ * return above (in pm_suspend_disk() ) if everything goes well.
+ * Otherwise, we fail gracefully and return to the normally
+ * scheduled program.
+ *
+ */
+
+static int pm_resume(void)
+{
+ int error;
+
+ pr_debug("PM: Reading pmdisk image.\n");
+
+ if ((error = pmdisk_read()))
+ goto Done;
+
+ pr_debug("PM: Preparing system for restore.\n");
+
+ if ((error = prepare()))
+ goto Free;
+
+ barrier();
+ mb();
+
+ /* FIXME: The following (comment and mdelay()) are from swsusp.
+ * Are they really necessary?
+ *
+ * We do not want some readahead with DMA to corrupt our memory, right?
+ * Do it with disabled interrupts for best effect. That way, if some
+ * driver scheduled DMA, we have good chance for DMA to finish ;-).
+ */
+ pr_debug("PM: Waiting for DMAs to settle down.\n");
+ mdelay(1000);
+
+ pr_debug("PM: Restoring saved image.\n");
+ pmdisk_restore();
+ pr_debug("PM: Restore failed, recovering.n");
+ finish();
+ Free:
+ pmdisk_free();
+ Done:
+ pr_debug("PM: Resume from disk failed.\n");
+ return 0;
+}
+
+late_initcall(pm_resume);
+
+
+static char * pm_disk_modes[] = {
+ [PM_DISK_FIRMWARE] = "firmware",
+ [PM_DISK_PLATFORM] = "platform",
+ [PM_DISK_SHUTDOWN] = "shutdown",
+ [PM_DISK_REBOOT] = "reboot",
+};
+
+/**
+ * disk - Control suspend-to-disk mode
+ *
+ * Suspend-to-disk can be handled in several ways. The greatest
+ * distinction is who writes memory to disk - the firmware or the OS.
+ * If the firmware does it, we assume that it also handles suspending
+ * the system.
+ * If the OS does it, then we have three options for putting the system
+ * to sleep - using the platform driver (e.g. ACPI or other PM registers),
+ * powering off the system or rebooting the system (for testing).
+ *
+ * The system will support either 'firmware' or 'platform', and that is
+ * known a priori (and encoded in pm_ops). But, the user may choose
+ * 'shutdown' or 'reboot' as alternatives.
+ *
+ * show() will display what the mode is currently set to.
+ * store() will accept one of
+ *
+ * 'firmware'
+ * 'platform'
+ * 'shutdown'
+ * 'reboot'
+ *
+ * It will only change to 'firmware' or 'platform' if the system
+ * supports it (as determined from pm_ops->pm_disk_mode).
+ */
+
+static ssize_t disk_show(struct subsystem * subsys, char * buf)
+{
+ return sprintf(buf,"%s\n",pm_disk_modes[pm_disk_mode]);
+}
+
+
+static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n)
+{
+ int error = 0;
+ int i;
+ u32 mode = 0;
+
+ down(&pm_sem);
+ for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) {
+ if (!strcmp(buf,pm_disk_modes[i])) {
+ mode = i;
+ break;
+ }
+ }
+ if (mode) {
+ if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT)
+ pm_disk_mode = mode;
+ else {
+ if (pm_ops && pm_ops->enter &&
+ (mode == pm_ops->pm_disk_mode))
+ pm_disk_mode = mode;
+ else
+ error = -EINVAL;
+ }
+ } else
+ error = -EINVAL;
+
+ pr_debug("PM: suspend-to-disk mode set to '%s'\n",
+ pm_disk_modes[mode]);
+ up(&pm_sem);
+ return error ? error : n;
+}
+
+power_attr(disk);
+
+static struct attribute * g[] = {
+ &disk_attr.attr,
+ NULL,
+};
+
+
+static struct attribute_group attr_group = {
+ .attrs = g,
+};
+
+
+static int __init pm_disk_init(void)
+{
+ return sysfs_create_group(&power_subsys.kset.kobj,&attr_group);
+}
+
+core_initcall(pm_disk_init);
diff -puN /dev/null kernel/power/Kconfig
--- /dev/null Thu Apr 11 07:25:15 2002
+++ 25-akpm/kernel/power/Kconfig Wed Sep 17 12:33:38 2003
@@ -0,0 +1,92 @@
+config PM
+ bool "Power Management support"
+ ---help---
+ "Power Management" means that parts of your computer are shut
+ off or put into a power conserving "sleep" mode if they are not
+ being used. There are two competing standards for doing this: APM
+ and ACPI. If you want to use either one, say Y here and then also
+ to the requisite support below.
+
+ Power Management is most important for battery powered laptop
+ computers; if you have a laptop, check out the Linux Laptop home
+ page on the WWW at
+ and the
+ Battery Powered Linux mini-HOWTO, available from
+ .
+
+ Note that, even if you say N here, Linux on the x86 architecture
+ will issue the hlt instruction if nothing is to be done, thereby
+ sending the processor to sleep and saving power.
+
+config SOFTWARE_SUSPEND
+ bool "Software Suspend (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && PM && SWAP
+ ---help---
+ Enable the possibilty of suspendig machine. It doesn't need APM.
+ You may suspend your machine by 'swsusp' or 'shutdown -z '
+ (patch for sysvinit needed).
+
+ It creates an image which is saved in your active swaps. By the next
+ booting the, pass 'resume=/dev/swappartition' and kernel will
+ detect the saved image, restore the memory from
+ it and then it continues to run as before you've suspended.
+ If you don't want the previous state to continue use the 'noresume'
+ kernel option. However note that your partitions will be fsck'd and
+ you must re-mkswap your swap partitions. It does not work with swap
+ files.
+
+ Right now you may boot without resuming and then later resume but
+ in meantime you cannot use those swap partitions/files which were
+ involved in suspending. Also in this case there is a risk that buffers
+ on disk won't match with saved ones.
+
+ For more information take a look at Documentation/swsusp.txt.
+
+config PM_DISK
+ bool "Suspend-to-Disk Support"
+ depends on PM && SWAP
+ ---help---
+ Suspend-to-disk is a power management state in which the contents
+ of memory are stored on disk and the entire system is shut down or
+ put into a low-power state (e.g. ACPI S4). When the computer is
+ turned back on, the stored image is loaded from disk and execution
+ resumes from where it left off before suspending.
+
+ This config option enables the core infrastructure necessary to
+ perform the suspend and resume transition.
+
+ Currently, this suspend-to-disk implementation is based on a forked
+ version of the swsusp code base. As such, it's still experimental,
+ and still relies on CONFIG_SWAP.
+
+ More information can be found in Documentation/power/.
+
+ If unsure, Say N.
+
+config PM_DISK_PARTITION
+ string "Default resume partition"
+ default ""
+ ---help---
+ The default resume partition is the partition that the pmdisk suspend-
+ to-disk implementation will look for a suspended disk image.
+
+ The partition specified here will be different for almost every user.
+ It should be a valid swap partition (at least for now) that is turned
+ on before suspending.
+
+ The partition specified can be overridden by specifying:
+
+ pmdisk=/dev/
+
+ which will set the resume partition to the device specified.
+
+ One may also do:
+
+ pmdisk=off
+
+ to inform the kernel not to perform a resume transition.
+
+ Note there is currently not a way to specify which device to save the
+ suspended image to. It will simply pick the first available swap
+ device.
+
diff -puN kernel/power/main.c~test5-pm2 kernel/power/main.c
--- 25/kernel/power/main.c~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/kernel/power/main.c Wed Sep 17 12:33:38 2003
@@ -8,32 +8,23 @@
*
*/
+#define DEBUG
+
#include
#include
-#include
#include
+#include
#include
#include
#include
-#include
#include "power.h"
-static DECLARE_MUTEX(pm_sem);
-
-static struct pm_ops * pm_ops = NULL;
-
-static u32 pm_disk_mode = PM_DISK_SHUTDOWN;
-
-#ifdef CONFIG_SOFTWARE_SUSPEND
-static int have_swsusp = 1;
-#else
-static int have_swsusp = 0;
-#endif
-
-extern long sys_sync(void);
+DECLARE_MUTEX(pm_sem);
+struct pm_ops * pm_ops = NULL;
+u32 pm_disk_mode = PM_DISK_SHUTDOWN;
/**
* pm_set_ops - Set the global power method table.
@@ -51,171 +42,6 @@ void pm_set_ops(struct pm_ops * ops)
/**
- * pm_suspend_standby - Enter 'standby' state.
- *
- * 'standby' is also known as 'Power-On Suspend'. Here, we power down
- * devices, disable interrupts, and enter the state.
- */
-
-static int pm_suspend_standby(void)
-{
- int error = 0;
- unsigned long flags;
-
- if (!pm_ops || !pm_ops->enter)
- return -EPERM;
-
- local_irq_save(flags);
- if ((error = device_power_down(PM_SUSPEND_STANDBY)))
- goto Done;
- error = pm_ops->enter(PM_SUSPEND_STANDBY);
- local_irq_restore(flags);
- device_power_up();
- Done:
- return error;
-}
-
-
-/**
- * pm_suspend_mem - Enter suspend-to-RAM state.
- *
- * Identical to pm_suspend_standby() - we power down devices, disable
- * interrupts, and enter the low-power state.
- */
-
-static int pm_suspend_mem(void)
-{
- int error = 0;
- unsigned long flags;
-
- if (!pm_ops || !pm_ops->enter)
- return -EPERM;
-
- local_irq_save(flags);
- if ((error = device_power_down(PM_SUSPEND_STANDBY)))
- goto Done;
- error = pm_ops->enter(PM_SUSPEND_STANDBY);
- local_irq_restore(flags);
- device_power_up();
- Done:
- return error;
-}
-
-
-/**
- * power_down - Shut machine down for hibernate.
- * @mode: Suspend-to-disk mode
- *
- * Use the platform driver, if configured so, and return gracefully if it
- * fails.
- * Otherwise, try to power off and reboot. If they fail, halt the machine,
- * there ain't no turning back.
- */
-
-static int power_down(u32 mode)
-{
- unsigned long flags;
- int error = 0;
-
- local_irq_save(flags);
- device_power_down(PM_SUSPEND_DISK);
- switch(mode) {
- case PM_DISK_PLATFORM:
- error = pm_ops->enter(PM_SUSPEND_DISK);
- if (error) {
- device_power_up();
- local_irq_restore(flags);
- return error;
- }
- case PM_DISK_SHUTDOWN:
- machine_power_off();
- break;
- case PM_DISK_REBOOT:
- machine_restart(NULL);
- break;
- }
- machine_halt();
- return 0;
-}
-
-
-static int in_suspend __nosavedata = 0;
-
-
-/**
- * free_some_memory - Try to free as much memory as possible
- *
- * ... but do not OOM-kill anyone
- *
- * Notice: all userland should be stopped at this point, or
- * livelock is possible.
- */
-
-static void free_some_memory(void)
-{
- printk("Freeing memory: ");
- while (shrink_all_memory(10000))
- printk(".");
- printk("|\n");
- blk_run_queues();
-}
-
-
-/**
- * pm_suspend_disk - The granpappy of power management.
- *
- * If we're going through the firmware, then get it over with quickly.
- *
- * If not, then call swsusp to do it's thing, then figure out how
- * to power down the system.
- */
-
-static int pm_suspend_disk(void)
-{
- int error;
-
- pr_debug("PM: Attempting to suspend to disk.\n");
- if (pm_disk_mode == PM_DISK_FIRMWARE)
- return pm_ops->enter(PM_SUSPEND_DISK);
-
- if (!have_swsusp)
- return -EPERM;
-
- pr_debug("PM: snapshotting memory.\n");
- in_suspend = 1;
- if ((error = swsusp_save()))
- goto Done;
-
- if (in_suspend) {
- pr_debug("PM: writing image.\n");
- error = swsusp_write();
- if (!error)
- error = power_down(pm_disk_mode);
- pr_debug("PM: Power down failed.\n");
- } else
- pr_debug("PM: Image restored successfully.\n");
- swsusp_free();
- Done:
- return error;
-}
-
-
-
-#define decl_state(_name) \
- { .name = __stringify(_name), .fn = pm_suspend_##_name }
-
-struct pm_state {
- char * name;
- int (*fn)(void);
-} pm_states[] = {
- [PM_SUSPEND_STANDBY] = decl_state(standby),
- [PM_SUSPEND_MEM] = decl_state(mem),
- [PM_SUSPEND_DISK] = decl_state(disk),
- { NULL },
-};
-
-
-/**
* suspend_prepare - Do prep work before entering low-power state.
* @state: State we're entering.
*
@@ -228,36 +54,47 @@ static int suspend_prepare(u32 state)
{
int error = 0;
+ if (!pm_ops || !pm_ops->enter)
+ return -EPERM;
+
pm_prepare_console();
- sys_sync();
if (freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
- if (pm_ops && pm_ops->prepare) {
+ if (pm_ops->prepare) {
if ((error = pm_ops->prepare(state)))
goto Thaw;
}
- /* Free memory before shutting down devices. */
- if (state == PM_SUSPEND_DISK)
- free_some_memory();
-
if ((error = device_suspend(state)))
goto Finish;
-
return 0;
- Done:
- pm_restore_console();
- return error;
Finish:
- if (pm_ops && pm_ops->finish)
+ if (pm_ops->finish)
pm_ops->finish(state);
Thaw:
thaw_processes();
- goto Done;
+ pm_restore_console();
+ return error;
+}
+
+
+static int suspend_enter(u32 state)
+{
+ int error = 0;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if ((error = device_power_down(state)))
+ goto Done;
+ error = pm_ops->enter(state);
+ device_power_up();
+ Done:
+ local_irq_restore(flags);
+ return error;
}
@@ -279,6 +116,16 @@ static void suspend_finish(u32 state)
}
+
+
+char * pm_states[] = {
+ [PM_SUSPEND_STANDBY] = "standby",
+ [PM_SUSPEND_MEM] = "mem",
+ [PM_SUSPEND_DISK] = "disk",
+ NULL,
+};
+
+
/**
* enter_state - Do common work of entering low-power state.
* @state: pm_state structure for state we're entering.
@@ -293,7 +140,6 @@ static void suspend_finish(u32 state)
static int enter_state(u32 state)
{
int error;
- struct pm_state * s = &pm_states[state];
if (down_trylock(&pm_sem))
return -EBUSY;
@@ -304,12 +150,17 @@ static int enter_state(u32 state)
goto Unlock;
}
- pr_debug("PM: Preparing system for suspend.\n");
+ if (state == PM_SUSPEND_DISK) {
+ error = pm_suspend_disk();
+ goto Unlock;
+ }
+
+ pr_debug("PM: Preparing system for suspend\n");
if ((error = suspend_prepare(state)))
goto Unlock;
pr_debug("PM: Entering state.\n");
- error = s->fn();
+ error = suspend_enter(state);
pr_debug("PM: Finishing up.\n");
suspend_finish(state);
@@ -335,138 +186,10 @@ int pm_suspend(u32 state)
}
-/**
- * pm_resume - Resume from a saved image.
- *
- * Called as a late_initcall (so all devices are discovered and
- * initialized), we call swsusp to see if we have a saved image or not.
- * If so, we quiesce devices, the restore the saved image. We will
- * return above (in pm_suspend_disk() ) if everything goes well.
- * Otherwise, we fail gracefully and return to the normally
- * scheduled program.
- *
- */
-
-static int pm_resume(void)
-{
- int error;
-
- if (!have_swsusp)
- return 0;
-
- pr_debug("PM: Reading swsusp image.\n");
-
- if ((error = swsusp_read()))
- goto Done;
-
- pr_debug("PM: Preparing system for restore.\n");
-
- if ((error = suspend_prepare(PM_SUSPEND_DISK)))
- goto Free;
-
- pr_debug("PM: Restoring saved image.\n");
- swsusp_restore();
-
- pr_debug("PM: Restore failed, recovering.n");
- suspend_finish(PM_SUSPEND_DISK);
- Free:
- swsusp_free();
- Done:
- pr_debug("PM: Resume from disk failed.\n");
- return 0;
-}
-
-late_initcall(pm_resume);
-
decl_subsys(power,NULL,NULL);
-#define power_attr(_name) \
-static struct subsys_attribute _name##_attr = { \
- .attr = { \
- .name = __stringify(_name), \
- .mode = 0644, \
- }, \
- .show = _name##_show, \
- .store = _name##_store, \
-}
-
-
-static char * pm_disk_modes[] = {
- [PM_DISK_FIRMWARE] = "firmware",
- [PM_DISK_PLATFORM] = "platform",
- [PM_DISK_SHUTDOWN] = "shutdown",
- [PM_DISK_REBOOT] = "reboot",
-};
-
-/**
- * disk - Control suspend-to-disk mode
- *
- * Suspend-to-disk can be handled in several ways. The greatest
- * distinction is who writes memory to disk - the firmware or the OS.
- * If the firmware does it, we assume that it also handles suspending
- * the system.
- * If the OS does it, then we have three options for putting the system
- * to sleep - using the platform driver (e.g. ACPI or other PM registers),
- * powering off the system or rebooting the system (for testing).
- *
- * The system will support either 'firmware' or 'platform', and that is
- * known a priori (and encoded in pm_ops). But, the user may choose
- * 'shutdown' or 'reboot' as alternatives.
- *
- * show() will display what the mode is currently set to.
- * store() will accept one of
- *
- * 'firmware'
- * 'platform'
- * 'shutdown'
- * 'reboot'
- *
- * It will only change to 'firmware' or 'platform' if the system
- * supports it (as determined from pm_ops->pm_disk_mode).
- */
-
-static ssize_t disk_show(struct subsystem * subsys, char * buf)
-{
- return sprintf(buf,"%s\n",pm_disk_modes[pm_disk_mode]);
-}
-
-
-static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n)
-{
- int error = 0;
- int i;
- u32 mode = 0;
-
- down(&pm_sem);
- for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) {
- if (!strcmp(buf,pm_disk_modes[i])) {
- mode = i;
- break;
- }
- }
- if (mode) {
- if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT)
- pm_disk_mode = mode;
- else {
- if (pm_ops && pm_ops->enter &&
- (mode == pm_ops->pm_disk_mode))
- pm_disk_mode = mode;
- else
- error = -EINVAL;
- }
- } else
- error = -EINVAL;
-
- pr_debug("PM: suspend-to-disk mode set to '%s'\n",
- pm_disk_modes[mode]);
- up(&pm_sem);
- return error ? error : n;
-}
-
-power_attr(disk);
-
/**
* state - control system power state.
*
@@ -480,27 +203,28 @@ power_attr(disk);
static ssize_t state_show(struct subsystem * subsys, char * buf)
{
- struct pm_state * state;
+ int i;
char * s = buf;
- for (state = &pm_states[0]; state->name; state++)
- s += sprintf(s,"%s ",state->name);
+ for (i = 0; i < PM_SUSPEND_MAX; i++) {
+ if (pm_states[i])
+ s += sprintf(s,"%s ",pm_states[i]);
+ }
s += sprintf(s,"\n");
return (s - buf);
}
static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n)
{
- u32 state;
- struct pm_state * s;
+ u32 state = PM_SUSPEND_STANDBY;
+ char ** s;
int error;
- for (state = 0; state < PM_SUSPEND_MAX; state++) {
- s = &pm_states[state];
- if (s->name && !strcmp(buf,s->name))
+ for (s = &pm_states[state]; *s; s++, state++) {
+ if (!strcmp(buf,*s))
break;
}
- if (s)
+ if (*s)
error = enter_state(state);
else
error = -EINVAL;
@@ -511,7 +235,6 @@ power_attr(state);
static struct attribute * g[] = {
&state_attr.attr,
- &disk_attr.attr,
NULL,
};
@@ -520,7 +243,7 @@ static struct attribute_group attr_group
};
-static int pm_init(void)
+static int __init pm_init(void)
{
int error = subsystem_register(&power_subsys);
if (!error)
diff -puN kernel/power/Makefile~test5-pm2 kernel/power/Makefile
--- 25/kernel/power/Makefile~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/kernel/power/Makefile Wed Sep 17 12:33:38 2003
@@ -1,4 +1,5 @@
obj-y := main.o process.o console.o pm.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
+obj-$(CONFIG_PM_DISK) += disk.o pmdisk.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
diff -puN /dev/null kernel/power/pmdisk.c
--- /dev/null Thu Apr 11 07:25:15 2002
+++ 25-akpm/kernel/power/pmdisk.c Wed Sep 17 12:33:38 2003
@@ -0,0 +1,942 @@
+/*
+ * kernel/power/pmdisk.c - Suspend-to-disk implmentation
+ *
+ * This STD implementation is initially derived from swsusp (suspend-to-swap).
+ * The original copyright on that was:
+ *
+ * Copyright (C) 1998-2001 Gabor Kuti
+ * Copyright (C) 1998,2001,2002 Pavel Machek
+ *
+ * The additional parts are:
+ *
+ * Copyright (C) 2003 Patrick Mochel
+ * Copyright (C) 2003 Open Source Development Lab
+ *
+ * This file is released under the GPLv2.
+ *
+ * For more information, please see the text files in Documentation/power/
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "power.h"
+
+
+extern int pmdisk_arch_suspend(int resume);
+
+#define __ADDRESS(x) ((unsigned long) phys_to_virt(x))
+#define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT)
+#define ADDRESS2(x) __ADDRESS(__pa(x)) /* Needed for x86-64 where some pages are in memory twice */
+
+/* References to section boundaries */
+extern char __nosave_begin, __nosave_end;
+
+extern int is_head_of_free_region(struct page *);
+
+/* Variables to be preserved over suspend */
+static int pagedir_order_check;
+static int nr_copy_pages_check;
+
+/* For resume= kernel option */
+static char resume_file[256] = CONFIG_PM_DISK_PARTITION;
+
+static dev_t resume_device;
+/* Local variables that should not be affected by save */
+unsigned int pmdisk_pages __nosavedata = 0;
+
+/* Suspend pagedir is allocated before final copy, therefore it
+ must be freed after resume
+
+ Warning: this is evil. There are actually two pagedirs at time of
+ resume. One is "pagedir_save", which is empty frame allocated at
+ time of suspend, that must be freed. Second is "pagedir_nosave",
+ allocated at time of resume, that travels through memory not to
+ collide with anything.
+ */
+suspend_pagedir_t *pm_pagedir_nosave __nosavedata = NULL;
+static suspend_pagedir_t *pagedir_save;
+static int pagedir_order __nosavedata = 0;
+
+struct link {
+ char dummy[PAGE_SIZE - sizeof(swp_entry_t)];
+ swp_entry_t next;
+};
+
+union diskpage {
+ union swap_header swh;
+ struct link link;
+ struct suspend_header sh;
+};
+
+/*
+ * XXX: We try to keep some more pages free so that I/O operations succeed
+ * without paging. Might this be more?
+ */
+#define PAGES_FOR_IO 512
+
+static const char name_suspend[] = "Suspend Machine: ";
+static const char name_resume[] = "Resume Machine: ";
+
+/*
+ * Debug
+ */
+#define DEBUG_DEFAULT
+#undef DEBUG_PROCESS
+#undef DEBUG_SLOW
+#define TEST_SWSUSP 0 /* Set to 1 to reboot instead of halt machine after suspension */
+
+#ifdef DEBUG_DEFAULT
+# define PRINTK(f, a...) printk(f, ## a)
+#else
+# define PRINTK(f, a...)
+#endif
+
+#ifdef DEBUG_SLOW
+#define MDELAY(a) mdelay(a)
+#else
+#define MDELAY(a)
+#endif
+
+/*
+ * Saving part...
+ */
+
+static __inline__ int fill_suspend_header(struct suspend_header *sh)
+{
+ memset((char *)sh, 0, sizeof(*sh));
+
+ sh->version_code = LINUX_VERSION_CODE;
+ sh->num_physpages = num_physpages;
+ strncpy(sh->machine, system_utsname.machine, 8);
+ strncpy(sh->version, system_utsname.version, 20);
+ /* FIXME: Is this bogus? --RR */
+ sh->num_cpus = num_online_cpus();
+ sh->page_size = PAGE_SIZE;
+ sh->suspend_pagedir = pm_pagedir_nosave;
+ BUG_ON (pagedir_save != pm_pagedir_nosave);
+ sh->num_pbes = pmdisk_pages;
+ /* TODO: needed? mounted fs' last mounted date comparison
+ * [so they haven't been mounted since last suspend.
+ * Maybe it isn't.] [we'd need to do this for _all_ fs-es]
+ */
+ return 0;
+}
+
+/* We memorize in swapfile_used what swap devices are used for suspension */
+#define SWAPFILE_UNUSED 0
+#define SWAPFILE_SUSPEND 1 /* This is the suspending device */
+#define SWAPFILE_IGNORED 2 /* Those are other swap devices ignored for suspension */
+
+static unsigned short swapfile_used[MAX_SWAPFILES];
+static unsigned short root_swap;
+#define MARK_SWAP_SUSPEND 0
+#define MARK_SWAP_RESUME 2
+
+static void mark_swapfiles(swp_entry_t prev, int mode)
+{
+ swp_entry_t entry;
+ union diskpage *cur;
+ struct page *page;
+
+ if (root_swap == 0xFFFF) /* ignored */
+ return;
+
+ page = alloc_page(GFP_ATOMIC);
+ if (!page)
+ panic("Out of memory in mark_swapfiles");
+ cur = page_address(page);
+ /* XXX: this is dirty hack to get first page of swap file */
+ entry = swp_entry(root_swap, 0);
+ rw_swap_page_sync(READ, entry, page);
+
+ if (mode == MARK_SWAP_RESUME) {
+ if (!memcmp("S1",cur->swh.magic.magic,2))
+ memcpy(cur->swh.magic.magic,"SWAP-SPACE",10);
+ else if (!memcmp("S2",cur->swh.magic.magic,2))
+ memcpy(cur->swh.magic.magic,"SWAPSPACE2",10);
+ else printk("%sUnable to find suspended-data signature (%.10s - misspelled?\n",
+ name_resume, cur->swh.magic.magic);
+ } else {
+ if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)))
+ memcpy(cur->swh.magic.magic,"S1SUSP....",10);
+ else if ((!memcmp("SWAPSPACE2",cur->swh.magic.magic,10)))
+ memcpy(cur->swh.magic.magic,"S2SUSP....",10);
+ else panic("\nSwapspace is not swapspace (%.10s)\n", cur->swh.magic.magic);
+ cur->link.next = prev; /* prev is the first/last swap page of the resume area */
+ /* link.next lies *no more* in last 4/8 bytes of magic */
+ }
+ rw_swap_page_sync(WRITE, entry, page);
+ __free_page(page);
+}
+
+static void read_swapfiles(void) /* This is called before saving image */
+{
+ int i, len;
+
+ len=strlen(resume_file);
+ root_swap = 0xFFFF;
+
+ swap_list_lock();
+ for(i=0; id_name.name, swap_info[i].swap_device, resume_device );
+#endif
+ swapfile_used[i] = SWAPFILE_IGNORED;
+ }
+ }
+ }
+ }
+ swap_list_unlock();
+}
+
+static void lock_swapdevices(void) /* This is called after saving image so modification
+ will be lost after resume... and that's what we want. */
+{
+ int i;
+
+ swap_list_lock();
+ for(i = 0; i< MAX_SWAPFILES; i++)
+ if(swapfile_used[i] == SWAPFILE_IGNORED) {
+ swap_info[i].flags ^= 0xFF; /* we make the device unusable. A new call to
+ lock_swapdevices can unlock the devices. */
+ }
+ swap_list_unlock();
+}
+
+static int write_suspend_image(void)
+{
+ int i;
+ swp_entry_t entry, prev = { 0 };
+ int nr_pgdir_pages = SUSPEND_PD_PAGES(pmdisk_pages);
+ union diskpage *cur, *buffer = (union diskpage *)get_zeroed_page(GFP_ATOMIC);
+ unsigned long address;
+ struct page *page;
+
+ printk( "Writing data to swap (%d pages): ", pmdisk_pages );
+ for (i=0; iaddress;
+ page = virt_to_page(address);
+ rw_swap_page_sync(WRITE, entry, page);
+ (pm_pagedir_nosave+i)->swap_address = entry;
+ }
+ printk( "|\n" );
+ printk( "Writing pagedir (%d pages): ", nr_pgdir_pages);
+ for (i=0; ilink.next = prev;
+ page = virt_to_page((unsigned long)cur);
+ rw_swap_page_sync(WRITE, entry, page);
+ prev = entry;
+ }
+ printk("H");
+ BUG_ON (sizeof(struct suspend_header) > PAGE_SIZE-sizeof(swp_entry_t));
+ BUG_ON (sizeof(union diskpage) != PAGE_SIZE);
+ if (!(entry = get_swap_page()).val)
+ panic( "\nNot enough swapspace when writing header" );
+ if (swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND)
+ panic("\nNot enough swapspace for header on suspend device" );
+
+ cur = (void *) buffer;
+ if (fill_suspend_header(&cur->sh))
+ panic("\nOut of memory while writing header");
+
+ cur->link.next = prev;
+
+ page = virt_to_page((unsigned long)cur);
+ rw_swap_page_sync(WRITE, entry, page);
+ prev = entry;
+
+ printk( "S" );
+ mark_swapfiles(prev, MARK_SWAP_SUSPEND);
+ printk( "|\n" );
+
+ MDELAY(1000);
+ free_page((unsigned long) buffer);
+ return 0;
+}
+
+/* if pagedir_p != NULL it also copies the counted pages */
+static int count_and_copy_data_pages(struct pbe *pagedir_p)
+{
+ int chunk_size;
+ int nr_copy_pages = 0;
+ int pfn;
+ struct page *page;
+
+ BUG_ON (max_pfn != num_physpages);
+
+ for (pfn = 0; pfn < max_pfn; pfn++) {
+ page = pfn_to_page(pfn);
+
+ if (!PageReserved(page)) {
+ if (PageNosave(page))
+ continue;
+
+ if ((chunk_size=is_head_of_free_region(page))!=0) {
+ pfn += chunk_size - 1;
+ continue;
+ }
+ } else if (PageReserved(page)) {
+ BUG_ON (PageNosave(page));
+
+ /*
+ * Just copy whole code segment. Hopefully it is not that big.
+ */
+ if ((ADDRESS(pfn) >= (unsigned long) ADDRESS2(&__nosave_begin)) &&
+ (ADDRESS(pfn) < (unsigned long) ADDRESS2(&__nosave_end))) {
+ PRINTK("[nosave %lx]", ADDRESS(pfn));
+ continue;
+ }
+ /* Hmm, perhaps copying all reserved pages is not too healthy as they may contain
+ critical bios data? */
+ } else BUG();
+
+ nr_copy_pages++;
+ if (pagedir_p) {
+ pagedir_p->orig_address = ADDRESS(pfn);
+ copy_page((void *) pagedir_p->address, (void *) pagedir_p->orig_address);
+ pagedir_p++;
+ }
+ }
+ return nr_copy_pages;
+}
+
+static void free_suspend_pagedir(unsigned long this_pagedir)
+{
+ struct page *page;
+ int pfn;
+ unsigned long this_pagedir_end = this_pagedir +
+ (PAGE_SIZE << pagedir_order);
+
+ for(pfn = 0; pfn < num_physpages; pfn++) {
+ page = pfn_to_page(pfn);
+ if (!TestClearPageNosave(page))
+ continue;
+
+ if (ADDRESS(pfn) >= this_pagedir && ADDRESS(pfn) < this_pagedir_end)
+ continue; /* old pagedir gets freed in one */
+
+ free_page(ADDRESS(pfn));
+ }
+ free_pages(this_pagedir, pagedir_order);
+}
+
+static suspend_pagedir_t *create_suspend_pagedir(int nr_copy_pages)
+{
+ int i;
+ suspend_pagedir_t *pagedir;
+ struct pbe *p;
+ struct page *page;
+
+ pagedir_order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages));
+
+ p = pagedir = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, pagedir_order);
+ if(!pagedir)
+ return NULL;
+
+ page = virt_to_page(pagedir);
+ for(i=0; i < 1<address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD);
+ if(!p->address) {
+ free_suspend_pagedir((unsigned long) pagedir);
+ return NULL;
+ }
+ SetPageNosave(virt_to_page(p->address));
+ p->orig_address = 0;
+ p++;
+ }
+ return pagedir;
+}
+
+
+int pmdisk_suspend(void)
+{
+ struct sysinfo i;
+ unsigned int nr_needed_pages = 0;
+
+ read_swapfiles();
+ drain_local_pages();
+
+ pm_pagedir_nosave = NULL;
+ printk( "/critical section: Counting pages to copy" );
+ pmdisk_pages = count_and_copy_data_pages(NULL);
+ nr_needed_pages = pmdisk_pages + PAGES_FOR_IO;
+
+ printk(" (pages needed: %d+%d=%d free: %d)\n",pmdisk_pages,PAGES_FOR_IO,nr_needed_pages,nr_free_pages());
+ if(nr_free_pages() < nr_needed_pages) {
+ printk(KERN_CRIT "%sCouldn't get enough free pages, on %d pages short\n",
+ name_suspend, nr_needed_pages-nr_free_pages());
+ root_swap = 0xFFFF;
+ return 1;
+ }
+ si_swapinfo(&i); /* FIXME: si_swapinfo(&i) returns all swap devices information.
+ We should only consider resume_device. */
+ if (i.freeswap < nr_needed_pages) {
+ printk(KERN_CRIT "%sThere's not enough swap space available, on %ld pages short\n",
+ name_suspend, nr_needed_pages-i.freeswap);
+ return 1;
+ }
+
+ PRINTK( "Alloc pagedir\n" );
+ pagedir_save = pm_pagedir_nosave = create_suspend_pagedir(pmdisk_pages);
+ if(!pm_pagedir_nosave) {
+ /* Shouldn't happen */
+ printk(KERN_CRIT "%sCouldn't allocate enough pages\n",name_suspend);
+ panic("Really should not happen");
+ return 1;
+ }
+ nr_copy_pages_check = pmdisk_pages;
+ pagedir_order_check = pagedir_order;
+
+ drain_local_pages(); /* During allocating of suspend pagedir, new cold pages may appear. Kill them */
+ if (pmdisk_pages != count_and_copy_data_pages(pm_pagedir_nosave)) /* copy */
+ BUG();
+
+ /*
+ * End of critical section. From now on, we can write to memory,
+ * but we should not touch disk. This specially means we must _not_
+ * touch swap space! Except we must write out our image of course.
+ */
+
+ printk( "critical section/: done (%d pages copied)\n", pmdisk_pages );
+ return 0;
+}
+
+
+/**
+ * suspend_save_image - Prepare and write saved image to swap.
+ *
+ * IRQs are re-enabled here so we can resume devices and safely write
+ * to the swap devices. We disable them again before we leave.
+ *
+ * The second lock_swapdevices() will unlock ignored swap devices since
+ * writing is finished.
+ * It is important _NOT_ to umount filesystems at this point. We want
+ * them synced (in case something goes wrong) but we DO not want to mark
+ * filesystem clean: it is not. (And it does not matter, if we resume
+ * correctly, we'll mark system clean, anyway.)
+ */
+
+static int suspend_save_image(void)
+{
+ int error;
+ device_resume();
+ lock_swapdevices();
+ error = write_suspend_image();
+ lock_swapdevices();
+ return error;
+}
+
+/*
+ * Magic happens here
+ */
+
+int pmdisk_resume(void)
+{
+ BUG_ON (nr_copy_pages_check != pmdisk_pages);
+ BUG_ON (pagedir_order_check != pagedir_order);
+
+ /* Even mappings of "global" things (vmalloc) need to be fixed */
+ __flush_tlb_global();
+ return 0;
+}
+
+/* pmdisk_arch_suspend() is implemented in arch/?/power/pmdisk.S,
+ and basically does:
+
+ if (!resume) {
+ save_processor_state();
+ SAVE_REGISTERS
+ return pmdisk_suspend();
+ }
+ GO_TO_SWAPPER_PAGE_TABLES
+ COPY_PAGES_BACK
+ RESTORE_REGISTERS
+ restore_processor_state();
+ return pmdisk_resume();
+
+ */
+
+
+/* More restore stuff */
+
+/* FIXME: Why not memcpy(to, from, 1<orig_address >= addr &&
+ (pagedir+i)->orig_address < addre)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * We check here that pagedir & pages it points to won't collide with pages
+ * where we're going to restore from the loaded pages later
+ */
+static int __init check_pagedir(void)
+{
+ int i;
+
+ for(i=0; i < pmdisk_pages; i++) {
+ unsigned long addr;
+
+ do {
+ addr = get_zeroed_page(GFP_ATOMIC);
+ if(!addr)
+ return -ENOMEM;
+ } while (does_collide(addr));
+
+ (pm_pagedir_nosave+i)->address = addr;
+ }
+ return 0;
+}
+
+static int __init relocate_pagedir(void)
+{
+ /*
+ * We have to avoid recursion (not to overflow kernel stack),
+ * and that's why code looks pretty cryptic
+ */
+ suspend_pagedir_t *new_pagedir, *old_pagedir = pm_pagedir_nosave;
+ void **eaten_memory = NULL;
+ void **c = eaten_memory, *m, *f;
+
+ printk("Relocating pagedir");
+
+ if(!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) {
+ printk("not necessary\n");
+ return 0;
+ }
+
+ while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
+ memset(m, 0, PAGE_SIZE);
+ if (!does_collide_order(old_pagedir, (unsigned long)m, pagedir_order))
+ break;
+ eaten_memory = m;
+ printk( "." );
+ *eaten_memory = c;
+ c = eaten_memory;
+ }
+
+ if (!m)
+ return -ENOMEM;
+
+ pm_pagedir_nosave = new_pagedir = m;
+ copy_pagedir(new_pagedir, old_pagedir);
+
+ c = eaten_memory;
+ while(c) {
+ printk(":");
+ f = *c;
+ c = *c;
+ if (f)
+ free_pages((unsigned long)f, pagedir_order);
+ }
+ printk("|\n");
+ return 0;
+}
+
+/*
+ * Sanity check if this image makes sense with this kernel/swap context
+ * I really don't think that it's foolproof but more than nothing..
+ */
+
+static int __init sanity_check_failed(char *reason)
+{
+ printk(KERN_ERR "%s%s\n",name_resume,reason);
+ return -EPERM;
+}
+
+static int __init sanity_check(struct suspend_header *sh)
+{
+ if(sh->version_code != LINUX_VERSION_CODE)
+ return sanity_check_failed("Incorrect kernel version");
+ if(sh->num_physpages != num_physpages)
+ return sanity_check_failed("Incorrect memory size");
+ if(strncmp(sh->machine, system_utsname.machine, 8))
+ return sanity_check_failed("Incorrect machine type");
+ if(strncmp(sh->version, system_utsname.version, 20))
+ return sanity_check_failed("Incorrect version");
+ if(sh->num_cpus != num_online_cpus())
+ return sanity_check_failed("Incorrect number of cpus");
+ if(sh->page_size != PAGE_SIZE)
+ return sanity_check_failed("Incorrect PAGE_SIZE");
+ return 0;
+}
+
+static struct block_device * resume_bdev;
+
+
+/**
+ * Using bio to read from swap.
+ * This code requires a bit more work than just using buffer heads
+ * but, it is the recommended way for 2.5/2.6.
+ * The following are to signal the beginning and end of I/O. Bios
+ * finish asynchronously, while we want them to happen synchronously.
+ * A simple atomic_t, and a wait loop take care of this problem.
+ */
+
+static atomic_t io_done = ATOMIC_INIT(0);
+
+static void start_io(void)
+{
+ atomic_set(&io_done,1);
+}
+
+static int end_io(struct bio * bio, unsigned int num, int err)
+{
+ atomic_set(&io_done,0);
+ return 0;
+}
+
+static void wait_io(void)
+{
+ blk_run_queues();
+ while(atomic_read(&io_done))
+ io_schedule();
+}
+
+
+/**
+ * submit - submit BIO request.
+ * @rw: READ or WRITE.
+ * @off physical offset of page.
+ * @page: page we're reading or writing.
+ *
+ * Straight from the textbook - allocate and initialize the bio.
+ * If we're writing, make sure the page is marked as dirty.
+ * Then submit it and wait.
+ */
+
+static int submit(int rw, pgoff_t page_off, void * page)
+{
+ int error = 0;
+ struct bio * bio;
+
+ bio = bio_alloc(GFP_ATOMIC,1);
+ if (!bio)
+ return -ENOMEM;
+ bio->bi_sector = page_off * (PAGE_SIZE >> 9);
+ bio_get(bio);
+ bio->bi_bdev = resume_bdev;
+ bio->bi_end_io = end_io;
+
+ if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) {
+ printk("ERROR: adding page to bio at %ld\n",page_off);
+ error = -EFAULT;
+ goto Done;
+ }
+
+ if (rw == WRITE)
+ bio_set_pages_dirty(bio);
+ start_io();
+ submit_bio(rw,bio);
+ wait_io();
+ Done:
+ bio_put(bio);
+ return error;
+}
+
+static int
+read_page(pgoff_t page_off, void * page)
+{
+ return submit(READ,page_off,page);
+}
+
+static int
+write_page(pgoff_t page_off, void * page)
+{
+ return submit(WRITE,page_off,page);
+}
+
+
+extern dev_t __init name_to_dev_t(const char *line);
+
+
+#define next_entry(diskpage) diskpage->link.next
+
+static int __init read_suspend_image(void)
+{
+ swp_entry_t next;
+ int i, nr_pgdir_pages;
+ union diskpage *cur;
+ int error = 0;
+
+ cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC);
+ if (!cur)
+ return -ENOMEM;
+
+ if ((error = read_page(0, cur)))
+ goto Done;
+
+ /*
+ * We have to read next position before we overwrite it
+ */
+ next = next_entry(cur);
+
+ if (!memcmp("S1",cur->swh.magic.magic,2))
+ memcpy(cur->swh.magic.magic,"SWAP-SPACE",10);
+ else if (!memcmp("S2",cur->swh.magic.magic,2))
+ memcpy(cur->swh.magic.magic,"SWAPSPACE2",10);
+ else if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) ||
+ (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) {
+ printk(KERN_ERR "pmdisk: Partition is normal swap space\n");
+ error = -EINVAL;
+ goto Done;
+ } else {
+ printk(KERN_ERR "pmdisk: Invalid partition type.\n");
+ error = -EINVAL;
+ goto Done;
+ }
+
+ /*
+ * Reset swap signature now.
+ */
+ if ((error = write_page(0,cur)))
+ goto Done;
+
+ printk( "%sSignature found, resuming\n", name_resume );
+ MDELAY(1000);
+
+ if ((error = read_page(swp_offset(next), cur)))
+ goto Done;
+ /* Is this same machine? */
+ if ((error = sanity_check(&cur->sh)))
+ goto Done;
+ next = next_entry(cur);
+
+ pagedir_save = cur->sh.suspend_pagedir;
+ pmdisk_pages = cur->sh.num_pbes;
+ nr_pgdir_pages = SUSPEND_PD_PAGES(pmdisk_pages);
+ pagedir_order = get_bitmask_order(nr_pgdir_pages);
+
+ pm_pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order);
+ if (!pm_pagedir_nosave) {
+ error = -ENOMEM;
+ goto Done;
+ }
+
+ PRINTK( "%sReading pagedir, ", name_resume );
+
+ /* We get pages in reverse order of saving! */
+ for (i=nr_pgdir_pages-1; i>=0; i--) {
+ BUG_ON (!next.val);
+ cur = (union diskpage *)((char *) pm_pagedir_nosave)+i;
+ error = read_page(swp_offset(next), cur);
+ if (error)
+ goto FreePagedir;
+ next = next_entry(cur);
+ }
+ BUG_ON (next.val);
+
+ if ((error = relocate_pagedir()))
+ goto FreePagedir;
+ if ((error = check_pagedir()))
+ goto FreePagedir;
+
+ printk( "Reading image data (%d pages): ", pmdisk_pages );
+ for(i=0; i < pmdisk_pages; i++) {
+ swp_entry_t swap_address = (pm_pagedir_nosave+i)->swap_address;
+ if (!(i%100))
+ printk( "." );
+ /* You do not need to check for overlaps...
+ ... check_pagedir already did this work */
+ error = read_page(swp_offset(swap_address),
+ (char *)((pm_pagedir_nosave+i)->address));
+ if (error)
+ goto FreePagedir;
+ }
+ printk( "|\n" );
+ Done:
+ free_page((unsigned long)cur);
+ return error;
+ FreePagedir:
+ free_pages((unsigned long)pm_pagedir_nosave,pagedir_order);
+ goto Done;
+}
+
+/**
+ * pmdisk_save - Snapshot memory
+ */
+
+int pmdisk_save(void)
+{
+ int error;
+
+#if defined (CONFIG_HIGHMEM) || defined (COFNIG_DISCONTIGMEM)
+ printk("pmdisk is not supported with high- or discontig-mem.\n");
+ return -EPERM;
+#endif
+ if ((error = arch_prepare_suspend()))
+ return error;
+ local_irq_disable();
+ error = pmdisk_arch_suspend(0);
+ local_irq_enable();
+ return error;
+}
+
+
+/**
+ * pmdisk_write - Write saved memory image to swap.
+ *
+ * pmdisk_arch_suspend(0) returns after system is resumed.
+ *
+ * pmdisk_arch_suspend() copies all "used" memory to "free" memory,
+ * then unsuspends all device drivers, and writes memory to disk
+ * using normal kernel mechanism.
+ */
+
+int pmdisk_write(void)
+{
+ return suspend_save_image();
+}
+
+
+/**
+ * pmdisk_read - Read saved image from swap.
+ */
+
+int __init pmdisk_read(void)
+{
+ int error;
+ char b[BDEVNAME_SIZE];
+
+ if (!strlen(resume_file))
+ return -ENOENT;
+
+ resume_device = name_to_dev_t(resume_file);
+ printk("pmdisk: Resume From Partition: %s, Device: %s\n",
+ resume_file, __bdevname(resume_device, b));
+
+ resume_bdev = open_by_devnum(resume_device, FMODE_READ, BDEV_RAW);
+ if (!IS_ERR(resume_bdev)) {
+ set_blocksize(resume_bdev, PAGE_SIZE);
+ error = read_suspend_image();
+ blkdev_put(resume_bdev, BDEV_RAW);
+ } else
+ error = PTR_ERR(resume_bdev);
+
+ if (!error)
+ PRINTK("Reading resume file was successful\n");
+ else
+ printk( "%sError %d resuming\n", name_resume, error );
+ MDELAY(1000);
+ return error;
+}
+
+
+/**
+ * pmdisk_restore - Replace running kernel with saved image.
+ */
+
+int __init pmdisk_restore(void)
+{
+ int error;
+ local_irq_disable();
+ error = pmdisk_arch_suspend(1);
+ local_irq_enable();
+ return error;
+}
+
+
+/**
+ * pmdisk_free - Free memory allocated to hold snapshot.
+ */
+
+int pmdisk_free(void)
+{
+ PRINTK( "Freeing prev allocated pagedir\n" );
+ free_suspend_pagedir((unsigned long) pagedir_save);
+ return 0;
+}
+
+static int __init pmdisk_setup(char *str)
+{
+ if (strlen(str)) {
+ if (!strcmp(str,"off"))
+ resume_file[0] = '\0';
+ else
+ strncpy(resume_file, str, 255);
+ } else
+ resume_file[0] = '\0';
+ return 1;
+}
+
+__setup("pmdisk=", pmdisk_setup);
+
diff -puN kernel/power/power.h~test5-pm2 kernel/power/power.h
--- 25/kernel/power/power.h~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/kernel/power/power.h Wed Sep 17 12:33:38 2003
@@ -9,35 +9,28 @@
#endif
-#ifdef CONFIG_SOFTWARE_SUSPEND
-extern int swsusp_save(void);
-extern int swsusp_write(void);
-extern int swsusp_read(void);
-extern int swsusp_restore(void);
-extern int swsusp_free(void);
+#ifdef CONFIG_PM_DISK
+extern int pm_suspend_disk(void);
+
#else
-static inline int swsusp_save(void)
-{
- return 0;
-}
-static inline int swsusp_write(void)
+static inline int pm_suspend_disk(void)
{
- return 0;
-}
-static inline int swsusp_read(void)
-{
- return 0;
-}
-static inline int swsusp_restore(void)
-{
- return 0;
-}
-static inline int swsusp_free(void)
-{
- return 0;
+ return -EPERM;
}
#endif
+extern struct semaphore pm_sem;
+#define power_attr(_name) \
+static struct subsys_attribute _name##_attr = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = 0644, \
+ }, \
+ .show = _name##_show, \
+ .store = _name##_store, \
+}
+
+extern struct subsystem power_subsys;
extern int freeze_processes(void);
extern void thaw_processes(void);
diff -puN kernel/power/swsusp.c~test5-pm2 kernel/power/swsusp.c
--- 25/kernel/power/swsusp.c~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/kernel/power/swsusp.c Wed Sep 17 12:33:38 2003
@@ -65,7 +65,15 @@
#include "power.h"
-unsigned char software_suspend_enabled = 1;
+extern long sys_sync(void);
+
+unsigned char software_suspend_enabled = 0;
+
+extern void do_magic(int resume);
+
+#define NORESUME 1
+#define RESUME_SPECIFIED 2
+
#define __ADDRESS(x) ((unsigned long) phys_to_virt(x))
#define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT)
@@ -83,7 +91,8 @@ spinlock_t suspend_pagedir_lock __nosave
static int pagedir_order_check;
static int nr_copy_pages_check;
-static char resume_file[256]; /* For resume= kernel option */
+static int resume_status;
+static char resume_file[256] = ""; /* For resume= kernel option */
static dev_t resume_device;
/* Local variables that should not be affected by save */
unsigned int nr_copy_pages __nosavedata = 0;
@@ -347,10 +356,15 @@ static int count_and_copy_data_pages(str
int pfn;
struct page *page;
+#ifdef CONFIG_DISCONTIGMEM
+ panic("Discontingmem not supported");
+#else
BUG_ON (max_pfn != num_physpages);
-
+#endif
for (pfn = 0; pfn < max_pfn; pfn++) {
page = pfn_to_page(pfn);
+ if (PageHighMem(page))
+ panic("Swsusp not supported on highmem boxes. Send 1GB of RAM to and try again ;-).");
if (!PageReserved(page)) {
if (PageNosave(page))
@@ -435,6 +449,56 @@ static suspend_pagedir_t *create_suspend
return pagedir;
}
+static int prepare_suspend_processes(void)
+{
+ sys_sync(); /* Syncing needs pdflushd, so do it before stopping processes */
+ if (freeze_processes()) {
+ printk( KERN_ERR "Suspend failed: Not all processes stopped!\n" );
+ thaw_processes();
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Try to free as much memory as possible, but do not OOM-kill anyone
+ *
+ * Notice: all userland should be stopped at this point, or livelock is possible.
+ */
+static void free_some_memory(void)
+{
+ printk("Freeing memory: ");
+ while (shrink_all_memory(10000))
+ printk(".");
+ printk("|\n");
+}
+
+/* Make disk drivers accept operations, again */
+static void drivers_unsuspend(void)
+{
+ device_resume();
+}
+
+/* Called from process context */
+static int drivers_suspend(void)
+{
+ return device_suspend(4);
+}
+
+#define RESUME_PHASE1 1 /* Called from interrupts disabled */
+#define RESUME_PHASE2 2 /* Called with interrupts enabled */
+#define RESUME_ALL_PHASES (RESUME_PHASE1 | RESUME_PHASE2)
+static void drivers_resume(int flags)
+{
+ if (flags & RESUME_PHASE1) {
+ device_resume();
+ }
+ if (flags & RESUME_PHASE2) {
+#ifdef SUSPEND_CONSOLE
+ update_screen(fg_console); /* Hmm, is this the problem? */
+#endif
+ }
+}
static int suspend_prepare_image(void)
{
@@ -488,14 +552,12 @@ static int suspend_prepare_image(void)
return 0;
}
-static int suspend_save_image(void)
+static void suspend_save_image(void)
{
- int error;
-
- device_resume();
+ drivers_unsuspend();
lock_swapdevices();
- error = write_suspend_image();
+ write_suspend_image();
lock_swapdevices(); /* This will unlock ignored swap devices since writing is finished */
/* It is important _NOT_ to umount filesystems at this point. We want
@@ -503,7 +565,29 @@ static int suspend_save_image(void)
* filesystem clean: it is not. (And it does not matter, if we resume
* correctly, we'll mark system clean, anyway.)
*/
- return error;
+}
+
+static void suspend_power_down(void)
+{
+ extern int C_A_D;
+ C_A_D = 0;
+ printk(KERN_EMERG "%s%s Trying to power down.\n", name_suspend, TEST_SWSUSP ? "Disable TEST_SWSUSP. NOT ": "");
+#ifdef CONFIG_VT
+ PRINTK(KERN_EMERG "shift_state: %04x\n", shift_state);
+ mdelay(1000);
+ if (TEST_SWSUSP ^ (!!(shift_state & (1 << KG_CTRL))))
+ machine_restart(NULL);
+ else
+#endif
+ {
+ device_shutdown();
+ machine_power_off();
+ }
+
+ printk(KERN_EMERG "%sProbably not capable for powerdown. System halted.\n", name_suspend);
+ machine_halt();
+ while (1);
+ /* NOTREACHED */
}
/*
@@ -515,21 +599,32 @@ void do_magic_resume_1(void)
barrier();
mb();
spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */
+
PRINTK( "Waiting for DMAs to settle down...\n");
- /* We do not want some readahead with DMA to corrupt our memory, right?
- Do it with disabled interrupts for best effect. That way, if some
- driver scheduled DMA, we have good chance for DMA to finish ;-). */
- mdelay(1000);
+ mdelay(1000); /* We do not want some readahead with DMA to corrupt our memory, right?
+ Do it with disabled interrupts for best effect. That way, if some
+ driver scheduled DMA, we have good chance for DMA to finish ;-). */
}
void do_magic_resume_2(void)
{
BUG_ON (nr_copy_pages_check != nr_copy_pages);
BUG_ON (pagedir_order_check != pagedir_order);
-
- /* Even mappings of "global" things (vmalloc) need to be fixed */
- __flush_tlb_global();
+
+ __flush_tlb_global(); /* Even mappings of "global" things (vmalloc) need to be fixed */
+
+ PRINTK( "Freeing prev allocated pagedir\n" );
+ free_suspend_pagedir((unsigned long) pagedir_save);
spin_unlock_irq(&suspend_pagedir_lock);
+ drivers_resume(RESUME_ALL_PHASES);
+
+ PRINTK( "Fixing swap signatures... " );
+ mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
+ PRINTK( "ok\n" );
+
+#ifdef SUSPEND_CONSOLE
+ update_screen(fg_console); /* Hmm, is this the problem? */
+#endif
}
/* do_magic() is implemented in arch/?/kernel/suspend_asm.S, and basically does:
@@ -554,28 +649,91 @@ void do_magic_suspend_1(void)
{
mb();
barrier();
+ BUG_ON(in_atomic());
spin_lock_irq(&suspend_pagedir_lock);
}
-int do_magic_suspend_2(void)
+void do_magic_suspend_2(void)
{
int is_problem;
read_swapfiles();
is_problem = suspend_prepare_image();
spin_unlock_irq(&suspend_pagedir_lock);
- if (!is_problem)
- return suspend_save_image();
+ if (!is_problem) {
+ kernel_fpu_end(); /* save_processor_state() does kernel_fpu_begin, and we need to revert it in order to pass in_atomic() checks */
+ BUG_ON(in_atomic());
+ suspend_save_image();
+ suspend_power_down(); /* FIXME: if suspend_power_down is commented out, console is lost after few suspends ?! */
+ }
+
printk(KERN_EMERG "%sSuspend failed, trying to recover...\n", name_suspend);
+ MDELAY(1000); /* So user can wait and report us messages if armageddon comes :-) */
+
barrier();
mb();
+ spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */
mdelay(1000);
- return -EFAULT;
+
+ free_pages((unsigned long) pagedir_nosave, pagedir_order);
+ spin_unlock_irq(&suspend_pagedir_lock);
+ mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
+}
+
+static void do_software_suspend(void)
+{
+ arch_prepare_suspend();
+ if (pm_prepare_console())
+ printk( "%sCan't allocate a console... proceeding\n", name_suspend);
+ if (!prepare_suspend_processes()) {
+
+ /* At this point, all user processes and "dangerous"
+ kernel threads are stopped. Free some memory, as we
+ need half of memory free. */
+
+ free_some_memory();
+
+ /* No need to invalidate any vfsmnt list --
+ * they will be valid after resume, anyway.
+ */
+ blk_run_queues();
+
+ /* Save state of all device drivers, and stop them. */
+ if(drivers_suspend()==0)
+ /* If stopping device drivers worked, we proceed basically into
+ * suspend_save_image.
+ *
+ * do_magic(0) returns after system is resumed.
+ *
+ * do_magic() copies all "used" memory to "free" memory, then
+ * unsuspends all device drivers, and writes memory to disk
+ * using normal kernel mechanism.
+ */
+ do_magic(0);
+ thaw_processes();
+ }
+ software_suspend_enabled = 1;
+ MDELAY(1000);
+ pm_restore_console();
+}
+
+/*
+ * This is main interface to the outside world. It needs to be
+ * called from process context.
+ */
+void software_suspend(void)
+{
+ if(!software_suspend_enabled)
+ return;
+
+ software_suspend_enabled = 0;
+ might_sleep();
+ do_software_suspend();
}
/* More restore stuff */
/* FIXME: Why not memcpy(to, from, 1<version_code != LINUX_VERSION_CODE)
return sanity_check_failed("Incorrect kernel version");
@@ -701,8 +859,7 @@ static int __init sanity_check(struct su
return 0;
}
-static int __init bdev_read_page(struct block_device *bdev,
- long pos, void *buf)
+static int bdev_read_page(struct block_device *bdev, long pos, void *buf)
{
struct buffer_head *bh;
BUG_ON (pos%PAGE_SIZE);
@@ -716,10 +873,31 @@ static int __init bdev_read_page(struct
return 0;
}
+static int bdev_write_page(struct block_device *bdev, long pos, void *buf)
+{
+#if 0
+ struct buffer_head *bh;
+ BUG_ON (pos%PAGE_SIZE);
+ bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
+ if (!bh || (!bh->b_data)) {
+ return -1;
+ }
+ memcpy(bh->b_data, buf, PAGE_SIZE); /* FIXME: may need kmap() */
+ BUG_ON(!buffer_uptodate(bh));
+ generic_make_request(WRITE, bh);
+ if (!buffer_uptodate(bh))
+ printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unsuccessful...\n", name_resume, resume_file);
+ wait_on_buffer(bh);
+ brelse(bh);
+ return 0;
+#endif
+ printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unimplemented...\n", name_resume, resume_file);
+ return 0;
+}
+
extern dev_t __init name_to_dev_t(const char *line);
-static int __init read_suspend_image(struct block_device *bdev,
- union diskpage *cur)
+static int __read_suspend_image(struct block_device *bdev, union diskpage *cur, int noresume)
{
swp_entry_t next;
int i, nr_pgdir_pages;
@@ -744,9 +922,18 @@ static int __init read_suspend_image(str
else if (!memcmp("S2",cur->swh.magic.magic,2))
memcpy(cur->swh.magic.magic,"SWAPSPACE2",10);
else {
- printk("swsusp: %s: Unable to find suspended-data signature (%.10s - misspelled?\n",
+ if (noresume)
+ return -EINVAL;
+ panic("%sUnable to find suspended-data signature (%.10s - misspelled?\n",
name_resume, cur->swh.magic.magic);
- return -EFAULT;
+ }
+ if (noresume) {
+ /* We don't do a sanity check here: we want to restore the swap
+ whatever version of kernel made the suspend image;
+ We need to write swap, but swap is *not* enabled so
+ we must write the device directly */
+ printk("%s: Fixing swap signatures %s...\n", name_resume, resume_file);
+ bdev_write_page(bdev, 0, cur);
}
printk( "%sSignature found, resuming\n", name_resume );
@@ -796,115 +983,117 @@ static int __init read_suspend_image(str
return 0;
}
-/**
- * swsusp_save - Snapshot memory
- */
-
-int swsusp_save(void)
-{
-#if defined (CONFIG_HIGHMEM) || defined (COFNIG_DISCONTIGMEM)
- printk("swsusp is not supported with high- or discontig-mem.\n");
- return -EPERM;
-#endif
- return 0;
-}
-
-
-/**
- * swsusp_write - Write saved memory image to swap.
- *
- * do_magic(0) returns after system is resumed.
- *
- * do_magic() copies all "used" memory to "free" memory, then
- * unsuspends all device drivers, and writes memory to disk
- * using normal kernel mechanism.
- */
-
-int swsusp_write(void)
-{
- arch_prepare_suspend();
- return do_magic(0);
-}
-
-
-/**
- * swsusp_read - Read saved image from swap.
- */
-
-int __init swsusp_read(void)
+static int read_suspend_image(const char * specialfile, int noresume)
{
union diskpage *cur;
+ unsigned long scratch_page = 0;
int error;
char b[BDEVNAME_SIZE];
- if (!strlen(resume_file))
- return -ENOENT;
-
- resume_device = name_to_dev_t(resume_file);
- printk("swsusp: Resume From Partition: %s, Device: %s\n",
- resume_file, __bdevname(resume_device, b));
-
- cur = (union diskpage *)get_zeroed_page(GFP_ATOMIC);
+ resume_device = name_to_dev_t(specialfile);
+ scratch_page = get_zeroed_page(GFP_ATOMIC);
+ cur = (void *) scratch_page;
if (cur) {
struct block_device *bdev;
+ printk("Resuming from device %s\n",
+ __bdevname(resume_device, b));
bdev = open_by_devnum(resume_device, FMODE_READ, BDEV_RAW);
- if (!IS_ERR(bdev)) {
+ if (IS_ERR(bdev)) {
+ error = PTR_ERR(bdev);
+ } else {
set_blocksize(bdev, PAGE_SIZE);
- error = read_suspend_image(bdev, cur);
+ error = __read_suspend_image(bdev, cur, noresume);
blkdev_put(bdev, BDEV_RAW);
- } else
- error = PTR_ERR(bdev);
- free_page((unsigned long)cur);
- } else
- error = -ENOMEM;
+ }
+ } else error = -ENOMEM;
- if (!error)
- PRINTK("Reading resume file was successful\n");
- else
- printk( "%sError %d resuming\n", name_resume, error );
+ if (scratch_page)
+ free_page(scratch_page);
+ switch (error) {
+ case 0:
+ PRINTK("Reading resume file was successful\n");
+ break;
+ case -EINVAL:
+ break;
+ case -EIO:
+ printk( "%sI/O error\n", name_resume);
+ break;
+ case -ENOENT:
+ printk( "%s%s: No such file or directory\n", name_resume, specialfile);
+ break;
+ case -ENOMEM:
+ printk( "%sNot enough memory\n", name_resume);
+ break;
+ default:
+ printk( "%sError %d resuming\n", name_resume, error );
+ }
MDELAY(1000);
return error;
}
-
-/**
- * swsusp_restore - Replace running kernel with saved image.
+/*
+ * Called from init kernel_thread.
+ * We check if we have an image and if so we try to resume
*/
-int __init swsusp_restore(void)
+void software_resume(void)
{
- return do_magic(1);
-}
+ if (num_online_cpus() > 1) {
+ printk(KERN_WARNING "Software Suspend has malfunctioning SMP support. Disabled :(\n");
+ return;
+ }
+ /* We enable the possibility of machine suspend */
+ software_suspend_enabled = 1;
+ if (!resume_status)
+ return;
+ printk( "%s", name_resume );
+ if (resume_status == NORESUME) {
+ if(resume_file[0])
+ read_suspend_image(resume_file, 1);
+ printk( "disabled\n" );
+ return;
+ }
+ MDELAY(1000);
-/**
- * swsusp_free - Free memory allocated to hold snapshot.
- */
+ if (pm_prepare_console())
+ printk("swsusp: Can't allocate a console... proceeding\n");
-int swsusp_free(void)
-{
- PRINTK( "Freeing prev allocated pagedir\n" );
- free_suspend_pagedir((unsigned long) pagedir_save);
+ if (!resume_file[0] && resume_status == RESUME_SPECIFIED) {
+ printk( "suspension device unspecified\n" );
+ return;
+ }
- PRINTK( "Fixing swap signatures... " );
- mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
- PRINTK( "ok\n" );
- return 0;
+ printk( "resuming from %s\n", resume_file);
+ if (read_suspend_image(resume_file, 0))
+ goto read_failure;
+ do_magic(1);
+ panic("This never returns");
+
+read_failure:
+ pm_restore_console();
+ return;
}
static int __init resume_setup(char *str)
{
- if (strlen(str))
- strncpy(resume_file, str, 255);
+ if (resume_status == NORESUME)
+ return 1;
+
+ strncpy( resume_file, str, 255 );
+ resume_status = RESUME_SPECIFIED;
+
return 1;
}
static int __init noresume_setup(char *str)
{
- resume_file[0] = '\0';
+ resume_status = NORESUME;
return 1;
}
__setup("noresume", noresume_setup);
__setup("resume=", resume_setup);
+EXPORT_SYMBOL(software_suspend);
+EXPORT_SYMBOL(software_suspend_enabled);
diff -puN kernel/sched.c~test5-pm2 kernel/sched.c
--- 25/kernel/sched.c~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/kernel/sched.c Wed Sep 17 12:33:38 2003
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -2421,6 +2422,9 @@ static int migration_thread(void * data)
struct list_head *head;
migration_req_t *req;
+ if (current->flags & PF_FREEZE)
+ refrigerator(PF_IOTHREAD);
+
spin_lock_irq(&rq->lock);
head = &rq->migration_queue;
current->state = TASK_INTERRUPTIBLE;
diff -puN kernel/sys.c~test5-pm2 kernel/sys.c
--- 25/kernel/sys.c~test5-pm2 Wed Sep 17 12:33:38 2003
+++ 25-akpm/kernel/sys.c Wed Sep 17 12:33:38 2003
@@ -456,8 +456,11 @@ asmlinkage long sys_reboot(int magic1, i
#ifdef CONFIG_SOFTWARE_SUSPEND
case LINUX_REBOOT_CMD_SW_SUSPEND:
- if (!pm_suspend(PM_SUSPEND_DISK))
- break;
+ if (!software_suspend_enabled) {
+ unlock_kernel();
+ return -EAGAIN;
+ }
+ software_suspend();
do_exit(0);
break;
#endif
_