From: Wu Liliu wuliliu@wxiat.com
Sunway inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I5GFQ0
--------------------------------
It used to show stack information by SP, that means it has nothing to do with task, which is unexpected. In order to show stack of task, we improve the implementation of show_stack().
However, the original task struct does not have sp, so we add it into thread_struct and do something necessary in __switch_to.
Meanwhile, walk_stackframe() can be reused, so refactor related codes for better support.
Signed-off-by: Wu Liliu wuliliu@wxiat.com
Signed-off-by: Gu Zitao guzitao@wxiat.com --- arch/sw_64/include/asm/processor.h | 1 + arch/sw_64/include/asm/stacktrace.h | 4 +- arch/sw_64/kernel/asm-offsets.c | 1 + arch/sw_64/kernel/entry.S | 6 +++ arch/sw_64/kernel/perf_event.c | 12 ++--- arch/sw_64/kernel/process.c | 1 + arch/sw_64/kernel/stacktrace.c | 78 +++++++++++++++++++++++------ arch/sw_64/kernel/traps.c | 58 ++++----------------- 8 files changed, 85 insertions(+), 76 deletions(-)
diff --git a/arch/sw_64/include/asm/processor.h b/arch/sw_64/include/asm/processor.h index 08e8cc8e5428..886f28635dd4 100644 --- a/arch/sw_64/include/asm/processor.h +++ b/arch/sw_64/include/asm/processor.h @@ -45,6 +45,7 @@ struct thread_struct { struct user_fpsimd_state fpstate; /* Callee-saved registers */ unsigned long ra; + unsigned long sp; unsigned long s[7]; /* s0 ~ s6 */ }; #define INIT_THREAD { } diff --git a/arch/sw_64/include/asm/stacktrace.h b/arch/sw_64/include/asm/stacktrace.h index 813aa5e7a91d..ed691a72573b 100644 --- a/arch/sw_64/include/asm/stacktrace.h +++ b/arch/sw_64/include/asm/stacktrace.h @@ -32,8 +32,8 @@ struct stack_frame { };
extern int unwind_frame(struct task_struct *tsk, struct stackframe *frame); -extern void walk_stackframe(struct task_struct *tsk, struct stackframe *frame, - int (*fn)(struct stackframe *, void *), void *data); +extern void walk_stackframe(struct task_struct *tsk, struct pt_regs *regs, + int (*fn)(unsigned long, void *), void *data);
static inline bool on_task_stack(struct task_struct *tsk, unsigned long sp, struct stack_info *info) diff --git a/arch/sw_64/kernel/asm-offsets.c b/arch/sw_64/kernel/asm-offsets.c index 0c7e1e26eb05..9e6c338a5edd 100644 --- a/arch/sw_64/kernel/asm-offsets.c +++ b/arch/sw_64/kernel/asm-offsets.c @@ -213,6 +213,7 @@ void foo(void) OFFSET(TASK_THREAD_FPCR, task_struct, thread.fpstate.fpcr); BLANK(); OFFSET(TASK_THREAD_RA, task_struct, thread.ra); + OFFSET(TASK_THREAD_SP, task_struct, thread.sp); OFFSET(TASK_THREAD_S0, task_struct, thread.s[0]); OFFSET(TASK_THREAD_S1, task_struct, thread.s[1]); OFFSET(TASK_THREAD_S2, task_struct, thread.s[2]); diff --git a/arch/sw_64/kernel/entry.S b/arch/sw_64/kernel/entry.S index 977c774ad799..f79c9a6ddf36 100644 --- a/arch/sw_64/kernel/entry.S +++ b/arch/sw_64/kernel/entry.S @@ -398,6 +398,7 @@ __switch_to: .prologue 0 /* Save context into prev->thread */ stl $26, TASK_THREAD_RA($17) + stl $30, TASK_THREAD_SP($17) stl $9, TASK_THREAD_S0($17) stl $10, TASK_THREAD_S1($17) stl $11, TASK_THREAD_S2($17) @@ -415,6 +416,11 @@ __switch_to: ldl $14, TASK_THREAD_S5($18) ldl $15, TASK_THREAD_S6($18) sys_call HMC_swpctx + /* + * SP has been saved and restored by HMC_swpctx, + * and restore it again here for future expansion. + */ + ldl $30, TASK_THREAD_SP($18) ldi $8, 0x3fff bic $sp, $8, $8 mov $17, $0 diff --git a/arch/sw_64/kernel/perf_event.c b/arch/sw_64/kernel/perf_event.c index 52ec34e33269..6e344239917b 100644 --- a/arch/sw_64/kernel/perf_event.c +++ b/arch/sw_64/kernel/perf_event.c @@ -761,24 +761,18 @@ void perf_callchain_user(struct perf_callchain_entry_ctx *entry, * whist unwinding the stackframe and is like a subroutine return so we use * the PC. */ -static int callchain_trace(struct stackframe *frame, void *data) +static int callchain_trace(unsigned long pc, void *data) { struct perf_callchain_entry_ctx *entry = data;
- perf_callchain_store(entry, frame->pc); - + perf_callchain_store(entry, pc); return 0; }
void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs) { - struct stackframe frame; - - frame.fp = regs->r15; - frame.pc = regs->pc; - - walk_stackframe(current, &frame, callchain_trace, entry); + walk_stackframe(NULL, regs, callchain_trace, entry); }
/* diff --git a/arch/sw_64/kernel/process.c b/arch/sw_64/kernel/process.c index 7a7578d530c6..da721d4266ee 100644 --- a/arch/sw_64/kernel/process.c +++ b/arch/sw_64/kernel/process.c @@ -169,6 +169,7 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
childti->pcb.ksp = (unsigned long) childregs; childti->pcb.flags = 7; /* set FEN, clear everything else */ + p->thread.sp = (unsigned long) childregs;
if (unlikely(p->flags & PF_KTHREAD)) { /* kernel thread */ diff --git a/arch/sw_64/kernel/stacktrace.c b/arch/sw_64/kernel/stacktrace.c index 2671331717ba..4e9acf99aaab 100644 --- a/arch/sw_64/kernel/stacktrace.c +++ b/arch/sw_64/kernel/stacktrace.c @@ -10,6 +10,8 @@ #include <linux/sched/debug.h> #include <linux/ftrace.h> #include <linux/perf_event.h> +#include <linux/kallsyms.h> + #include <asm/stacktrace.h>
/* @@ -59,40 +61,84 @@ int unwind_frame(struct task_struct *tsk, struct stackframe *frame) } EXPORT_SYMBOL_GPL(unwind_frame);
-void walk_stackframe(struct task_struct *tsk, struct stackframe *frame, - int (*fn)(struct stackframe *, void *), void *data) +void walk_stackframe(struct task_struct *tsk, struct pt_regs *regs, + int (*fn)(unsigned long, void *), void *data) { + unsigned long pc, fp; + + struct stackframe frame; + + if (regs) { + pc = regs->pc; + fp = regs->r15; + } else if (tsk == current || tsk == NULL) { + fp = (unsigned long)__builtin_frame_address(0); + pc = (unsigned long)walk_stackframe; + } else { + fp = tsk->thread.s[6]; + pc = tsk->thread.ra; + } + + if (!__kernel_text_address(pc) || fn(pc, data)) + return; + + frame.pc = pc; + frame.fp = fp; while (1) { int ret; - - if (fn(frame, data)) - break; - ret = unwind_frame(tsk, frame); + ret = unwind_frame(tsk, &frame); if (ret < 0) break; + + if (fn(frame.pc, data)) + break; } } EXPORT_SYMBOL_GPL(walk_stackframe);
#else /* !CONFIG_FRAME_POINTER */ -void walk_stackframe(struct task_struct *tsk, struct stackframe *frame, - int (*fn)(struct stackframe *, void *), void *data) +void walk_stackframe(struct task_struct *tsk, struct pt_regs *regs, + int (*fn)(unsigned long, void *), void *data) { - unsigned long *sp = (unsigned long *)current_thread_info()->pcb.ksp; - unsigned long addr; - struct perf_callchain_entry_ctx *entry = data; + unsigned long *ksp; + unsigned long sp, pc; + + if (regs) { + sp = (unsigned long)(regs+1); + pc = regs->pc; + } else if (tsk == current || tsk == NULL) { + register unsigned long current_sp __asm__ ("$30"); + sp = current_sp; + pc = (unsigned long)walk_stackframe; + } else { + sp = tsk->thread.sp; + pc = tsk->thread.ra; + }
- perf_callchain_store(entry, frame->pc); - while (!kstack_end(sp) && entry->nr < entry->max_stack) { - addr = *sp++; - if (__kernel_text_address(addr)) - perf_callchain_store(entry, addr); + ksp = (unsigned long *)sp; + + while (!kstack_end(ksp)) { + if (__kernel_text_address(pc) && fn(pc, data)) + break; + pc = (*ksp++) - 0x4; } } EXPORT_SYMBOL_GPL(walk_stackframe);
#endif/* CONFIG_FRAME_POINTER */
+static int print_address_trace(unsigned long pc, void *data) +{ + print_ip_sym((const char *)data, pc); + return 0; +} + +void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl) +{ + pr_info("Trace:\n"); + walk_stackframe(task, NULL, print_address_trace, (void *)loglvl); +} + /* * Save stack-backtrace addresses into a stack_trace buffer. */ diff --git a/arch/sw_64/kernel/traps.c b/arch/sw_64/kernel/traps.c index d656eca5f961..4e95cab13daa 100644 --- a/arch/sw_64/kernel/traps.c +++ b/arch/sw_64/kernel/traps.c @@ -12,13 +12,20 @@ #include <linux/extable.h> #include <linux/perf_event.h> #include <linux/kdebug.h> +#include <linux/sched.h> #include <linux/kexec.h> +#include <linux/kallsyms.h> +#include <linux/sched/task_stack.h> +#include <linux/sched/debug.h>
#include <asm/gentrap.h> #include <asm/mmu_context.h> #include <asm/fpu.h> #include <asm/kprobes.h> #include <asm/uprobes.h> +#include <asm/stacktrace.h> +#include <asm/processor.h> +#include <asm/ptrace.h>
#include "proto.h"
@@ -68,53 +75,6 @@ dik_show_code(unsigned int *pc) printk("\n"); }
-static void -dik_show_trace(unsigned long *sp, const char *loglvl) -{ - long i = 0; - unsigned long tmp; - - printk("%sTrace:\n", loglvl); - while (0x1ff8 & (unsigned long)sp) { - tmp = *sp; - sp++; - if (!__kernel_text_address(tmp)) - continue; - printk("%s[<%lx>] %pSR\n", loglvl, tmp, (void *)tmp); - if (i > 40) { - printk("%s ...", loglvl); - break; - } - } - printk("\n"); -} - -static int kstack_depth_to_print = 24; - -void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl) -{ - unsigned long *stack; - int i; - - /* - * debugging aid: "show_stack(NULL, NULL, KERN_EMERG);" prints the - * back trace for this cpu. - */ - if (sp == NULL) - sp = (unsigned long *)&sp; - - stack = sp; - for (i = 0; i < kstack_depth_to_print; i++) { - if (((long) stack & (THREAD_SIZE-1)) == 0) - break; - if (i && ((i % 4) == 0)) - printk("%s ", loglvl); - printk("%016lx ", *stack++); - } - printk("\n"); - dik_show_trace(sp, loglvl); -} - void die_if_kernel(char *str, struct pt_regs *regs, long err) { if (regs->ps & 8) @@ -125,7 +85,7 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err) printk("%s(%d): %s %ld\n", current->comm, task_pid_nr(current), str, err); dik_show_regs(regs); add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); - dik_show_trace((unsigned long *)(regs+1), KERN_DEFAULT); + show_stack(current, NULL, KERN_EMERG); dik_show_code((unsigned int *)regs->pc);
if (test_and_set_thread_flag(TIF_DIE_IF_KERNEL)) { @@ -535,7 +495,7 @@ do_entUna(void *va, unsigned long opcode, unsigned long reg,
dik_show_regs(regs); dik_show_code((unsigned int *)pc); - dik_show_trace((unsigned long *)(regs+1), KERN_DEFAULT); + show_stack(current, NULL, KERN_EMERG);
if (test_and_set_thread_flag(TIF_DIE_IF_KERNEL)) { printk("die_if_kernel recursion detected.\n");