* [PATCH] Resume from initramfs
@ 2005-01-31 12:30 Hannes Reinecke
2005-01-31 12:43 ` Matthew Garrett
2005-01-31 12:51 ` Pavel Machek
0 siblings, 2 replies; 10+ messages in thread
From: Hannes Reinecke @ 2005-01-31 12:30 UTC (permalink / raw)
To: Linux Kernel; +Cc: Pavel Machek, mjg59
[-- Attachment #1: Type: text/plain, Size: 1455 bytes --]
Hi all,
seeing as it is that the current software suspend allows suspending only
from built-in devices I've hacked the swsusp code to allow also for
manual resume. Thus it is now be capable to suspend to modular devices also.
This is actually based on a previous patch by mjg59 at scrf.ucam.org,
augmented by suggestions from Pavel Machek.
For a clean implementation I've split up the function swsusp_read() into
the distinct functions swsusp_check() and swsusp_read().
Furthermore the function prepare() has been split into
prepare_processes() and prepare_devices().
With this we now have the functionality to first check whether the
device really contains a resume image, then freeze all processes and
free some memory, read in the resume image and shut down all devices.
It actually makes checking for a resume image faster than the current
implementation.
resume can be started by 'echo <major>:<minor> > /sys/power/resume".
Note that this _only_ works from within initramfs when _no_ devices are
mounted. Otherwise resume will not be able to freeze the swapper task
and consequently fail.
And yes, it needs to be properly documented. Will do once the patch is
accepted in principle :-).
Oh, and the usual applies: works for me, might eat your disk, beware of
nasal demons.
Cheers,
Hannes
--
Dr. Hannes Reinecke hare@suse.de
SuSE Linux AG S390 & zSeries
Maxfeldstraße 5 +49 911 74053 688
90409 Nürnberg http://www.suse.de
[-- Attachment #2: swsusp-from-initramfs.patch --]
[-- Type: text/x-patch, Size: 10987 bytes --]
Subject: activate 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.
Signed-off-by: Hannes Reinecke <hare@suse.de>
--- linux-2.6.10/init/do_mounts.c.orig 2005-01-28 10:25:35.000000000 +0100
+++ linux-2.6.10/init/do_mounts.c 2005-01-28 10:30:43.000000000 +0100
@@ -53,7 +53,7 @@ static int __init readwrite(char *str)
__setup("ro", readonly);
__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 buf[32];
@@ -135,7 +135,7 @@ fail:
* 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 *p;
@@ -144,7 +144,8 @@ dev_t __init name_to_dev_t(char *name)
#ifdef CONFIG_SYSFS
int mkdir_err = sys_mkdir("/sys", 0700);
- if (sys_mount("sysfs", "/sys", "sysfs", 0, NULL) < 0)
+ int mount_err = sys_mount("sysfs", "/sys", "sysfs", 0, NULL);
+ if (mount_err < 0 && mount_err != -EBUSY)
goto out;
#endif
@@ -196,7 +197,8 @@ dev_t __init name_to_dev_t(char *name)
res = try_name(s, part);
done:
#ifdef CONFIG_SYSFS
- sys_umount("/sys", 0);
+ if (!mount_err)
+ sys_umount("/sys", 0);
out:
if (!mkdir_err)
sys_rmdir("/sys");
--- linux-2.6.10/kernel/power/disk.c.orig 2005-01-28 10:25:28.000000000 +0100
+++ linux-2.6.10/kernel/power/disk.c 2005-01-31 11:59:04.308199464 +0100
@@ -9,6 +9,8 @@
*
*/
+#define DEBUG
+
#include <linux/suspend.h>
#include <linux/syscalls.h>
#include <linux/reboot.h>
@@ -25,6 +27,7 @@ extern struct pm_ops * pm_ops;
extern int swsusp_suspend(void);
extern int swsusp_write(void);
+extern int swsusp_check(void);
extern int swsusp_read(void);
extern int swsusp_resume(void);
extern int swsusp_free(void);
@@ -32,6 +35,7 @@ extern int swsusp_free(void);
static int noresume = 0;
char resume_file[256] = CONFIG_PM_STD_PARTITION;
+dev_t swsusp_resume_device;
/**
* power_down - Shut machine down for hibernate.
@@ -121,45 +125,54 @@ static void finish(void)
}
-static int prepare(void)
+static int prepare_processes(void)
{
int error;
pm_prepare_console();
sys_sync();
- if (freeze_processes()) {
+
+ if (freeze_processes() > 1) {
error = -EBUSY;
- goto Thaw;
+ return error;
}
if (pm_disk_mode == PM_DISK_PLATFORM) {
if (pm_ops && pm_ops->prepare) {
if ((error = pm_ops->prepare(PM_SUSPEND_DISK)))
- goto Thaw;
+ return error;
}
}
/* Free memory before shutting down devices. */
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();
if ((error = device_suspend(PMSG_FREEZE))) {
printk("Some devices failed to suspend\n");
- goto Finish;
+ platform_finish();
+ enable_nonboot_cpus();
+ return error;
}
return 0;
- Finish:
- platform_finish();
- Thaw:
- enable_nonboot_cpus();
- thaw_processes();
- pm_restore_console();
- return error;
}
-
/**
* pm_suspend_disk - The granpappy of power management.
*
@@ -173,8 +186,15 @@ int pm_suspend_disk(void)
{
int error;
- if ((error = prepare()))
+ error = prepare_processes();
+ if (!error) {
+ error = prepare_devices();
+ }
+
+ if (error) {
+ unprepare_processes();
return error;
+ }
pr_debug("PM: Attempting to suspend to disk.\n");
if (pm_disk_mode == PM_DISK_FIRMWARE)
@@ -223,15 +243,28 @@ static int software_resume(void)
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())) {
+ unprepare_processes();
+ goto Done;
+ }
+
pr_debug("PM: Reading swsusp image.\n");
if ((error = swsusp_read()))
goto Done;
- 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;
+ }
barrier();
mb();
@@ -329,8 +362,43 @@ static ssize_t disk_store(struct subsyst
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;
+ set_current_state(TASK_STOPPED);
+ software_resume();
+ set_current_state(TASK_RUNNING);
+ }
+ }
+
+ return error >= 0 ? n : error;
+}
+
+power_attr(resume);
+
static struct attribute * g[] = {
&disk_attr.attr,
+ &resume_attr.attr,
NULL,
};
--- linux-2.6.10/kernel/power/swsusp.c.orig 2005-01-28 10:25:28.000000000 +0100
+++ linux-2.6.10/kernel/power/swsusp.c 2005-01-28 16:45:14.000000000 +0100
@@ -36,6 +36,8 @@
* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
*/
+#define DEBUG
+
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/suspend.h>
@@ -80,7 +82,7 @@ static int pagedir_order_check;
static int nr_copy_pages_check;
extern char resume_file[];
-static dev_t resume_device;
+
/* Local variables that should not be affected by save */
unsigned int nr_copy_pages __nosavedata = 0;
@@ -170,7 +172,7 @@ static int is_resume_device(const struct
struct inode *inode = file->f_dentry->d_inode;
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 */
@@ -898,7 +900,7 @@ int swsusp_resume(void)
/*
* 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;
@@ -912,7 +914,7 @@ static int __init does_collide_order(uns
* 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;
@@ -930,7 +932,7 @@ static int __init check_pagedir(void)
return 0;
}
-static int __init swsusp_pagedir_relocate(void)
+static int swsusp_pagedir_relocate(void)
{
/*
* We have to avoid recursion (not to overflow kernel stack),
@@ -1075,7 +1077,7 @@ static int bio_write_page(pgoff_t page_o
* 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();
if(swsusp_info.version_code != LINUX_VERSION_CODE)
@@ -1096,7 +1098,7 @@ static const char * __init sanity_check(
}
-static int __init check_header(void)
+static int check_header(void)
{
const char * reason = NULL;
int error;
@@ -1114,7 +1116,7 @@ static int __init check_header(void)
return error;
}
-static int __init check_sig(void)
+static int check_sig(void)
{
int error;
@@ -1144,7 +1146,7 @@ static int __init check_sig(void)
* already did that.
*/
-static int __init data_read(void)
+static int data_read(void)
{
struct pbe * p;
int error;
@@ -1169,9 +1171,9 @@ static int __init data_read(void)
}
-extern dev_t __init name_to_dev_t(const char *line);
+extern dev_t name_to_dev_t(const char *line);
-static int __init read_pagedir(void)
+static int read_pagedir(void)
{
unsigned long addr;
int i, n = swsusp_info.pagedir_pages;
@@ -1196,7 +1198,7 @@ static int __init read_pagedir(void)
return error;
}
-static int __init read_suspend_image(void)
+static int check_suspend_image(void)
{
int error = 0;
@@ -1204,6 +1206,13 @@ static int __init read_suspend_image(voi
return error;
if ((error = check_header()))
return error;
+ return error;
+}
+
+static int read_suspend_image(void)
+{
+ int error = 0;
+
if ((error = read_pagedir()))
return error;
if ((error = data_read()))
@@ -1212,29 +1221,56 @@ static int __init read_suspend_image(voi
}
/**
- * 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;
- if (!strlen(resume_file))
- return -ENOENT;
-
- resume_device = name_to_dev_t(resume_file);
- pr_debug("swsusp: Resume From Partition: %s\n", resume_file);
+ if (!swsusp_resume_device) {
+ if (!strlen(resume_file))
+ 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_bdev = open_by_devnum(resume_device, FMODE_READ);
+ resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ);
if (!IS_ERR(resume_bdev)) {
set_blocksize(resume_bdev, PAGE_SIZE);
- error = read_suspend_image();
- blkdev_put(resume_bdev);
+ if((error = check_suspend_image()))
+ blkdev_put(resume_bdev);
} else
error = PTR_ERR(resume_bdev);
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
pr_debug("swsusp: Error %d resuming\n", error);
return error;
--- linux-2.6.10/include/linux/suspend.h.orig 2005-01-28 10:25:28.000000000 +0100
+++ linux-2.6.10/include/linux/suspend.h 2005-01-28 10:30:43.000000000 +0100
@@ -25,6 +25,8 @@ typedef struct pbe {
#define SUSPEND_PD_PAGES(x) (((x)*sizeof(struct pbe))/PAGE_SIZE+1)
+
+extern dev_t swsusp_resume_device;
/* mm/vmscan.c */
extern int shrink_mem(void);
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH] Resume from initramfs
2005-01-31 12:30 [PATCH] Resume from initramfs Hannes Reinecke
@ 2005-01-31 12:43 ` Matthew Garrett
2005-01-31 13:15 ` Hannes Reinecke
2005-01-31 12:51 ` Pavel Machek
1 sibling, 1 reply; 10+ messages in thread
From: Matthew Garrett @ 2005-01-31 12:43 UTC (permalink / raw)
To: Hannes Reinecke; +Cc: Linux Kernel, Pavel Machek
One thing - if swsusp_read() fails (eg, due to there not actually being
a suspend image), the processes will have been frozen but not woken up.
The failure path in software_resume needs to call thaw_processes before
exiting.
--
Matthew Garrett | mjg59@srcf.ucam.org
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Resume from initramfs
2005-01-31 12:43 ` Matthew Garrett
@ 2005-01-31 13:15 ` Hannes Reinecke
0 siblings, 0 replies; 10+ messages in thread
From: Hannes Reinecke @ 2005-01-31 13:15 UTC (permalink / raw)
To: Matthew Garrett; +Cc: Linux Kernel, Pavel Machek
Matthew Garrett wrote:
> One thing - if swsusp_read() fails (eg, due to there not actually being
> a suspend image), the processes will have been frozen but not woken up.
> The failure path in software_resume needs to call thaw_processes before
> exiting.
>
You are, of course, correct. Will be fixing it.
Cheers,
Hannes
--
Dr. Hannes Reinecke hare@suse.de
SuSE Linux AG S390 & zSeries
Maxfeldstraße 5 +49 911 74053 688
90409 Nürnberg http://www.suse.de
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Resume from initramfs
2005-01-31 12:30 [PATCH] Resume from initramfs Hannes Reinecke
2005-01-31 12:43 ` Matthew Garrett
@ 2005-01-31 12:51 ` Pavel Machek
2005-01-31 14:09 ` Hannes Reinecke
1 sibling, 1 reply; 10+ messages in thread
From: Pavel Machek @ 2005-01-31 12:51 UTC (permalink / raw)
To: Hannes Reinecke; +Cc: Linux Kernel, mjg59
Hi!
> seeing as it is that the current software suspend allows suspending only
> from built-in devices I've hacked the swsusp code to allow also for
> manual resume. Thus it is now be capable to suspend to modular devices also.
> This is actually based on a previous patch by mjg59 at scrf.ucam.org,
> augmented by suggestions from Pavel Machek.
> For a clean implementation I've split up the function swsusp_read() into
> the distinct functions swsusp_check() and swsusp_read().
> Furthermore the function prepare() has been split into
> prepare_processes() and prepare_devices().
> With this we now have the functionality to first check whether the
> device really contains a resume image, then freeze all processes and
> free some memory, read in the resume image and shut down all devices.
> It actually makes checking for a resume image faster than the current
> implementation.
>
> resume can be started by 'echo <major>:<minor> > /sys/power/resume".
> Note that this _only_ works from within initramfs when _no_ devices are
> mounted. Otherwise resume will not be able to freeze the swapper task
> and consequently fail.
> And yes, it needs to be properly documented. Will do once the patch is
> accepted in principle :-).
In priciple it looks okay, but minor details still need to be ironed
out.
> Oh, and the usual applies: works for me, might eat your disk, beware of
> nasal demons.
:-) Please try to inline patches, it makes it easier to reply to
them.
At one point you did something like
read_data()
blk_device_put()
If read_data does blk_device_get(), it should also do the put()
itself. Otherwise some caller will forget it.
Pavel
> --- linux-2.6.10/init/do_mounts.c.orig 2005-01-28 10:25:35.000000000 +0100
> +++ linux-2.6.10/init/do_mounts.c 2005-01-28 10:30:43.000000000 +0100
> @@ -135,7 +135,7 @@ fail:
> * 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 *p;
Why do you need this one? /sys/power/resume accepts numeric values, it
should not need to translate...
> @@ -144,7 +144,8 @@ dev_t __init name_to_dev_t(char *name)
>
> #ifdef CONFIG_SYSFS
> int mkdir_err = sys_mkdir("/sys", 0700);
> - if (sys_mount("sysfs", "/sys", "sysfs", 0, NULL) < 0)
> + int mount_err = sys_mount("sysfs", "/sys", "sysfs", 0, NULL);
> + if (mount_err < 0 && mount_err != -EBUSY)
> goto out;
> #endif
>
This is probably not acceptable. Why do you need it? It should be
easily doable from initrd.
> --- linux-2.6.10/kernel/power/disk.c.orig 2005-01-28 10:25:28.000000000 +0100
> +++ linux-2.6.10/kernel/power/disk.c 2005-01-31 11:59:04.308199464 +0100
> @@ -121,45 +125,54 @@ static void finish(void)
> }
>
>
> -static int prepare(void)
> +static int prepare_processes(void)
> {
> int error;
>
> pm_prepare_console();
>
> sys_sync();
> - if (freeze_processes()) {
> +
> + if (freeze_processes() > 1) {
> error = -EBUSY;
> - goto Thaw;
> + return error;
> }
What does freeze_processes() == 1 mean and why is it suddenly ok?
> - 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;
> + }
I'd not add parenthesis for single command....
> + 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;
> + set_current_state(TASK_STOPPED);
> + software_resume();
> + set_current_state(TASK_RUNNING);
Ugh, now that is "interesting" hack. You set yourself as stopped to
avoid refrigerator.... Is it really needed?
> --- linux-2.6.10/kernel/power/swsusp.c.orig 2005-01-28 10:25:28.000000000 +0100
> +++ linux-2.6.10/kernel/power/swsusp.c 2005-01-28 16:45:14.000000000 +0100
> + } else {
> + pr_debug("swsusp: Resume From Partition %d:%d\n",
> + MAJOR(swsusp_resume_device),MINOR(swsusp_resume_device));
Missing space after ",".
> - resume_bdev = open_by_devnum(resume_device, FMODE_READ);
> + resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ);
> if (!IS_ERR(resume_bdev)) {
> set_blocksize(resume_bdev, PAGE_SIZE);
> - error = read_suspend_image();
> - blkdev_put(resume_bdev);
> + if((error = check_suspend_image()))
> + blkdev_put(resume_bdev);
Please put space after "if" and fix formatting here.
Pavel
--
People were complaining that M$ turns users into beta-testers...
...jr ghea gurz vagb qrirybcref, naq gurl frrz gb yvxr vg gung jnl!
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH] Resume from initramfs
2005-01-31 12:51 ` Pavel Machek
@ 2005-01-31 14:09 ` Hannes Reinecke
2005-01-31 14:18 ` Matthew Garrett
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Hannes Reinecke @ 2005-01-31 14:09 UTC (permalink / raw)
To: Pavel Machek; +Cc: Linux Kernel, mjg59
Pavel Machek wrote:
> Hi!
>
[ .. ]
>
> In priciple it looks okay, but minor details still need to be ironed
> out.
>
>
Cool. THX.
> At one point you did something like
>
> read_data()
> blk_device_put()
>
> If read_data does blk_device_get(), it should also do the put()
> itself. Otherwise some caller will forget it.
>
Not so easy. blkdev_put() is done implicitely by open_by_devnum().
So if that fails we have to do it by hand (cf. swsusp_check()).
And I'll keep the reference to the block device open to avoid duplicate
checking in swsusp_read(). So I'll have to close it there as the block
device is local to swsusp.c.
But you are correct in another point, in that there is an error path
when prepare_processes fails we have to drop the reference again.
>>--- linux-2.6.10/init/do_mounts.c.orig 2005-01-28 10:25:35.000000000 +0100
>>+++ linux-2.6.10/init/do_mounts.c 2005-01-28 10:30:43.000000000 +0100
>>@@ -135,7 +135,7 @@ fail:
>> * 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 *p;
>
>
> Why do you need this one? /sys/power/resume accepts numeric values, it
> should not need to translate...
>
swsusp_check is used by both entry points, and is itself not a init
function.
I simply found it bad style to reference a __init function from there.
And name_to_dev_t is evil in itself. I'd gladly be rid of it if possible.
>
>>@@ -144,7 +144,8 @@ dev_t __init name_to_dev_t(char *name)
>>
>> #ifdef CONFIG_SYSFS
>> int mkdir_err = sys_mkdir("/sys", 0700);
>>- if (sys_mount("sysfs", "/sys", "sysfs", 0, NULL) < 0)
>>+ int mount_err = sys_mount("sysfs", "/sys", "sysfs", 0, NULL);
>>+ if (mount_err < 0 && mount_err != -EBUSY)
>> goto out;
>> #endif
>>
>
>
> This is probably not acceptable. Why do you need it? It should be
> easily doable from initrd.
>
If /sys is mounted the device lookup will fail. How sane is that?
But yes, it doesn't belong to this patch, so I'll be sending a separate
one for it.
>
[ .. ]
>
> What does freeze_processes() == 1 mean and why is it suddenly ok?
>
>
Sorry, leftover. Cleaned up.
>>+ 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;
>>+ set_current_state(TASK_STOPPED);
>>+ software_resume();
>>+ set_current_state(TASK_RUNNING);
>
>
> Ugh, now that is "interesting" hack. You set yourself as stopped to
> avoid refrigerator.... Is it really needed?
>
No, again a leftover.
How about this version? Better?
Cheers,
Hannes
--
Subject: activate resume from initramfs
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 only be used from initramfs; other usage will most likely not
work.
Signed-off-by: Hannes Reinecke <hare@suse.de>
--- linux-2.6.10/init/do_mounts.c.orig 2005-01-31 13:54:17.000000000 +0100
+++ linux-2.6.10/init/do_mounts.c 2005-01-31 14:46:08.000000000 +0100
@@ -53,7 +53,7 @@ static int __init readwrite(char *str)
__setup("ro", readonly);
__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 buf[32];
@@ -135,7 +135,7 @@ fail:
* 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 *p;
--- linux-2.6.10/kernel/power/disk.c.orig 2005-01-31 13:54:17.000000000
+0100
+++ linux-2.6.10/kernel/power/disk.c 2005-01-31 14:55:14.000000000 +0100
@@ -9,6 +9,8 @@
*
*/
+#define DEBUG
+
#include <linux/suspend.h>
#include <linux/syscalls.h>
#include <linux/reboot.h>
@@ -25,13 +27,16 @@ extern struct pm_ops * pm_ops;
extern int swsusp_suspend(void);
extern int swsusp_write(void);
+extern int swsusp_check(void);
extern int swsusp_read(void);
+extern void swsusp_close(void);
extern int swsusp_resume(void);
extern int swsusp_free(void);
static int noresume = 0;
char resume_file[256] = CONFIG_PM_STD_PARTITION;
+dev_t swsusp_resume_device;
/**
* power_down - Shut machine down for hibernate.
@@ -121,45 +126,54 @@ static void finish(void)
}
-static int prepare(void)
+static int prepare_processes(void)
{
int error;
pm_prepare_console();
sys_sync();
+
if (freeze_processes()) {
error = -EBUSY;
- goto Thaw;
+ return error;
}
if (pm_disk_mode == PM_DISK_PLATFORM) {
if (pm_ops && pm_ops->prepare) {
if ((error = pm_ops->prepare(PM_SUSPEND_DISK)))
- goto Thaw;
+ return error;
}
}
/* Free memory before shutting down devices. */
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();
if ((error = device_suspend(PMSG_FREEZE))) {
printk("Some devices failed to suspend\n");
- goto Finish;
+ platform_finish();
+ enable_nonboot_cpus();
+ return error;
}
return 0;
- Finish:
- platform_finish();
- Thaw:
- enable_nonboot_cpus();
- thaw_processes();
- pm_restore_console();
- return error;
}
-
/**
* pm_suspend_disk - The granpappy of power management.
*
@@ -173,8 +187,15 @@ int pm_suspend_disk(void)
{
int error;
- if ((error = prepare()))
+ error = prepare_processes();
+ if (!error) {
+ error = prepare_devices();
+ }
+
+ if (error) {
+ unprepare_processes();
return error;
+ }
pr_debug("PM: Attempting to suspend to disk.\n");
if (pm_disk_mode == PM_DISK_FIRMWARE)
@@ -223,14 +244,26 @@ static int software_resume(void)
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");
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;
barrier();
@@ -242,6 +275,8 @@ static int software_resume(void)
finish();
Free:
swsusp_free();
+ Cleanup:
+ unprepare_processes();
Done:
pr_debug("PM: Resume from disk failed.\n");
return 0;
@@ -329,8 +364,41 @@ static ssize_t disk_store(struct subsyst
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[] = {
&disk_attr.attr,
+ &resume_attr.attr,
NULL,
};
--- linux-2.6.10/kernel/power/swsusp.c.orig 2005-01-31
13:54:17.000000000 +0100
+++ linux-2.6.10/kernel/power/swsusp.c 2005-01-31 14:53:36.000000000 +0100
@@ -36,6 +36,8 @@
* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
*/
+#define DEBUG
+
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/suspend.h>
@@ -80,7 +82,7 @@ static int pagedir_order_check;
static int nr_copy_pages_check;
extern char resume_file[];
-static dev_t resume_device;
+
/* Local variables that should not be affected by save */
unsigned int nr_copy_pages __nosavedata = 0;
@@ -170,7 +172,7 @@ static int is_resume_device(const struct
struct inode *inode = file->f_dentry->d_inode;
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 */
@@ -898,7 +900,7 @@ int swsusp_resume(void)
/*
* 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;
@@ -912,7 +914,7 @@ static int __init does_collide_order(uns
* 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;
@@ -930,7 +932,7 @@ static int __init check_pagedir(void)
return 0;
}
-static int __init swsusp_pagedir_relocate(void)
+static int swsusp_pagedir_relocate(void)
{
/*
* We have to avoid recursion (not to overflow kernel stack),
@@ -1075,7 +1077,7 @@ static int bio_write_page(pgoff_t page_o
* 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();
if(swsusp_info.version_code != LINUX_VERSION_CODE)
@@ -1096,7 +1098,7 @@ static const char * __init sanity_check(
}
-static int __init check_header(void)
+static int check_header(void)
{
const char * reason = NULL;
int error;
@@ -1114,7 +1116,7 @@ static int __init check_header(void)
return error;
}
-static int __init check_sig(void)
+static int check_sig(void)
{
int error;
@@ -1144,7 +1146,7 @@ static int __init check_sig(void)
* already did that.
*/
-static int __init data_read(void)
+static int data_read(void)
{
struct pbe * p;
int error;
@@ -1169,9 +1171,9 @@ static int __init data_read(void)
}
-extern dev_t __init name_to_dev_t(const char *line);
+extern dev_t name_to_dev_t(const char *line);
-static int __init read_pagedir(void)
+static int read_pagedir(void)
{
unsigned long addr;
int i, n = swsusp_info.pagedir_pages;
@@ -1196,7 +1198,7 @@ static int __init read_pagedir(void)
return error;
}
-static int __init read_suspend_image(void)
+static int check_suspend_image(void)
{
int error = 0;
@@ -1204,6 +1206,13 @@ static int __init read_suspend_image(voi
return error;
if ((error = check_header()))
return error;
+ return error;
+}
+
+static int read_suspend_image(void)
+{
+ int error = 0;
+
if ((error = read_pagedir()))
return error;
if ((error = data_read()))
@@ -1212,30 +1221,74 @@ static int __init read_suspend_image(voi
}
/**
- * 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;
- if (!strlen(resume_file))
- return -ENOENT;
-
- resume_device = name_to_dev_t(resume_file);
- pr_debug("swsusp: Resume From Partition: %s\n", resume_file);
+ if (!swsusp_resume_device) {
+ if (!strlen(resume_file))
+ 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_bdev = open_by_devnum(resume_device, FMODE_READ);
+ resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ);
if (!IS_ERR(resume_bdev)) {
set_blocksize(resume_bdev, PAGE_SIZE);
- error = read_suspend_image();
- blkdev_put(resume_bdev);
+ error = check_suspend_image();
+ if (error)
+ blkdev_put(resume_bdev);
} else
error = PTR_ERR(resume_bdev);
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
pr_debug("swsusp: Error %d resuming\n", error);
return error;
}
+
+/**
+ * swsusp_close - close swap device.
+ */
+
+int swsusp_close(void)
+{
+ int error;
+
+ if (IS_ERR(resume_bdev)) {
+ pr_debug("swsusp: block device not initialised\n");
+ return;
+ }
+
+ blkdev_put(resume_bdev);
+}
--- linux-2.6.10/include/linux/suspend.h.orig 2005-01-31
13:54:17.000000000 +0100
+++ linux-2.6.10/include/linux/suspend.h 2005-01-31 13:54:33.000000000 +0100
@@ -25,6 +25,8 @@ typedef struct pbe {
#define SUSPEND_PD_PAGES(x) (((x)*sizeof(struct pbe))/PAGE_SIZE+1)
+
+extern dev_t swsusp_resume_device;
/* mm/vmscan.c */
extern int shrink_mem(void);
--
Dr. Hannes Reinecke hare@suse.de
SuSE Linux AG S390 & zSeries
Maxfeldstraße 5 +49 911 74053 688
90409 Nürnberg http://www.suse.de
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH] Resume from initramfs
2005-01-31 14:09 ` Hannes Reinecke
@ 2005-01-31 14:18 ` Matthew Garrett
2005-01-31 14:26 ` Hannes Reinecke
2005-01-31 14:26 ` Andreas Schwab
2005-01-31 18:15 ` Pavel Machek
2 siblings, 1 reply; 10+ messages in thread
From: Matthew Garrett @ 2005-01-31 14:18 UTC (permalink / raw)
To: Hannes Reinecke; +Cc: Pavel Machek, Linux Kernel
On Mon, 2005-01-31 at 15:09 +0100, Hannes Reinecke wrote:
> swsusp_check is used by both entry points, and is itself not a init
> function.
> I simply found it bad style to reference a __init function from there.
> And name_to_dev_t is evil in itself. I'd gladly be rid of it if possible.
name_to_dev_t won't work once userspace has started - you need to
set_fs(KERNEL_DS) at least one of the calls in it, IIRC.
--
Matthew Garrett | mjg59@srcf.ucam.org
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Resume from initramfs
2005-01-31 14:18 ` Matthew Garrett
@ 2005-01-31 14:26 ` Hannes Reinecke
0 siblings, 0 replies; 10+ messages in thread
From: Hannes Reinecke @ 2005-01-31 14:26 UTC (permalink / raw)
To: Matthew Garrett; +Cc: Pavel Machek, Linux Kernel
Matthew Garrett wrote:
> On Mon, 2005-01-31 at 15:09 +0100, Hannes Reinecke wrote:
>
>
>>swsusp_check is used by both entry points, and is itself not a init
>>function.
>>I simply found it bad style to reference a __init function from there.
>>And name_to_dev_t is evil in itself. I'd gladly be rid of it if possible.
>
>
> name_to_dev_t won't work once userspace has started - you need to
> set_fs(KERNEL_DS) at least one of the calls in it, IIRC.
>
I'm not advocating to use it.
But referencing a non-existing function is just plain evil.
We should better seperate both entry points to do the necessary device
resolution themselves before calling generic functions.
Cheers,
Hannes
--
Dr. Hannes Reinecke hare@suse.de
SuSE Linux AG S390 & zSeries
Maxfeldstraße 5 +49 911 74053 688
90409 Nürnberg http://www.suse.de
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Resume from initramfs
2005-01-31 14:09 ` Hannes Reinecke
2005-01-31 14:18 ` Matthew Garrett
@ 2005-01-31 14:26 ` Andreas Schwab
2005-01-31 14:28 ` Hannes Reinecke
2005-01-31 18:15 ` Pavel Machek
2 siblings, 1 reply; 10+ messages in thread
From: Andreas Schwab @ 2005-01-31 14:26 UTC (permalink / raw)
To: Hannes Reinecke; +Cc: Pavel Machek, Linux Kernel, mjg59
Hannes Reinecke <hare@suse.de> writes:
> --- linux-2.6.10/kernel/power/disk.c.orig 2005-01-31
> 13:54:17.000000000 +0100
> +++ linux-2.6.10/kernel/power/disk.c 2005-01-31 14:55:14.000000000 +0100
> @@ -9,6 +9,8 @@
> *
> */
>
> +#define DEBUG
> +
> #include <linux/suspend.h>
> #include <linux/syscalls.h>
> #include <linux/reboot.h>
> --- linux-2.6.10/kernel/power/swsusp.c.orig 2005-01-31
> 13:54:17.000000000 +0100
> +++ linux-2.6.10/kernel/power/swsusp.c 2005-01-31 14:53:36.000000000 +0100
> @@ -36,6 +36,8 @@
> * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
> */
>
> +#define DEBUG
> +
> #include <linux/module.h>
> #include <linux/mm.h>
> #include <linux/suspend.h>
Another leftovers?
Andreas.
--
Andreas Schwab, SuSE Labs, schwab@suse.de
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Resume from initramfs
2005-01-31 14:26 ` Andreas Schwab
@ 2005-01-31 14:28 ` Hannes Reinecke
0 siblings, 0 replies; 10+ messages in thread
From: Hannes Reinecke @ 2005-01-31 14:28 UTC (permalink / raw)
To: Andreas Schwab; +Cc: Pavel Machek, Linux Kernel, mjg59
Andreas Schwab wrote:
> Hannes Reinecke <hare@suse.de> writes:
>
>
>>--- linux-2.6.10/kernel/power/disk.c.orig 2005-01-31
>> 13:54:17.000000000 +0100
>>+++ linux-2.6.10/kernel/power/disk.c 2005-01-31 14:55:14.000000000 +0100
>>@@ -9,6 +9,8 @@
>> *
>> */
>>
>>+#define DEBUG
>>+
>> #include <linux/suspend.h>
>> #include <linux/syscalls.h>
>> #include <linux/reboot.h>
>>--- linux-2.6.10/kernel/power/swsusp.c.orig 2005-01-31
>> 13:54:17.000000000 +0100
>>+++ linux-2.6.10/kernel/power/swsusp.c 2005-01-31 14:53:36.000000000 +0100
>>@@ -36,6 +36,8 @@
>> * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
>> */
>>
>>+#define DEBUG
>>+
>> #include <linux/module.h>
>> #include <linux/mm.h>
>> #include <linux/suspend.h>
>
>
> Another leftovers?
>
> Andreas.
>
Yes. I'll clean that up once someone (ie Pavel) gives his thumbs-up.
Doesn't do any harm, really. swsusp in itself is chatty enough, two
additional lines don't really matter.
Cheers,
Hannes
--
Dr. Hannes Reinecke hare@suse.de
SuSE Linux AG S390 & zSeries
Maxfeldstraße 5 +49 911 74053 688
90409 Nürnberg http://www.suse.de
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Resume from initramfs
2005-01-31 14:09 ` Hannes Reinecke
2005-01-31 14:18 ` Matthew Garrett
2005-01-31 14:26 ` Andreas Schwab
@ 2005-01-31 18:15 ` Pavel Machek
2 siblings, 0 replies; 10+ messages in thread
From: Pavel Machek @ 2005-01-31 18:15 UTC (permalink / raw)
To: Hannes Reinecke; +Cc: Linux Kernel, mjg59
Hi!
> >>--- linux-2.6.10/init/do_mounts.c.orig 2005-01-28
> >>10:25:35.000000000 +0100
> >>+++ linux-2.6.10/init/do_mounts.c 2005-01-28 10:30:43.000000000 +0100
> >>@@ -135,7 +135,7 @@ fail:
> >> * 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 *p;
> >
> >
> >Why do you need this one? /sys/power/resume accepts numeric values, it
> >should not need to translate...
> >
> swsusp_check is used by both entry points, and is itself not a init
> function.
> I simply found it bad style to reference a __init function from there.
> And name_to_dev_t is evil in itself. I'd gladly be rid of it if
> possible.
Can you do name_to_dev_t during resume= parsing? That's always done
during early boot...
> How about this version? Better?
Yes, a bit :-). We still need the docs :-).
Pavel
> @@ -121,45 +126,54 @@ static void finish(void)
> }
>
>
> -static int prepare(void)
> +static int prepare_processes(void)
> {
> int error;
>
> pm_prepare_console();
>
> sys_sync();
> +
> if (freeze_processes()) {
> error = -EBUSY;
> - goto Thaw;
> + return error;
> }
>
> if (pm_disk_mode == PM_DISK_PLATFORM) {
> if (pm_ops && pm_ops->prepare) {
> if ((error = pm_ops->prepare(PM_SUSPEND_DISK)))
> - goto Thaw;
> + return error;
> }
> }
If freezing processes fails, it returns with processes running, here
it returns with processes frozen. Bug?
Pavel
--
People were complaining that M$ turns users into beta-testers...
...jr ghea gurz vagb qrirybcref, naq gurl frrz gb yvxr vg gung jnl!
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2005-01-31 18:16 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-01-31 12:30 [PATCH] Resume from initramfs Hannes Reinecke
2005-01-31 12:43 ` Matthew Garrett
2005-01-31 13:15 ` Hannes Reinecke
2005-01-31 12:51 ` Pavel Machek
2005-01-31 14:09 ` Hannes Reinecke
2005-01-31 14:18 ` Matthew Garrett
2005-01-31 14:26 ` Hannes Reinecke
2005-01-31 14:26 ` Andreas Schwab
2005-01-31 14:28 ` Hannes Reinecke
2005-01-31 18:15 ` Pavel Machek
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox