aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/events
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@fb.com>2016-09-01 21:37:24 -0400
committerDavid S. Miller <davem@davemloft.net>2016-09-02 13:46:44 -0400
commitaa6a5f3cb2b2edc5b9aab0b4fdfdfa9c3b5096a8 (patch)
tree8cf03de3cadf8e080094e1d1c671ada0d85d1ec2 /kernel/events
parentfdc15d388d600d5a1599e14c700af105a5b60761 (diff)
perf, bpf: add perf events core support for BPF_PROG_TYPE_PERF_EVENT programs
Allow attaching BPF_PROG_TYPE_PERF_EVENT programs to sw and hw perf events via overflow_handler mechanism. When program is attached the overflow_handlers become stacked. The program acts as a filter. Returning zero from the program means that the normal perf_event_output handler will not be called and sampling event won't be stored in the ring buffer. The overflow_handler_context==NULL is an additional safety check to make sure programs are not attached to hw breakpoints and watchdog in case other checks (that prevent that now anyway) get accidentally relaxed in the future. The program refcnt is incremented in case perf_events are inhereted when target task is forked. Similar to kprobe and tracepoint programs there is no ioctl to detach the program or swap already attached program. The user space expected to close(perf_event_fd) like it does right now for kprobe+bpf. That restriction simplifies the code quite a bit. The invocation of overflow_handler in __perf_event_overflow() is now done via READ_ONCE, since that pointer can be replaced when the program is attached while perf_event itself could have been active already. There is no need to do similar treatment for event->prog, since it's assigned only once before it's accessed. Signed-off-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'kernel/events')
-rw-r--r--kernel/events/core.c89
1 files changed, 88 insertions, 1 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 3cfabdf7b942..85bf4c37911f 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7022,7 +7022,7 @@ static int __perf_event_overflow(struct perf_event *event,
7022 irq_work_queue(&event->pending); 7022 irq_work_queue(&event->pending);
7023 } 7023 }
7024 7024
7025 event->overflow_handler(event, data, regs); 7025 READ_ONCE(event->overflow_handler)(event, data, regs);
7026 7026
7027 if (*perf_event_fasync(event) && event->pending_kill) { 7027 if (*perf_event_fasync(event) && event->pending_kill) {
7028 event->pending_wakeup = 1; 7028 event->pending_wakeup = 1;
@@ -7637,11 +7637,83 @@ static void perf_event_free_filter(struct perf_event *event)
7637 ftrace_profile_free_filter(event); 7637 ftrace_profile_free_filter(event);
7638} 7638}
7639 7639
7640#ifdef CONFIG_BPF_SYSCALL
7641static void bpf_overflow_handler(struct perf_event *event,
7642 struct perf_sample_data *data,
7643 struct pt_regs *regs)
7644{
7645 struct bpf_perf_event_data_kern ctx = {
7646 .data = data,
7647 .regs = regs,
7648 };
7649 int ret = 0;
7650
7651 preempt_disable();
7652 if (unlikely(__this_cpu_inc_return(bpf_prog_active) != 1))
7653 goto out;
7654 rcu_read_lock();
7655 ret = BPF_PROG_RUN(event->prog, (void *)&ctx);
7656 rcu_read_unlock();
7657out:
7658 __this_cpu_dec(bpf_prog_active);
7659 preempt_enable();
7660 if (!ret)
7661 return;
7662
7663 event->orig_overflow_handler(event, data, regs);
7664}
7665
7666static int perf_event_set_bpf_handler(struct perf_event *event, u32 prog_fd)
7667{
7668 struct bpf_prog *prog;
7669
7670 if (event->overflow_handler_context)
7671 /* hw breakpoint or kernel counter */
7672 return -EINVAL;
7673
7674 if (event->prog)
7675 return -EEXIST;
7676
7677 prog = bpf_prog_get_type(prog_fd, BPF_PROG_TYPE_PERF_EVENT);
7678 if (IS_ERR(prog))
7679 return PTR_ERR(prog);
7680
7681 event->prog = prog;
7682 event->orig_overflow_handler = READ_ONCE(event->overflow_handler);
7683 WRITE_ONCE(event->overflow_handler, bpf_overflow_handler);
7684 return 0;
7685}
7686
7687static void perf_event_free_bpf_handler(struct perf_event *event)
7688{
7689 struct bpf_prog *prog = event->prog;
7690
7691 if (!prog)
7692 return;
7693
7694 WRITE_ONCE(event->overflow_handler, event->orig_overflow_handler);
7695 event->prog = NULL;
7696 bpf_prog_put(prog);
7697}
7698#else
7699static int perf_event_set_bpf_handler(struct perf_event *event, u32 prog_fd)
7700{
7701 return -EOPNOTSUPP;
7702}
7703static void perf_event_free_bpf_handler(struct perf_event *event)
7704{
7705}
7706#endif
7707
7640static int perf_event_set_bpf_prog(struct perf_event *event, u32 prog_fd) 7708static int perf_event_set_bpf_prog(struct perf_event *event, u32 prog_fd)
7641{ 7709{
7642 bool is_kprobe, is_tracepoint; 7710 bool is_kprobe, is_tracepoint;
7643 struct bpf_prog *prog; 7711 struct bpf_prog *prog;
7644 7712
7713 if (event->attr.type == PERF_TYPE_HARDWARE ||
7714 event->attr.type == PERF_TYPE_SOFTWARE)
7715 return perf_event_set_bpf_handler(event, prog_fd);
7716
7645 if (event->attr.type != PERF_TYPE_TRACEPOINT) 7717 if (event->attr.type != PERF_TYPE_TRACEPOINT)
7646 return -EINVAL; 7718 return -EINVAL;
7647 7719
@@ -7682,6 +7754,8 @@ static void perf_event_free_bpf_prog(struct perf_event *event)
7682{ 7754{
7683 struct bpf_prog *prog; 7755 struct bpf_prog *prog;
7684 7756
7757 perf_event_free_bpf_handler(event);
7758
7685 if (!event->tp_event) 7759 if (!event->tp_event)
7686 return; 7760 return;
7687 7761
@@ -8998,6 +9072,19 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
8998 if (!overflow_handler && parent_event) { 9072 if (!overflow_handler && parent_event) {
8999 overflow_handler = parent_event->overflow_handler; 9073 overflow_handler = parent_event->overflow_handler;
9000 context = parent_event->overflow_handler_context; 9074 context = parent_event->overflow_handler_context;
9075#ifdef CONFIG_BPF_SYSCALL
9076 if (overflow_handler == bpf_overflow_handler) {
9077 struct bpf_prog *prog = bpf_prog_inc(parent_event->prog);
9078
9079 if (IS_ERR(prog)) {
9080 err = PTR_ERR(prog);
9081 goto err_ns;
9082 }
9083 event->prog = prog;
9084 event->orig_overflow_handler =
9085 parent_event->orig_overflow_handler;
9086 }
9087#endif
9001 } 9088 }
9002 9089
9003 if (overflow_handler) { 9090 if (overflow_handler) {