diff options
Diffstat (limited to 'arch/x86/kernel/ftrace.c')
-rw-r--r-- | arch/x86/kernel/ftrace.c | 258 |
1 files changed, 130 insertions, 128 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index d98b5a8ecf4c..924153edd973 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
@@ -24,134 +24,6 @@ | |||
24 | #include <asm/nmi.h> | 24 | #include <asm/nmi.h> |
25 | 25 | ||
26 | 26 | ||
27 | |||
28 | #ifdef CONFIG_FUNCTION_RET_TRACER | ||
29 | |||
30 | /* | ||
31 | * These functions are picked from those used on | ||
32 | * this page for dynamic ftrace. They have been | ||
33 | * simplified to ignore all traces in NMI context. | ||
34 | */ | ||
35 | static atomic_t in_nmi; | ||
36 | |||
37 | void ftrace_nmi_enter(void) | ||
38 | { | ||
39 | atomic_inc(&in_nmi); | ||
40 | } | ||
41 | |||
42 | void ftrace_nmi_exit(void) | ||
43 | { | ||
44 | atomic_dec(&in_nmi); | ||
45 | } | ||
46 | |||
47 | /* Add a function return address to the trace stack on thread info.*/ | ||
48 | static int push_return_trace(unsigned long ret, unsigned long long time, | ||
49 | unsigned long func) | ||
50 | { | ||
51 | int index; | ||
52 | struct thread_info *ti = current_thread_info(); | ||
53 | |||
54 | /* The return trace stack is full */ | ||
55 | if (ti->curr_ret_stack == FTRACE_RET_STACK_SIZE - 1) | ||
56 | return -EBUSY; | ||
57 | |||
58 | index = ++ti->curr_ret_stack; | ||
59 | barrier(); | ||
60 | ti->ret_stack[index].ret = ret; | ||
61 | ti->ret_stack[index].func = func; | ||
62 | ti->ret_stack[index].calltime = time; | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | /* Retrieve a function return address to the trace stack on thread info.*/ | ||
68 | static void pop_return_trace(unsigned long *ret, unsigned long long *time, | ||
69 | unsigned long *func) | ||
70 | { | ||
71 | int index; | ||
72 | |||
73 | struct thread_info *ti = current_thread_info(); | ||
74 | index = ti->curr_ret_stack; | ||
75 | *ret = ti->ret_stack[index].ret; | ||
76 | *func = ti->ret_stack[index].func; | ||
77 | *time = ti->ret_stack[index].calltime; | ||
78 | ti->curr_ret_stack--; | ||
79 | } | ||
80 | |||
81 | /* | ||
82 | * Send the trace to the ring-buffer. | ||
83 | * @return the original return address. | ||
84 | */ | ||
85 | unsigned long ftrace_return_to_handler(void) | ||
86 | { | ||
87 | struct ftrace_retfunc trace; | ||
88 | pop_return_trace(&trace.ret, &trace.calltime, &trace.func); | ||
89 | trace.rettime = cpu_clock(raw_smp_processor_id()); | ||
90 | ftrace_function_return(&trace); | ||
91 | |||
92 | return trace.ret; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Hook the return address and push it in the stack of return addrs | ||
97 | * in current thread info. | ||
98 | */ | ||
99 | void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) | ||
100 | { | ||
101 | unsigned long old; | ||
102 | unsigned long long calltime; | ||
103 | int faulted; | ||
104 | unsigned long return_hooker = (unsigned long) | ||
105 | &return_to_handler; | ||
106 | |||
107 | /* Nmi's are currently unsupported */ | ||
108 | if (atomic_read(&in_nmi)) | ||
109 | return; | ||
110 | |||
111 | /* | ||
112 | * Protect against fault, even if it shouldn't | ||
113 | * happen. This tool is too much intrusive to | ||
114 | * ignore such a protection. | ||
115 | */ | ||
116 | asm volatile( | ||
117 | "1: movl (%[parent_old]), %[old]\n" | ||
118 | "2: movl %[return_hooker], (%[parent_replaced])\n" | ||
119 | " movl $0, %[faulted]\n" | ||
120 | |||
121 | ".section .fixup, \"ax\"\n" | ||
122 | "3: movl $1, %[faulted]\n" | ||
123 | ".previous\n" | ||
124 | |||
125 | ".section __ex_table, \"a\"\n" | ||
126 | " .long 1b, 3b\n" | ||
127 | " .long 2b, 3b\n" | ||
128 | ".previous\n" | ||
129 | |||
130 | : [parent_replaced] "=r" (parent), [old] "=r" (old), | ||
131 | [faulted] "=r" (faulted) | ||
132 | : [parent_old] "0" (parent), [return_hooker] "r" (return_hooker) | ||
133 | : "memory" | ||
134 | ); | ||
135 | |||
136 | if (WARN_ON(faulted)) { | ||
137 | unregister_ftrace_return(); | ||
138 | return; | ||
139 | } | ||
140 | |||
141 | if (WARN_ON(!__kernel_text_address(old))) { | ||
142 | unregister_ftrace_return(); | ||
143 | *parent = old; | ||
144 | return; | ||
145 | } | ||
146 | |||
147 | calltime = cpu_clock(raw_smp_processor_id()); | ||
148 | |||
149 | if (push_return_trace(old, calltime, self_addr) == -EBUSY) | ||
150 | *parent = old; | ||
151 | } | ||
152 | |||
153 | #endif | ||
154 | |||
155 | #ifdef CONFIG_DYNAMIC_FTRACE | 27 | #ifdef CONFIG_DYNAMIC_FTRACE |
156 | 28 | ||
157 | union ftrace_code_union { | 29 | union ftrace_code_union { |
@@ -450,3 +322,133 @@ int __init ftrace_dyn_arch_init(void *data) | |||
450 | return 0; | 322 | return 0; |
451 | } | 323 | } |
452 | #endif | 324 | #endif |
325 | |||
326 | #ifdef CONFIG_FUNCTION_RET_TRACER | ||
327 | |||
328 | #ifndef CONFIG_DYNAMIC_FTRACE | ||
329 | |||
330 | /* | ||
331 | * These functions are picked from those used on | ||
332 | * this page for dynamic ftrace. They have been | ||
333 | * simplified to ignore all traces in NMI context. | ||
334 | */ | ||
335 | static atomic_t in_nmi; | ||
336 | |||
337 | void ftrace_nmi_enter(void) | ||
338 | { | ||
339 | atomic_inc(&in_nmi); | ||
340 | } | ||
341 | |||
342 | void ftrace_nmi_exit(void) | ||
343 | { | ||
344 | atomic_dec(&in_nmi); | ||
345 | } | ||
346 | #endif /* !CONFIG_DYNAMIC_FTRACE */ | ||
347 | |||
348 | /* Add a function return address to the trace stack on thread info.*/ | ||
349 | static int push_return_trace(unsigned long ret, unsigned long long time, | ||
350 | unsigned long func) | ||
351 | { | ||
352 | int index; | ||
353 | struct thread_info *ti = current_thread_info(); | ||
354 | |||
355 | /* The return trace stack is full */ | ||
356 | if (ti->curr_ret_stack == FTRACE_RET_STACK_SIZE - 1) | ||
357 | return -EBUSY; | ||
358 | |||
359 | index = ++ti->curr_ret_stack; | ||
360 | barrier(); | ||
361 | ti->ret_stack[index].ret = ret; | ||
362 | ti->ret_stack[index].func = func; | ||
363 | ti->ret_stack[index].calltime = time; | ||
364 | |||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | /* Retrieve a function return address to the trace stack on thread info.*/ | ||
369 | static void pop_return_trace(unsigned long *ret, unsigned long long *time, | ||
370 | unsigned long *func) | ||
371 | { | ||
372 | int index; | ||
373 | |||
374 | struct thread_info *ti = current_thread_info(); | ||
375 | index = ti->curr_ret_stack; | ||
376 | *ret = ti->ret_stack[index].ret; | ||
377 | *func = ti->ret_stack[index].func; | ||
378 | *time = ti->ret_stack[index].calltime; | ||
379 | ti->curr_ret_stack--; | ||
380 | } | ||
381 | |||
382 | /* | ||
383 | * Send the trace to the ring-buffer. | ||
384 | * @return the original return address. | ||
385 | */ | ||
386 | unsigned long ftrace_return_to_handler(void) | ||
387 | { | ||
388 | struct ftrace_retfunc trace; | ||
389 | pop_return_trace(&trace.ret, &trace.calltime, &trace.func); | ||
390 | trace.rettime = cpu_clock(raw_smp_processor_id()); | ||
391 | ftrace_function_return(&trace); | ||
392 | |||
393 | return trace.ret; | ||
394 | } | ||
395 | |||
396 | /* | ||
397 | * Hook the return address and push it in the stack of return addrs | ||
398 | * in current thread info. | ||
399 | */ | ||
400 | void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) | ||
401 | { | ||
402 | unsigned long old; | ||
403 | unsigned long long calltime; | ||
404 | int faulted; | ||
405 | unsigned long return_hooker = (unsigned long) | ||
406 | &return_to_handler; | ||
407 | |||
408 | /* Nmi's are currently unsupported */ | ||
409 | if (atomic_read(&in_nmi)) | ||
410 | return; | ||
411 | |||
412 | /* | ||
413 | * Protect against fault, even if it shouldn't | ||
414 | * happen. This tool is too much intrusive to | ||
415 | * ignore such a protection. | ||
416 | */ | ||
417 | asm volatile( | ||
418 | "1: movl (%[parent_old]), %[old]\n" | ||
419 | "2: movl %[return_hooker], (%[parent_replaced])\n" | ||
420 | " movl $0, %[faulted]\n" | ||
421 | |||
422 | ".section .fixup, \"ax\"\n" | ||
423 | "3: movl $1, %[faulted]\n" | ||
424 | ".previous\n" | ||
425 | |||
426 | ".section __ex_table, \"a\"\n" | ||
427 | " .long 1b, 3b\n" | ||
428 | " .long 2b, 3b\n" | ||
429 | ".previous\n" | ||
430 | |||
431 | : [parent_replaced] "=r" (parent), [old] "=r" (old), | ||
432 | [faulted] "=r" (faulted) | ||
433 | : [parent_old] "0" (parent), [return_hooker] "r" (return_hooker) | ||
434 | : "memory" | ||
435 | ); | ||
436 | |||
437 | if (WARN_ON(faulted)) { | ||
438 | unregister_ftrace_return(); | ||
439 | return; | ||
440 | } | ||
441 | |||
442 | if (WARN_ON(!__kernel_text_address(old))) { | ||
443 | unregister_ftrace_return(); | ||
444 | *parent = old; | ||
445 | return; | ||
446 | } | ||
447 | |||
448 | calltime = cpu_clock(raw_smp_processor_id()); | ||
449 | |||
450 | if (push_return_trace(old, calltime, self_addr) == -EBUSY) | ||
451 | *parent = old; | ||
452 | } | ||
453 | |||
454 | #endif /* CONFIG_FUNCTION_RET_TRACER */ | ||