Skip to content

Commit 78c98f9

Browse files
author
Martin Schwidefsky
committed
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 <[email protected]>
1 parent 1c705ad commit 78c98f9

File tree

16 files changed

+521
-205
lines changed

16 files changed

+521
-205
lines changed

arch/s390/include/asm/processor.h

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -156,25 +156,6 @@ struct thread_struct {
156156

157157
typedef struct thread_struct thread_struct;
158158

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-
178159
#define ARCH_MIN_TASKALIGN 8
179160

180161
#define INIT_THREAD { \
@@ -206,11 +187,7 @@ struct mm_struct;
206187
struct seq_file;
207188
struct pt_regs;
208189

209-
typedef int (*dump_trace_func_t)(void *data, unsigned long address, int reliable);
210-
void dump_trace(dump_trace_func_t func, void *data,
211-
struct task_struct *task, unsigned long sp);
212190
void show_registers(struct pt_regs *regs);
213-
214191
void show_cacheinfo(struct seq_file *m);
215192

216193
/* Free all resources held by a thread. */
@@ -244,55 +221,6 @@ static __no_kasan_or_inline unsigned short stap(void)
244221
return cpu_address;
245222
}
246223

247-
#define CALL_ARGS_0() \
248-
register unsigned long r2 asm("2")
249-
#define CALL_ARGS_1(arg1) \
250-
register unsigned long r2 asm("2") = (unsigned long)(arg1)
251-
#define CALL_ARGS_2(arg1, arg2) \
252-
CALL_ARGS_1(arg1); \
253-
register unsigned long r3 asm("3") = (unsigned long)(arg2)
254-
#define CALL_ARGS_3(arg1, arg2, arg3) \
255-
CALL_ARGS_2(arg1, arg2); \
256-
register unsigned long r4 asm("4") = (unsigned long)(arg3)
257-
#define CALL_ARGS_4(arg1, arg2, arg3, arg4) \
258-
CALL_ARGS_3(arg1, arg2, arg3); \
259-
register unsigned long r4 asm("5") = (unsigned long)(arg4)
260-
#define CALL_ARGS_5(arg1, arg2, arg3, arg4, arg5) \
261-
CALL_ARGS_4(arg1, arg2, arg3, arg4); \
262-
register unsigned long r4 asm("6") = (unsigned long)(arg5)
263-
264-
#define CALL_FMT_0 "=&d" (r2) :
265-
#define CALL_FMT_1 "+&d" (r2) :
266-
#define CALL_FMT_2 CALL_FMT_1 "d" (r3),
267-
#define CALL_FMT_3 CALL_FMT_2 "d" (r4),
268-
#define CALL_FMT_4 CALL_FMT_3 "d" (r5),
269-
#define CALL_FMT_5 CALL_FMT_4 "d" (r6),
270-
271-
#define CALL_CLOBBER_5 "0", "1", "14", "cc", "memory"
272-
#define CALL_CLOBBER_4 CALL_CLOBBER_5
273-
#define CALL_CLOBBER_3 CALL_CLOBBER_4, "5"
274-
#define CALL_CLOBBER_2 CALL_CLOBBER_3, "4"
275-
#define CALL_CLOBBER_1 CALL_CLOBBER_2, "3"
276-
#define CALL_CLOBBER_0 CALL_CLOBBER_1
277-
278-
#define CALL_ON_STACK(fn, stack, nr, args...) \
279-
({ \
280-
CALL_ARGS_##nr(args); \
281-
unsigned long prev; \
282-
\
283-
asm volatile( \
284-
" la %[_prev],0(15)\n" \
285-
" la 15,0(%[_stack])\n" \
286-
" stg %[_prev],%[_bc](15)\n" \
287-
" brasl 14,%[_fn]\n" \
288-
" la 15,0(%[_prev])\n" \
289-
: [_prev] "=&a" (prev), CALL_FMT_##nr \
290-
[_stack] "a" (stack), \
291-
[_bc] "i" (offsetof(struct stack_frame, back_chain)), \
292-
[_fn] "X" (fn) : CALL_CLOBBER_##nr); \
293-
r2; \
294-
})
295-
296224
/*
297225
* Give up the time slice of the virtual PU.
298226
*/

arch/s390/include/asm/stacktrace.h

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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 */

arch/s390/include/asm/unwind.h

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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 */

arch/s390/kernel/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ CFLAGS_smp.o := -Wno-nonnull
3939
#
4040
CFLAGS_stacktrace.o += -fno-optimize-sibling-calls
4141
CFLAGS_dumpstack.o += -fno-optimize-sibling-calls
42+
CFLAGS_unwind_bc.o += -fno-optimize-sibling-calls
4243

4344
#
4445
# Pass UTS_MACHINE for user_regset definition
@@ -51,7 +52,7 @@ obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o early_nobss.o
5152
obj-y += sysinfo.o lgr.o os_info.o machine_kexec.o pgm_check.o
5253
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o
5354
obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o
54-
obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o
55+
obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o unwind_bc.o
5556

5657
extra-y += head64.o vmlinux.lds
5758

arch/s390/kernel/asm-offsets.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <asm/pgtable.h>
1717
#include <asm/gmap.h>
1818
#include <asm/nmi.h>
19+
#include <asm/stacktrace.h>
1920

2021
int main(void)
2122
{

0 commit comments

Comments
 (0)