Commit c5ac25e0 authored by Jinyang He's avatar Jinyang He Committed by Huacai Chen
Browse files

LoongArch: Strip guess unwinder out from prologue unwinder



The prolugue unwinder rely on symbol info. When PC is not in kernel text
address, it cannot find relative symbol info and it will be broken. The
guess unwinder will be used in this case. And the guess unwinder code in
prolugue unwinder is redundant. Strip it out and set the unwinder type
in unwind_state. Make guess_unwinder::unwind_next_frame() as default way
when other unwinders cannot unwind in some extreme case.

Signed-off-by: default avatarJinyang He <hejinyang@loongson.cn>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent 5bb8d344
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ struct unwind_state {
	unsigned long sp, pc, ra;
};

bool default_next_frame(struct unwind_state *state);

void unwind_start(struct unwind_state *state,
		  struct task_struct *task, struct pt_regs *regs);
bool unwind_next_frame(struct unwind_state *state);
@@ -50,4 +52,31 @@ static inline unsigned long unwind_graph_addr(struct unwind_state *state,
	return ftrace_graph_ret_addr(state->task, &state->graph_idx,
				     pc, (unsigned long *)(cfa - GRAPH_FAKE_OFFSET));
}

static __always_inline void __unwind_start(struct unwind_state *state,
					struct task_struct *task, struct pt_regs *regs)
{
	memset(state, 0, sizeof(*state));
	if (regs) {
		state->sp = regs->regs[3];
		state->pc = regs->csr_era;
		state->ra = regs->regs[1];
	} else if (task && task != current) {
		state->sp = thread_saved_fp(task);
		state->pc = thread_saved_ra(task);
		state->ra = 0;
	} else {
		state->sp = (unsigned long)__builtin_frame_address(0);
		state->pc = (unsigned long)__builtin_return_address(0);
		state->ra = 0;
	}
	state->task = task;
	get_stack_info(state->sp, state->task, &state->stack_info);
	state->pc = unwind_graph_addr(state, state->pc, state->sp);
}

static __always_inline unsigned long __unwind_get_return_address(struct unwind_state *state)
{
	return unwind_done(state) ? 0 : state->pc;
}
#endif /* _ASM_UNWIND_H */
+1 −1
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ extra-y := vmlinux.lds
obj-y		+= head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \
		   traps.o irq.o idle.o process.o dma.o mem.o io.o reset.o switch.o \
		   elf.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o \
		   alternative.o unaligned.o
		   alternative.o unaligned.o unwind.o

obj-$(CONFIG_ACPI)		+= acpi.o
obj-$(CONFIG_EFI) 		+= efi.o
+0 −3
Original line number Diff line number Diff line
@@ -72,9 +72,6 @@ static void show_backtrace(struct task_struct *task, const struct pt_regs *regs,
	if (!task)
		task = current;

	if (user_mode(regs))
		state.type = UNWINDER_GUESS;

	printk("%sCall Trace:", loglvl);
	for (unwind_start(&state, task, pregs);
	      !unwind_done(&state); unwind_next_frame(&state)) {
+32 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
 */
#include <linux/kernel.h>
#include <linux/ftrace.h>

#include <asm/unwind.h>

bool default_next_frame(struct unwind_state *state)
{
	struct stack_info *info = &state->stack_info;
	unsigned long addr;

	if (unwind_done(state))
		return false;

	do {
		for (state->sp += sizeof(unsigned long);
		     state->sp < info->end; state->sp += sizeof(unsigned long)) {
			addr = *(unsigned long *)(state->sp);
			state->pc = unwind_graph_addr(state, addr, state->sp + 8);
			if (__kernel_text_address(state->pc))
				return true;
		}

		state->sp = info->next_sp;

	} while (!get_stack_info(state->sp, state->task, info));

	return false;
}
+3 −49
Original line number Diff line number Diff line
@@ -2,41 +2,18 @@
/*
 * Copyright (C) 2022 Loongson Technology Corporation Limited
 */
#include <linux/kernel.h>
#include <linux/ftrace.h>

#include <asm/unwind.h>

unsigned long unwind_get_return_address(struct unwind_state *state)
{
	if (unwind_done(state))
		return 0;

	return state->pc;
	return __unwind_get_return_address(state);
}
EXPORT_SYMBOL_GPL(unwind_get_return_address);

void unwind_start(struct unwind_state *state, struct task_struct *task,
		    struct pt_regs *regs)
{
	memset(state, 0, sizeof(*state));

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

	state->task = task;
	state->first = true;
	state->pc = unwind_graph_addr(state, state->pc, state->sp);
	get_stack_info(state->sp, state->task, &state->stack_info);

	__unwind_start(state, task, regs);
	if (!unwind_done(state) && !__kernel_text_address(state->pc))
		unwind_next_frame(state);
}
@@ -44,29 +21,6 @@ EXPORT_SYMBOL_GPL(unwind_start);

bool unwind_next_frame(struct unwind_state *state)
{
	struct stack_info *info = &state->stack_info;
	unsigned long addr;

	if (unwind_done(state))
		return false;

	if (state->first)
		state->first = false;

	do {
		for (state->sp += sizeof(unsigned long);
		     state->sp < info->end;
		     state->sp += sizeof(unsigned long)) {
			addr = *(unsigned long *)(state->sp);
			state->pc = unwind_graph_addr(state, addr, state->sp + 8);
			if (__kernel_text_address(state->pc))
				return true;
		}

		state->sp = info->next_sp;

	} while (!get_stack_info(state->sp, state->task, info));

	return false;
	return default_next_frame(state);
}
EXPORT_SYMBOL_GPL(unwind_next_frame);
Loading