summaryrefslogtreecommitdiff
path: root/arch/arm64/kernel
diff options
context:
space:
mode:
authorStepan Moskovchenko <stepanm@codeaurora.org>2014-11-13 16:53:38 -0800
committerRohit Vaswani <rvaswani@codeaurora.org>2016-03-01 12:22:13 -0800
commitc298ac768c90b3e62a7d94e427b45feee40b5408 (patch)
treeea922b3fbd55b8617ca4f33460e37cb3f3076617 /arch/arm64/kernel
parentd4b065ff4717f80fd33c092e855ff89cb58c9f36 (diff)
arm64: update die handler from ARM / x86
Commit 02df19b4227e5b799e4642e88b568f9474fa78d0 ("ARM: 7424/1: update die handler from x86") refactored the ARM die() logic to avoid a deadlock if a kernel oops happens while holding a spinlock that may also be used in an IRQ handler. Refactor the ARM64 die() handler in a similar way, to avoid similar deadlocks, which would have otherwise prevented the kernel from printing the full panic output and properly completing the panic() path. Change-Id: I217a8d719f64b54467fa88a583d83f72193d7580 Signed-off-by: Stepan Moskovchenko <stepanm@codeaurora.org>
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r--arch/arm64/kernel/traps.c61
1 files changed, 49 insertions, 12 deletions
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index c56a8ae4172c..ab2d291cea72 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -227,39 +227,76 @@ static int __die(const char *str, int err, struct thread_info *thread,
return ret;
}
-static DEFINE_RAW_SPINLOCK(die_lock);
+static arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED;
+static int die_owner = -1;
+static unsigned int die_nest_count;
-/*
- * This function is protected against re-entrancy.
- */
-void die(const char *str, struct pt_regs *regs, int err)
+static unsigned long oops_begin(void)
{
- struct thread_info *thread = current_thread_info();
- int ret;
+ int cpu;
+ unsigned long flags;
oops_enter();
- raw_spin_lock_irq(&die_lock);
+ /* racy, but better than risking deadlock. */
+ raw_local_irq_save(flags);
+ cpu = smp_processor_id();
+ if (!arch_spin_trylock(&die_lock)) {
+ if (cpu == die_owner)
+ /* nested oops. should stop eventually */;
+ else
+ arch_spin_lock(&die_lock);
+ }
+ die_nest_count++;
+ die_owner = cpu;
console_verbose();
bust_spinlocks(1);
- ret = __die(str, err, thread, regs);
+ return flags;
+}
- if (regs && kexec_should_crash(thread->task))
+static void oops_end(unsigned long flags, struct pt_regs *regs, int notify)
+{
+ if (regs && kexec_should_crash(current))
crash_kexec(regs);
bust_spinlocks(0);
+ die_owner = -1;
add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
- raw_spin_unlock_irq(&die_lock);
+ die_nest_count--;
+ if (!die_nest_count)
+ /* Nest count reaches zero, release the lock. */
+ arch_spin_unlock(&die_lock);
+ raw_local_irq_restore(flags);
oops_exit();
if (in_interrupt())
panic("Fatal exception in interrupt");
if (panic_on_oops)
panic("Fatal exception");
- if (ret != NOTIFY_STOP)
+ if (notify != NOTIFY_STOP)
do_exit(SIGSEGV);
}
+/*
+ * This function is protected against re-entrancy.
+ */
+void die(const char *str, struct pt_regs *regs, int err)
+{
+ struct thread_info *thread = current_thread_info();
+ enum bug_trap_type bug_type = BUG_TRAP_TYPE_NONE;
+ unsigned long flags = oops_begin();
+ int ret;
+
+ if (!user_mode(regs))
+ bug_type = report_bug(regs->pc, regs);
+ if (bug_type != BUG_TRAP_TYPE_NONE)
+ str = "Oops - BUG";
+
+ ret = __die(str, err, thread, regs);
+
+ oops_end(flags, regs, ret);
+}
+
void arm64_notify_die(const char *str, struct pt_regs *regs,
struct siginfo *info, int err)
{