diff options
author | Josh Poimboeuf <jpoimboe@redhat.com> | 2016-08-19 07:52:57 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-08-24 06:15:14 -0400 |
commit | 9a7c348ba6a46f6270d4fe49577649dad5664fe7 (patch) | |
tree | 747a399fc9f6f3f1e234e27a164b9bb5e6ac015a | |
parent | daa460a88c09b26b68e8b017de589c217e901afb (diff) |
ftrace: Add return address pointer to ftrace_ret_stack
Storing this value will help prevent unwinders from getting out of sync
with the function graph tracer ret_stack. Now instead of needing a
stateful iterator, they can compare the return address pointer to find
the right ret_stack entry.
Note that an array of 50 ftrace_ret_stack structs is allocated for every
task. So when an arch implements this, it will add either 200 or 400
bytes of memory usage per task (depending on whether it's a 32-bit or
64-bit platform).
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Byungchul Park <byungchul.park@lge.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Nilay Vaish <nilayvaish@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/a95cfcc39e8f26b89a430c56926af0bb217bc0a1.1471607358.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | Documentation/trace/ftrace-design.txt | 11 | ||||
-rw-r--r-- | arch/arm/kernel/ftrace.c | 2 | ||||
-rw-r--r-- | arch/arm64/kernel/ftrace.c | 2 | ||||
-rw-r--r-- | arch/blackfin/kernel/ftrace.c | 2 | ||||
-rw-r--r-- | arch/microblaze/kernel/ftrace.c | 2 | ||||
-rw-r--r-- | arch/mips/kernel/ftrace.c | 4 | ||||
-rw-r--r-- | arch/parisc/kernel/ftrace.c | 2 | ||||
-rw-r--r-- | arch/powerpc/kernel/ftrace.c | 3 | ||||
-rw-r--r-- | arch/s390/kernel/ftrace.c | 3 | ||||
-rw-r--r-- | arch/sh/kernel/ftrace.c | 2 | ||||
-rw-r--r-- | arch/sparc/kernel/ftrace.c | 2 | ||||
-rw-r--r-- | arch/tile/kernel/ftrace.c | 2 | ||||
-rw-r--r-- | arch/x86/kernel/ftrace.c | 2 | ||||
-rw-r--r-- | include/linux/ftrace.h | 5 | ||||
-rw-r--r-- | kernel/trace/trace_functions_graph.c | 5 |
15 files changed, 34 insertions, 15 deletions
diff --git a/Documentation/trace/ftrace-design.txt b/Documentation/trace/ftrace-design.txt index dd5f916b351d..a273dd0bbaaa 100644 --- a/Documentation/trace/ftrace-design.txt +++ b/Documentation/trace/ftrace-design.txt | |||
@@ -203,6 +203,17 @@ along to ftrace_push_return_trace() instead of a stub value of 0. | |||
203 | 203 | ||
204 | Similarly, when you call ftrace_return_to_handler(), pass it the frame pointer. | 204 | Similarly, when you call ftrace_return_to_handler(), pass it the frame pointer. |
205 | 205 | ||
206 | HAVE_FUNCTION_GRAPH_RET_ADDR_PTR | ||
207 | -------------------------------- | ||
208 | |||
209 | An arch may pass in a pointer to the return address on the stack. This | ||
210 | prevents potential stack unwinding issues where the unwinder gets out of | ||
211 | sync with ret_stack and the wrong addresses are reported by | ||
212 | ftrace_graph_ret_addr(). | ||
213 | |||
214 | Adding support for it is easy: just define the macro in asm/ftrace.h and | ||
215 | pass the return address pointer as the 'retp' argument to | ||
216 | ftrace_push_return_trace(). | ||
206 | 217 | ||
207 | HAVE_FTRACE_NMI_ENTER | 218 | HAVE_FTRACE_NMI_ENTER |
208 | --------------------- | 219 | --------------------- |
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index 709ee1d6d4df..3f1759411d51 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c | |||
@@ -218,7 +218,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, | |||
218 | } | 218 | } |
219 | 219 | ||
220 | err = ftrace_push_return_trace(old, self_addr, &trace.depth, | 220 | err = ftrace_push_return_trace(old, self_addr, &trace.depth, |
221 | frame_pointer); | 221 | frame_pointer, NULL); |
222 | if (err == -EBUSY) { | 222 | if (err == -EBUSY) { |
223 | *parent = old; | 223 | *parent = old; |
224 | return; | 224 | return; |
diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index ebecf9aa33d1..40ad08ac569a 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c | |||
@@ -138,7 +138,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, | |||
138 | return; | 138 | return; |
139 | 139 | ||
140 | err = ftrace_push_return_trace(old, self_addr, &trace.depth, | 140 | err = ftrace_push_return_trace(old, self_addr, &trace.depth, |
141 | frame_pointer); | 141 | frame_pointer, NULL); |
142 | if (err == -EBUSY) | 142 | if (err == -EBUSY) |
143 | return; | 143 | return; |
144 | else | 144 | else |
diff --git a/arch/blackfin/kernel/ftrace.c b/arch/blackfin/kernel/ftrace.c index 095de0fa044d..8dad7589b843 100644 --- a/arch/blackfin/kernel/ftrace.c +++ b/arch/blackfin/kernel/ftrace.c | |||
@@ -107,7 +107,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, | |||
107 | return; | 107 | return; |
108 | 108 | ||
109 | if (ftrace_push_return_trace(*parent, self_addr, &trace.depth, | 109 | if (ftrace_push_return_trace(*parent, self_addr, &trace.depth, |
110 | frame_pointer) == -EBUSY) | 110 | frame_pointer, NULL) == -EBUSY) |
111 | return; | 111 | return; |
112 | 112 | ||
113 | trace.func = self_addr; | 113 | trace.func = self_addr; |
diff --git a/arch/microblaze/kernel/ftrace.c b/arch/microblaze/kernel/ftrace.c index fc7b48a52cd5..d57563c58a26 100644 --- a/arch/microblaze/kernel/ftrace.c +++ b/arch/microblaze/kernel/ftrace.c | |||
@@ -63,7 +63,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) | |||
63 | return; | 63 | return; |
64 | } | 64 | } |
65 | 65 | ||
66 | err = ftrace_push_return_trace(old, self_addr, &trace.depth, 0); | 66 | err = ftrace_push_return_trace(old, self_addr, &trace.depth, 0, NULL); |
67 | if (err == -EBUSY) { | 67 | if (err == -EBUSY) { |
68 | *parent = old; | 68 | *parent = old; |
69 | return; | 69 | return; |
diff --git a/arch/mips/kernel/ftrace.c b/arch/mips/kernel/ftrace.c index 937c54bc8ccc..30a3b75e88eb 100644 --- a/arch/mips/kernel/ftrace.c +++ b/arch/mips/kernel/ftrace.c | |||
@@ -382,8 +382,8 @@ void prepare_ftrace_return(unsigned long *parent_ra_addr, unsigned long self_ra, | |||
382 | if (unlikely(faulted)) | 382 | if (unlikely(faulted)) |
383 | goto out; | 383 | goto out; |
384 | 384 | ||
385 | if (ftrace_push_return_trace(old_parent_ra, self_ra, &trace.depth, fp) | 385 | if (ftrace_push_return_trace(old_parent_ra, self_ra, &trace.depth, fp, |
386 | == -EBUSY) { | 386 | NULL) == -EBUSY) { |
387 | *parent_ra_addr = old_parent_ra; | 387 | *parent_ra_addr = old_parent_ra; |
388 | return; | 388 | return; |
389 | } | 389 | } |
diff --git a/arch/parisc/kernel/ftrace.c b/arch/parisc/kernel/ftrace.c index a828a0adf52c..5a5506a35395 100644 --- a/arch/parisc/kernel/ftrace.c +++ b/arch/parisc/kernel/ftrace.c | |||
@@ -48,7 +48,7 @@ static void __hot prepare_ftrace_return(unsigned long *parent, | |||
48 | return; | 48 | return; |
49 | 49 | ||
50 | if (ftrace_push_return_trace(old, self_addr, &trace.depth, | 50 | if (ftrace_push_return_trace(old, self_addr, &trace.depth, |
51 | 0 ) == -EBUSY) | 51 | 0, NULL) == -EBUSY) |
52 | return; | 52 | return; |
53 | 53 | ||
54 | /* activate parisc_return_to_handler() as return point */ | 54 | /* activate parisc_return_to_handler() as return point */ |
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c index cc52d9795f88..a95639b8d4ac 100644 --- a/arch/powerpc/kernel/ftrace.c +++ b/arch/powerpc/kernel/ftrace.c | |||
@@ -593,7 +593,8 @@ unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip) | |||
593 | if (!ftrace_graph_entry(&trace)) | 593 | if (!ftrace_graph_entry(&trace)) |
594 | goto out; | 594 | goto out; |
595 | 595 | ||
596 | if (ftrace_push_return_trace(parent, ip, &trace.depth, 0) == -EBUSY) | 596 | if (ftrace_push_return_trace(parent, ip, &trace.depth, 0, |
597 | NULL) == -EBUSY) | ||
597 | goto out; | 598 | goto out; |
598 | 599 | ||
599 | parent = return_hooker; | 600 | parent = return_hooker; |
diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c index 0f7bfeba6da6..60a8a4e207ed 100644 --- a/arch/s390/kernel/ftrace.c +++ b/arch/s390/kernel/ftrace.c | |||
@@ -209,7 +209,8 @@ unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip) | |||
209 | /* Only trace if the calling function expects to. */ | 209 | /* Only trace if the calling function expects to. */ |
210 | if (!ftrace_graph_entry(&trace)) | 210 | if (!ftrace_graph_entry(&trace)) |
211 | goto out; | 211 | goto out; |
212 | if (ftrace_push_return_trace(parent, ip, &trace.depth, 0) == -EBUSY) | 212 | if (ftrace_push_return_trace(parent, ip, &trace.depth, 0, |
213 | NULL) == -EBUSY) | ||
213 | goto out; | 214 | goto out; |
214 | parent = (unsigned long) return_to_handler; | 215 | parent = (unsigned long) return_to_handler; |
215 | out: | 216 | out: |
diff --git a/arch/sh/kernel/ftrace.c b/arch/sh/kernel/ftrace.c index 38993e09ef03..95eccd49672f 100644 --- a/arch/sh/kernel/ftrace.c +++ b/arch/sh/kernel/ftrace.c | |||
@@ -382,7 +382,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) | |||
382 | return; | 382 | return; |
383 | } | 383 | } |
384 | 384 | ||
385 | err = ftrace_push_return_trace(old, self_addr, &trace.depth, 0); | 385 | err = ftrace_push_return_trace(old, self_addr, &trace.depth, 0, NULL); |
386 | if (err == -EBUSY) { | 386 | if (err == -EBUSY) { |
387 | __raw_writel(old, parent); | 387 | __raw_writel(old, parent); |
388 | return; | 388 | return; |
diff --git a/arch/sparc/kernel/ftrace.c b/arch/sparc/kernel/ftrace.c index 0a2d2ddff543..6bcff698069b 100644 --- a/arch/sparc/kernel/ftrace.c +++ b/arch/sparc/kernel/ftrace.c | |||
@@ -131,7 +131,7 @@ unsigned long prepare_ftrace_return(unsigned long parent, | |||
131 | return parent + 8UL; | 131 | return parent + 8UL; |
132 | 132 | ||
133 | if (ftrace_push_return_trace(parent, self_addr, &trace.depth, | 133 | if (ftrace_push_return_trace(parent, self_addr, &trace.depth, |
134 | frame_pointer) == -EBUSY) | 134 | frame_pointer, NULL) == -EBUSY) |
135 | return parent + 8UL; | 135 | return parent + 8UL; |
136 | 136 | ||
137 | trace.func = self_addr; | 137 | trace.func = self_addr; |
diff --git a/arch/tile/kernel/ftrace.c b/arch/tile/kernel/ftrace.c index 4a572088b270..b827a418b155 100644 --- a/arch/tile/kernel/ftrace.c +++ b/arch/tile/kernel/ftrace.c | |||
@@ -184,7 +184,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, | |||
184 | *parent = return_hooker; | 184 | *parent = return_hooker; |
185 | 185 | ||
186 | err = ftrace_push_return_trace(old, self_addr, &trace.depth, | 186 | err = ftrace_push_return_trace(old, self_addr, &trace.depth, |
187 | frame_pointer); | 187 | frame_pointer, NULL); |
188 | if (err == -EBUSY) { | 188 | if (err == -EBUSY) { |
189 | *parent = old; | 189 | *parent = old; |
190 | return; | 190 | return; |
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index d036cfb4495d..ae3b1fb2f582 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
@@ -1029,7 +1029,7 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, | |||
1029 | } | 1029 | } |
1030 | 1030 | ||
1031 | if (ftrace_push_return_trace(old, self_addr, &trace.depth, | 1031 | if (ftrace_push_return_trace(old, self_addr, &trace.depth, |
1032 | frame_pointer) == -EBUSY) { | 1032 | frame_pointer, NULL) == -EBUSY) { |
1033 | *parent = old; | 1033 | *parent = old; |
1034 | return; | 1034 | return; |
1035 | } | 1035 | } |
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 4ad9ccc60e38..483e02a50d37 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -798,6 +798,9 @@ struct ftrace_ret_stack { | |||
798 | #ifdef HAVE_FUNCTION_GRAPH_FP_TEST | 798 | #ifdef HAVE_FUNCTION_GRAPH_FP_TEST |
799 | unsigned long fp; | 799 | unsigned long fp; |
800 | #endif | 800 | #endif |
801 | #ifdef HAVE_FUNCTION_GRAPH_RET_ADDR_PTR | ||
802 | unsigned long *retp; | ||
803 | #endif | ||
801 | }; | 804 | }; |
802 | 805 | ||
803 | /* | 806 | /* |
@@ -809,7 +812,7 @@ extern void return_to_handler(void); | |||
809 | 812 | ||
810 | extern int | 813 | extern int |
811 | ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth, | 814 | ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth, |
812 | unsigned long frame_pointer); | 815 | unsigned long frame_pointer, unsigned long *retp); |
813 | 816 | ||
814 | /* | 817 | /* |
815 | * Sometimes we don't want to trace a function with the function | 818 | * Sometimes we don't want to trace a function with the function |
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 0e03ed0eac68..f7212ec643e2 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
@@ -119,7 +119,7 @@ print_graph_duration(struct trace_array *tr, unsigned long long duration, | |||
119 | /* Add a function return address to the trace stack on thread info.*/ | 119 | /* Add a function return address to the trace stack on thread info.*/ |
120 | int | 120 | int |
121 | ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth, | 121 | ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth, |
122 | unsigned long frame_pointer) | 122 | unsigned long frame_pointer, unsigned long *retp) |
123 | { | 123 | { |
124 | unsigned long long calltime; | 124 | unsigned long long calltime; |
125 | int index; | 125 | int index; |
@@ -174,6 +174,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth, | |||
174 | #ifdef HAVE_FUNCTION_GRAPH_FP_TEST | 174 | #ifdef HAVE_FUNCTION_GRAPH_FP_TEST |
175 | current->ret_stack[index].fp = frame_pointer; | 175 | current->ret_stack[index].fp = frame_pointer; |
176 | #endif | 176 | #endif |
177 | #ifdef HAVE_FUNCTION_GRAPH_RET_ADDR_PTR | ||
178 | current->ret_stack[index].retp = retp; | ||
179 | #endif | ||
177 | *depth = current->curr_ret_stack; | 180 | *depth = current->curr_ret_stack; |
178 | 181 | ||
179 | return 0; | 182 | return 0; |