libbpf: Add support for bpf_link-based cgroup attachment
authorAndrii Nakryiko <andriin@fb.com>
Mon, 30 Mar 2020 03:00:00 +0000 (20:00 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 31 Mar 2020 00:36:41 +0000 (17:36 -0700)
Add bpf_program__attach_cgroup(), which uses BPF_LINK_CREATE subcommand to
create an FD-based kernel bpf_link. Also add low-level bpf_link_create() API.

If expected_attach_type is not specified explicitly with
bpf_program__set_expected_attach_type(), libbpf will try to determine proper
attach type from BPF program's section definition.

Also add support for bpf_link's underlying BPF program replacement:
  - unconditional through high-level bpf_link__update_program() API;
  - cmpxchg-like with specifying expected current BPF program through
    low-level bpf_link_update() API.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20200330030001.2312810-4-andriin@fb.com
tools/include/uapi/linux/bpf.h
tools/lib/bpf/bpf.c
tools/lib/bpf/bpf.h
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf.h
tools/lib/bpf/libbpf.map

index 37dffe5..2e29a67 100644 (file)
@@ -112,6 +112,7 @@ enum bpf_cmd {
        BPF_MAP_UPDATE_BATCH,
        BPF_MAP_DELETE_BATCH,
        BPF_LINK_CREATE,
+       BPF_LINK_UPDATE,
 };
 
 enum bpf_map_type {
@@ -577,6 +578,17 @@ union bpf_attr {
                __u32           attach_type;    /* attach type */
                __u32           flags;          /* extra flags */
        } link_create;
+
+       struct { /* struct used by BPF_LINK_UPDATE command */
+               __u32           link_fd;        /* link fd */
+               /* new program fd to update link with */
+               __u32           new_prog_fd;
+               __u32           flags;          /* extra flags */
+               /* expected link's program fd; is specified only if
+                * BPF_F_REPLACE flag is set in flags */
+               __u32           old_prog_fd;
+       } link_update;
+
 } __attribute__((aligned(8)));
 
 /* The description below is an attempt at providing documentation to eBPF
index 7322017..5cc1b07 100644 (file)
@@ -585,6 +585,40 @@ int bpf_prog_detach2(int prog_fd, int target_fd, enum bpf_attach_type type)
        return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
 }
 
+int bpf_link_create(int prog_fd, int target_fd,
+                   enum bpf_attach_type attach_type,
+                   const struct bpf_link_create_opts *opts)
+{
+       union bpf_attr attr;
+
+       if (!OPTS_VALID(opts, bpf_link_create_opts))
+               return -EINVAL;
+
+       memset(&attr, 0, sizeof(attr));
+       attr.link_create.prog_fd = prog_fd;
+       attr.link_create.target_fd = target_fd;
+       attr.link_create.attach_type = attach_type;
+
+       return sys_bpf(BPF_LINK_CREATE, &attr, sizeof(attr));
+}
+
+int bpf_link_update(int link_fd, int new_prog_fd,
+                   const struct bpf_link_update_opts *opts)
+{
+       union bpf_attr attr;
+
+       if (!OPTS_VALID(opts, bpf_link_update_opts))
+               return -EINVAL;
+
+       memset(&attr, 0, sizeof(attr));
+       attr.link_update.link_fd = link_fd;
+       attr.link_update.new_prog_fd = new_prog_fd;
+       attr.link_update.flags = OPTS_GET(opts, flags, 0);
+       attr.link_update.old_prog_fd = OPTS_GET(opts, old_prog_fd, 0);
+
+       return sys_bpf(BPF_LINK_UPDATE, &attr, sizeof(attr));
+}
+
 int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
                   __u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt)
 {
index b976e77..46d47af 100644 (file)
@@ -168,6 +168,25 @@ LIBBPF_API int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type);
 LIBBPF_API int bpf_prog_detach2(int prog_fd, int attachable_fd,
                                enum bpf_attach_type type);
 
+struct bpf_link_create_opts {
+       size_t sz; /* size of this struct for forward/backward compatibility */
+};
+#define bpf_link_create_opts__last_field sz
+
+LIBBPF_API int bpf_link_create(int prog_fd, int target_fd,
+                              enum bpf_attach_type attach_type,
+                              const struct bpf_link_create_opts *opts);
+
+struct bpf_link_update_opts {
+       size_t sz; /* size of this struct for forward/backward compatibility */
+       __u32 flags;       /* extra flags */
+       __u32 old_prog_fd; /* expected old program FD */
+};
+#define bpf_link_update_opts__last_field old_prog_fd
+
+LIBBPF_API int bpf_link_update(int link_fd, int new_prog_fd,
+                              const struct bpf_link_update_opts *opts);
+
 struct bpf_prog_test_run_attr {
        int prog_fd;
        int repeat;
index 0638e71..ff91742 100644 (file)
@@ -6978,6 +6978,12 @@ struct bpf_link {
        bool disconnected;
 };
 
+/* Replace link's underlying BPF program with the new one */
+int bpf_link__update_program(struct bpf_link *link, struct bpf_program *prog)
+{
+       return bpf_link_update(bpf_link__fd(link), bpf_program__fd(prog), NULL);
+}
+
 /* Release "ownership" of underlying BPF resource (typically, BPF program
  * attached to some BPF hook, e.g., tracepoint, kprobe, etc). Disconnected
  * link, when destructed through bpf_link__destroy() call won't attempt to
@@ -7533,6 +7539,46 @@ static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
        return bpf_program__attach_lsm(prog);
 }
 
+struct bpf_link *
+bpf_program__attach_cgroup(struct bpf_program *prog, int cgroup_fd)
+{
+       const struct bpf_sec_def *sec_def;
+       enum bpf_attach_type attach_type;
+       char errmsg[STRERR_BUFSIZE];
+       struct bpf_link *link;
+       int prog_fd, link_fd;
+
+       prog_fd = bpf_program__fd(prog);
+       if (prog_fd < 0) {
+               pr_warn("program '%s': can't attach before loaded\n",
+                       bpf_program__title(prog, false));
+               return ERR_PTR(-EINVAL);
+       }
+
+       link = calloc(1, sizeof(*link));
+       if (!link)
+               return ERR_PTR(-ENOMEM);
+       link->detach = &bpf_link__detach_fd;
+
+       attach_type = bpf_program__get_expected_attach_type(prog);
+       if (!attach_type) {
+               sec_def = find_sec_def(bpf_program__title(prog, false));
+               if (sec_def)
+                       attach_type = sec_def->attach_type;
+       }
+       link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, NULL);
+       if (link_fd < 0) {
+               link_fd = -errno;
+               free(link);
+               pr_warn("program '%s': failed to attach to cgroup: %s\n",
+                       bpf_program__title(prog, false),
+                       libbpf_strerror_r(link_fd, errmsg, sizeof(errmsg)));
+               return ERR_PTR(link_fd);
+       }
+       link->fd = link_fd;
+       return link;
+}
+
 struct bpf_link *bpf_program__attach(struct bpf_program *prog)
 {
        const struct bpf_sec_def *sec_def;
index 5534872..44df1d3 100644 (file)
@@ -224,6 +224,8 @@ LIBBPF_API int bpf_link__fd(const struct bpf_link *link);
 LIBBPF_API const char *bpf_link__pin_path(const struct bpf_link *link);
 LIBBPF_API int bpf_link__pin(struct bpf_link *link, const char *path);
 LIBBPF_API int bpf_link__unpin(struct bpf_link *link);
+LIBBPF_API int bpf_link__update_program(struct bpf_link *link,
+                                       struct bpf_program *prog);
 LIBBPF_API void bpf_link__disconnect(struct bpf_link *link);
 LIBBPF_API int bpf_link__destroy(struct bpf_link *link);
 
@@ -245,13 +247,17 @@ bpf_program__attach_tracepoint(struct bpf_program *prog,
 LIBBPF_API struct bpf_link *
 bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
                                   const char *tp_name);
-
 LIBBPF_API struct bpf_link *
 bpf_program__attach_trace(struct bpf_program *prog);
 LIBBPF_API struct bpf_link *
 bpf_program__attach_lsm(struct bpf_program *prog);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_cgroup(struct bpf_program *prog, int cgroup_fd);
+
 struct bpf_map;
+
 LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(struct bpf_map *map);
+
 struct bpf_insn;
 
 /*
index eabd3d3..bb88316 100644 (file)
@@ -243,7 +243,11 @@ LIBBPF_0.0.8 {
                bpf_link__pin;
                bpf_link__pin_path;
                bpf_link__unpin;
+               bpf_link__update_program;
+               bpf_link_create;
+               bpf_link_update;
                bpf_map__set_initial_value;
+               bpf_program__attach_cgroup;
                bpf_program__attach_lsm;
                bpf_program__is_lsm;
                bpf_program__set_attach_target;