diff options
author | Martin KaFai Lau <kafai@fb.com> | 2016-02-03 15:28:28 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-02-09 05:08:58 -0500 |
commit | a7636d9ecfa3ab7800a7c04c1f89378229eff609 (patch) | |
tree | 8b61d2320c6405d671e1a4fe2ff6ace3a98fbbb3 | |
parent | 156d22386503e1efc58b0f3244895b1c0f930018 (diff) |
kprobes: Optimize hot path by using percpu counter to collect 'nhit' statistics
When doing ebpf+kprobe on some hot TCP functions (e.g.
tcp_rcv_established), the kprobe_dispatcher() function
shows up in 'perf report'.
In kprobe_dispatcher(), there is a lot of cache bouncing
on 'tk->nhit++'. 'tk->nhit' and 'tk->tp.flags' also share
the same cacheline.
perf report (cycles:pp):
8.30% ipv4_dst_check
4.74% copy_user_enhanced_fast_string
3.93% dst_release
2.80% tcp_v4_rcv
2.31% queued_spin_lock_slowpath
2.30% _raw_spin_lock
1.88% mlx4_en_process_rx_cq
1.84% eth_get_headlen
1.81% ip_rcv_finish
~~~~
1.71% kprobe_dispatcher
~~~~
1.55% mlx4_en_xmit
1.09% __probe_kernel_read
perf report after patch:
9.15% ipv4_dst_check
5.00% copy_user_enhanced_fast_string
4.12% dst_release
2.96% tcp_v4_rcv
2.50% _raw_spin_lock
2.39% queued_spin_lock_slowpath
2.11% eth_get_headlen
2.03% mlx4_en_process_rx_cq
1.69% mlx4_en_xmit
1.19% ip_rcv_finish
1.12% __probe_kernel_read
1.02% ehci_hcd_cleanup
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: Josef Bacik <jbacik@fb.com>
Cc: Kernel Team <kernel-team@fb.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1454531308-2441898-1-git-send-email-kafai@fb.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | kernel/trace/trace_kprobe.c | 19 |
1 files changed, 15 insertions, 4 deletions
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index c9956440d0e6..21b81a41dae5 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c | |||
@@ -30,7 +30,7 @@ | |||
30 | struct trace_kprobe { | 30 | struct trace_kprobe { |
31 | struct list_head list; | 31 | struct list_head list; |
32 | struct kretprobe rp; /* Use rp.kp for kprobe use */ | 32 | struct kretprobe rp; /* Use rp.kp for kprobe use */ |
33 | unsigned long nhit; | 33 | unsigned long __percpu *nhit; |
34 | const char *symbol; /* symbol name */ | 34 | const char *symbol; /* symbol name */ |
35 | struct trace_probe tp; | 35 | struct trace_probe tp; |
36 | }; | 36 | }; |
@@ -274,6 +274,10 @@ static struct trace_kprobe *alloc_trace_kprobe(const char *group, | |||
274 | if (!tk) | 274 | if (!tk) |
275 | return ERR_PTR(ret); | 275 | return ERR_PTR(ret); |
276 | 276 | ||
277 | tk->nhit = alloc_percpu(unsigned long); | ||
278 | if (!tk->nhit) | ||
279 | goto error; | ||
280 | |||
277 | if (symbol) { | 281 | if (symbol) { |
278 | tk->symbol = kstrdup(symbol, GFP_KERNEL); | 282 | tk->symbol = kstrdup(symbol, GFP_KERNEL); |
279 | if (!tk->symbol) | 283 | if (!tk->symbol) |
@@ -313,6 +317,7 @@ static struct trace_kprobe *alloc_trace_kprobe(const char *group, | |||
313 | error: | 317 | error: |
314 | kfree(tk->tp.call.name); | 318 | kfree(tk->tp.call.name); |
315 | kfree(tk->symbol); | 319 | kfree(tk->symbol); |
320 | free_percpu(tk->nhit); | ||
316 | kfree(tk); | 321 | kfree(tk); |
317 | return ERR_PTR(ret); | 322 | return ERR_PTR(ret); |
318 | } | 323 | } |
@@ -327,6 +332,7 @@ static void free_trace_kprobe(struct trace_kprobe *tk) | |||
327 | kfree(tk->tp.call.class->system); | 332 | kfree(tk->tp.call.class->system); |
328 | kfree(tk->tp.call.name); | 333 | kfree(tk->tp.call.name); |
329 | kfree(tk->symbol); | 334 | kfree(tk->symbol); |
335 | free_percpu(tk->nhit); | ||
330 | kfree(tk); | 336 | kfree(tk); |
331 | } | 337 | } |
332 | 338 | ||
@@ -874,9 +880,14 @@ static const struct file_operations kprobe_events_ops = { | |||
874 | static int probes_profile_seq_show(struct seq_file *m, void *v) | 880 | static int probes_profile_seq_show(struct seq_file *m, void *v) |
875 | { | 881 | { |
876 | struct trace_kprobe *tk = v; | 882 | struct trace_kprobe *tk = v; |
883 | unsigned long nhit = 0; | ||
884 | int cpu; | ||
885 | |||
886 | for_each_possible_cpu(cpu) | ||
887 | nhit += *per_cpu_ptr(tk->nhit, cpu); | ||
877 | 888 | ||
878 | seq_printf(m, " %-44s %15lu %15lu\n", | 889 | seq_printf(m, " %-44s %15lu %15lu\n", |
879 | trace_event_name(&tk->tp.call), tk->nhit, | 890 | trace_event_name(&tk->tp.call), nhit, |
880 | tk->rp.kp.nmissed); | 891 | tk->rp.kp.nmissed); |
881 | 892 | ||
882 | return 0; | 893 | return 0; |
@@ -1225,7 +1236,7 @@ static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs) | |||
1225 | { | 1236 | { |
1226 | struct trace_kprobe *tk = container_of(kp, struct trace_kprobe, rp.kp); | 1237 | struct trace_kprobe *tk = container_of(kp, struct trace_kprobe, rp.kp); |
1227 | 1238 | ||
1228 | tk->nhit++; | 1239 | raw_cpu_inc(*tk->nhit); |
1229 | 1240 | ||
1230 | if (tk->tp.flags & TP_FLAG_TRACE) | 1241 | if (tk->tp.flags & TP_FLAG_TRACE) |
1231 | kprobe_trace_func(tk, regs); | 1242 | kprobe_trace_func(tk, regs); |
@@ -1242,7 +1253,7 @@ kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs) | |||
1242 | { | 1253 | { |
1243 | struct trace_kprobe *tk = container_of(ri->rp, struct trace_kprobe, rp); | 1254 | struct trace_kprobe *tk = container_of(ri->rp, struct trace_kprobe, rp); |
1244 | 1255 | ||
1245 | tk->nhit++; | 1256 | raw_cpu_inc(*tk->nhit); |
1246 | 1257 | ||
1247 | if (tk->tp.flags & TP_FLAG_TRACE) | 1258 | if (tk->tp.flags & TP_FLAG_TRACE) |
1248 | kretprobe_trace_func(tk, ri, regs); | 1259 | kretprobe_trace_func(tk, ri, regs); |