Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

openrisc: support framepointers and STACKTRACE_SUPPORT

For lockdep support a reliable stack trace mechanism is needed. This
patch adds support in OpenRISC for the stacktrace framework, implemented
by a simple unwinder api. The unwinder api supports both framepointer
and basic stack tracing.

The unwinder is now used to replace the stack_dump() implementation as
well. The new traces are inline with other architectures trace format:

Call trace:
[<c0004448>] show_stack+0x3c/0x58
[<c031c940>] dump_stack+0xa8/0xe4
[<c0008104>] __cpu_up+0x64/0x130
[<c000d268>] bringup_cpu+0x3c/0x178
[<c000d038>] cpuhp_invoke_callback+0xa8/0x1fc
[<c000d680>] cpuhp_up_callbacks+0x44/0x14c
[<c000e400>] cpu_up+0x14c/0x1bc
[<c041da60>] smp_init+0x104/0x15c
[<c033843c>] ? kernel_init+0x0/0x140
[<c0415e04>] kernel_init_freeable+0xbc/0x25c
[<c033843c>] ? kernel_init+0x0/0x140
[<c0338458>] kernel_init+0x1c/0x140
[<c003a174>] ? schedule_tail+0x18/0xa0
[<c0006b80>] ret_from_fork+0x1c/0x9c

Signed-off-by: Stafford Horne <shorne@gmail.com>

+224 -48
+4
arch/openrisc/Kconfig
··· 33 33 select ARCH_USE_QUEUED_SPINLOCKS 34 34 select ARCH_USE_QUEUED_RWLOCKS 35 35 select OMPIC if SMP 36 + select ARCH_WANT_FRAME_POINTERS 36 37 37 38 config CPU_BIG_ENDIAN 38 39 def_bool y ··· 60 59 #These can be reimplemented in assembly later if so inclined 61 60 config GENERIC_CSUM 62 61 def_bool y 62 + 63 + config STACKTRACE_SUPPORT 64 + def_bool y 63 65 64 66 source "init/Kconfig" 65 67
+20
arch/openrisc/include/asm/unwinder.h
··· 1 + /* 2 + * OpenRISC unwinder.h 3 + * 4 + * Architecture API for unwinding stacks. 5 + * 6 + * Copyright (C) 2017 Stafford Horne <shorne@gmail.com> 7 + * 8 + * This file is licensed under the terms of the GNU General Public License 9 + * version 2. This program is licensed "as is" without any warranty of any 10 + * kind, whether express or implied. 11 + */ 12 + 13 + #ifndef __ASM_OPENRISC_UNWINDER_H 14 + #define __ASM_OPENRISC_UNWINDER_H 15 + 16 + void unwind_stack(void *data, unsigned long *stack, 17 + void (*trace)(void *data, unsigned long addr, 18 + int reliable)); 19 + 20 + #endif /* __ASM_OPENRISC_UNWINDER_H */
+2 -1
arch/openrisc/kernel/Makefile
··· 6 6 7 7 obj-y := setup.o or32_ksyms.o process.o dma.o \ 8 8 traps.o time.o irq.o entry.o ptrace.o signal.o \ 9 - sys_call_table.o 9 + sys_call_table.o unwinder.o 10 10 11 11 obj-$(CONFIG_SMP) += smp.o 12 + obj-$(CONFIG_STACKTRACE) += stacktrace.o 12 13 obj-$(CONFIG_MODULES) += module.o 13 14 obj-$(CONFIG_OF) += prom.o 14 15
+86
arch/openrisc/kernel/stacktrace.c
··· 1 + /* 2 + * Stack trace utility for OpenRISC 3 + * 4 + * Copyright (C) 2017 Stafford Horne <shorne@gmail.com> 5 + * 6 + * This file is licensed under the terms of the GNU General Public License 7 + * version 2. This program is licensed "as is" without any warranty of any 8 + * kind, whether express or implied. 9 + * 10 + * Losely based on work from sh and powerpc. 11 + */ 12 + 13 + #include <linux/export.h> 14 + #include <linux/sched.h> 15 + #include <linux/sched/debug.h> 16 + #include <linux/stacktrace.h> 17 + 18 + #include <asm/processor.h> 19 + #include <asm/unwinder.h> 20 + 21 + /* 22 + * Save stack-backtrace addresses into a stack_trace buffer. 23 + */ 24 + static void 25 + save_stack_address(void *data, unsigned long addr, int reliable) 26 + { 27 + struct stack_trace *trace = data; 28 + 29 + if (!reliable) 30 + return; 31 + 32 + if (trace->skip > 0) { 33 + trace->skip--; 34 + return; 35 + } 36 + 37 + if (trace->nr_entries < trace->max_entries) 38 + trace->entries[trace->nr_entries++] = addr; 39 + } 40 + 41 + void save_stack_trace(struct stack_trace *trace) 42 + { 43 + unwind_stack(trace, (unsigned long *) &trace, save_stack_address); 44 + } 45 + EXPORT_SYMBOL_GPL(save_stack_trace); 46 + 47 + static void 48 + save_stack_address_nosched(void *data, unsigned long addr, int reliable) 49 + { 50 + struct stack_trace *trace = (struct stack_trace *)data; 51 + 52 + if (!reliable) 53 + return; 54 + 55 + if (in_sched_functions(addr)) 56 + return; 57 + 58 + if (trace->skip > 0) { 59 + trace->skip--; 60 + return; 61 + } 62 + 63 + if (trace->nr_entries < trace->max_entries) 64 + trace->entries[trace->nr_entries++] = addr; 65 + } 66 + 67 + void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 68 + { 69 + unsigned long *sp = NULL; 70 + 71 + if (tsk == current) 72 + sp = (unsigned long *) &sp; 73 + else 74 + sp = (unsigned long *) KSTK_ESP(tsk); 75 + 76 + unwind_stack(trace, sp, save_stack_address_nosched); 77 + } 78 + EXPORT_SYMBOL_GPL(save_stack_trace_tsk); 79 + 80 + void 81 + save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) 82 + { 83 + unwind_stack(trace, (unsigned long *) regs->sp, 84 + save_stack_address_nosched); 85 + } 86 + EXPORT_SYMBOL_GPL(save_stack_trace_regs);
+7 -47
arch/openrisc/kernel/traps.c
··· 38 38 #include <asm/segment.h> 39 39 #include <asm/io.h> 40 40 #include <asm/pgtable.h> 41 + #include <asm/unwinder.h> 41 42 42 43 extern char _etext, _stext; 43 44 ··· 46 45 int lwa_flag; 47 46 unsigned long __user *lwa_addr; 48 47 49 - static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) 48 + void print_trace(void *data, unsigned long addr, int reliable) 50 49 { 51 - return p > (void *)tinfo && p < (void *)tinfo + THREAD_SIZE - 3; 52 - } 53 - 54 - void show_trace(struct task_struct *task, unsigned long *stack) 55 - { 56 - struct thread_info *context; 57 - unsigned long addr; 58 - 59 - context = (struct thread_info *) 60 - ((unsigned long)stack & (~(THREAD_SIZE - 1))); 61 - 62 - while (valid_stack_ptr(context, stack)) { 63 - addr = *stack++; 64 - if (__kernel_text_address(addr)) { 65 - printk(" [<%08lx>]", addr); 66 - print_symbol(" %s", addr); 67 - printk("\n"); 68 - } 69 - } 70 - printk(" =======================\n"); 50 + pr_emerg("[<%p>] %s%pS\n", (void *) addr, reliable ? "" : "? ", 51 + (void *) addr); 71 52 } 72 53 73 54 /* displays a short stack trace */ 74 55 void show_stack(struct task_struct *task, unsigned long *esp) 75 56 { 76 - unsigned long addr, *stack; 77 - int i; 78 - 79 57 if (esp == NULL) 80 58 esp = (unsigned long *)&esp; 81 59 82 - stack = esp; 83 - 84 - printk("Stack dump [0x%08lx]:\n", (unsigned long)esp); 85 - for (i = 0; i < kstack_depth_to_print; i++) { 86 - if (kstack_end(stack)) 87 - break; 88 - if (__get_user(addr, stack)) { 89 - /* This message matches "failing address" marked 90 - s390 in ksymoops, so lines containing it will 91 - not be filtered out by ksymoops. */ 92 - printk("Failing address 0x%lx\n", (unsigned long)stack); 93 - break; 94 - } 95 - stack++; 96 - 97 - printk("sp + %02d: 0x%08lx\n", i * 4, addr); 98 - } 99 - printk("\n"); 100 - 101 - show_trace(task, esp); 102 - 103 - return; 60 + pr_emerg("Call trace:\n"); 61 + unwind_stack(NULL, esp, print_trace); 104 62 } 105 63 106 64 void show_trace_task(struct task_struct *tsk) ··· 75 115 int in_kernel = 1; 76 116 unsigned long esp; 77 117 78 - esp = (unsigned long)(&regs->sp); 118 + esp = (unsigned long)(regs->sp); 79 119 if (user_mode(regs)) 80 120 in_kernel = 0; 81 121
+105
arch/openrisc/kernel/unwinder.c
··· 1 + /* 2 + * OpenRISC unwinder.c 3 + * 4 + * Reusable arch specific api for unwinding stacks. 5 + * 6 + * Copyright (C) 2017 Stafford Horne <shorne@gmail.com> 7 + * 8 + * This file is licensed under the terms of the GNU General Public License 9 + * version 2. This program is licensed "as is" without any warranty of any 10 + * kind, whether express or implied. 11 + */ 12 + 13 + #include <linux/sched/task_stack.h> 14 + #include <linux/kernel.h> 15 + 16 + #include <asm/unwinder.h> 17 + 18 + #ifdef CONFIG_FRAME_POINTER 19 + struct or1k_frameinfo { 20 + unsigned long *fp; 21 + unsigned long ra; 22 + unsigned long top; 23 + }; 24 + 25 + /* 26 + * Verify a frameinfo structure. The return address should be a valid text 27 + * address. The frame pointer may be null if its the last frame, otherwise 28 + * the frame pointer should point to a location in the stack after the the 29 + * top of the next frame up. 30 + */ 31 + static inline int or1k_frameinfo_valid(struct or1k_frameinfo *frameinfo) 32 + { 33 + return (frameinfo->fp == NULL || 34 + (!kstack_end(frameinfo->fp) && 35 + frameinfo->fp > &frameinfo->top)) && 36 + __kernel_text_address(frameinfo->ra); 37 + } 38 + 39 + /* 40 + * Create a stack trace doing scanning which is frame pointer aware. We can 41 + * get reliable stack traces by matching the previously found frame 42 + * pointer with the top of the stack address every time we find a valid 43 + * or1k_frameinfo. 44 + * 45 + * Ideally the stack parameter will be passed as FP, but it can not be 46 + * guaranteed. Therefore we scan each address looking for the first sign 47 + * of a return address. 48 + * 49 + * The OpenRISC stack frame looks something like the following. The 50 + * location SP is held in r1 and location FP is held in r2 when frame pointers 51 + * enabled. 52 + * 53 + * SP -> (top of stack) 54 + * - (callee saved registers) 55 + * - (local variables) 56 + * FP-8 -> previous FP \ 57 + * FP-4 -> return address |- or1k_frameinfo 58 + * FP -> (previous top of stack) / 59 + */ 60 + void unwind_stack(void *data, unsigned long *stack, 61 + void (*trace)(void *data, unsigned long addr, int reliable)) 62 + { 63 + unsigned long *next_fp = NULL; 64 + struct or1k_frameinfo *frameinfo = NULL; 65 + int reliable = 0; 66 + 67 + while (!kstack_end(stack)) { 68 + frameinfo = container_of(stack, 69 + struct or1k_frameinfo, 70 + top); 71 + 72 + if (__kernel_text_address(frameinfo->ra)) { 73 + if (or1k_frameinfo_valid(frameinfo) && 74 + (next_fp == NULL || 75 + next_fp == &frameinfo->top)) { 76 + reliable = 1; 77 + next_fp = frameinfo->fp; 78 + } else 79 + reliable = 0; 80 + 81 + trace(data, frameinfo->ra, reliable); 82 + } 83 + stack++; 84 + } 85 + } 86 + 87 + #else /* CONFIG_FRAME_POINTER */ 88 + 89 + /* 90 + * Create a stack trace by doing a simple scan treating all text addresses 91 + * as return addresses. 92 + */ 93 + void unwind_stack(void *data, unsigned long *stack, 94 + void (*trace)(void *data, unsigned long addr, int reliable)) 95 + { 96 + unsigned long addr; 97 + 98 + while (!kstack_end(stack)) { 99 + addr = *stack++; 100 + if (__kernel_text_address(addr)) 101 + trace(data, addr, 0); 102 + } 103 + } 104 + #endif /* CONFIG_FRAME_POINTER */ 105 +