aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/trace_event_perf.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/trace/trace_event_perf.c')
-rw-r--r--kernel/trace/trace_event_perf.c86
1 files changed, 86 insertions, 0 deletions
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
index d72af0b03822..fdeeb5c49627 100644
--- a/kernel/trace/trace_event_perf.c
+++ b/kernel/trace/trace_event_perf.c
@@ -24,6 +24,11 @@ static int total_ref_count;
24static int perf_trace_event_perm(struct ftrace_event_call *tp_event, 24static int perf_trace_event_perm(struct ftrace_event_call *tp_event,
25 struct perf_event *p_event) 25 struct perf_event *p_event)
26{ 26{
27 /* The ftrace function trace is allowed only for root. */
28 if (ftrace_event_is_function(tp_event) &&
29 perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
30 return -EPERM;
31
27 /* No tracing, just counting, so no obvious leak */ 32 /* No tracing, just counting, so no obvious leak */
28 if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW)) 33 if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW))
29 return 0; 34 return 0;
@@ -250,3 +255,84 @@ __kprobes void *perf_trace_buf_prepare(int size, unsigned short type,
250 return raw_data; 255 return raw_data;
251} 256}
252EXPORT_SYMBOL_GPL(perf_trace_buf_prepare); 257EXPORT_SYMBOL_GPL(perf_trace_buf_prepare);
258
259#ifdef CONFIG_FUNCTION_TRACER
260static void
261perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip)
262{
263 struct ftrace_entry *entry;
264 struct hlist_head *head;
265 struct pt_regs regs;
266 int rctx;
267
268#define ENTRY_SIZE (ALIGN(sizeof(struct ftrace_entry) + sizeof(u32), \
269 sizeof(u64)) - sizeof(u32))
270
271 BUILD_BUG_ON(ENTRY_SIZE > PERF_MAX_TRACE_SIZE);
272
273 perf_fetch_caller_regs(&regs);
274
275 entry = perf_trace_buf_prepare(ENTRY_SIZE, TRACE_FN, NULL, &rctx);
276 if (!entry)
277 return;
278
279 entry->ip = ip;
280 entry->parent_ip = parent_ip;
281
282 head = this_cpu_ptr(event_function.perf_events);
283 perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, 0,
284 1, &regs, head);
285
286#undef ENTRY_SIZE
287}
288
289static int perf_ftrace_function_register(struct perf_event *event)
290{
291 struct ftrace_ops *ops = &event->ftrace_ops;
292
293 ops->flags |= FTRACE_OPS_FL_CONTROL;
294 ops->func = perf_ftrace_function_call;
295 return register_ftrace_function(ops);
296}
297
298static int perf_ftrace_function_unregister(struct perf_event *event)
299{
300 struct ftrace_ops *ops = &event->ftrace_ops;
301 return unregister_ftrace_function(ops);
302}
303
304static void perf_ftrace_function_enable(struct perf_event *event)
305{
306 ftrace_function_local_enable(&event->ftrace_ops);
307}
308
309static void perf_ftrace_function_disable(struct perf_event *event)
310{
311 ftrace_function_local_disable(&event->ftrace_ops);
312}
313
314int perf_ftrace_event_register(struct ftrace_event_call *call,
315 enum trace_reg type, void *data)
316{
317 switch (type) {
318 case TRACE_REG_REGISTER:
319 case TRACE_REG_UNREGISTER:
320 break;
321 case TRACE_REG_PERF_REGISTER:
322 case TRACE_REG_PERF_UNREGISTER:
323 return 0;
324 case TRACE_REG_PERF_OPEN:
325 return perf_ftrace_function_register(data);
326 case TRACE_REG_PERF_CLOSE:
327 return perf_ftrace_function_unregister(data);
328 case TRACE_REG_PERF_ADD:
329 perf_ftrace_function_enable(data);
330 return 0;
331 case TRACE_REG_PERF_DEL:
332 perf_ftrace_function_disable(data);
333 return 0;
334 }
335
336 return -EINVAL;
337}
338#endif /* CONFIG_FUNCTION_TRACER */