diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r-- | kernel/trace/ftrace.c | 68 |
1 files changed, 61 insertions, 7 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 7847806eefef..1752a63f37c0 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -29,6 +29,8 @@ | |||
29 | #include <linux/list.h> | 29 | #include <linux/list.h> |
30 | #include <linux/hash.h> | 30 | #include <linux/hash.h> |
31 | 31 | ||
32 | #include <trace/sched.h> | ||
33 | |||
32 | #include <asm/ftrace.h> | 34 | #include <asm/ftrace.h> |
33 | 35 | ||
34 | #include "trace.h" | 36 | #include "trace.h" |
@@ -339,7 +341,7 @@ static inline int record_frozen(struct dyn_ftrace *rec) | |||
339 | 341 | ||
340 | static void ftrace_free_rec(struct dyn_ftrace *rec) | 342 | static void ftrace_free_rec(struct dyn_ftrace *rec) |
341 | { | 343 | { |
342 | rec->ip = (unsigned long)ftrace_free_records; | 344 | rec->freelist = ftrace_free_records; |
343 | ftrace_free_records = rec; | 345 | ftrace_free_records = rec; |
344 | rec->flags |= FTRACE_FL_FREE; | 346 | rec->flags |= FTRACE_FL_FREE; |
345 | } | 347 | } |
@@ -356,9 +358,14 @@ void ftrace_release(void *start, unsigned long size) | |||
356 | 358 | ||
357 | mutex_lock(&ftrace_lock); | 359 | mutex_lock(&ftrace_lock); |
358 | do_for_each_ftrace_rec(pg, rec) { | 360 | do_for_each_ftrace_rec(pg, rec) { |
359 | if ((rec->ip >= s) && (rec->ip < e) && | 361 | if ((rec->ip >= s) && (rec->ip < e)) { |
360 | !(rec->flags & FTRACE_FL_FREE)) | 362 | /* |
363 | * rec->ip is changed in ftrace_free_rec() | ||
364 | * It should not between s and e if record was freed. | ||
365 | */ | ||
366 | FTRACE_WARN_ON(rec->flags & FTRACE_FL_FREE); | ||
361 | ftrace_free_rec(rec); | 367 | ftrace_free_rec(rec); |
368 | } | ||
362 | } while_for_each_ftrace_rec(); | 369 | } while_for_each_ftrace_rec(); |
363 | mutex_unlock(&ftrace_lock); | 370 | mutex_unlock(&ftrace_lock); |
364 | } | 371 | } |
@@ -377,7 +384,7 @@ static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) | |||
377 | return NULL; | 384 | return NULL; |
378 | } | 385 | } |
379 | 386 | ||
380 | ftrace_free_records = (void *)rec->ip; | 387 | ftrace_free_records = rec->freelist; |
381 | memset(rec, 0, sizeof(*rec)); | 388 | memset(rec, 0, sizeof(*rec)); |
382 | return rec; | 389 | return rec; |
383 | } | 390 | } |
@@ -409,7 +416,7 @@ ftrace_record_ip(unsigned long ip) | |||
409 | return NULL; | 416 | return NULL; |
410 | 417 | ||
411 | rec->ip = ip; | 418 | rec->ip = ip; |
412 | rec->flags = (unsigned long)ftrace_new_addrs; | 419 | rec->newlist = ftrace_new_addrs; |
413 | ftrace_new_addrs = rec; | 420 | ftrace_new_addrs = rec; |
414 | 421 | ||
415 | return rec; | 422 | return rec; |
@@ -729,7 +736,7 @@ static int ftrace_update_code(struct module *mod) | |||
729 | return -1; | 736 | return -1; |
730 | 737 | ||
731 | p = ftrace_new_addrs; | 738 | p = ftrace_new_addrs; |
732 | ftrace_new_addrs = (struct dyn_ftrace *)p->flags; | 739 | ftrace_new_addrs = p->newlist; |
733 | p->flags = 0L; | 740 | p->flags = 0L; |
734 | 741 | ||
735 | /* convert record (i.e, patch mcount-call with NOP) */ | 742 | /* convert record (i.e, patch mcount-call with NOP) */ |
@@ -2262,7 +2269,7 @@ ftrace_pid_read(struct file *file, char __user *ubuf, | |||
2262 | if (ftrace_pid_trace == ftrace_swapper_pid) | 2269 | if (ftrace_pid_trace == ftrace_swapper_pid) |
2263 | r = sprintf(buf, "swapper tasks\n"); | 2270 | r = sprintf(buf, "swapper tasks\n"); |
2264 | else if (ftrace_pid_trace) | 2271 | else if (ftrace_pid_trace) |
2265 | r = sprintf(buf, "%u\n", pid_nr(ftrace_pid_trace)); | 2272 | r = sprintf(buf, "%u\n", pid_vnr(ftrace_pid_trace)); |
2266 | else | 2273 | else |
2267 | r = sprintf(buf, "no pid\n"); | 2274 | r = sprintf(buf, "no pid\n"); |
2268 | 2275 | ||
@@ -2590,6 +2597,38 @@ free: | |||
2590 | return ret; | 2597 | return ret; |
2591 | } | 2598 | } |
2592 | 2599 | ||
2600 | static void | ||
2601 | ftrace_graph_probe_sched_switch(struct rq *__rq, struct task_struct *prev, | ||
2602 | struct task_struct *next) | ||
2603 | { | ||
2604 | unsigned long long timestamp; | ||
2605 | int index; | ||
2606 | |||
2607 | /* | ||
2608 | * Does the user want to count the time a function was asleep. | ||
2609 | * If so, do not update the time stamps. | ||
2610 | */ | ||
2611 | if (trace_flags & TRACE_ITER_SLEEP_TIME) | ||
2612 | return; | ||
2613 | |||
2614 | timestamp = trace_clock_local(); | ||
2615 | |||
2616 | prev->ftrace_timestamp = timestamp; | ||
2617 | |||
2618 | /* only process tasks that we timestamped */ | ||
2619 | if (!next->ftrace_timestamp) | ||
2620 | return; | ||
2621 | |||
2622 | /* | ||
2623 | * Update all the counters in next to make up for the | ||
2624 | * time next was sleeping. | ||
2625 | */ | ||
2626 | timestamp -= next->ftrace_timestamp; | ||
2627 | |||
2628 | for (index = next->curr_ret_stack; index >= 0; index--) | ||
2629 | next->ret_stack[index].calltime += timestamp; | ||
2630 | } | ||
2631 | |||
2593 | /* Allocate a return stack for each task */ | 2632 | /* Allocate a return stack for each task */ |
2594 | static int start_graph_tracing(void) | 2633 | static int start_graph_tracing(void) |
2595 | { | 2634 | { |
@@ -2611,6 +2650,13 @@ static int start_graph_tracing(void) | |||
2611 | ret = alloc_retstack_tasklist(ret_stack_list); | 2650 | ret = alloc_retstack_tasklist(ret_stack_list); |
2612 | } while (ret == -EAGAIN); | 2651 | } while (ret == -EAGAIN); |
2613 | 2652 | ||
2653 | if (!ret) { | ||
2654 | ret = register_trace_sched_switch(ftrace_graph_probe_sched_switch); | ||
2655 | if (ret) | ||
2656 | pr_info("ftrace_graph: Couldn't activate tracepoint" | ||
2657 | " probe to kernel_sched_switch\n"); | ||
2658 | } | ||
2659 | |||
2614 | kfree(ret_stack_list); | 2660 | kfree(ret_stack_list); |
2615 | return ret; | 2661 | return ret; |
2616 | } | 2662 | } |
@@ -2643,6 +2689,12 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc, | |||
2643 | 2689 | ||
2644 | mutex_lock(&ftrace_lock); | 2690 | mutex_lock(&ftrace_lock); |
2645 | 2691 | ||
2692 | /* we currently allow only one tracer registered at a time */ | ||
2693 | if (atomic_read(&ftrace_graph_active)) { | ||
2694 | ret = -EBUSY; | ||
2695 | goto out; | ||
2696 | } | ||
2697 | |||
2646 | ftrace_suspend_notifier.notifier_call = ftrace_suspend_notifier_call; | 2698 | ftrace_suspend_notifier.notifier_call = ftrace_suspend_notifier_call; |
2647 | register_pm_notifier(&ftrace_suspend_notifier); | 2699 | register_pm_notifier(&ftrace_suspend_notifier); |
2648 | 2700 | ||
@@ -2668,6 +2720,7 @@ void unregister_ftrace_graph(void) | |||
2668 | mutex_lock(&ftrace_lock); | 2720 | mutex_lock(&ftrace_lock); |
2669 | 2721 | ||
2670 | atomic_dec(&ftrace_graph_active); | 2722 | atomic_dec(&ftrace_graph_active); |
2723 | unregister_trace_sched_switch(ftrace_graph_probe_sched_switch); | ||
2671 | ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; | 2724 | ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; |
2672 | ftrace_graph_entry = ftrace_graph_entry_stub; | 2725 | ftrace_graph_entry = ftrace_graph_entry_stub; |
2673 | ftrace_shutdown(FTRACE_STOP_FUNC_RET); | 2726 | ftrace_shutdown(FTRACE_STOP_FUNC_RET); |
@@ -2688,6 +2741,7 @@ void ftrace_graph_init_task(struct task_struct *t) | |||
2688 | t->curr_ret_stack = -1; | 2741 | t->curr_ret_stack = -1; |
2689 | atomic_set(&t->tracing_graph_pause, 0); | 2742 | atomic_set(&t->tracing_graph_pause, 0); |
2690 | atomic_set(&t->trace_overrun, 0); | 2743 | atomic_set(&t->trace_overrun, 0); |
2744 | t->ftrace_timestamp = 0; | ||
2691 | } else | 2745 | } else |
2692 | t->ret_stack = NULL; | 2746 | t->ret_stack = NULL; |
2693 | } | 2747 | } |