summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@osdl.org>2003-09-09 18:16:21 -0700
committerPatrick Mochel <mochel@osdl.org>2003-09-09 18:16:21 -0700
commit5a8b19df81101221af527e4cdc591c186e738b95 (patch)
tree3813b5a68a4835c6633be32d6483764205fe2187 /kernel
parentc521afac12ad04a7a9a4eacc778a7db65b89efa0 (diff)
parent7e5a77f5998f1256783b4898046e77121bd2d34d (diff)
Merge bk://kernel.bkbits.net//home/mochel/linux-2.5-power
into osdl.org:/home/mochel/src/kernel/linux-2.5-power
Diffstat (limited to 'kernel')
-rw-r--r--kernel/power/Kconfig28
-rw-r--r--kernel/power/Makefile3
-rw-r--r--kernel/power/disk.c28
-rw-r--r--kernel/power/pmdisk.c193
-rw-r--r--kernel/power/power.h2
-rw-r--r--kernel/power/swsusp.c670
-rw-r--r--kernel/sys.c7
7 files changed, 533 insertions, 398 deletions
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index c30771af69f0..c844f7170be1 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -62,3 +62,31 @@ config PM_DISK
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/<other device>
+
+ 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 --git a/kernel/power/Makefile b/kernel/power/Makefile
index 7f127b848827..d00edd15c0fd 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -1,4 +1,5 @@
obj-y := main.o process.o console.o pm.o
-obj-$(CONFIG_SOFTWARE_SUSPEND) += disk.o swsusp.o
+obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
+obj-$(CONFIG_PM_DISK) += disk.o pmdisk.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
diff --git a/kernel/power/disk.c b/kernel/power/disk.c
index 64a3130a5411..d8c259552344 100644
--- a/kernel/power/disk.c
+++ b/kernel/power/disk.c
@@ -22,11 +22,11 @@
extern u32 pm_disk_mode;
extern struct pm_ops * pm_ops;
-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);
+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);
@@ -146,7 +146,7 @@ static int prepare(void)
*
* 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
+ * If not, then call pmdis to do it's thing, then figure out how
* to power down the system.
*/
@@ -163,7 +163,7 @@ int pm_suspend_disk(void)
pr_debug("PM: snapshotting memory.\n");
in_suspend = 1;
- if ((error = swsusp_save()))
+ if ((error = pmdisk_save()))
goto Done;
if (in_suspend) {
@@ -175,14 +175,14 @@ int pm_suspend_disk(void)
mb();
barrier();
- error = swsusp_write();
+ 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");
- swsusp_free();
+ pmdisk_free();
Done:
finish();
return error;
@@ -193,7 +193,7 @@ int pm_suspend_disk(void)
* 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.
+ * 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
@@ -205,9 +205,9 @@ static int pm_resume(void)
{
int error;
- pr_debug("PM: Reading swsusp image.\n");
+ pr_debug("PM: Reading pmdisk image.\n");
- if ((error = swsusp_read()))
+ if ((error = pmdisk_read()))
goto Done;
pr_debug("PM: Preparing system for restore.\n");
@@ -229,11 +229,11 @@ static int pm_resume(void)
mdelay(1000);
pr_debug("PM: Restoring saved image.\n");
- swsusp_restore();
+ pmdisk_restore();
pr_debug("PM: Restore failed, recovering.n");
finish();
Free:
- swsusp_free();
+ pmdisk_free();
Done:
pr_debug("PM: Resume from disk failed.\n");
return 0;
diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c
index bb795b6dc537..42434427ba5e 100644
--- a/kernel/power/pmdisk.c
+++ b/kernel/power/pmdisk.c
@@ -1,37 +1,21 @@
/*
- * linux/kernel/suspend.c
+ * kernel/power/pmdisk.c - Suspend-to-disk implmentation
*
- * This file is to realize architecture-independent
- * machine suspend feature using pretty near only high-level routines
+ * This STD implementation is initially derived from swsusp (suspend-to-swap).
+ * The original copyright on that was:
*
* Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu>
* Copyright (C) 1998,2001,2002 Pavel Machek <pavel@suse.cz>
*
- * I'd like to thank the following people for their work:
+ * The additional parts are:
*
- * Pavel Machek <pavel@ucw.cz>:
- * Modifications, defectiveness pointing, being with me at the very beginning,
- * suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17.
- *
- * Steve Doddi <dirk@loth.demon.co.uk>:
- * Support the possibility of hardware state restoring.
- *
- * Raph <grey.havens@earthling.net>:
- * Support for preserving states of network devices and virtual console
- * (including X and svgatextmode)
- *
- * Kurt Garloff <garloff@suse.de>:
- * Straightened the critical function in order to prevent compilers from
- * playing tricks with local variables.
- *
- * Andreas Mohr <a.mohr@mailto.de>
- *
- * Alex Badea <vampire@go.ro>:
- * Fixed runaway init
+ * Copyright (C) 2003 Patrick Mochel
+ * Copyright (C) 2003 Open Source Development Lab
+ *
+ * This file is released under the GPLv2.
*
- * More state savers are welcome. Especially for the scsi layer...
+ * For more information, please see the text files in Documentation/power/
*
- * For TODOs,FIXMEs also look in Documentation/swsusp.txt
*/
#include <linux/mm.h>
@@ -48,7 +32,7 @@
#include "power.h"
-extern int swsusp_arch_suspend(int resume);
+extern int pmdisk_arch_suspend(int resume);
#define __ADDRESS(x) ((unsigned long) phys_to_virt(x))
#define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT)
@@ -63,10 +47,12 @@ extern int is_head_of_free_region(struct page *);
static int pagedir_order_check;
static int nr_copy_pages_check;
-static char resume_file[256]; /* For resume= kernel option */
+/* 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 nr_copy_pages __nosavedata = 0;
+unsigned int pmdisk_pages __nosavedata = 0;
/* Suspend pagedir is allocated before final copy, therefore it
must be freed after resume
@@ -77,7 +63,7 @@ unsigned int nr_copy_pages __nosavedata = 0;
allocated at time of resume, that travels through memory not to
collide with anything.
*/
-suspend_pagedir_t *pagedir_nosave __nosavedata = NULL;
+suspend_pagedir_t *pm_pagedir_nosave __nosavedata = NULL;
static suspend_pagedir_t *pagedir_save;
static int pagedir_order __nosavedata = 0;
@@ -136,9 +122,9 @@ static __inline__ int fill_suspend_header(struct suspend_header *sh)
/* FIXME: Is this bogus? --RR */
sh->num_cpus = num_online_cpus();
sh->page_size = PAGE_SIZE;
- sh->suspend_pagedir = pagedir_nosave;
- BUG_ON (pagedir_save != pagedir_nosave);
- sh->num_pbes = nr_copy_pages;
+ 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]
@@ -248,13 +234,13 @@ static int write_suspend_image(void)
{
int i;
swp_entry_t entry, prev = { 0 };
- int nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages);
+ 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): ", nr_copy_pages );
- for (i=0; i<nr_copy_pages; i++) {
+ printk( "Writing data to swap (%d pages): ", pmdisk_pages );
+ for (i=0; i<pmdisk_pages; i++) {
if (!(i%100))
printk( "." );
if (!(entry = get_swap_page()).val)
@@ -263,16 +249,16 @@ static int write_suspend_image(void)
if (swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND)
panic("\nPage %d: not enough swapspace on suspend device", i );
- address = (pagedir_nosave+i)->address;
+ address = (pm_pagedir_nosave+i)->address;
page = virt_to_page(address);
rw_swap_page_sync(WRITE, entry, page);
- (pagedir_nosave+i)->swap_address = entry;
+ (pm_pagedir_nosave+i)->swap_address = entry;
}
printk( "|\n" );
printk( "Writing pagedir (%d pages): ", nr_pgdir_pages);
for (i=0; i<nr_pgdir_pages; i++) {
- cur = (union diskpage *)((char *) pagedir_nosave)+i;
- BUG_ON ((char *) cur != (((char *) pagedir_nosave) + i*PAGE_SIZE));
+ cur = (union diskpage *)((char *) pm_pagedir_nosave)+i;
+ BUG_ON ((char *) cur != (((char *) pm_pagedir_nosave) + i*PAGE_SIZE));
printk( "." );
if (!(entry = get_swap_page()).val) {
printk(KERN_CRIT "Not enough swapspace when writing pgdir\n" );
@@ -416,7 +402,7 @@ static suspend_pagedir_t *create_suspend_pagedir(int nr_copy_pages)
}
-int swsusp_suspend(void)
+int pmdisk_suspend(void)
{
struct sysinfo i;
unsigned int nr_needed_pages = 0;
@@ -424,12 +410,12 @@ int swsusp_suspend(void)
read_swapfiles();
drain_local_pages();
- pagedir_nosave = NULL;
+ pm_pagedir_nosave = NULL;
printk( "/critical section: Counting pages to copy" );
- nr_copy_pages = count_and_copy_data_pages(NULL);
- nr_needed_pages = nr_copy_pages + PAGES_FOR_IO;
+ 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",nr_copy_pages,PAGES_FOR_IO,nr_needed_pages,nr_free_pages());
+ 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());
@@ -445,18 +431,18 @@ int swsusp_suspend(void)
}
PRINTK( "Alloc pagedir\n" );
- pagedir_save = pagedir_nosave = create_suspend_pagedir(nr_copy_pages);
- if(!pagedir_nosave) {
+ 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 = nr_copy_pages;
+ 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 (nr_copy_pages != count_and_copy_data_pages(pagedir_nosave)) /* copy */
+ if (pmdisk_pages != count_and_copy_data_pages(pm_pagedir_nosave)) /* copy */
BUG();
/*
@@ -465,7 +451,7 @@ int swsusp_suspend(void)
* touch swap space! Except we must write out our image of course.
*/
- printk( "critical section/: done (%d pages copied)\n", nr_copy_pages );
+ printk( "critical section/: done (%d pages copied)\n", pmdisk_pages );
return 0;
}
@@ -498,9 +484,9 @@ static int suspend_save_image(void)
* Magic happens here
*/
-int swsusp_resume(void)
+int pmdisk_resume(void)
{
- BUG_ON (nr_copy_pages_check != nr_copy_pages);
+ 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 */
@@ -508,19 +494,19 @@ int swsusp_resume(void)
return 0;
}
-/* swsusp_arch_suspend() is implemented in arch/?/power/swsusp.S,
+/* pmdisk_arch_suspend() is implemented in arch/?/power/pmdisk.S,
and basically does:
if (!resume) {
save_processor_state();
SAVE_REGISTERS
- return swsusp_suspend();
+ return pmdisk_suspend();
}
GO_TO_SWAPPER_PAGE_TABLES
COPY_PAGES_BACK
RESTORE_REGISTERS
restore_processor_state();
- return swsusp_resume();
+ return pmdisk_resume();
*/
@@ -540,7 +526,7 @@ static void __init copy_pagedir(suspend_pagedir_t *to, suspend_pagedir_t *from)
}
}
-#define does_collide(addr) does_collide_order(pagedir_nosave, addr, 0)
+#define does_collide(addr) does_collide_order(pm_pagedir_nosave, addr, 0)
/*
* Returns true if given address/order collides with any orig_address
@@ -551,7 +537,7 @@ static int __init does_collide_order(suspend_pagedir_t *pagedir,
int i;
unsigned long addre = addr + (PAGE_SIZE<<order);
- for(i=0; i < nr_copy_pages; i++)
+ for(i=0; i < pmdisk_pages; i++)
if((pagedir+i)->orig_address >= addr &&
(pagedir+i)->orig_address < addre)
return 1;
@@ -567,7 +553,7 @@ static int __init check_pagedir(void)
{
int i;
- for(i=0; i < nr_copy_pages; i++) {
+ for(i=0; i < pmdisk_pages; i++) {
unsigned long addr;
do {
@@ -576,7 +562,7 @@ static int __init check_pagedir(void)
return -ENOMEM;
} while (does_collide(addr));
- (pagedir_nosave+i)->address = addr;
+ (pm_pagedir_nosave+i)->address = addr;
}
return 0;
}
@@ -587,7 +573,7 @@ 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 = pagedir_nosave;
+ suspend_pagedir_t *new_pagedir, *old_pagedir = pm_pagedir_nosave;
void **eaten_memory = NULL;
void **c = eaten_memory, *m, *f;
@@ -611,7 +597,7 @@ static int __init relocate_pagedir(void)
if (!m)
return -ENOMEM;
- pagedir_nosave = new_pagedir = m;
+ pm_pagedir_nosave = new_pagedir = m;
copy_pagedir(new_pagedir, old_pagedir);
c = eaten_memory;
@@ -770,11 +756,11 @@ static int __init read_suspend_image(void)
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 "swsusp: Partition is normal swap space\n");
+ printk(KERN_ERR "pmdisk: Partition is normal swap space\n");
error = -EINVAL;
goto Done;
} else {
- printk(KERN_ERR "swsusp: Invalid partition type.\n");
+ printk(KERN_ERR "pmdisk: Invalid partition type.\n");
error = -EINVAL;
goto Done;
}
@@ -796,12 +782,12 @@ static int __init read_suspend_image(void)
next = next_entry(cur);
pagedir_save = cur->sh.suspend_pagedir;
- nr_copy_pages = cur->sh.num_pbes;
- nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages);
+ pmdisk_pages = cur->sh.num_pbes;
+ nr_pgdir_pages = SUSPEND_PD_PAGES(pmdisk_pages);
pagedir_order = get_bitmask_order(nr_pgdir_pages);
- pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order);
- if (!pagedir_nosave) {
+ pm_pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order);
+ if (!pm_pagedir_nosave) {
error = -ENOMEM;
goto Done;
}
@@ -811,7 +797,7 @@ static int __init read_suspend_image(void)
/* 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 *) pagedir_nosave)+i;
+ cur = (union diskpage *)((char *) pm_pagedir_nosave)+i;
error = read_page(swp_offset(next), cur);
if (error)
goto FreePagedir;
@@ -824,15 +810,15 @@ static int __init read_suspend_image(void)
if ((error = check_pagedir()))
goto FreePagedir;
- printk( "Reading image data (%d pages): ", nr_copy_pages );
- for(i=0; i < nr_copy_pages; i++) {
- swp_entry_t swap_address = (pagedir_nosave+i)->swap_address;
+ 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 *)((pagedir_nosave+i)->address));
+ (char *)((pm_pagedir_nosave+i)->address));
if (error)
goto FreePagedir;
}
@@ -841,52 +827,52 @@ static int __init read_suspend_image(void)
free_page((unsigned long)cur);
return error;
FreePagedir:
- free_pages((unsigned long)pagedir_nosave,pagedir_order);
+ free_pages((unsigned long)pm_pagedir_nosave,pagedir_order);
goto Done;
}
/**
- * swsusp_save - Snapshot memory
+ * pmdisk_save - Snapshot memory
*/
-int swsusp_save(void)
+int pmdisk_save(void)
{
int error;
#if defined (CONFIG_HIGHMEM) || defined (COFNIG_DISCONTIGMEM)
- printk("swsusp is not supported with high- or discontig-mem.\n");
+ 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 = swsusp_arch_suspend(0);
+ error = pmdisk_arch_suspend(0);
local_irq_enable();
return error;
}
/**
- * swsusp_write - Write saved memory image to swap.
+ * pmdisk_write - Write saved memory image to swap.
*
- * swsusp_arch_suspend(0) returns after system is resumed.
+ * pmdisk_arch_suspend(0) returns after system is resumed.
*
- * swsusp_arch_suspend() copies all "used" memory to "free" memory,
+ * 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 swsusp_write(void)
+int pmdisk_write(void)
{
return suspend_save_image();
}
/**
- * swsusp_read - Read saved image from swap.
+ * pmdisk_read - Read saved image from swap.
*/
-int __init swsusp_read(void)
+int __init pmdisk_read(void)
{
int error;
char b[BDEVNAME_SIZE];
@@ -895,7 +881,7 @@ int __init swsusp_read(void)
return -ENOENT;
resume_device = name_to_dev_t(resume_file);
- printk("swsusp: Resume From Partition: %s, Device: %s\n",
+ 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);
@@ -916,54 +902,41 @@ int __init swsusp_read(void)
/**
- * swsusp_restore - Replace running kernel with saved image.
+ * pmdisk_restore - Replace running kernel with saved image.
*/
-int __init swsusp_restore(void)
+int __init pmdisk_restore(void)
{
int error;
local_irq_disable();
- error = swsusp_arch_suspend(1);
+ error = pmdisk_arch_suspend(1);
local_irq_enable();
return error;
}
/**
- * swsusp_free - Free memory allocated to hold snapshot.
+ * pmdisk_free - Free memory allocated to hold snapshot.
*/
-int swsusp_free(void)
+int pmdisk_free(void)
{
PRINTK( "Freeing prev allocated pagedir\n" );
free_suspend_pagedir((unsigned long) pagedir_save);
return 0;
}
-
-int software_suspend(void)
+static int __init pmdisk_setup(char *str)
{
- struct pm_ops swsusp_ops = {
- .pm_disk_mode = PM_DISK_SHUTDOWN,
- };
-
- pm_set_ops(&swsusp_ops);
- return pm_suspend(PM_SUSPEND_DISK);
-}
-
-static int __init resume_setup(char *str)
-{
- if (strlen(str))
- strncpy(resume_file, str, 255);
- return 1;
-}
-
-static int __init noresume_setup(char *str)
-{
- resume_file[0] = '\0';
+ 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("noresume", noresume_setup);
-__setup("resume=", resume_setup);
+__setup("pmdisk=", pmdisk_setup);
diff --git a/kernel/power/power.h b/kernel/power/power.h
index e0874ed266f5..d180b0a192cd 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -9,7 +9,7 @@
#endif
-#ifdef CONFIG_SOFTWARE_SUSPEND
+#ifdef CONFIG_PM_DISK
extern int pm_suspend_disk(void);
#else
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c
index bb795b6dc537..13b1396f317b 100644
--- a/kernel/power/swsusp.c
+++ b/kernel/power/swsusp.c
@@ -34,21 +34,46 @@
* For TODOs,FIXMEs also look in Documentation/swsusp.txt
*/
+#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/bio.h>
#include <linux/suspend.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
+#include <linux/utsname.h>
#include <linux/version.h>
+#include <linux/delay.h>
#include <linux/reboot.h>
+#include <linux/bitops.h>
+#include <linux/vt_kern.h>
+#include <linux/kbd_kern.h>
+#include <linux/keyboard.h>
+#include <linux/spinlock.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/swap.h>
+#include <linux/pm.h>
#include <linux/device.h>
+#include <linux/buffer_head.h>
#include <linux/swapops.h>
#include <linux/bootmem.h>
+#include <asm/uaccess.h>
#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
#include "power.h"
+extern long sys_sync(void);
+
+unsigned char software_suspend_enabled = 0;
+
+extern void do_magic(int resume);
+
+#define NORESUME 1
+#define RESUME_SPECIFIED 2
-extern int swsusp_arch_suspend(int resume);
#define __ADDRESS(x) ((unsigned long) phys_to_virt(x))
#define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT)
@@ -59,11 +84,15 @@ extern char __nosave_begin, __nosave_end;
extern int is_head_of_free_region(struct page *);
+/* Locks */
+spinlock_t suspend_pagedir_lock __nosavedata = SPIN_LOCK_UNLOCKED;
+
/* Variables to be preserved over suspend */
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;
@@ -327,10 +356,15 @@ static int count_and_copy_data_pages(struct pbe *pagedir_p)
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 <pavel@ucw.cz> and try again ;-).");
if (!PageReserved(page)) {
if (PageNosave(page))
@@ -415,13 +449,62 @@ static suspend_pagedir_t *create_suspend_pagedir(int nr_copy_pages)
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
+ }
+}
-int swsusp_suspend(void)
+static int suspend_prepare_image(void)
{
struct sysinfo i;
unsigned int nr_needed_pages = 0;
- read_swapfiles();
drain_local_pages();
pagedir_nosave = NULL;
@@ -469,66 +552,188 @@ int swsusp_suspend(void)
return 0;
}
+static void suspend_save_image(void)
+{
+ drivers_unsuspend();
-/**
- * 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.)
- */
+ lock_swapdevices();
+ write_suspend_image();
+ lock_swapdevices(); /* This will unlock ignored swap devices since writing is finished */
-static int suspend_save_image(void)
+ /* 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 void suspend_power_down(void)
{
- int error;
- device_resume();
- lock_swapdevices();
- error = write_suspend_image();
- lock_swapdevices();
- return error;
+ 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 */
}
/*
* Magic happens here
*/
-int swsusp_resume(void)
+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");
+ 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();
- return 0;
+
+ __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
}
-/* swsusp_arch_suspend() is implemented in arch/?/power/swsusp.S,
- and basically does:
+/* do_magic() is implemented in arch/?/kernel/suspend_asm.S, and basically does:
if (!resume) {
+ do_magic_suspend_1();
save_processor_state();
SAVE_REGISTERS
- return swsusp_suspend();
+ do_magic_suspend_2();
+ return;
}
GO_TO_SWAPPER_PAGE_TABLES
+ do_magic_resume_1();
COPY_PAGES_BACK
RESTORE_REGISTERS
restore_processor_state();
- return swsusp_resume();
+ do_magic_resume_2();
*/
+void do_magic_suspend_1(void)
+{
+ mb();
+ barrier();
+ BUG_ON(in_atomic());
+ spin_lock_irq(&suspend_pagedir_lock);
+}
+
+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) {
+ 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);
+
+ 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<<pagedir_order*PAGE_SIZE)? */
-static void __init copy_pagedir(suspend_pagedir_t *to, suspend_pagedir_t *from)
+static void copy_pagedir(suspend_pagedir_t *to, suspend_pagedir_t *from)
{
int i;
char *topointer=(char *)to, *frompointer=(char *)from;
@@ -545,8 +750,8 @@ static void __init copy_pagedir(suspend_pagedir_t *to, suspend_pagedir_t *from)
/*
* Returns true if given address/order collides with any orig_address
*/
-static int __init does_collide_order(suspend_pagedir_t *pagedir,
- unsigned long addr, int order)
+static int does_collide_order(suspend_pagedir_t *pagedir, unsigned long addr,
+ int order)
{
int i;
unsigned long addre = addr + (PAGE_SIZE<<order);
@@ -563,7 +768,7 @@ static int __init does_collide_order(suspend_pagedir_t *pagedir,
* 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)
+static int check_pagedir(void)
{
int i;
@@ -581,7 +786,7 @@ static int __init check_pagedir(void)
return 0;
}
-static int __init relocate_pagedir(void)
+static int relocate_pagedir(void)
{
/*
* We have to avoid recursion (not to overflow kernel stack),
@@ -631,13 +836,13 @@ static int __init relocate_pagedir(void)
* I really don't think that it's foolproof but more than nothing..
*/
-static int __init sanity_check_failed(char *reason)
+static int 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)
+static int sanity_check(struct suspend_header *sh)
{
if(sh->version_code != LINUX_VERSION_CODE)
return sanity_check_failed("Incorrect kernel version");
@@ -654,146 +859,90 @@ static int __init sanity_check(struct suspend_header *sh)
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)
+static int bdev_read_page(struct block_device *bdev, long pos, void *buf)
{
- atomic_set(&io_done,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(buf, bh->b_data, PAGE_SIZE); /* FIXME: may need kmap() */
+ BUG_ON(!buffer_uptodate(bh));
+ brelse(bh);
return 0;
-}
+}
-static void wait_io(void)
+static int bdev_write_page(struct block_device *bdev, long pos, void *buf)
{
- 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);
+#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);
-
-#define next_entry(diskpage) diskpage->link.next
-
-static int __init read_suspend_image(void)
+static int __read_suspend_image(struct block_device *bdev, union diskpage *cur, int noresume)
{
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;
+#define PREPARENEXT \
+ { next = cur->link.next; \
+ next.val = swp_offset(next) * PAGE_SIZE; \
+ }
- if ((error = read_page(0, cur)))
- goto Done;
+ if (bdev_read_page(bdev, 0, cur)) return -EIO;
- /*
- * We have to read next position before we overwrite it
- */
- next = next_entry(cur);
+ if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) ||
+ (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) {
+ printk(KERN_ERR "%sThis is normal swap space\n", name_resume );
+ return -EINVAL;
+ }
+
+ PREPARENEXT; /* We have to read next position before we overwrite it */
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 "swsusp: Partition is normal swap space\n");
- error = -EINVAL;
- goto Done;
- } else {
- printk(KERN_ERR "swsusp: Invalid partition type.\n");
- error = -EINVAL;
- goto Done;
+ else {
+ if (noresume)
+ return -EINVAL;
+ panic("%sUnable to find suspended-data signature (%.10s - misspelled?\n",
+ name_resume, cur->swh.magic.magic);
+ }
+ 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);
}
-
- /*
- * 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);
+ if (bdev_read_page(bdev, next.val, cur)) return -EIO;
+ if (sanity_check(&cur->sh)) /* Is this same machine? */
+ return -EPERM;
+ PREPARENEXT;
pagedir_save = cur->sh.suspend_pagedir;
nr_copy_pages = cur->sh.num_pbes;
@@ -801,10 +950,8 @@ static int __init read_suspend_image(void)
pagedir_order = get_bitmask_order(nr_pgdir_pages);
pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order);
- if (!pagedir_nosave) {
- error = -ENOMEM;
- goto Done;
- }
+ if (!pagedir_nosave)
+ return -ENOMEM;
PRINTK( "%sReading pagedir, ", name_resume );
@@ -812,17 +959,15 @@ static int __init read_suspend_image(void)
for (i=nr_pgdir_pages-1; i>=0; i--) {
BUG_ON (!next.val);
cur = (union diskpage *)((char *) pagedir_nosave)+i;
- error = read_page(swp_offset(next), cur);
- if (error)
- goto FreePagedir;
- next = next_entry(cur);
+ if (bdev_read_page(bdev, next.val, cur)) return -EIO;
+ PREPARENEXT;
}
BUG_ON (next.val);
- if ((error = relocate_pagedir()))
- goto FreePagedir;
- if ((error = check_pagedir()))
- goto FreePagedir;
+ if (relocate_pagedir())
+ return -ENOMEM;
+ if (check_pagedir())
+ return -ENOMEM;
printk( "Reading image data (%d pages): ", nr_copy_pages );
for(i=0; i < nr_copy_pages; i++) {
@@ -831,139 +976,124 @@ static int __init read_suspend_image(void)
printk( "." );
/* You do not need to check for overlaps...
... check_pagedir already did this work */
- error = read_page(swp_offset(swap_address),
- (char *)((pagedir_nosave+i)->address));
- if (error)
- goto FreePagedir;
+ if (bdev_read_page(bdev, swp_offset(swap_address) * PAGE_SIZE, (char *)((pagedir_nosave+i)->address)))
+ return -EIO;
}
printk( "|\n" );
- Done:
- free_page((unsigned long)cur);
- return error;
- FreePagedir:
- free_pages((unsigned long)pagedir_nosave,pagedir_order);
- goto Done;
-}
-
-/**
- * swsusp_save - Snapshot memory
- */
-
-int swsusp_save(void)
-{
- int error;
-
-#if defined (CONFIG_HIGHMEM) || defined (COFNIG_DISCONTIGMEM)
- printk("swsusp is not supported with high- or discontig-mem.\n");
- return -EPERM;
-#endif
- if ((error = arch_prepare_suspend()))
- return error;
- local_irq_disable();
- error = swsusp_arch_suspend(0);
- local_irq_enable();
- return error;
-}
-
-
-/**
- * swsusp_write - Write saved memory image to swap.
- *
- * swsusp_arch_suspend(0) returns after system is resumed.
- *
- * swsusp_arch_suspend() 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)
-{
- return suspend_save_image();
+ return 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));
-
- 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);
+ 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)) {
+ error = PTR_ERR(bdev);
+ } else {
+ set_blocksize(bdev, PAGE_SIZE);
+ error = __read_suspend_image(bdev, cur, noresume);
+ blkdev_put(bdev, BDEV_RAW);
+ }
+ } 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)
{
- int error;
- local_irq_disable();
- error = swsusp_arch_suspend(1);
- local_irq_enable();
- return error;
-}
-
+ 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;
-/**
- * swsusp_free - Free memory allocated to hold snapshot.
- */
+ printk( "%s", name_resume );
+ if (resume_status == NORESUME) {
+ if(resume_file[0])
+ read_suspend_image(resume_file, 1);
+ printk( "disabled\n" );
+ return;
+ }
+ MDELAY(1000);
-int swsusp_free(void)
-{
- PRINTK( "Freeing prev allocated pagedir\n" );
- free_suspend_pagedir((unsigned long) pagedir_save);
- return 0;
-}
+ if (pm_prepare_console())
+ printk("swsusp: Can't allocate a console... proceeding\n");
+ if (!resume_file[0] && resume_status == RESUME_SPECIFIED) {
+ printk( "suspension device unspecified\n" );
+ return;
+ }
-int software_suspend(void)
-{
- struct pm_ops swsusp_ops = {
- .pm_disk_mode = PM_DISK_SHUTDOWN,
- };
+ printk( "resuming from %s\n", resume_file);
+ if (read_suspend_image(resume_file, 0))
+ goto read_failure;
+ do_magic(1);
+ panic("This never returns");
- pm_set_ops(&swsusp_ops);
- return pm_suspend(PM_SUSPEND_DISK);
+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 --git a/kernel/sys.c b/kernel/sys.c
index d77453173d29..b172afa53be1 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -456,8 +456,11 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user
#ifdef CONFIG_SOFTWARE_SUSPEND
case LINUX_REBOOT_CMD_SW_SUSPEND:
- if (!software_suspend())
- break;
+ if (!software_suspend_enabled) {
+ unlock_kernel();
+ return -EAGAIN;
+ }
+ software_suspend();
do_exit(0);
break;
#endif