libbpf: Add BPF object skeleton support
authorAndrii Nakryiko <andriin@fb.com>
Sat, 14 Dec 2019 01:43:36 +0000 (17:43 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Sun, 15 Dec 2019 23:58:05 +0000 (15:58 -0800)
Add new set of APIs, allowing to open/load/attach BPF object through BPF
object skeleton, generated by bpftool for a specific BPF object file. All the
xxx_skeleton() APIs wrap up corresponding bpf_object_xxx() APIs, but
additionally also automate map/program lookups by name, global data
initialization and mmap()-ing, etc.  All this greatly improves and simplifies
userspace usability of working with BPF programs. See follow up patches for
examples.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Link: https://lore.kernel.org/bpf/20191214014341.3442258-13-andriin@fb.com
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf.h
tools/lib/bpf/libbpf.map

index b6bd6c47c919455ec3314a631714dd5612992a90..a1a902fa6e0c5e86600e14962f6350496b3abd4b 100644 (file)
@@ -6793,3 +6793,165 @@ int libbpf_num_possible_cpus(void)
        WRITE_ONCE(cpus, tmp_cpus);
        return tmp_cpus;
 }
+
+int bpf_object__open_skeleton(struct bpf_object_skeleton *s,
+                             const struct bpf_object_open_opts *opts)
+{
+       DECLARE_LIBBPF_OPTS(bpf_object_open_opts, skel_opts,
+               .object_name = s->name,
+       );
+       struct bpf_object *obj;
+       int i;
+
+       /* Attempt to preserve opts->object_name, unless overriden by user
+        * explicitly. Overwriting object name for skeletons is discouraged,
+        * as it breaks global data maps, because they contain object name
+        * prefix as their own map name prefix. When skeleton is generated,
+        * bpftool is making an assumption that this name will stay the same.
+        */
+       if (opts) {
+               memcpy(&skel_opts, opts, sizeof(*opts));
+               if (!opts->object_name)
+                       skel_opts.object_name = s->name;
+       }
+
+       obj = bpf_object__open_mem(s->data, s->data_sz, &skel_opts);
+       if (IS_ERR(obj)) {
+               pr_warn("failed to initialize skeleton BPF object '%s': %ld\n",
+                       s->name, PTR_ERR(obj));
+               return PTR_ERR(obj);
+       }
+
+       *s->obj = obj;
+
+       for (i = 0; i < s->map_cnt; i++) {
+               struct bpf_map **map = s->maps[i].map;
+               const char *name = s->maps[i].name;
+               void **mmaped = s->maps[i].mmaped;
+
+               *map = bpf_object__find_map_by_name(obj, name);
+               if (!*map) {
+                       pr_warn("failed to find skeleton map '%s'\n", name);
+                       return -ESRCH;
+               }
+
+               if (mmaped)
+                       *mmaped = (*map)->mmaped;
+       }
+
+       for (i = 0; i < s->prog_cnt; i++) {
+               struct bpf_program **prog = s->progs[i].prog;
+               const char *name = s->progs[i].name;
+
+               *prog = bpf_object__find_program_by_name(obj, name);
+               if (!*prog) {
+                       pr_warn("failed to find skeleton program '%s'\n", name);
+                       return -ESRCH;
+               }
+       }
+
+       return 0;
+}
+
+int bpf_object__load_skeleton(struct bpf_object_skeleton *s)
+{
+       int i, err;
+
+       err = bpf_object__load(*s->obj);
+       if (err) {
+               pr_warn("failed to load BPF skeleton '%s': %d\n", s->name, err);
+               return err;
+       }
+
+       for (i = 0; i < s->map_cnt; i++) {
+               struct bpf_map *map = *s->maps[i].map;
+               size_t mmap_sz = bpf_map_mmap_sz(map);
+               int prot, map_fd = bpf_map__fd(map);
+               void **mmaped = s->maps[i].mmaped;
+               void *remapped;
+
+               if (!mmaped)
+                       continue;
+
+               if (!(map->def.map_flags & BPF_F_MMAPABLE)) {
+                       *mmaped = NULL;
+                       continue;
+               }
+
+               if (map->def.map_flags & BPF_F_RDONLY_PROG)
+                       prot = PROT_READ;
+               else
+                       prot = PROT_READ | PROT_WRITE;
+
+               /* Remap anonymous mmap()-ed "map initialization image" as
+                * a BPF map-backed mmap()-ed memory, but preserving the same
+                * memory address. This will cause kernel to change process'
+                * page table to point to a different piece of kernel memory,
+                * but from userspace point of view memory address (and its
+                * contents, being identical at this point) will stay the
+                * same. This mapping will be released by bpf_object__close()
+                * as per normal clean up procedure, so we don't need to worry
+                * about it from skeleton's clean up perspective.
+                */
+               remapped = mmap(*mmaped, mmap_sz, prot, MAP_SHARED | MAP_FIXED,
+                               map_fd, 0);
+               if (remapped == MAP_FAILED) {
+                       err = -errno;
+                       *mmaped = NULL;
+                       pr_warn("failed to re-mmap() map '%s': %d\n",
+                                bpf_map__name(map), err);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+int bpf_object__attach_skeleton(struct bpf_object_skeleton *s)
+{
+       int i;
+
+       for (i = 0; i < s->prog_cnt; i++) {
+               struct bpf_program *prog = *s->progs[i].prog;
+               struct bpf_link **link = s->progs[i].link;
+               const struct bpf_sec_def *sec_def;
+               const char *sec_name = bpf_program__title(prog, false);
+
+               sec_def = find_sec_def(sec_name);
+               if (!sec_def || !sec_def->attach_fn)
+                       continue;
+
+               *link = sec_def->attach_fn(sec_def, prog);
+               if (IS_ERR(*link)) {
+                       pr_warn("failed to auto-attach program '%s': %ld\n",
+                               bpf_program__name(prog), PTR_ERR(*link));
+                       return PTR_ERR(*link);
+               }
+       }
+
+       return 0;
+}
+
+void bpf_object__detach_skeleton(struct bpf_object_skeleton *s)
+{
+       int i;
+
+       for (i = 0; i < s->prog_cnt; i++) {
+               struct bpf_link **link = s->progs[i].link;
+
+               if (!IS_ERR_OR_NULL(*link))
+                       bpf_link__destroy(*link);
+               *link = NULL;
+       }
+}
+
+void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s)
+{
+       if (s->progs)
+               bpf_object__detach_skeleton(s);
+       if (s->obj)
+               bpf_object__close(*s->obj);
+       free(s->maps);
+       free(s->progs);
+       free(s);
+}
index f37bd4a3e14b2966dd97355fa095cceb50daea76..623191e714154b4649588064a2543a05497f5652 100644 (file)
@@ -631,6 +631,44 @@ BPF_EMBED_OBJ_DECLARE(NAME)
 #define BPF_EMBED_OBJ(NAME, PATH) __BPF_EMBED_OBJ(NAME, PATH, 16, ".quad")
 #endif
 
+struct bpf_map_skeleton {
+       const char *name;
+       struct bpf_map **map;
+       void **mmaped;
+};
+
+struct bpf_prog_skeleton {
+       const char *name;
+       struct bpf_program **prog;
+       struct bpf_link **link;
+};
+
+struct bpf_object_skeleton {
+       size_t sz; /* size of this struct, for forward/backward compatibility */
+
+       const char *name;
+       void *data;
+       size_t data_sz;
+
+       struct bpf_object **obj;
+
+       int map_cnt;
+       int map_skel_sz; /* sizeof(struct bpf_skeleton_map) */
+       struct bpf_map_skeleton *maps;
+
+       int prog_cnt;
+       int prog_skel_sz; /* sizeof(struct bpf_skeleton_prog) */
+       struct bpf_prog_skeleton *progs;
+};
+
+LIBBPF_API int
+bpf_object__open_skeleton(struct bpf_object_skeleton *s,
+                         const struct bpf_object_open_opts *opts);
+LIBBPF_API int bpf_object__load_skeleton(struct bpf_object_skeleton *s);
+LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s);
+LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s);
+LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
index 5a7630748eebd86d1966b5fa5edd66c7513d52b3..1376d992a703a358f11d89406db31a35a3ee3e50 100644 (file)
@@ -213,6 +213,11 @@ LIBBPF_0.0.7 {
        global:
                btf_dump__emit_type_decl;
                bpf_object__find_program_by_name;
+               bpf_object__attach_skeleton;
+               bpf_object__destroy_skeleton;
+               bpf_object__detach_skeleton;
+               bpf_object__load_skeleton;
+               bpf_object__open_skeleton;
                bpf_program__attach;
                bpf_program__name;
                btf__align_of;