public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Hannes Reinecke <hare@suse.de>
To: Pavel Machek <pavel@suse.cz>
Cc: Linux Kernel <linux-kernel@vger.kernel.org>, mjg59@srcf.ucam.org
Subject: Re: [PATCH] Resume from initramfs
Date: Mon, 31 Jan 2005 15:09:56 +0100	[thread overview]
Message-ID: <41FE3C34.4000200@suse.de> (raw)
In-Reply-To: <20050131125110.GD6279@elf.ucw.cz>

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

  reply	other threads:[~2005-01-31 14:11 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=41FE3C34.4000200@suse.de \
    --to=hare@suse.de \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mjg59@srcf.ucam.org \
    --cc=pavel@suse.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox