diff options
author | Alexei Starovoitov <ast@fb.com> | 2016-04-06 21:43:24 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-04-07 21:04:26 -0400 |
commit | 1e1dcd93b468901e114f279c94a0b356adc5e7cd (patch) | |
tree | b0bee9a35251caacc44d9a3ffbbae90e8afb237d | |
parent | e93735be6a1898dd9f8de8f55254cc76309777ce (diff) |
perf: split perf_trace_buf_prepare into alloc and update parts
split allows to move expensive update of 'struct trace_entry' to later phase.
Repurpose unused 1st argument of perf_tp_event() to indicate event type.
While splitting use temp variable 'rctx' instead of '*rctx' to avoid
unnecessary loads done by the compiler due to -fno-strict-aliasing
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/perf_event.h | 2 | ||||
-rw-r--r-- | include/linux/trace_events.h | 8 | ||||
-rw-r--r-- | include/trace/perf.h | 8 | ||||
-rw-r--r-- | kernel/events/core.c | 6 | ||||
-rw-r--r-- | kernel/trace/trace_event_perf.c | 39 | ||||
-rw-r--r-- | kernel/trace/trace_kprobe.c | 10 | ||||
-rw-r--r-- | kernel/trace/trace_syscalls.c | 13 | ||||
-rw-r--r-- | kernel/trace/trace_uprobe.c | 5 |
8 files changed, 49 insertions, 42 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index e89f7199c223..eb41b535ef38 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -1016,7 +1016,7 @@ static inline bool perf_paranoid_kernel(void) | |||
1016 | } | 1016 | } |
1017 | 1017 | ||
1018 | extern void perf_event_init(void); | 1018 | extern void perf_event_init(void); |
1019 | extern void perf_tp_event(u64 addr, u64 count, void *record, | 1019 | extern void perf_tp_event(u16 event_type, u64 count, void *record, |
1020 | int entry_size, struct pt_regs *regs, | 1020 | int entry_size, struct pt_regs *regs, |
1021 | struct hlist_head *head, int rctx, | 1021 | struct hlist_head *head, int rctx, |
1022 | struct task_struct *task); | 1022 | struct task_struct *task); |
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 0810f81b6db2..56f795e6a093 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h | |||
@@ -605,15 +605,15 @@ extern void perf_trace_del(struct perf_event *event, int flags); | |||
605 | extern int ftrace_profile_set_filter(struct perf_event *event, int event_id, | 605 | extern int ftrace_profile_set_filter(struct perf_event *event, int event_id, |
606 | char *filter_str); | 606 | char *filter_str); |
607 | extern void ftrace_profile_free_filter(struct perf_event *event); | 607 | extern void ftrace_profile_free_filter(struct perf_event *event); |
608 | extern void *perf_trace_buf_prepare(int size, unsigned short type, | 608 | void perf_trace_buf_update(void *record, u16 type); |
609 | struct pt_regs **regs, int *rctxp); | 609 | void *perf_trace_buf_alloc(int size, struct pt_regs **regs, int *rctxp); |
610 | 610 | ||
611 | static inline void | 611 | static inline void |
612 | perf_trace_buf_submit(void *raw_data, int size, int rctx, u64 addr, | 612 | perf_trace_buf_submit(void *raw_data, int size, int rctx, u16 type, |
613 | u64 count, struct pt_regs *regs, void *head, | 613 | u64 count, struct pt_regs *regs, void *head, |
614 | struct task_struct *task) | 614 | struct task_struct *task) |
615 | { | 615 | { |
616 | perf_tp_event(addr, count, raw_data, size, regs, head, rctx, task); | 616 | perf_tp_event(type, count, raw_data, size, regs, head, rctx, task); |
617 | } | 617 | } |
618 | #endif | 618 | #endif |
619 | 619 | ||
diff --git a/include/trace/perf.h b/include/trace/perf.h index 6f7e37869065..77cd9043b7e4 100644 --- a/include/trace/perf.h +++ b/include/trace/perf.h | |||
@@ -53,8 +53,7 @@ perf_trace_##call(void *__data, proto) \ | |||
53 | sizeof(u64)); \ | 53 | sizeof(u64)); \ |
54 | __entry_size -= sizeof(u32); \ | 54 | __entry_size -= sizeof(u32); \ |
55 | \ | 55 | \ |
56 | entry = perf_trace_buf_prepare(__entry_size, \ | 56 | entry = perf_trace_buf_alloc(__entry_size, &__regs, &rctx); \ |
57 | event_call->event.type, &__regs, &rctx); \ | ||
58 | if (!entry) \ | 57 | if (!entry) \ |
59 | return; \ | 58 | return; \ |
60 | \ | 59 | \ |
@@ -64,8 +63,9 @@ perf_trace_##call(void *__data, proto) \ | |||
64 | \ | 63 | \ |
65 | { assign; } \ | 64 | { assign; } \ |
66 | \ | 65 | \ |
67 | perf_trace_buf_submit(entry, __entry_size, rctx, 0, \ | 66 | perf_trace_buf_submit(entry, __entry_size, rctx, \ |
68 | __count, __regs, head, __task); \ | 67 | event_call->event.type, __count, __regs, \ |
68 | head, __task); \ | ||
69 | } | 69 | } |
70 | 70 | ||
71 | /* | 71 | /* |
diff --git a/kernel/events/core.c b/kernel/events/core.c index de24fbce5277..d8512883c0a0 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -6987,7 +6987,7 @@ static int perf_tp_event_match(struct perf_event *event, | |||
6987 | return 1; | 6987 | return 1; |
6988 | } | 6988 | } |
6989 | 6989 | ||
6990 | void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, | 6990 | void perf_tp_event(u16 event_type, u64 count, void *record, int entry_size, |
6991 | struct pt_regs *regs, struct hlist_head *head, int rctx, | 6991 | struct pt_regs *regs, struct hlist_head *head, int rctx, |
6992 | struct task_struct *task) | 6992 | struct task_struct *task) |
6993 | { | 6993 | { |
@@ -6999,9 +6999,11 @@ void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, | |||
6999 | .data = record, | 6999 | .data = record, |
7000 | }; | 7000 | }; |
7001 | 7001 | ||
7002 | perf_sample_data_init(&data, addr, 0); | 7002 | perf_sample_data_init(&data, 0, 0); |
7003 | data.raw = &raw; | 7003 | data.raw = &raw; |
7004 | 7004 | ||
7005 | perf_trace_buf_update(record, event_type); | ||
7006 | |||
7005 | hlist_for_each_entry_rcu(event, head, hlist_entry) { | 7007 | hlist_for_each_entry_rcu(event, head, hlist_entry) { |
7006 | if (perf_tp_event_match(event, &data, regs)) | 7008 | if (perf_tp_event_match(event, &data, regs)) |
7007 | perf_swevent_event(event, count, &data, regs); | 7009 | perf_swevent_event(event, count, &data, regs); |
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index 7a68afca8249..5a927075977f 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c | |||
@@ -260,42 +260,43 @@ void perf_trace_del(struct perf_event *p_event, int flags) | |||
260 | tp_event->class->reg(tp_event, TRACE_REG_PERF_DEL, p_event); | 260 | tp_event->class->reg(tp_event, TRACE_REG_PERF_DEL, p_event); |
261 | } | 261 | } |
262 | 262 | ||
263 | void *perf_trace_buf_prepare(int size, unsigned short type, | 263 | void *perf_trace_buf_alloc(int size, struct pt_regs **regs, int *rctxp) |
264 | struct pt_regs **regs, int *rctxp) | ||
265 | { | 264 | { |
266 | struct trace_entry *entry; | ||
267 | unsigned long flags; | ||
268 | char *raw_data; | 265 | char *raw_data; |
269 | int pc; | 266 | int rctx; |
270 | 267 | ||
271 | BUILD_BUG_ON(PERF_MAX_TRACE_SIZE % sizeof(unsigned long)); | 268 | BUILD_BUG_ON(PERF_MAX_TRACE_SIZE % sizeof(unsigned long)); |
272 | 269 | ||
273 | if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, | 270 | if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, |
274 | "perf buffer not large enough")) | 271 | "perf buffer not large enough")) |
275 | return NULL; | 272 | return NULL; |
276 | 273 | ||
277 | pc = preempt_count(); | 274 | *rctxp = rctx = perf_swevent_get_recursion_context(); |
278 | 275 | if (rctx < 0) | |
279 | *rctxp = perf_swevent_get_recursion_context(); | ||
280 | if (*rctxp < 0) | ||
281 | return NULL; | 276 | return NULL; |
282 | 277 | ||
283 | if (regs) | 278 | if (regs) |
284 | *regs = this_cpu_ptr(&__perf_regs[*rctxp]); | 279 | *regs = this_cpu_ptr(&__perf_regs[rctx]); |
285 | raw_data = this_cpu_ptr(perf_trace_buf[*rctxp]); | 280 | raw_data = this_cpu_ptr(perf_trace_buf[rctx]); |
286 | 281 | ||
287 | /* zero the dead bytes from align to not leak stack to user */ | 282 | /* zero the dead bytes from align to not leak stack to user */ |
288 | memset(&raw_data[size - sizeof(u64)], 0, sizeof(u64)); | 283 | memset(&raw_data[size - sizeof(u64)], 0, sizeof(u64)); |
284 | return raw_data; | ||
285 | } | ||
286 | EXPORT_SYMBOL_GPL(perf_trace_buf_alloc); | ||
287 | NOKPROBE_SYMBOL(perf_trace_buf_alloc); | ||
288 | |||
289 | void perf_trace_buf_update(void *record, u16 type) | ||
290 | { | ||
291 | struct trace_entry *entry = record; | ||
292 | int pc = preempt_count(); | ||
293 | unsigned long flags; | ||
289 | 294 | ||
290 | entry = (struct trace_entry *)raw_data; | ||
291 | local_save_flags(flags); | 295 | local_save_flags(flags); |
292 | tracing_generic_entry_update(entry, flags, pc); | 296 | tracing_generic_entry_update(entry, flags, pc); |
293 | entry->type = type; | 297 | entry->type = type; |
294 | |||
295 | return raw_data; | ||
296 | } | 298 | } |
297 | EXPORT_SYMBOL_GPL(perf_trace_buf_prepare); | 299 | NOKPROBE_SYMBOL(perf_trace_buf_update); |
298 | NOKPROBE_SYMBOL(perf_trace_buf_prepare); | ||
299 | 300 | ||
300 | #ifdef CONFIG_FUNCTION_TRACER | 301 | #ifdef CONFIG_FUNCTION_TRACER |
301 | static void | 302 | static void |
@@ -319,13 +320,13 @@ perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip, | |||
319 | memset(®s, 0, sizeof(regs)); | 320 | memset(®s, 0, sizeof(regs)); |
320 | perf_fetch_caller_regs(®s); | 321 | perf_fetch_caller_regs(®s); |
321 | 322 | ||
322 | entry = perf_trace_buf_prepare(ENTRY_SIZE, TRACE_FN, NULL, &rctx); | 323 | entry = perf_trace_buf_alloc(ENTRY_SIZE, NULL, &rctx); |
323 | if (!entry) | 324 | if (!entry) |
324 | return; | 325 | return; |
325 | 326 | ||
326 | entry->ip = ip; | 327 | entry->ip = ip; |
327 | entry->parent_ip = parent_ip; | 328 | entry->parent_ip = parent_ip; |
328 | perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, 0, | 329 | perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, TRACE_FN, |
329 | 1, ®s, head, NULL); | 330 | 1, ®s, head, NULL); |
330 | 331 | ||
331 | #undef ENTRY_SIZE | 332 | #undef ENTRY_SIZE |
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 919e0ddd8fcc..5546eec0505f 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c | |||
@@ -1149,14 +1149,15 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs) | |||
1149 | size = ALIGN(__size + sizeof(u32), sizeof(u64)); | 1149 | size = ALIGN(__size + sizeof(u32), sizeof(u64)); |
1150 | size -= sizeof(u32); | 1150 | size -= sizeof(u32); |
1151 | 1151 | ||
1152 | entry = perf_trace_buf_prepare(size, call->event.type, NULL, &rctx); | 1152 | entry = perf_trace_buf_alloc(size, NULL, &rctx); |
1153 | if (!entry) | 1153 | if (!entry) |
1154 | return; | 1154 | return; |
1155 | 1155 | ||
1156 | entry->ip = (unsigned long)tk->rp.kp.addr; | 1156 | entry->ip = (unsigned long)tk->rp.kp.addr; |
1157 | memset(&entry[1], 0, dsize); | 1157 | memset(&entry[1], 0, dsize); |
1158 | store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); | 1158 | store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); |
1159 | perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL); | 1159 | perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, |
1160 | head, NULL); | ||
1160 | } | 1161 | } |
1161 | NOKPROBE_SYMBOL(kprobe_perf_func); | 1162 | NOKPROBE_SYMBOL(kprobe_perf_func); |
1162 | 1163 | ||
@@ -1184,14 +1185,15 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, | |||
1184 | size = ALIGN(__size + sizeof(u32), sizeof(u64)); | 1185 | size = ALIGN(__size + sizeof(u32), sizeof(u64)); |
1185 | size -= sizeof(u32); | 1186 | size -= sizeof(u32); |
1186 | 1187 | ||
1187 | entry = perf_trace_buf_prepare(size, call->event.type, NULL, &rctx); | 1188 | entry = perf_trace_buf_alloc(size, NULL, &rctx); |
1188 | if (!entry) | 1189 | if (!entry) |
1189 | return; | 1190 | return; |
1190 | 1191 | ||
1191 | entry->func = (unsigned long)tk->rp.kp.addr; | 1192 | entry->func = (unsigned long)tk->rp.kp.addr; |
1192 | entry->ret_ip = (unsigned long)ri->ret_addr; | 1193 | entry->ret_ip = (unsigned long)ri->ret_addr; |
1193 | store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); | 1194 | store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); |
1194 | perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL); | 1195 | perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, |
1196 | head, NULL); | ||
1195 | } | 1197 | } |
1196 | NOKPROBE_SYMBOL(kretprobe_perf_func); | 1198 | NOKPROBE_SYMBOL(kretprobe_perf_func); |
1197 | #endif /* CONFIG_PERF_EVENTS */ | 1199 | #endif /* CONFIG_PERF_EVENTS */ |
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index e78f364cc192..b2b6efc083a4 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c | |||
@@ -587,15 +587,16 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) | |||
587 | size = ALIGN(size + sizeof(u32), sizeof(u64)); | 587 | size = ALIGN(size + sizeof(u32), sizeof(u64)); |
588 | size -= sizeof(u32); | 588 | size -= sizeof(u32); |
589 | 589 | ||
590 | rec = (struct syscall_trace_enter *)perf_trace_buf_prepare(size, | 590 | rec = perf_trace_buf_alloc(size, NULL, &rctx); |
591 | sys_data->enter_event->event.type, NULL, &rctx); | ||
592 | if (!rec) | 591 | if (!rec) |
593 | return; | 592 | return; |
594 | 593 | ||
595 | rec->nr = syscall_nr; | 594 | rec->nr = syscall_nr; |
596 | syscall_get_arguments(current, regs, 0, sys_data->nb_args, | 595 | syscall_get_arguments(current, regs, 0, sys_data->nb_args, |
597 | (unsigned long *)&rec->args); | 596 | (unsigned long *)&rec->args); |
598 | perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL); | 597 | perf_trace_buf_submit(rec, size, rctx, |
598 | sys_data->enter_event->event.type, 1, regs, | ||
599 | head, NULL); | ||
599 | } | 600 | } |
600 | 601 | ||
601 | static int perf_sysenter_enable(struct trace_event_call *call) | 602 | static int perf_sysenter_enable(struct trace_event_call *call) |
@@ -660,14 +661,14 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) | |||
660 | size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64)); | 661 | size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64)); |
661 | size -= sizeof(u32); | 662 | size -= sizeof(u32); |
662 | 663 | ||
663 | rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size, | 664 | rec = perf_trace_buf_alloc(size, NULL, &rctx); |
664 | sys_data->exit_event->event.type, NULL, &rctx); | ||
665 | if (!rec) | 665 | if (!rec) |
666 | return; | 666 | return; |
667 | 667 | ||
668 | rec->nr = syscall_nr; | 668 | rec->nr = syscall_nr; |
669 | rec->ret = syscall_get_return_value(current, regs); | 669 | rec->ret = syscall_get_return_value(current, regs); |
670 | perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL); | 670 | perf_trace_buf_submit(rec, size, rctx, sys_data->exit_event->event.type, |
671 | 1, regs, head, NULL); | ||
671 | } | 672 | } |
672 | 673 | ||
673 | static int perf_sysexit_enable(struct trace_event_call *call) | 674 | static int perf_sysexit_enable(struct trace_event_call *call) |
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 7915142c89e4..c53485441c88 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c | |||
@@ -1131,7 +1131,7 @@ static void __uprobe_perf_func(struct trace_uprobe *tu, | |||
1131 | if (hlist_empty(head)) | 1131 | if (hlist_empty(head)) |
1132 | goto out; | 1132 | goto out; |
1133 | 1133 | ||
1134 | entry = perf_trace_buf_prepare(size, call->event.type, NULL, &rctx); | 1134 | entry = perf_trace_buf_alloc(size, NULL, &rctx); |
1135 | if (!entry) | 1135 | if (!entry) |
1136 | goto out; | 1136 | goto out; |
1137 | 1137 | ||
@@ -1152,7 +1152,8 @@ static void __uprobe_perf_func(struct trace_uprobe *tu, | |||
1152 | memset(data + len, 0, size - esize - len); | 1152 | memset(data + len, 0, size - esize - len); |
1153 | } | 1153 | } |
1154 | 1154 | ||
1155 | perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL); | 1155 | perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, |
1156 | head, NULL); | ||
1156 | out: | 1157 | out: |
1157 | preempt_enable(); | 1158 | preempt_enable(); |
1158 | } | 1159 | } |