Skip to contentSkip to conversation

swsusp: do not use higher order memory allocations on suspend

RFC PATCH In review Rafael J. Wysocki 3 participants 7 messages Jan 28, 2005 – Jan 31, 2005 3 messages not in the archive

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 swsusp: do not use higher order memory allocations on suspend

Hi,

The following patch is (yet) an(other) attempt to eliminate the need for using higher order memory allocations on suspend. It accomplishes this by replacing the array of page backup entries with a list, so it is only necessary to allocate individual memory pages.

I have noticed that the suspend code in swsusp is actually independent of the resume code and vice versa, so it is not necessary to change them both at once. Therefore I have decided, for now, to only modify the suspend part which is more straightforward. The resume code is almost unaffected by this patch (the only thing changed in this code is a BUG_ON() that is removed).

The patch is against 2.6.11-rc2. It has been tested and quite thoroughly debugged on x86-64. Still, if anyone could test it on another architecture, I'd be grateful. Of course comments, suggestions etc. are welcome.

Greets,
RJW

include/linux/suspend.h +12 -3
diff -Nru linux-2.6.11-rc2-orig/include/linux/suspend.h linux-2.6.11-rc2/include/linux/suspend.h
@@ -16,11 +16,20 @@
 	unsigned long address;		/* address of the copy */
 	unsigned long orig_address;	/* original address of page */
 	swp_entry_t swap_address;	
-	swp_entry_t dummy;		/* we need scratch space at 
-					 * end of page (see link, diskpage)
-					 */
+
+	struct pbe *next;
 } suspend_pagedir_t;
 
+#define for_each_pbe(pbe, pblist) \
+	for (pbe = pblist ; pbe ; pbe = pbe->next)
+
+#define PBES_PER_PAGE      (PAGE_SIZE/sizeof(struct pbe))
+#define PB_PAGE_SKIP       (PBES_PER_PAGE-1)
+
+#define for_each_pb_page(pbe, pblist) \
+	for (pbe = pblist ; pbe ; pbe = (pbe+PB_PAGE_SKIP)->next)
+
+
 #define SWAP_FILENAME_MAXLENGTH	32
 
 
kernel/power/swsusp.c +102 -73

319 lines not on this page · raw message

Signature · 3 lines
- Would you tell me, please, which way I ought to go from here?
- That depends a good deal on where you want to get to.
		-- Lewis Carroll "Alice's Adventures in Wonderland"
#1 lore raw
Quoted 5 lines
kernel/power/swsusp.c +0 -1
@@ -373,15 +377,22 @@
 
 static int write_pagedir(void)
 {
-	unsigned long addr = (unsigned long)pagedir_nosave;

kernel/power/swsusp.c line 387

Quoted 10 lines
kernel/power/swsusp.c +6 -2
 	int error = 0;
-	int n = SUSPEND_PD_PAGES(nr_copy_pages);
-	int i;
+	unsigned n = 0;
+	struct pbe * pbe;
+
+	printk( "Writing pagedir ...");
 
+
+	pr_debug("\b\b\bdone (%u pages)\n", n);

Just cosmetic:
Why do you use pr_debug here instead of printk like you did above?

Signature · 7 lines
MyExcuse:
the printer thinks its a router.

Martin Zwickel <[email protected]>
Research & Development

TechnoTrend AG <http://www.technotrend.de>
#2 lore raw
Quoted 13 lines · Martin Zwickel
Quoted 13 lines
kernel/power/swsusp.c +4 -3
@@ -373,15 +377,22 @@
 
 static int write_pagedir(void)
 {
-	unsigned long addr = (unsigned long)pagedir_nosave;
 	int error = 0;
-	int n = SUSPEND_PD_PAGES(nr_copy_pages);
-	int i;
+	unsigned n = 0;
+	struct pbe * pbe;
+
+	printk( "Writing pagedir ...");
 
Quoted 5 lines

Review line 387

Quoted 2 lines
(quoted diff) +2 -0
+
+	pr_debug("\b\b\bdone (%u pages)\n", n);

Just cosmetic:
Why do you use pr_debug here instead of printk like you did above?

By mistake. :-) Thanks!

Greets,
RJW

Signature · 3 lines
- Would you tell me, please, which way I ought to go from here?
- That depends a good deal on where you want to get to.
		-- Lewis Carroll "Alice's Adventures in Wonderland"
#3 lore raw
In reply to Rafael J. Wysocki

Hi!

Quoted 8 lines

The following patch is (yet) an(other) attempt to eliminate the need for using higher order memory allocations on suspend. It accomplishes this by replacing the array of page backup entries with a list, so it is only necessary to allocate individual memory pages.

I have noticed that the suspend code in swsusp is actually independent of the resume code and vice versa, so it is not necessary to change them both at once.

:-) Heh, nice idea, but it is going to be really confusing to anyone reading the code in between.

Quoted 7 lines

Therefore I have decided, for now, to only modify the suspend part which is more straightforward. The resume code is almost unaffected by this patch (the only thing changed in this code is a BUG_ON() that is removed).

The patch is against 2.6.11-rc2. It has been tested and quite thoroughly debugged on x86-64. Still, if anyone could test it on another architecture, I'd be grateful. Of course comments, suggestions etc. are welcome.

I'll do some testing later.

include/linux/suspend.h line 20

Quoted 12 lines

diff -Nru linux-2.6.11-rc2-orig/include/linux/suspend.h linux-2.6.11-rc2/include/linux/suspend.h

include/linux/suspend.h +2 -3
@@ -16,11 +16,20 @@
 	unsigned long address;		/* address of the copy */
 	unsigned long orig_address;	/* original address of page */
 	swp_entry_t swap_address;	
-	swp_entry_t dummy;		/* we need scratch space at 
-					 * end of page (see link, diskpage)
-					 */
+
+	struct pbe *next;

It is still used as a scratch space at end of page, please leave the comment.

include/linux/suspend.h

Quoted 10 lines
include/linux/suspend.h +4 -3
 	printk( "Writing data to swap (%d pages)...     ", nr_copy_pages );
-	for (i = 0; i < nr_copy_pages && !error; i++) {
+	for_each_pbe(p, pagedir_nosave) {
 		if (!(i%mod))
 			printk( "\b\b\b\b%3d%%", i / mod );
-		error = write_page((pagedir_nosave+i)->address,
-					  &((pagedir_nosave+i)->swap_address));
+		error = write_page(p->address, &(p->swap_address));
+		if (error)
+			goto Exit;

Can you do return error instead? Goto is ugly. Or just break. Same in next chunks.

include/linux/suspend.h

Quoted 9 lines
include/linux/suspend.h +3 -4
-
-static void calc_order(void)
+static int calc_nr(int nr_copy)
 {
-	int diff = 0;
-	int order = 0;
+	int extra = 0;
+	int mod = (nr_copy % PBES_PER_PAGE) ? 1 : 0;

!!( ) is usually used for this.

include/linux/suspend.h

Quoted 1 line
include/linux/suspend.h +1 -0
+	int diff = (nr_copy / PBES_PER_PAGE) + mod;

Pavel

Signature · 2 lines
People were complaining that M$ turns users into beta-testers...
...jr ghea gurz vagb qrirybcref, naq gurl frrz gb yvxr vg gung jnl!
#4 lore raw
Quoted 2 lines · you

Hi!

[-- snip --]

Quoted 18 lines

I'll do some testing later.

include/linux/suspend.h line 20

Quoted 12 lines

diff -Nru linux-2.6.11-rc2-orig/include/linux/suspend.h linux-2.6.11-rc2/include/linux/suspend.h

include/linux/suspend.h +2 -3
@@ -16,11 +16,20 @@
 	unsigned long address;		/* address of the copy */
 	unsigned long orig_address;	/* original address of page */
 	swp_entry_t swap_address;	
-	swp_entry_t dummy;		/* we need scratch space at 
-					 * end of page (see link, diskpage)
-					 */
+
+	struct pbe *next;

It is still used as a scratch space at end of page, please leave the comment.

OK (I've changed it a bit, so it's clear that this is not only scratch).

Quoted 5 lines
Quoted 5 lines
include/linux/suspend.h +1 -1
 	printk( "Writing data to swap (%d pages)...     ", nr_copy_pages );
-	for (i = 0; i < nr_copy_pages && !error; i++) {
+	for_each_pbe(p, pagedir_nosave) {
 		if (!(i%mod))
 			printk( "\b\b\b\b%3d%%", i / mod );
Quoted 8 lines

include/linux/suspend.h

Quoted 5 lines
include/linux/suspend.h +3 -2
-		error = write_page((pagedir_nosave+i)->address,
-					  &((pagedir_nosave+i)->swap_address));
+		error = write_page(p->address, &(p->swap_address));
+		if (error)
+			goto Exit;

Can you do return error instead? Goto is ugly. Or just break. Same in next chunks.

Sure.

Quoted 10 lines

include/linux/suspend.h

Quoted 8 lines
include/linux/suspend.h +3 -4
-
-static void calc_order(void)
+static int calc_nr(int nr_copy)
 {
-	int diff = 0;
-	int order = 0;
+	int extra = 0;
+	int mod = (nr_copy % PBES_PER_PAGE) ? 1 : 0;

!!( ) is usually used for this.

OK

Additionally, I fixed a bug in alloc_pagedir() which might have been triggered if get_zereod_page() failed (unlikely, but still). Corrected patch follows.

Greets,
RJW

include/linux/suspend.h +14 -3
diff -Nru linux-2.6.11-rc2-orig/include/linux/suspend.h linux-2.6.11-rc2/include/linux/suspend.h
@@ -16,11 +16,22 @@
 	unsigned long address;		/* address of the copy */
 	unsigned long orig_address;	/* original address of page */
 	swp_entry_t swap_address;	
-	swp_entry_t dummy;		/* we need scratch space at 
-					 * end of page (see link, diskpage)
-					 */
+
+	struct pbe *next;	/* also used as scratch space at
+				 * end of page (see link, diskpage)
+				 */
 } suspend_pagedir_t;
 
+#define for_each_pbe(pbe, pblist) \
+	for (pbe = pblist ; pbe ; pbe = pbe->next)
+
+#define PBES_PER_PAGE      (PAGE_SIZE/sizeof(struct pbe))
+#define PB_PAGE_SKIP       (PBES_PER_PAGE-1)
+
+#define for_each_pb_page(pbe, pblist) \
+	for (pbe = pblist ; pbe ; pbe = (pbe+PB_PAGE_SKIP)->next)
+
+
 #define SWAP_FILENAME_MAXLENGTH	32
 
 
kernel/power/swsusp.c +103 -72

313 lines not on this page · raw message

Signature · 3 lines
- Would you tell me, please, which way I ought to go from here?
- That depends a good deal on where you want to get to.
		-- Lewis Carroll "Alice's Adventures in Wonderland"

A message that is not in the archive (sent off-list or not yet received): [email protected]

A message that is not in the archive (sent off-list or not yet received): [email protected]

A message that is not in the archive (sent off-list or not yet received): [email protected]

#8 lore raw

Hi!

Quoted 15 lines
Quoted 12 lines

static inline void free_pagedir(struct pbe *pblist)

{
        struct pbe *pbe;

        while (pblist) {
                pbe = pblist + PB_PAGE_SKIP;
                pblist = pbe->next;
                free_page((unsigned long)pbe);
        }
        pr_debug("free_pagedir(): done\n");
}
Quoted 2 lines

Should not you free_page(pblist) instead? This passes address in middle of page to free_page, that seems wrong.

Quoted 7 lines

Certainly. It should be something like that:

while (pblist) {
	pbe = pblist;
	pblist = (pbe + PB_PAGE_SKIP)->next;
	free_page((unsigned long)pbe);
}

Hmm, I see, my "fix" leaks one page of memory during each suspend... I've fixed it properly now and will eventually propagete it back.

This should be right..

        while (pblist) {
                pbe = (pblist + PB_PAGE_SKIP)->next;
                free_page((unsigned long)pblist);
                pblist = pbe;
        }


								Pavel
Signature · 2 lines
People were complaining that M$ turns users into beta-testers...
...jr ghea gurz vagb qrirybcref, naq gurl frrz gb yvxr vg gung jnl!
#9 lore raw
Quoted 29 lines · Pavel Machek

Hi!

Quoted 15 lines
Quoted 12 lines

static inline void free_pagedir(struct pbe *pblist)

{
        struct pbe *pbe;

        while (pblist) {
                pbe = pblist + PB_PAGE_SKIP;
                pblist = pbe->next;
                free_page((unsigned long)pbe);
        }
        pr_debug("free_pagedir(): done\n");
}
Quoted 2 lines

Should not you free_page(pblist) instead? This passes address in middle of page to free_page, that seems wrong.

Quoted 7 lines

Certainly. It should be something like that:

while (pblist) {
	pbe = pblist;
	pblist = (pbe + PB_PAGE_SKIP)->next;
	free_page((unsigned long)pbe);
}

Hmm, I see, my "fix" leaks one page of memory during each suspend... I've fixed it properly now and will eventually propagete it back.

Quoted 7 lines

This should be right..

        while (pblist) {
                pbe = (pblist + PB_PAGE_SKIP)->next;
                free_page((unsigned long)pblist);
                pblist = pbe;
        }

Sure it is.

Greets,
RJW

Signature · 3 lines
- Would you tell me, please, which way I ought to go from here?
- That depends a good deal on where you want to get to.
		-- Lewis Carroll "Alice's Adventures in Wonderland"