From b344e24a8e8ceda83d1285d22e3e5baf4f5e42d3 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sun, 16 Aug 2009 21:54:48 +0100 Subject: sh: unwinder: Introduce UNWINDER_BUG() and UNWINDER_BUG_ON() We can't assume that if we execute the unwinder code and the unwinder was already running that it has faulted. Clearly two kernel threads can invoke the unwinder at the same time and may be running simultaneously. The previous approach used BUG() and BUG_ON() in the unwinder code to detect whether the unwinder was incapable of unwinding the stack, and that the next available unwinder should be used instead. A better approach is to explicitly invoke a trap handler to switch unwinders when the current unwinder cannot continue. Signed-off-by: Matt Fleming --- arch/sh/kernel/unwinder.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) (limited to 'arch/sh/kernel/unwinder.c') diff --git a/arch/sh/kernel/unwinder.c b/arch/sh/kernel/unwinder.c index 2b30fa28b44..b9c122abe25 100644 --- a/arch/sh/kernel/unwinder.c +++ b/arch/sh/kernel/unwinder.c @@ -53,8 +53,6 @@ static struct list_head unwinder_list = { static DEFINE_SPINLOCK(unwinder_lock); -static atomic_t unwinder_running = ATOMIC_INIT(0); - /** * select_unwinder - Select the best registered stack unwinder. * @@ -122,6 +120,8 @@ int unwinder_register(struct unwinder *u) return ret; } +int unwinder_faulted = 0; + /* * Unwind the call stack and pass information to the stacktrace_ops * functions. Also handle the case where we need to switch to a new @@ -144,19 +144,40 @@ void unwind_stack(struct task_struct *task, struct pt_regs *regs, * Hopefully this will give us a semi-reliable stacktrace so we * can diagnose why curr_unwinder->dump() faulted. */ - if (atomic_inc_return(&unwinder_running) != 1) { + if (unwinder_faulted) { spin_lock_irqsave(&unwinder_lock, flags); - if (!list_is_singular(&unwinder_list)) { + /* Make sure no one beat us to changing the unwinder */ + if (unwinder_faulted && !list_is_singular(&unwinder_list)) { list_del(&curr_unwinder->list); curr_unwinder = select_unwinder(); + + unwinder_faulted = 0; } spin_unlock_irqrestore(&unwinder_lock, flags); - atomic_dec(&unwinder_running); } curr_unwinder->dump(task, regs, sp, ops, data); +} + +/* + * Trap handler for UWINDER_BUG() statements. We must switch to the + * unwinder with the next highest rating. + */ +BUILD_TRAP_HANDLER(unwinder) +{ + insn_size_t insn; + TRAP_HANDLER_DECL; + + /* Rewind */ + regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); + insn = *(insn_size_t *)instruction_pointer(regs); + + /* Switch unwinders when unwind_stack() is called */ + unwinder_faulted = 1; - atomic_dec(&unwinder_running); +#ifdef CONFIG_BUG + handle_BUG(regs); +#endif } -- cgit v1.2.3-70-g09d2 From 4ab8f241f6d510470c15b62ac10f6905ff5c97bd Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sat, 22 Aug 2009 03:43:15 +0900 Subject: sh: Export unwind_stack() to satisfy modular oprofile. If the oprofile code is built as a module, unwind_stack() as used by the oprofile backtrace code is not available, causing build breakage. Signed-off-by: Paul Mundt --- arch/sh/kernel/unwinder.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/sh/kernel/unwinder.c') diff --git a/arch/sh/kernel/unwinder.c b/arch/sh/kernel/unwinder.c index 2b30fa28b44..5f56ff3f55e 100644 --- a/arch/sh/kernel/unwinder.c +++ b/arch/sh/kernel/unwinder.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -160,3 +161,4 @@ void unwind_stack(struct task_struct *task, struct pt_regs *regs, atomic_dec(&unwinder_running); } +EXPORT_SYMBOL_GPL(unwind_stack); -- cgit v1.2.3-70-g09d2 From e115f2c17cbceee93b34d787a7a4a867fc73e7b4 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sat, 22 Aug 2009 05:28:25 +0900 Subject: sh: unwinder: Use a special bug flag for unwinder traps. This simplifies the unwinder trap handling, dropping the use of the special trapa vector and simply piggybacking on top of the BUG support. A new BUGFLAG_UNWINDER is added for flagging the unwinder fault, before continuing on with regular BUG dispatch. Signed-off-by: Paul Mundt --- arch/sh/include/asm/bug.h | 11 ++++++----- arch/sh/kernel/debugtraps.S | 6 +----- arch/sh/kernel/traps.c | 21 +++++++++++++++++---- arch/sh/kernel/unwinder.c | 21 --------------------- 4 files changed, 24 insertions(+), 35 deletions(-) (limited to 'arch/sh/kernel/unwinder.c') diff --git a/arch/sh/include/asm/bug.h b/arch/sh/include/asm/bug.h index b7d9822fd6c..23c5504a3a0 100644 --- a/arch/sh/include/asm/bug.h +++ b/arch/sh/include/asm/bug.h @@ -1,8 +1,8 @@ #ifndef __ASM_SH_BUG_H #define __ASM_SH_BUG_H -#define TRAPA_UNWINDER_BUG_OPCODE 0xc33b /* trapa #0x3b */ #define TRAPA_BUG_OPCODE 0xc33e /* trapa #0x3e */ +#define BUGFLAG_UNWINDER (1 << 1) #ifdef CONFIG_GENERIC_BUG #define HAVE_ARCH_BUG @@ -73,15 +73,16 @@ do { \ unlikely(__ret_warn_on); \ }) -#define UNWINDER_BUG() \ +#define UNWINDER_BUG() \ do { \ __asm__ __volatile__ ( \ "1:\t.short %O0\n" \ - _EMIT_BUG_ENTRY \ + _EMIT_BUG_ENTRY \ : \ - : "n" (TRAPA_UNWINDER_BUG_OPCODE), \ + : "n" (TRAPA_BUG_OPCODE), \ "i" (__FILE__), \ - "i" (__LINE__), "i" (0), \ + "i" (__LINE__), \ + "i" (BUGFLAG_UNWINDER), \ "i" (sizeof(struct bug_entry))); \ } while (0) diff --git a/arch/sh/kernel/debugtraps.S b/arch/sh/kernel/debugtraps.S index cb00e4a82d4..591741383ee 100644 --- a/arch/sh/kernel/debugtraps.S +++ b/arch/sh/kernel/debugtraps.S @@ -19,10 +19,6 @@ #if !defined(CONFIG_SH_STANDARD_BIOS) #define sh_bios_handler debug_trap_handler -#endif - -#if !defined(CONFIG_DWARF_UNWINDER) -#define unwinder_trap_handler debug_trap_handler #endif .data @@ -39,7 +35,7 @@ ENTRY(debug_trap_table) .long debug_trap_handler /* 0x38 */ .long debug_trap_handler /* 0x39 */ .long debug_trap_handler /* 0x3a */ - .long unwinder_trap_handler /* 0x3b */ + .long debug_trap_handler /* 0x3b */ .long breakpoint_trap_handler /* 0x3c */ .long singlestep_trap_handler /* 0x3d */ .long bug_trap_handler /* 0x3e */ diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 881b9a32b7d..f69bd968fcc 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -5,18 +5,32 @@ #include #include #include +#include #include #ifdef CONFIG_BUG void handle_BUG(struct pt_regs *regs) { + const struct bug_entry *bug; + unsigned long bugaddr = regs->pc; enum bug_trap_type tt; - tt = report_bug(regs->pc, regs); + + if (!is_valid_bugaddr(bugaddr)) + goto invalid; + + bug = find_bug(bugaddr); + + /* Switch unwinders when unwind_stack() is called */ + if (bug->flags & BUGFLAG_UNWINDER) + unwinder_faulted = 1; + + tt = report_bug(bugaddr, regs); if (tt == BUG_TRAP_TYPE_WARN) { - regs->pc += instruction_size(regs->pc); + regs->pc += instruction_size(bugaddr); return; } +invalid: die("Kernel BUG", regs, TRAPA_BUG_OPCODE & 0xff); } @@ -28,8 +42,7 @@ int is_valid_bugaddr(unsigned long addr) return 0; if (probe_kernel_address((insn_size_t *)addr, opcode)) return 0; - - if (opcode == TRAPA_BUG_OPCODE || opcode == TRAPA_UNWINDER_BUG_OPCODE) + if (opcode == TRAPA_BUG_OPCODE) return 1; return 0; diff --git a/arch/sh/kernel/unwinder.c b/arch/sh/kernel/unwinder.c index e83861d9739..468889d958f 100644 --- a/arch/sh/kernel/unwinder.c +++ b/arch/sh/kernel/unwinder.c @@ -161,25 +161,4 @@ void unwind_stack(struct task_struct *task, struct pt_regs *regs, curr_unwinder->dump(task, regs, sp, ops, data); } - -/* - * Trap handler for UWINDER_BUG() statements. We must switch to the - * unwinder with the next highest rating. - */ -BUILD_TRAP_HANDLER(unwinder) -{ - insn_size_t insn; - TRAP_HANDLER_DECL; - - /* Rewind */ - regs->pc -= instruction_size(ctrl_inw(regs->pc - 4)); - insn = *(insn_size_t *)instruction_pointer(regs); - - /* Switch unwinders when unwind_stack() is called */ - unwinder_faulted = 1; - -#ifdef CONFIG_BUG - handle_BUG(regs); -#endif -} EXPORT_SYMBOL_GPL(unwind_stack); -- cgit v1.2.3-70-g09d2