public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: "Rafael J. Wysocki" <rjw@sisk.pl>
To: Pavel Machek <pavel@ucw.cz>
Cc: LKML <linux-kernel@vger.kernel.org>, Andrew Morton <akpm@osdl.org>
Subject: [PATCH 3/3][Fix] swsusp: prevent swsusp from failing if there's too many pagedir pages
Date: Sun, 25 Sep 2005 20:44:00 +0200	[thread overview]
Message-ID: <200509252044.00928.rjw@sisk.pl> (raw)
In-Reply-To: <200509252018.36867.rjw@sisk.pl>

There's a silent assumption in swsusp that always
sizeof(struct swsusp_info) <= PAGE_SIZE, which is wrong, because
eg. on x86-64 sizeof(swp_entry_t) = 8.  This causes swsusp to skip some pagedir
pages while reading the image if there are too many of them (depending on the
architecture, approx. 500 on x86-64).

The following patch makes swsusp more flexible with this respect, by allocating
separate swap page(s) (as many as needed) for storing the swap offsets of
pagedir pages (without the patch they are all stored in a struct swsusp_info,
and there may be not enough room for them in this structure).

Please consider for applying,
Rafael


Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Index: linux-2.6.14-rc2-git3/kernel/power/power.h
===================================================================
--- linux-2.6.14-rc2-git3.orig/kernel/power/power.h	2005-09-25 18:42:31.000000000 +0200
+++ linux-2.6.14-rc2-git3/kernel/power/power.h	2005-09-25 18:56:17.000000000 +0200
@@ -9,6 +9,7 @@
 #define SUSPEND_CONSOLE	(MAX_NR_CONSOLES-1)
 #endif
 
+#define MAX_PB_SWP_PAGES	128
 
 struct swsusp_info {
 	struct new_utsname	uts;
@@ -18,10 +19,12 @@
 	unsigned long		image_pages;
 	unsigned long		pagedir_pages;
 	suspend_pagedir_t	* suspend_pagedir;
-	swp_entry_t		pagedir[768];
+	unsigned		pb_swp_pages;
+	swp_entry_t		pb_swp[MAX_PB_SWP_PAGES];
+	swp_entry_t		* pb_swp_mem[MAX_PB_SWP_PAGES];
 } __attribute__((aligned(PAGE_SIZE)));
 
-
+/* pb_swp_mem and pb_swp_pages are needed for error recovery */
 
 #ifdef CONFIG_SOFTWARE_SUSPEND
 extern int pm_suspend_disk(void);
Index: linux-2.6.14-rc2-git3/kernel/power/swsusp.c
===================================================================
--- linux-2.6.14-rc2-git3.orig/kernel/power/swsusp.c	2005-09-25 18:52:48.000000000 +0200
+++ linux-2.6.14-rc2-git3/kernel/power/swsusp.c	2005-09-25 19:10:16.000000000 +0200
@@ -496,10 +496,17 @@
 
 static void free_pagedir_entries(void)
 {
-	int i;
+	swp_entry_t **ptr, *entry;
+	int j, k;
 
-	for (i = 0; i < swsusp_info.pagedir_pages; i++)
-		swap_free(swsusp_info.pagedir[i]);
+	for (j = 0, ptr = swsusp_info.pb_swp_mem;
+	     j < swsusp_info.pb_swp_pages; j++, ptr++) {
+		for (k = 0, entry = *ptr;
+		     k < PAGE_SIZE/sizeof(swp_entry_t); k++, entry++)
+			if (entry->val)
+				swap_free(*entry);
+		free_page((unsigned long)*ptr);
+	}
 }
 
 
@@ -510,18 +517,49 @@
 
 static int write_pagedir(void)
 {
-	int error = 0;
-	unsigned n = 0;
+	int error = -EFAULT;
+	unsigned k, n, pages = 0;
 	struct pbe * pbe;
+	swp_entry_t * buf;
+
+	if (!(buf = (swp_entry_t *)get_zeroed_page(GFP_KERNEL)))
+		return -ENOMEM;
 
 	printk( "Writing pagedir...");
+	n = 0;
+	swsusp_info.pb_swp_mem[0] = buf;
+	swsusp_info.pb_swp_pages = 1;
+	k = 0;
 	for_each_pb_page (pbe, pagedir_nosave) {
-		if ((error = write_page((unsigned long)pbe, &swsusp_info.pagedir[n++])))
+		error = write_page((unsigned long)pbe, &buf[k++]);
+		if (!error) {
+			pages++;
+			if (k >= PAGE_SIZE/sizeof(swp_entry_t)) {
+				error = write_page((unsigned long)buf, &swsusp_info.pb_swp[n++]);
+				if (n >= MAX_PB_SWP_PAGES)
+					error = -ENOMEM;
+				if (!error) {
+					buf = (swp_entry_t *)get_zeroed_page(GFP_KERNEL);
+					if (buf) {
+						swsusp_info.pb_swp_mem[n] = buf;
+						swsusp_info.pb_swp_pages++;
+						k = 0;
+					} else
+						error = -ENOMEM;
+				}
+			}
+		}
+		if (error)
 			return error;
 	}
+	if (k > 0)
+		error = write_page((unsigned long)buf, &swsusp_info.pb_swp[n++]);
+
+	if (!error) {
+		swsusp_info.pagedir_pages = pages;
+		printk("done (%u pages)\n", pages);
+	}
 
-	swsusp_info.pagedir_pages = n;
-	printk("done (%u pages)\n", n);
 	return error;
 }
 
@@ -1414,33 +1452,58 @@
 static int read_pagedir(struct pbe *pblist)
 {
 	struct pbe *pbpage, *p;
-	unsigned i = 0;
-	int error;
+	unsigned k, n = 0, pages = 0;
+	int error = -EFAULT;
+	swp_entry_t * buf;
+	unsigned long offset;
 
 	if (!pblist)
 		return -EFAULT;
 
+	if (!(buf = (swp_entry_t *)get_zeroed_page(GFP_KERNEL)))
+		return -ENOMEM;
+
 	printk("swsusp: Reading pagedir (%lu pages)\n",
 			swsusp_info.pagedir_pages);
 
-	for_each_pb_page (pbpage, pblist) {
-		unsigned long offset = swp_offset(swsusp_info.pagedir[i++]);
+	if (!(offset = swp_offset(swsusp_info.pb_swp[n++])))
+		goto free_buf;
 
+	if ((error = bio_read_page(offset, (swp_entry_t *)buf)))
+		goto free_buf;
+
+	k = 0;
+	for_each_pb_page (pbpage, pblist) {
 		error = -EFAULT;
+		offset = swp_offset(buf[k++]);
 		if (offset) {
 			p = (pbpage + PB_PAGE_SKIP)->next;
 			error = bio_read_page(offset, (void *)pbpage);
 			(pbpage + PB_PAGE_SKIP)->next = p;
 		}
-		if (error)
+		if (!error) {
+			pages++;
+			if (k >= PAGE_SIZE/sizeof(swp_entry_t)) {
+				error = -EFAULT;
+				offset = swp_offset(swsusp_info.pb_swp[n++]);
+				if (offset) {
+					error = bio_read_page(offset, (swp_entry_t *)buf);
+					k = 0;
+				}
+				if (error)
+					break;
+			}
+		} else
 			break;
 	}
 
+free_buf:
 	if (error)
 		free_pagedir(pblist);
 	else
-		BUG_ON(i != swsusp_info.pagedir_pages);
+		BUG_ON(pages != swsusp_info.pagedir_pages);
 
+	free_page((unsigned long)buf);
 	return error;
 }
 

  parent reply	other threads:[~2005-09-25 18:45 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-09-25 18:18 [PATCH 0/3] swsusp: fix some obscure bugs Rafael J. Wysocki
2005-09-25 18:24 ` [PATCH 1/3][Fix] swsusp: remove wrong code from data_free Rafael J. Wysocki
2005-09-25 18:30 ` [PATCH 2/3][Fix] swsusp: prevent possible memory leak Rafael J. Wysocki
2005-09-25 18:44 ` Rafael J. Wysocki [this message]
2005-09-26 10:33   ` [PATCH 3/3][Fix] swsusp: prevent swsusp from failing if there's too many pagedir pages Pavel Machek
2005-09-26 11:11     ` Rafael J. Wysocki
2005-09-26 11:20       ` Pavel Machek
2005-09-26 12:54         ` Rafael J. Wysocki
2005-09-26 14:26           ` Pavel Machek
2005-09-26 19:19             ` Rafael J. Wysocki
2005-09-26 23:22               ` Pavel Machek
2005-09-26 19:29             ` [PATCH][Fix] swsusp: avoid problems if there are too many pages to save Rafael J. Wysocki
2005-09-26 23:14               ` Pavel Machek
2005-09-25 21:47 ` [PATCH 0/3] swsusp: fix some obscure bugs Pavel Machek
2005-09-25 21:59   ` Andrew Morton

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=200509252044.00928.rjw@sisk.pl \
    --to=rjw@sisk.pl \
    --cc=akpm@osdl.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pavel@ucw.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