Skip to content

Commit dc74a9e

Browse files
MQ-mengqingchenhuacai
authored andcommitted
LoongArch: Add generic ex-handler unwind in prologue unwinder
When exception is triggered, code flow go handle_\exception in some cases. One of stackframe in this case as follows, high -> +-------+ | REGS | <- a pt_regs | | | | <- ex trigger | REGS | <- ex pt_regs <-+ | | | | | | low -> +-------+ ->unwind-+ When unwinder unwinds to handler_\exception it cannot go on prologue analysis. Because it is an asynchronous code flow, we should get the next frame PC from regs->csr_era rather than regs->regs[1]. At init time we copy the handlers to eentry and also copy them to NUMA-affine memory named pcpu_handlers if NUMA is enabled. Thus, unwinder cannot unwind normally. To solve this, we try to give some hints in handler_\exception and fixup unwinders in unwind_next_frame(). Reported-by: Qing Zhang <[email protected]> Signed-off-by: Jinyang He <[email protected]> Signed-off-by: Huacai Chen <[email protected]>
1 parent c5ac25e commit dc74a9e

File tree

4 files changed

+93
-15
lines changed

4 files changed

+93
-15
lines changed

arch/loongarch/include/asm/unwind.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct unwind_state {
2222
char type; /* UNWINDER_XXX */
2323
struct stack_info stack_info;
2424
struct task_struct *task;
25-
bool first, error, is_ftrace;
25+
bool first, error, reset;
2626
int graph_idx;
2727
unsigned long sp, pc, ra;
2828
};

arch/loongarch/kernel/genex.S

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,17 @@ SYM_FUNC_END(except_vec_cex)
6767
.macro BUILD_HANDLER exception handler prep
6868
.align 5
6969
SYM_FUNC_START(handle_\exception)
70+
666:
7071
BACKUP_T0T1
7172
SAVE_ALL
7273
build_prep_\prep
7374
move a0, sp
7475
la.abs t0, do_\handler
7576
jirl ra, t0, 0
77+
668:
7678
RESTORE_ALL_AND_RET
7779
SYM_FUNC_END(handle_\exception)
80+
SYM_DATA(unwind_hint_\exception, .word 668b - 666b)
7881
.endm
7982

8083
BUILD_HANDLER ade ade badv

arch/loongarch/kernel/unwind_prologue.c

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,103 @@
22
/*
33
* Copyright (C) 2022 Loongson Technology Corporation Limited
44
*/
5+
#include <linux/cpumask.h>
56
#include <linux/ftrace.h>
67
#include <linux/kallsyms.h>
78

89
#include <asm/inst.h>
10+
#include <asm/loongson.h>
911
#include <asm/ptrace.h>
12+
#include <asm/setup.h>
1013
#include <asm/unwind.h>
1114

12-
static inline void unwind_state_fixup(struct unwind_state *state)
15+
extern const int unwind_hint_ade;
16+
extern const int unwind_hint_ale;
17+
extern const int unwind_hint_bp;
18+
extern const int unwind_hint_fpe;
19+
extern const int unwind_hint_fpu;
20+
extern const int unwind_hint_lsx;
21+
extern const int unwind_hint_lasx;
22+
extern const int unwind_hint_lbt;
23+
extern const int unwind_hint_ri;
24+
extern const int unwind_hint_watch;
25+
extern unsigned long eentry;
26+
#ifdef CONFIG_NUMA
27+
extern unsigned long pcpu_handlers[NR_CPUS];
28+
#endif
29+
30+
static inline bool scan_handlers(unsigned long entry_offset)
1331
{
14-
#ifdef CONFIG_DYNAMIC_FTRACE
15-
static unsigned long ftrace = (unsigned long)ftrace_call + 4;
32+
int idx, offset;
33+
34+
if (entry_offset >= EXCCODE_INT_START * VECSIZE)
35+
return false;
36+
37+
idx = entry_offset / VECSIZE;
38+
offset = entry_offset % VECSIZE;
39+
switch (idx) {
40+
case EXCCODE_ADE:
41+
return offset == unwind_hint_ade;
42+
case EXCCODE_ALE:
43+
return offset == unwind_hint_ale;
44+
case EXCCODE_BP:
45+
return offset == unwind_hint_bp;
46+
case EXCCODE_FPE:
47+
return offset == unwind_hint_fpe;
48+
case EXCCODE_FPDIS:
49+
return offset == unwind_hint_fpu;
50+
case EXCCODE_LSXDIS:
51+
return offset == unwind_hint_lsx;
52+
case EXCCODE_LASXDIS:
53+
return offset == unwind_hint_lasx;
54+
case EXCCODE_BTDIS:
55+
return offset == unwind_hint_lbt;
56+
case EXCCODE_INE:
57+
return offset == unwind_hint_ri;
58+
case EXCCODE_WATCH:
59+
return offset == unwind_hint_watch;
60+
default:
61+
return false;
62+
}
63+
}
64+
65+
static inline bool fix_exception(unsigned long pc)
66+
{
67+
#ifdef CONFIG_NUMA
68+
int cpu;
69+
70+
for_each_possible_cpu(cpu) {
71+
if (!pcpu_handlers[cpu])
72+
continue;
73+
if (scan_handlers(pc - pcpu_handlers[cpu]))
74+
return true;
75+
}
76+
#endif
77+
return scan_handlers(pc - eentry);
78+
}
1679

17-
if (state->pc == ftrace)
18-
state->is_ftrace = true;
80+
/*
81+
* As we meet ftrace_regs_entry, reset first flag like first doing
82+
* tracing. Prologue analysis will stop soon because PC is at entry.
83+
*/
84+
static inline bool fix_ftrace(unsigned long pc)
85+
{
86+
#ifdef CONFIG_DYNAMIC_FTRACE
87+
return pc == (unsigned long)ftrace_call + LOONGARCH_INSN_SIZE;
88+
#else
89+
return false;
1990
#endif
2091
}
2192

93+
static inline bool unwind_state_fixup(struct unwind_state *state)
94+
{
95+
if (!fix_exception(state->pc) && !fix_ftrace(state->pc))
96+
return false;
97+
98+
state->reset = true;
99+
return true;
100+
}
101+
22102
/*
23103
* LoongArch function prologue is like follows,
24104
* [instructions not use stack var]
@@ -39,14 +119,10 @@ static bool unwind_by_prologue(struct unwind_state *state)
39119
if (state->sp >= info->end || state->sp < info->begin)
40120
return false;
41121

42-
if (state->is_ftrace) {
43-
/*
44-
* As we meet ftrace_regs_entry, reset first flag like first doing
45-
* tracing. Prologue analysis will stop soon because PC is at entry.
46-
*/
122+
if (state->reset) {
47123
regs = (struct pt_regs *)state->sp;
48124
state->first = true;
49-
state->is_ftrace = false;
125+
state->reset = false;
50126
state->pc = regs->csr_era;
51127
state->ra = regs->regs[1];
52128
state->sp = regs->regs[3];
@@ -112,8 +188,7 @@ static bool unwind_by_prologue(struct unwind_state *state)
112188

113189
out:
114190
state->first = false;
115-
unwind_state_fixup(state);
116-
return !!__kernel_text_address(state->pc);
191+
return unwind_state_fixup(state) || __kernel_text_address(state->pc);
117192
}
118193

119194
static bool next_frame(struct unwind_state *state)

arch/loongarch/mm/tlb.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ static void output_pgtable_bits_defines(void)
251251
}
252252

253253
#ifdef CONFIG_NUMA
254-
static unsigned long pcpu_handlers[NR_CPUS];
254+
unsigned long pcpu_handlers[NR_CPUS];
255255
#endif
256256
extern long exception_handlers[VECSIZE * 128 / sizeof(long)];
257257

0 commit comments

Comments
 (0)