libbpf: add bpf_object__open_{file, mem} w/ extensible opts
authorAndrii Nakryiko <andriin@fb.com>
Fri, 4 Oct 2019 22:40:35 +0000 (15:40 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Sun, 6 Oct 2019 01:09:47 +0000 (18:09 -0700)
Add new set of bpf_object__open APIs using new approach to optional
parameters extensibility allowing simpler ABI compatibility approach.

This patch demonstrates an approach to implementing libbpf APIs that
makes it easy to extend existing APIs with extra optional parameters in
such a way, that ABI compatibility is preserved without having to do
symbol versioning and generating lots of boilerplate code to handle it.
To facilitate succinct code for working with options, add OPTS_VALID,
OPTS_HAS, and OPTS_GET macros that hide all the NULL, size, and zero
checks.

Additionally, newly added libbpf APIs are encouraged to follow similar
pattern of having all mandatory parameters as formal function parameters
and always have optional (NULL-able) xxx_opts struct, which should
always have real struct size as a first field and the rest would be
optional parameters added over time, which tune the behavior of existing
API, if specified by user.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf.h
tools/lib/bpf/libbpf.map
tools/lib/bpf/libbpf_internal.h

index 024334b29b54a7d35309629163ef5d8a0bc8aeb9..d471d33400ae9fda6ea514b4a45db12c99cee050 100644 (file)
@@ -505,7 +505,8 @@ static __u32 get_kernel_version(void)
 
 static struct bpf_object *bpf_object__new(const char *path,
                                          const void *obj_buf,
-                                         size_t obj_buf_sz)
+                                         size_t obj_buf_sz,
+                                         const char *obj_name)
 {
        struct bpf_object *obj;
        char *end;
@@ -517,11 +518,17 @@ static struct bpf_object *bpf_object__new(const char *path,
        }
 
        strcpy(obj->path, path);
-       /* Using basename() GNU version which doesn't modify arg. */
-       strncpy(obj->name, basename((void *)path), sizeof(obj->name) - 1);
-       end = strchr(obj->name, '.');
-       if (end)
-               *end = 0;
+       if (obj_name) {
+               strncpy(obj->name, obj_name, sizeof(obj->name) - 1);
+               obj->name[sizeof(obj->name) - 1] = 0;
+       } else {
+               /* Using basename() GNU version which doesn't modify arg. */
+               strncpy(obj->name, basename((void *)path),
+                       sizeof(obj->name) - 1);
+               end = strchr(obj->name, '.');
+               if (end)
+                       *end = 0;
+       }
 
        obj->efile.fd = -1;
        /*
@@ -3547,7 +3554,7 @@ bpf_object__load_progs(struct bpf_object *obj, int log_level)
 
 static struct bpf_object *
 __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
-                  int flags)
+                  const char *obj_name, int flags)
 {
        struct bpf_object *obj;
        int err;
@@ -3557,7 +3564,7 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
                return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
        }
 
-       obj = bpf_object__new(path, obj_buf, obj_buf_sz);
+       obj = bpf_object__new(path, obj_buf, obj_buf_sz, obj_name);
        if (IS_ERR(obj))
                return obj;
 
@@ -3583,7 +3590,7 @@ __bpf_object__open_xattr(struct bpf_object_open_attr *attr, int flags)
 
        pr_debug("loading %s\n", attr->file);
 
-       return __bpf_object__open(attr->file, NULL, 0, flags);
+       return __bpf_object__open(attr->file, NULL, 0, NULL, flags);
 }
 
 struct bpf_object *bpf_object__open_xattr(struct bpf_object_open_attr *attr)
@@ -3601,25 +3608,67 @@ struct bpf_object *bpf_object__open(const char *path)
        return bpf_object__open_xattr(&attr);
 }
 
-struct bpf_object *bpf_object__open_buffer(void *obj_buf,
-                                          size_t obj_buf_sz,
-                                          const char *name)
+struct bpf_object *
+bpf_object__open_file(const char *path, struct bpf_object_open_opts *opts)
+{
+       const char *obj_name;
+       bool relaxed_maps;
+
+       if (!OPTS_VALID(opts, bpf_object_open_opts))
+               return ERR_PTR(-EINVAL);
+       if (!path)
+               return ERR_PTR(-EINVAL);
+
+       pr_debug("loading %s\n", path);
+
+       obj_name = OPTS_GET(opts, object_name, path);
+       relaxed_maps = OPTS_GET(opts, relaxed_maps, false);
+       return __bpf_object__open(path, NULL, 0, obj_name,
+                                 relaxed_maps ? MAPS_RELAX_COMPAT : 0);
+}
+
+struct bpf_object *
+bpf_object__open_mem(const void *obj_buf, size_t obj_buf_sz,
+                    struct bpf_object_open_opts *opts)
 {
        char tmp_name[64];
+       const char *obj_name;
+       bool relaxed_maps;
 
-       /* param validation */
-       if (!obj_buf || obj_buf_sz <= 0)
-               return NULL;
+       if (!OPTS_VALID(opts, bpf_object_open_opts))
+               return ERR_PTR(-EINVAL);
+       if (!obj_buf || obj_buf_sz == 0)
+               return ERR_PTR(-EINVAL);
 
-       if (!name) {
+       obj_name = OPTS_GET(opts, object_name, NULL);
+       if (!obj_name) {
                snprintf(tmp_name, sizeof(tmp_name), "%lx-%lx",
                         (unsigned long)obj_buf,
                         (unsigned long)obj_buf_sz);
-               name = tmp_name;
+               obj_name = tmp_name;
        }
-       pr_debug("loading object '%s' from buffer\n", name);
+       pr_debug("loading object '%s' from buffer\n", obj_name);
+
+       relaxed_maps = OPTS_GET(opts, relaxed_maps, false);
+       return __bpf_object__open(obj_name, obj_buf, obj_buf_sz, obj_name,
+                                 relaxed_maps ? MAPS_RELAX_COMPAT : 0);
+}
+
+struct bpf_object *
+bpf_object__open_buffer(const void *obj_buf, size_t obj_buf_sz,
+                       const char *name)
+{
+       LIBBPF_OPTS(bpf_object_open_opts, opts,
+               .object_name = name,
+               /* wrong default, but backwards-compatible */
+               .relaxed_maps = true,
+       );
+
+       /* returning NULL is wrong, but backwards-compatible */
+       if (!obj_buf || obj_buf_sz == 0)
+               return NULL;
 
-       return __bpf_object__open(name, obj_buf, obj_buf_sz, true);
+       return bpf_object__open_mem(obj_buf, obj_buf_sz, &opts);
 }
 
 int bpf_object__unload(struct bpf_object *obj)
index 2905dffd70b21f62c4b2f52667491d3be4d626fa..667e6853e51f0c79432cf35a1401a32ebaf4b6a4 100644 (file)
@@ -67,12 +67,52 @@ struct bpf_object_open_attr {
        enum bpf_prog_type prog_type;
 };
 
+/* Helper macro to declare and initialize libbpf options struct
+ *
+ * This dance with uninitialized declaration, followed by memset to zero,
+ * followed by assignment using compound literal syntax is done to preserve
+ * ability to use a nice struct field initialization syntax and **hopefully**
+ * have all the padding bytes initialized to zero. It's not guaranteed though,
+ * when copying literal, that compiler won't copy garbage in literal's padding
+ * bytes, but that's the best way I've found and it seems to work in practice.
+ */
+#define LIBBPF_OPTS(TYPE, NAME, ...)                                       \
+       struct TYPE NAME;                                                   \
+       memset(&NAME, 0, sizeof(struct TYPE));                              \
+       NAME = (struct TYPE) {                                              \
+               .sz = sizeof(struct TYPE),                                  \
+               __VA_ARGS__                                                 \
+       }
+
+struct bpf_object_open_opts {
+       /* size of this struct, for forward/backward compatiblity */
+       size_t sz;
+       /* object name override, if provided:
+        * - for object open from file, this will override setting object
+        *   name from file path's base name;
+        * - for object open from memory buffer, this will specify an object
+        *   name and will override default "<addr>-<buf-size>" name;
+        */
+       const char *object_name;
+       /* parse map definitions non-strictly, allowing extra attributes/data */
+       bool relaxed_maps;
+};
+#define bpf_object_open_opts__last_field relaxed_maps
+
 LIBBPF_API struct bpf_object *bpf_object__open(const char *path);
 LIBBPF_API struct bpf_object *
+bpf_object__open_file(const char *path, struct bpf_object_open_opts *opts);
+LIBBPF_API struct bpf_object *
+bpf_object__open_mem(const void *obj_buf, size_t obj_buf_sz,
+                    struct bpf_object_open_opts *opts);
+
+/* deprecated bpf_object__open variants */
+LIBBPF_API struct bpf_object *
+bpf_object__open_buffer(const void *obj_buf, size_t obj_buf_sz,
+                       const char *name);
+LIBBPF_API struct bpf_object *
 bpf_object__open_xattr(struct bpf_object_open_attr *attr);
-LIBBPF_API struct bpf_object *bpf_object__open_buffer(void *obj_buf,
-                                                     size_t obj_buf_sz,
-                                                     const char *name);
+
 int bpf_object__section_size(const struct bpf_object *obj, const char *name,
                             __u32 *size);
 int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
index 8d10ca03d78d741ed3e16eb208b4c626baabf309..4d241fd92dd476a99bf3a04faedd6f95424b4ea9 100644 (file)
@@ -192,4 +192,7 @@ LIBBPF_0.0.5 {
 } LIBBPF_0.0.4;
 
 LIBBPF_0.0.6 {
+       global:
+               bpf_object__open_file;
+               bpf_object__open_mem;
 } LIBBPF_0.0.5;
index 2e83a34f8c79a7e018a65ad6cdcd0ea3dc5b4764..f51444fc7eb7261eeb28b58c5458a33fe25de521 100644 (file)
@@ -47,6 +47,38 @@ do {                         \
 #define pr_info(fmt, ...)      __pr(LIBBPF_INFO, fmt, ##__VA_ARGS__)
 #define pr_debug(fmt, ...)     __pr(LIBBPF_DEBUG, fmt, ##__VA_ARGS__)
 
+static inline bool libbpf_validate_opts(const char *opts,
+                                       size_t opts_sz, size_t user_sz,
+                                       const char *type_name)
+{
+       if (user_sz < sizeof(size_t)) {
+               pr_warning("%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_warning("%s has non-zero extra bytes",
+                                          type_name);
+                               return false;
+                       }
+               }
+       }
+       return true;
+}
+
+#define OPTS_VALID(opts, type)                                               \
+       (!(opts) || libbpf_validate_opts((const char *)opts,                  \
+                                        offsetofend(struct type,             \
+                                                    type##__last_field),     \
+                                        (opts)->sz, #type))
+#define OPTS_HAS(opts, field) \
+       ((opts) && opts->sz >= offsetofend(typeof(*(opts)), field))
+#define OPTS_GET(opts, field, fallback_value) \
+       (OPTS_HAS(opts, field) ? (opts)->field : fallback_value)
+
 int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
                         const char *str_sec, size_t str_len);