[PATCH] x86-64: Safe interrupts in oops_begin/end

Rather than blindly re-enabling interrupts in oops_end(), save their state
in oope_begin() and then restore that state.

Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c
index f238d60..64a59cb 100644
--- a/arch/x86_64/kernel/traps.c
+++ b/arch/x86_64/kernel/traps.c
@@ -343,30 +343,33 @@
 static DEFINE_SPINLOCK(die_lock);
 static int die_owner = -1;
 
-void oops_begin(void)
+unsigned long oops_begin(void)
 {
-	int cpu = safe_smp_processor_id(); 
-	/* racy, but better than risking deadlock. */ 
-	local_irq_disable();
+	int cpu = safe_smp_processor_id();
+	unsigned long flags;
+
+	/* racy, but better than risking deadlock. */
+	local_irq_save(flags);
 	if (!spin_trylock(&die_lock)) { 
 		if (cpu == die_owner) 
 			/* nested oops. should stop eventually */;
 		else
-			spin_lock(&die_lock); 
+			spin_lock(&die_lock);
 	}
-	die_owner = cpu; 
+	die_owner = cpu;
 	console_verbose();
-	bust_spinlocks(1); 
+	bust_spinlocks(1);
+	return flags;
 }
 
-void oops_end(void)
+void oops_end(unsigned long flags)
 { 
 	die_owner = -1;
-	bust_spinlocks(0); 
-	spin_unlock(&die_lock); 
+	bust_spinlocks(0);
+	spin_unlock_irqrestore(&die_lock, flags);
 	if (panic_on_oops)
-		panic("Oops"); 
-} 
+		panic("Oops");
+}
 
 void __die(const char * str, struct pt_regs * regs, long err)
 {
@@ -392,10 +395,11 @@
 
 void die(const char * str, struct pt_regs * regs, long err)
 {
-	oops_begin();
+	unsigned long flags = oops_begin();
+
 	handle_BUG(regs);
 	__die(str, regs, err);
-	oops_end();
+	oops_end(flags);
 	do_exit(SIGSEGV); 
 }
 static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err)
@@ -406,7 +410,8 @@
 
 void die_nmi(char *str, struct pt_regs *regs)
 {
-	oops_begin();
+	unsigned long flags = oops_begin();
+
 	/*
 	 * We are in trouble anyway, lets at least try
 	 * to get a message out.
@@ -416,7 +421,7 @@
 	if (panic_on_timeout || panic_on_oops)
 		panic("nmi watchdog");
 	printk("console shuts up ...\n");
-	oops_end();
+	oops_end(flags);
 	do_exit(SIGSEGV);
 }
 
diff --git a/arch/x86_64/mm/fault.c b/arch/x86_64/mm/fault.c
index 816732d..b75b872 100644
--- a/arch/x86_64/mm/fault.c
+++ b/arch/x86_64/mm/fault.c
@@ -221,12 +221,13 @@
 static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs,
 				 unsigned long error_code)
 {
-	oops_begin();
+	unsigned long flags = oops_begin();
+
 	printk(KERN_ALERT "%s: Corrupted page table at address %lx\n",
 	       current->comm, address);
 	dump_pagetable(address);
 	__die("Bad pagetable", regs, error_code);
-	oops_end();
+	oops_end(flags);
 	do_exit(SIGKILL);
 }
 
@@ -304,6 +305,7 @@
 	unsigned long address;
 	const struct exception_table_entry *fixup;
 	int write;
+	unsigned long flags;
 	siginfo_t info;
 
 #ifdef CONFIG_CHECKING
@@ -521,7 +523,7 @@
  * terminate things with extreme prejudice.
  */
 
-	oops_begin(); 
+	flags = oops_begin();
 
 	if (address < PAGE_SIZE)
 		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
@@ -534,7 +536,7 @@
 	__die("Oops", regs, error_code);
 	/* Executive summary in case the body of the oops scrolled away */
 	printk(KERN_EMERG "CR2: %016lx\n", address);
-	oops_end(); 
+	oops_end(flags);
 	do_exit(SIGKILL);
 
 /*
diff --git a/include/asm-x86_64/kdebug.h b/include/asm-x86_64/kdebug.h
index b903419..f604e84 100644
--- a/include/asm-x86_64/kdebug.h
+++ b/include/asm-x86_64/kdebug.h
@@ -46,7 +46,7 @@
 extern void __die(const char *,struct pt_regs *,long);
 extern void show_registers(struct pt_regs *regs);
 extern void dump_pagetable(unsigned long);
-extern void oops_begin(void);
-extern void oops_end(void);
+extern unsigned long oops_begin(void);
+extern void oops_end(unsigned long);
 
 #endif
diff --git a/include/asm-x86_64/proto.h b/include/asm-x86_64/proto.h
index 764b5f8..dbb37b0 100644
--- a/include/asm-x86_64/proto.h
+++ b/include/asm-x86_64/proto.h
@@ -74,9 +74,6 @@
 
 extern void swap_low_mappings(void);
 
-extern void oops_begin(void);
-extern void die(const char *,struct pt_regs *,long);
-extern void __die(const char * str, struct pt_regs * regs, long err);
 extern void __show_regs(struct pt_regs * regs);
 extern void show_regs(struct pt_regs * regs);