]>
Commit | Line | Data |
---|---|---|
5bdc9b44 | 1 | /* |
5bdc9b44 HC |
2 | * Stack trace management functions |
3 | * | |
a53c8fab | 4 | * Copyright IBM Corp. 2006 |
5bdc9b44 HC |
5 | * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> |
6 | */ | |
7 | ||
8 | #include <linux/sched.h> | |
9 | #include <linux/stacktrace.h> | |
10 | #include <linux/kallsyms.h> | |
8de2ce86 | 11 | #include <linux/module.h> |
5bdc9b44 | 12 | |
4d284cac | 13 | static unsigned long save_context_stack(struct stack_trace *trace, |
4d284cac HC |
14 | unsigned long sp, |
15 | unsigned long low, | |
a3afe70b HC |
16 | unsigned long high, |
17 | int savesched) | |
5bdc9b44 HC |
18 | { |
19 | struct stack_frame *sf; | |
20 | struct pt_regs *regs; | |
21 | unsigned long addr; | |
22 | ||
23 | while(1) { | |
24 | sp &= PSW_ADDR_INSN; | |
25 | if (sp < low || sp > high) | |
26 | return sp; | |
27 | sf = (struct stack_frame *)sp; | |
28 | while(1) { | |
29 | addr = sf->gprs[8] & PSW_ADDR_INSN; | |
e90a2857 | 30 | if (!trace->skip) |
5bdc9b44 HC |
31 | trace->entries[trace->nr_entries++] = addr; |
32 | else | |
e90a2857 | 33 | trace->skip--; |
5bdc9b44 HC |
34 | if (trace->nr_entries >= trace->max_entries) |
35 | return sp; | |
36 | low = sp; | |
37 | sp = sf->back_chain & PSW_ADDR_INSN; | |
38 | if (!sp) | |
39 | break; | |
40 | if (sp <= low || sp > high - sizeof(*sf)) | |
41 | return sp; | |
42 | sf = (struct stack_frame *)sp; | |
43 | } | |
44 | /* Zero backchain detected, check for interrupt frame. */ | |
45 | sp = (unsigned long)(sf + 1); | |
46 | if (sp <= low || sp > high - sizeof(*regs)) | |
47 | return sp; | |
48 | regs = (struct pt_regs *)sp; | |
49 | addr = regs->psw.addr & PSW_ADDR_INSN; | |
a3afe70b HC |
50 | if (savesched || !in_sched_functions(addr)) { |
51 | if (!trace->skip) | |
52 | trace->entries[trace->nr_entries++] = addr; | |
53 | else | |
54 | trace->skip--; | |
55 | } | |
5bdc9b44 HC |
56 | if (trace->nr_entries >= trace->max_entries) |
57 | return sp; | |
58 | low = sp; | |
59 | sp = regs->gprs[15]; | |
60 | } | |
61 | } | |
62 | ||
ab1b6f03 | 63 | void save_stack_trace(struct stack_trace *trace) |
5bdc9b44 HC |
64 | { |
65 | register unsigned long sp asm ("15"); | |
75e9de18 | 66 | unsigned long orig_sp, new_sp; |
5bdc9b44 | 67 | |
75e9de18 | 68 | orig_sp = sp & PSW_ADDR_INSN; |
e90a2857 HC |
69 | new_sp = save_context_stack(trace, orig_sp, |
70 | S390_lowcore.panic_stack - PAGE_SIZE, | |
a3afe70b | 71 | S390_lowcore.panic_stack, 1); |
ab1b6f03 | 72 | if (new_sp != orig_sp) |
5bdc9b44 | 73 | return; |
e90a2857 HC |
74 | new_sp = save_context_stack(trace, new_sp, |
75 | S390_lowcore.async_stack - ASYNC_SIZE, | |
a3afe70b | 76 | S390_lowcore.async_stack, 1); |
ab1b6f03 | 77 | if (new_sp != orig_sp) |
5bdc9b44 | 78 | return; |
e90a2857 | 79 | save_context_stack(trace, new_sp, |
ab1b6f03 | 80 | S390_lowcore.thread_info, |
a3afe70b HC |
81 | S390_lowcore.thread_info + THREAD_SIZE, 1); |
82 | } | |
7b4c9505 | 83 | EXPORT_SYMBOL_GPL(save_stack_trace); |
a3afe70b HC |
84 | |
85 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |
86 | { | |
87 | unsigned long sp, low, high; | |
88 | ||
89 | sp = tsk->thread.ksp & PSW_ADDR_INSN; | |
90 | low = (unsigned long) task_stack_page(tsk); | |
91 | high = (unsigned long) task_pt_regs(tsk); | |
92 | save_context_stack(trace, sp, low, high, 0); | |
93 | if (trace->nr_entries < trace->max_entries) | |
94 | trace->entries[trace->nr_entries++] = ULONG_MAX; | |
5bdc9b44 | 95 | } |
7b4c9505 | 96 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); |