diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-21 12:05:47 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-21 12:05:47 -0400 |
| commit | bd4c3a3441144cd46d1f544046523724c5bc6e94 (patch) | |
| tree | 8b5c67249a7a163caf3f88cbcb9df5236fcc3b93 /kernel/trace | |
| parent | b3727c24da69971503a4ca98b3b877753c6a4393 (diff) | |
| parent | 583a22e7c154dc0a3938db522696b4bc7f098f59 (diff) | |
Merge branch 'tracing-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'tracing-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
kernel/profile.c: Switch /proc/irq/prof_cpu_mask to seq_file
tracing: Export trace_profile_buf symbols
tracing/events: use list_for_entry_continue
tracing: remove max_tracer_type_len
function-graph: use ftrace_graph_funcs directly
tracing: Remove markers
tracing: Allocate the ftrace event profile buffer dynamically
tracing: Factorize the events profile accounting
Diffstat (limited to 'kernel/trace')
| -rw-r--r-- | kernel/trace/ftrace.c | 23 | ||||
| -rw-r--r-- | kernel/trace/trace.c | 49 | ||||
| -rw-r--r-- | kernel/trace/trace_event_profile.c | 82 | ||||
| -rw-r--r-- | kernel/trace/trace_events.c | 49 | ||||
| -rw-r--r-- | kernel/trace/trace_printk.c | 1 | ||||
| -rw-r--r-- | kernel/trace/trace_syscalls.c | 97 |
6 files changed, 193 insertions, 108 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index cc615f84751b..c71e91bf7372 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -2414,11 +2414,9 @@ unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; | |||
| 2414 | static void * | 2414 | static void * |
| 2415 | __g_next(struct seq_file *m, loff_t *pos) | 2415 | __g_next(struct seq_file *m, loff_t *pos) |
| 2416 | { | 2416 | { |
| 2417 | unsigned long *array = m->private; | ||
| 2418 | |||
| 2419 | if (*pos >= ftrace_graph_count) | 2417 | if (*pos >= ftrace_graph_count) |
| 2420 | return NULL; | 2418 | return NULL; |
| 2421 | return &array[*pos]; | 2419 | return &ftrace_graph_funcs[*pos]; |
| 2422 | } | 2420 | } |
| 2423 | 2421 | ||
| 2424 | static void * | 2422 | static void * |
| @@ -2482,16 +2480,10 @@ ftrace_graph_open(struct inode *inode, struct file *file) | |||
| 2482 | ftrace_graph_count = 0; | 2480 | ftrace_graph_count = 0; |
| 2483 | memset(ftrace_graph_funcs, 0, sizeof(ftrace_graph_funcs)); | 2481 | memset(ftrace_graph_funcs, 0, sizeof(ftrace_graph_funcs)); |
| 2484 | } | 2482 | } |
| 2483 | mutex_unlock(&graph_lock); | ||
| 2485 | 2484 | ||
| 2486 | if (file->f_mode & FMODE_READ) { | 2485 | if (file->f_mode & FMODE_READ) |
| 2487 | ret = seq_open(file, &ftrace_graph_seq_ops); | 2486 | ret = seq_open(file, &ftrace_graph_seq_ops); |
| 2488 | if (!ret) { | ||
| 2489 | struct seq_file *m = file->private_data; | ||
| 2490 | m->private = ftrace_graph_funcs; | ||
| 2491 | } | ||
| 2492 | } else | ||
| 2493 | file->private_data = ftrace_graph_funcs; | ||
| 2494 | mutex_unlock(&graph_lock); | ||
| 2495 | 2487 | ||
| 2496 | return ret; | 2488 | return ret; |
| 2497 | } | 2489 | } |
| @@ -2560,7 +2552,6 @@ ftrace_graph_write(struct file *file, const char __user *ubuf, | |||
| 2560 | size_t cnt, loff_t *ppos) | 2552 | size_t cnt, loff_t *ppos) |
| 2561 | { | 2553 | { |
| 2562 | struct trace_parser parser; | 2554 | struct trace_parser parser; |
| 2563 | unsigned long *array; | ||
| 2564 | size_t read = 0; | 2555 | size_t read = 0; |
| 2565 | ssize_t ret; | 2556 | ssize_t ret; |
| 2566 | 2557 | ||
| @@ -2574,12 +2565,6 @@ ftrace_graph_write(struct file *file, const char __user *ubuf, | |||
| 2574 | goto out; | 2565 | goto out; |
| 2575 | } | 2566 | } |
| 2576 | 2567 | ||
| 2577 | if (file->f_mode & FMODE_READ) { | ||
| 2578 | struct seq_file *m = file->private_data; | ||
| 2579 | array = m->private; | ||
| 2580 | } else | ||
| 2581 | array = file->private_data; | ||
| 2582 | |||
| 2583 | if (trace_parser_get_init(&parser, FTRACE_BUFF_MAX)) { | 2568 | if (trace_parser_get_init(&parser, FTRACE_BUFF_MAX)) { |
| 2584 | ret = -ENOMEM; | 2569 | ret = -ENOMEM; |
| 2585 | goto out; | 2570 | goto out; |
| @@ -2591,7 +2576,7 @@ ftrace_graph_write(struct file *file, const char __user *ubuf, | |||
| 2591 | parser.buffer[parser.idx] = 0; | 2576 | parser.buffer[parser.idx] = 0; |
| 2592 | 2577 | ||
| 2593 | /* we allow only one expression at a time */ | 2578 | /* we allow only one expression at a time */ |
| 2594 | ret = ftrace_set_func(array, &ftrace_graph_count, | 2579 | ret = ftrace_set_func(ftrace_graph_funcs, &ftrace_graph_count, |
| 2595 | parser.buffer); | 2580 | parser.buffer); |
| 2596 | if (ret) | 2581 | if (ret) |
| 2597 | goto out; | 2582 | goto out; |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index fd52a19dd172..861308072d28 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
| @@ -125,13 +125,13 @@ int ftrace_dump_on_oops; | |||
| 125 | 125 | ||
| 126 | static int tracing_set_tracer(const char *buf); | 126 | static int tracing_set_tracer(const char *buf); |
| 127 | 127 | ||
| 128 | #define BOOTUP_TRACER_SIZE 100 | 128 | #define MAX_TRACER_SIZE 100 |
| 129 | static char bootup_tracer_buf[BOOTUP_TRACER_SIZE] __initdata; | 129 | static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata; |
| 130 | static char *default_bootup_tracer; | 130 | static char *default_bootup_tracer; |
| 131 | 131 | ||
| 132 | static int __init set_ftrace(char *str) | 132 | static int __init set_ftrace(char *str) |
| 133 | { | 133 | { |
| 134 | strncpy(bootup_tracer_buf, str, BOOTUP_TRACER_SIZE); | 134 | strncpy(bootup_tracer_buf, str, MAX_TRACER_SIZE); |
| 135 | default_bootup_tracer = bootup_tracer_buf; | 135 | default_bootup_tracer = bootup_tracer_buf; |
| 136 | /* We are using ftrace early, expand it */ | 136 | /* We are using ftrace early, expand it */ |
| 137 | ring_buffer_expanded = 1; | 137 | ring_buffer_expanded = 1; |
| @@ -242,13 +242,6 @@ static struct tracer *trace_types __read_mostly; | |||
| 242 | static struct tracer *current_trace __read_mostly; | 242 | static struct tracer *current_trace __read_mostly; |
| 243 | 243 | ||
| 244 | /* | 244 | /* |
| 245 | * max_tracer_type_len is used to simplify the allocating of | ||
| 246 | * buffers to read userspace tracer names. We keep track of | ||
| 247 | * the longest tracer name registered. | ||
| 248 | */ | ||
| 249 | static int max_tracer_type_len; | ||
| 250 | |||
| 251 | /* | ||
| 252 | * trace_types_lock is used to protect the trace_types list. | 245 | * trace_types_lock is used to protect the trace_types list. |
| 253 | * This lock is also used to keep user access serialized. | 246 | * This lock is also used to keep user access serialized. |
| 254 | * Accesses from userspace will grab this lock while userspace | 247 | * Accesses from userspace will grab this lock while userspace |
| @@ -619,7 +612,6 @@ __releases(kernel_lock) | |||
| 619 | __acquires(kernel_lock) | 612 | __acquires(kernel_lock) |
| 620 | { | 613 | { |
| 621 | struct tracer *t; | 614 | struct tracer *t; |
| 622 | int len; | ||
| 623 | int ret = 0; | 615 | int ret = 0; |
| 624 | 616 | ||
| 625 | if (!type->name) { | 617 | if (!type->name) { |
| @@ -627,6 +619,11 @@ __acquires(kernel_lock) | |||
| 627 | return -1; | 619 | return -1; |
| 628 | } | 620 | } |
| 629 | 621 | ||
| 622 | if (strlen(type->name) > MAX_TRACER_SIZE) { | ||
| 623 | pr_info("Tracer has a name longer than %d\n", MAX_TRACER_SIZE); | ||
| 624 | return -1; | ||
| 625 | } | ||
| 626 | |||
| 630 | /* | 627 | /* |
| 631 | * When this gets called we hold the BKL which means that | 628 | * When this gets called we hold the BKL which means that |
| 632 | * preemption is disabled. Various trace selftests however | 629 | * preemption is disabled. Various trace selftests however |
| @@ -641,7 +638,7 @@ __acquires(kernel_lock) | |||
| 641 | for (t = trace_types; t; t = t->next) { | 638 | for (t = trace_types; t; t = t->next) { |
| 642 | if (strcmp(type->name, t->name) == 0) { | 639 | if (strcmp(type->name, t->name) == 0) { |
| 643 | /* already found */ | 640 | /* already found */ |
| 644 | pr_info("Trace %s already registered\n", | 641 | pr_info("Tracer %s already registered\n", |
| 645 | type->name); | 642 | type->name); |
| 646 | ret = -1; | 643 | ret = -1; |
| 647 | goto out; | 644 | goto out; |
| @@ -692,9 +689,6 @@ __acquires(kernel_lock) | |||
| 692 | 689 | ||
| 693 | type->next = trace_types; | 690 | type->next = trace_types; |
| 694 | trace_types = type; | 691 | trace_types = type; |
| 695 | len = strlen(type->name); | ||
| 696 | if (len > max_tracer_type_len) | ||
| 697 | max_tracer_type_len = len; | ||
| 698 | 692 | ||
| 699 | out: | 693 | out: |
| 700 | tracing_selftest_running = false; | 694 | tracing_selftest_running = false; |
| @@ -703,7 +697,7 @@ __acquires(kernel_lock) | |||
| 703 | if (ret || !default_bootup_tracer) | 697 | if (ret || !default_bootup_tracer) |
| 704 | goto out_unlock; | 698 | goto out_unlock; |
| 705 | 699 | ||
| 706 | if (strncmp(default_bootup_tracer, type->name, BOOTUP_TRACER_SIZE)) | 700 | if (strncmp(default_bootup_tracer, type->name, MAX_TRACER_SIZE)) |
| 707 | goto out_unlock; | 701 | goto out_unlock; |
| 708 | 702 | ||
| 709 | printk(KERN_INFO "Starting tracer '%s'\n", type->name); | 703 | printk(KERN_INFO "Starting tracer '%s'\n", type->name); |
| @@ -725,14 +719,13 @@ __acquires(kernel_lock) | |||
| 725 | void unregister_tracer(struct tracer *type) | 719 | void unregister_tracer(struct tracer *type) |
| 726 | { | 720 | { |
| 727 | struct tracer **t; | 721 | struct tracer **t; |
| 728 | int len; | ||
| 729 | 722 | ||
| 730 | mutex_lock(&trace_types_lock); | 723 | mutex_lock(&trace_types_lock); |
| 731 | for (t = &trace_types; *t; t = &(*t)->next) { | 724 | for (t = &trace_types; *t; t = &(*t)->next) { |
| 732 | if (*t == type) | 725 | if (*t == type) |
| 733 | goto found; | 726 | goto found; |
| 734 | } | 727 | } |
| 735 | pr_info("Trace %s not registered\n", type->name); | 728 | pr_info("Tracer %s not registered\n", type->name); |
| 736 | goto out; | 729 | goto out; |
| 737 | 730 | ||
| 738 | found: | 731 | found: |
| @@ -745,17 +738,7 @@ void unregister_tracer(struct tracer *type) | |||
| 745 | current_trace->stop(&global_trace); | 738 | current_trace->stop(&global_trace); |
| 746 | current_trace = &nop_trace; | 739 | current_trace = &nop_trace; |
| 747 | } | 740 | } |
| 748 | 741 | out: | |
| 749 | if (strlen(type->name) != max_tracer_type_len) | ||
| 750 | goto out; | ||
| 751 | |||
| 752 | max_tracer_type_len = 0; | ||
| 753 | for (t = &trace_types; *t; t = &(*t)->next) { | ||
| 754 | len = strlen((*t)->name); | ||
| 755 | if (len > max_tracer_type_len) | ||
| 756 | max_tracer_type_len = len; | ||
| 757 | } | ||
| 758 | out: | ||
| 759 | mutex_unlock(&trace_types_lock); | 742 | mutex_unlock(&trace_types_lock); |
| 760 | } | 743 | } |
| 761 | 744 | ||
| @@ -2604,7 +2587,7 @@ static ssize_t | |||
| 2604 | tracing_set_trace_read(struct file *filp, char __user *ubuf, | 2587 | tracing_set_trace_read(struct file *filp, char __user *ubuf, |
| 2605 | size_t cnt, loff_t *ppos) | 2588 | size_t cnt, loff_t *ppos) |
| 2606 | { | 2589 | { |
| 2607 | char buf[max_tracer_type_len+2]; | 2590 | char buf[MAX_TRACER_SIZE+2]; |
| 2608 | int r; | 2591 | int r; |
| 2609 | 2592 | ||
| 2610 | mutex_lock(&trace_types_lock); | 2593 | mutex_lock(&trace_types_lock); |
| @@ -2754,15 +2737,15 @@ static ssize_t | |||
| 2754 | tracing_set_trace_write(struct file *filp, const char __user *ubuf, | 2737 | tracing_set_trace_write(struct file *filp, const char __user *ubuf, |
| 2755 | size_t cnt, loff_t *ppos) | 2738 | size_t cnt, loff_t *ppos) |
| 2756 | { | 2739 | { |
| 2757 | char buf[max_tracer_type_len+1]; | 2740 | char buf[MAX_TRACER_SIZE+1]; |
| 2758 | int i; | 2741 | int i; |
| 2759 | size_t ret; | 2742 | size_t ret; |
| 2760 | int err; | 2743 | int err; |
| 2761 | 2744 | ||
| 2762 | ret = cnt; | 2745 | ret = cnt; |
| 2763 | 2746 | ||
| 2764 | if (cnt > max_tracer_type_len) | 2747 | if (cnt > MAX_TRACER_SIZE) |
| 2765 | cnt = max_tracer_type_len; | 2748 | cnt = MAX_TRACER_SIZE; |
| 2766 | 2749 | ||
| 2767 | if (copy_from_user(&buf, ubuf, cnt)) | 2750 | if (copy_from_user(&buf, ubuf, cnt)) |
| 2768 | return -EFAULT; | 2751 | return -EFAULT; |
diff --git a/kernel/trace/trace_event_profile.c b/kernel/trace/trace_event_profile.c index 55a25c933d15..dd44b8768867 100644 --- a/kernel/trace/trace_event_profile.c +++ b/kernel/trace/trace_event_profile.c | |||
| @@ -8,6 +8,57 @@ | |||
| 8 | #include <linux/module.h> | 8 | #include <linux/module.h> |
| 9 | #include "trace.h" | 9 | #include "trace.h" |
| 10 | 10 | ||
| 11 | /* | ||
| 12 | * We can't use a size but a type in alloc_percpu() | ||
| 13 | * So let's create a dummy type that matches the desired size | ||
| 14 | */ | ||
| 15 | typedef struct {char buf[FTRACE_MAX_PROFILE_SIZE];} profile_buf_t; | ||
| 16 | |||
| 17 | char *trace_profile_buf; | ||
| 18 | EXPORT_SYMBOL_GPL(trace_profile_buf); | ||
| 19 | |||
| 20 | char *trace_profile_buf_nmi; | ||
| 21 | EXPORT_SYMBOL_GPL(trace_profile_buf_nmi); | ||
| 22 | |||
| 23 | /* Count the events in use (per event id, not per instance) */ | ||
| 24 | static int total_profile_count; | ||
| 25 | |||
| 26 | static int ftrace_profile_enable_event(struct ftrace_event_call *event) | ||
| 27 | { | ||
| 28 | char *buf; | ||
| 29 | int ret = -ENOMEM; | ||
| 30 | |||
| 31 | if (atomic_inc_return(&event->profile_count)) | ||
| 32 | return 0; | ||
| 33 | |||
| 34 | if (!total_profile_count++) { | ||
| 35 | buf = (char *)alloc_percpu(profile_buf_t); | ||
| 36 | if (!buf) | ||
| 37 | goto fail_buf; | ||
| 38 | |||
| 39 | rcu_assign_pointer(trace_profile_buf, buf); | ||
| 40 | |||
| 41 | buf = (char *)alloc_percpu(profile_buf_t); | ||
| 42 | if (!buf) | ||
| 43 | goto fail_buf_nmi; | ||
| 44 | |||
| 45 | rcu_assign_pointer(trace_profile_buf_nmi, buf); | ||
| 46 | } | ||
| 47 | |||
| 48 | ret = event->profile_enable(); | ||
| 49 | if (!ret) | ||
| 50 | return 0; | ||
| 51 | |||
| 52 | kfree(trace_profile_buf_nmi); | ||
| 53 | fail_buf_nmi: | ||
| 54 | kfree(trace_profile_buf); | ||
| 55 | fail_buf: | ||
| 56 | total_profile_count--; | ||
| 57 | atomic_dec(&event->profile_count); | ||
| 58 | |||
| 59 | return ret; | ||
| 60 | } | ||
| 61 | |||
| 11 | int ftrace_profile_enable(int event_id) | 62 | int ftrace_profile_enable(int event_id) |
| 12 | { | 63 | { |
| 13 | struct ftrace_event_call *event; | 64 | struct ftrace_event_call *event; |
| @@ -17,7 +68,7 @@ int ftrace_profile_enable(int event_id) | |||
| 17 | list_for_each_entry(event, &ftrace_events, list) { | 68 | list_for_each_entry(event, &ftrace_events, list) { |
| 18 | if (event->id == event_id && event->profile_enable && | 69 | if (event->id == event_id && event->profile_enable && |
| 19 | try_module_get(event->mod)) { | 70 | try_module_get(event->mod)) { |
| 20 | ret = event->profile_enable(event); | 71 | ret = ftrace_profile_enable_event(event); |
| 21 | break; | 72 | break; |
| 22 | } | 73 | } |
| 23 | } | 74 | } |
| @@ -26,6 +77,33 @@ int ftrace_profile_enable(int event_id) | |||
| 26 | return ret; | 77 | return ret; |
| 27 | } | 78 | } |
| 28 | 79 | ||
| 80 | static void ftrace_profile_disable_event(struct ftrace_event_call *event) | ||
| 81 | { | ||
| 82 | char *buf, *nmi_buf; | ||
| 83 | |||
| 84 | if (!atomic_add_negative(-1, &event->profile_count)) | ||
| 85 | return; | ||
| 86 | |||
| 87 | event->profile_disable(); | ||
| 88 | |||
| 89 | if (!--total_profile_count) { | ||
| 90 | buf = trace_profile_buf; | ||
| 91 | rcu_assign_pointer(trace_profile_buf, NULL); | ||
| 92 | |||
| 93 | nmi_buf = trace_profile_buf_nmi; | ||
| 94 | rcu_assign_pointer(trace_profile_buf_nmi, NULL); | ||
| 95 | |||
| 96 | /* | ||
| 97 | * Ensure every events in profiling have finished before | ||
| 98 | * releasing the buffers | ||
| 99 | */ | ||
| 100 | synchronize_sched(); | ||
| 101 | |||
| 102 | free_percpu(buf); | ||
| 103 | free_percpu(nmi_buf); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 29 | void ftrace_profile_disable(int event_id) | 107 | void ftrace_profile_disable(int event_id) |
| 30 | { | 108 | { |
| 31 | struct ftrace_event_call *event; | 109 | struct ftrace_event_call *event; |
| @@ -33,7 +111,7 @@ void ftrace_profile_disable(int event_id) | |||
| 33 | mutex_lock(&event_mutex); | 111 | mutex_lock(&event_mutex); |
| 34 | list_for_each_entry(event, &ftrace_events, list) { | 112 | list_for_each_entry(event, &ftrace_events, list) { |
| 35 | if (event->id == event_id) { | 113 | if (event->id == event_id) { |
| 36 | event->profile_disable(event); | 114 | ftrace_profile_disable_event(event); |
| 37 | module_put(event->mod); | 115 | module_put(event->mod); |
| 38 | break; | 116 | break; |
| 39 | } | 117 | } |
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 56c260b83a9c..6f03c8a1105e 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
| @@ -271,42 +271,32 @@ ftrace_event_write(struct file *file, const char __user *ubuf, | |||
| 271 | static void * | 271 | static void * |
| 272 | t_next(struct seq_file *m, void *v, loff_t *pos) | 272 | t_next(struct seq_file *m, void *v, loff_t *pos) |
| 273 | { | 273 | { |
| 274 | struct list_head *list = m->private; | 274 | struct ftrace_event_call *call = v; |
| 275 | struct ftrace_event_call *call; | ||
| 276 | 275 | ||
| 277 | (*pos)++; | 276 | (*pos)++; |
| 278 | 277 | ||
| 279 | for (;;) { | 278 | list_for_each_entry_continue(call, &ftrace_events, list) { |
| 280 | if (list == &ftrace_events) | ||
| 281 | return NULL; | ||
| 282 | |||
| 283 | call = list_entry(list, struct ftrace_event_call, list); | ||
| 284 | |||
| 285 | /* | 279 | /* |
| 286 | * The ftrace subsystem is for showing formats only. | 280 | * The ftrace subsystem is for showing formats only. |
| 287 | * They can not be enabled or disabled via the event files. | 281 | * They can not be enabled or disabled via the event files. |
| 288 | */ | 282 | */ |
| 289 | if (call->regfunc) | 283 | if (call->regfunc) |
| 290 | break; | 284 | return call; |
| 291 | |||
| 292 | list = list->next; | ||
| 293 | } | 285 | } |
| 294 | 286 | ||
| 295 | m->private = list->next; | 287 | return NULL; |
| 296 | |||
| 297 | return call; | ||
| 298 | } | 288 | } |
| 299 | 289 | ||
| 300 | static void *t_start(struct seq_file *m, loff_t *pos) | 290 | static void *t_start(struct seq_file *m, loff_t *pos) |
| 301 | { | 291 | { |
| 302 | struct ftrace_event_call *call = NULL; | 292 | struct ftrace_event_call *call; |
| 303 | loff_t l; | 293 | loff_t l; |
| 304 | 294 | ||
| 305 | mutex_lock(&event_mutex); | 295 | mutex_lock(&event_mutex); |
| 306 | 296 | ||
| 307 | m->private = ftrace_events.next; | 297 | call = list_entry(&ftrace_events, struct ftrace_event_call, list); |
| 308 | for (l = 0; l <= *pos; ) { | 298 | for (l = 0; l <= *pos; ) { |
| 309 | call = t_next(m, NULL, &l); | 299 | call = t_next(m, call, &l); |
| 310 | if (!call) | 300 | if (!call) |
| 311 | break; | 301 | break; |
| 312 | } | 302 | } |
| @@ -316,37 +306,28 @@ static void *t_start(struct seq_file *m, loff_t *pos) | |||
| 316 | static void * | 306 | static void * |
| 317 | s_next(struct seq_file *m, void *v, loff_t *pos) | 307 | s_next(struct seq_file *m, void *v, loff_t *pos) |
| 318 | { | 308 | { |
| 319 | struct list_head *list = m->private; | 309 | struct ftrace_event_call *call = v; |
| 320 | struct ftrace_event_call *call; | ||
| 321 | 310 | ||
| 322 | (*pos)++; | 311 | (*pos)++; |
| 323 | 312 | ||
| 324 | retry: | 313 | list_for_each_entry_continue(call, &ftrace_events, list) { |
| 325 | if (list == &ftrace_events) | 314 | if (call->enabled) |
| 326 | return NULL; | 315 | return call; |
| 327 | |||
| 328 | call = list_entry(list, struct ftrace_event_call, list); | ||
| 329 | |||
| 330 | if (!call->enabled) { | ||
| 331 | list = list->next; | ||
| 332 | goto retry; | ||
| 333 | } | 316 | } |
| 334 | 317 | ||
| 335 | m->private = list->next; | 318 | return NULL; |
| 336 | |||
| 337 | return call; | ||
| 338 | } | 319 | } |
| 339 | 320 | ||
| 340 | static void *s_start(struct seq_file *m, loff_t *pos) | 321 | static void *s_start(struct seq_file *m, loff_t *pos) |
| 341 | { | 322 | { |
| 342 | struct ftrace_event_call *call = NULL; | 323 | struct ftrace_event_call *call; |
| 343 | loff_t l; | 324 | loff_t l; |
| 344 | 325 | ||
| 345 | mutex_lock(&event_mutex); | 326 | mutex_lock(&event_mutex); |
| 346 | 327 | ||
| 347 | m->private = ftrace_events.next; | 328 | call = list_entry(&ftrace_events, struct ftrace_event_call, list); |
| 348 | for (l = 0; l <= *pos; ) { | 329 | for (l = 0; l <= *pos; ) { |
| 349 | call = s_next(m, NULL, &l); | 330 | call = s_next(m, call, &l); |
| 350 | if (!call) | 331 | if (!call) |
| 351 | break; | 332 | break; |
| 352 | } | 333 | } |
diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c index 687699d365ae..2547d8813cf0 100644 --- a/kernel/trace/trace_printk.c +++ b/kernel/trace/trace_printk.c | |||
| @@ -11,7 +11,6 @@ | |||
| 11 | #include <linux/ftrace.h> | 11 | #include <linux/ftrace.h> |
| 12 | #include <linux/string.h> | 12 | #include <linux/string.h> |
| 13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
| 14 | #include <linux/marker.h> | ||
| 15 | #include <linux/mutex.h> | 14 | #include <linux/mutex.h> |
| 16 | #include <linux/ctype.h> | 15 | #include <linux/ctype.h> |
| 17 | #include <linux/list.h> | 16 | #include <linux/list.h> |
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 8712ce3c6a0e..7a3550cf2597 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c | |||
| @@ -384,10 +384,13 @@ static int sys_prof_refcount_exit; | |||
| 384 | 384 | ||
| 385 | static void prof_syscall_enter(struct pt_regs *regs, long id) | 385 | static void prof_syscall_enter(struct pt_regs *regs, long id) |
| 386 | { | 386 | { |
| 387 | struct syscall_trace_enter *rec; | ||
| 388 | struct syscall_metadata *sys_data; | 387 | struct syscall_metadata *sys_data; |
| 388 | struct syscall_trace_enter *rec; | ||
| 389 | unsigned long flags; | ||
| 390 | char *raw_data; | ||
| 389 | int syscall_nr; | 391 | int syscall_nr; |
| 390 | int size; | 392 | int size; |
| 393 | int cpu; | ||
| 391 | 394 | ||
| 392 | syscall_nr = syscall_get_nr(current, regs); | 395 | syscall_nr = syscall_get_nr(current, regs); |
| 393 | if (!test_bit(syscall_nr, enabled_prof_enter_syscalls)) | 396 | if (!test_bit(syscall_nr, enabled_prof_enter_syscalls)) |
| @@ -402,20 +405,38 @@ static void prof_syscall_enter(struct pt_regs *regs, long id) | |||
| 402 | size = ALIGN(size + sizeof(u32), sizeof(u64)); | 405 | size = ALIGN(size + sizeof(u32), sizeof(u64)); |
| 403 | size -= sizeof(u32); | 406 | size -= sizeof(u32); |
| 404 | 407 | ||
| 405 | do { | 408 | if (WARN_ONCE(size > FTRACE_MAX_PROFILE_SIZE, |
| 406 | char raw_data[size]; | 409 | "profile buffer not large enough")) |
| 410 | return; | ||
| 411 | |||
| 412 | /* Protect the per cpu buffer, begin the rcu read side */ | ||
| 413 | local_irq_save(flags); | ||
| 407 | 414 | ||
| 408 | /* zero the dead bytes from align to not leak stack to user */ | 415 | cpu = smp_processor_id(); |
| 409 | *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; | 416 | |
| 417 | if (in_nmi()) | ||
| 418 | raw_data = rcu_dereference(trace_profile_buf_nmi); | ||
| 419 | else | ||
| 420 | raw_data = rcu_dereference(trace_profile_buf); | ||
| 421 | |||
| 422 | if (!raw_data) | ||
| 423 | goto end; | ||
| 410 | 424 | ||
| 411 | rec = (struct syscall_trace_enter *) raw_data; | 425 | raw_data = per_cpu_ptr(raw_data, cpu); |
| 412 | tracing_generic_entry_update(&rec->ent, 0, 0); | 426 | |
| 413 | rec->ent.type = sys_data->enter_id; | 427 | /* zero the dead bytes from align to not leak stack to user */ |
| 414 | rec->nr = syscall_nr; | 428 | *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; |
| 415 | syscall_get_arguments(current, regs, 0, sys_data->nb_args, | 429 | |
| 416 | (unsigned long *)&rec->args); | 430 | rec = (struct syscall_trace_enter *) raw_data; |
| 417 | perf_tpcounter_event(sys_data->enter_id, 0, 1, rec, size); | 431 | tracing_generic_entry_update(&rec->ent, 0, 0); |
| 418 | } while(0); | 432 | rec->ent.type = sys_data->enter_id; |
| 433 | rec->nr = syscall_nr; | ||
| 434 | syscall_get_arguments(current, regs, 0, sys_data->nb_args, | ||
| 435 | (unsigned long *)&rec->args); | ||
| 436 | perf_tpcounter_event(sys_data->enter_id, 0, 1, rec, size); | ||
| 437 | |||
| 438 | end: | ||
| 439 | local_irq_restore(flags); | ||
| 419 | } | 440 | } |
| 420 | 441 | ||
| 421 | int reg_prof_syscall_enter(char *name) | 442 | int reg_prof_syscall_enter(char *name) |
| @@ -460,8 +481,12 @@ void unreg_prof_syscall_enter(char *name) | |||
| 460 | static void prof_syscall_exit(struct pt_regs *regs, long ret) | 481 | static void prof_syscall_exit(struct pt_regs *regs, long ret) |
| 461 | { | 482 | { |
| 462 | struct syscall_metadata *sys_data; | 483 | struct syscall_metadata *sys_data; |
| 463 | struct syscall_trace_exit rec; | 484 | struct syscall_trace_exit *rec; |
| 485 | unsigned long flags; | ||
| 464 | int syscall_nr; | 486 | int syscall_nr; |
| 487 | char *raw_data; | ||
| 488 | int size; | ||
| 489 | int cpu; | ||
| 465 | 490 | ||
| 466 | syscall_nr = syscall_get_nr(current, regs); | 491 | syscall_nr = syscall_get_nr(current, regs); |
| 467 | if (!test_bit(syscall_nr, enabled_prof_exit_syscalls)) | 492 | if (!test_bit(syscall_nr, enabled_prof_exit_syscalls)) |
| @@ -471,12 +496,46 @@ static void prof_syscall_exit(struct pt_regs *regs, long ret) | |||
| 471 | if (!sys_data) | 496 | if (!sys_data) |
| 472 | return; | 497 | return; |
| 473 | 498 | ||
| 474 | tracing_generic_entry_update(&rec.ent, 0, 0); | 499 | /* We can probably do that at build time */ |
| 475 | rec.ent.type = sys_data->exit_id; | 500 | size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64)); |
| 476 | rec.nr = syscall_nr; | 501 | size -= sizeof(u32); |
| 477 | rec.ret = syscall_get_return_value(current, regs); | ||
| 478 | 502 | ||
| 479 | perf_tpcounter_event(sys_data->exit_id, 0, 1, &rec, sizeof(rec)); | 503 | /* |
| 504 | * Impossible, but be paranoid with the future | ||
| 505 | * How to put this check outside runtime? | ||
| 506 | */ | ||
| 507 | if (WARN_ONCE(size > FTRACE_MAX_PROFILE_SIZE, | ||
| 508 | "exit event has grown above profile buffer size")) | ||
| 509 | return; | ||
| 510 | |||
| 511 | /* Protect the per cpu buffer, begin the rcu read side */ | ||
| 512 | local_irq_save(flags); | ||
| 513 | cpu = smp_processor_id(); | ||
| 514 | |||
| 515 | if (in_nmi()) | ||
| 516 | raw_data = rcu_dereference(trace_profile_buf_nmi); | ||
| 517 | else | ||
| 518 | raw_data = rcu_dereference(trace_profile_buf); | ||
| 519 | |||
| 520 | if (!raw_data) | ||
| 521 | goto end; | ||
| 522 | |||
| 523 | raw_data = per_cpu_ptr(raw_data, cpu); | ||
| 524 | |||
| 525 | /* zero the dead bytes from align to not leak stack to user */ | ||
| 526 | *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; | ||
| 527 | |||
| 528 | rec = (struct syscall_trace_exit *)raw_data; | ||
| 529 | |||
| 530 | tracing_generic_entry_update(&rec->ent, 0, 0); | ||
| 531 | rec->ent.type = sys_data->exit_id; | ||
| 532 | rec->nr = syscall_nr; | ||
| 533 | rec->ret = syscall_get_return_value(current, regs); | ||
| 534 | |||
| 535 | perf_tpcounter_event(sys_data->exit_id, 0, 1, rec, size); | ||
| 536 | |||
| 537 | end: | ||
| 538 | local_irq_restore(flags); | ||
| 480 | } | 539 | } |
| 481 | 540 | ||
| 482 | int reg_prof_syscall_exit(char *name) | 541 | int reg_prof_syscall_exit(char *name) |
