Skip to content

Commit

Permalink
LoongArch: Add stacktrace support
Browse files Browse the repository at this point in the history
Use common arch_stack_walk infrastructure to avoid duplicated code and
avoid taking care of the stack storage and filtering.
Add sra (means __schedule return address) and scfa (means __schedule call
frame address) to thread_info and store it in switch_to().

Now we can print the process stack by cat /proc/*/stack and can better
support ftrace.

Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
  • Loading branch information
zhangqingmy authored and intel-lab-lkp committed Aug 1, 2022
1 parent 2237d07 commit 102a43b
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 7 deletions.
5 changes: 5 additions & 0 deletions arch/loongarch/Kconfig
Expand Up @@ -38,6 +38,7 @@ config LOONGARCH
select ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE if !PREEMPTION
select ARCH_MIGHT_HAVE_PC_PARPORT
select ARCH_MIGHT_HAVE_PC_SERIO
select ARCH_STACKWALK
select ARCH_SPARSEMEM_ENABLE
select ARCH_SUPPORTS_ACPI
select ARCH_SUPPORTS_ATOMIC_RMW
Expand Down Expand Up @@ -140,6 +141,10 @@ config LOCKDEP_SUPPORT
bool
default y

config STACKTRACE_SUPPORT
bool
default y

# MACH_LOONGSON32 and MACH_LOONGSON64 are delibrately carried over from the
# MIPS Loongson code, to preserve Loongson-specific code paths in drivers that
# are shared between architectures, and specifically expecting the symbols.
Expand Down
9 changes: 9 additions & 0 deletions arch/loongarch/include/asm/processor.h
Expand Up @@ -101,6 +101,10 @@ struct thread_struct {
unsigned long reg23, reg24, reg25, reg26; /* s0-s3 */
unsigned long reg27, reg28, reg29, reg30, reg31; /* s4-s8 */

/* __schedule() return address / call frame address */
unsigned long sched_ra;
unsigned long sched_cfa;

/* CSR registers */
unsigned long csr_prmd;
unsigned long csr_crmd;
Expand Down Expand Up @@ -129,6 +133,9 @@ struct thread_struct {
struct loongarch_fpu fpu FPU_ALIGN;
};

#define thread_saved_ra(tsk) (tsk->thread.sched_ra)
#define thread_saved_fp(tsk) (tsk->thread.sched_cfa)

#define INIT_THREAD { \
/* \
* Main processor registers \
Expand All @@ -145,6 +152,8 @@ struct thread_struct {
.reg29 = 0, \
.reg30 = 0, \
.reg31 = 0, \
.sched_ra = 0, \
.sched_cfa = 0, \
.csr_crmd = 0, \
.csr_prmd = 0, \
.csr_euen = 0, \
Expand Down
14 changes: 9 additions & 5 deletions arch/loongarch/include/asm/switch_to.h
Expand Up @@ -15,23 +15,27 @@ struct task_struct;
* @prev: The task previously executed.
* @next: The task to begin executing.
* @next_ti: task_thread_info(next).
* @sched_ra: __schedule return address.
* @sched_cfa: __schedule call frame address.
*
* This function is used whilst scheduling to save the context of prev & load
* the context of next. Returns prev.
*/
extern asmlinkage struct task_struct *__switch_to(struct task_struct *prev,
struct task_struct *next, struct thread_info *next_ti);
struct task_struct *next, struct thread_info *next_ti,
void *sched_ra, void *sched_cfa);

/*
* For newly created kernel threads switch_to() will return to
* ret_from_kernel_thread, newly created user threads to ret_from_fork.
* That is, everything following __switch_to() will be skipped for new threads.
* So everything that matters to new threads should be placed before __switch_to().
*/
#define switch_to(prev, next, last) \
do { \
lose_fpu_inatomic(1, prev); \
(last) = __switch_to(prev, next, task_thread_info(next)); \
#define switch_to(prev, next, last) \
do { \
lose_fpu_inatomic(1, prev); \
(last) = __switch_to(prev, next, task_thread_info(next), \
__builtin_return_address(0), __builtin_frame_address(0)); \
} while (0)

#endif /* _ASM_SWITCH_TO_H */
4 changes: 2 additions & 2 deletions arch/loongarch/include/asm/uaccess.h
Expand Up @@ -229,13 +229,13 @@ extern unsigned long __copy_user(void *to, const void *from, __kernel_size_t n);
static inline unsigned long __must_check
raw_copy_from_user(void *to, const void __user *from, unsigned long n)
{
return __copy_user(to, from, n);
return __copy_user(to, (__force const void *)from, n);
}

static inline unsigned long __must_check
raw_copy_to_user(void __user *to, const void *from, unsigned long n)
{
return __copy_user(to, from, n);
return __copy_user((__force void *)to, from, n);
}

#define INLINE_COPY_FROM_USER
Expand Down
1 change: 1 addition & 0 deletions arch/loongarch/kernel/Makefile
Expand Up @@ -15,6 +15,7 @@ obj-$(CONFIG_EFI) += efi.o
obj-$(CONFIG_CPU_HAS_FPU) += fpu.o

obj-$(CONFIG_MODULES) += module.o module-sections.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o

obj-$(CONFIG_PROC_FS) += proc.o

Expand Down
2 changes: 2 additions & 0 deletions arch/loongarch/kernel/asm-offsets.c
Expand Up @@ -103,6 +103,8 @@ void output_thread_defines(void)
OFFSET(THREAD_REG29, task_struct, thread.reg29);
OFFSET(THREAD_REG30, task_struct, thread.reg30);
OFFSET(THREAD_REG31, task_struct, thread.reg31);
OFFSET(THREAD_SCHED_RA, task_struct, thread.sched_ra);
OFFSET(THREAD_SCHED_CFA, task_struct, thread.sched_cfa);
OFFSET(THREAD_CSRCRMD, task_struct,
thread.csr_crmd);
OFFSET(THREAD_CSRPRMD, task_struct,
Expand Down
3 changes: 3 additions & 0 deletions arch/loongarch/kernel/process.c
Expand Up @@ -135,6 +135,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
childregs = (struct pt_regs *) childksp - 1;
/* Put the stack after the struct pt_regs. */
childksp = (unsigned long) childregs;
p->thread.sched_cfa = 0;
p->thread.csr_euen = 0;
p->thread.csr_crmd = csr_read32(LOONGARCH_CSR_CRMD);
p->thread.csr_prmd = csr_read32(LOONGARCH_CSR_PRMD);
Expand All @@ -145,6 +146,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
p->thread.reg23 = (unsigned long)args->fn;
p->thread.reg24 = (unsigned long)args->fn_arg;
p->thread.reg01 = (unsigned long)ret_from_kernel_thread;
p->thread.sched_ra = (unsigned long)ret_from_kernel_thread;
memset(childregs, 0, sizeof(struct pt_regs));
childregs->csr_euen = p->thread.csr_euen;
childregs->csr_crmd = p->thread.csr_crmd;
Expand All @@ -161,6 +163,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)

p->thread.reg03 = (unsigned long) childregs;
p->thread.reg01 = (unsigned long) ret_from_fork;
p->thread.sched_ra = (unsigned long) ret_from_fork;

/*
* New tasks lose permission to use the fpu. This accelerates context
Expand Down
37 changes: 37 additions & 0 deletions arch/loongarch/kernel/stacktrace.c
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Stack trace management functions
*
* Copyright (C) 2022 Loongson Technology Corporation Limited
*/
#include <linux/sched.h>
#include <linux/stacktrace.h>

#include <asm/stacktrace.h>
#include <asm/unwind.h>

void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
struct task_struct *task, struct pt_regs *regs)
{
struct pt_regs dummyregs;
struct unwind_state state;
unsigned long addr;

regs = &dummyregs;

if (task == current) {
regs->csr_era = (unsigned long)__builtin_return_address(0);
regs->regs[3] = (unsigned long)__builtin_frame_address(0);
} else {
regs->csr_era = thread_saved_ra(task);
regs->regs[3] = thread_saved_fp(task);
}

regs->regs[1] = 0;
for (unwind_start(&state, task, regs);
!unwind_done(&state); unwind_next_frame(&state)) {
addr = unwind_get_return_address(&state);
if (!addr || !consume_entry(cookie, addr))
break;
}
}
2 changes: 2 additions & 0 deletions arch/loongarch/kernel/switch.S
Expand Up @@ -21,6 +21,8 @@ SYM_FUNC_START(__switch_to)

cpu_save_nonscratch a0
stptr.d ra, a0, THREAD_REG01
stptr.d a3, a0, THREAD_SCHED_RA
stptr.d a4, a0, THREAD_SCHED_CFA
move tp, a2
cpu_restore_nonscratch a1

Expand Down

0 comments on commit 102a43b

Please sign in to comment.