libbpf: Add bpf_cookie support to bpf_link_create() API
authorAndrii Nakryiko <andrii@kernel.org>
Sun, 15 Aug 2021 07:06:03 +0000 (00:06 -0700)
committerDaniel Borkmann <daniel@iogearbox.net>
Mon, 16 Aug 2021 22:45:08 +0000 (00:45 +0200)
Add ability to specify bpf_cookie value when creating BPF perf link with
bpf_link_create() low-level API.

Given BPF_LINK_CREATE command is growing and keeps getting new fields that are
specific to the type of BPF_LINK, extend libbpf side of bpf_link_create() API
and corresponding OPTS struct to accomodate such changes. Add extra checks to
prevent using incompatible/unexpected combinations of fields.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20210815070609.987780-11-andrii@kernel.org
tools/lib/bpf/bpf.c
tools/lib/bpf/bpf.h
tools/lib/bpf/libbpf_internal.h

index 86dcac4..2401fad 100644 (file)
@@ -684,8 +684,13 @@ int bpf_link_create(int prog_fd, int target_fd,
        iter_info_len = OPTS_GET(opts, iter_info_len, 0);
        target_btf_id = OPTS_GET(opts, target_btf_id, 0);
 
-       if (iter_info_len && target_btf_id)
-               return libbpf_err(-EINVAL);
+       /* validate we don't have unexpected combinations of non-zero fields */
+       if (iter_info_len || target_btf_id) {
+               if (iter_info_len && target_btf_id)
+                       return libbpf_err(-EINVAL);
+               if (!OPTS_ZEROED(opts, target_btf_id))
+                       return libbpf_err(-EINVAL);
+       }
 
        memset(&attr, 0, sizeof(attr));
        attr.link_create.prog_fd = prog_fd;
@@ -693,14 +698,27 @@ int bpf_link_create(int prog_fd, int target_fd,
        attr.link_create.attach_type = attach_type;
        attr.link_create.flags = OPTS_GET(opts, flags, 0);
 
-       if (iter_info_len) {
-               attr.link_create.iter_info =
-                       ptr_to_u64(OPTS_GET(opts, iter_info, (void *)0));
-               attr.link_create.iter_info_len = iter_info_len;
-       } else if (target_btf_id) {
+       if (target_btf_id) {
                attr.link_create.target_btf_id = target_btf_id;
+               goto proceed;
        }
 
+       switch (attach_type) {
+       case BPF_TRACE_ITER:
+               attr.link_create.iter_info = ptr_to_u64(OPTS_GET(opts, iter_info, (void *)0));
+               attr.link_create.iter_info_len = iter_info_len;
+               break;
+       case BPF_PERF_EVENT:
+               attr.link_create.perf_event.bpf_cookie = OPTS_GET(opts, perf_event.bpf_cookie, 0);
+               if (!OPTS_ZEROED(opts, perf_event))
+                       return libbpf_err(-EINVAL);
+               break;
+       default:
+               if (!OPTS_ZEROED(opts, flags))
+                       return libbpf_err(-EINVAL);
+               break;
+       }
+proceed:
        fd = sys_bpf(BPF_LINK_CREATE, &attr, sizeof(attr));
        return libbpf_err_errno(fd);
 }
index 4f758f8..6fffb3c 100644 (file)
@@ -177,8 +177,14 @@ struct bpf_link_create_opts {
        union bpf_iter_link_info *iter_info;
        __u32 iter_info_len;
        __u32 target_btf_id;
+       union {
+               struct {
+                       __u64 bpf_cookie;
+               } perf_event;
+       };
+       size_t :0;
 };
-#define bpf_link_create_opts__last_field target_btf_id
+#define bpf_link_create_opts__last_field perf_event
 
 LIBBPF_API int bpf_link_create(int prog_fd, int target_fd,
                               enum bpf_attach_type attach_type,
index f7b691d..533b021 100644 (file)
@@ -196,6 +196,17 @@ void *libbpf_add_mem(void **data, size_t *cap_cnt, size_t elem_sz,
                     size_t cur_cnt, size_t max_cnt, size_t add_cnt);
 int libbpf_ensure_mem(void **data, size_t *cap_cnt, size_t elem_sz, size_t need_cnt);
 
+static inline bool libbpf_is_mem_zeroed(const char *p, ssize_t len)
+{
+       while (len > 0) {
+               if (*p)
+                       return false;
+               p++;
+               len--;
+       }
+       return true;
+}
+
 static inline bool libbpf_validate_opts(const char *opts,
                                        size_t opts_sz, size_t user_sz,
                                        const char *type_name)
@@ -204,16 +215,9 @@ static inline bool libbpf_validate_opts(const char *opts,
                pr_warn("%s size (%zu) is too small\n", type_name, user_sz);
                return false;
        }
-       if (user_sz > opts_sz) {
-               size_t i;
-
-               for (i = opts_sz; i < user_sz; i++) {
-                       if (opts[i]) {
-                               pr_warn("%s has non-zero extra bytes\n",
-                                       type_name);
-                               return false;
-                       }
-               }
+       if (!libbpf_is_mem_zeroed(opts + opts_sz, (ssize_t)user_sz - opts_sz)) {
+               pr_warn("%s has non-zero extra bytes\n", type_name);
+               return false;
        }
        return true;
 }
@@ -233,6 +237,14 @@ static inline bool libbpf_validate_opts(const char *opts,
                        (opts)->field = value;  \
        } while (0)
 
+#define OPTS_ZEROED(opts, last_nonzero_field)                                \
+({                                                                           \
+       ssize_t __off = offsetofend(typeof(*(opts)), last_nonzero_field);     \
+       !(opts) || libbpf_is_mem_zeroed((const void *)opts + __off,           \
+                                       (opts)->sz - __off);                  \
+})
+
+
 int parse_cpu_mask_str(const char *s, bool **mask, int *mask_sz);
 int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz);
 int libbpf__load_raw_btf(const char *raw_types, size_t types_len,