[PATCH RFC 2/4] trace/bpf: Add support for attach/detach of ftrace events to BPF

From: Joel Fernandes (Google)
Date: Wed Jul 10 2019 - 10:16:10 EST


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) <joel@xxxxxxxxxxxxxxxxx>
---
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