
From: Patrick Mochel <mochel@osdl.org>

Below is a patch that should fix the problem. The problem was due to the
fact that I removed the kernel_fpu_end() call from
kernel/power/swsusp.c::swsusp_suspend(). kernel_fpu_end() disables
preempt, which causes any subsequent schedule() to trigger that BUG(). I
apologize for this regression.

The patch needs a bit more explanation, because I didn't simply replace 
the call. Doing so would be a layering violation of the structure of the 
code. kernel_fpu_end() is called by save_processor_state(), which is 
called by swsusp_arch_suspend(). We should be calling it from the same 
chain, and we shouldn't be calling a function from generic code that is 
defined on only two architectures. 

I modified swsusp_arch_suspend() to unconditionally call
restore_processor_state() when exiting, which provides the same layering. 
However, it is still too late WRT to the other things that 
swsusp_suspend() was doing (resuming drivers and writing the image). 

Since those are unrelated to snapshotting memory, I divorced them from the 
function. swsusp_save() now is the function that is responsible for 
snapshoting memory, while swsusp_write() is responsible for writing the 
image, only. 

The resulting code is cleaner and more streamlined. It behaves as
predicted locally. Please try it and report whether or not it works for 
you. 

Andrew, please apply this to -test4-mm5. I realize I said I would not
touch swsusp any more, and this patch may become irrelevant in the future. 
But, it fixes a real bug now. 



 25-akpm/arch/i386/power/swsusp.S |    2 -
 25-akpm/kernel/power/disk.c      |   28 +++++++++++-------------
 25-akpm/kernel/power/main.c      |    9 +++----
 25-akpm/kernel/power/swsusp.c    |   44 +++++++++++++++------------------------
 4 files changed, 36 insertions(+), 47 deletions(-)

diff -puN arch/i386/power/swsusp.S~swsusp-fpu-fix arch/i386/power/swsusp.S
--- 25/arch/i386/power/swsusp.S~swsusp-fpu-fix	Thu Sep  4 13:16:18 2003
+++ 25-akpm/arch/i386/power/swsusp.S	Thu Sep  4 13:16:18 2003
@@ -76,10 +76,10 @@ ENTRY(swsusp_arch_suspend)
 	movl saved_context_edx, %edx
 	movl saved_context_esi, %esi
 	movl saved_context_edi, %edi
-	call restore_processor_state
 	pushl saved_context_eflags ; popfl
 	call swsusp_resume
 .L1449:
+	call restore_processor_state
 	popl %ebx
 	ret
 
diff -puN kernel/power/disk.c~swsusp-fpu-fix kernel/power/disk.c
--- 25/kernel/power/disk.c~swsusp-fpu-fix	Thu Sep  4 13:16:18 2003
+++ 25-akpm/kernel/power/disk.c	Thu Sep  4 13:16:18 2003
@@ -163,27 +163,27 @@ int pm_suspend_disk(void)
 
 	pr_debug("PM: snapshotting memory.\n");
 	in_suspend = 1;
-	local_irq_disable();
 	if ((error = swsusp_save()))
 		goto Done;
 
-	pr_debug("PM: writing image.\n");
+	if (in_suspend) {
+		pr_debug("PM: writing image.\n");
 
-	/*
-	 * FIXME: Leftover from swsusp. Are they necessary?
-	 */
-	mb();
-	barrier();
-
-	error = swsusp_write();
-	if (!error && in_suspend) {
-		error = power_down(pm_disk_mode);
-		pr_debug("PM: Power down failed.\n");
+		/*
+		 * FIXME: Leftover from swsusp. Are they necessary?
+		 */
+		mb();
+		barrier();
+
+		error = swsusp_write();
+		if (!error) {
+			error = power_down(pm_disk_mode);
+			pr_debug("PM: Power down failed.\n");
+		}
 	} else
 		pr_debug("PM: Image restored successfully.\n");
 	swsusp_free();
  Done:
-	local_irq_enable();
 	finish();
 	return error;
 }
@@ -217,7 +217,6 @@ static int pm_resume(void)
 
 	barrier();
 	mb();
-	local_irq_disable();
 
 	/* FIXME: The following (comment and mdelay()) are from swsusp.
 	 * Are they really necessary?
@@ -231,7 +230,6 @@ static int pm_resume(void)
 
 	pr_debug("PM: Restoring saved image.\n");
 	swsusp_restore();
-	local_irq_enable();
 	pr_debug("PM: Restore failed, recovering.n");
 	finish();
  Free:
diff -puN kernel/power/main.c~swsusp-fpu-fix kernel/power/main.c
--- 25/kernel/power/main.c~swsusp-fpu-fix	Thu Sep  4 13:16:18 2003
+++ 25-akpm/kernel/power/main.c	Thu Sep  4 13:16:18 2003
@@ -73,15 +73,14 @@ static int suspend_prepare(u32 state)
 		goto Finish;
 
 	return 0;
- Done:
-	pm_restore_console();
-	return error;
+
  Finish:
 	if (pm_ops->finish)
 		pm_ops->finish(state);
  Thaw:
 	thaw_processes();
-	goto Done;
+	pm_restore_console();
+	return error;
 }
 
 
@@ -94,9 +93,9 @@ static int suspend_enter(u32 state)
 	if ((error = device_power_down(state)))
 		goto Done;
 	error = pm_ops->enter(state);
-	local_irq_restore(flags);
 	device_power_up();
  Done:
+	local_irq_restore(flags);
 	return error;
 }
 
diff -puN kernel/power/swsusp.c~swsusp-fpu-fix kernel/power/swsusp.c
--- 25/kernel/power/swsusp.c~swsusp-fpu-fix	Thu Sep  4 13:16:18 2003
+++ 25-akpm/kernel/power/swsusp.c	Thu Sep  4 13:16:18 2003
@@ -416,11 +416,12 @@ static suspend_pagedir_t *create_suspend
 }
 
 
-static int suspend_prepare_image(void)
+int swsusp_suspend(void)
 {
 	struct sysinfo i;
 	unsigned int nr_needed_pages = 0;
 
+	read_swapfiles();
 	drain_local_pages();
 
 	pagedir_nosave = NULL;
@@ -486,12 +487,10 @@ static int suspend_prepare_image(void)
 static int suspend_save_image(void)
 {
 	int error;
-	local_irq_enable();
 	device_resume();
 	lock_swapdevices();
 	error = write_suspend_image();
 	lock_swapdevices();
-	local_irq_disable();
 	return error;
 }
 
@@ -515,35 +514,17 @@ int swsusp_resume(void)
 	if (!resume) {
 		save_processor_state();
 		SAVE_REGISTERS
-		swsusp_suspend();
-		return;
+		return swsusp_suspend();
 	}
 	GO_TO_SWAPPER_PAGE_TABLES
 	COPY_PAGES_BACK
 	RESTORE_REGISTERS
 	restore_processor_state();
-	swsusp_resume();
+	return swsusp_resume();
 
  */
 
 
-int swsusp_suspend(void)
-{
-	int error;
-	read_swapfiles();
-	error = suspend_prepare_image();
-	if (!error)
-		error = suspend_save_image();
-	if (error) {
-		printk(KERN_EMERG "%sSuspend failed, trying to recover...\n",
-		       name_suspend);
-		barrier();
-		mb();
-		mdelay(1000);
-	}
-	return error;
-}
-
 /* More restore stuff */
 
 /* FIXME: Why not memcpy(to, from, 1<<pagedir_order*PAGE_SIZE)? */
@@ -870,11 +851,18 @@ static int __init read_suspend_image(voi
 
 int swsusp_save(void) 
 {
+	int error;
+
 #if defined (CONFIG_HIGHMEM) || defined (COFNIG_DISCONTIGMEM)
 	printk("swsusp is not supported with high- or discontig-mem.\n");
 	return -EPERM;
 #endif
-	return arch_prepare_suspend();
+	if ((error = arch_prepare_suspend()))
+		return error;
+	local_irq_disable();
+	error = swsusp_arch_suspend(0);
+	local_irq_enable();
+	return error;
 }
 
 
@@ -890,7 +878,7 @@ int swsusp_save(void) 
 
 int swsusp_write(void)
 {
-	return swsusp_arch_suspend(0);
+	return suspend_save_image();
 }
 
 
@@ -933,7 +921,11 @@ int __init swsusp_read(void)
 
 int __init swsusp_restore(void)
 {
-	return swsusp_arch_suspend(1);
+	int error;
+	local_irq_disable();
+	error = swsusp_arch_suspend(1);
+	local_irq_enable();
+	return error;
 }
 
 

_
