From 97f361e2498ada54b48a235619eaf5af8e46427e Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 17 Aug 2009 05:07:38 +0900 Subject: sh: unwinder: Move initialization to early_initcall() and tidy up locking. This moves the initialization over to an early_initcall(). This fixes up some lockdep interaction issues. At the same time, kill off some superfluous locking in the init path. Signed-off-by: Paul Mundt --- arch/sh/kernel/dwarf.c | 32 +++++++++++++++----------------- arch/sh/kernel/irq.c | 4 ---- 2 files changed, 15 insertions(+), 21 deletions(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index 5fd6e604816..d0652153f57 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -26,10 +26,10 @@ #include static LIST_HEAD(dwarf_cie_list); -DEFINE_SPINLOCK(dwarf_cie_lock); +static DEFINE_SPINLOCK(dwarf_cie_lock); static LIST_HEAD(dwarf_fde_list); -DEFINE_SPINLOCK(dwarf_fde_lock); +static DEFINE_SPINLOCK(dwarf_fde_lock); static struct dwarf_cie *cached_cie; @@ -264,7 +264,7 @@ static inline int dwarf_entry_len(char *addr, unsigned long *len) */ static struct dwarf_cie *dwarf_lookup_cie(unsigned long cie_ptr) { - struct dwarf_cie *cie, *n; + struct dwarf_cie *cie; unsigned long flags; spin_lock_irqsave(&dwarf_cie_lock, flags); @@ -278,7 +278,7 @@ static struct dwarf_cie *dwarf_lookup_cie(unsigned long cie_ptr) goto out; } - list_for_each_entry_safe(cie, n, &dwarf_cie_list, link) { + list_for_each_entry(cie, &dwarf_cie_list, link) { if (cie->cie_pointer == cie_ptr) { cached_cie = cie; break; @@ -299,11 +299,12 @@ out: */ struct dwarf_fde *dwarf_lookup_fde(unsigned long pc) { + struct dwarf_fde *fde; unsigned long flags; - struct dwarf_fde *fde, *n; spin_lock_irqsave(&dwarf_fde_lock, flags); - list_for_each_entry_safe(fde, n, &dwarf_fde_list, link) { + + list_for_each_entry(fde, &dwarf_fde_list, link) { unsigned long start, end; start = fde->initial_location; @@ -787,24 +788,19 @@ static struct unwinder dwarf_unwinder = { static void dwarf_unwinder_cleanup(void) { - struct dwarf_cie *cie, *m; - struct dwarf_fde *fde, *n; - unsigned long flags; + struct dwarf_cie *cie; + struct dwarf_fde *fde; /* * Deallocate all the memory allocated for the DWARF unwinder. * Traverse all the FDE/CIE lists and remove and free all the * memory associated with those data structures. */ - spin_lock_irqsave(&dwarf_cie_lock, flags); - list_for_each_entry_safe(cie, m, &dwarf_cie_list, link) + list_for_each_entry(cie, &dwarf_cie_list, link) kfree(cie); - spin_unlock_irqrestore(&dwarf_cie_lock, flags); - spin_lock_irqsave(&dwarf_fde_lock, flags); - list_for_each_entry_safe(fde, n, &dwarf_fde_list, link) + list_for_each_entry(fde, &dwarf_fde_list, link) kfree(fde); - spin_unlock_irqrestore(&dwarf_fde_lock, flags); } /** @@ -816,7 +812,7 @@ static void dwarf_unwinder_cleanup(void) * easy to lookup the FDE for a given PC, so we build a list of FDE * and CIE entries that make it easier. */ -void dwarf_unwinder_init(void) +static int __init dwarf_unwinder_init(void) { u32 entry_type; void *p, *entry; @@ -877,9 +873,11 @@ void dwarf_unwinder_init(void) if (err) goto out; - return; + return 0; out: printk(KERN_ERR "Failed to initialise DWARF unwinder: %d\n", err); dwarf_unwinder_cleanup(); + return -EINVAL; } +early_initcall(dwarf_unwinder_init); diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index 2bb43dc74f2..278c68c6048 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -262,9 +261,6 @@ void __init init_IRQ(void) sh_mv.mv_init_irq(); irq_ctx_init(smp_processor_id()); - - /* This needs to be early, but not too early.. */ - dwarf_unwinder_init(); } #ifdef CONFIG_SPARSE_IRQ -- cgit v1.2.3 From fb3f3e7fc6d4afb32f9eba32124beaf40313de3c Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sun, 16 Aug 2009 15:44:08 +0100 Subject: sh: unwinder: Fix memory leak and create our own kmem cache Plug a memory leak in dwarf_unwinder_dump() where we didn't free the memory that we had previously allocated for the DWARF frames and DWARF registers. Now is also a opportune time to implement our own mempool and kmem cache. It's a good idea to have a certain number of frame and register objects in reserve at all times, so that we are guaranteed to have our allocation satisfied even when memory is scarce. Since we have pools to allocate from we can implement the registers for each frame as a linked list as opposed to a sparsely populated array. Whilst it's true that the lookup time for a linked list is larger than for arrays, there's only usually a maximum of 8 registers per frame. So the overhead isn't that much of a concern. Signed-off-by: Matt Fleming --- arch/sh/kernel/dwarf.c | 201 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 136 insertions(+), 65 deletions(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index d0652153f57..e4810375207 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,17 @@ #include #include +/* Reserve enough memory for two stack frames */ +#define DWARF_FRAME_MIN_REQ 2 +/* ... with 4 registers per frame. */ +#define DWARF_REG_MIN_REQ (DWARF_FRAME_MIN_REQ * 4) + +static struct kmem_cache *dwarf_frame_cachep; +static mempool_t *dwarf_frame_pool; + +static struct kmem_cache *dwarf_reg_cachep; +static mempool_t *dwarf_reg_pool; + static LIST_HEAD(dwarf_cie_list); static DEFINE_SPINLOCK(dwarf_cie_lock); @@ -33,33 +45,25 @@ static DEFINE_SPINLOCK(dwarf_fde_lock); static struct dwarf_cie *cached_cie; -/* - * Figure out whether we need to allocate some dwarf registers. If dwarf - * registers have already been allocated then we may need to realloc - * them. "reg" is a register number that we need to be able to access - * after this call. +/** + * dwarf_frame_alloc_reg - allocate memory for a DWARF register + * @frame: the DWARF frame whose list of registers we insert on + * @reg_num: the register number + * + * Allocate space for, and initialise, a dwarf reg from + * dwarf_reg_pool and insert it onto the (unsorted) linked-list of + * dwarf registers for @frame. * - * Register numbers start at zero, therefore we need to allocate space - * for "reg" + 1 registers. + * Return the initialised DWARF reg. */ -static void dwarf_frame_alloc_regs(struct dwarf_frame *frame, - unsigned int reg) +static struct dwarf_reg *dwarf_frame_alloc_reg(struct dwarf_frame *frame, + unsigned int reg_num) { - struct dwarf_reg *regs; - unsigned int num_regs = reg + 1; - size_t new_size; - size_t old_size; + struct dwarf_reg *reg; - new_size = num_regs * sizeof(*regs); - old_size = frame->num_regs * sizeof(*regs); - - /* Fast path: don't allocate any regs if we've already got enough. */ - if (frame->num_regs >= num_regs) - return; - - regs = kzalloc(new_size, GFP_ATOMIC); - if (!regs) { - printk(KERN_WARNING "Unable to allocate DWARF registers\n"); + reg = mempool_alloc(dwarf_reg_pool, GFP_ATOMIC); + if (!reg) { + printk(KERN_WARNING "Unable to allocate a DWARF register\n"); /* * Let's just bomb hard here, we have no way to * gracefully recover. @@ -67,13 +71,44 @@ static void dwarf_frame_alloc_regs(struct dwarf_frame *frame, BUG(); } - if (frame->regs) { - memcpy(regs, frame->regs, old_size); - kfree(frame->regs); + reg->number = reg_num; + reg->addr = 0; + reg->flags = 0; + + list_add(®->link, &frame->reg_list); + + return reg; +} + +static void dwarf_frame_free_regs(struct dwarf_frame *frame) +{ + struct dwarf_reg *reg, *n; + + list_for_each_entry_safe(reg, n, &frame->reg_list, link) { + list_del(®->link); + mempool_free(reg, dwarf_reg_pool); + } +} + +/** + * dwarf_frame_reg - return a DWARF register + * @frame: the DWARF frame to search in for @reg_num + * @reg_num: the register number to search for + * + * Lookup and return the dwarf reg @reg_num for this frame. Return + * NULL if @reg_num is an register invalid number. + */ +static struct dwarf_reg *dwarf_frame_reg(struct dwarf_frame *frame, + unsigned int reg_num) +{ + struct dwarf_reg *reg; + + list_for_each_entry(reg, &frame->reg_list, link) { + if (reg->number == reg_num) + return reg; } - frame->regs = regs; - frame->num_regs = num_regs; + return NULL; } /** @@ -347,6 +382,7 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, unsigned char insn; unsigned char *current_insn; unsigned int count, delta, reg, expr_len, offset; + struct dwarf_reg *regp; current_insn = insn_start; @@ -369,9 +405,9 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, count = dwarf_read_uleb128(current_insn, &offset); current_insn += count; offset *= cie->data_alignment_factor; - dwarf_frame_alloc_regs(frame, reg); - frame->regs[reg].addr = offset; - frame->regs[reg].flags |= DWARF_REG_OFFSET; + regp = dwarf_frame_alloc_reg(frame, reg); + regp->addr = offset; + regp->flags |= DWARF_REG_OFFSET; continue; /* NOTREACHED */ case DW_CFA_restore: @@ -453,17 +489,18 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, count = dwarf_read_leb128(current_insn, &offset); current_insn += count; offset *= cie->data_alignment_factor; - dwarf_frame_alloc_regs(frame, reg); - frame->regs[reg].flags |= DWARF_REG_OFFSET; - frame->regs[reg].addr = offset; + regp = dwarf_frame_alloc_reg(frame, reg); + regp->flags |= DWARF_REG_OFFSET; + regp->addr = offset; break; case DW_CFA_val_offset: count = dwarf_read_uleb128(current_insn, ®); current_insn += count; count = dwarf_read_leb128(current_insn, &offset); offset *= cie->data_alignment_factor; - frame->regs[reg].flags |= DWARF_REG_OFFSET; - frame->regs[reg].addr = offset; + regp = dwarf_frame_alloc_reg(frame, reg); + regp->flags |= DWARF_REG_OFFSET; + regp->addr = offset; break; case DW_CFA_GNU_args_size: count = dwarf_read_uleb128(current_insn, &offset); @@ -474,9 +511,10 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, current_insn += count; count = dwarf_read_uleb128(current_insn, &offset); offset *= cie->data_alignment_factor; - dwarf_frame_alloc_regs(frame, reg); - frame->regs[reg].flags |= DWARF_REG_OFFSET; - frame->regs[reg].addr = -offset; + + regp = dwarf_frame_alloc_reg(frame, reg); + regp->flags |= DWARF_REG_OFFSET; + regp->addr = -offset; break; default: pr_debug("unhandled DWARF instruction 0x%x\n", insn); @@ -502,8 +540,8 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, struct dwarf_frame *frame; struct dwarf_cie *cie; struct dwarf_fde *fde; + struct dwarf_reg *reg; unsigned long addr; - int i, offset; /* * If this is the first invocation of this recursive function we @@ -516,11 +554,16 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, if (!pc && !prev) pc = (unsigned long)current_text_addr(); - frame = kzalloc(sizeof(*frame), GFP_ATOMIC); - if (!frame) - return NULL; + frame = mempool_alloc(dwarf_frame_pool, GFP_ATOMIC); + if (!frame) { + printk(KERN_ERR "Unable to allocate a dwarf frame\n"); + BUG(); + } + INIT_LIST_HEAD(&frame->reg_list); + frame->flags = 0; frame->prev = prev; + frame->return_addr = 0; fde = dwarf_lookup_fde(pc); if (!fde) { @@ -540,7 +583,7 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, * case above, which sucks because we could print a * warning here. */ - return NULL; + goto bail; } cie = dwarf_lookup_cie(fde->cie_pointer); @@ -560,10 +603,10 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, switch (frame->flags) { case DWARF_FRAME_CFA_REG_OFFSET: if (prev) { - BUG_ON(!prev->regs[frame->cfa_register].flags); + reg = dwarf_frame_reg(prev, frame->cfa_register); + BUG_ON(!reg); - addr = prev->cfa; - addr += prev->regs[frame->cfa_register].addr; + addr = prev->cfa + reg->addr; frame->cfa = __raw_readl(addr); } else { @@ -584,23 +627,18 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, } /* If we haven't seen the return address reg, we're screwed. */ - BUG_ON(!frame->regs[DWARF_ARCH_RA_REG].flags); - - for (i = 0; i <= frame->num_regs; i++) { - struct dwarf_reg *reg = &frame->regs[i]; - - if (!reg->flags) - continue; + reg = dwarf_frame_reg(frame, DWARF_ARCH_RA_REG); + BUG_ON(!reg); - offset = reg->addr; - offset += frame->cfa; - } - - addr = frame->cfa + frame->regs[DWARF_ARCH_RA_REG].addr; + addr = frame->cfa + reg->addr; frame->return_addr = __raw_readl(addr); - frame->next = dwarf_unwind_stack(frame->return_addr, frame); return frame; + +bail: + dwarf_frame_free_regs(frame); + mempool_free(frame, dwarf_frame_pool); + return NULL; } static int dwarf_parse_cie(void *entry, void *p, unsigned long len, @@ -770,14 +808,29 @@ static void dwarf_unwinder_dump(struct task_struct *task, struct pt_regs *regs, unsigned long *sp, const struct stacktrace_ops *ops, void *data) { - struct dwarf_frame *frame; + struct dwarf_frame *frame, *_frame; + unsigned long return_addr; + + _frame = NULL; + return_addr = 0; - frame = dwarf_unwind_stack(0, NULL); + while (1) { + frame = dwarf_unwind_stack(return_addr, _frame); + + if (_frame) { + dwarf_frame_free_regs(_frame); + mempool_free(_frame, dwarf_frame_pool); + } + + _frame = frame; + + if (!frame || !frame->return_addr) + break; - while (frame && frame->return_addr) { - ops->address(data, frame->return_addr, 1); - frame = frame->next; + return_addr = frame->return_addr; + ops->address(data, return_addr, 1); } + } static struct unwinder dwarf_unwinder = { @@ -801,6 +854,9 @@ static void dwarf_unwinder_cleanup(void) list_for_each_entry(fde, &dwarf_fde_list, link) kfree(fde); + + kmem_cache_destroy(dwarf_reg_cachep); + kmem_cache_destroy(dwarf_frame_cachep); } /** @@ -827,6 +883,21 @@ static int __init dwarf_unwinder_init(void) f_entries = 0; entry = &__start_eh_frame; + dwarf_frame_cachep = kmem_cache_create("dwarf_frames", + sizeof(struct dwarf_frame), 0, SLAB_PANIC, NULL); + dwarf_reg_cachep = kmem_cache_create("dwarf_regs", + sizeof(struct dwarf_reg), 0, SLAB_PANIC, NULL); + + dwarf_frame_pool = mempool_create(DWARF_FRAME_MIN_REQ, + mempool_alloc_slab, + mempool_free_slab, + dwarf_frame_cachep); + + dwarf_reg_pool = mempool_create(DWARF_REG_MIN_REQ, + mempool_alloc_slab, + mempool_free_slab, + dwarf_reg_cachep); + while ((char *)entry < __stop_eh_frame) { p = entry; -- cgit v1.2.3 From 97efbbd5886e27b61c19c77d41f6491f5d96fbd0 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sun, 16 Aug 2009 15:56:35 +0100 Subject: sh: unwinder: Set the flags for DW_CFA_val_offset ops as DWARF_VAL_OFFSET The handling of DW_CFA_val_offset ops was incorrectly using the DWARF_REG_OFFSET flag but the register's value cannot be calculated using the DWARF_REG_OFFSET method. Create a new flag to indicate that a different method must be used to calculate the register's value even though there is no implementation for DWARF_VAL_OFFSET yet; it's mainly just a place holder. Signed-off-by: Matt Fleming --- arch/sh/kernel/dwarf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index e4810375207..d271d04adcc 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -11,6 +11,7 @@ * * TODO: * - DWARF64 doesn't work. + * - Registers with DWARF_VAL_OFFSET rules aren't handled properly. */ /* #define DEBUG */ @@ -499,7 +500,7 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, count = dwarf_read_leb128(current_insn, &offset); offset *= cie->data_alignment_factor; regp = dwarf_frame_alloc_reg(frame, reg); - regp->flags |= DWARF_REG_OFFSET; + regp->flags |= DWARF_VAL_OFFSET; regp->addr = offset; break; case DW_CFA_GNU_args_size: -- cgit v1.2.3 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/debugtraps.S | 6 +++++- arch/sh/kernel/dwarf.c | 38 +++++++++++++++++++++----------------- arch/sh/kernel/traps.c | 7 +++++-- arch/sh/kernel/traps_32.c | 1 + arch/sh/kernel/unwinder.c | 33 +++++++++++++++++++++++++++------ 5 files changed, 59 insertions(+), 26 deletions(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/debugtraps.S b/arch/sh/kernel/debugtraps.S index 591741383ee..cb00e4a82d4 100644 --- a/arch/sh/kernel/debugtraps.S +++ b/arch/sh/kernel/debugtraps.S @@ -19,6 +19,10 @@ #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 @@ -35,7 +39,7 @@ ENTRY(debug_trap_table) .long debug_trap_handler /* 0x38 */ .long debug_trap_handler /* 0x39 */ .long debug_trap_handler /* 0x3a */ - .long debug_trap_handler /* 0x3b */ + .long unwinder_trap_handler /* 0x3b */ .long breakpoint_trap_handler /* 0x3c */ .long singlestep_trap_handler /* 0x3d */ .long bug_trap_handler /* 0x3e */ diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index d271d04adcc..606ece37eb4 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -69,7 +69,7 @@ static struct dwarf_reg *dwarf_frame_alloc_reg(struct dwarf_frame *frame, * Let's just bomb hard here, we have no way to * gracefully recover. */ - BUG(); + UNWINDER_BUG(); } reg->number = reg_num; @@ -232,7 +232,7 @@ static int dwarf_read_encoded_value(char *addr, unsigned long *val, break; default: pr_debug("encoding=0x%x\n", (encoding & 0x70)); - BUG(); + UNWINDER_BUG(); } if ((encoding & 0x07) == 0x00) @@ -247,7 +247,7 @@ static int dwarf_read_encoded_value(char *addr, unsigned long *val, break; default: pr_debug("encoding=0x%x\n", encoding); - BUG(); + UNWINDER_BUG(); } return count; @@ -519,6 +519,7 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, break; default: pr_debug("unhandled DWARF instruction 0x%x\n", insn); + UNWINDER_BUG(); break; } } @@ -535,8 +536,8 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, * on the callstack. Each of the lower (older) stack frames are * linked via the "prev" member. */ -struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, - struct dwarf_frame *prev) +struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, + struct dwarf_frame *prev) { struct dwarf_frame *frame; struct dwarf_cie *cie; @@ -558,7 +559,7 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, frame = mempool_alloc(dwarf_frame_pool, GFP_ATOMIC); if (!frame) { printk(KERN_ERR "Unable to allocate a dwarf frame\n"); - BUG(); + UNWINDER_BUG(); } INIT_LIST_HEAD(&frame->reg_list); @@ -605,7 +606,8 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, case DWARF_FRAME_CFA_REG_OFFSET: if (prev) { reg = dwarf_frame_reg(prev, frame->cfa_register); - BUG_ON(!reg); + UNWINDER_BUG_ON(!reg); + UNWINDER_BUG_ON(reg->flags != DWARF_REG_OFFSET); addr = prev->cfa + reg->addr; frame->cfa = __raw_readl(addr); @@ -624,12 +626,13 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, frame->cfa += frame->cfa_offset; break; default: - BUG(); + UNWINDER_BUG(); } /* If we haven't seen the return address reg, we're screwed. */ reg = dwarf_frame_reg(frame, DWARF_ARCH_RA_REG); - BUG_ON(!reg); + UNWINDER_BUG_ON(!reg); + UNWINDER_BUG_ON(reg->flags != DWARF_REG_OFFSET); addr = frame->cfa + reg->addr; frame->return_addr = __raw_readl(addr); @@ -664,7 +667,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, cie->cie_pointer = (unsigned long)entry; cie->version = *(char *)p++; - BUG_ON(cie->version != 1); + UNWINDER_BUG_ON(cie->version != 1); cie->augmentation = p; p += strlen(cie->augmentation) + 1; @@ -694,7 +697,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, count = dwarf_read_uleb128(p, &length); p += count; - BUG_ON((unsigned char *)p > end); + UNWINDER_BUG_ON((unsigned char *)p > end); cie->initial_instructions = p + length; cie->augmentation++; @@ -722,16 +725,16 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, * routine in the CIE * augmentation. */ - BUG(); + UNWINDER_BUG(); } else if (*cie->augmentation == 'S') { - BUG(); + UNWINDER_BUG(); } else { /* * Unknown augmentation. Assume * 'z' augmentation. */ p = cie->initial_instructions; - BUG_ON(!p); + UNWINDER_BUG_ON(!p); break; } } @@ -805,9 +808,11 @@ static int dwarf_parse_fde(void *entry, u32 entry_type, return 0; } -static void dwarf_unwinder_dump(struct task_struct *task, struct pt_regs *regs, +static void dwarf_unwinder_dump(struct task_struct *task, + struct pt_regs *regs, unsigned long *sp, - const struct stacktrace_ops *ops, void *data) + const struct stacktrace_ops *ops, + void *data) { struct dwarf_frame *frame, *_frame; unsigned long return_addr; @@ -831,7 +836,6 @@ static void dwarf_unwinder_dump(struct task_struct *task, struct pt_regs *regs, return_addr = frame->return_addr; ops->address(data, return_addr, 1); } - } static struct unwinder dwarf_unwinder = { diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index b3e0067db35..881b9a32b7d 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -8,7 +8,7 @@ #include #ifdef CONFIG_BUG -static void handle_BUG(struct pt_regs *regs) +void handle_BUG(struct pt_regs *regs) { enum bug_trap_type tt; tt = report_bug(regs->pc, regs); @@ -29,7 +29,10 @@ int is_valid_bugaddr(unsigned long addr) if (probe_kernel_address((insn_size_t *)addr, opcode)) return 0; - return opcode == TRAPA_BUG_OPCODE; + if (opcode == TRAPA_BUG_OPCODE || opcode == TRAPA_UNWINDER_BUG_OPCODE) + return 1; + + return 0; } #endif diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 563426487c6..05a04b6df84 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -136,6 +136,7 @@ static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) regs->pc = fixup->fixup; return; } + die(str, regs, err); } } 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 From fe98dd31eb9fe055703e0171134b4d074558d4e9 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 20 Aug 2009 17:00:21 +0100 Subject: sh: Setup the frame pointer in handle_interrupt When CONFIG_DWARF_UNWINDER is enabled setup r14 in handle_interrupt, so that we can figure out what function was running when we were interrupted. Signed-off-by: Matt Fleming --- arch/sh/kernel/cpu/sh3/entry.S | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S index 67ad6467c69..f89ec87e3fc 100644 --- a/arch/sh/kernel/cpu/sh3/entry.S +++ b/arch/sh/kernel/cpu/sh3/entry.S @@ -508,6 +508,8 @@ ENTRY(handle_interrupt) bsr save_regs ! needs original pr value in k3 mov #-1, k2 ! default vector kept in k2 + setup_frame_reg + ! Setup return address and jump to do_IRQ mov.l 4f, r9 ! fetch return address lds r9, pr ! put return address in pr -- cgit v1.2.3 From 5480675dc60c7dda7146e506981b2b40a775cc1e Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 20 Aug 2009 19:42:34 +0100 Subject: sh: Fix bug calculating the end of the FDE instructions The 'end' member of struct dwarf_fde denotes one byte past the end of the CFA instruction stream for an FDE. The value of 'end' was being calcualted incorrectly, it was being set too high. This resulted in dwarf_cfa_execute_insns() interpreting data past the end of valid instructions, thus causing all sorts of weird crashes. Signed-off-by: Matt Fleming --- arch/sh/kernel/dwarf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index 606ece37eb4..e6f427cff5b 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -751,7 +751,8 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, } static int dwarf_parse_fde(void *entry, u32 entry_type, - void *start, unsigned long len) + void *start, unsigned long len, + unsigned char *end) { struct dwarf_fde *fde; struct dwarf_cie *cie; @@ -798,7 +799,7 @@ static int dwarf_parse_fde(void *entry, u32 entry_type, /* Call frame instructions. */ fde->instructions = p; - fde->end = start + len; + fde->end = end; /* Add to list. */ spin_lock_irqsave(&dwarf_fde_lock, flags); @@ -932,7 +933,7 @@ static int __init dwarf_unwinder_init(void) else c_entries++; } else { - err = dwarf_parse_fde(entry, entry_type, p, len); + err = dwarf_parse_fde(entry, entry_type, p, len, end); if (err < 0) goto out; else -- cgit v1.2.3 From 5580e9044df9c0e87861739d8c527006ead92e52 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 20 Aug 2009 19:53:49 +0100 Subject: sh: Handle the DWARF op, DW_CFA_undefined Allow a DWARF register to have an undefined value. When applied to the DWARF return address register this lets lets us label a function as having no direct caller, e.g. kernel_thread_helper(). Signed-off-by: Matt Fleming --- arch/sh/kernel/dwarf.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'arch/sh/kernel') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index e6f427cff5b..577302f31e6 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -452,6 +452,8 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, case DW_CFA_undefined: count = dwarf_read_uleb128(current_insn, ®); current_insn += count; + regp = dwarf_frame_alloc_reg(frame, reg); + regp->flags |= DWARF_UNDEFINED; break; case DW_CFA_def_cfa: count = dwarf_read_uleb128(current_insn, @@ -629,9 +631,16 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, UNWINDER_BUG(); } - /* If we haven't seen the return address reg, we're screwed. */ reg = dwarf_frame_reg(frame, DWARF_ARCH_RA_REG); - UNWINDER_BUG_ON(!reg); + + /* + * If we haven't seen the return address register or the return + * address column is undefined then we must assume that this is + * the end of the callstack. + */ + if (!reg || reg->flags == DWARF_UNDEFINED) + goto bail; + UNWINDER_BUG_ON(reg->flags != DWARF_REG_OFFSET); addr = frame->cfa + reg->addr; -- cgit v1.2.3 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') 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 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/kernel/debugtraps.S | 6 +----- arch/sh/kernel/traps.c | 21 +++++++++++++++++---- arch/sh/kernel/unwinder.c | 21 --------------------- 3 files changed, 18 insertions(+), 30 deletions(-) (limited to 'arch/sh/kernel') 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