Add a new bpf file to each trace event. The following commands can be
written into it:
attach:<fd>     Attaches BPF prog fd to tracepoint
detach:<fd>     Detaches BPF prog fd to tracepoint

Reading the bpf file will show all the attached programs to the
tracepoint.

Signed-off-by: Joel Fernandes (Google) <j...@joelfernandes.org>
---
 include/linux/bpf_trace.h    |   6 ++
 include/linux/trace_events.h |   1 +
 kernel/trace/bpf_trace.c     | 169 +++++++++++++++++++++++++++++++++++
 kernel/trace/trace.h         |   1 +
 kernel/trace/trace_events.c  |   9 +-
 5 files changed, 184 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf_trace.h b/include/linux/bpf_trace.h
index 4a593827fd87..1fe73501809c 100644
--- a/include/linux/bpf_trace.h
+++ b/include/linux/bpf_trace.h
@@ -9,6 +9,12 @@
 struct bpf_raw_tracepoint {
        struct bpf_raw_event_map *btp;
        struct bpf_prog *prog;
+       /*
+        * Multiple programs can be attached to a tracepoint,
+        * All of these are linked to each other and can be reached
+        * from the event's bpf_attach file in tracefs.
+        */
+       struct list_head event_attached;
 };
 
 struct bpf_raw_tracepoint *bpf_raw_tracepoint_open(char *tp_name, int prog_fd);
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 8a62731673f7..525f2ac44aa3 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -371,6 +371,7 @@ struct trace_event_file {
        struct trace_array              *tr;
        struct trace_subsystem_dir      *system;
        struct list_head                triggers;
+       struct list_head                bpf_attached;
 
        /*
         * 32 bit flags:
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index c4b543bc617f..28621ad88c12 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -1469,3 +1469,172 @@ struct bpf_raw_tracepoint *bpf_raw_tracepoint_open(char 
*tp_name, int prog_fd)
        bpf_put_raw_tracepoint(btp);
        return ERR_PTR(err);
 }
+
+enum event_bpf_cmd { BPF_ATTACH, BPF_DETACH };
+#define BPF_CMD_BUF_LEN 32
+
+static ssize_t
+event_bpf_attach_write(struct file *filp, const char __user *ubuf,
+                   size_t cnt, loff_t *ppos)
+{
+       int err, prog_fd, cmd_num, len;
+       struct trace_event_call *call;
+       struct trace_event_file *file;
+       struct bpf_raw_tracepoint *raw_tp, *next;
+       char buf[BPF_CMD_BUF_LEN], *end, *tok;
+       enum event_bpf_cmd cmd;
+       struct bpf_prog *prog;
+       bool prog_put = true;
+
+       len = min((int)cnt, BPF_CMD_BUF_LEN - 1);
+
+       err = copy_from_user(buf, ubuf, len);
+       if (err)
+               return err;
+       buf[len] = 0;
+
+       /* Parse 2 arguments of format: <cmd>:<fd> */
+       end = &buf[0];
+       cmd_num = 1;
+       while (cmd_num < 3) {
+               tok = strsep(&end, ":");
+               if (!tok)
+                       return -EINVAL;
+
+               switch (cmd_num) {
+               case 1:
+                       if (!strncmp(tok, "attach", 6))
+                               cmd = BPF_ATTACH;
+                       else if (!strncmp(tok, "detach", 6))
+                               cmd = BPF_DETACH;
+                       else
+                               return -EINVAL;
+                       break;
+               case 2:
+                       err = kstrtoint(tok, 10, &prog_fd);
+                       if (err)
+                               return err;
+                       break;
+               }
+               cmd_num++;
+       }
+       if (cmd_num != 3)
+               return -EINVAL;
+
+       file = event_file_data(filp);
+       /* Command is to attach fd to tracepoint */
+       if (cmd == BPF_ATTACH) {
+               mutex_lock(&event_mutex);
+               call = file->event_call;
+
+               raw_tp = bpf_raw_tracepoint_open((char *)call->tp->name,
+                                                prog_fd);
+               if (IS_ERR(raw_tp)) {
+                       mutex_unlock(&event_mutex);
+                       return PTR_ERR(raw_tp);
+               }
+
+               list_add(&raw_tp->event_attached, &file->bpf_attached);
+               mutex_unlock(&event_mutex);
+               *ppos += cnt;
+               return cnt;
+       }
+
+       /* Command is to detach fd from tracepoint */
+       prog = bpf_prog_get(prog_fd);
+       if (IS_ERR(prog))
+               return PTR_ERR(prog);
+
+       mutex_lock(&event_mutex);
+       list_for_each_entry_safe(raw_tp, next, &file->bpf_attached,
+                                event_attached) {
+               if (raw_tp->prog == prog) {
+                       list_del(&raw_tp->event_attached);
+                       bpf_raw_tracepoint_close(raw_tp);
+                       prog_put = false;
+                       break;
+               }
+       }
+       mutex_unlock(&event_mutex);
+
+       if (prog_put)
+               bpf_prog_put(prog);
+       *ppos += cnt;
+       return cnt;
+}
+
+static void *event_bpf_attach_next(struct seq_file *m, void *t, loff_t *pos)
+{
+       struct trace_event_file *file = event_file_data(m->private);
+
+       return seq_list_next(t, &file->bpf_attached, pos);
+}
+
+static void *event_bpf_attach_start(struct seq_file *m, loff_t *pos)
+{
+       struct trace_event_file *event_file;
+
+       /* ->stop() is called even if ->start() fails */
+       mutex_lock(&event_mutex);
+       event_file = event_file_data(m->private);
+       if (unlikely(!event_file))
+               return ERR_PTR(-ENODEV);
+
+       if (list_empty(&event_file->bpf_attached))
+               return NULL;
+
+       return seq_list_start(&event_file->bpf_attached, *pos);
+}
+
+static void event_bpf_attach_stop(struct seq_file *m, void *t)
+{
+       mutex_unlock(&event_mutex);
+}
+
+static int event_bpf_attach_show(struct seq_file *m, void *v)
+{
+       struct bpf_raw_tracepoint *raw_tp;
+
+       raw_tp = list_entry(v, struct bpf_raw_tracepoint, event_attached);
+       seq_printf(m, "prog id: %u\n", raw_tp->prog->aux->id);
+       return 0;
+}
+
+static const struct seq_operations event_bpf_attach_seq_ops = {
+       .start = event_bpf_attach_start,
+       .next = event_bpf_attach_next,
+       .stop = event_bpf_attach_stop,
+       .show = event_bpf_attach_show,
+};
+
+static int event_bpf_attach_open(struct inode *inode, struct file *file)
+{
+       int ret = 0;
+
+       mutex_lock(&event_mutex);
+
+       if (unlikely(!event_file_data(file))) {
+               mutex_unlock(&event_mutex);
+               return -ENODEV;
+       }
+
+       if (file->f_mode & FMODE_READ) {
+               ret = seq_open(file, &event_bpf_attach_seq_ops);
+               if (!ret) {
+                       struct seq_file *m = file->private_data;
+
+                       m->private = file;
+               }
+       }
+
+       mutex_unlock(&event_mutex);
+
+       return ret;
+}
+
+const struct file_operations event_bpf_attach_fops = {
+       .open = event_bpf_attach_open,
+       .read = seq_read,
+       .write = event_bpf_attach_write,
+       .llseek = default_llseek,
+};
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 005f08629b8b..e33828d24eb2 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1582,6 +1582,7 @@ extern struct list_head ftrace_events;
 
 extern const struct file_operations event_trigger_fops;
 extern const struct file_operations event_hist_fops;
+extern const struct file_operations event_bpf_attach_fops;
 
 #ifdef CONFIG_HIST_TRIGGERS
 extern int register_trigger_hist_cmd(void);
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 67851fb66b6b..79420d5efaef 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -2018,8 +2018,10 @@ event_create_dir(struct dentry *parent, struct 
trace_event_file *file)
                trace_create_file("trigger", 0644, file->dir, file,
                                  &event_trigger_fops);
 
-               trace_create_file("bpf_attach", 0644, file->dir, file,
-                                 &bpf_attach_trigger_fops);
+#ifdef CONFIG_BPF_EVENTS
+               trace_create_file("bpf", 0644, file->dir, file,
+                                 &event_bpf_attach_fops);
+#endif
        }
 
 #ifdef CONFIG_HIST_TRIGGERS
@@ -2267,6 +2269,9 @@ trace_create_new_event(struct trace_event_call *call,
        atomic_set(&file->sm_ref, 0);
        atomic_set(&file->tm_ref, 0);
        INIT_LIST_HEAD(&file->triggers);
+#ifdef CONFIG_BPF_EVENTS
+       INIT_LIST_HEAD(&file->bpf_attached);
+#endif
        list_add(&file->list, &tr->events);
 
        return file;
-- 
2.22.0.410.gd8fdbe21b5-goog

Reply via email to