diff options
-rw-r--r-- | arch/x86/kernel/entry_32.S | 5 | ||||
-rw-r--r-- | arch/x86/kernel/ftrace.c | 48 | ||||
-rw-r--r-- | include/linux/ftrace.h | 5 | ||||
-rw-r--r-- | kernel/trace/ftrace.c | 35 |
4 files changed, 72 insertions, 21 deletions
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 7def9fd5c1e6..958af86186c4 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S | |||
@@ -1174,6 +1174,11 @@ ftrace_call: | |||
1174 | popl %edx | 1174 | popl %edx |
1175 | popl %ecx | 1175 | popl %ecx |
1176 | popl %eax | 1176 | popl %eax |
1177 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
1178 | .globl ftrace_graph_call | ||
1179 | ftrace_graph_call: | ||
1180 | jmp ftrace_stub | ||
1181 | #endif | ||
1177 | 1182 | ||
1178 | .globl ftrace_stub | 1183 | .globl ftrace_stub |
1179 | ftrace_stub: | 1184 | ftrace_stub: |
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 26b2d92d48b3..7ef914e6a2f6 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
@@ -111,7 +111,6 @@ static void ftrace_mod_code(void) | |||
111 | */ | 111 | */ |
112 | mod_code_status = probe_kernel_write(mod_code_ip, mod_code_newcode, | 112 | mod_code_status = probe_kernel_write(mod_code_ip, mod_code_newcode, |
113 | MCOUNT_INSN_SIZE); | 113 | MCOUNT_INSN_SIZE); |
114 | |||
115 | } | 114 | } |
116 | 115 | ||
117 | void ftrace_nmi_enter(void) | 116 | void ftrace_nmi_enter(void) |
@@ -325,7 +324,51 @@ int __init ftrace_dyn_arch_init(void *data) | |||
325 | 324 | ||
326 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 325 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
327 | 326 | ||
328 | #ifndef CONFIG_DYNAMIC_FTRACE | 327 | #ifdef CONFIG_DYNAMIC_FTRACE |
328 | extern void ftrace_graph_call(void); | ||
329 | |||
330 | static int ftrace_mod_jmp(unsigned long ip, | ||
331 | int old_offset, int new_offset) | ||
332 | { | ||
333 | unsigned char code[MCOUNT_INSN_SIZE]; | ||
334 | |||
335 | if (probe_kernel_read(code, (void *)ip, MCOUNT_INSN_SIZE)) | ||
336 | return -EFAULT; | ||
337 | |||
338 | if (code[0] != 0xe9 || old_offset != *(int *)(&code[1])) | ||
339 | return -EINVAL; | ||
340 | |||
341 | *(int *)(&code[1]) = new_offset; | ||
342 | |||
343 | if (do_ftrace_mod_code(ip, &code)) | ||
344 | return -EPERM; | ||
345 | |||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | int ftrace_enable_ftrace_graph_caller(void) | ||
350 | { | ||
351 | unsigned long ip = (unsigned long)(&ftrace_graph_call); | ||
352 | int old_offset, new_offset; | ||
353 | |||
354 | old_offset = (unsigned long)(&ftrace_stub) - (ip + MCOUNT_INSN_SIZE); | ||
355 | new_offset = (unsigned long)(&ftrace_graph_caller) - (ip + MCOUNT_INSN_SIZE); | ||
356 | |||
357 | return ftrace_mod_jmp(ip, old_offset, new_offset); | ||
358 | } | ||
359 | |||
360 | int ftrace_disable_ftrace_graph_caller(void) | ||
361 | { | ||
362 | unsigned long ip = (unsigned long)(&ftrace_graph_call); | ||
363 | int old_offset, new_offset; | ||
364 | |||
365 | old_offset = (unsigned long)(&ftrace_graph_caller) - (ip + MCOUNT_INSN_SIZE); | ||
366 | new_offset = (unsigned long)(&ftrace_stub) - (ip + MCOUNT_INSN_SIZE); | ||
367 | |||
368 | return ftrace_mod_jmp(ip, old_offset, new_offset); | ||
369 | } | ||
370 | |||
371 | #else /* CONFIG_DYNAMIC_FTRACE */ | ||
329 | 372 | ||
330 | /* | 373 | /* |
331 | * These functions are picked from those used on | 374 | * These functions are picked from those used on |
@@ -343,6 +386,7 @@ void ftrace_nmi_exit(void) | |||
343 | { | 386 | { |
344 | atomic_dec(&in_nmi); | 387 | atomic_dec(&in_nmi); |
345 | } | 388 | } |
389 | |||
346 | #endif /* !CONFIG_DYNAMIC_FTRACE */ | 390 | #endif /* !CONFIG_DYNAMIC_FTRACE */ |
347 | 391 | ||
348 | /* Add a function return address to the trace stack on thread info.*/ | 392 | /* Add a function return address to the trace stack on thread info.*/ |
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index fc2d54987198..f9792c0d73f6 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -117,6 +117,11 @@ extern void ftrace_call(void); | |||
117 | extern void mcount_call(void); | 117 | extern void mcount_call(void); |
118 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 118 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
119 | extern void ftrace_graph_caller(void); | 119 | extern void ftrace_graph_caller(void); |
120 | extern int ftrace_enable_ftrace_graph_caller(void); | ||
121 | extern int ftrace_disable_ftrace_graph_caller(void); | ||
122 | #else | ||
123 | static inline int ftrace_enable_ftrace_graph_caller(void) { return 0; } | ||
124 | static inline int ftrace_disable_ftrace_graph_caller(void) { return 0; } | ||
120 | #endif | 125 | #endif |
121 | 126 | ||
122 | /** | 127 | /** |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 00d98c65fad0..5f7c8642d58b 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -281,6 +281,8 @@ enum { | |||
281 | FTRACE_UPDATE_TRACE_FUNC = (1 << 2), | 281 | FTRACE_UPDATE_TRACE_FUNC = (1 << 2), |
282 | FTRACE_ENABLE_MCOUNT = (1 << 3), | 282 | FTRACE_ENABLE_MCOUNT = (1 << 3), |
283 | FTRACE_DISABLE_MCOUNT = (1 << 4), | 283 | FTRACE_DISABLE_MCOUNT = (1 << 4), |
284 | FTRACE_START_FUNC_RET = (1 << 5), | ||
285 | FTRACE_STOP_FUNC_RET = (1 << 6), | ||
284 | }; | 286 | }; |
285 | 287 | ||
286 | static int ftrace_filtered; | 288 | static int ftrace_filtered; |
@@ -465,14 +467,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable) | |||
465 | unsigned long ip, fl; | 467 | unsigned long ip, fl; |
466 | unsigned long ftrace_addr; | 468 | unsigned long ftrace_addr; |
467 | 469 | ||
468 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
469 | if (ftrace_tracing_type == FTRACE_TYPE_ENTER) | ||
470 | ftrace_addr = (unsigned long)ftrace_caller; | ||
471 | else | ||
472 | ftrace_addr = (unsigned long)ftrace_graph_caller; | ||
473 | #else | ||
474 | ftrace_addr = (unsigned long)ftrace_caller; | 470 | ftrace_addr = (unsigned long)ftrace_caller; |
475 | #endif | ||
476 | 471 | ||
477 | ip = rec->ip; | 472 | ip = rec->ip; |
478 | 473 | ||
@@ -605,6 +600,11 @@ static int __ftrace_modify_code(void *data) | |||
605 | if (*command & FTRACE_UPDATE_TRACE_FUNC) | 600 | if (*command & FTRACE_UPDATE_TRACE_FUNC) |
606 | ftrace_update_ftrace_func(ftrace_trace_function); | 601 | ftrace_update_ftrace_func(ftrace_trace_function); |
607 | 602 | ||
603 | if (*command & FTRACE_START_FUNC_RET) | ||
604 | ftrace_enable_ftrace_graph_caller(); | ||
605 | else if (*command & FTRACE_STOP_FUNC_RET) | ||
606 | ftrace_disable_ftrace_graph_caller(); | ||
607 | |||
608 | return 0; | 608 | return 0; |
609 | } | 609 | } |
610 | 610 | ||
@@ -629,10 +629,8 @@ static void ftrace_startup_enable(int command) | |||
629 | ftrace_run_update_code(command); | 629 | ftrace_run_update_code(command); |
630 | } | 630 | } |
631 | 631 | ||
632 | static void ftrace_startup(void) | 632 | static void ftrace_startup(int command) |
633 | { | 633 | { |
634 | int command = 0; | ||
635 | |||
636 | if (unlikely(ftrace_disabled)) | 634 | if (unlikely(ftrace_disabled)) |
637 | return; | 635 | return; |
638 | 636 | ||
@@ -645,10 +643,8 @@ static void ftrace_startup(void) | |||
645 | mutex_unlock(&ftrace_start_lock); | 643 | mutex_unlock(&ftrace_start_lock); |
646 | } | 644 | } |
647 | 645 | ||
648 | static void ftrace_shutdown(void) | 646 | static void ftrace_shutdown(int command) |
649 | { | 647 | { |
650 | int command = 0; | ||
651 | |||
652 | if (unlikely(ftrace_disabled)) | 648 | if (unlikely(ftrace_disabled)) |
653 | return; | 649 | return; |
654 | 650 | ||
@@ -1453,8 +1449,9 @@ device_initcall(ftrace_nodyn_init); | |||
1453 | 1449 | ||
1454 | static inline int ftrace_init_dyn_debugfs(struct dentry *d_tracer) { return 0; } | 1450 | static inline int ftrace_init_dyn_debugfs(struct dentry *d_tracer) { return 0; } |
1455 | static inline void ftrace_startup_enable(int command) { } | 1451 | static inline void ftrace_startup_enable(int command) { } |
1456 | # define ftrace_startup() do { } while (0) | 1452 | /* Keep as macros so we do not need to define the commands */ |
1457 | # define ftrace_shutdown() do { } while (0) | 1453 | # define ftrace_startup(command) do { } while (0) |
1454 | # define ftrace_shutdown(command) do { } while (0) | ||
1458 | # define ftrace_startup_sysctl() do { } while (0) | 1455 | # define ftrace_startup_sysctl() do { } while (0) |
1459 | # define ftrace_shutdown_sysctl() do { } while (0) | 1456 | # define ftrace_shutdown_sysctl() do { } while (0) |
1460 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 1457 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
@@ -1585,7 +1582,7 @@ int register_ftrace_function(struct ftrace_ops *ops) | |||
1585 | } | 1582 | } |
1586 | 1583 | ||
1587 | ret = __register_ftrace_function(ops); | 1584 | ret = __register_ftrace_function(ops); |
1588 | ftrace_startup(); | 1585 | ftrace_startup(0); |
1589 | 1586 | ||
1590 | out: | 1587 | out: |
1591 | mutex_unlock(&ftrace_sysctl_lock); | 1588 | mutex_unlock(&ftrace_sysctl_lock); |
@@ -1604,7 +1601,7 @@ int unregister_ftrace_function(struct ftrace_ops *ops) | |||
1604 | 1601 | ||
1605 | mutex_lock(&ftrace_sysctl_lock); | 1602 | mutex_lock(&ftrace_sysctl_lock); |
1606 | ret = __unregister_ftrace_function(ops); | 1603 | ret = __unregister_ftrace_function(ops); |
1607 | ftrace_shutdown(); | 1604 | ftrace_shutdown(0); |
1608 | mutex_unlock(&ftrace_sysctl_lock); | 1605 | mutex_unlock(&ftrace_sysctl_lock); |
1609 | 1606 | ||
1610 | return ret; | 1607 | return ret; |
@@ -1751,7 +1748,7 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc, | |||
1751 | ftrace_tracing_type = FTRACE_TYPE_RETURN; | 1748 | ftrace_tracing_type = FTRACE_TYPE_RETURN; |
1752 | ftrace_graph_return = retfunc; | 1749 | ftrace_graph_return = retfunc; |
1753 | ftrace_graph_entry = entryfunc; | 1750 | ftrace_graph_entry = entryfunc; |
1754 | ftrace_startup(); | 1751 | ftrace_startup(FTRACE_START_FUNC_RET); |
1755 | 1752 | ||
1756 | out: | 1753 | out: |
1757 | mutex_unlock(&ftrace_sysctl_lock); | 1754 | mutex_unlock(&ftrace_sysctl_lock); |
@@ -1765,7 +1762,7 @@ void unregister_ftrace_graph(void) | |||
1765 | atomic_dec(&ftrace_graph_active); | 1762 | atomic_dec(&ftrace_graph_active); |
1766 | ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; | 1763 | ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; |
1767 | ftrace_graph_entry = (trace_func_graph_ent_t)ftrace_stub; | 1764 | ftrace_graph_entry = (trace_func_graph_ent_t)ftrace_stub; |
1768 | ftrace_shutdown(); | 1765 | ftrace_shutdown(FTRACE_STOP_FUNC_RET); |
1769 | /* Restore normal tracing type */ | 1766 | /* Restore normal tracing type */ |
1770 | ftrace_tracing_type = FTRACE_TYPE_ENTER; | 1767 | ftrace_tracing_type = FTRACE_TYPE_ENTER; |
1771 | 1768 | ||