aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/trace_kprobe.c
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2009-11-05 22:13:05 -0500
committerIngo Molnar <mingo@elte.hu>2009-11-08 04:31:42 -0500
commit444a2a3bcd6d5bed5c823136f68fcc93c0fe283f (patch)
tree6a57308586b4e723238646074e79298845803520 /kernel/trace/trace_kprobe.c
parent09879b99d44d701c603935ef2549004405d7f8f9 (diff)
tracing, perf_events: Protect the buffer from recursion in perf
While tracing using events with perf, if one enables the lockdep:lock_acquire event, it will infect every other perf trace events. Basically, you can enable whatever set of trace events through perf but if this event is part of the set, the only result we can get is a long list of lock_acquire events of rcu read lock, and only that. This is because of a recursion inside perf. 1) When a trace event is triggered, it will fill a per cpu buffer and submit it to perf. 2) Perf will commit this event but will also protect some data using rcu_read_lock 3) A recursion appears: rcu_read_lock triggers a lock_acquire event that will fill the per cpu event and then submit the buffer to perf. 4) Perf detects a recursion and ignores it 5) Perf continues its work on the previous event, but its buffer has been overwritten by the lock_acquire event, it has then been turned into a lock_acquire event of rcu read lock Such scenario also happens with lock_release with rcu_read_unlock(). We could turn the rcu_read_lock() into __rcu_read_lock() to drop the lock debugging from perf fast path, but that would make us lose the rcu debugging and that doesn't prevent from other possible kind of recursion from perf in the future. This patch adds a recursion protection based on a counter on the perf trace per cpu buffers to solve the problem. -v2: Fixed lost whitespace, added reviewed-by tag Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Reviewed-by: Masami Hiramatsu <mhiramat@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Li Zefan <lizf@cn.fujitsu.com> Cc: Jason Baron <jbaron@redhat.com> LKML-Reference: <1257477185-7838-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/trace/trace_kprobe.c')
-rw-r--r--kernel/trace/trace_kprobe.c50
1 files changed, 42 insertions, 8 deletions
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index cf17a6694f32..3696476f307d 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -1208,6 +1208,7 @@ static __kprobes int kprobe_profile_func(struct kprobe *kp,
1208 struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp); 1208 struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp);
1209 struct ftrace_event_call *call = &tp->call; 1209 struct ftrace_event_call *call = &tp->call;
1210 struct kprobe_trace_entry *entry; 1210 struct kprobe_trace_entry *entry;
1211 struct perf_trace_buf *trace_buf;
1211 struct trace_entry *ent; 1212 struct trace_entry *ent;
1212 int size, __size, i, pc, __cpu; 1213 int size, __size, i, pc, __cpu;
1213 unsigned long irq_flags; 1214 unsigned long irq_flags;
@@ -1229,14 +1230,26 @@ static __kprobes int kprobe_profile_func(struct kprobe *kp,
1229 __cpu = smp_processor_id(); 1230 __cpu = smp_processor_id();
1230 1231
1231 if (in_nmi()) 1232 if (in_nmi())
1232 raw_data = rcu_dereference(trace_profile_buf_nmi); 1233 trace_buf = rcu_dereference(perf_trace_buf_nmi);
1233 else 1234 else
1234 raw_data = rcu_dereference(trace_profile_buf); 1235 trace_buf = rcu_dereference(perf_trace_buf);
1235 1236
1236 if (!raw_data) 1237 if (!trace_buf)
1237 goto end; 1238 goto end;
1238 1239
1239 raw_data = per_cpu_ptr(raw_data, __cpu); 1240 trace_buf = per_cpu_ptr(trace_buf, __cpu);
1241
1242 if (trace_buf->recursion++)
1243 goto end_recursion;
1244
1245 /*
1246 * Make recursion update visible before entering perf_tp_event
1247 * so that we protect from perf recursions.
1248 */
1249 barrier();
1250
1251 raw_data = trace_buf->buf;
1252
1240 /* Zero dead bytes from alignment to avoid buffer leak to userspace */ 1253 /* Zero dead bytes from alignment to avoid buffer leak to userspace */
1241 *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; 1254 *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL;
1242 entry = (struct kprobe_trace_entry *)raw_data; 1255 entry = (struct kprobe_trace_entry *)raw_data;
@@ -1249,8 +1262,12 @@ static __kprobes int kprobe_profile_func(struct kprobe *kp,
1249 for (i = 0; i < tp->nr_args; i++) 1262 for (i = 0; i < tp->nr_args; i++)
1250 entry->args[i] = call_fetch(&tp->args[i].fetch, regs); 1263 entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
1251 perf_tp_event(call->id, entry->ip, 1, entry, size); 1264 perf_tp_event(call->id, entry->ip, 1, entry, size);
1265
1266end_recursion:
1267 trace_buf->recursion--;
1252end: 1268end:
1253 local_irq_restore(irq_flags); 1269 local_irq_restore(irq_flags);
1270
1254 return 0; 1271 return 0;
1255} 1272}
1256 1273
@@ -1261,6 +1278,7 @@ static __kprobes int kretprobe_profile_func(struct kretprobe_instance *ri,
1261 struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp); 1278 struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp);
1262 struct ftrace_event_call *call = &tp->call; 1279 struct ftrace_event_call *call = &tp->call;
1263 struct kretprobe_trace_entry *entry; 1280 struct kretprobe_trace_entry *entry;
1281 struct perf_trace_buf *trace_buf;
1264 struct trace_entry *ent; 1282 struct trace_entry *ent;
1265 int size, __size, i, pc, __cpu; 1283 int size, __size, i, pc, __cpu;
1266 unsigned long irq_flags; 1284 unsigned long irq_flags;
@@ -1282,14 +1300,26 @@ static __kprobes int kretprobe_profile_func(struct kretprobe_instance *ri,
1282 __cpu = smp_processor_id(); 1300 __cpu = smp_processor_id();
1283 1301
1284 if (in_nmi()) 1302 if (in_nmi())
1285 raw_data = rcu_dereference(trace_profile_buf_nmi); 1303 trace_buf = rcu_dereference(perf_trace_buf_nmi);
1286 else 1304 else
1287 raw_data = rcu_dereference(trace_profile_buf); 1305 trace_buf = rcu_dereference(perf_trace_buf);
1288 1306
1289 if (!raw_data) 1307 if (!trace_buf)
1290 goto end; 1308 goto end;
1291 1309
1292 raw_data = per_cpu_ptr(raw_data, __cpu); 1310 trace_buf = per_cpu_ptr(trace_buf, __cpu);
1311
1312 if (trace_buf->recursion++)
1313 goto end_recursion;
1314
1315 /*
1316 * Make recursion update visible before entering perf_tp_event
1317 * so that we protect from perf recursions.
1318 */
1319 barrier();
1320
1321 raw_data = trace_buf->buf;
1322
1293 /* Zero dead bytes from alignment to avoid buffer leak to userspace */ 1323 /* Zero dead bytes from alignment to avoid buffer leak to userspace */
1294 *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL; 1324 *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL;
1295 entry = (struct kretprobe_trace_entry *)raw_data; 1325 entry = (struct kretprobe_trace_entry *)raw_data;
@@ -1303,8 +1333,12 @@ static __kprobes int kretprobe_profile_func(struct kretprobe_instance *ri,
1303 for (i = 0; i < tp->nr_args; i++) 1333 for (i = 0; i < tp->nr_args; i++)
1304 entry->args[i] = call_fetch(&tp->args[i].fetch, regs); 1334 entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
1305 perf_tp_event(call->id, entry->ret_ip, 1, entry, size); 1335 perf_tp_event(call->id, entry->ret_ip, 1, entry, size);
1336
1337end_recursion:
1338 trace_buf->recursion--;
1306end: 1339end:
1307 local_irq_restore(irq_flags); 1340 local_irq_restore(irq_flags);
1341
1308 return 0; 1342 return 0;
1309} 1343}
1310 1344