diff mbox series

[v3,bpf-next,12/17] libbpf: add BPF object skeleton support

Message ID 20191213223214.2791885-13-andriin@fb.com
State Changes Requested
Delegated to: BPF Maintainers
Headers show
Series Add code-generated BPF object skeleton support | expand

Commit Message

Andrii Nakryiko Dec. 13, 2019, 10:32 p.m. UTC
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.

Acked-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/lib/bpf/libbpf.c | 162 +++++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h |  37 ++++++++++
 2 files changed, 199 insertions(+)

Comments

Alexei Starovoitov Dec. 13, 2019, 11:59 p.m. UTC | #1
On Fri, Dec 13, 2019 at 02:32:09PM -0800, Andrii Nakryiko wrote:
> 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.
> 
> Acked-by: Martin KaFai Lau <kafai@fb.com>
> Signed-off-by: Andrii Nakryiko <andriin@fb.com>
...
> +int bpf_object__open_skeleton(struct bpf_object_skeleton *s,
> +			      const struct bpf_object_open_opts *opts);
> +int bpf_object__load_skeleton(struct bpf_object_skeleton *s);
> +int bpf_object__attach_skeleton(struct bpf_object_skeleton *s);
> +void bpf_object__detach_skeleton(struct bpf_object_skeleton *s);
> +void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s);

libbpf.map and LIBBPF_API update for them is missing ?
The intent was to expose them as stable libbpf api, right?
Andrii Nakryiko Dec. 14, 2019, 1:07 a.m. UTC | #2
On Fri, Dec 13, 2019 at 3:59 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> On Fri, Dec 13, 2019 at 02:32:09PM -0800, Andrii Nakryiko wrote:
> > 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.
> >
> > Acked-by: Martin KaFai Lau <kafai@fb.com>
> > Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> ...
> > +int bpf_object__open_skeleton(struct bpf_object_skeleton *s,
> > +                           const struct bpf_object_open_opts *opts);
> > +int bpf_object__load_skeleton(struct bpf_object_skeleton *s);
> > +int bpf_object__attach_skeleton(struct bpf_object_skeleton *s);
> > +void bpf_object__detach_skeleton(struct bpf_object_skeleton *s);
> > +void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s);
>
> libbpf.map and LIBBPF_API update for them is missing ?
> The intent was to expose them as stable libbpf api, right?
>

Yeah, my bad, forgot to add LIBBPF_API.
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 6546a9dbfc14..8388f8321170 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -6790,3 +6790,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);
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index f37bd4a3e14b..3cee5f8f0180 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -631,6 +631,43 @@  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;
+};
+
+int bpf_object__open_skeleton(struct bpf_object_skeleton *s,
+			      const struct bpf_object_open_opts *opts);
+int bpf_object__load_skeleton(struct bpf_object_skeleton *s);
+int bpf_object__attach_skeleton(struct bpf_object_skeleton *s);
+void bpf_object__detach_skeleton(struct bpf_object_skeleton *s);
+void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif