Skip to contentSkip to conversation

Fix memory leak in swsusp

PATCH Pavel Machek 7 participants 19 messages Jun 9, 2004 – Jun 13, 2004

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

Hi!

This fixes 2 memory leaks in swsusp: during relocating pagedir, eaten pages were not properly freed in error path and even regular freeing path was freeing one page too little. Please apply,

Pavel

kernel/power/swsusp.c +16 -11
@@ -455,7 +455,7 @@
@@ -503,6 +503,9 @@
 		if (!pbe)
 			continue;
 		pbe->orig_address = (long) page_address(page);
+		/* Copy page is dangerous: it likes to mess with
+		   preempt count on specific cpus. Wrong preempt count is then copied,
+		   oops. */
 		copy_page((void *)pbe->address, (void *)pbe->orig_address);
 		pbe++;
 	}
@@ -923,8 +952,9 @@
 	suspend_pagedir_t *new_pagedir, *old_pagedir = pagedir_nosave;
 	void **eaten_memory = NULL;
 	void **c = eaten_memory, *m, *f;
+	int ret = 0;
 
-	printk("Relocating pagedir");
+	printk("Relocating pagedir ");
 
 	if(!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) {
 		printk("not necessary\n");
@@ -941,22 +971,23 @@
 		c = eaten_memory;
 	}
 
-	if (!m)
-		return -ENOMEM;
-
-	pagedir_nosave = new_pagedir = m;
-	copy_pagedir(new_pagedir, old_pagedir);
+	if (!m) {
+		printk("out of memory\n");
+		ret = -ENOMEM;
+	} else {
+		pagedir_nosave = new_pagedir = m;
+		copy_pagedir(new_pagedir, old_pagedir);
+	}
 
 	c = eaten_memory;
-	while(c) {
+	while (c) {
 		printk(":");
-		f = *c;
+		f = c;
 		c = *c;
-		if (f)
-			free_pages((unsigned long)f, pagedir_order);
+		free_pages((unsigned long)f, pagedir_order);
 	}
 	printk("|\n");
-	return 0;
+	return ret;
 }
 
 /*
Signature · 1 line
934a471f20d6580d5aad759bf0d97ddc
#1 lore raw
Quoted 4 lines · Pavel Machek

This fixes 2 memory leaks in swsusp: during relocating pagedir, eaten pages were not properly freed in error path and even regular freeing path was freeing one page too little. Please apply,

Thanks for the patch.

Here is the same fix for pmdisk.

Cheers,

Signature · 57 lines
Visit Openswan at http://www.openswan.org/
Email:  Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
===== kernel/power/pmdisk.c 1.84 vs edited =====
--- 1.84/kernel/power/pmdisk.c	2004-04-13 03:55:18 +10:00
+++ edited/kernel/power/pmdisk.c	2004-06-10 20:47:15 +10:00
@@ -795,6 +795,7 @@
 	suspend_pagedir_t *new_pagedir, *old_pagedir = pm_pagedir_nosave;
 	void **eaten_memory = NULL;
 	void **c = eaten_memory, *m, *f;
+	int err;
 
 	pr_debug("pmdisk: Relocating pagedir\n");
 
@@ -803,32 +804,31 @@
 		return 0;
 	}
 
+	err = -ENOMEM;
 	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
 		memset(m, 0, PAGE_SIZE);
-		if (!does_collide_order(old_pagedir, (unsigned long)m, pagedir_order))
+		if (!does_collide_order(old_pagedir, (unsigned long)m,
+					pagedir_order)) {
+			pm_pagedir_nosave = new_pagedir = m;
+			copy_pagedir(new_pagedir, old_pagedir);
+			err = 0;
 			break;
+		}
 		eaten_memory = m;
 		printk( "." ); 
 		*eaten_memory = c;
 		c = eaten_memory;
 	}
 
-	if (!m)
-		return -ENOMEM;
-
-	pm_pagedir_nosave = new_pagedir = m;
-	copy_pagedir(new_pagedir, old_pagedir);
-
 	c = eaten_memory;
 	while(c) {
 		printk(":");
-		f = *c;
+		f = c;
 		c = *c;
-		if (f)
-			free_pages((unsigned long)f, pagedir_order);
+		free_pages((unsigned long)f, pagedir_order);
 	}
 	printk("|\n");
-	return 0;
+	return err;
 }
#2 lore raw

Review line 809

Quoted 8 lines · Herbert Xu
(quoted diff) +1 -0
@@ -803,32 +804,31 @@
 		return 0;
 	}
 
+	err = -ENOMEM;
 	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
 		memset(m, 0, PAGE_SIZE);

BTW, what does this memset do?

Thanks,

Signature · 4 lines
Visit Openswan at http://www.openswan.org/
Email:  Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
#3 lore raw

Hi!

Quoted 9 lines

Review line 809

Quoted 7 lines
(quoted diff) +1 -0
@@ -803,32 +804,31 @@
 		return 0;
 	}
 
+	err = -ENOMEM;
 	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
 		memset(m, 0, PAGE_SIZE);

BTW, what does this memset do?

Someone (me?) was trying to be carefull, and was not carefull enough. AFAICS it should be memset(..., PAGE_SIZE << pagedir_order)

Signature · 1 line
64 bytes from 195.113.31.123: icmp_seq=28 ttl=51 time=448769.1 ms         
#4 lore raw
In reply to Herbert Xu

Hi!

Quoted 9 lines

Review line 809

Quoted 7 lines
(quoted diff) +1 -0
@@ -803,32 +804,31 @@
 		return 0;
 	}
 
+	err = -ENOMEM;
 	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
 		memset(m, 0, PAGE_SIZE);

BTW, what does this memset do?

Here's incremental fix, it compiles but not tested.

BTW I have problems getting mail to you:

This is the Postfix program at host atrey.karlin.mff.cuni.cz.

I'm sorry to have to inform you that the message returned below could not be delivered to one or more destinations.

For further assistance, please send mail to <postmaster>

If you do so, please include this problem report. You can delete your own text from the message returned below.

The Postfix program

<[email protected]>: host arnor.apana.org.au[203.14.152.115]said:

    550 mail from 195.113.31.123 rejected: administrative prohibition

									Pavel
kernel/power/swsusp.c renamed from linux/kernel/power/swsusp.c +1 -1
@@ -962,7 +962,7 @@
 	}
 
 	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
-		memset(m, 0, PAGE_SIZE);
+		memset(m, 0, PAGE_SIZE << pagedir_order);
 		if (!does_collide_order(old_pagedir, (unsigned long)m, pagedir_order))
 			break;
 		eaten_memory = m;
Signature · 1 line
People were complaining that M$ turns users into beta-testers...
#5 lore raw
Quoted 11 lines · Pavel Machek
Quoted 9 lines

kernel/power/swsusp.c line 809

Quoted 7 lines
kernel/power/swsusp.c renamed from linux/kernel/power/swsusp.c +1 -0
@@ -803,32 +804,31 @@
 		return 0;
 	}
 
+	err = -ENOMEM;
 	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
 		memset(m, 0, PAGE_SIZE);

BTW, what does this memset do?

Here's incremental fix, it compiles but not tested.

Thanks, but my question remains: why do we need the memset? The area allocated is either thrown away because it collides or is overwritten with the pagedir stuff straight away.

Quoted 1 line

BTW I have problems getting mail to you:

Sorry, that should be fixed now.

Cheers,

Signature · 4 lines
Visit Openswan at http://www.openswan.org/
Email:  Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
#6 lore raw

Hi!

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines

kernel/power/swsusp.c line 809

Quoted 7 lines
kernel/power/swsusp.c renamed from linux/kernel/power/swsusp.c +1 -0
@@ -803,32 +804,31 @@
 		return 0;
 	}
 
+	err = -ENOMEM;
 	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
 		memset(m, 0, PAGE_SIZE);

BTW, what does this memset do?

Quoted 5 lines
Quoted 1 line

Here's incremental fix, it compiles but not tested.

Thanks, but my question remains: why do we need the memset? The area allocated is either thrown away because it collides or is overwritten with the pagedir stuff straight away.

You are right, it should not be needed.
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!
#7 lore raw
Quoted 6 lines · Pavel Machek
Quoted 4 lines

Thanks, but my question remains: why do we need the memset? The area allocated is either thrown away because it collides or is overwritten with the pagedir stuff straight away.

You are right, it should not be needed.

Great. Here's the patch that removes the memset calls from both pmdisk and swsusp. It depends on the previous patches in this thread.

Cheers,

Signature · 4 lines
Visit Openswan at http://www.openswan.org/
Email:  Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
kernel/power/pmdisk.c +0 -1
diff -Nru a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c
@@ -806,7 +806,6 @@
 
 	err = -ENOMEM;
 	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
-		memset(m, 0, PAGE_SIZE);
 		if (!does_collide_order(old_pagedir, (unsigned long)m,
 					pagedir_order)) {
 			pm_pagedir_nosave = new_pagedir = m;
kernel/power/swsusp.c renamed from kernel/power/swsusp.c.orig +0 -1
@@ -936,7 +936,6 @@
 	}
 
 	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
-		memset(m, 0, PAGE_SIZE);
 		if (!does_collide_order(old_pagedir, (unsigned long)m, pagedir_order))
 			break;
 		eaten_memory = m;
#8 lore raw

Hi!

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines

Thanks, but my question remains: why do we need the memset? The area allocated is either thrown away because it collides or is overwritten with the pagedir stuff straight away.

You are right, it should not be needed.

Great. Here's the patch that removes the memset calls from both pmdisk and swsusp. It depends on the previous patches in this thread.

If you want more cleanups, copy_pagedir() should be probably replaced by simple memset...
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 3 lines · Pavel Machek

If you want more cleanups, copy_pagedir() should be probably replaced by simple memset...

Yes that's a great idea. Here is the patch to do that. Again it relies on all the previous patches in this thread.

Cheers,

Signature · 4 lines
Visit Openswan at http://www.openswan.org/
Email:  Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

===== kernel/power/pmdisk.c 1.86 vs edited =====

kernel/power/pmdisk.c +4 -16
@@ -732,19 +732,6 @@
 
 /* More restore stuff */
 
-/* FIXME: Why not memcpy(to, from, 1<<pagedir_order*PAGE_SIZE)? */
-static void __init copy_pagedir(suspend_pagedir_t *to, suspend_pagedir_t *from)
-{
-	int i;
-	char *topointer=(char *)to, *frompointer=(char *)from;
-
-	for(i=0; i < 1 << pagedir_order; i++) {
-		copy_page(topointer, frompointer);
-		topointer += PAGE_SIZE;
-		frompointer += PAGE_SIZE;
-	}
-}
-
 #define does_collide(addr) does_collide_order(pm_pagedir_nosave, addr, 0)
 
 /*
@@ -792,7 +779,7 @@
 	 * We have to avoid recursion (not to overflow kernel stack),
 	 * and that's why code looks pretty cryptic 
 	 */
-	suspend_pagedir_t *new_pagedir, *old_pagedir = pm_pagedir_nosave;
+	suspend_pagedir_t *old_pagedir = pm_pagedir_nosave;
 	void **eaten_memory = NULL;
 	void **c = eaten_memory, *m, *f;
 	int err;
@@ -808,8 +795,9 @@
 	while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order))) {
 		if (!does_collide_order(old_pagedir, (unsigned long)m,
 					pagedir_order)) {
-			pm_pagedir_nosave = new_pagedir = m;
-			copy_pagedir(new_pagedir, old_pagedir);
+			pm_pagedir_nosave =
+				memcpy(m, old_pagedir,
+				       PAGE_SIZE << pagedir_order);
 			err = 0;
 			break;
 		}
kernel/power/swsusp.c renamed from kernel/power/swsusp.c.orig +3 -16
@@ -863,19 +863,6 @@
 
 /* More restore stuff */
 
-/* FIXME: Why not memcpy(to, from, 1<<pagedir_order*PAGE_SIZE)? */
-static void copy_pagedir(suspend_pagedir_t *to, suspend_pagedir_t *from)
-{
-	int i;
-	char *topointer=(char *)to, *frompointer=(char *)from;
-
-	for(i=0; i < 1 << pagedir_order; i++) {
-		copy_page(topointer, frompointer);
-		topointer += PAGE_SIZE;
-		frompointer += PAGE_SIZE;
-	}
-}
-
 #define does_collide(addr) does_collide_order(pagedir_nosave, addr, 0)
 
 /*
@@ -923,7 +910,7 @@
 	 * We have to avoid recursion (not to overflow kernel stack),
 	 * and that's why code looks pretty cryptic 
 	 */
-	suspend_pagedir_t *new_pagedir, *old_pagedir = pagedir_nosave;
+	suspend_pagedir_t *old_pagedir = pagedir_nosave;
 	void **eaten_memory = NULL;
 	void **c = eaten_memory, *m, *f;
 	int ret = 0;
@@ -948,8 +935,8 @@
 		printk("out of memory\n");
 		ret = -ENOMEM;
 	} else {
-		pagedir_nosave = new_pagedir = m;
-		copy_pagedir(new_pagedir, old_pagedir);
+		pagedir_nosave =
+			memcpy(m, old_pagedir, PAGE_SIZE << pagedir_order);
 	}
 
 	c = eaten_memory;
#10 lore raw

Hi.

Herbert Xu wrote:

[...]

Quoted 3 lines
kernel/power/pmdisk.c +3 -0
+			pm_pagedir_nosave =
+				memcpy(m, old_pagedir,
+				       PAGE_SIZE << pagedir_order);

[...]

kernel/power/swsusp.c line 942

Quoted 5 lines
kernel/power/swsusp.c renamed from kernel/power/swsusp.c.orig +2 -0
+		pagedir_nosave =
+			memcpy(m, old_pagedir, PAGE_SIZE << pagedir_order);
 	}
 
 	c = eaten_memory;

We were avoiding the use of memcpy because it messes up the preempt count with 3DNow, and potentially as other unseen side effects. The preempt could possibly simply be reset at resume time, but the point remains.

Regards,

Nigel

Signature · 4 lines
Nigel & Michelle Cunningham
C/- Westminster Presbyterian Church Belconnen
61 Templeton Street, Cook, ACT 2614.
+61 (417) 100 574 (mobile)
#11 lore raw
Quoted 4 lines · Nigel Cunningham

We were avoiding the use of memcpy because it messes up the preempt count with 3DNow, and potentially as other unseen side effects. The preempt could possibly simply be reset at resume time, but the point remains.

eh? memcpy just copies memory. Maybe your meant copy_*_user()?

#12 lore raw
Quoted 7 lines · Andrew Morton
Quoted 4 lines · Nigel Cunningham

We were avoiding the use of memcpy because it messes up the preempt count with 3DNow, and potentially as other unseen side effects. The preempt could possibly simply be reset at resume time, but the point remains.

eh? memcpy just copies memory. Maybe your meant copy_*_user()?

See arch/i386/lib/memcpy.c The 3dnow routine does kernel_fpu_begin()/..end() which futzes with preempt count. What I'm missing though is that the count afterwards should be the same as it was before the memcpy. Why is this a problem for the suspend folks?

Dave

#13 lore raw

Hi!

Quoted 10 lines
Quoted 5 lines
Quoted 3 lines

We were avoiding the use of memcpy because it messes up the preempt count with 3DNow, and potentially as other unseen side effects. The preempt could possibly simply be reset at resume time, but the point remains.

eh? memcpy just copies memory. Maybe your meant copy_*_user()?

See arch/i386/lib/memcpy.c The 3dnow routine does kernel_fpu_begin()/..end() which futzes with preempt count. What I'm missing though is that the count afterwards should be the same as it was before the memcpy. Why is this a problem for the suspend folks?

It does not hurt here, but as someone else explained already it is a problem when doing atomic copy of memory. (You increment, than copy whole memory, you decrement; but you only decrement in original, not in copy...)
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!
#14 lore raw
In reply to Andrew Morton

Hi.

Quoted 6 lines · Andrew Morton
Quoted 1 line · Nigel Cunningham

We were avoiding the use of memcpy because it messes up the preempt count with 3DNow, and potentially as other unseen side effects. The preempt could possibly simply be reset at resume time, but the point remains.

eh? memcpy just copies memory. Maybe your meant copy_*_user()?

At some stage, you copy the page that contains the preempt count for the process that is doing the suspending. If you use memcpy on a 3Dnow machine, the preempt count is incremented prior to doing the copy of the page. Then, at resume time, it is one too high.

Regards,

Nigel

Signature · 4 lines
Nigel & Michelle Cunningham
C/- Westminster Presbyterian Church Belconnen
61 Templeton Street, Cook, ACT 2614.
+61 (417) 100 574 (mobile)
#15 lore raw

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 5 lines · Nigel Cunningham

Hi.
At some stage, you copy the page that contains the preempt count for the process that is doing the suspending. If you use memcpy on a 3Dnow machine, the preempt count is incremented prior to doing the copy of the page. Then, at resume time, it is one too high.

Well, if we know that it is one too high, why not decrement right after the resume?

Jeff.

- --
"Reality is merely an illusion, albeit a very persistent one."
- Albert Einstein
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)

iD8DBQFAy5KfwFP0+seVj/4RAlTXAJsFHALWNJ+mgVu7xEhfsk2Hqcq/wwCgmEmh EpOv1mA9B35xcbzxT+lSHIo=
=Bn1K
-----END PGP SIGNATURE-----

#16 lore raw

Hi!

Quoted 7 lines
Quoted 4 lines

At some stage, you copy the page that contains the preempt count for the process that is doing the suspending. If you use memcpy on a 3Dnow machine, the preempt count is incremented prior to doing the copy of the page. Then, at resume time, it is one too high.

Well, if we know that it is one too high, why not decrement right after the resume?

You are suggesting very dirty hack. See archives why it is basically undoable.

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!
#17 lore raw
In reply to Nigel Cunningham
Quoted 4 lines · Nigel Cunningham

At some stage, you copy the page that contains the preempt count for the process that is doing the suspending. If you use memcpy on a 3Dnow machine, the preempt count is incremented prior to doing the copy of the page. Then, at resume time, it is one too high.

Nope, this function only copies the swsusp page data structure so this is irrelevant.

Signature · 4 lines
Visit Openswan at http://www.openswan.org/
Email:  Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
#18 lore raw
In reply to Nigel Cunningham
Quoted 4 lines · Nigel Cunningham

We were avoiding the use of memcpy because it messes up the preempt count with 3DNow, and potentially as other unseen side effects. The preempt could possibly simply be reset at resume time, but the point remains.

In what way does it mess up the preempt count?

Signature · 4 lines
Visit Openswan at http://www.openswan.org/
Email:  Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt