public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [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: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: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: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: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: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: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