Pull bugzilla-5653 into release branch
[pandora-kernel.git] / arch / x86_64 / kernel / traps.c
index 2700b13..cea335e 100644 (file)
@@ -102,6 +102,8 @@ static inline void preempt_conditional_cli(struct pt_regs *regs)
 {
        if (regs->eflags & X86_EFLAGS_IF)
                local_irq_disable();
+       /* Make sure to not schedule here because we could be running
+          on an exception stack. */
        preempt_enable_no_resched();
 }
 
@@ -385,6 +387,7 @@ void out_of_line_bug(void)
 
 static DEFINE_SPINLOCK(die_lock);
 static int die_owner = -1;
+static unsigned int die_nest_count;
 
 unsigned __kprobes long oops_begin(void)
 {
@@ -399,6 +402,7 @@ unsigned __kprobes long oops_begin(void)
                else
                        spin_lock(&die_lock);
        }
+       die_nest_count++;
        die_owner = cpu;
        console_verbose();
        bust_spinlocks(1);
@@ -409,7 +413,13 @@ void __kprobes oops_end(unsigned long flags)
 { 
        die_owner = -1;
        bust_spinlocks(0);
-       spin_unlock_irqrestore(&die_lock, flags);
+       die_nest_count--;
+       if (die_nest_count)
+               /* We still own the lock */
+               local_irq_restore(flags);
+       else
+               /* Nest count reaches zero, release the lock. */
+               spin_unlock_irqrestore(&die_lock, flags);
        if (panic_on_oops)
                panic("Oops");
 }
@@ -464,6 +474,8 @@ void __kprobes die_nmi(char *str, struct pt_regs *regs)
                panic("nmi watchdog");
        printk("console shuts up ...\n");
        oops_end(flags);
+       nmi_exit();
+       local_irq_enable();
        do_exit(SIGSEGV);
 }
 
@@ -473,8 +485,6 @@ static void __kprobes do_trap(int trapnr, int signr, char *str,
 {
        struct task_struct *tsk = current;
 
-       conditional_sti(regs);
-
        tsk->thread.error_code = error_code;
        tsk->thread.trap_no = trapnr;
 
@@ -511,6 +521,7 @@ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
        if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
                                                        == NOTIFY_STOP) \
                return; \
+       conditional_sti(regs);                                          \
        do_trap(trapnr, signr, str, regs, error_code, NULL); \
 }
 
@@ -525,6 +536,7 @@ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
        if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
                                                        == NOTIFY_STOP) \
                return; \
+       conditional_sti(regs);                                          \
        do_trap(trapnr, signr, str, regs, error_code, &info); \
 }
 
@@ -538,7 +550,17 @@ DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS)
 DO_ERROR(11, SIGBUS,  "segment not present", segment_not_present)
 DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0)
 DO_ERROR(18, SIGSEGV, "reserved", reserved)
-DO_ERROR(12, SIGBUS,  "stack segment", stack_segment)
+
+/* Runs on IST stack */
+asmlinkage void do_stack_segment(struct pt_regs *regs, long error_code)
+{
+       if (notify_die(DIE_TRAP, "stack segment", regs, error_code,
+                       12, SIGBUS) == NOTIFY_STOP)
+               return;
+       preempt_conditional_sti(regs);
+       do_trap(12, SIGBUS, "stack segment", regs, error_code, NULL);
+       preempt_conditional_cli(regs);
+}
 
 asmlinkage void do_double_fault(struct pt_regs * regs, long error_code)
 {
@@ -672,8 +694,9 @@ asmlinkage void __kprobes do_int3(struct pt_regs * regs, long error_code)
        if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) == NOTIFY_STOP) {
                return;
        }
+       preempt_conditional_sti(regs);
        do_trap(3, SIGTRAP, "int3", regs, error_code, NULL);
-       return;
+       preempt_conditional_cli(regs);
 }
 
 /* Help handler running on IST stack to switch back to user stack