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

s390/unwind: introduce stack unwind API

Rework the dump_trace() stack unwinder interface to support different
unwinding algorithms. The new interface looks like this:

struct unwind_state state;
unwind_for_each_frame(&state, task, regs, start_stack)
do_something(state.sp, state.ip, state.reliable);

The unwind_bc.c file contains the implementation for the classic
back-chain unwinder.

One positive side effect of the new code is it now handles ftraced
functions gracefully. It prints the real name of the return function
instead of 'return_to_handler'.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

+521 -205
-72
arch/s390/include/asm/processor.h
··· 156 156 157 157 typedef struct thread_struct thread_struct; 158 158 159 - /* 160 - * Stack layout of a C stack frame. 161 - */ 162 - #ifndef __PACK_STACK 163 - struct stack_frame { 164 - unsigned long back_chain; 165 - unsigned long empty1[5]; 166 - unsigned long gprs[10]; 167 - unsigned int empty2[8]; 168 - }; 169 - #else 170 - struct stack_frame { 171 - unsigned long empty1[5]; 172 - unsigned int empty2[8]; 173 - unsigned long gprs[10]; 174 - unsigned long back_chain; 175 - }; 176 - #endif 177 - 178 159 #define ARCH_MIN_TASKALIGN 8 179 160 180 161 #define INIT_THREAD { \ ··· 187 206 struct seq_file; 188 207 struct pt_regs; 189 208 190 - typedef int (*dump_trace_func_t)(void *data, unsigned long address, int reliable); 191 - void dump_trace(dump_trace_func_t func, void *data, 192 - struct task_struct *task, unsigned long sp); 193 209 void show_registers(struct pt_regs *regs); 194 - 195 210 void show_cacheinfo(struct seq_file *m); 196 211 197 212 /* Free all resources held by a thread. */ ··· 220 243 asm volatile("stap %0" : "=Q" (cpu_address)); 221 244 return cpu_address; 222 245 } 223 - 224 - #define CALL_ARGS_0() \ 225 - register unsigned long r2 asm("2") 226 - #define CALL_ARGS_1(arg1) \ 227 - register unsigned long r2 asm("2") = (unsigned long)(arg1) 228 - #define CALL_ARGS_2(arg1, arg2) \ 229 - CALL_ARGS_1(arg1); \ 230 - register unsigned long r3 asm("3") = (unsigned long)(arg2) 231 - #define CALL_ARGS_3(arg1, arg2, arg3) \ 232 - CALL_ARGS_2(arg1, arg2); \ 233 - register unsigned long r4 asm("4") = (unsigned long)(arg3) 234 - #define CALL_ARGS_4(arg1, arg2, arg3, arg4) \ 235 - CALL_ARGS_3(arg1, arg2, arg3); \ 236 - register unsigned long r4 asm("5") = (unsigned long)(arg4) 237 - #define CALL_ARGS_5(arg1, arg2, arg3, arg4, arg5) \ 238 - CALL_ARGS_4(arg1, arg2, arg3, arg4); \ 239 - register unsigned long r4 asm("6") = (unsigned long)(arg5) 240 - 241 - #define CALL_FMT_0 "=&d" (r2) : 242 - #define CALL_FMT_1 "+&d" (r2) : 243 - #define CALL_FMT_2 CALL_FMT_1 "d" (r3), 244 - #define CALL_FMT_3 CALL_FMT_2 "d" (r4), 245 - #define CALL_FMT_4 CALL_FMT_3 "d" (r5), 246 - #define CALL_FMT_5 CALL_FMT_4 "d" (r6), 247 - 248 - #define CALL_CLOBBER_5 "0", "1", "14", "cc", "memory" 249 - #define CALL_CLOBBER_4 CALL_CLOBBER_5 250 - #define CALL_CLOBBER_3 CALL_CLOBBER_4, "5" 251 - #define CALL_CLOBBER_2 CALL_CLOBBER_3, "4" 252 - #define CALL_CLOBBER_1 CALL_CLOBBER_2, "3" 253 - #define CALL_CLOBBER_0 CALL_CLOBBER_1 254 - 255 - #define CALL_ON_STACK(fn, stack, nr, args...) \ 256 - ({ \ 257 - CALL_ARGS_##nr(args); \ 258 - unsigned long prev; \ 259 - \ 260 - asm volatile( \ 261 - " la %[_prev],0(15)\n" \ 262 - " la 15,0(%[_stack])\n" \ 263 - " stg %[_prev],%[_bc](15)\n" \ 264 - " brasl 14,%[_fn]\n" \ 265 - " la 15,0(%[_prev])\n" \ 266 - : [_prev] "=&a" (prev), CALL_FMT_##nr \ 267 - [_stack] "a" (stack), \ 268 - [_bc] "i" (offsetof(struct stack_frame, back_chain)), \ 269 - [_fn] "X" (fn) : CALL_CLOBBER_##nr); \ 270 - r2; \ 271 - }) 272 246 273 247 /* 274 248 * Give up the time slice of the virtual PU.
+114
arch/s390/include/asm/stacktrace.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _ASM_S390_STACKTRACE_H 3 + #define _ASM_S390_STACKTRACE_H 4 + 5 + #include <linux/uaccess.h> 6 + #include <linux/ptrace.h> 7 + #include <asm/switch_to.h> 8 + 9 + enum stack_type { 10 + STACK_TYPE_UNKNOWN, 11 + STACK_TYPE_TASK, 12 + STACK_TYPE_IRQ, 13 + STACK_TYPE_NODAT, 14 + STACK_TYPE_RESTART, 15 + }; 16 + 17 + struct stack_info { 18 + enum stack_type type; 19 + unsigned long begin, end; 20 + }; 21 + 22 + const char *stack_type_name(enum stack_type type); 23 + int get_stack_info(unsigned long sp, struct task_struct *task, 24 + struct stack_info *info, unsigned long *visit_mask); 25 + 26 + static inline bool on_stack(struct stack_info *info, 27 + unsigned long addr, size_t len) 28 + { 29 + if (info->type == STACK_TYPE_UNKNOWN) 30 + return false; 31 + if (addr + len < addr) 32 + return false; 33 + return addr >= info->begin && addr + len < info->end; 34 + } 35 + 36 + static inline unsigned long get_stack_pointer(struct task_struct *task, 37 + struct pt_regs *regs) 38 + { 39 + if (regs) 40 + return (unsigned long) kernel_stack_pointer(regs); 41 + if (task == current) 42 + return current_stack_pointer(); 43 + return (unsigned long) task->thread.ksp; 44 + } 45 + 46 + /* 47 + * Stack layout of a C stack frame. 48 + */ 49 + #ifndef __PACK_STACK 50 + struct stack_frame { 51 + unsigned long back_chain; 52 + unsigned long empty1[5]; 53 + unsigned long gprs[10]; 54 + unsigned int empty2[8]; 55 + }; 56 + #else 57 + struct stack_frame { 58 + unsigned long empty1[5]; 59 + unsigned int empty2[8]; 60 + unsigned long gprs[10]; 61 + unsigned long back_chain; 62 + }; 63 + #endif 64 + 65 + #define CALL_ARGS_0() \ 66 + register unsigned long r2 asm("2") 67 + #define CALL_ARGS_1(arg1) \ 68 + register unsigned long r2 asm("2") = (unsigned long)(arg1) 69 + #define CALL_ARGS_2(arg1, arg2) \ 70 + CALL_ARGS_1(arg1); \ 71 + register unsigned long r3 asm("3") = (unsigned long)(arg2) 72 + #define CALL_ARGS_3(arg1, arg2, arg3) \ 73 + CALL_ARGS_2(arg1, arg2); \ 74 + register unsigned long r4 asm("4") = (unsigned long)(arg3) 75 + #define CALL_ARGS_4(arg1, arg2, arg3, arg4) \ 76 + CALL_ARGS_3(arg1, arg2, arg3); \ 77 + register unsigned long r4 asm("5") = (unsigned long)(arg4) 78 + #define CALL_ARGS_5(arg1, arg2, arg3, arg4, arg5) \ 79 + CALL_ARGS_4(arg1, arg2, arg3, arg4); \ 80 + register unsigned long r4 asm("6") = (unsigned long)(arg5) 81 + 82 + #define CALL_FMT_0 "=&d" (r2) : 83 + #define CALL_FMT_1 "+&d" (r2) : 84 + #define CALL_FMT_2 CALL_FMT_1 "d" (r3), 85 + #define CALL_FMT_3 CALL_FMT_2 "d" (r4), 86 + #define CALL_FMT_4 CALL_FMT_3 "d" (r5), 87 + #define CALL_FMT_5 CALL_FMT_4 "d" (r6), 88 + 89 + #define CALL_CLOBBER_5 "0", "1", "14", "cc", "memory" 90 + #define CALL_CLOBBER_4 CALL_CLOBBER_5 91 + #define CALL_CLOBBER_3 CALL_CLOBBER_4, "5" 92 + #define CALL_CLOBBER_2 CALL_CLOBBER_3, "4" 93 + #define CALL_CLOBBER_1 CALL_CLOBBER_2, "3" 94 + #define CALL_CLOBBER_0 CALL_CLOBBER_1 95 + 96 + #define CALL_ON_STACK(fn, stack, nr, args...) \ 97 + ({ \ 98 + CALL_ARGS_##nr(args); \ 99 + unsigned long prev; \ 100 + \ 101 + asm volatile( \ 102 + " la %[_prev],0(15)\n" \ 103 + " la 15,0(%[_stack])\n" \ 104 + " stg %[_prev],%[_bc](15)\n" \ 105 + " brasl 14,%[_fn]\n" \ 106 + " la 15,0(%[_prev])\n" \ 107 + : [_prev] "=&a" (prev), CALL_FMT_##nr \ 108 + [_stack] "a" (stack), \ 109 + [_bc] "i" (offsetof(struct stack_frame, back_chain)), \ 110 + [_fn] "X" (fn) : CALL_CLOBBER_##nr); \ 111 + r2; \ 112 + }) 113 + 114 + #endif /* _ASM_S390_STACKTRACE_H */
+101
arch/s390/include/asm/unwind.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _ASM_S390_UNWIND_H 3 + #define _ASM_S390_UNWIND_H 4 + 5 + #include <linux/sched.h> 6 + #include <linux/ftrace.h> 7 + #include <asm/ptrace.h> 8 + #include <asm/stacktrace.h> 9 + 10 + /* 11 + * To use the stack unwinder it has to be initialized with unwind_start. 12 + * There four combinations for task and regs: 13 + * 1) task==NULL, regs==NULL: the unwind starts for the task that is currently 14 + * running, sp/ip picked up from the CPU registers 15 + * 2) task==NULL, regs!=NULL: the unwind starts from the sp/ip found in 16 + * the struct pt_regs of an interrupt frame for the current task 17 + * 3) task!=NULL, regs==NULL: the unwind starts for an inactive task with 18 + * the sp picked up from task->thread.ksp and the ip picked up from the 19 + * return address stored by __switch_to 20 + * 4) task!=NULL, regs!=NULL: the sp/ip are picked up from the interrupt 21 + * frame 'regs' of a inactive task 22 + * If 'first_frame' is not zero unwind_start skips unwind frames until it 23 + * reaches the specified stack pointer. 24 + * The end of the unwinding is indicated with unwind_done, this can be true 25 + * right after unwind_start, e.g. with first_frame!=0 that can not be found. 26 + * unwind_next_frame skips to the next frame. 27 + * Once the unwind is completed unwind_error() can be used to check if there 28 + * has been a situation where the unwinder could not correctly understand 29 + * the tasks call chain. 30 + */ 31 + 32 + struct unwind_state { 33 + struct stack_info stack_info; 34 + unsigned long stack_mask; 35 + struct task_struct *task; 36 + struct pt_regs *regs; 37 + unsigned long sp, ip; 38 + int graph_idx; 39 + bool reliable; 40 + bool error; 41 + }; 42 + 43 + void __unwind_start(struct unwind_state *state, struct task_struct *task, 44 + struct pt_regs *regs, unsigned long first_frame); 45 + bool unwind_next_frame(struct unwind_state *state); 46 + unsigned long unwind_get_return_address(struct unwind_state *state); 47 + 48 + static inline bool unwind_done(struct unwind_state *state) 49 + { 50 + return state->stack_info.type == STACK_TYPE_UNKNOWN; 51 + } 52 + 53 + static inline bool unwind_error(struct unwind_state *state) 54 + { 55 + return state->error; 56 + } 57 + 58 + static inline void unwind_start(struct unwind_state *state, 59 + struct task_struct *task, 60 + struct pt_regs *regs, 61 + unsigned long sp) 62 + { 63 + sp = sp ? : get_stack_pointer(task, regs); 64 + __unwind_start(state, task, regs, sp); 65 + } 66 + 67 + static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state) 68 + { 69 + return unwind_done(state) ? NULL : state->regs; 70 + } 71 + 72 + #define unwind_for_each_frame(state, task, regs, first_frame) \ 73 + for (unwind_start(state, task, regs, first_frame); \ 74 + !unwind_done(state); \ 75 + unwind_next_frame(state)) 76 + 77 + static inline void unwind_init(void) {} 78 + static inline void unwind_module_init(struct module *mod, void *orc_ip, 79 + size_t orc_ip_size, void *orc, 80 + size_t orc_size) {} 81 + 82 + #ifdef CONFIG_KASAN 83 + /* 84 + * This disables KASAN checking when reading a value from another task's stack, 85 + * since the other task could be running on another CPU and could have poisoned 86 + * the stack in the meantime. 87 + */ 88 + #define READ_ONCE_TASK_STACK(task, x) \ 89 + ({ \ 90 + unsigned long val; \ 91 + if (task == current) \ 92 + val = READ_ONCE(x); \ 93 + else \ 94 + val = READ_ONCE_NOCHECK(x); \ 95 + val; \ 96 + }) 97 + #else 98 + #define READ_ONCE_TASK_STACK(task, x) READ_ONCE(x) 99 + #endif 100 + 101 + #endif /* _ASM_S390_UNWIND_H */
+2 -1
arch/s390/kernel/Makefile
··· 39 39 # 40 40 CFLAGS_stacktrace.o += -fno-optimize-sibling-calls 41 41 CFLAGS_dumpstack.o += -fno-optimize-sibling-calls 42 + CFLAGS_unwind_bc.o += -fno-optimize-sibling-calls 42 43 43 44 # 44 45 # Pass UTS_MACHINE for user_regset definition ··· 52 51 obj-y += sysinfo.o lgr.o os_info.o machine_kexec.o pgm_check.o 53 52 obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o 54 53 obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o 55 - obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o 54 + obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o unwind_bc.o 56 55 57 56 extra-y += head64.o vmlinux.lds 58 57
+1
arch/s390/kernel/asm-offsets.c
··· 16 16 #include <asm/pgtable.h> 17 17 #include <asm/gmap.h> 18 18 #include <asm/nmi.h> 19 + #include <asm/stacktrace.h> 19 20 20 21 int main(void) 21 22 {
+98 -69
arch/s390/kernel/dumpstack.c
··· 21 21 #include <asm/debug.h> 22 22 #include <asm/dis.h> 23 23 #include <asm/ipl.h> 24 + #include <asm/unwind.h> 24 25 25 - /* 26 - * For dump_trace we have tree different stack to consider: 27 - * - the panic stack which is used if the kernel stack has overflown 28 - * - the asynchronous interrupt stack (cpu related) 29 - * - the synchronous kernel stack (process related) 30 - * The stack trace can start at any of the three stacks and can potentially 31 - * touch all of them. The order is: panic stack, async stack, sync stack. 32 - */ 33 - static unsigned long __no_sanitize_address 34 - __dump_trace(dump_trace_func_t func, void *data, unsigned long sp, 35 - unsigned long low, unsigned long high) 26 + const char *stack_type_name(enum stack_type type) 36 27 { 37 - struct stack_frame *sf; 38 - struct pt_regs *regs; 39 - 40 - while (1) { 41 - if (sp < low || sp > high - sizeof(*sf)) 42 - return sp; 43 - sf = (struct stack_frame *) sp; 44 - if (func(data, sf->gprs[8], 0)) 45 - return sp; 46 - /* Follow the backchain. */ 47 - while (1) { 48 - low = sp; 49 - sp = sf->back_chain; 50 - if (!sp) 51 - break; 52 - if (sp <= low || sp > high - sizeof(*sf)) 53 - return sp; 54 - sf = (struct stack_frame *) sp; 55 - if (func(data, sf->gprs[8], 1)) 56 - return sp; 57 - } 58 - /* Zero backchain detected, check for interrupt frame. */ 59 - sp = (unsigned long) (sf + 1); 60 - if (sp <= low || sp > high - sizeof(*regs)) 61 - return sp; 62 - regs = (struct pt_regs *) sp; 63 - if (!user_mode(regs)) { 64 - if (func(data, regs->psw.addr, 1)) 65 - return sp; 66 - } 67 - low = sp; 68 - sp = regs->gprs[15]; 28 + switch (type) { 29 + case STACK_TYPE_TASK: 30 + return "task"; 31 + case STACK_TYPE_IRQ: 32 + return "irq"; 33 + case STACK_TYPE_NODAT: 34 + return "nodat"; 35 + case STACK_TYPE_RESTART: 36 + return "restart"; 37 + default: 38 + return "unknown"; 69 39 } 70 40 } 71 41 72 - void dump_trace(dump_trace_func_t func, void *data, struct task_struct *task, 73 - unsigned long sp) 42 + static inline bool in_stack(unsigned long sp, struct stack_info *info, 43 + enum stack_type type, unsigned long low, 44 + unsigned long high) 74 45 { 75 - unsigned long frame_size; 46 + if (sp < low || sp >= high) 47 + return false; 48 + info->type = type; 49 + info->begin = low; 50 + info->end = high; 51 + return true; 52 + } 53 + 54 + static bool in_task_stack(unsigned long sp, struct task_struct *task, 55 + struct stack_info *info) 56 + { 57 + unsigned long stack; 58 + 59 + stack = (unsigned long) task_stack_page(task); 60 + return in_stack(sp, info, STACK_TYPE_TASK, stack, stack + THREAD_SIZE); 61 + } 62 + 63 + static bool in_irq_stack(unsigned long sp, struct stack_info *info) 64 + { 65 + unsigned long frame_size, top; 76 66 77 67 frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs); 78 - #ifdef CONFIG_CHECK_STACK 79 - sp = __dump_trace(func, data, sp, 80 - S390_lowcore.nodat_stack + frame_size - THREAD_SIZE, 81 - S390_lowcore.nodat_stack + frame_size); 82 - #endif 83 - sp = __dump_trace(func, data, sp, 84 - S390_lowcore.async_stack + frame_size - THREAD_SIZE, 85 - S390_lowcore.async_stack + frame_size); 86 - task = task ?: current; 87 - __dump_trace(func, data, sp, 88 - (unsigned long)task_stack_page(task), 89 - (unsigned long)task_stack_page(task) + THREAD_SIZE); 68 + top = S390_lowcore.async_stack + frame_size; 69 + return in_stack(sp, info, STACK_TYPE_IRQ, top - THREAD_SIZE, top); 90 70 } 91 - EXPORT_SYMBOL_GPL(dump_trace); 92 71 93 - static int show_address(void *data, unsigned long address, int reliable) 72 + static bool in_nodat_stack(unsigned long sp, struct stack_info *info) 94 73 { 95 - if (reliable) 96 - printk(" [<%016lx>] %pSR \n", address, (void *)address); 97 - else 98 - printk("([<%016lx>] %pSR)\n", address, (void *)address); 74 + unsigned long frame_size, top; 75 + 76 + frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs); 77 + top = S390_lowcore.nodat_stack + frame_size; 78 + return in_stack(sp, info, STACK_TYPE_NODAT, top - THREAD_SIZE, top); 79 + } 80 + 81 + static bool in_restart_stack(unsigned long sp, struct stack_info *info) 82 + { 83 + unsigned long frame_size, top; 84 + 85 + frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs); 86 + top = S390_lowcore.restart_stack + frame_size; 87 + return in_stack(sp, info, STACK_TYPE_RESTART, top - THREAD_SIZE, top); 88 + } 89 + 90 + int get_stack_info(unsigned long sp, struct task_struct *task, 91 + struct stack_info *info, unsigned long *visit_mask) 92 + { 93 + if (!sp) 94 + goto unknown; 95 + 96 + task = task ? : current; 97 + 98 + /* Check per-task stack */ 99 + if (in_task_stack(sp, task, info)) 100 + goto recursion_check; 101 + 102 + if (task != current) 103 + goto unknown; 104 + 105 + /* Check per-cpu stacks */ 106 + if (!in_irq_stack(sp, info) && 107 + !in_nodat_stack(sp, info) && 108 + !in_restart_stack(sp, info)) 109 + goto unknown; 110 + 111 + recursion_check: 112 + /* 113 + * Make sure we don't iterate through any given stack more than once. 114 + * If it comes up a second time then there's something wrong going on: 115 + * just break out and report an unknown stack type. 116 + */ 117 + if (*visit_mask & (1UL << info->type)) { 118 + printk_deferred_once(KERN_WARNING 119 + "WARNING: stack recursion on stack type %d\n", 120 + info->type); 121 + goto unknown; 122 + } 123 + *visit_mask |= 1UL << info->type; 99 124 return 0; 125 + unknown: 126 + info->type = STACK_TYPE_UNKNOWN; 127 + return -EINVAL; 100 128 } 101 129 102 130 void show_stack(struct task_struct *task, unsigned long *stack) 103 131 { 104 - unsigned long sp = (unsigned long) stack; 132 + struct unwind_state state; 105 133 106 - if (!sp) 107 - sp = task ? task->thread.ksp : current_stack_pointer(); 108 134 printk("Call Trace:\n"); 109 - dump_trace(show_address, NULL, task, sp); 110 135 if (!task) 111 136 task = current; 112 - debug_show_held_locks(task); 137 + unwind_for_each_frame(&state, task, NULL, (unsigned long) stack) 138 + printk(state.reliable ? " [<%016lx>] %pSR \n" : 139 + "([<%016lx>] %pSR)\n", 140 + state.ip, (void *) state.ip); 141 + debug_show_held_locks(task ? : current); 113 142 } 114 143 115 144 static void show_last_breaking_event(struct pt_regs *regs)
+1
arch/s390/kernel/irq.c
··· 26 26 #include <asm/lowcore.h> 27 27 #include <asm/irq.h> 28 28 #include <asm/hw_irq.h> 29 + #include <asm/stacktrace.h> 29 30 #include "entry.h" 30 31 31 32 DEFINE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat);
+1
arch/s390/kernel/machine_kexec.c
··· 27 27 #include <asm/cacheflush.h> 28 28 #include <asm/os_info.h> 29 29 #include <asm/set_memory.h> 30 + #include <asm/stacktrace.h> 30 31 #include <asm/switch_to.h> 31 32 #include <asm/nmi.h> 32 33
+5 -11
arch/s390/kernel/perf_event.c
··· 21 21 #include <asm/lowcore.h> 22 22 #include <asm/processor.h> 23 23 #include <asm/sysinfo.h> 24 + #include <asm/unwind.h> 24 25 25 26 const char *perf_pmu_name(void) 26 27 { ··· 220 219 } 221 220 arch_initcall(service_level_perf_register); 222 221 223 - static int __perf_callchain_kernel(void *data, unsigned long address, int reliable) 224 - { 225 - struct perf_callchain_entry_ctx *entry = data; 226 - 227 - perf_callchain_store(entry, address); 228 - return 0; 229 - } 230 - 231 222 void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, 232 223 struct pt_regs *regs) 233 224 { 234 - if (user_mode(regs)) 235 - return; 236 - dump_trace(__perf_callchain_kernel, entry, NULL, regs->gprs[15]); 225 + struct unwind_state state; 226 + 227 + unwind_for_each_frame(&state, current, regs, 0) 228 + perf_callchain_store(entry, state.ip); 237 229 } 238 230 239 231 /* Perf definitions for PMU event attributes in sysfs */
+1
arch/s390/kernel/process.c
··· 37 37 #include <asm/irq.h> 38 38 #include <asm/nmi.h> 39 39 #include <asm/smp.h> 40 + #include <asm/stacktrace.h> 40 41 #include <asm/switch_to.h> 41 42 #include <asm/runtime_instr.h> 42 43 #include "entry.h"
+1
arch/s390/kernel/setup.c
··· 66 66 #include <asm/diag.h> 67 67 #include <asm/os_info.h> 68 68 #include <asm/sclp.h> 69 + #include <asm/stacktrace.h> 69 70 #include <asm/sysinfo.h> 70 71 #include <asm/numa.h> 71 72 #include <asm/alternative.h>
+1
arch/s390/kernel/smp.c
··· 53 53 #include <asm/sigp.h> 54 54 #include <asm/idle.h> 55 55 #include <asm/nmi.h> 56 + #include <asm/stacktrace.h> 56 57 #include <asm/topology.h> 57 58 #include "entry.h" 58 59
+31 -38
arch/s390/kernel/stacktrace.c
··· 11 11 #include <linux/stacktrace.h> 12 12 #include <linux/kallsyms.h> 13 13 #include <linux/export.h> 14 - 15 - static int __save_address(void *data, unsigned long address, int nosched) 16 - { 17 - struct stack_trace *trace = data; 18 - 19 - if (nosched && in_sched_functions(address)) 20 - return 0; 21 - if (trace->skip > 0) { 22 - trace->skip--; 23 - return 0; 24 - } 25 - if (trace->nr_entries < trace->max_entries) { 26 - trace->entries[trace->nr_entries++] = address; 27 - return 0; 28 - } 29 - return 1; 30 - } 31 - 32 - static int save_address(void *data, unsigned long address, int reliable) 33 - { 34 - return __save_address(data, address, 0); 35 - } 36 - 37 - static int save_address_nosched(void *data, unsigned long address, int reliable) 38 - { 39 - return __save_address(data, address, 1); 40 - } 14 + #include <asm/stacktrace.h> 15 + #include <asm/unwind.h> 41 16 42 17 void save_stack_trace(struct stack_trace *trace) 43 18 { 44 - unsigned long sp; 19 + struct unwind_state state; 45 20 46 - sp = current_stack_pointer(); 47 - dump_trace(save_address, trace, NULL, sp); 21 + unwind_for_each_frame(&state, current, NULL, 0) { 22 + if (trace->nr_entries >= trace->max_entries) 23 + break; 24 + if (trace->skip > 0) 25 + trace->skip--; 26 + else 27 + trace->entries[trace->nr_entries++] = state.ip; 28 + } 48 29 if (trace->nr_entries < trace->max_entries) 49 30 trace->entries[trace->nr_entries++] = ULONG_MAX; 50 31 } ··· 33 52 34 53 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 35 54 { 36 - unsigned long sp; 55 + struct unwind_state state; 37 56 38 - sp = tsk->thread.ksp; 39 - if (tsk == current) 40 - sp = current_stack_pointer(); 41 - dump_trace(save_address_nosched, trace, tsk, sp); 57 + unwind_for_each_frame(&state, tsk, NULL, 0) { 58 + if (trace->nr_entries >= trace->max_entries) 59 + break; 60 + if (in_sched_functions(state.ip)) 61 + continue; 62 + if (trace->skip > 0) 63 + trace->skip--; 64 + else 65 + trace->entries[trace->nr_entries++] = state.ip; 66 + } 42 67 if (trace->nr_entries < trace->max_entries) 43 68 trace->entries[trace->nr_entries++] = ULONG_MAX; 44 69 } ··· 52 65 53 66 void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) 54 67 { 55 - unsigned long sp; 68 + struct unwind_state state; 56 69 57 - sp = kernel_stack_pointer(regs); 58 - dump_trace(save_address, trace, NULL, sp); 70 + unwind_for_each_frame(&state, current, regs, 0) { 71 + if (trace->nr_entries >= trace->max_entries) 72 + break; 73 + if (trace->skip > 0) 74 + trace->skip--; 75 + else 76 + trace->entries[trace->nr_entries++] = state.ip; 77 + } 59 78 if (trace->nr_entries < trace->max_entries) 60 79 trace->entries[trace->nr_entries++] = ULONG_MAX; 61 80 }
+155
arch/s390/kernel/unwind_bc.c
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #include <linux/sched.h> 3 + #include <linux/sched/task.h> 4 + #include <linux/sched/task_stack.h> 5 + #include <linux/interrupt.h> 6 + #include <asm/sections.h> 7 + #include <asm/ptrace.h> 8 + #include <asm/bitops.h> 9 + #include <asm/stacktrace.h> 10 + #include <asm/unwind.h> 11 + 12 + unsigned long unwind_get_return_address(struct unwind_state *state) 13 + { 14 + if (unwind_done(state)) 15 + return 0; 16 + return __kernel_text_address(state->ip) ? state->ip : 0; 17 + } 18 + EXPORT_SYMBOL_GPL(unwind_get_return_address); 19 + 20 + static bool outside_of_stack(struct unwind_state *state, unsigned long sp) 21 + { 22 + return (sp <= state->sp) || 23 + (sp + sizeof(struct stack_frame) > state->stack_info.end); 24 + } 25 + 26 + static bool update_stack_info(struct unwind_state *state, unsigned long sp) 27 + { 28 + struct stack_info *info = &state->stack_info; 29 + unsigned long *mask = &state->stack_mask; 30 + 31 + /* New stack pointer leaves the current stack */ 32 + if (get_stack_info(sp, state->task, info, mask) != 0 || 33 + !on_stack(info, sp, sizeof(struct stack_frame))) 34 + /* 'sp' does not point to a valid stack */ 35 + return false; 36 + return true; 37 + } 38 + 39 + bool unwind_next_frame(struct unwind_state *state) 40 + { 41 + struct stack_info *info = &state->stack_info; 42 + struct stack_frame *sf; 43 + struct pt_regs *regs; 44 + unsigned long sp, ip; 45 + bool reliable; 46 + 47 + regs = state->regs; 48 + if (unlikely(regs)) { 49 + sp = READ_ONCE_TASK_STACK(state->task, regs->gprs[15]); 50 + if (unlikely(outside_of_stack(state, sp))) { 51 + if (!update_stack_info(state, sp)) 52 + goto out_err; 53 + } 54 + sf = (struct stack_frame *) sp; 55 + ip = READ_ONCE_TASK_STACK(state->task, sf->gprs[8]); 56 + reliable = false; 57 + regs = NULL; 58 + } else { 59 + sf = (struct stack_frame *) state->sp; 60 + sp = READ_ONCE_TASK_STACK(state->task, sf->back_chain); 61 + if (likely(sp)) { 62 + /* Non-zero back-chain points to the previous frame */ 63 + if (unlikely(outside_of_stack(state, sp))) { 64 + if (!update_stack_info(state, sp)) 65 + goto out_err; 66 + } 67 + sf = (struct stack_frame *) sp; 68 + ip = READ_ONCE_TASK_STACK(state->task, sf->gprs[8]); 69 + reliable = true; 70 + } else { 71 + /* No back-chain, look for a pt_regs structure */ 72 + sp = state->sp + STACK_FRAME_OVERHEAD; 73 + if (!on_stack(info, sp, sizeof(struct pt_regs))) 74 + goto out_stop; 75 + regs = (struct pt_regs *) sp; 76 + if (user_mode(regs)) 77 + goto out_stop; 78 + ip = READ_ONCE_TASK_STACK(state->task, regs->psw.addr); 79 + reliable = true; 80 + } 81 + } 82 + 83 + #ifdef CONFIG_FUNCTION_GRAPH_TRACER 84 + /* Decode any ftrace redirection */ 85 + if (ip == (unsigned long) return_to_handler) 86 + ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, 87 + ip, NULL); 88 + #endif 89 + 90 + /* Update unwind state */ 91 + state->sp = sp; 92 + state->ip = ip; 93 + state->regs = regs; 94 + state->reliable = reliable; 95 + return true; 96 + 97 + out_err: 98 + state->error = true; 99 + out_stop: 100 + state->stack_info.type = STACK_TYPE_UNKNOWN; 101 + return false; 102 + } 103 + EXPORT_SYMBOL_GPL(unwind_next_frame); 104 + 105 + void __unwind_start(struct unwind_state *state, struct task_struct *task, 106 + struct pt_regs *regs, unsigned long sp) 107 + { 108 + struct stack_info *info = &state->stack_info; 109 + unsigned long *mask = &state->stack_mask; 110 + struct stack_frame *sf; 111 + unsigned long ip; 112 + bool reliable; 113 + 114 + memset(state, 0, sizeof(*state)); 115 + state->task = task; 116 + state->regs = regs; 117 + 118 + /* Don't even attempt to start from user mode regs: */ 119 + if (regs && user_mode(regs)) { 120 + info->type = STACK_TYPE_UNKNOWN; 121 + return; 122 + } 123 + 124 + /* Get current stack pointer and initialize stack info */ 125 + if (get_stack_info(sp, task, info, mask) != 0 || 126 + !on_stack(info, sp, sizeof(struct stack_frame))) { 127 + /* Something is wrong with the stack pointer */ 128 + info->type = STACK_TYPE_UNKNOWN; 129 + state->error = true; 130 + return; 131 + } 132 + 133 + /* Get the instruction pointer from pt_regs or the stack frame */ 134 + if (regs) { 135 + ip = READ_ONCE_TASK_STACK(state->task, regs->psw.addr); 136 + reliable = true; 137 + } else { 138 + sf = (struct stack_frame *) sp; 139 + ip = READ_ONCE_TASK_STACK(state->task, sf->gprs[8]); 140 + reliable = false; 141 + } 142 + 143 + #ifdef CONFIG_FUNCTION_GRAPH_TRACER 144 + /* Decode any ftrace redirection */ 145 + if (ip == (unsigned long) return_to_handler) 146 + ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, 147 + ip, NULL); 148 + #endif 149 + 150 + /* Update unwind state */ 151 + state->sp = sp; 152 + state->ip = ip; 153 + state->reliable = reliable; 154 + } 155 + EXPORT_SYMBOL_GPL(__unwind_start);
+1
arch/s390/mm/maccess.c
··· 16 16 #include <linux/cpu.h> 17 17 #include <asm/ctl_reg.h> 18 18 #include <asm/io.h> 19 + #include <asm/stacktrace.h> 19 20 20 21 static notrace long s390_kernel_write_odd(void *dst, const void *src, size_t size) 21 22 {
+8 -14
arch/s390/oprofile/init.c
··· 13 13 #include <linux/oprofile.h> 14 14 #include <linux/init.h> 15 15 #include <asm/processor.h> 16 - 17 - static int __s390_backtrace(void *data, unsigned long address, int reliable) 18 - { 19 - unsigned int *depth = data; 20 - 21 - if (*depth == 0) 22 - return 1; 23 - (*depth)--; 24 - oprofile_add_trace(address); 25 - return 0; 26 - } 16 + #include <asm/unwind.h> 27 17 28 18 static void s390_backtrace(struct pt_regs *regs, unsigned int depth) 29 19 { 30 - if (user_mode(regs)) 31 - return; 32 - dump_trace(__s390_backtrace, &depth, NULL, regs->gprs[15]); 20 + struct unwind_state state; 21 + 22 + unwind_for_each_frame(&state, current, regs, 0) { 23 + if (depth-- == 0) 24 + break; 25 + oprofile_add_trace(state.ip); 26 + } 33 27 } 34 28 35 29 int __init oprofile_arch_init(struct oprofile_operations *ops)