]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - arch/x86/kernel/stacktrace.c
x86: Make save_stack_address() !CONFIG_FRAME_POINTER friendly
[mirror_ubuntu-hirsute-kernel.git] / arch / x86 / kernel / stacktrace.c
CommitLineData
21b32bbf 1/*
21b32bbf
IM
2 * Stack trace management functions
3 *
8f47e163 4 * Copyright (C) 2006-2009 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
21b32bbf
IM
5 */
6#include <linux/sched.h>
7#include <linux/stacktrace.h>
c0b766f1 8#include <linux/module.h>
02b67518 9#include <linux/uaccess.h>
c0b766f1 10#include <asm/stacktrace.h>
21b32bbf 11
c0b766f1 12static void save_stack_warning(void *data, char *msg)
21b32bbf 13{
21b32bbf
IM
14}
15
c0b766f1
AK
16static void
17save_stack_warning_symbol(void *data, char *msg, unsigned long symbol)
21b32bbf 18{
21b32bbf
IM
19}
20
c0b766f1 21static int save_stack_stack(void *data, char *name)
21b32bbf 22{
29a67975 23 return 0;
c0b766f1 24}
21b32bbf 25
bc850d6b 26static void save_stack_address(void *data, unsigned long addr, int reliable)
c0b766f1 27{
ade1af77 28 struct stack_trace *trace = data;
147ec4d2 29#ifdef CONFIG_FRAME_POINTER
1650743c
VN
30 if (!reliable)
31 return;
147ec4d2 32#endif
c0b766f1
AK
33 if (trace->skip > 0) {
34 trace->skip--;
35 return;
21b32bbf 36 }
006e84ee 37 if (trace->nr_entries < trace->max_entries)
c0b766f1 38 trace->entries[trace->nr_entries++] = addr;
21b32bbf
IM
39}
40
5bc27dc2
AV
41static void
42save_stack_address_nosched(void *data, unsigned long addr, int reliable)
9745512c 43{
147ec4d2
ON
44 struct stack_trace *trace = data;
45#ifdef CONFIG_FRAME_POINTER
1650743c
VN
46 if (!reliable)
47 return;
147ec4d2 48#endif
9745512c
AV
49 if (in_sched_functions(addr))
50 return;
51 if (trace->skip > 0) {
52 trace->skip--;
53 return;
54 }
55 if (trace->nr_entries < trace->max_entries)
56 trace->entries[trace->nr_entries++] = addr;
57}
58
9689ba8a 59static const struct stacktrace_ops save_stack_ops = {
61c1917f
FW
60 .warning = save_stack_warning,
61 .warning_symbol = save_stack_warning_symbol,
62 .stack = save_stack_stack,
63 .address = save_stack_address,
64 .walk_stack = print_context_stack,
c0b766f1 65};
21b32bbf 66
9745512c 67static const struct stacktrace_ops save_stack_ops_nosched = {
61c1917f
FW
68 .warning = save_stack_warning,
69 .warning_symbol = save_stack_warning_symbol,
70 .stack = save_stack_stack,
71 .address = save_stack_address_nosched,
72 .walk_stack = print_context_stack,
9745512c
AV
73};
74
21b32bbf
IM
75/*
76 * Save stack-backtrace addresses into a stack_trace buffer.
21b32bbf 77 */
ab1b6f03 78void save_stack_trace(struct stack_trace *trace)
21b32bbf 79{
5bc27dc2 80 dump_trace(current, NULL, NULL, 0, &save_stack_ops, trace);
006e84ee
CM
81 if (trace->nr_entries < trace->max_entries)
82 trace->entries[trace->nr_entries++] = ULONG_MAX;
21b32bbf 83}
8594698e 84EXPORT_SYMBOL_GPL(save_stack_trace);
9745512c 85
acc6be54
VN
86void save_stack_trace_bp(struct stack_trace *trace, unsigned long bp)
87{
88 dump_trace(current, NULL, NULL, bp, &save_stack_ops, trace);
89 if (trace->nr_entries < trace->max_entries)
90 trace->entries[trace->nr_entries++] = ULONG_MAX;
91}
92
9745512c
AV
93void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
94{
5bc27dc2 95 dump_trace(tsk, NULL, NULL, 0, &save_stack_ops_nosched, trace);
9745512c
AV
96 if (trace->nr_entries < trace->max_entries)
97 trace->entries[trace->nr_entries++] = ULONG_MAX;
98}
8594698e 99EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
02b67518
TE
100
101/* Userspace stacktrace - based on kernel/trace/trace_sysprof.c */
102
c9cf4dbb 103struct stack_frame_user {
02b67518 104 const void __user *next_fp;
8d7c6a96 105 unsigned long ret_addr;
02b67518
TE
106};
107
c9cf4dbb
FW
108static int
109copy_stack_frame(const void __user *fp, struct stack_frame_user *frame)
02b67518
TE
110{
111 int ret;
112
113 if (!access_ok(VERIFY_READ, fp, sizeof(*frame)))
114 return 0;
115
116 ret = 1;
117 pagefault_disable();
118 if (__copy_from_user_inatomic(frame, fp, sizeof(*frame)))
119 ret = 0;
120 pagefault_enable();
121
122 return ret;
123}
124
8d7c6a96
TE
125static inline void __save_stack_trace_user(struct stack_trace *trace)
126{
127 const struct pt_regs *regs = task_pt_regs(current);
128 const void __user *fp = (const void __user *)regs->bp;
129
130 if (trace->nr_entries < trace->max_entries)
131 trace->entries[trace->nr_entries++] = regs->ip;
132
133 while (trace->nr_entries < trace->max_entries) {
c9cf4dbb 134 struct stack_frame_user frame;
8d7c6a96
TE
135
136 frame.next_fp = NULL;
137 frame.ret_addr = 0;
138 if (!copy_stack_frame(fp, &frame))
139 break;
140 if ((unsigned long)fp < regs->sp)
141 break;
142 if (frame.ret_addr) {
143 trace->entries[trace->nr_entries++] =
144 frame.ret_addr;
145 }
146 if (fp == frame.next_fp)
147 break;
148 fp = frame.next_fp;
149 }
150}
151
02b67518
TE
152void save_stack_trace_user(struct stack_trace *trace)
153{
154 /*
155 * Trace user stack if we are not a kernel thread
156 */
157 if (current->mm) {
8d7c6a96 158 __save_stack_trace_user(trace);
02b67518
TE
159 }
160 if (trace->nr_entries < trace->max_entries)
161 trace->entries[trace->nr_entries++] = ULONG_MAX;
162}
163