Commit 1026f161 authored by Pavel Machek's avatar Pavel Machek Committed by Linus Torvalds

[PATCH] swsusp: enable resume from initrd

From: <mjg59@scrf.ucam.org>

When using a fully modularized kernel it is necessary to activate resume
manually as the device node might not be available during kernel init.

This patch implements a new sysfs attribute '/sys/power/resume' which allows
for manual activation of software resume.  When read from it prints the
configured resume device in 'major:minor' format.  When written to it expects
a device in 'major:minor' format.  This device is then checked for a suspended
image and resume is started if a valid image is found.  The original
functionality is left in place.

It should be used from initramfs, or with care.
Signed-off-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarPavel Machek <pavel@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 4a408c2f
...@@ -36,6 +36,8 @@ typedef struct pbe { ...@@ -36,6 +36,8 @@ typedef struct pbe {
#define SUSPEND_PD_PAGES(x) (((x)*sizeof(struct pbe))/PAGE_SIZE+1) #define SUSPEND_PD_PAGES(x) (((x)*sizeof(struct pbe))/PAGE_SIZE+1)
extern dev_t swsusp_resume_device;
/* mm/vmscan.c */ /* mm/vmscan.c */
extern int shrink_mem(void); extern int shrink_mem(void);
......
...@@ -53,7 +53,7 @@ static int __init readwrite(char *str) ...@@ -53,7 +53,7 @@ static int __init readwrite(char *str)
__setup("ro", readonly); __setup("ro", readonly);
__setup("rw", readwrite); __setup("rw", readwrite);
static dev_t __init try_name(char *name, int part) static dev_t try_name(char *name, int part)
{ {
char path[64]; char path[64];
char buf[32]; char buf[32];
...@@ -135,7 +135,7 @@ static dev_t __init try_name(char *name, int part) ...@@ -135,7 +135,7 @@ static dev_t __init try_name(char *name, int part)
* is mounted on rootfs /sys. * is mounted on rootfs /sys.
*/ */
dev_t __init name_to_dev_t(char *name) dev_t name_to_dev_t(char *name)
{ {
char s[32]; char s[32];
char *p; char *p;
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/device.h>
#include "power.h" #include "power.h"
...@@ -25,13 +24,16 @@ extern struct pm_ops * pm_ops; ...@@ -25,13 +24,16 @@ extern struct pm_ops * pm_ops;
extern int swsusp_suspend(void); extern int swsusp_suspend(void);
extern int swsusp_write(void); extern int swsusp_write(void);
extern int swsusp_check(void);
extern int swsusp_read(void); extern int swsusp_read(void);
extern void swsusp_close(void);
extern int swsusp_resume(void); extern int swsusp_resume(void);
extern int swsusp_free(void); extern int swsusp_free(void);
static int noresume = 0; static int noresume = 0;
char resume_file[256] = CONFIG_PM_STD_PARTITION; char resume_file[256] = CONFIG_PM_STD_PARTITION;
dev_t swsusp_resume_device;
/** /**
* power_down - Shut machine down for hibernate. * power_down - Shut machine down for hibernate.
...@@ -121,44 +123,53 @@ static void finish(void) ...@@ -121,44 +123,53 @@ static void finish(void)
} }
static int prepare(void) static int prepare_processes(void)
{ {
int error; int error;
pm_prepare_console(); pm_prepare_console();
sys_sync(); sys_sync();
if (freeze_processes()) { if (freeze_processes()) {
error = -EBUSY; error = -EBUSY;
goto Thaw; return error;
} }
if (pm_disk_mode == PM_DISK_PLATFORM) { if (pm_disk_mode == PM_DISK_PLATFORM) {
if (pm_ops && pm_ops->prepare) { if (pm_ops && pm_ops->prepare) {
if ((error = pm_ops->prepare(PM_SUSPEND_DISK))) if ((error = pm_ops->prepare(PM_SUSPEND_DISK)))
goto Thaw; return error;
} }
} }
/* Free memory before shutting down devices. */ /* Free memory before shutting down devices. */
free_some_memory(); free_some_memory();
return 0;
}
static void unprepare_processes(void)
{
enable_nonboot_cpus();
thaw_processes();
pm_restore_console();
}
static int prepare_devices(void)
{
int error;
disable_nonboot_cpus(); disable_nonboot_cpus();
if ((error = device_suspend(PMSG_FREEZE))) { if ((error = device_suspend(PMSG_FREEZE))) {
printk("Some devices failed to suspend\n"); printk("Some devices failed to suspend\n");
goto Finish;
}
return 0;
Finish:
platform_finish(); platform_finish();
Thaw:
enable_nonboot_cpus(); enable_nonboot_cpus();
thaw_processes();
pm_restore_console();
return error; return error;
} }
return 0;
}
/** /**
* pm_suspend_disk - The granpappy of power management. * pm_suspend_disk - The granpappy of power management.
...@@ -173,8 +184,15 @@ int pm_suspend_disk(void) ...@@ -173,8 +184,15 @@ int pm_suspend_disk(void)
{ {
int error; int error;
if ((error = prepare())) error = prepare_processes();
if (!error) {
error = prepare_devices();
}
if (error) {
unprepare_processes();
return error; return error;
}
pr_debug("PM: Attempting to suspend to disk.\n"); pr_debug("PM: Attempting to suspend to disk.\n");
if (pm_disk_mode == PM_DISK_FIRMWARE) if (pm_disk_mode == PM_DISK_FIRMWARE)
...@@ -223,14 +241,26 @@ static int software_resume(void) ...@@ -223,14 +241,26 @@ static int software_resume(void)
return 0; return 0;
} }
pr_debug("PM: Checking swsusp image.\n");
if ((error = swsusp_check()))
goto Done;
pr_debug("PM: Preparing processes for restore.\n");
if ((error = prepare_processes())) {
swsusp_close();
goto Cleanup;
}
pr_debug("PM: Reading swsusp image.\n"); pr_debug("PM: Reading swsusp image.\n");
if ((error = swsusp_read())) if ((error = swsusp_read()))
goto Done; goto Cleanup;
pr_debug("PM: Preparing system for restore.\n"); pr_debug("PM: Preparing devices for restore.\n");
if ((error = prepare())) if ((error = prepare_devices()))
goto Free; goto Free;
mb(); mb();
...@@ -241,6 +271,8 @@ static int software_resume(void) ...@@ -241,6 +271,8 @@ static int software_resume(void)
finish(); finish();
Free: Free:
swsusp_free(); swsusp_free();
Cleanup:
unprepare_processes();
Done: Done:
pr_debug("PM: Resume from disk failed.\n"); pr_debug("PM: Resume from disk failed.\n");
return 0; return 0;
...@@ -328,8 +360,41 @@ static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n) ...@@ -328,8 +360,41 @@ static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n)
power_attr(disk); power_attr(disk);
static ssize_t resume_show(struct subsystem * subsys, char *buf)
{
return sprintf(buf,"%d:%d\n", MAJOR(swsusp_resume_device),
MINOR(swsusp_resume_device));
}
static ssize_t resume_store(struct subsystem * subsys, const char * buf, size_t n)
{
int len;
char *p;
unsigned int maj, min;
int error = -EINVAL;
dev_t res;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
if (sscanf(buf, "%u:%u", &maj, &min) == 2) {
res = MKDEV(maj,min);
if (maj == MAJOR(res) && min == MINOR(res)) {
swsusp_resume_device = res;
printk("Attempting manual resume\n");
noresume = 0;
software_resume();
}
}
return error >= 0 ? n : error;
}
power_attr(resume);
static struct attribute * g[] = { static struct attribute * g[] = {
&disk_attr.attr, &disk_attr.attr,
&resume_attr.attr,
NULL, NULL,
}; };
......
...@@ -79,7 +79,7 @@ extern const void __nosave_begin, __nosave_end; ...@@ -79,7 +79,7 @@ extern const void __nosave_begin, __nosave_end;
static int nr_copy_pages_check; static int nr_copy_pages_check;
extern char resume_file[]; extern char resume_file[];
static dev_t resume_device;
/* Local variables that should not be affected by save */ /* Local variables that should not be affected by save */
unsigned int nr_copy_pages __nosavedata = 0; unsigned int nr_copy_pages __nosavedata = 0;
...@@ -169,7 +169,7 @@ static int is_resume_device(const struct swap_info_struct *swap_info) ...@@ -169,7 +169,7 @@ static int is_resume_device(const struct swap_info_struct *swap_info)
struct inode *inode = file->f_dentry->d_inode; struct inode *inode = file->f_dentry->d_inode;
return S_ISBLK(inode->i_mode) && return S_ISBLK(inode->i_mode) &&
resume_device == MKDEV(imajor(inode), iminor(inode)); swsusp_resume_device == MKDEV(imajor(inode), iminor(inode));
} }
static int swsusp_swap_check(void) /* This is called before saving image */ static int swsusp_swap_check(void) /* This is called before saving image */
...@@ -940,7 +940,7 @@ int swsusp_resume(void) ...@@ -940,7 +940,7 @@ int swsusp_resume(void)
/* /*
* Returns true if given address/order collides with any orig_address * Returns true if given address/order collides with any orig_address
*/ */
static int __init does_collide_order(unsigned long addr, int order) static int does_collide_order(unsigned long addr, int order)
{ {
int i; int i;
...@@ -974,7 +974,7 @@ static inline void eat_page(void *page) ...@@ -974,7 +974,7 @@ static inline void eat_page(void *page)
*eaten_memory = c; *eaten_memory = c;
} }
static unsigned long __init get_usable_page(unsigned gfp_mask) static unsigned long get_usable_page(unsigned gfp_mask)
{ {
unsigned long m; unsigned long m;
...@@ -988,7 +988,7 @@ static unsigned long __init get_usable_page(unsigned gfp_mask) ...@@ -988,7 +988,7 @@ static unsigned long __init get_usable_page(unsigned gfp_mask)
return m; return m;
} }
static void __init free_eaten_memory(void) static void free_eaten_memory(void)
{ {
unsigned long m; unsigned long m;
void **c; void **c;
...@@ -1011,7 +1011,7 @@ static void __init free_eaten_memory(void) ...@@ -1011,7 +1011,7 @@ static void __init free_eaten_memory(void)
* pages later * pages later
*/ */
static int __init check_pagedir(struct pbe *pblist) static int check_pagedir(struct pbe *pblist)
{ {
struct pbe *p; struct pbe *p;
...@@ -1035,7 +1035,7 @@ static int __init check_pagedir(struct pbe *pblist) ...@@ -1035,7 +1035,7 @@ static int __init check_pagedir(struct pbe *pblist)
* restore from the loaded pages later. We relocate them here. * restore from the loaded pages later. We relocate them here.
*/ */
static struct pbe * __init swsusp_pagedir_relocate(struct pbe *pblist) static struct pbe * swsusp_pagedir_relocate(struct pbe *pblist)
{ {
struct zone *zone; struct zone *zone;
unsigned long zone_pfn; unsigned long zone_pfn;
...@@ -1184,7 +1184,7 @@ static int bio_write_page(pgoff_t page_off, void * page) ...@@ -1184,7 +1184,7 @@ static int bio_write_page(pgoff_t page_off, void * page)
* I really don't think that it's foolproof but more than nothing.. * I really don't think that it's foolproof but more than nothing..
*/ */
static const char * __init sanity_check(void) static const char * sanity_check(void)
{ {
dump_info(); dump_info();
if(swsusp_info.version_code != LINUX_VERSION_CODE) if(swsusp_info.version_code != LINUX_VERSION_CODE)
...@@ -1205,7 +1205,7 @@ static const char * __init sanity_check(void) ...@@ -1205,7 +1205,7 @@ static const char * __init sanity_check(void)
} }
static int __init check_header(void) static int check_header(void)
{ {
const char * reason = NULL; const char * reason = NULL;
int error; int error;
...@@ -1223,7 +1223,7 @@ static int __init check_header(void) ...@@ -1223,7 +1223,7 @@ static int __init check_header(void)
return error; return error;
} }
static int __init check_sig(void) static int check_sig(void)
{ {
int error; int error;
...@@ -1253,7 +1253,7 @@ static int __init check_sig(void) ...@@ -1253,7 +1253,7 @@ static int __init check_sig(void)
* already did that. * already did that.
*/ */
static int __init data_read(struct pbe *pblist) static int data_read(struct pbe *pblist)
{ {
struct pbe * p; struct pbe * p;
int error = 0; int error = 0;
...@@ -1281,13 +1281,13 @@ static int __init data_read(struct pbe *pblist) ...@@ -1281,13 +1281,13 @@ static int __init data_read(struct pbe *pblist)
return error; return error;
} }
extern dev_t __init name_to_dev_t(const char *line); extern dev_t name_to_dev_t(const char *line);
/** /**
* read_pagedir - Read page backup list pages from swap * read_pagedir - Read page backup list pages from swap
*/ */
static int __init read_pagedir(struct pbe *pblist) static int read_pagedir(struct pbe *pblist)
{ {
struct pbe *pbpage, *p; struct pbe *pbpage, *p;
unsigned i = 0; unsigned i = 0;
...@@ -1321,10 +1321,9 @@ static int __init read_pagedir(struct pbe *pblist) ...@@ -1321,10 +1321,9 @@ static int __init read_pagedir(struct pbe *pblist)
} }
static int __init read_suspend_image(void) static int check_suspend_image(void)
{ {
int error = 0; int error = 0;
struct pbe *p;
if ((error = check_sig())) if ((error = check_sig()))
return error; return error;
...@@ -1332,6 +1331,14 @@ static int __init read_suspend_image(void) ...@@ -1332,6 +1331,14 @@ static int __init read_suspend_image(void)
if ((error = check_header())) if ((error = check_header()))
return error; return error;
return 0;
}
static int read_suspend_image(void)
{
int error = 0;
struct pbe *p;
if (!(p = alloc_pagedir(nr_copy_pages))) if (!(p = alloc_pagedir(nr_copy_pages)))
return -ENOMEM; return -ENOMEM;
...@@ -1362,30 +1369,72 @@ static int __init read_suspend_image(void) ...@@ -1362,30 +1369,72 @@ static int __init read_suspend_image(void)
} }
/** /**
* swsusp_read - Read saved image from swap. * swsusp_check - Check for saved image in swap
*/ */
int __init swsusp_read(void) int swsusp_check(void)
{ {
int error; int error;
if (!swsusp_resume_device) {
if (!strlen(resume_file)) if (!strlen(resume_file))
return -ENOENT; return -ENOENT;
swsusp_resume_device = name_to_dev_t(resume_file);
pr_debug("swsusp: Resume From Partition %s\n", resume_file);
} else {
pr_debug("swsusp: Resume From Partition %d:%d\n",
MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device));
}
resume_device = name_to_dev_t(resume_file); resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ);
pr_debug("swsusp: Resume From Partition: %s\n", resume_file);
resume_bdev = open_by_devnum(resume_device, FMODE_READ);
if (!IS_ERR(resume_bdev)) { if (!IS_ERR(resume_bdev)) {
set_blocksize(resume_bdev, PAGE_SIZE); set_blocksize(resume_bdev, PAGE_SIZE);
error = read_suspend_image(); error = check_suspend_image();
if (error)
blkdev_put(resume_bdev); blkdev_put(resume_bdev);
} else } else
error = PTR_ERR(resume_bdev); error = PTR_ERR(resume_bdev);
if (!error) if (!error)
pr_debug("Reading resume file was successful\n"); pr_debug("swsusp: resume file found\n");
else
pr_debug("swsusp: Error %d check for resume file\n", error);
return error;
}
/**
* swsusp_read - Read saved image from swap.
*/
int swsusp_read(void)
{
int error;
if (IS_ERR(resume_bdev)) {
pr_debug("swsusp: block device not initialised\n");
return PTR_ERR(resume_bdev);
}
error = read_suspend_image();
blkdev_put(resume_bdev);
if (!error)
pr_debug("swsusp: Reading resume file was successful\n");
else else
pr_debug("swsusp: Error %d resuming\n", error); pr_debug("swsusp: Error %d resuming\n", error);
return error; return error;
} }
/**
* swsusp_close - close swap device.
*/
void swsusp_close(void)
{
if (IS_ERR(resume_bdev)) {
pr_debug("swsusp: block device not initialised\n");
return;
}
blkdev_put(resume_bdev);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment