bpf: Add support for BPF_OBJ_GET_INFO_BY_FD for bpf_link
authorAndrii Nakryiko <andriin@fb.com>
Wed, 29 Apr 2020 00:16:08 +0000 (17:16 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 29 Apr 2020 00:27:08 +0000 (17:27 -0700)
Add ability to fetch bpf_link details through BPF_OBJ_GET_INFO_BY_FD command.
Also enhance show_fdinfo to potentially include bpf_link type-specific
information (similarly to obj_info).

Also introduce enum bpf_link_type stored in bpf_link itself and expose it in
UAPI. bpf_link_tracing also now will store and return bpf_attach_type.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20200429001614.1544-5-andriin@fb.com
include/linux/bpf-cgroup.h
include/linux/bpf.h
include/linux/bpf_types.h
include/uapi/linux/bpf.h
kernel/bpf/btf.c
kernel/bpf/cgroup.c
kernel/bpf/syscall.c
kernel/bpf/verifier.c
tools/include/uapi/linux/bpf.h

index a9cb9a5bf8e9b90c86fc313ae2bbbf45e27ac72c..272626cc3fc9dbd40aa828eeb236a150a2b9774c 100644 (file)
@@ -57,8 +57,6 @@ struct bpf_cgroup_link {
        enum bpf_attach_type type;
 };
 
-extern const struct bpf_link_ops bpf_cgroup_link_lops;
-
 struct bpf_prog_list {
        struct list_head node;
        struct bpf_prog *prog;
index 875d1f0af803987483d766253134a0359d1d2c1f..c07b1d2f38245e0b72aa4a200584abb988103670 100644 (file)
@@ -1026,9 +1026,11 @@ extern const struct file_operations bpf_prog_fops;
        extern const struct bpf_verifier_ops _name ## _verifier_ops;
 #define BPF_MAP_TYPE(_id, _ops) \
        extern const struct bpf_map_ops _ops;
+#define BPF_LINK_TYPE(_id, _name)
 #include <linux/bpf_types.h>
 #undef BPF_PROG_TYPE
 #undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 
 extern const struct bpf_prog_ops bpf_offload_prog_ops;
 extern const struct bpf_verifier_ops tc_cls_act_analyzer_ops;
@@ -1086,6 +1088,7 @@ int bpf_prog_new_fd(struct bpf_prog *prog);
 struct bpf_link {
        atomic64_t refcnt;
        u32 id;
+       enum bpf_link_type type;
        const struct bpf_link_ops *ops;
        struct bpf_prog *prog;
        struct work_struct work;
@@ -1103,9 +1106,12 @@ struct bpf_link_ops {
        void (*dealloc)(struct bpf_link *link);
        int (*update_prog)(struct bpf_link *link, struct bpf_prog *new_prog,
                           struct bpf_prog *old_prog);
+       void (*show_fdinfo)(const struct bpf_link *link, struct seq_file *seq);
+       int (*fill_link_info)(const struct bpf_link *link,
+                             struct bpf_link_info *info);
 };
 
-void bpf_link_init(struct bpf_link *link,
+void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
                   const struct bpf_link_ops *ops, struct bpf_prog *prog);
 int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer);
 int bpf_link_settle(struct bpf_link_primer *primer);
index ba0c2d56f8a323ade0e95bfcfe6901ce77b3a05f..8345cdf553b8d78c29fa0cfdefcdc273099245ab 100644 (file)
@@ -118,3 +118,9 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_STACK, stack_map_ops)
 #if defined(CONFIG_BPF_JIT)
 BPF_MAP_TYPE(BPF_MAP_TYPE_STRUCT_OPS, bpf_struct_ops_map_ops)
 #endif
+
+BPF_LINK_TYPE(BPF_LINK_TYPE_RAW_TRACEPOINT, raw_tracepoint)
+BPF_LINK_TYPE(BPF_LINK_TYPE_TRACING, tracing)
+#ifdef CONFIG_CGROUP_BPF
+BPF_LINK_TYPE(BPF_LINK_TYPE_CGROUP, cgroup)
+#endif
index 7e6541fceade84cc8c9a7545e1f47fb92d5e5e9d..0eccafae55bbc1b589a22e6308cbc4557d04f74a 100644 (file)
@@ -222,6 +222,15 @@ enum bpf_attach_type {
 
 #define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
 
+enum bpf_link_type {
+       BPF_LINK_TYPE_UNSPEC = 0,
+       BPF_LINK_TYPE_RAW_TRACEPOINT = 1,
+       BPF_LINK_TYPE_TRACING = 2,
+       BPF_LINK_TYPE_CGROUP = 3,
+
+       MAX_BPF_LINK_TYPE,
+};
+
 /* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
  *
  * NONE(default): No further bpf programs allowed in the subtree.
@@ -3612,6 +3621,25 @@ struct bpf_btf_info {
        __u32 id;
 } __attribute__((aligned(8)));
 
+struct bpf_link_info {
+       __u32 type;
+       __u32 id;
+       __u32 prog_id;
+       union {
+               struct {
+                       __aligned_u64 tp_name; /* in/out: tp_name buffer ptr */
+                       __u32 tp_name_len;     /* in/out: tp_name buffer len */
+               } raw_tracepoint;
+               struct {
+                       __u32 attach_type;
+               } tracing;
+               struct {
+                       __u64 cgroup_id;
+                       __u32 attach_type;
+               } cgroup;
+       };
+} __attribute__((aligned(8)));
+
 /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
  * by user and intended to be used by socket (e.g. to bind to, depends on
  * attach attach type).
index d65c6912bdaf66d6d1f0bde1ea759fde2928e2d4..a2cfba89a8e1d780b93d9a6c723b7363059e8486 100644 (file)
@@ -3482,6 +3482,7 @@ extern char __weak __stop_BTF[];
 extern struct btf *btf_vmlinux;
 
 #define BPF_MAP_TYPE(_id, _ops)
+#define BPF_LINK_TYPE(_id, _name)
 static union {
        struct bpf_ctx_convert {
 #define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
@@ -3508,6 +3509,7 @@ static u8 bpf_ctx_convert_map[] = {
        0, /* avoid empty array */
 };
 #undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 
 static const struct btf_member *
 btf_get_prog_ctx_type(struct bpf_verifier_log *log, struct btf *btf,
index 1bdf37fca8791a5e2491efe7c489a899881faf04..5c0e964105ac9070d284d160da7b43305ec8afda 100644 (file)
@@ -833,10 +833,48 @@ static void bpf_cgroup_link_dealloc(struct bpf_link *link)
        kfree(cg_link);
 }
 
-const struct bpf_link_ops bpf_cgroup_link_lops = {
+static void bpf_cgroup_link_show_fdinfo(const struct bpf_link *link,
+                                       struct seq_file *seq)
+{
+       struct bpf_cgroup_link *cg_link =
+               container_of(link, struct bpf_cgroup_link, link);
+       u64 cg_id = 0;
+
+       mutex_lock(&cgroup_mutex);
+       if (cg_link->cgroup)
+               cg_id = cgroup_id(cg_link->cgroup);
+       mutex_unlock(&cgroup_mutex);
+
+       seq_printf(seq,
+                  "cgroup_id:\t%llu\n"
+                  "attach_type:\t%d\n",
+                  cg_id,
+                  cg_link->type);
+}
+
+static int bpf_cgroup_link_fill_link_info(const struct bpf_link *link,
+                                         struct bpf_link_info *info)
+{
+       struct bpf_cgroup_link *cg_link =
+               container_of(link, struct bpf_cgroup_link, link);
+       u64 cg_id = 0;
+
+       mutex_lock(&cgroup_mutex);
+       if (cg_link->cgroup)
+               cg_id = cgroup_id(cg_link->cgroup);
+       mutex_unlock(&cgroup_mutex);
+
+       info->cgroup.cgroup_id = cg_id;
+       info->cgroup.attach_type = cg_link->type;
+       return 0;
+}
+
+static const struct bpf_link_ops bpf_cgroup_link_lops = {
        .release = bpf_cgroup_link_release,
        .dealloc = bpf_cgroup_link_dealloc,
        .update_prog = cgroup_bpf_replace,
+       .show_fdinfo = bpf_cgroup_link_show_fdinfo,
+       .fill_link_info = bpf_cgroup_link_fill_link_info,
 };
 
 int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
@@ -858,7 +896,8 @@ int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
                err = -ENOMEM;
                goto out_put_cgroup;
        }
-       bpf_link_init(&link->link, &bpf_cgroup_link_lops, prog);
+       bpf_link_init(&link->link, BPF_LINK_TYPE_CGROUP, &bpf_cgroup_link_lops,
+                     prog);
        link->cgroup = cgrp;
        link->type = attr->link_create.attach_type;
 
index 1c213a7305022bfcc95bcd2e43f1ff93abc74eab..d23c04cbe14f7e488f2b9d6ca881da67215ab336 100644 (file)
@@ -51,9 +51,11 @@ static const struct bpf_map_ops * const bpf_map_types[] = {
 #define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type)
 #define BPF_MAP_TYPE(_id, _ops) \
        [_id] = &_ops,
+#define BPF_LINK_TYPE(_id, _name)
 #include <linux/bpf_types.h>
 #undef BPF_PROG_TYPE
 #undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 };
 
 /*
@@ -1548,9 +1550,11 @@ static const struct bpf_prog_ops * const bpf_prog_types[] = {
 #define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
        [_id] = & _name ## _prog_ops,
 #define BPF_MAP_TYPE(_id, _ops)
+#define BPF_LINK_TYPE(_id, _name)
 #include <linux/bpf_types.h>
 #undef BPF_PROG_TYPE
 #undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 };
 
 static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog)
@@ -2183,10 +2187,11 @@ static int bpf_obj_get(const union bpf_attr *attr)
                                attr->file_flags);
 }
 
-void bpf_link_init(struct bpf_link *link,
+void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
                   const struct bpf_link_ops *ops, struct bpf_prog *prog)
 {
        atomic64_set(&link->refcnt, 1);
+       link->type = type;
        link->id = 0;
        link->ops = ops;
        link->prog = prog;
@@ -2266,27 +2271,23 @@ static int bpf_link_release(struct inode *inode, struct file *filp)
        return 0;
 }
 
-#ifdef CONFIG_PROC_FS
-static const struct bpf_link_ops bpf_raw_tp_lops;
-static const struct bpf_link_ops bpf_tracing_link_lops;
+#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type)
+#define BPF_MAP_TYPE(_id, _ops)
+#define BPF_LINK_TYPE(_id, _name) [_id] = #_name,
+static const char *bpf_link_type_strs[] = {
+       [BPF_LINK_TYPE_UNSPEC] = "<invalid>",
+#include <linux/bpf_types.h>
+};
+#undef BPF_PROG_TYPE
+#undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 
+#ifdef CONFIG_PROC_FS
 static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
 {
        const struct bpf_link *link = filp->private_data;
        const struct bpf_prog *prog = link->prog;
        char prog_tag[sizeof(prog->tag) * 2 + 1] = { };
-       const char *link_type;
-
-       if (link->ops == &bpf_raw_tp_lops)
-               link_type = "raw_tracepoint";
-       else if (link->ops == &bpf_tracing_link_lops)
-               link_type = "tracing";
-#ifdef CONFIG_CGROUP_BPF
-       else if (link->ops == &bpf_cgroup_link_lops)
-               link_type = "cgroup";
-#endif
-       else
-               link_type = "unknown";
 
        bin2hex(prog_tag, prog->tag, sizeof(prog->tag));
        seq_printf(m,
@@ -2294,10 +2295,12 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
                   "link_id:\t%u\n"
                   "prog_tag:\t%s\n"
                   "prog_id:\t%u\n",
-                  link_type,
+                  bpf_link_type_strs[link->type],
                   link->id,
                   prog_tag,
                   prog->aux->id);
+       if (link->ops->show_fdinfo)
+               link->ops->show_fdinfo(link, m);
 }
 #endif
 
@@ -2403,6 +2406,7 @@ struct bpf_link *bpf_link_get_from_fd(u32 ufd)
 
 struct bpf_tracing_link {
        struct bpf_link link;
+       enum bpf_attach_type attach_type;
 };
 
 static void bpf_tracing_link_release(struct bpf_link *link)
@@ -2418,9 +2422,33 @@ static void bpf_tracing_link_dealloc(struct bpf_link *link)
        kfree(tr_link);
 }
 
+static void bpf_tracing_link_show_fdinfo(const struct bpf_link *link,
+                                        struct seq_file *seq)
+{
+       struct bpf_tracing_link *tr_link =
+               container_of(link, struct bpf_tracing_link, link);
+
+       seq_printf(seq,
+                  "attach_type:\t%d\n",
+                  tr_link->attach_type);
+}
+
+static int bpf_tracing_link_fill_link_info(const struct bpf_link *link,
+                                          struct bpf_link_info *info)
+{
+       struct bpf_tracing_link *tr_link =
+               container_of(link, struct bpf_tracing_link, link);
+
+       info->tracing.attach_type = tr_link->attach_type;
+
+       return 0;
+}
+
 static const struct bpf_link_ops bpf_tracing_link_lops = {
        .release = bpf_tracing_link_release,
        .dealloc = bpf_tracing_link_dealloc,
+       .show_fdinfo = bpf_tracing_link_show_fdinfo,
+       .fill_link_info = bpf_tracing_link_fill_link_info,
 };
 
 static int bpf_tracing_prog_attach(struct bpf_prog *prog)
@@ -2460,7 +2488,9 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
                err = -ENOMEM;
                goto out_put_prog;
        }
-       bpf_link_init(&link->link, &bpf_tracing_link_lops, prog);
+       bpf_link_init(&link->link, BPF_LINK_TYPE_TRACING,
+                     &bpf_tracing_link_lops, prog);
+       link->attach_type = prog->expected_attach_type;
 
        err = bpf_link_prime(&link->link, &link_primer);
        if (err) {
@@ -2502,9 +2532,56 @@ static void bpf_raw_tp_link_dealloc(struct bpf_link *link)
        kfree(raw_tp);
 }
 
+static void bpf_raw_tp_link_show_fdinfo(const struct bpf_link *link,
+                                       struct seq_file *seq)
+{
+       struct bpf_raw_tp_link *raw_tp_link =
+               container_of(link, struct bpf_raw_tp_link, link);
+
+       seq_printf(seq,
+                  "tp_name:\t%s\n",
+                  raw_tp_link->btp->tp->name);
+}
+
+static int bpf_raw_tp_link_fill_link_info(const struct bpf_link *link,
+                                         struct bpf_link_info *info)
+{
+       struct bpf_raw_tp_link *raw_tp_link =
+               container_of(link, struct bpf_raw_tp_link, link);
+       char __user *ubuf = u64_to_user_ptr(info->raw_tracepoint.tp_name);
+       const char *tp_name = raw_tp_link->btp->tp->name;
+       u32 ulen = info->raw_tracepoint.tp_name_len;
+       size_t tp_len = strlen(tp_name);
+
+       if (ulen && !ubuf)
+               return -EINVAL;
+
+       info->raw_tracepoint.tp_name_len = tp_len + 1;
+
+       if (!ubuf)
+               return 0;
+
+       if (ulen >= tp_len + 1) {
+               if (copy_to_user(ubuf, tp_name, tp_len + 1))
+                       return -EFAULT;
+       } else {
+               char zero = '\0';
+
+               if (copy_to_user(ubuf, tp_name, ulen - 1))
+                       return -EFAULT;
+               if (put_user(zero, ubuf + ulen - 1))
+                       return -EFAULT;
+               return -ENOSPC;
+       }
+
+       return 0;
+}
+
 static const struct bpf_link_ops bpf_raw_tp_link_lops = {
        .release = bpf_raw_tp_link_release,
        .dealloc = bpf_raw_tp_link_dealloc,
+       .show_fdinfo = bpf_raw_tp_link_show_fdinfo,
+       .fill_link_info = bpf_raw_tp_link_fill_link_info,
 };
 
 #define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd
@@ -2570,7 +2647,8 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
                err = -ENOMEM;
                goto out_put_btp;
        }
-       bpf_link_init(&link->link, &bpf_raw_tp_link_lops, prog);
+       bpf_link_init(&link->link, BPF_LINK_TYPE_RAW_TRACEPOINT,
+                     &bpf_raw_tp_link_lops, prog);
        link->btp = btp;
 
        err = bpf_link_prime(&link->link, &link_primer);
@@ -3366,6 +3444,42 @@ static int bpf_btf_get_info_by_fd(struct btf *btf,
        return btf_get_info_by_fd(btf, attr, uattr);
 }
 
+static int bpf_link_get_info_by_fd(struct bpf_link *link,
+                                 const union bpf_attr *attr,
+                                 union bpf_attr __user *uattr)
+{
+       struct bpf_link_info __user *uinfo = u64_to_user_ptr(attr->info.info);
+       struct bpf_link_info info;
+       u32 info_len = attr->info.info_len;
+       int err;
+
+       err = bpf_check_uarg_tail_zero(uinfo, sizeof(info), info_len);
+       if (err)
+               return err;
+       info_len = min_t(u32, sizeof(info), info_len);
+
+       memset(&info, 0, sizeof(info));
+       if (copy_from_user(&info, uinfo, info_len))
+               return -EFAULT;
+
+       info.type = link->type;
+       info.id = link->id;
+       info.prog_id = link->prog->aux->id;
+
+       if (link->ops->fill_link_info) {
+               err = link->ops->fill_link_info(link, &info);
+               if (err)
+                       return err;
+       }
+
+       if (copy_to_user(uinfo, &info, info_len) ||
+           put_user(info_len, &uattr->info.info_len))
+               return -EFAULT;
+
+       return 0;
+}
+
+
 #define BPF_OBJ_GET_INFO_BY_FD_LAST_FIELD info.info
 
 static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
@@ -3390,6 +3504,9 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
                                             uattr);
        else if (f.file->f_op == &btf_fops)
                err = bpf_btf_get_info_by_fd(f.file->private_data, attr, uattr);
+       else if (f.file->f_op == &bpf_link_fops)
+               err = bpf_link_get_info_by_fd(f.file->private_data,
+                                             attr, uattr);
        else
                err = -EINVAL;
 
index 91728e0f27ebab39dceb080a9869a66499253e84..2b337e32aa9482a2be6b130efac582b53137953f 100644 (file)
@@ -28,9 +28,11 @@ static const struct bpf_verifier_ops * const bpf_verifier_ops[] = {
 #define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
        [_id] = & _name ## _verifier_ops,
 #define BPF_MAP_TYPE(_id, _ops)
+#define BPF_LINK_TYPE(_id, _name)
 #include <linux/bpf_types.h>
 #undef BPF_PROG_TYPE
 #undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 };
 
 /* bpf_check() is a static code analyzer that walks eBPF program
index 4a6c47f3febea1dddcfda17c8e3ee868e424c5cc..0eccafae55bbc1b589a22e6308cbc4557d04f74a 100644 (file)
@@ -113,6 +113,8 @@ enum bpf_cmd {
        BPF_MAP_DELETE_BATCH,
        BPF_LINK_CREATE,
        BPF_LINK_UPDATE,
+       BPF_LINK_GET_FD_BY_ID,
+       BPF_LINK_GET_NEXT_ID,
 };
 
 enum bpf_map_type {
@@ -220,6 +222,15 @@ enum bpf_attach_type {
 
 #define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
 
+enum bpf_link_type {
+       BPF_LINK_TYPE_UNSPEC = 0,
+       BPF_LINK_TYPE_RAW_TRACEPOINT = 1,
+       BPF_LINK_TYPE_TRACING = 2,
+       BPF_LINK_TYPE_CGROUP = 3,
+
+       MAX_BPF_LINK_TYPE,
+};
+
 /* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
  *
  * NONE(default): No further bpf programs allowed in the subtree.
@@ -523,6 +534,7 @@ union bpf_attr {
                        __u32           prog_id;
                        __u32           map_id;
                        __u32           btf_id;
+                       __u32           link_id;
                };
                __u32           next_id;
                __u32           open_flags;
@@ -3609,6 +3621,25 @@ struct bpf_btf_info {
        __u32 id;
 } __attribute__((aligned(8)));
 
+struct bpf_link_info {
+       __u32 type;
+       __u32 id;
+       __u32 prog_id;
+       union {
+               struct {
+                       __aligned_u64 tp_name; /* in/out: tp_name buffer ptr */
+                       __u32 tp_name_len;     /* in/out: tp_name buffer len */
+               } raw_tracepoint;
+               struct {
+                       __u32 attach_type;
+               } tracing;
+               struct {
+                       __u64 cgroup_id;
+                       __u32 attach_type;
+               } cgroup;
+       };
+} __attribute__((aligned(8)));
+
 /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
  * by user and intended to be used by socket (e.g. to bind to, depends on
  * attach attach type).