diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r-- | kernel/trace/ftrace.c | 82 |
1 files changed, 48 insertions, 34 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1904797f4a8a..2404b59b3097 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -24,9 +24,11 @@ | |||
24 | #include <linux/uaccess.h> | 24 | #include <linux/uaccess.h> |
25 | #include <linux/ftrace.h> | 25 | #include <linux/ftrace.h> |
26 | #include <linux/sysctl.h> | 26 | #include <linux/sysctl.h> |
27 | #include <linux/slab.h> | ||
27 | #include <linux/ctype.h> | 28 | #include <linux/ctype.h> |
28 | #include <linux/list.h> | 29 | #include <linux/list.h> |
29 | #include <linux/hash.h> | 30 | #include <linux/hash.h> |
31 | #include <linux/rcupdate.h> | ||
30 | 32 | ||
31 | #include <trace/events/sched.h> | 33 | #include <trace/events/sched.h> |
32 | 34 | ||
@@ -84,22 +86,22 @@ ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; | |||
84 | ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; | 86 | ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; |
85 | ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; | 87 | ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; |
86 | 88 | ||
87 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 89 | /* |
88 | static int ftrace_set_func(unsigned long *array, int *idx, char *buffer); | 90 | * Traverse the ftrace_list, invoking all entries. The reason that we |
89 | #endif | 91 | * can use rcu_dereference_raw() is that elements removed from this list |
90 | 92 | * are simply leaked, so there is no need to interact with a grace-period | |
93 | * mechanism. The rcu_dereference_raw() calls are needed to handle | ||
94 | * concurrent insertions into the ftrace_list. | ||
95 | * | ||
96 | * Silly Alpha and silly pointer-speculation compiler optimizations! | ||
97 | */ | ||
91 | static void ftrace_list_func(unsigned long ip, unsigned long parent_ip) | 98 | static void ftrace_list_func(unsigned long ip, unsigned long parent_ip) |
92 | { | 99 | { |
93 | struct ftrace_ops *op = ftrace_list; | 100 | struct ftrace_ops *op = rcu_dereference_raw(ftrace_list); /*see above*/ |
94 | |||
95 | /* in case someone actually ports this to alpha! */ | ||
96 | read_barrier_depends(); | ||
97 | 101 | ||
98 | while (op != &ftrace_list_end) { | 102 | while (op != &ftrace_list_end) { |
99 | /* silly alpha */ | ||
100 | read_barrier_depends(); | ||
101 | op->func(ip, parent_ip); | 103 | op->func(ip, parent_ip); |
102 | op = op->next; | 104 | op = rcu_dereference_raw(op->next); /*see above*/ |
103 | }; | 105 | }; |
104 | } | 106 | } |
105 | 107 | ||
@@ -154,8 +156,7 @@ static int __register_ftrace_function(struct ftrace_ops *ops) | |||
154 | * the ops->next pointer is valid before another CPU sees | 156 | * the ops->next pointer is valid before another CPU sees |
155 | * the ops pointer included into the ftrace_list. | 157 | * the ops pointer included into the ftrace_list. |
156 | */ | 158 | */ |
157 | smp_wmb(); | 159 | rcu_assign_pointer(ftrace_list, ops); |
158 | ftrace_list = ops; | ||
159 | 160 | ||
160 | if (ftrace_enabled) { | 161 | if (ftrace_enabled) { |
161 | ftrace_func_t func; | 162 | ftrace_func_t func; |
@@ -2276,6 +2277,8 @@ __setup("ftrace_filter=", set_ftrace_filter); | |||
2276 | 2277 | ||
2277 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 2278 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
2278 | static char ftrace_graph_buf[FTRACE_FILTER_SIZE] __initdata; | 2279 | static char ftrace_graph_buf[FTRACE_FILTER_SIZE] __initdata; |
2280 | static int ftrace_set_func(unsigned long *array, int *idx, char *buffer); | ||
2281 | |||
2279 | static int __init set_graph_function(char *str) | 2282 | static int __init set_graph_function(char *str) |
2280 | { | 2283 | { |
2281 | strlcpy(ftrace_graph_buf, str, FTRACE_FILTER_SIZE); | 2284 | strlcpy(ftrace_graph_buf, str, FTRACE_FILTER_SIZE); |
@@ -2402,6 +2405,7 @@ static const struct file_operations ftrace_notrace_fops = { | |||
2402 | static DEFINE_MUTEX(graph_lock); | 2405 | static DEFINE_MUTEX(graph_lock); |
2403 | 2406 | ||
2404 | int ftrace_graph_count; | 2407 | int ftrace_graph_count; |
2408 | int ftrace_graph_filter_enabled; | ||
2405 | unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; | 2409 | unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; |
2406 | 2410 | ||
2407 | static void * | 2411 | static void * |
@@ -2424,7 +2428,7 @@ static void *g_start(struct seq_file *m, loff_t *pos) | |||
2424 | mutex_lock(&graph_lock); | 2428 | mutex_lock(&graph_lock); |
2425 | 2429 | ||
2426 | /* Nothing, tell g_show to print all functions are enabled */ | 2430 | /* Nothing, tell g_show to print all functions are enabled */ |
2427 | if (!ftrace_graph_count && !*pos) | 2431 | if (!ftrace_graph_filter_enabled && !*pos) |
2428 | return (void *)1; | 2432 | return (void *)1; |
2429 | 2433 | ||
2430 | return __g_next(m, pos); | 2434 | return __g_next(m, pos); |
@@ -2470,6 +2474,7 @@ ftrace_graph_open(struct inode *inode, struct file *file) | |||
2470 | mutex_lock(&graph_lock); | 2474 | mutex_lock(&graph_lock); |
2471 | if ((file->f_mode & FMODE_WRITE) && | 2475 | if ((file->f_mode & FMODE_WRITE) && |
2472 | (file->f_flags & O_TRUNC)) { | 2476 | (file->f_flags & O_TRUNC)) { |
2477 | ftrace_graph_filter_enabled = 0; | ||
2473 | ftrace_graph_count = 0; | 2478 | ftrace_graph_count = 0; |
2474 | memset(ftrace_graph_funcs, 0, sizeof(ftrace_graph_funcs)); | 2479 | memset(ftrace_graph_funcs, 0, sizeof(ftrace_graph_funcs)); |
2475 | } | 2480 | } |
@@ -2495,7 +2500,7 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer) | |||
2495 | struct dyn_ftrace *rec; | 2500 | struct dyn_ftrace *rec; |
2496 | struct ftrace_page *pg; | 2501 | struct ftrace_page *pg; |
2497 | int search_len; | 2502 | int search_len; |
2498 | int found = 0; | 2503 | int fail = 1; |
2499 | int type, not; | 2504 | int type, not; |
2500 | char *search; | 2505 | char *search; |
2501 | bool exists; | 2506 | bool exists; |
@@ -2506,37 +2511,51 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer) | |||
2506 | 2511 | ||
2507 | /* decode regex */ | 2512 | /* decode regex */ |
2508 | type = filter_parse_regex(buffer, strlen(buffer), &search, ¬); | 2513 | type = filter_parse_regex(buffer, strlen(buffer), &search, ¬); |
2509 | if (not) | 2514 | if (!not && *idx >= FTRACE_GRAPH_MAX_FUNCS) |
2510 | return -EINVAL; | 2515 | return -EBUSY; |
2511 | 2516 | ||
2512 | search_len = strlen(search); | 2517 | search_len = strlen(search); |
2513 | 2518 | ||
2514 | mutex_lock(&ftrace_lock); | 2519 | mutex_lock(&ftrace_lock); |
2515 | do_for_each_ftrace_rec(pg, rec) { | 2520 | do_for_each_ftrace_rec(pg, rec) { |
2516 | 2521 | ||
2517 | if (*idx >= FTRACE_GRAPH_MAX_FUNCS) | ||
2518 | break; | ||
2519 | |||
2520 | if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) | 2522 | if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) |
2521 | continue; | 2523 | continue; |
2522 | 2524 | ||
2523 | if (ftrace_match_record(rec, search, search_len, type)) { | 2525 | if (ftrace_match_record(rec, search, search_len, type)) { |
2524 | /* ensure it is not already in the array */ | 2526 | /* if it is in the array */ |
2525 | exists = false; | 2527 | exists = false; |
2526 | for (i = 0; i < *idx; i++) | 2528 | for (i = 0; i < *idx; i++) { |
2527 | if (array[i] == rec->ip) { | 2529 | if (array[i] == rec->ip) { |
2528 | exists = true; | 2530 | exists = true; |
2529 | break; | 2531 | break; |
2530 | } | 2532 | } |
2531 | if (!exists) | 2533 | } |
2532 | array[(*idx)++] = rec->ip; | 2534 | |
2533 | found = 1; | 2535 | if (!not) { |
2536 | fail = 0; | ||
2537 | if (!exists) { | ||
2538 | array[(*idx)++] = rec->ip; | ||
2539 | if (*idx >= FTRACE_GRAPH_MAX_FUNCS) | ||
2540 | goto out; | ||
2541 | } | ||
2542 | } else { | ||
2543 | if (exists) { | ||
2544 | array[i] = array[--(*idx)]; | ||
2545 | array[*idx] = 0; | ||
2546 | fail = 0; | ||
2547 | } | ||
2548 | } | ||
2534 | } | 2549 | } |
2535 | } while_for_each_ftrace_rec(); | 2550 | } while_for_each_ftrace_rec(); |
2536 | 2551 | out: | |
2537 | mutex_unlock(&ftrace_lock); | 2552 | mutex_unlock(&ftrace_lock); |
2538 | 2553 | ||
2539 | return found ? 0 : -EINVAL; | 2554 | if (fail) |
2555 | return -EINVAL; | ||
2556 | |||
2557 | ftrace_graph_filter_enabled = 1; | ||
2558 | return 0; | ||
2540 | } | 2559 | } |
2541 | 2560 | ||
2542 | static ssize_t | 2561 | static ssize_t |
@@ -2546,16 +2565,11 @@ ftrace_graph_write(struct file *file, const char __user *ubuf, | |||
2546 | struct trace_parser parser; | 2565 | struct trace_parser parser; |
2547 | ssize_t read, ret; | 2566 | ssize_t read, ret; |
2548 | 2567 | ||
2549 | if (!cnt || cnt < 0) | 2568 | if (!cnt) |
2550 | return 0; | 2569 | return 0; |
2551 | 2570 | ||
2552 | mutex_lock(&graph_lock); | 2571 | mutex_lock(&graph_lock); |
2553 | 2572 | ||
2554 | if (ftrace_graph_count >= FTRACE_GRAPH_MAX_FUNCS) { | ||
2555 | ret = -EBUSY; | ||
2556 | goto out_unlock; | ||
2557 | } | ||
2558 | |||
2559 | if (trace_parser_get_init(&parser, FTRACE_BUFF_MAX)) { | 2573 | if (trace_parser_get_init(&parser, FTRACE_BUFF_MAX)) { |
2560 | ret = -ENOMEM; | 2574 | ret = -ENOMEM; |
2561 | goto out_unlock; | 2575 | goto out_unlock; |
@@ -3340,6 +3354,7 @@ void ftrace_graph_init_task(struct task_struct *t) | |||
3340 | { | 3354 | { |
3341 | /* Make sure we do not use the parent ret_stack */ | 3355 | /* Make sure we do not use the parent ret_stack */ |
3342 | t->ret_stack = NULL; | 3356 | t->ret_stack = NULL; |
3357 | t->curr_ret_stack = -1; | ||
3343 | 3358 | ||
3344 | if (ftrace_graph_active) { | 3359 | if (ftrace_graph_active) { |
3345 | struct ftrace_ret_stack *ret_stack; | 3360 | struct ftrace_ret_stack *ret_stack; |
@@ -3349,7 +3364,6 @@ void ftrace_graph_init_task(struct task_struct *t) | |||
3349 | GFP_KERNEL); | 3364 | GFP_KERNEL); |
3350 | if (!ret_stack) | 3365 | if (!ret_stack) |
3351 | return; | 3366 | return; |
3352 | t->curr_ret_stack = -1; | ||
3353 | atomic_set(&t->tracing_graph_pause, 0); | 3367 | atomic_set(&t->tracing_graph_pause, 0); |
3354 | atomic_set(&t->trace_overrun, 0); | 3368 | atomic_set(&t->trace_overrun, 0); |
3355 | t->ftrace_timestamp = 0; | 3369 | t->ftrace_timestamp = 0; |