bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support
@ 2019-12-10  1:14 Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 01/15] libbpf: don't require root for bpf_object__open() Andrii Nakryiko
                   ` (15 more replies)
  0 siblings, 16 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

This patch set introduces an alternative and complimentary to existing libbpf
API interface for working with BPF objects, maps, programs, and global data
from userspace side. This approach is relying on code generation. bpftool
produces a struct (a.k.a. skeleton) tailored and specific to provided BPF
object file. It includes hard-coded fields and data structures for every map,
program, link, and global data present.

Altogether this approach significantly reduces amount of userspace boilerplate
code required to open, load, attach, and work with BPF objects. It improves
attach/detach story, by providing pre-allocated space for bpf_links, and
ensuring they are properly detached on shutdown. It allows to do away with by
name/title lookups of maps and programs, because libbpf's skeleton API, in
conjunction with generated code from bpftool, is filling in hard-coded fields
with actual pointers to corresponding struct bpf_map/bpf_program/bpf_link.

Also, thanks to BPF array mmap() support, working with global data (variables)
from userspace is now as natural as it is from BPF side: each variable is just
a struct field inside skeleton struct. Furthermore, this allows to have
a natural way for userspace to pre-initialize global data (including
previously impossible to initialize .rodata) by just assigning values to the
same per-variable fields. Libbpf will carefully take into account this
initialization image, will use it to pre-populate BPF maps at creation time,
and will re-mmap() BPF map's contents at exactly the same userspace memory
address such that it can continue working with all the same pointers without
any interruptions. If kernel doesn't support mmap(), global data will still be
successfully initialized, but after map creation global data structures inside
skeleton will be NULL-ed out. This allows userspace application to gracefully
handle lack of mmap() support, if necessary.

A bunch of selftests are also converted to using skeletons, demonstrating
significant simplification of userspace part of test and reduction in amount
of code necessary.

rfc->v1:
- runqslower moved out into separate patch set waiting for vmlinux.h
  improvements;
- skeleton generation code deals with unknown internal maps more gracefully.

Andrii Nakryiko (15):
  libbpf: don't require root for bpf_object__open()
  libbpf: add generic bpf_program__attach()
  libbpf: move non-public APIs from libbpf.h to libbpf_internal.h
  libbpf: add BPF_EMBED_OBJ macro for embedding BPF .o files
  libbpf: expose field/var declaration emitting API internally
  libbpf: expose BPF program's function name
  libbpf: refactor global data map initialization
  libbpf: postpone BTF ID finding for TRACING programs to load phase
  libbpf: reduce log level of supported section names dump
  libbpf: add experimental BPF object skeleton support
  bpftool: add skeleton codegen command
  selftests/bpf: add BPF skeletons selftests and convert attach_probe.c
  selftests/bpf: convert few more selftest to skeletons
  selftests/bpf: add test validating data section to struct convertion
    layout
  bpftool: add `gen skeleton` BASH completions

 tools/bpf/bpftool/Makefile                    |   2 +-
 tools/bpf/bpftool/bash-completion/bpftool     |  11 +
 tools/bpf/bpftool/gen.c                       | 530 ++++++++++++++++
 tools/bpf/bpftool/main.c                      |   3 +-
 tools/bpf/bpftool/main.h                      |   1 +
 tools/bpf/bpftool/net.c                       |   1 +
 tools/lib/bpf/btf_dump.c                      |  61 +-
 tools/lib/bpf/libbpf.c                        | 583 ++++++++++++++----
 tools/lib/bpf/libbpf.h                        |  63 +-
 tools/lib/bpf/libbpf.map                      |   4 +
 tools/lib/bpf/libbpf_internal.h               |  61 ++
 tools/testing/selftests/bpf/.gitignore        |   2 +
 tools/testing/selftests/bpf/Makefile          |  36 +-
 .../selftests/bpf/prog_tests/attach_probe.c   | 154 +----
 .../selftests/bpf/prog_tests/fentry_fexit.c   | 105 ++--
 .../selftests/bpf/prog_tests/fentry_test.c    |  72 +--
 tools/testing/selftests/bpf/prog_tests/mmap.c |  58 +-
 .../selftests/bpf/prog_tests/probe_user.c     |   6 +-
 .../selftests/bpf/prog_tests/rdonly_maps.c    |  11 +-
 .../selftests/bpf/prog_tests/skeleton.c       |  47 ++
 .../bpf/prog_tests/stacktrace_build_id.c      |  79 +--
 .../bpf/prog_tests/stacktrace_build_id_nmi.c  |  84 +--
 .../selftests/bpf/progs/test_attach_probe.c   |  34 +-
 .../selftests/bpf/progs/test_skeleton.c       |  36 ++
 24 files changed, 1444 insertions(+), 600 deletions(-)
 create mode 100644 tools/bpf/bpftool/gen.c
 create mode 100644 tools/testing/selftests/bpf/prog_tests/skeleton.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_skeleton.c

-- 
2.17.1


^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 01/15] libbpf: don't require root for bpf_object__open()
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 02/15] libbpf: add generic bpf_program__attach() Andrii Nakryiko
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Reorganize bpf_object__open and bpf_object__load steps such that
bpf_object__open doesn't need root access. This was previously done for
feature probing and BTF sanitization. This doesn't have to happen on open,
though, so move all those steps into the load phase.

This is important, because it makes it possible for tools like bpftool, to
just open BPF object file and inspect their contents: programs, maps, BTF,
etc. For such operations it is prohibitive to require root access. On the
other hand, there is a lot of custom libbpf logic in those steps, so its best
avoided for tools to reimplement all that on their own.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/lib/bpf/libbpf.c | 83 +++++++++++++++++++++---------------------
 1 file changed, 41 insertions(+), 42 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 3f09772192f1..1e29a47da4f5 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -101,13 +101,6 @@ void libbpf_print(enum libbpf_print_level level, const char *format, ...)
 
 #define STRERR_BUFSIZE  128
 
-#define CHECK_ERR(action, err, out) do {	\
-	err = action;			\
-	if (err)			\
-		goto out;		\
-} while (0)
-
-
 /* Copied from tools/perf/util/util.h */
 #ifndef zfree
 # define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
@@ -864,8 +857,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
 	def->value_size = data->d_size;
 	def->max_entries = 1;
 	def->map_flags = type == LIBBPF_MAP_RODATA ? BPF_F_RDONLY_PROG : 0;
-	if (obj->caps.array_mmap)
-		def->map_flags |= BPF_F_MMAPABLE;
+	def->map_flags |= BPF_F_MMAPABLE;
 
 	pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n",
 		 map_name, map->sec_idx, map->sec_offset, def->map_flags);
@@ -888,8 +880,6 @@ static int bpf_object__init_global_data_maps(struct bpf_object *obj)
 {
 	int err;
 
-	if (!obj->caps.global_data)
-		return 0;
 	/*
 	 * Populate obj->maps with libbpf internal maps.
 	 */
@@ -1393,10 +1383,11 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
 	return 0;
 }
 
-static int bpf_object__init_maps(struct bpf_object *obj, bool relaxed_maps,
-				 const char *pin_root_path)
+static int bpf_object__init_maps(struct bpf_object *obj,
+				 struct bpf_object_open_opts *opts)
 {
-	bool strict = !relaxed_maps;
+	const char *pin_root_path = OPTS_GET(opts, pin_root_path, NULL);
+	bool strict = !OPTS_GET(opts, relaxed_maps, false);
 	int err;
 
 	err = bpf_object__init_user_maps(obj, strict);
@@ -1592,8 +1583,7 @@ static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)
 	return 0;
 }
 
-static int bpf_object__elf_collect(struct bpf_object *obj, bool relaxed_maps,
-				   const char *pin_root_path)
+static int bpf_object__elf_collect(struct bpf_object *obj)
 {
 	Elf *elf = obj->efile.elf;
 	GElf_Ehdr *ep = &obj->efile.ehdr;
@@ -1728,14 +1718,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, bool relaxed_maps,
 		pr_warn("Corrupted ELF file: index of strtab invalid\n");
 		return -LIBBPF_ERRNO__FORMAT;
 	}
-	err = bpf_object__init_btf(obj, btf_data, btf_ext_data);
-	if (!err)
-		err = bpf_object__init_maps(obj, relaxed_maps, pin_root_path);
-	if (!err)
-		err = bpf_object__sanitize_and_load_btf(obj);
-	if (!err)
-		err = bpf_object__init_prog_names(obj);
-	return err;
+	return bpf_object__init_btf(obj, btf_data, btf_ext_data);
 }
 
 static struct bpf_program *
@@ -1875,11 +1858,6 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
 		pr_warn("bad data relo against section %u\n", shdr_idx);
 		return -LIBBPF_ERRNO__RELOC;
 	}
-	if (!obj->caps.global_data) {
-		pr_warn("relocation: kernel does not support global \'%s\' variable access in insns[%d]\n",
-			name, insn_idx);
-		return -LIBBPF_ERRNO__RELOC;
-	}
 	for (map_idx = 0; map_idx < nr_maps; map_idx++) {
 		map = &obj->maps[map_idx];
 		if (map->libbpf_type != type)
@@ -3917,12 +3895,10 @@ static struct bpf_object *
 __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
 		   struct bpf_object_open_opts *opts)
 {
-	const char *pin_root_path;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
 	const char *obj_name;
 	char tmp_name[64];
-	bool relaxed_maps;
 	__u32 attach_prog_fd;
 	int err;
 
@@ -3952,16 +3928,16 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
 		return obj;
 
 	obj->relaxed_core_relocs = OPTS_GET(opts, relaxed_core_relocs, false);
-	relaxed_maps = OPTS_GET(opts, relaxed_maps, false);
-	pin_root_path = OPTS_GET(opts, pin_root_path, NULL);
 	attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0);
 
-	CHECK_ERR(bpf_object__elf_init(obj), err, out);
-	CHECK_ERR(bpf_object__check_endianness(obj), err, out);
-	CHECK_ERR(bpf_object__probe_caps(obj), err, out);
-	CHECK_ERR(bpf_object__elf_collect(obj, relaxed_maps, pin_root_path),
-		  err, out);
-	CHECK_ERR(bpf_object__collect_reloc(obj), err, out);
+	err = bpf_object__elf_init(obj);
+	err = err ? : bpf_object__check_endianness(obj);
+	err = err ? : bpf_object__elf_collect(obj);
+	err = err ? : bpf_object__init_maps(obj, opts);
+	err = err ? : bpf_object__init_prog_names(obj);
+	err = err ? : bpf_object__collect_reloc(obj);
+	if (err)
+		goto out;
 	bpf_object__elf_finish(obj);
 
 	bpf_object__for_each_program(prog, obj) {
@@ -4079,6 +4055,24 @@ int bpf_object__unload(struct bpf_object *obj)
 	return 0;
 }
 
+static int bpf_object__sanitize_maps(struct bpf_object *obj)
+{
+	struct bpf_map *m;
+
+	bpf_object__for_each_map(m, obj) {
+		if (!bpf_map__is_internal(m))
+			continue;
+		if (!obj->caps.global_data) {
+			pr_warn("kernel doesn't support global data\n");
+			return -ENOTSUP;
+		}
+		if (!obj->caps.array_mmap)
+			m->def.map_flags ^= BPF_F_MMAPABLE;
+	}
+
+	return 0;
+}
+
 int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
 {
 	struct bpf_object *obj;
@@ -4097,9 +4091,14 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
 
 	obj->loaded = true;
 
-	CHECK_ERR(bpf_object__create_maps(obj), err, out);
-	CHECK_ERR(bpf_object__relocate(obj, attr->target_btf_path), err, out);
-	CHECK_ERR(bpf_object__load_progs(obj, attr->log_level), err, out);
+	err = bpf_object__probe_caps(obj);
+	err = err ? : bpf_object__sanitize_and_load_btf(obj);
+	err = err ? : bpf_object__sanitize_maps(obj);
+	err = err ? : bpf_object__create_maps(obj);
+	err = err ? : bpf_object__relocate(obj, attr->target_btf_path);
+	err = err ? : bpf_object__load_progs(obj, attr->log_level);
+	if (err)
+		goto out;
 
 	return 0;
 out:
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 02/15] libbpf: add generic bpf_program__attach()
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 01/15] libbpf: don't require root for bpf_object__open() Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 03/15] libbpf: move non-public APIs from libbpf.h to libbpf_internal.h Andrii Nakryiko
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Generalize BPF program attaching and allow libbpf to auto-detect type (and
extra parameters, where applicable) and attach supported BPF program types
based on program sections. Currently this is supported for:
- kprobe/kretprobe;
- tracepoint;
- raw tracepoint;
- tracing programs (typed raw TP/fentry/fexit).

More types support can be trivially added within this framework.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/lib/bpf/libbpf.c                        | 181 ++++++++++++++----
 tools/lib/bpf/libbpf.h                        |   2 +
 tools/lib/bpf/libbpf.map                      |   2 +
 .../selftests/bpf/prog_tests/probe_user.c     |   6 +-
 4 files changed, 153 insertions(+), 38 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 1e29a47da4f5..edfe1cf1e940 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -4971,7 +4971,28 @@ void bpf_program__set_expected_attach_type(struct bpf_program *prog,
  */
 #define BPF_APROG_COMPAT(string, ptype) BPF_PROG_SEC(string, ptype)
 
-static const struct {
+#define SEC_DEF(sec_pfx, ptype, ...) {					    \
+	.sec = sec_pfx,							    \
+	.len = sizeof(sec_pfx) - 1,					    \
+	.prog_type = BPF_PROG_TYPE_##ptype,				    \
+	__VA_ARGS__							    \
+}
+
+struct bpf_sec_def;
+
+typedef struct bpf_link *(*attach_fn_t)(const struct bpf_sec_def *sec,
+					struct bpf_program *prog);
+
+static struct bpf_link *attach_kprobe(const struct bpf_sec_def *sec,
+				      struct bpf_program *prog);
+static struct bpf_link *attach_tp(const struct bpf_sec_def *sec,
+				  struct bpf_program *prog);
+static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
+				      struct bpf_program *prog);
+static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
+				     struct bpf_program *prog);
+
+struct bpf_sec_def {
 	const char *sec;
 	size_t len;
 	enum bpf_prog_type prog_type;
@@ -4979,24 +5000,39 @@ static const struct {
 	bool is_attachable;
 	bool is_attach_btf;
 	enum bpf_attach_type attach_type;
-} section_names[] = {
+	attach_fn_t attach_fn;
+};
+
+static const struct bpf_sec_def section_defs[] = {
 	BPF_PROG_SEC("socket",			BPF_PROG_TYPE_SOCKET_FILTER),
-	BPF_PROG_SEC("kprobe/",			BPF_PROG_TYPE_KPROBE),
+	SEC_DEF("kprobe/", KPROBE,
+		.attach_fn = attach_kprobe),
 	BPF_PROG_SEC("uprobe/",			BPF_PROG_TYPE_KPROBE),
-	BPF_PROG_SEC("kretprobe/",		BPF_PROG_TYPE_KPROBE),
+	SEC_DEF("kretprobe/", KPROBE,
+		.attach_fn = attach_kprobe),
 	BPF_PROG_SEC("uretprobe/",		BPF_PROG_TYPE_KPROBE),
 	BPF_PROG_SEC("classifier",		BPF_PROG_TYPE_SCHED_CLS),
 	BPF_PROG_SEC("action",			BPF_PROG_TYPE_SCHED_ACT),
-	BPF_PROG_SEC("tracepoint/",		BPF_PROG_TYPE_TRACEPOINT),
-	BPF_PROG_SEC("tp/",			BPF_PROG_TYPE_TRACEPOINT),
-	BPF_PROG_SEC("raw_tracepoint/",		BPF_PROG_TYPE_RAW_TRACEPOINT),
-	BPF_PROG_SEC("raw_tp/",			BPF_PROG_TYPE_RAW_TRACEPOINT),
-	BPF_PROG_BTF("tp_btf/",			BPF_PROG_TYPE_TRACING,
-						BPF_TRACE_RAW_TP),
-	BPF_PROG_BTF("fentry/",			BPF_PROG_TYPE_TRACING,
-						BPF_TRACE_FENTRY),
-	BPF_PROG_BTF("fexit/",			BPF_PROG_TYPE_TRACING,
-						BPF_TRACE_FEXIT),
+	SEC_DEF("tracepoint/", TRACEPOINT,
+		.attach_fn = attach_tp),
+	SEC_DEF("tp/", TRACEPOINT,
+		.attach_fn = attach_tp),
+	SEC_DEF("raw_tracepoint/", RAW_TRACEPOINT,
+		.attach_fn = attach_raw_tp),
+	SEC_DEF("raw_tp/", RAW_TRACEPOINT,
+		.attach_fn = attach_raw_tp),
+	SEC_DEF("tp_btf/", TRACING,
+		.expected_attach_type = BPF_TRACE_RAW_TP,
+		.is_attach_btf = true,
+		.attach_fn = attach_trace),
+	SEC_DEF("fentry/", TRACING,
+		.expected_attach_type = BPF_TRACE_FENTRY,
+		.is_attach_btf = true,
+		.attach_fn = attach_trace),
+	SEC_DEF("fexit/", TRACING,
+		.expected_attach_type = BPF_TRACE_FEXIT,
+		.is_attach_btf = true,
+		.attach_fn = attach_trace),
 	BPF_PROG_SEC("xdp",			BPF_PROG_TYPE_XDP),
 	BPF_PROG_SEC("perf_event",		BPF_PROG_TYPE_PERF_EVENT),
 	BPF_PROG_SEC("lwt_in",			BPF_PROG_TYPE_LWT_IN),
@@ -5058,12 +5094,26 @@ static const struct {
 #undef BPF_APROG_SEC
 #undef BPF_EAPROG_SEC
 #undef BPF_APROG_COMPAT
+#undef SEC_DEF
 
 #define MAX_TYPE_NAME_SIZE 32
 
+static const struct bpf_sec_def *find_sec_def(const char *sec_name)
+{
+	int i, n = ARRAY_SIZE(section_defs);
+
+	for (i = 0; i < n; i++) {
+		if (strncmp(sec_name,
+			    section_defs[i].sec, section_defs[i].len))
+			continue;
+		return &section_defs[i];
+	}
+	return NULL;
+}
+
 static char *libbpf_get_type_names(bool attach_type)
 {
-	int i, len = ARRAY_SIZE(section_names) * MAX_TYPE_NAME_SIZE;
+	int i, len = ARRAY_SIZE(section_defs) * MAX_TYPE_NAME_SIZE;
 	char *buf;
 
 	buf = malloc(len);
@@ -5072,16 +5122,16 @@ static char *libbpf_get_type_names(bool attach_type)
 
 	buf[0] = '\0';
 	/* Forge string buf with all available names */
-	for (i = 0; i < ARRAY_SIZE(section_names); i++) {
-		if (attach_type && !section_names[i].is_attachable)
+	for (i = 0; i < ARRAY_SIZE(section_defs); i++) {
+		if (attach_type && !section_defs[i].is_attachable)
 			continue;
 
-		if (strlen(buf) + strlen(section_names[i].sec) + 2 > len) {
+		if (strlen(buf) + strlen(section_defs[i].sec) + 2 > len) {
 			free(buf);
 			return NULL;
 		}
 		strcat(buf, " ");
-		strcat(buf, section_names[i].sec);
+		strcat(buf, section_defs[i].sec);
 	}
 
 	return buf;
@@ -5090,19 +5140,19 @@ static char *libbpf_get_type_names(bool attach_type)
 int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
 			     enum bpf_attach_type *expected_attach_type)
 {
+	const struct bpf_sec_def *sec_def;
 	char *type_names;
-	int i;
 
 	if (!name)
 		return -EINVAL;
 
-	for (i = 0; i < ARRAY_SIZE(section_names); i++) {
-		if (strncmp(name, section_names[i].sec, section_names[i].len))
-			continue;
-		*prog_type = section_names[i].prog_type;
-		*expected_attach_type = section_names[i].expected_attach_type;
+	sec_def = find_sec_def(name);
+	if (sec_def) {
+		*prog_type = sec_def->prog_type;
+		*expected_attach_type = sec_def->expected_attach_type;
 		return 0;
 	}
+
 	pr_warn("failed to guess program type from ELF section '%s'\n", name);
 	type_names = libbpf_get_type_names(false);
 	if (type_names != NULL) {
@@ -5185,16 +5235,16 @@ static int libbpf_find_attach_btf_id(const char *name,
 	if (!name)
 		return -EINVAL;
 
-	for (i = 0; i < ARRAY_SIZE(section_names); i++) {
-		if (!section_names[i].is_attach_btf)
+	for (i = 0; i < ARRAY_SIZE(section_defs); i++) {
+		if (!section_defs[i].is_attach_btf)
 			continue;
-		if (strncmp(name, section_names[i].sec, section_names[i].len))
+		if (strncmp(name, section_defs[i].sec, section_defs[i].len))
 			continue;
 		if (attach_prog_fd)
-			err = libbpf_find_prog_btf_id(name + section_names[i].len,
+			err = libbpf_find_prog_btf_id(name + section_defs[i].len,
 						      attach_prog_fd);
 		else
-			err = libbpf_find_vmlinux_btf_id(name + section_names[i].len,
+			err = libbpf_find_vmlinux_btf_id(name + section_defs[i].len,
 							 attach_type);
 		if (err <= 0)
 			pr_warn("%s is not found in vmlinux BTF\n", name);
@@ -5213,12 +5263,12 @@ int libbpf_attach_type_by_name(const char *name,
 	if (!name)
 		return -EINVAL;
 
-	for (i = 0; i < ARRAY_SIZE(section_names); i++) {
-		if (strncmp(name, section_names[i].sec, section_names[i].len))
+	for (i = 0; i < ARRAY_SIZE(section_defs); i++) {
+		if (strncmp(name, section_defs[i].sec, section_defs[i].len))
 			continue;
-		if (!section_names[i].is_attachable)
+		if (!section_defs[i].is_attachable)
 			return -EINVAL;
-		*attach_type = section_names[i].attach_type;
+		*attach_type = section_defs[i].attach_type;
 		return 0;
 	}
 	pr_warn("failed to guess attach type based on ELF section name '%s'\n", name);
@@ -5678,6 +5728,18 @@ struct bpf_link *bpf_program__attach_kprobe(struct bpf_program *prog,
 	return link;
 }
 
+static struct bpf_link *attach_kprobe(const struct bpf_sec_def *sec,
+				      struct bpf_program *prog)
+{
+	const char *func_name;
+	bool retprobe;
+
+	func_name = bpf_program__title(prog, false) + sec->len;
+	retprobe = strcmp(sec->sec, "kretprobe/") == 0;
+
+	return bpf_program__attach_kprobe(prog, retprobe, func_name);
+}
+
 struct bpf_link *bpf_program__attach_uprobe(struct bpf_program *prog,
 					    bool retprobe, pid_t pid,
 					    const char *binary_path,
@@ -5790,6 +5852,32 @@ struct bpf_link *bpf_program__attach_tracepoint(struct bpf_program *prog,
 	return link;
 }
 
+static struct bpf_link *attach_tp(const struct bpf_sec_def *sec,
+				  struct bpf_program *prog)
+{
+	char *sec_name, *tp_cat, *tp_name;
+	struct bpf_link *link;
+
+	sec_name = strdup(bpf_program__title(prog, false));
+	if (!sec_name)
+		return ERR_PTR(-ENOMEM);
+
+	/* extract "tp/<category>/<name>" */
+	tp_cat = sec_name + sec->len;
+	tp_name = strchr(tp_cat, '/');
+	if (!tp_name) {
+		link = ERR_PTR(-EINVAL);
+		goto out;
+	}
+	*tp_name = '\0';
+	tp_name++;
+
+	link = bpf_program__attach_tracepoint(prog, tp_cat, tp_name);
+out:
+	free(sec_name);
+	return link;
+}
+
 static int bpf_link__destroy_fd(struct bpf_link *link)
 {
 	struct bpf_link_fd *l = (void *)link;
@@ -5829,6 +5917,14 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
 	return (struct bpf_link *)link;
 }
 
+static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
+				      struct bpf_program *prog)
+{
+	const char *tp_name = bpf_program__title(prog, false) + sec->len;
+
+	return bpf_program__attach_raw_tracepoint(prog, tp_name);
+}
+
 struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
 {
 	char errmsg[STRERR_BUFSIZE];
@@ -5860,6 +5956,23 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
 	return (struct bpf_link *)link;
 }
 
+static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
+				     struct bpf_program *prog)
+{
+	return bpf_program__attach_trace(prog);
+}
+
+struct bpf_link *bpf_program__attach(struct bpf_program *prog)
+{
+	const struct bpf_sec_def *sec_def;
+
+	sec_def = find_sec_def(bpf_program__title(prog, false));
+	if (!sec_def || !sec_def->attach_fn)
+		return ERR_PTR(-ESRCH);
+
+	return sec_def->attach_fn(sec_def, prog);
+}
+
 enum bpf_perf_event_ret
 bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
 			   void **copy_mem, size_t *copy_size,
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 0dbf4bfba0c4..804f445c9957 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -237,6 +237,8 @@ struct bpf_link;
 
 LIBBPF_API int bpf_link__destroy(struct bpf_link *link);
 
+LIBBPF_API struct bpf_link *
+bpf_program__attach(struct bpf_program *prog);
 LIBBPF_API struct bpf_link *
 bpf_program__attach_perf_event(struct bpf_program *prog, int pfd);
 LIBBPF_API struct bpf_link *
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 495df575f87f..757a88f64b5a 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -210,4 +210,6 @@ LIBBPF_0.0.6 {
 } LIBBPF_0.0.5;
 
 LIBBPF_0.0.7 {
+	global:
+		bpf_program__attach;
 } LIBBPF_0.0.6;
diff --git a/tools/testing/selftests/bpf/prog_tests/probe_user.c b/tools/testing/selftests/bpf/prog_tests/probe_user.c
index 8a3187dec048..7aecfd9e87d1 100644
--- a/tools/testing/selftests/bpf/prog_tests/probe_user.c
+++ b/tools/testing/selftests/bpf/prog_tests/probe_user.c
@@ -3,8 +3,7 @@
 
 void test_probe_user(void)
 {
-#define kprobe_name "__sys_connect"
-	const char *prog_name = "kprobe/" kprobe_name;
+	const char *prog_name = "kprobe/__sys_connect";
 	const char *obj_file = "./test_probe_user.o";
 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, );
 	int err, results_map_fd, sock_fd, duration = 0;
@@ -33,8 +32,7 @@ void test_probe_user(void)
 		  "err %d\n", results_map_fd))
 		goto cleanup;
 
-	kprobe_link = bpf_program__attach_kprobe(kprobe_prog, false,
-						 kprobe_name);
+	kprobe_link = bpf_program__attach(kprobe_prog);
 	if (CHECK(IS_ERR(kprobe_link), "attach_kprobe",
 		  "err %ld\n", PTR_ERR(kprobe_link))) {
 		kprobe_link = NULL;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 03/15] libbpf: move non-public APIs from libbpf.h to libbpf_internal.h
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 01/15] libbpf: don't require root for bpf_object__open() Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 02/15] libbpf: add generic bpf_program__attach() Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:33   ` Jakub Kicinski
  2019-12-10  1:14 ` [PATCH bpf-next 04/15] libbpf: add BPF_EMBED_OBJ macro for embedding BPF .o files Andrii Nakryiko
                   ` (12 subsequent siblings)
  15 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Few libbpf APIs are not public but currently exposed through libbpf.h to be
used by bpftool. Move them to libbpf_internal.h, where intent of being
non-stable and non-public is much more obvious.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/bpf/bpftool/net.c         |  1 +
 tools/lib/bpf/libbpf.h          | 17 -----------------
 tools/lib/bpf/libbpf_internal.h | 17 +++++++++++++++++
 3 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c
index 4f52d3151616..d93bee298e54 100644
--- a/tools/bpf/bpftool/net.c
+++ b/tools/bpf/bpftool/net.c
@@ -18,6 +18,7 @@
 
 #include <bpf.h>
 #include <nlattr.h>
+#include "libbpf_internal.h"
 #include "main.h"
 #include "netlink_dumper.h"
 
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 804f445c9957..2698fbcb0c79 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -126,11 +126,6 @@ bpf_object__open_buffer(const void *obj_buf, size_t obj_buf_sz,
 LIBBPF_API struct bpf_object *
 bpf_object__open_xattr(struct bpf_object_open_attr *attr);
 
-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,
-				__u32 *off);
-
 enum libbpf_pin_type {
 	LIBBPF_PIN_NONE,
 	/* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */
@@ -514,18 +509,6 @@ bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
 			   void **copy_mem, size_t *copy_size,
 			   bpf_perf_event_print_t fn, void *private_data);
 
-struct nlattr;
-typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
-int libbpf_netlink_open(unsigned int *nl_pid);
-int libbpf_nl_get_link(int sock, unsigned int nl_pid,
-		       libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie);
-int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex,
-			libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie);
-int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
-			libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie);
-int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
-			 libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie);
-
 struct bpf_prog_linfo;
 struct bpf_prog_info;
 
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index 97ac17a64a58..7ee0c8691835 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -98,6 +98,23 @@ static inline bool libbpf_validate_opts(const char *opts,
 int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
 			 const char *str_sec, size_t str_len);
 
+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,
+				__u32 *off);
+
+struct nlattr;
+typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
+int libbpf_netlink_open(unsigned int *nl_pid);
+int libbpf_nl_get_link(int sock, unsigned int nl_pid,
+		       libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie);
+int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex,
+			libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie);
+int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
+			libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie);
+int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
+			 libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie);
+
 struct btf_ext_info {
 	/*
 	 * info points to the individual info section (e.g. func_info and
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 04/15] libbpf: add BPF_EMBED_OBJ macro for embedding BPF .o files
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (2 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 03/15] libbpf: move non-public APIs from libbpf.h to libbpf_internal.h Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 05/15] libbpf: expose field/var declaration emitting API internally Andrii Nakryiko
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Add a convenience macro BPF_EMBED_OBJ, which allows to embed other files
(typically used to embed BPF .o files) into a hosting userspace programs. To
C program it is exposed as struct bpf_embed_data, containing a pointer to
raw data and its size in bytes.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/lib/bpf/libbpf.h                        | 35 +++++++++++++++++++
 .../selftests/bpf/prog_tests/attach_probe.c   | 23 ++----------
 2 files changed, 38 insertions(+), 20 deletions(-)

diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 2698fbcb0c79..fa803dde1f46 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -615,6 +615,41 @@ bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear);
  */
 LIBBPF_API int libbpf_num_possible_cpus(void);
 
+struct bpf_embed_data {
+	void *data;
+	size_t size;
+};
+
+#define BPF_EMBED_OBJ_DECLARE(NAME)					\
+extern struct bpf_embed_data NAME##_embed;				\
+extern char NAME##_data[];						\
+extern char NAME##_data_end[];
+
+#define __BPF_EMBED_OBJ(NAME, PATH, SZ, ASM_TYPE)			\
+asm (									\
+"	.pushsection \".rodata\", \"a\", @progbits		\n"	\
+"	.global "#NAME"_data					\n"	\
+#NAME"_data:							\n"	\
+"	.incbin \"" PATH "\"					\n"	\
+"	.global "#NAME"_data_end				\n"	\
+#NAME"_data_end:						\n"	\
+"	.global "#NAME"_embed					\n"	\
+"	.type "#NAME"_embed, @object				\n"	\
+"	.size "#NAME"_size, "#SZ"				\n"	\
+"	.align 8,						\n"	\
+#NAME"_embed:							\n"	\
+"	"ASM_TYPE" "#NAME"_data					\n"	\
+"	"ASM_TYPE" "#NAME"_data_end - "#NAME"_data 		\n"	\
+"	.popsection						\n"	\
+);									\
+BPF_EMBED_OBJ_DECLARE(NAME)
+
+#if __SIZEOF_POINTER__ == 4
+#define BPF_EMBED_OBJ(NAME, PATH) __BPF_EMBED_OBJ(NAME, PATH, 8, ".long")
+#else
+#define BPF_EMBED_OBJ(NAME, PATH) __BPF_EMBED_OBJ(NAME, PATH, 16, ".quad")
+#endif
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c
index a83111a32d4a..b2e7c1424b07 100644
--- a/tools/testing/selftests/bpf/prog_tests/attach_probe.c
+++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c
@@ -1,24 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <test_progs.h>
 
-#define EMBED_FILE(NAME, PATH)						    \
-asm (									    \
-"      .pushsection \".rodata\", \"a\", @progbits              \n"	    \
-"      .global "#NAME"_data                                    \n"	    \
-#NAME"_data:                                                   \n"	    \
-"      .incbin \"" PATH "\"                                    \n"	    \
-#NAME"_data_end:                                               \n"	    \
-"      .global "#NAME"_size                                    \n"	    \
-"      .type "#NAME"_size, @object                             \n"	    \
-"      .size "#NAME"_size, 4                                   \n"	    \
-"      .align 4,                                               \n"	    \
-#NAME"_size:                                                   \n"	    \
-"      .int "#NAME"_data_end - "#NAME"_data                    \n"	    \
-"      .popsection                                             \n"	    \
-);									    \
-extern char NAME##_data[];						    \
-extern int NAME##_size;
-
 ssize_t get_base_addr() {
 	size_t start;
 	char buf[256];
@@ -39,7 +21,7 @@ ssize_t get_base_addr() {
 	return -EINVAL;
 }
 
-EMBED_FILE(probe, "test_attach_probe.o");
+BPF_EMBED_OBJ(probe, "test_attach_probe.o");
 
 void test_attach_probe(void)
 {
@@ -73,7 +55,8 @@ void test_attach_probe(void)
 	uprobe_offset = (size_t)&get_base_addr - base_addr;
 
 	/* open object */
-	obj = bpf_object__open_mem(probe_data, probe_size, &open_opts);
+	obj = bpf_object__open_mem(probe_embed.data, probe_embed.size,
+				   &open_opts);
 	if (CHECK(IS_ERR(obj), "obj_open_mem", "err %ld\n", PTR_ERR(obj)))
 		return;
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 05/15] libbpf: expose field/var declaration emitting API internally
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (3 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 04/15] libbpf: add BPF_EMBED_OBJ macro for embedding BPF .o files Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 06/15] libbpf: expose BPF program's function name Andrii Nakryiko
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Expose useful function emitting field/variable declaration compilable C syntax
of a provided BTF type. This is going to be used by bpftool when emitting data
section layout as a struct. This is not a trivial algorithm, so better to
expose it as internal API and let bpftool harness existing libbpf code. As
part of making this API useful in a stand-alone fashion, move initialization
of some of the internal btf_dumper state to early phase.

Also expose useful btf_align_of() function returning alignment of a given BTF
type. This is also used for data section layout-compatible struct definition,
specifically to determine necessary extra padding between fields/variables.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/lib/bpf/btf_dump.c        | 61 ++++++++++++++++-----------------
 tools/lib/bpf/libbpf_internal.h |  6 ++++
 2 files changed, 36 insertions(+), 31 deletions(-)

diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
index cb126d8fcf75..e597eb9541f5 100644
--- a/tools/lib/bpf/btf_dump.c
+++ b/tools/lib/bpf/btf_dump.c
@@ -116,6 +116,8 @@ static void btf_dump_printf(const struct btf_dump *d, const char *fmt, ...)
 	va_end(args);
 }
 
+static int btf_dump_mark_referenced(struct btf_dump *d);
+
 struct btf_dump *btf_dump__new(const struct btf *btf,
 			       const struct btf_ext *btf_ext,
 			       const struct btf_dump_opts *opts,
@@ -137,18 +139,39 @@ struct btf_dump *btf_dump__new(const struct btf *btf,
 	if (IS_ERR(d->type_names)) {
 		err = PTR_ERR(d->type_names);
 		d->type_names = NULL;
-		btf_dump__free(d);
-		return ERR_PTR(err);
 	}
 	d->ident_names = hashmap__new(str_hash_fn, str_equal_fn, NULL);
 	if (IS_ERR(d->ident_names)) {
 		err = PTR_ERR(d->ident_names);
 		d->ident_names = NULL;
-		btf_dump__free(d);
-		return ERR_PTR(err);
+		goto err;
+	}
+	d->type_states = calloc(1 + btf__get_nr_types(d->btf),
+				sizeof(d->type_states[0]));
+	if (!d->type_states) {
+		err = -ENOMEM;
+		goto err;
+	}
+	d->cached_names = calloc(1 + btf__get_nr_types(d->btf),
+				 sizeof(d->cached_names[0]));
+	if (!d->cached_names) {
+		err = -ENOMEM;
+		goto err;
 	}
 
+	/* VOID is special */
+	d->type_states[0].order_state = ORDERED;
+	d->type_states[0].emit_state = EMITTED;
+
+	/* eagerly determine referenced types for anon enums */
+	err = btf_dump_mark_referenced(d);
+	if (err)
+		goto err;
+
 	return d;
+err:
+	btf_dump__free(d);
+	return ERR_PTR(err);
 }
 
 void btf_dump__free(struct btf_dump *d)
@@ -175,7 +198,6 @@ void btf_dump__free(struct btf_dump *d)
 	free(d);
 }
 
-static int btf_dump_mark_referenced(struct btf_dump *d);
 static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr);
 static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id);
 
@@ -202,27 +224,6 @@ int btf_dump__dump_type(struct btf_dump *d, __u32 id)
 	if (id > btf__get_nr_types(d->btf))
 		return -EINVAL;
 
-	/* type states are lazily allocated, as they might not be needed */
-	if (!d->type_states) {
-		d->type_states = calloc(1 + btf__get_nr_types(d->btf),
-					sizeof(d->type_states[0]));
-		if (!d->type_states)
-			return -ENOMEM;
-		d->cached_names = calloc(1 + btf__get_nr_types(d->btf),
-					 sizeof(d->cached_names[0]));
-		if (!d->cached_names)
-			return -ENOMEM;
-
-		/* VOID is special */
-		d->type_states[0].order_state = ORDERED;
-		d->type_states[0].emit_state = EMITTED;
-
-		/* eagerly determine referenced types for anon enums */
-		err = btf_dump_mark_referenced(d);
-		if (err)
-			return err;
-	}
-
 	d->emit_queue_cnt = 0;
 	err = btf_dump_order_type(d, id, false);
 	if (err < 0)
@@ -565,8 +566,6 @@ struct id_stack {
 	int cnt;
 };
 
-static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
-				    const char *fname, int lvl);
 static void btf_dump_emit_type_chain(struct btf_dump *d,
 				     struct id_stack *decl_stack,
 				     const char *fname, int lvl);
@@ -752,7 +751,7 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
 	}
 }
 
-static int btf_align_of(const struct btf *btf, __u32 id)
+int btf_align_of(const struct btf *btf, __u32 id)
 {
 	const struct btf_type *t = btf__type_by_id(btf, id);
 	__u16 kind = btf_kind(t);
@@ -1051,8 +1050,8 @@ static int btf_dump_push_decl_stack_id(struct btf_dump *d, __u32 id)
  * of a stack frame. Some care is required to "pop" stack frames after
  * processing type declaration chain.
  */
-static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
-				    const char *fname, int lvl)
+void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
+			     const char *fname, int lvl)
 {
 	struct id_stack decl_stack;
 	const struct btf_type *t;
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index 7ee0c8691835..bb7fd22eb0ab 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -98,6 +98,12 @@ static inline bool libbpf_validate_opts(const char *opts,
 int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
 			 const char *str_sec, size_t str_len);
 
+struct btf_dump;
+
+void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id, const char *fname,
+			     int lvl);
+int btf_align_of(const struct btf *btf, __u32 id);
+
 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,
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 06/15] libbpf: expose BPF program's function name
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (4 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 05/15] libbpf: expose field/var declaration emitting API internally Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-11 19:38   ` [Potential Spoof] " Martin Lau
  2019-12-10  1:14 ` [PATCH bpf-next 07/15] libbpf: refactor global data map initialization Andrii Nakryiko
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Add APIs to get BPF program function name, as opposed to bpf_program__title(),
which returns BPF program function's section name. Function name has a benefit
of being a valid C identifier and uniquely identifies a specific BPF program,
while section name can be duplicated across multiple independent BPF programs.

Add also bpf_object__find_program_by_name(), similar to
bpf_object__find_program_by_title(), to facilitate looking up BPF programs by
their C function names.

Convert one of selftests to new API for look up.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/lib/bpf/libbpf.c                        | 28 +++++++++++++++----
 tools/lib/bpf/libbpf.h                        |  9 ++++--
 tools/lib/bpf/libbpf.map                      |  2 ++
 .../selftests/bpf/prog_tests/rdonly_maps.c    | 11 +++-----
 4 files changed, 36 insertions(+), 14 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index edfe1cf1e940..f13752c4d271 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -209,8 +209,8 @@ static const char * const libbpf_type_to_btf_name[] = {
 };
 
 struct bpf_map {
-	int fd;
 	char *name;
+	int fd;
 	int sec_idx;
 	size_t sec_offset;
 	int map_ifindex;
@@ -1384,7 +1384,7 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
 }
 
 static int bpf_object__init_maps(struct bpf_object *obj,
-				 struct bpf_object_open_opts *opts)
+				 const struct bpf_object_open_opts *opts)
 {
 	const char *pin_root_path = OPTS_GET(opts, pin_root_path, NULL);
 	bool strict = !OPTS_GET(opts, relaxed_maps, false);
@@ -1748,6 +1748,19 @@ bpf_object__find_program_by_title(const struct bpf_object *obj,
 	return NULL;
 }
 
+struct bpf_program *
+bpf_object__find_program_by_name(const struct bpf_object *obj,
+				 const char *name)
+{
+	struct bpf_program *prog;
+
+	bpf_object__for_each_program(prog, obj) {
+		if (!strcmp(prog->name, name))
+			return prog;
+	}
+	return NULL;
+}
+
 static bool bpf_object__shndx_is_data(const struct bpf_object *obj,
 				      int shndx)
 {
@@ -3893,7 +3906,7 @@ static int libbpf_find_attach_btf_id(const char *name,
 				     __u32 attach_prog_fd);
 static struct bpf_object *
 __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
-		   struct bpf_object_open_opts *opts)
+		   const struct bpf_object_open_opts *opts)
 {
 	struct bpf_program *prog;
 	struct bpf_object *obj;
@@ -4002,7 +4015,7 @@ struct bpf_object *bpf_object__open(const char *path)
 }
 
 struct bpf_object *
-bpf_object__open_file(const char *path, struct bpf_object_open_opts *opts)
+bpf_object__open_file(const char *path, const struct bpf_object_open_opts *opts)
 {
 	if (!path)
 		return ERR_PTR(-EINVAL);
@@ -4014,7 +4027,7 @@ bpf_object__open_file(const char *path, struct bpf_object_open_opts *opts)
 
 struct bpf_object *
 bpf_object__open_mem(const void *obj_buf, size_t obj_buf_sz,
-		     struct bpf_object_open_opts *opts)
+		     const struct bpf_object_open_opts *opts)
 {
 	if (!obj_buf || obj_buf_sz == 0)
 		return ERR_PTR(-EINVAL);
@@ -4819,6 +4832,11 @@ void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex)
 	prog->prog_ifindex = ifindex;
 }
 
+const char *bpf_program__name(const struct bpf_program *prog)
+{
+	return prog->name;
+}
+
 const char *bpf_program__title(const struct bpf_program *prog, bool needs_copy)
 {
 	const char *title;
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index fa803dde1f46..7fa583ebe56f 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -114,10 +114,10 @@ struct bpf_object_open_opts {
 
 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);
+bpf_object__open_file(const char *path, const 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);
+		     const struct bpf_object_open_opts *opts);
 
 /* deprecated bpf_object__open variants */
 LIBBPF_API struct bpf_object *
@@ -156,6 +156,7 @@ struct bpf_object_load_attr {
 LIBBPF_API int bpf_object__load(struct bpf_object *obj);
 LIBBPF_API int bpf_object__load_xattr(struct bpf_object_load_attr *attr);
 LIBBPF_API int bpf_object__unload(struct bpf_object *obj);
+
 LIBBPF_API const char *bpf_object__name(const struct bpf_object *obj);
 LIBBPF_API unsigned int bpf_object__kversion(const struct bpf_object *obj);
 
@@ -166,6 +167,9 @@ LIBBPF_API int bpf_object__btf_fd(const struct bpf_object *obj);
 LIBBPF_API struct bpf_program *
 bpf_object__find_program_by_title(const struct bpf_object *obj,
 				  const char *title);
+LIBBPF_API struct bpf_program *
+bpf_object__find_program_by_name(const struct bpf_object *obj,
+				 const char *name);
 
 LIBBPF_API struct bpf_object *bpf_object__next(struct bpf_object *prev);
 #define bpf_object__for_each_safe(pos, tmp)			\
@@ -209,6 +213,7 @@ LIBBPF_API void *bpf_program__priv(const struct bpf_program *prog);
 LIBBPF_API void bpf_program__set_ifindex(struct bpf_program *prog,
 					 __u32 ifindex);
 
+LIBBPF_API const char *bpf_program__name(const struct bpf_program *prog);
 LIBBPF_API const char *bpf_program__title(const struct bpf_program *prog,
 					  bool needs_copy);
 
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 757a88f64b5a..f2b2fa0f5c2a 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -211,5 +211,7 @@ LIBBPF_0.0.6 {
 
 LIBBPF_0.0.7 {
 	global:
+		bpf_object__find_program_by_name;
 		bpf_program__attach;
+		bpf_program__name;
 } LIBBPF_0.0.6;
diff --git a/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c b/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c
index d90acc13d1ec..563e12120e77 100644
--- a/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c
+++ b/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c
@@ -16,14 +16,11 @@ struct rdonly_map_subtest {
 
 void test_rdonly_maps(void)
 {
-	const char *prog_name_skip_loop = "raw_tracepoint/sys_enter:skip_loop";
-	const char *prog_name_part_loop = "raw_tracepoint/sys_enter:part_loop";
-	const char *prog_name_full_loop = "raw_tracepoint/sys_enter:full_loop";
 	const char *file = "test_rdonly_maps.o";
 	struct rdonly_map_subtest subtests[] = {
-		{ "skip loop", prog_name_skip_loop, 0, 0 },
-		{ "part loop", prog_name_part_loop, 3, 2 + 3 + 4 },
-		{ "full loop", prog_name_full_loop, 4, 2 + 3 + 4 + 5 },
+		{ "skip loop", "skip_loop", 0, 0 },
+		{ "part loop", "part_loop", 3, 2 + 3 + 4 },
+		{ "full loop", "full_loop", 4, 2 + 3 + 4 + 5 },
 	};
 	int i, err, zero = 0, duration = 0;
 	struct bpf_link *link = NULL;
@@ -50,7 +47,7 @@ void test_rdonly_maps(void)
 		if (!test__start_subtest(t->subtest_name))
 			continue;
 
-		prog = bpf_object__find_program_by_title(obj, t->prog_name);
+		prog = bpf_object__find_program_by_name(obj, t->prog_name);
 		if (CHECK(!prog, "find_prog", "prog '%s' not found\n",
 			  t->prog_name))
 			goto cleanup;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 07/15] libbpf: refactor global data map initialization
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (5 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 06/15] libbpf: expose BPF program's function name Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 08/15] libbpf: postpone BTF ID finding for TRACING programs to load phase Andrii Nakryiko
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Refactor global data map initialization to use anonymous mmap()-ed memory
instead of malloc()-ed one. This allows to do a transparent re-mmap()-ing of
already existing memory address to point to BPF map's memory after
bpf_object__load() step (done in follow up patch). This choreographed setup
allows to have a nice and unsurprising way to pre-initialize read-only (and
r/w as well) maps by user and after BPF map creation keep working with
mmap()-ed contents of this map. All in a way that doesn't require user code to
update any pointers: the illusion of working with memory contents is preserved
before and after actual BPF map instantiation.

Selftests and runqslower example demonstrate this feature in follow up patches.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/lib/bpf/libbpf.c | 92 +++++++++++++++++++++++++-----------------
 1 file changed, 55 insertions(+), 37 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index f13752c4d271..ff00a767adfb 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -221,16 +221,12 @@ struct bpf_map {
 	void *priv;
 	bpf_map_clear_priv_t clear_priv;
 	enum libbpf_map_type libbpf_type;
+	void *mmaped;
 	char *pin_path;
 	bool pinned;
 	bool reused;
 };
 
-struct bpf_secdata {
-	void *rodata;
-	void *data;
-};
-
 static LIST_HEAD(bpf_objects_list);
 
 struct bpf_object {
@@ -243,7 +239,6 @@ struct bpf_object {
 	struct bpf_map *maps;
 	size_t nr_maps;
 	size_t maps_cap;
-	struct bpf_secdata sections;
 
 	bool loaded;
 	bool has_pseudo_calls;
@@ -828,13 +823,24 @@ static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
 	return &obj->maps[obj->nr_maps++];
 }
 
+static size_t bpf_map_mmap_sz(const struct bpf_map *map)
+{
+	long page_sz = sysconf(_SC_PAGE_SIZE);
+	size_t map_sz;
+
+	map_sz = roundup(map->def.value_size, 8) * map->def.max_entries;
+	map_sz = roundup(map_sz, page_sz);
+	return map_sz;
+}
+
 static int
 bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
-			      int sec_idx, Elf_Data *data, void **data_buff)
+			      int sec_idx, Elf_Data *data)
 {
 	char map_name[BPF_OBJ_NAME_LEN];
 	struct bpf_map_def *def;
 	struct bpf_map *map;
+	int err;
 
 	map = bpf_object__add_map(obj);
 	if (IS_ERR(map))
@@ -862,16 +868,20 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
 	pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n",
 		 map_name, map->sec_idx, map->sec_offset, def->map_flags);
 
-	if (data_buff) {
-		*data_buff = malloc(data->d_size);
-		if (!*data_buff) {
-			zfree(&map->name);
-			pr_warn("failed to alloc map content buffer\n");
-			return -ENOMEM;
-		}
-		memcpy(*data_buff, data->d_buf, data->d_size);
+	map->mmaped = mmap(NULL, bpf_map_mmap_sz(map), PROT_READ | PROT_WRITE,
+			   MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+	if (map->mmaped == MAP_FAILED) {
+		err = -errno;
+		map->mmaped = NULL;
+		pr_warn("failed to alloc map '%s' content buffer: %d\n",
+			map->name, err);
+		zfree(&map->name);
+		return err;
 	}
 
+	if (type != LIBBPF_MAP_BSS)
+		memcpy(map->mmaped, data->d_buf, data->d_size);
+
 	pr_debug("map %td is \"%s\"\n", map - obj->maps, map->name);
 	return 0;
 }
@@ -886,23 +896,21 @@ static int bpf_object__init_global_data_maps(struct bpf_object *obj)
 	if (obj->efile.data_shndx >= 0) {
 		err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA,
 						    obj->efile.data_shndx,
-						    obj->efile.data,
-						    &obj->sections.data);
+						    obj->efile.data);
 		if (err)
 			return err;
 	}
 	if (obj->efile.rodata_shndx >= 0) {
 		err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA,
 						    obj->efile.rodata_shndx,
-						    obj->efile.rodata,
-						    &obj->sections.rodata);
+						    obj->efile.rodata);
 		if (err)
 			return err;
 	}
 	if (obj->efile.bss_shndx >= 0) {
 		err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS,
 						    obj->efile.bss_shndx,
-						    obj->efile.bss, NULL);
+						    obj->efile.bss);
 		if (err)
 			return err;
 	}
@@ -2291,27 +2299,32 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map)
 {
 	char *cp, errmsg[STRERR_BUFSIZE];
 	int err, zero = 0;
-	__u8 *data;
 
 	/* Nothing to do here since kernel already zero-initializes .bss map. */
 	if (map->libbpf_type == LIBBPF_MAP_BSS)
 		return 0;
 
-	data = map->libbpf_type == LIBBPF_MAP_DATA ?
-	       obj->sections.data : obj->sections.rodata;
+	err = bpf_map_update_elem(map->fd, &zero, map->mmaped, 0);
+	if (err) {
+		err = -errno;
+		cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
+		pr_warn("Error setting initial map(%s) contents: %s\n",
+			map->name, cp);
+		return err;
+	}
 
-	err = bpf_map_update_elem(map->fd, &zero, data, 0);
 	/* Freeze .rodata map as read-only from syscall side. */
-	if (!err && map->libbpf_type == LIBBPF_MAP_RODATA) {
+	if (map->libbpf_type == LIBBPF_MAP_RODATA) {
 		err = bpf_map_freeze(map->fd);
 		if (err) {
-			cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
+			err = -errno;
+			cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
 			pr_warn("Error freezing map(%s) as read-only: %s\n",
 				map->name, cp);
-			err = 0;
+			return err;
 		}
 	}
-	return err;
+	return 0;
 }
 
 static int
@@ -4682,17 +4695,22 @@ void bpf_object__close(struct bpf_object *obj)
 	btf_ext__free(obj->btf_ext);
 
 	for (i = 0; i < obj->nr_maps; i++) {
-		zfree(&obj->maps[i].name);
-		zfree(&obj->maps[i].pin_path);
-		if (obj->maps[i].clear_priv)
-			obj->maps[i].clear_priv(&obj->maps[i],
-						obj->maps[i].priv);
-		obj->maps[i].priv = NULL;
-		obj->maps[i].clear_priv = NULL;
+		struct bpf_map *map = &obj->maps[i];
+
+		if (map->clear_priv)
+			map->clear_priv(map, map->priv);
+		map->priv = NULL;
+		map->clear_priv = NULL;
+
+		if (map->mmaped) {
+			munmap(map->mmaped, bpf_map_mmap_sz(map));
+			map->mmaped = NULL;
+		}
+
+		zfree(&map->name);
+		zfree(&map->pin_path);
 	}
 
-	zfree(&obj->sections.rodata);
-	zfree(&obj->sections.data);
 	zfree(&obj->maps);
 	obj->nr_maps = 0;
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 08/15] libbpf: postpone BTF ID finding for TRACING programs to load phase
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (6 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 07/15] libbpf: refactor global data map initialization Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 09/15] libbpf: reduce log level of supported section names dump Andrii Nakryiko
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Move BTF ID determination for BPF_PROG_TYPE_TRACING programs to a load phase.
Performing it at open step is inconvenient, because it prevents BPF skeleton
generation on older host kernel, which doesn't contain BTF_KIND_FUNCs
information in vmlinux BTF. This is a common set up, though, when, e.g.,
selftests are compiled on older host kernel, but the test program itself is
executed in qemu VM with bleeding edge kernel. Having this BTF searching
performed at load time allows to successfully use bpf_object__open() for
codegen and inspection of BPF object file.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/lib/bpf/libbpf.c | 37 ++++++++++++++++++-------------------
 1 file changed, 18 insertions(+), 19 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index ff00a767adfb..f4a3ed73c9f5 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -3811,11 +3811,22 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
 	return ret;
 }
 
-int
-bpf_program__load(struct bpf_program *prog,
-		  char *license, __u32 kern_version)
+static int libbpf_find_attach_btf_id(const char *name,
+				     enum bpf_attach_type attach_type,
+				     __u32 attach_prog_fd);
+
+int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
 {
-	int err = 0, fd, i;
+	int err = 0, fd, i, btf_id;
+
+	if (prog->type == BPF_PROG_TYPE_TRACING) {
+		btf_id = libbpf_find_attach_btf_id(prog->section_name,
+						   prog->expected_attach_type,
+						   prog->attach_prog_fd);
+		if (btf_id <= 0)
+			return btf_id;
+		prog->attach_btf_id = btf_id;
+	}
 
 	if (prog->instances.nr < 0 || !prog->instances.fds) {
 		if (prog->preprocessor) {
@@ -3839,7 +3850,7 @@ bpf_program__load(struct bpf_program *prog,
 				prog->section_name, prog->instances.nr);
 		}
 		err = load_program(prog, prog->insns, prog->insns_cnt,
-				   license, kern_version, &fd);
+				   license, kern_ver, &fd);
 		if (!err)
 			prog->instances.fds[0] = fd;
 		goto out;
@@ -3868,9 +3879,7 @@ bpf_program__load(struct bpf_program *prog,
 		}
 
 		err = load_program(prog, result.new_insn_ptr,
-				   result.new_insn_cnt,
-				   license, kern_version, &fd);
-
+				   result.new_insn_cnt, license, kern_ver, &fd);
 		if (err) {
 			pr_warn("Loading the %dth instance of program '%s' failed\n",
 				i, prog->section_name);
@@ -3914,9 +3923,6 @@ bpf_object__load_progs(struct bpf_object *obj, int log_level)
 	return 0;
 }
 
-static int libbpf_find_attach_btf_id(const char *name,
-				     enum bpf_attach_type attach_type,
-				     __u32 attach_prog_fd);
 static struct bpf_object *
 __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
 		   const struct bpf_object_open_opts *opts)
@@ -3980,15 +3986,8 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
 
 		bpf_program__set_type(prog, prog_type);
 		bpf_program__set_expected_attach_type(prog, attach_type);
-		if (prog_type == BPF_PROG_TYPE_TRACING) {
-			err = libbpf_find_attach_btf_id(prog->section_name,
-							attach_type,
-							attach_prog_fd);
-			if (err <= 0)
-				goto out;
-			prog->attach_btf_id = err;
+		if (prog_type == BPF_PROG_TYPE_TRACING)
 			prog->attach_prog_fd = attach_prog_fd;
-		}
 	}
 
 	return obj;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 09/15] libbpf: reduce log level of supported section names dump
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (7 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 08/15] libbpf: postpone BTF ID finding for TRACING programs to load phase Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 10/15] libbpf: add experimental BPF object skeleton support Andrii Nakryiko
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

It's quite spammy. And now that bpf_object__open() is trying to determine
program type from its section name, we are getting these verbose messages all
the time. Reduce their log level to DEBUG.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/lib/bpf/libbpf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index f4a3ed73c9f5..a4cce8f1e6b1 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -5191,7 +5191,7 @@ int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
 	pr_warn("failed to guess program type from ELF section '%s'\n", name);
 	type_names = libbpf_get_type_names(false);
 	if (type_names != NULL) {
-		pr_info("supported section(type) names are:%s\n", type_names);
+		pr_debug("supported section(type) names are:%s\n", type_names);
 		free(type_names);
 	}
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 10/15] libbpf: add experimental BPF object skeleton support
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (8 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 09/15] libbpf: reduce log level of supported section names dump Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 11/15] bpftool: add skeleton codegen command Andrii Nakryiko
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Add new set of APIs (currently experimental and not public), 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>
---
 tools/lib/bpf/libbpf.c          | 162 ++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf_internal.h |  38 ++++++++
 2 files changed, 200 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index a4cce8f1e6b1..6152266a864a 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -6727,3 +6727,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_internal.h b/tools/lib/bpf/libbpf_internal.h
index bb7fd22eb0ab..9e0f87473673 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -237,4 +237,42 @@ struct bpf_field_reloc {
 	enum bpf_field_info_kind kind;
 };
 
+
+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);
+
 #endif /* __LIBBPF_LIBBPF_INTERNAL_H */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (9 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 10/15] libbpf: add experimental BPF object skeleton support Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:57   ` Jakub Kicinski
                     ` (2 more replies)
  2019-12-10  1:14 ` [PATCH bpf-next 12/15] selftests/bpf: add BPF skeletons selftests and convert attach_probe.c Andrii Nakryiko
                   ` (4 subsequent siblings)
  15 siblings, 3 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Add `bpftool gen skeleton` command, which takes in compiled BPF .o object file
and dumps a BPF skeleton struct and related code to work with that skeleton.
Skeleton itself is tailored to a specific structure of provided BPF object
file, containing accessors (just plain struct fields) for every map and
program, as well as dedicated space for bpf_links. If BPF program is using
global variables, corresponding structure definitions of compatible memory
layout are emitted as well, making it possible to initialize and subsequently
read/update global variables values using simple and clear C syntax for
accessing fields. This skeleton majorly improves usability of
opening/loading/attaching of BPF object, as well as interacting with it
throughout the lifetime of loaded BPF object.

Generated skeleton struct has the following structure:

struct <object-name> {
	/* used by libbpf's skeleton API */
	struct bpf_object_skeleton *skeleton;
	/* bpf_object for libbpf APIs */
	struct bpf_object *obj;
	struct {
		/* for every defined map in BPF object: */
		struct bpf_map *<map-name>;
	} maps;
	struct {
		/* for every program in BPF object: */
		struct bpf_program *<program-name>;
	} progs;
	struct {
		/* for every program in BPF object: */
		struct bpf_link *<program-name>;
	} links;
	/* for every present global data section: */
	struct <object-name>__<one of bss, data, or rodata> {
		/* memory layout of corresponding data section,
		 * with every defined variable represented as a struct field
		 * with exactly the same type, but without const/volatile
		 * modifiers, e.g.:
		 */
		 int *my_var_1;
		 ...
	} *<one of bss, data, or rodata>;
};

This provides great usability improvements:
- no need to look up maps and programs by name, instead just
  my_obj->maps.my_map or my_obj->progs.my_prog would give necessary
  bpf_map/bpf_program pointers, which user can pass to existing libbpf APIs;
- pre-defined places for bpf_links, which will be automatically populated for
  program types that libbpf knows how to attach automatically (currently
  tracepoints, kprobe/kretprobe, raw tracepoint and tracing programs). On
  tearing down skeleton, all active bpf_links will be destroyed (meaning BPF
  programs will be detached, if they are attached). For cases in which libbpf
  doesn't know how to auto-attach BPF program, user can manually create link
  after loading skeleton and they will be auto-detached on skeleton
  destruction:

	my_obj->links.my_fancy_prog = bpf_program__attach_cgroup_whatever(
		my_obj->progs.my_fancy_prog, <whatever extra param);

- it's extremely easy and convenient to work with global data from userspace
  now. Both for read-only and read/write variables, it's possible to
  pre-initialize them before skeleton is loaded:

	skel = my_obj__open(raw_embed_data);
	my_obj->rodata->my_var = 123;
	my_obj__load(skel); /* 123 will be initialization value for my_var */

  After load, if kernel supports mmap() for BPF arrays, user can still read
  (and write for .bss and .data) variables values, but at that point it will
  be directly mmap()-ed to BPF array, backing global variables. This allows to
  seamlessly exchange data with BPF side. From userspace program's POV, all
  the pointers and memory contents stay the same, but mapped kernel memory
  changes to point to created map.
  If kernel doesn't yet support mmap() for BPF arrays, it's still possible to
  use those data section structs to pre-initialize .bss, .data, and .rodata,
  but after load their pointers will be reset to NULL, allowing user code to
  gracefully handle this condition, if necessary.

Given a big surface area, skeleton is kept as an experimental non-public
API for now, until more feedback and real-world experience is collected.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/bpf/bpftool/Makefile |   2 +-
 tools/bpf/bpftool/gen.c    | 530 +++++++++++++++++++++++++++++++++++++
 tools/bpf/bpftool/main.c   |   1 +
 tools/bpf/bpftool/main.h   |   1 +
 4 files changed, 533 insertions(+), 1 deletion(-)
 create mode 100644 tools/bpf/bpftool/gen.c

diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
index 39bc6f0f4f0b..56354ea2acdc 100644
--- a/tools/bpf/bpftool/Makefile
+++ b/tools/bpf/bpftool/Makefile
@@ -38,7 +38,7 @@ $(LIBBPF)-clean:
 prefix ?= /usr/local
 bash_compdir ?= /usr/share/bash-completion/completions
 
-CFLAGS += -O2
+CFLAGS += -O0 -g
 CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers
 CFLAGS += $(filter-out -Wswitch-enum,$(EXTRA_WARNINGS))
 CFLAGS += -DPACKAGE='"bpftool"' -D__EXPORTED_HEADERS__ \
diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
new file mode 100644
index 000000000000..f617bde0dad1
--- /dev/null
+++ b/tools/bpf/bpftool/gen.c
@@ -0,0 +1,530 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2019 Facebook */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/err.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <bpf.h>
+#include <libbpf.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "btf.h"
+#include "libbpf_internal.h"
+#include "json_writer.h"
+#include "main.h"
+
+
+#define MAX_OBJ_NAME_LEN 64
+
+static void sanitize_identifier(char *name)
+{
+	int i;
+
+	for (i = 0; name[i]; i++)
+		if (!isalnum(name[i]) && name[i] != '_')
+			name[i] = '_';
+}
+
+static bool str_has_suffix(const char *str, const char *suffix)
+{
+	size_t i, n1 = strlen(str), n2 = strlen(suffix);
+
+	if (n1 < n2)
+		return false;
+
+	for (i = 0; i < n2; i++) {
+		if (str[n1 - i - 1] != suffix[n2 - i - 1])
+			return false;
+	}
+
+	return true;
+}
+
+static void get_obj_name(char *name, const char *file)
+{
+	/* Using basename() GNU version which doesn't modify arg. */
+	strncpy(name, basename(file), MAX_OBJ_NAME_LEN - 1);
+	name[MAX_OBJ_NAME_LEN - 1] = '\0';
+	if (str_has_suffix(name, ".o"))
+		name[strlen(name) - 2] = '\0';
+	sanitize_identifier(name);
+}
+
+static void get_header_guard(char *guard, const char *obj_name)
+{
+	int i;
+
+	sprintf(guard, "__%s_SKEL_H__", obj_name);
+	for (i = 0; guard[i]; i++)
+		guard[i] = toupper(guard[i]);
+}
+
+static const char *get_map_ident(const struct bpf_map *map)
+{
+	const char *name = bpf_map__name(map);
+
+	if (!bpf_map__is_internal(map))
+		return name;
+
+	if (str_has_suffix(name, ".data"))
+		return "data";
+	else if (str_has_suffix(name, ".rodata"))
+		return "rodata";
+	else if (str_has_suffix(name, ".bss"))
+		return "bss";
+	else
+		return NULL;
+}
+
+static void codegen_btf_dump_printf(void *ct, const char *fmt, va_list args)
+{
+	vprintf(fmt, args);
+}
+
+static int codegen_datasec_def(struct bpf_object *obj,
+			       struct btf *btf,
+			       struct btf_dump *d,
+			       const struct btf_type *sec,
+			       const char *obj_name)
+{
+	const char *sec_name = btf__name_by_offset(btf, sec->name_off);
+	const struct btf_var_secinfo* sec_var = btf_var_secinfos(sec);
+	const char *sec_ident;
+	int i, off = 0, pad_cnt = 0, vlen = btf_vlen(sec);
+
+	if (strcmp(sec_name, ".data") == 0) {
+		sec_ident = "data";
+	} else if (strcmp(sec_name, ".bss") == 0) {
+		sec_ident = "bss";
+	} else if (strcmp(sec_name, ".rodata") == 0) {
+		sec_ident = "rodata";
+	} else {
+		return 0;
+	}
+
+	printf("	struct %s__%s {\n", obj_name, sec_ident);
+	for (i = 0; i < vlen; i++, sec_var++) {
+		const struct btf_type *var = btf__type_by_id(btf, sec_var->type);
+		const char *var_name = btf__name_by_offset(btf, var->name_off);
+		int align = btf_align_of(btf, var->type);
+		int need_off = sec_var->offset;
+		int align_off;
+		__u32 var_type_id = var->type;
+		const struct btf_type *t = btf__type_by_id(btf, var_type_id);
+
+		while (btf_is_mod(t)) {
+			var_type_id = t->type;
+			t = btf__type_by_id(btf, var_type_id);
+		}
+
+		if (off > need_off) {
+			p_err("Something is wrong for %s's variable #%d: need offset %d, already at %d.\n",
+			      sec_name, i, need_off, off);
+			return -1;
+		}
+
+		align_off = (off + align - 1) / align * align;
+		if (align_off != need_off) {
+			printf("\t\tchar __pad%d[%d];\n",
+			       pad_cnt, need_off - off);
+			pad_cnt++;
+		}
+
+		printf("\t\t");
+		btf_dump_emit_type_decl(d, var_type_id, var_name, 2);
+		printf(";\n");
+
+		off = sec_var->offset + sec_var->size;
+	}
+	printf("	} *%s;\n", sec_ident);
+	return 0;
+}
+
+static int codegen_datasecs(struct bpf_object *obj, const char *obj_name)
+{
+	struct btf_dump *d;
+	struct btf *btf = bpf_object__btf(obj);
+	int i, err = 0, n = btf__get_nr_types(btf);
+
+	d = btf_dump__new(btf, NULL, NULL, codegen_btf_dump_printf);
+	if (IS_ERR(d))
+		return PTR_ERR(d);
+
+
+	for (i = 1; i <= n; i++) {
+		const struct btf_type *t = btf__type_by_id(btf, i);
+
+		if (!btf_is_datasec(t))
+			continue;
+
+		err = codegen_datasec_def(obj, btf, d, t, obj_name);
+		if (err)
+			goto out;
+	}
+out:
+	btf_dump__free(d);
+	return err;
+}
+
+static int codegen(const char *template, ...)
+{
+	const char *src, *end;
+	int skip_tabs = 0, n;
+	char *s, *dst;
+	va_list args;
+	char c;
+
+	n = strlen(template);
+	s = malloc(n + 1);
+	if (!s)
+		return -ENOMEM;
+	src = template;
+	dst = s;
+
+	/* find out "baseline" indentation to skip */
+	while ((c = *src++)) {
+		if (c == '\t') {
+			skip_tabs++;
+		} else if (c == '\n') {
+			break;
+		} else {
+			p_err("unrecognized character at pos %td in template '%s'",
+			      src - template - 1, template);
+			return -EINVAL;
+		}
+	}
+
+	while (*src) {
+		/* skip baseline indentation tabs */
+		for (n = skip_tabs; n > 0; n--, src++) {
+			if (*src != '\t') {
+				p_err("not enough tabs at pos %td in template '%s'",
+				      src - template - 1, template);
+				return -EINVAL;
+			}
+		}
+		/* trim trailing whitespace */
+		end = strchrnul(src, '\n');
+		for (n = end - src; n > 0 && isspace(src[n - 1]); n--)
+			;
+		memcpy(dst, src, n);
+		dst += n;
+		if (*end)
+			*dst++ = '\n';
+		src = *end ? end + 1 : end;
+	}
+	*dst++ = '\0';
+
+	/* print out using adjusted template */
+	va_start(args, template);
+	n = vprintf(s, args);
+	va_end(args);
+	return n;
+}
+
+static int do_skeleton(int argc, char **argv)
+{
+	char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")];
+	size_t i, map_cnt = 0, prog_cnt = 0;
+	char obj_name[MAX_OBJ_NAME_LEN];
+	const char *file, *ident;
+	struct bpf_program *prog;
+	struct bpf_object *obj;
+	struct bpf_map *map;
+	struct btf *btf;
+	int err = -1;
+
+	if (!REQ_ARGS(1)) {
+		usage();
+		return -1;
+	}
+	file = GET_ARG();
+
+	if (argc) {
+		p_err("extra unknown arguments");
+		return -1;
+	}
+
+	obj = bpf_object__open_file(file, NULL);
+	if (IS_ERR(obj)) {
+		p_err("failed to open BPF object file: %ld", PTR_ERR(obj));
+		return -1;
+	}
+
+	get_obj_name(obj_name, file);
+	get_header_guard(header_guard, obj_name);
+
+	bpf_object__for_each_map(map, obj) {
+		ident = get_map_ident(map);
+		if (!ident) {
+			p_err("ignoring unrecognized internal map '%s'...",
+			      bpf_map__name(map));
+			continue;
+		}
+		map_cnt++;
+	}
+	bpf_object__for_each_program(prog, obj) {
+		prog_cnt++;
+	}
+
+	codegen("\
+		\n\
+		/* THIS FILE IS AUTOGENERATED! */			    \n\
+		#ifndef %2$s						    \n\
+		#define %2$s						    \n\
+									    \n\
+		#include <stdlib.h>					    \n\
+		#include <libbpf.h>					    \n\
+		#include <libbpf_internal.h>				    \n\
+									    \n\
+		struct %1$s {						    \n\
+			struct bpf_object_skeleton *skeleton;		    \n\
+			struct bpf_object *obj;				    \n\
+		",
+		obj_name, header_guard
+	);
+
+	if (map_cnt) {
+		printf("\tstruct {\n");
+		bpf_object__for_each_map(map, obj) {
+			ident = get_map_ident(map);
+			if (!ident)
+				continue;
+			printf("\t\tstruct bpf_map *%s;\n", ident);
+		}
+		printf("\t} maps;\n");
+	}
+
+	if (prog_cnt) {
+		printf("\tstruct {\n");
+		bpf_object__for_each_program(prog, obj) {
+			printf("\t\tstruct bpf_program *%s;\n",
+			       bpf_program__name(prog));
+		}
+		printf("\t} progs;\n");
+		printf("\tstruct {\n");
+		bpf_object__for_each_program(prog, obj) {
+			printf("\t\tstruct bpf_link *%s;\n",
+			       bpf_program__name(prog));
+		}
+		printf("\t} links;\n");
+	}
+	
+	btf = bpf_object__btf(obj);
+	if (btf) {
+		err = codegen_datasecs(obj, obj_name);
+		if (err)
+			goto out;
+	}
+
+	codegen("\
+		\n\
+		};							    \n\
+									    \n\
+		static inline struct bpf_object_skeleton *		    \n\
+		%1$s__create_skeleton(struct %1$s *obj, struct bpf_embed_data *embed)\n\
+		{							    \n\
+			struct bpf_object_skeleton *s;			    \n\
+									    \n\
+			s = calloc(1, sizeof(*s));			    \n\
+			if (!s)						    \n\
+				return NULL;				    \n\
+									    \n\
+			s->sz = sizeof(*s);				    \n\
+			s->name = \"%1$s\";				    \n\
+			s->data = embed->data;				    \n\
+			s->data_sz = embed->size;			    \n\
+			s->obj = &obj->obj;				    \n\
+		",
+		obj_name
+	);
+	if (map_cnt) {
+		codegen("\
+			\n\
+									    \n\
+				/* maps */				    \n\
+				s->map_cnt = %zu;			    \n\
+				s->map_skel_sz = sizeof(*s->maps);	    \n\
+				s->maps = calloc(s->map_cnt, s->map_skel_sz);\n\
+				if (!s->maps)				    \n\
+					goto err;			    \n\
+			",
+			map_cnt
+		);
+		i = 0;
+		bpf_object__for_each_map(map, obj) {
+			const char *ident = get_map_ident(map);
+
+			if (!ident)
+				continue;
+
+			codegen("\
+				\n\
+									    \n\
+					s->maps[%zu].name = \"%s\";	    \n\
+					s->maps[%zu].map = &obj->maps.%s;   \n\
+				",
+				i, bpf_map__name(map), i, ident);
+			/* memory-mapped internal maps */
+			if (bpf_map__is_internal(map) &&
+			    (bpf_map__def(map)->map_flags & BPF_F_MMAPABLE)) {
+				printf("\ts->maps[%zu].mmaped = (void **)&obj->%s;\n",
+				       i, ident);
+			}
+			i++;
+		}
+	}
+	if (prog_cnt) {
+		codegen("\
+			\n\
+									    \n\
+				/* programs */				    \n\
+				s->prog_cnt = %zu;			    \n\
+				s->prog_skel_sz = sizeof(*s->progs);	    \n\
+				s->progs = calloc(s->prog_cnt, s->prog_skel_sz);\n\
+				if (!s->progs)				    \n\
+					goto err;			    \n\
+			",
+			prog_cnt
+		);
+		i = 0;
+		bpf_object__for_each_program(prog, obj) {
+			codegen("\
+				\n\
+									    \n\
+					s->progs[%1$zu].name = \"%2$s\";    \n\
+					s->progs[%1$zu].prog = &obj->progs.%2$s;\n\
+					s->progs[%1$zu].link = &obj->links.%2$s;\n\
+				",
+				i, bpf_program__name(prog));
+			i++;
+		}
+	}
+	codegen("\
+		\n\
+									    \n\
+			return s;					    \n\
+		err:							    \n\
+			bpf_object__destroy_skeleton(s);		    \n\
+			return NULL;					    \n\
+		}							    \n\
+									    \n\
+		static void						    \n\
+		%1$s__destroy(struct %1$s *obj)				    \n\
+		{							    \n\
+			if (!obj)					    \n\
+				return;					    \n\
+			if (obj->skeleton)				    \n\
+				bpf_object__destroy_skeleton(obj->skeleton);\n\
+			free(obj);					    \n\
+		}							    \n\
+									    \n\
+		static inline struct %1$s *				    \n\
+		%1$s__open_opts(struct bpf_embed_data *embed, const struct bpf_object_open_opts *opts)\n\
+		{							    \n\
+			struct %1$s *obj;				    \n\
+									    \n\
+			obj = calloc(1, sizeof(*obj));			    \n\
+			if (!obj)					    \n\
+				return NULL;				    \n\
+									    \n\
+			obj->skeleton = %1$s__create_skeleton(obj, embed);  \n\
+			if (!obj->skeleton)				    \n\
+				goto err;				    \n\
+									    \n\
+			if (bpf_object__open_skeleton(obj->skeleton, opts)) \n\
+				goto err;				    \n\
+									    \n\
+			return obj;					    \n\
+		err:							    \n\
+			%1$s__destroy(obj);				    \n\
+			return NULL;					    \n\
+		}							    \n\
+									    \n\
+		static inline struct %1$s *				    \n\
+		%1$s__open(struct bpf_embed_data *embed)		    \n\
+		{							    \n\
+			return %1$s__open_opts(embed, NULL);		    \n\
+		}							    \n\
+									    \n\
+		static inline int					    \n\
+		%1$s__load(struct %1$s *obj)				    \n\
+		{							    \n\
+			return bpf_object__load_skeleton(obj->skeleton);    \n\
+		}							    \n\
+									    \n\
+		static inline struct %1$s *				    \n\
+		%1$s__open_and_load(struct bpf_embed_data *embed)	    \n\
+		{							    \n\
+			struct %1$s *obj;				    \n\
+									    \n\
+			obj = %1$s__open(embed);			    \n\
+			if (!obj)					    \n\
+				return NULL;				    \n\
+			if (%1$s__load(obj)) {				    \n\
+				%1$s__destroy(obj);			    \n\
+				return NULL;				    \n\
+			}						    \n\
+			return obj;					    \n\
+		}							    \n\
+									    \n\
+		static inline int					    \n\
+		%1$s__attach(struct %1$s *obj)				    \n\
+		{							    \n\
+			return bpf_object__attach_skeleton(obj->skeleton);  \n\
+		}							    \n\
+									    \n\
+		static inline void					    \n\
+		%1$s__detach(struct %1$s *obj)				    \n\
+		{							    \n\
+			return bpf_object__detach_skeleton(obj->skeleton);  \n\
+		}							    \n\
+									    \n\
+		#endif /* %2$s */					    \n\
+		",
+		obj_name, header_guard
+	);
+	err = 0;
+out:
+	bpf_object__close(obj);
+	return err;
+}
+
+static int do_help(int argc, char **argv)
+{
+	if (json_output) {
+		jsonw_null(json_wtr);
+		return 0;
+	}
+
+	fprintf(stderr,
+		"Usage: %1$s gen skeleton FILE\n"
+		"       %1$s gen help\n"
+		"\n"
+		"       " HELP_SPEC_OPTIONS "\n"
+		"",
+		bin_name);
+
+	return 0;
+}
+
+static const struct cmd cmds[] = {
+	{ "skeleton",	do_skeleton },
+	{ "help",	do_help },
+	{ 0 }
+};
+
+int do_gen(int argc, char **argv)
+{
+	return cmd_select(cmds, argc, argv, do_help);
+}
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index 4764581ff9ea..758b294e8a7d 100644
--- a/tools/bpf/bpftool/main.c
+++ b/tools/bpf/bpftool/main.c
@@ -227,6 +227,7 @@ static const struct cmd cmds[] = {
 	{ "net",	do_net },
 	{ "feature",	do_feature },
 	{ "btf",	do_btf },
+	{ "gen",	do_gen },
 	{ "version",	do_version },
 	{ 0 }
 };
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 2899095f8254..7f49571bf6ce 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -155,6 +155,7 @@ int do_net(int argc, char **arg);
 int do_tracelog(int argc, char **arg);
 int do_feature(int argc, char **argv);
 int do_btf(int argc, char **argv);
+int do_gen(int argc, char **argv);
 
 int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what);
 int prog_parse_fd(int *argc, char ***argv);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 12/15] selftests/bpf: add BPF skeletons selftests and convert attach_probe.c
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (10 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 11/15] bpftool: add skeleton codegen command Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 13/15] selftests/bpf: convert few more selftest to skeletons Andrii Nakryiko
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Add BPF skeleton generation to selftest/bpf's Makefile. Convert attach_probe.c
to use skeleton.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/testing/selftests/bpf/.gitignore        |   2 +
 tools/testing/selftests/bpf/Makefile          |  36 +++--
 .../selftests/bpf/prog_tests/attach_probe.c   | 135 +++++-------------
 .../selftests/bpf/progs/test_attach_probe.c   |  34 ++---
 4 files changed, 74 insertions(+), 133 deletions(-)

diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore
index 419652458da4..ce5af95ede42 100644
--- a/tools/testing/selftests/bpf/.gitignore
+++ b/tools/testing/selftests/bpf/.gitignore
@@ -38,5 +38,7 @@ test_hashmap
 test_btf_dump
 xdping
 test_cpp
+*.skel.h
 /no_alu32
 /bpf_gcc
+/tools
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index e0fe01d9ec33..01788c0d68dd 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -3,10 +3,12 @@ include ../../../../scripts/Kbuild.include
 include ../../../scripts/Makefile.arch
 
 CURDIR := $(abspath .)
-LIBDIR := $(abspath ../../../lib)
+TOOLSDIR := $(abspath ../../..)
+LIBDIR := $(TOOLSDIR)/lib
 BPFDIR := $(LIBDIR)/bpf
-TOOLSDIR := $(abspath ../../../include)
-APIDIR := $(TOOLSDIR)/uapi
+TOOLSINCDIR := $(TOOLSDIR)/include
+BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool
+APIDIR := $(TOOLSINCDIR)/uapi
 GENDIR := $(abspath ../../../../include/generated)
 GENHDR := $(GENDIR)/autoconf.h
 
@@ -19,7 +21,7 @@ LLC		?= llc
 LLVM_OBJCOPY	?= llvm-objcopy
 BPF_GCC		?= $(shell command -v bpf-gcc;)
 CFLAGS += -g -Wall -O2 $(GENFLAGS) -I$(APIDIR) -I$(LIBDIR) -I$(BPFDIR)	\
-	  -I$(GENDIR) -I$(TOOLSDIR) -I$(CURDIR)				\
+	  -I$(GENDIR) -I$(TOOLSINCDIR) -I$(CURDIR)			\
 	  -Dbpf_prog_load=bpf_prog_test_load				\
 	  -Dbpf_load_program=bpf_test_load_program
 LDLIBS += -lcap -lelf -lrt -lpthread
@@ -117,6 +119,12 @@ $(OUTPUT)/test_cgroup_attach: cgroup_helpers.c
 # force a rebuild of BPFOBJ when its dependencies are updated
 force:
 
+DEFAULT_BPFTOOL := $(OUTPUT)/tools/usr/local/sbin/bpftool
+BPFTOOL ?= $(DEFAULT_BPFTOOL)
+
+$(DEFAULT_BPFTOOL): force
+	$(MAKE) -C $(BPFTOOLDIR) DESTDIR=$(OUTPUT)/tools install
+
 $(BPFOBJ): force
 	$(MAKE) -C $(BPFDIR) OUTPUT=$(OUTPUT)/
 
@@ -180,6 +188,8 @@ define GCC_BPF_BUILD_RULE
 	$(BPF_GCC) $3 $4 -O2 -c $1 -o $2
 endef
 
+SKEL_BLACKLIST := btf__% test_pinning_invalid.c
+
 # Set up extra TRUNNER_XXX "temporary" variables in the environment (relies on
 # $eval()) and pass control to DEFINE_TEST_RUNNER_RULES.
 # Parameters:
@@ -195,8 +205,11 @@ TRUNNER_EXTRA_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o,		\
 				 $$(filter %.c,$(TRUNNER_EXTRA_SOURCES)))
 TRUNNER_EXTRA_HDRS := $$(filter %.h,$(TRUNNER_EXTRA_SOURCES))
 TRUNNER_TESTS_HDR := $(TRUNNER_TESTS_DIR)/tests.h
-TRUNNER_BPF_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o,		\
-				$$(notdir $$(wildcard $(TRUNNER_BPF_PROGS_DIR)/*.c)))
+TRUNNER_BPF_SRCS := $$(notdir $$(wildcard $(TRUNNER_BPF_PROGS_DIR)/*.c))
+TRUNNER_BPF_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o, $$(TRUNNER_BPF_SRCS))
+TRUNNER_BPF_SKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.skel.h,	\
+				 $$(filter-out $(SKEL_BLACKLIST),	\
+					       $$(TRUNNER_BPF_SRCS)))
 
 # Evaluate rules now with extra TRUNNER_XXX variables above already defined
 $$(eval $$(call DEFINE_TEST_RUNNER_RULES,$1,$2))
@@ -226,6 +239,11 @@ $(TRUNNER_BPF_OBJS): $(TRUNNER_OUTPUT)/%.o:				\
 	$$(call $(TRUNNER_BPF_BUILD_RULE),$$<,$$@,			\
 					  $(TRUNNER_BPF_CFLAGS),	\
 					  $(TRUNNER_BPF_LDFLAGS))
+
+$(TRUNNER_BPF_SKELS): $(TRUNNER_OUTPUT)/%.skel.h:			\
+		      $(TRUNNER_OUTPUT)/%.o				\
+		      | $(BPFTOOL) $(TRUNNER_OUTPUT)
+	$$(BPFTOOL) gen skeleton $$< > $$@
 endif
 
 # ensure we set up tests.h header generation rule just once
@@ -245,6 +263,7 @@ $(TRUNNER_TEST_OBJS): $(TRUNNER_OUTPUT)/%.test.o:			\
 		      $(TRUNNER_TESTS_DIR)/%.c				\
 		      $(TRUNNER_EXTRA_HDRS)				\
 		      $(TRUNNER_BPF_OBJS)				\
+		      $(TRUNNER_BPF_SKELS)				\
 		      $$(BPFOBJ) | $(TRUNNER_OUTPUT)
 	cd $$(@D) && $$(CC) $$(CFLAGS) -c $(CURDIR)/$$< $$(LDLIBS) -o $$(@F)
 
@@ -255,9 +274,9 @@ $(TRUNNER_EXTRA_OBJS): $(TRUNNER_OUTPUT)/%.o:				\
 		       $$(BPFOBJ) | $(TRUNNER_OUTPUT)
 	$$(CC) $$(CFLAGS) -c $$< $$(LDLIBS) -o $$@
 
+# only copy extra resources if in flavored build
 $(TRUNNER_BINARY)-extras: $(TRUNNER_EXTRA_FILES) | $(TRUNNER_OUTPUT)
 ifneq ($2,)
-	# only copy extra resources if in flavored build
 	cp -a $$^ $(TRUNNER_OUTPUT)/
 endif
 
@@ -323,4 +342,5 @@ $(OUTPUT)/test_cpp: test_cpp.cpp $(BPFOBJ)
 
 EXTRA_CLEAN := $(TEST_CUSTOM_PROGS)					\
 	prog_tests/tests.h map_tests/tests.h verifier/tests.h		\
-	feature $(OUTPUT)/*.o $(OUTPUT)/no_alu32 $(OUTPUT)/bpf_gcc
+	feature $(OUTPUT)/*.o $(OUTPUT)/no_alu32 $(OUTPUT)/bpf_gcc	\
+	tools *.skel.h
diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c
index b2e7c1424b07..60da1d08daa0 100644
--- a/tools/testing/selftests/bpf/prog_tests/attach_probe.c
+++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <test_progs.h>
+#include "test_attach_probe.skel.h"
 
 ssize_t get_base_addr() {
 	size_t start;
@@ -25,26 +26,10 @@ BPF_EMBED_OBJ(probe, "test_attach_probe.o");
 
 void test_attach_probe(void)
 {
-	const char *kprobe_name = "kprobe/sys_nanosleep";
-	const char *kretprobe_name = "kretprobe/sys_nanosleep";
-	const char *uprobe_name = "uprobe/trigger_func";
-	const char *uretprobe_name = "uretprobe/trigger_func";
-	const int kprobe_idx = 0, kretprobe_idx = 1;
-	const int uprobe_idx = 2, uretprobe_idx = 3;
-	const char *obj_name = "attach_probe";
-	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
-		.object_name = obj_name,
-		.relaxed_maps = true,
-	);
-	struct bpf_program *kprobe_prog, *kretprobe_prog;
-	struct bpf_program *uprobe_prog, *uretprobe_prog;
-	struct bpf_object *obj;
-	int err, duration = 0, res;
-	struct bpf_link *kprobe_link = NULL;
-	struct bpf_link *kretprobe_link = NULL;
-	struct bpf_link *uprobe_link = NULL;
-	struct bpf_link *uretprobe_link = NULL;
-	int results_map_fd;
+	int duration = 0;
+	struct bpf_link *kprobe_link, *kretprobe_link;
+	struct bpf_link *uprobe_link, *uretprobe_link;
+	struct test_attach_probe* skel;
 	size_t uprobe_offset;
 	ssize_t base_addr;
 
@@ -54,124 +39,68 @@ void test_attach_probe(void)
 		return;
 	uprobe_offset = (size_t)&get_base_addr - base_addr;
 
-	/* open object */
-	obj = bpf_object__open_mem(probe_embed.data, probe_embed.size,
-				   &open_opts);
-	if (CHECK(IS_ERR(obj), "obj_open_mem", "err %ld\n", PTR_ERR(obj)))
+	skel = test_attach_probe__open_and_load(&probe_embed);
+	if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
 		return;
-
-	if (CHECK(strcmp(bpf_object__name(obj), obj_name), "obj_name",
-		  "wrong obj name '%s', expected '%s'\n",
-		  bpf_object__name(obj), obj_name))
-		goto cleanup;
-
-	kprobe_prog = bpf_object__find_program_by_title(obj, kprobe_name);
-	if (CHECK(!kprobe_prog, "find_probe",
-		  "prog '%s' not found\n", kprobe_name))
-		goto cleanup;
-	kretprobe_prog = bpf_object__find_program_by_title(obj, kretprobe_name);
-	if (CHECK(!kretprobe_prog, "find_probe",
-		  "prog '%s' not found\n", kretprobe_name))
-		goto cleanup;
-	uprobe_prog = bpf_object__find_program_by_title(obj, uprobe_name);
-	if (CHECK(!uprobe_prog, "find_probe",
-		  "prog '%s' not found\n", uprobe_name))
-		goto cleanup;
-	uretprobe_prog = bpf_object__find_program_by_title(obj, uretprobe_name);
-	if (CHECK(!uretprobe_prog, "find_probe",
-		  "prog '%s' not found\n", uretprobe_name))
+	if (CHECK(!skel->bss, "check_bss", ".bss wasn't mmap()-ed\n"))
 		goto cleanup;
 
-	/* create maps && load programs */
-	err = bpf_object__load(obj);
-	if (CHECK(err, "obj_load", "err %d\n", err))
-		goto cleanup;
-
-	/* load maps */
-	results_map_fd = bpf_find_map(__func__, obj, "results_map");
-	if (CHECK(results_map_fd < 0, "find_results_map",
-		  "err %d\n", results_map_fd))
-		goto cleanup;
-
-	kprobe_link = bpf_program__attach_kprobe(kprobe_prog,
+	kprobe_link = bpf_program__attach_kprobe(skel->progs.handle_kprobe,
 						 false /* retprobe */,
 						 SYS_NANOSLEEP_KPROBE_NAME);
 	if (CHECK(IS_ERR(kprobe_link), "attach_kprobe",
-		  "err %ld\n", PTR_ERR(kprobe_link))) {
-		kprobe_link = NULL;
+		  "err %ld\n", PTR_ERR(kprobe_link)))
 		goto cleanup;
-	}
-	kretprobe_link = bpf_program__attach_kprobe(kretprobe_prog,
+	skel->links.handle_kprobe = kprobe_link;
+
+	kretprobe_link = bpf_program__attach_kprobe(skel->progs.handle_kretprobe,
 						    true /* retprobe */,
 						    SYS_NANOSLEEP_KPROBE_NAME);
 	if (CHECK(IS_ERR(kretprobe_link), "attach_kretprobe",
-		  "err %ld\n", PTR_ERR(kretprobe_link))) {
-		kretprobe_link = NULL;
+		  "err %ld\n", PTR_ERR(kretprobe_link)))
 		goto cleanup;
-	}
-	uprobe_link = bpf_program__attach_uprobe(uprobe_prog,
+	skel->links.handle_kretprobe = kretprobe_link;
+
+	uprobe_link = bpf_program__attach_uprobe(skel->progs.handle_uprobe,
 						 false /* retprobe */,
 						 0 /* self pid */,
 						 "/proc/self/exe",
 						 uprobe_offset);
 	if (CHECK(IS_ERR(uprobe_link), "attach_uprobe",
-		  "err %ld\n", PTR_ERR(uprobe_link))) {
-		uprobe_link = NULL;
+		  "err %ld\n", PTR_ERR(uprobe_link)))
 		goto cleanup;
-	}
-	uretprobe_link = bpf_program__attach_uprobe(uretprobe_prog,
+	skel->links.handle_uprobe = uprobe_link;
+
+	uretprobe_link = bpf_program__attach_uprobe(skel->progs.handle_uretprobe,
 						    true /* retprobe */,
 						    -1 /* any pid */,
 						    "/proc/self/exe",
 						    uprobe_offset);
 	if (CHECK(IS_ERR(uretprobe_link), "attach_uretprobe",
-		  "err %ld\n", PTR_ERR(uretprobe_link))) {
-		uretprobe_link = NULL;
+		  "err %ld\n", PTR_ERR(uretprobe_link)))
 		goto cleanup;
-	}
+	skel->links.handle_uretprobe = uretprobe_link;
 
 	/* trigger & validate kprobe && kretprobe */
 	usleep(1);
 
-	err = bpf_map_lookup_elem(results_map_fd, &kprobe_idx, &res);
-	if (CHECK(err, "get_kprobe_res",
-		  "failed to get kprobe res: %d\n", err))
+	if (CHECK(skel->bss->kprobe_res != 1, "check_kprobe_res",
+		  "wrong kprobe res: %d\n", skel->bss->kprobe_res))
 		goto cleanup;
-	if (CHECK(res != kprobe_idx + 1, "check_kprobe_res",
-		  "wrong kprobe res: %d\n", res))
-		goto cleanup;
-
-	err = bpf_map_lookup_elem(results_map_fd, &kretprobe_idx, &res);
-	if (CHECK(err, "get_kretprobe_res",
-		  "failed to get kretprobe res: %d\n", err))
-		goto cleanup;
-	if (CHECK(res != kretprobe_idx + 1, "check_kretprobe_res",
-		  "wrong kretprobe res: %d\n", res))
+	if (CHECK(skel->bss->kretprobe_res != 2, "check_kretprobe_res",
+		  "wrong kretprobe res: %d\n", skel->bss->kretprobe_res))
 		goto cleanup;
 
 	/* trigger & validate uprobe & uretprobe */
 	get_base_addr();
 
-	err = bpf_map_lookup_elem(results_map_fd, &uprobe_idx, &res);
-	if (CHECK(err, "get_uprobe_res",
-		  "failed to get uprobe res: %d\n", err))
-		goto cleanup;
-	if (CHECK(res != uprobe_idx + 1, "check_uprobe_res",
-		  "wrong uprobe res: %d\n", res))
-		goto cleanup;
-
-	err = bpf_map_lookup_elem(results_map_fd, &uretprobe_idx, &res);
-	if (CHECK(err, "get_uretprobe_res",
-		  "failed to get uretprobe res: %d\n", err))
+	if (CHECK(skel->bss->uprobe_res != 3, "check_uprobe_res",
+		  "wrong uprobe res: %d\n", skel->bss->uprobe_res))
 		goto cleanup;
-	if (CHECK(res != uretprobe_idx + 1, "check_uretprobe_res",
-		  "wrong uretprobe res: %d\n", res))
+	if (CHECK(skel->bss->uretprobe_res != 4, "check_uretprobe_res",
+		  "wrong uretprobe res: %d\n", skel->bss->uretprobe_res))
 		goto cleanup;
 
 cleanup:
-	bpf_link__destroy(kprobe_link);
-	bpf_link__destroy(kretprobe_link);
-	bpf_link__destroy(uprobe_link);
-	bpf_link__destroy(uretprobe_link);
-	bpf_object__close(obj);
+	test_attach_probe__destroy(skel);
 }
diff --git a/tools/testing/selftests/bpf/progs/test_attach_probe.c b/tools/testing/selftests/bpf/progs/test_attach_probe.c
index 534621e38906..221b69700625 100644
--- a/tools/testing/selftests/bpf/progs/test_attach_probe.c
+++ b/tools/testing/selftests/bpf/progs/test_attach_probe.c
@@ -5,46 +5,36 @@
 #include <linux/bpf.h>
 #include "bpf_helpers.h"
 
-struct {
-	__uint(type, BPF_MAP_TYPE_ARRAY);
-	__uint(max_entries, 4);
-	__type(key, int);
-	__type(value, int);
-} results_map SEC(".maps");
+int kprobe_res = 0;
+int kretprobe_res = 0;
+int uprobe_res = 0;
+int uretprobe_res = 0;
 
 SEC("kprobe/sys_nanosleep")
-int handle_sys_nanosleep_entry(struct pt_regs *ctx)
+int handle_kprobe(struct pt_regs *ctx)
 {
-	const int key = 0, value = 1;
-
-	bpf_map_update_elem(&results_map, &key, &value, 0);
+	kprobe_res = 1;
 	return 0;
 }
 
 SEC("kretprobe/sys_nanosleep")
-int handle_sys_getpid_return(struct pt_regs *ctx)
+int handle_kretprobe(struct pt_regs *ctx)
 {
-	const int key = 1, value = 2;
-
-	bpf_map_update_elem(&results_map, &key, &value, 0);
+	kretprobe_res = 2;
 	return 0;
 }
 
 SEC("uprobe/trigger_func")
-int handle_uprobe_entry(struct pt_regs *ctx)
+int handle_uprobe(struct pt_regs *ctx)
 {
-	const int key = 2, value = 3;
-
-	bpf_map_update_elem(&results_map, &key, &value, 0);
+	uprobe_res = 3;
 	return 0;
 }
 
 SEC("uretprobe/trigger_func")
-int handle_uprobe_return(struct pt_regs *ctx)
+int handle_uretprobe(struct pt_regs *ctx)
 {
-	const int key = 3, value = 4;
-
-	bpf_map_update_elem(&results_map, &key, &value, 0);
+	uretprobe_res = 4;
 	return 0;
 }
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 13/15] selftests/bpf: convert few more selftest to skeletons
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (11 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 12/15] selftests/bpf: add BPF skeletons selftests and convert attach_probe.c Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 14/15] selftests/bpf: add test validating data section to struct convertion layout Andrii Nakryiko
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Convert few more selftests to use generated BPF skeletons as a demonstration
on how to use it.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 .../selftests/bpf/prog_tests/fentry_fexit.c   | 105 ++++++------------
 .../selftests/bpf/prog_tests/fentry_test.c    |  72 +++++-------
 tools/testing/selftests/bpf/prog_tests/mmap.c |  58 ++++------
 .../bpf/prog_tests/stacktrace_build_id.c      |  79 +++++--------
 .../bpf/prog_tests/stacktrace_build_id_nmi.c  |  84 ++++++--------
 5 files changed, 149 insertions(+), 249 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c
index 40bcff2cc274..110fcf053fd0 100644
--- a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c
+++ b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c
@@ -1,90 +1,59 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) 2019 Facebook */
 #include <test_progs.h>
+#include "test_pkt_access.skel.h"
+#include "fentry_test.skel.h"
+#include "fexit_test.skel.h"
+
+BPF_EMBED_OBJ(pkt_access, "test_pkt_access.o");
+BPF_EMBED_OBJ(fentry, "fentry_test.o");
+BPF_EMBED_OBJ(fexit, "fexit_test.o");
 
 void test_fentry_fexit(void)
 {
-	struct bpf_prog_load_attr attr_fentry = {
-		.file = "./fentry_test.o",
-	};
-	struct bpf_prog_load_attr attr_fexit = {
-		.file = "./fexit_test.o",
-	};
-
-	struct bpf_object *obj_fentry = NULL, *obj_fexit = NULL, *pkt_obj;
-	struct bpf_map *data_map_fentry, *data_map_fexit;
-	char fentry_name[] = "fentry/bpf_fentry_testX";
-	char fexit_name[] = "fexit/bpf_fentry_testX";
-	int err, pkt_fd, kfree_skb_fd, i;
-	struct bpf_link *link[12] = {};
-	struct bpf_program *prog[12];
-	__u32 duration, retval;
-	const int zero = 0;
-	u64 result[12];
-
-	err = bpf_prog_load("./test_pkt_access.o", BPF_PROG_TYPE_SCHED_CLS,
-			    &pkt_obj, &pkt_fd);
-	if (CHECK(err, "prog_load sched cls", "err %d errno %d\n", err, errno))
+	struct test_pkt_access *pkt_skel = NULL;
+	struct fentry_test *fentry_skel = NULL;
+	struct fexit_test *fexit_skel = NULL;
+	__u64 *fentry_res, *fexit_res;
+	__u32 duration = 0, retval;
+	int err, pkt_fd, i;
+
+	pkt_skel = test_pkt_access__open_and_load(&pkt_access_embed);
+	if (CHECK(!pkt_skel, "pkt_skel_load", "pkt_access skeleton failed\n"))
 		return;
-	err = bpf_prog_load_xattr(&attr_fentry, &obj_fentry, &kfree_skb_fd);
-	if (CHECK(err, "prog_load fail", "err %d errno %d\n", err, errno))
+	fentry_skel = fentry_test__open_and_load(&fentry_embed);
+	if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n"))
 		goto close_prog;
-	err = bpf_prog_load_xattr(&attr_fexit, &obj_fexit, &kfree_skb_fd);
-	if (CHECK(err, "prog_load fail", "err %d errno %d\n", err, errno))
+	fexit_skel = fexit_test__open_and_load(&fexit_embed);
+	if (CHECK(!fexit_skel, "fexit_skel_load", "fexit skeleton failed\n"))
 		goto close_prog;
 
-	for (i = 0; i < 6; i++) {
-		fentry_name[sizeof(fentry_name) - 2] = '1' + i;
-		prog[i] = bpf_object__find_program_by_title(obj_fentry, fentry_name);
-		if (CHECK(!prog[i], "find_prog", "prog %s not found\n", fentry_name))
-			goto close_prog;
-		link[i] = bpf_program__attach_trace(prog[i]);
-		if (CHECK(IS_ERR(link[i]), "attach_trace", "failed to link\n"))
-			goto close_prog;
-	}
-	data_map_fentry = bpf_object__find_map_by_name(obj_fentry, "fentry_t.bss");
-	if (CHECK(!data_map_fentry, "find_data_map", "data map not found\n"))
+	err = fentry_test__attach(fentry_skel);
+	if (CHECK(err, "fentry_attach", "fentry attach failed: %d\n", err))
 		goto close_prog;
-
-	for (i = 6; i < 12; i++) {
-		fexit_name[sizeof(fexit_name) - 2] = '1' + i - 6;
-		prog[i] = bpf_object__find_program_by_title(obj_fexit, fexit_name);
-		if (CHECK(!prog[i], "find_prog", "prog %s not found\n", fexit_name))
-			goto close_prog;
-		link[i] = bpf_program__attach_trace(prog[i]);
-		if (CHECK(IS_ERR(link[i]), "attach_trace", "failed to link\n"))
-			goto close_prog;
-	}
-	data_map_fexit = bpf_object__find_map_by_name(obj_fexit, "fexit_te.bss");
-	if (CHECK(!data_map_fexit, "find_data_map", "data map not found\n"))
+	err = fexit_test__attach(fexit_skel);
+	if (CHECK(err, "fexit_attach", "fexit attach failed: %d\n", err))
 		goto close_prog;
 
+	pkt_fd = bpf_program__fd(pkt_skel->progs.test_pkt_access);
 	err = bpf_prog_test_run(pkt_fd, 1, &pkt_v6, sizeof(pkt_v6),
 				NULL, NULL, &retval, &duration);
 	CHECK(err || retval, "ipv6",
 	      "err %d errno %d retval %d duration %d\n",
 	      err, errno, retval, duration);
 
-	err = bpf_map_lookup_elem(bpf_map__fd(data_map_fentry), &zero, &result);
-	if (CHECK(err, "get_result",
-		  "failed to get output data: %d\n", err))
-		goto close_prog;
-
-	err = bpf_map_lookup_elem(bpf_map__fd(data_map_fexit), &zero, result + 6);
-	if (CHECK(err, "get_result",
-		  "failed to get output data: %d\n", err))
-		goto close_prog;
-
-	for (i = 0; i < 12; i++)
-		if (CHECK(result[i] != 1, "result", "bpf_fentry_test%d failed err %ld\n",
-			  i % 6 + 1, result[i]))
-			goto close_prog;
+	fentry_res = (__u64 *)fentry_skel->bss;
+	fexit_res = (__u64 *)fexit_skel->bss;
+	printf("%lld\n", fentry_skel->bss->test1_result);
+	for (i = 0; i < 6; i++) {
+		CHECK(fentry_res[i] != 1, "result",
+		      "fentry_test%d failed err %lld\n", i + 1, fentry_res[i]);
+		CHECK(fexit_res[i] != 1, "result",
+		      "fexit_test%d failed err %lld\n", i + 1, fexit_res[i]);
+	}
 
 close_prog:
-	for (i = 0; i < 12; i++)
-		if (!IS_ERR_OR_NULL(link[i]))
-			bpf_link__destroy(link[i]);
-	bpf_object__close(obj_fentry);
-	bpf_object__close(obj_fexit);
-	bpf_object__close(pkt_obj);
+	test_pkt_access__destroy(pkt_skel);
+	fentry_test__destroy(fentry_skel);
+	fexit_test__destroy(fexit_skel);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_test.c b/tools/testing/selftests/bpf/prog_tests/fentry_test.c
index 9fb103193878..46a4afdf507a 100644
--- a/tools/testing/selftests/bpf/prog_tests/fentry_test.c
+++ b/tools/testing/selftests/bpf/prog_tests/fentry_test.c
@@ -1,64 +1,46 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) 2019 Facebook */
 #include <test_progs.h>
+#include "test_pkt_access.skel.h"
+#include "fentry_test.skel.h"
+
+BPF_EMBED_OBJ_DECLARE(pkt_access);
+BPF_EMBED_OBJ_DECLARE(fentry);
 
 void test_fentry_test(void)
 {
-	struct bpf_prog_load_attr attr = {
-		.file = "./fentry_test.o",
-	};
-
-	char prog_name[] = "fentry/bpf_fentry_testX";
-	struct bpf_object *obj = NULL, *pkt_obj;
-	int err, pkt_fd, kfree_skb_fd, i;
-	struct bpf_link *link[6] = {};
-	struct bpf_program *prog[6];
+	struct test_pkt_access *pkt_skel = NULL;
+	struct fentry_test *fentry_skel = NULL;
+	int err, pkt_fd, i;
 	__u32 duration, retval;
-	struct bpf_map *data_map;
-	const int zero = 0;
-	u64 result[6];
+	__u64 *result;
 
-	err = bpf_prog_load("./test_pkt_access.o", BPF_PROG_TYPE_SCHED_CLS,
-			    &pkt_obj, &pkt_fd);
-	if (CHECK(err, "prog_load sched cls", "err %d errno %d\n", err, errno))
+	pkt_skel = test_pkt_access__open_and_load(&pkt_access_embed);
+	if (CHECK(!pkt_skel, "pkt_skel_load", "pkt_access skeleton failed\n"))
 		return;
-	err = bpf_prog_load_xattr(&attr, &obj, &kfree_skb_fd);
-	if (CHECK(err, "prog_load fail", "err %d errno %d\n", err, errno))
-		goto close_prog;
+	fentry_skel = fentry_test__open_and_load(&fentry_embed);
+	if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n"))
+		goto cleanup;
 
-	for (i = 0; i < 6; i++) {
-		prog_name[sizeof(prog_name) - 2] = '1' + i;
-		prog[i] = bpf_object__find_program_by_title(obj, prog_name);
-		if (CHECK(!prog[i], "find_prog", "prog %s not found\n", prog_name))
-			goto close_prog;
-		link[i] = bpf_program__attach_trace(prog[i]);
-		if (CHECK(IS_ERR(link[i]), "attach_trace", "failed to link\n"))
-			goto close_prog;
-	}
-	data_map = bpf_object__find_map_by_name(obj, "fentry_t.bss");
-	if (CHECK(!data_map, "find_data_map", "data map not found\n"))
-		goto close_prog;
+	err = fentry_test__attach(fentry_skel);
+	if (CHECK(err, "fentry_attach", "fentry attach failed: %d\n", err))
+		goto cleanup;
 
+	pkt_fd = bpf_program__fd(pkt_skel->progs.test_pkt_access);
 	err = bpf_prog_test_run(pkt_fd, 1, &pkt_v6, sizeof(pkt_v6),
 				NULL, NULL, &retval, &duration);
 	CHECK(err || retval, "ipv6",
 	      "err %d errno %d retval %d duration %d\n",
 	      err, errno, retval, duration);
 
-	err = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, &result);
-	if (CHECK(err, "get_result",
-		  "failed to get output data: %d\n", err))
-		goto close_prog;
-
-	for (i = 0; i < 6; i++)
-		if (CHECK(result[i] != 1, "result", "bpf_fentry_test%d failed err %ld\n",
-			  i + 1, result[i]))
-			goto close_prog;
+	result = (__u64 *)fentry_skel->bss;
+	for (i = 0; i < 6; i++) {
+		if (CHECK(result[i] != 1, "result",
+			  "fentry_test%d failed err %lld\n", i + 1, result[i]))
+			goto cleanup;
+	}
 
-close_prog:
-	for (i = 0; i < 6; i++)
-		if (!IS_ERR_OR_NULL(link[i]))
-			bpf_link__destroy(link[i]);
-	bpf_object__close(obj);
-	bpf_object__close(pkt_obj);
+cleanup:
+	fentry_test__destroy(fentry_skel);
+	test_pkt_access__destroy(pkt_skel);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/mmap.c b/tools/testing/selftests/bpf/prog_tests/mmap.c
index 051a6d48762c..95a44d37ccea 100644
--- a/tools/testing/selftests/bpf/prog_tests/mmap.c
+++ b/tools/testing/selftests/bpf/prog_tests/mmap.c
@@ -1,59 +1,41 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <test_progs.h>
 #include <sys/mman.h>
+#include "test_mmap.skel.h"
 
 struct map_data {
 	__u64 val[512 * 4];
 };
 
-struct bss_data {
-	__u64 in_val;
-	__u64 out_val;
-};
-
 static size_t roundup_page(size_t sz)
 {
 	long page_size = sysconf(_SC_PAGE_SIZE);
 	return (sz + page_size - 1) / page_size * page_size;
 }
 
+BPF_EMBED_OBJ(test_mmap, "test_mmap.o");
+
 void test_mmap(void)
 {
-	const char *file = "test_mmap.o";
-	const char *probe_name = "raw_tracepoint/sys_enter";
-	const char *tp_name = "sys_enter";
-	const size_t bss_sz = roundup_page(sizeof(struct bss_data));
+	const size_t bss_sz = roundup_page(sizeof(struct test_mmap__bss));
 	const size_t map_sz = roundup_page(sizeof(struct map_data));
 	const int zero = 0, one = 1, two = 2, far = 1500;
 	const long page_size = sysconf(_SC_PAGE_SIZE);
 	int err, duration = 0, i, data_map_fd;
-	struct bpf_program *prog;
-	struct bpf_object *obj;
-	struct bpf_link *link = NULL;
 	struct bpf_map *data_map, *bss_map;
 	void *bss_mmaped = NULL, *map_mmaped = NULL, *tmp1, *tmp2;
-	volatile struct bss_data *bss_data;
-	volatile struct map_data *map_data;
+	struct test_mmap__bss *bss_data;
+	struct map_data *map_data;
+	struct test_mmap *skel;
 	__u64 val = 0;
 
-	obj = bpf_object__open_file("test_mmap.o", NULL);
-	if (CHECK(IS_ERR(obj), "obj_open", "failed to open '%s': %ld\n",
-		  file, PTR_ERR(obj)))
+
+	skel = test_mmap__open_and_load(&test_mmap_embed);
+	if (CHECK(!skel, "skel_open_and_load", "skeleton open/load failed\n"))
 		return;
-	prog = bpf_object__find_program_by_title(obj, probe_name);
-	if (CHECK(!prog, "find_probe", "prog '%s' not found\n", probe_name))
-		goto cleanup;
-	err = bpf_object__load(obj);
-	if (CHECK(err, "obj_load", "failed to load prog '%s': %d\n",
-		  probe_name, err))
-		goto cleanup;
 
-	bss_map = bpf_object__find_map_by_name(obj, "test_mma.bss");
-	if (CHECK(!bss_map, "find_bss_map", ".bss map not found\n"))
-		goto cleanup;
-	data_map = bpf_object__find_map_by_name(obj, "data_map");
-	if (CHECK(!data_map, "find_data_map", "data_map map not found\n"))
-		goto cleanup;
+	bss_map = skel->maps.bss;
+	data_map = skel->maps.data_map;
 	data_map_fd = bpf_map__fd(data_map);
 
 	bss_mmaped = mmap(NULL, bss_sz, PROT_READ | PROT_WRITE, MAP_SHARED,
@@ -77,13 +59,15 @@ void test_mmap(void)
 
 	CHECK_FAIL(bss_data->in_val);
 	CHECK_FAIL(bss_data->out_val);
+	CHECK_FAIL(skel->bss->in_val);
+	CHECK_FAIL(skel->bss->out_val);
 	CHECK_FAIL(map_data->val[0]);
 	CHECK_FAIL(map_data->val[1]);
 	CHECK_FAIL(map_data->val[2]);
 	CHECK_FAIL(map_data->val[far]);
 
-	link = bpf_program__attach_raw_tracepoint(prog, tp_name);
-	if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", PTR_ERR(link)))
+	err = test_mmap__attach(skel);
+	if (CHECK(err, "attach_raw_tp", "err %d\n", err))
 		goto cleanup;
 
 	bss_data->in_val = 123;
@@ -94,6 +78,8 @@ void test_mmap(void)
 
 	CHECK_FAIL(bss_data->in_val != 123);
 	CHECK_FAIL(bss_data->out_val != 123);
+	CHECK_FAIL(skel->bss->in_val != 123);
+	CHECK_FAIL(skel->bss->out_val != 123);
 	CHECK_FAIL(map_data->val[0] != 111);
 	CHECK_FAIL(map_data->val[1] != 222);
 	CHECK_FAIL(map_data->val[2] != 123);
@@ -160,6 +146,8 @@ void test_mmap(void)
 	usleep(1);
 	CHECK_FAIL(bss_data->in_val != 321);
 	CHECK_FAIL(bss_data->out_val != 321);
+	CHECK_FAIL(skel->bss->in_val != 321);
+	CHECK_FAIL(skel->bss->out_val != 321);
 	CHECK_FAIL(map_data->val[0] != 111);
 	CHECK_FAIL(map_data->val[1] != 222);
 	CHECK_FAIL(map_data->val[2] != 321);
@@ -203,6 +191,8 @@ void test_mmap(void)
 	map_data = tmp2;
 	CHECK_FAIL(bss_data->in_val != 321);
 	CHECK_FAIL(bss_data->out_val != 321);
+	CHECK_FAIL(skel->bss->in_val != 321);
+	CHECK_FAIL(skel->bss->out_val != 321);
 	CHECK_FAIL(map_data->val[0] != 111);
 	CHECK_FAIL(map_data->val[1] != 222);
 	CHECK_FAIL(map_data->val[2] != 321);
@@ -214,7 +204,5 @@ void test_mmap(void)
 		CHECK_FAIL(munmap(bss_mmaped, bss_sz));
 	if (map_mmaped)
 		CHECK_FAIL(munmap(map_mmaped, map_sz));
-	if (!IS_ERR_OR_NULL(link))
-		bpf_link__destroy(link);
-	bpf_object__close(obj);
+	test_mmap__destroy(skel);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c
index d841dced971f..4af8b8253f25 100644
--- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c
+++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c
@@ -1,16 +1,16 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <test_progs.h>
+#include "test_stacktrace_build_id.skel.h"
+
+BPF_EMBED_OBJ(stacktrace_build_id, "test_stacktrace_build_id.o");
 
 void test_stacktrace_build_id(void)
 {
+
 	int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
-	const char *prog_name = "tracepoint/random/urandom_read";
-	const char *file = "./test_stacktrace_build_id.o";
-	int err, prog_fd, stack_trace_len;
+	struct test_stacktrace_build_id *skel;
+	int err, stack_trace_len;
 	__u32 key, previous_key, val, duration = 0;
-	struct bpf_program *prog;
-	struct bpf_object *obj;
-	struct bpf_link *link = NULL;
 	char buf[256];
 	int i, j;
 	struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH];
@@ -18,43 +18,24 @@ void test_stacktrace_build_id(void)
 	int retry = 1;
 
 retry:
-	err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
-	if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
+	skel = test_stacktrace_build_id__open_and_load(&stacktrace_build_id_embed);
+	if (CHECK(!skel, "skel_open_and_load", "skeleton open/load failed\n"))
 		return;
 
-	prog = bpf_object__find_program_by_title(obj, prog_name);
-	if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name))
-		goto close_prog;
-
-	link = bpf_program__attach_tracepoint(prog, "random", "urandom_read");
-	if (CHECK(IS_ERR(link), "attach_tp", "err %ld\n", PTR_ERR(link)))
-		goto close_prog;
+	err = test_stacktrace_build_id__attach(skel);
+	if (CHECK(err, "attach_tp", "err %d\n", err))
+		goto cleanup;
 
 	/* find map fds */
-	control_map_fd = bpf_find_map(__func__, obj, "control_map");
-	if (CHECK(control_map_fd < 0, "bpf_find_map control_map",
-		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
-
-	stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap");
-	if (CHECK(stackid_hmap_fd < 0, "bpf_find_map stackid_hmap",
-		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
-
-	stackmap_fd = bpf_find_map(__func__, obj, "stackmap");
-	if (CHECK(stackmap_fd < 0, "bpf_find_map stackmap", "err %d errno %d\n",
-		  err, errno))
-		goto disable_pmu;
-
-	stack_amap_fd = bpf_find_map(__func__, obj, "stack_amap");
-	if (CHECK(stack_amap_fd < 0, "bpf_find_map stack_amap",
-		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
+	control_map_fd = bpf_map__fd(skel->maps.control_map);
+	stackid_hmap_fd = bpf_map__fd(skel->maps.stackid_hmap);
+	stackmap_fd = bpf_map__fd(skel->maps.stackmap);
+	stack_amap_fd = bpf_map__fd(skel->maps.stack_amap);
 
 	if (CHECK_FAIL(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")))
-		goto disable_pmu;
+		goto cleanup;
 	if (CHECK_FAIL(system("./urandom_read")))
-		goto disable_pmu;
+		goto cleanup;
 	/* disable stack trace collection */
 	key = 0;
 	val = 1;
@@ -66,23 +47,23 @@ void test_stacktrace_build_id(void)
 	err = compare_map_keys(stackid_hmap_fd, stackmap_fd);
 	if (CHECK(err, "compare_map_keys stackid_hmap vs. stackmap",
 		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
+		goto cleanup;
 
 	err = compare_map_keys(stackmap_fd, stackid_hmap_fd);
 	if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap",
 		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
+		goto cleanup;
 
 	err = extract_build_id(buf, 256);
 
 	if (CHECK(err, "get build_id with readelf",
 		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
+		goto cleanup;
 
 	err = bpf_map_get_next_key(stackmap_fd, NULL, &key);
 	if (CHECK(err, "get_next_key from stackmap",
 		  "err %d, errno %d\n", err, errno))
-		goto disable_pmu;
+		goto cleanup;
 
 	do {
 		char build_id[64];
@@ -90,7 +71,7 @@ void test_stacktrace_build_id(void)
 		err = bpf_map_lookup_elem(stackmap_fd, &key, id_offs);
 		if (CHECK(err, "lookup_elem from stackmap",
 			  "err %d, errno %d\n", err, errno))
-			goto disable_pmu;
+			goto cleanup;
 		for (i = 0; i < PERF_MAX_STACK_DEPTH; ++i)
 			if (id_offs[i].status == BPF_STACK_BUILD_ID_VALID &&
 			    id_offs[i].offset != 0) {
@@ -108,8 +89,7 @@ void test_stacktrace_build_id(void)
 	 * try it one more time.
 	 */
 	if (build_id_matches < 1 && retry--) {
-		bpf_link__destroy(link);
-		bpf_object__close(obj);
+		test_stacktrace_build_id__destroy(skel);
 		printf("%s:WARN:Didn't find expected build ID from the map, retrying\n",
 		       __func__);
 		goto retry;
@@ -117,17 +97,14 @@ void test_stacktrace_build_id(void)
 
 	if (CHECK(build_id_matches < 1, "build id match",
 		  "Didn't find expected build ID from the map\n"))
-		goto disable_pmu;
+		goto cleanup;
 
-	stack_trace_len = PERF_MAX_STACK_DEPTH
-		* sizeof(struct bpf_stack_build_id);
+	stack_trace_len = PERF_MAX_STACK_DEPTH *
+			  sizeof(struct bpf_stack_build_id);
 	err = compare_stack_ips(stackmap_fd, stack_amap_fd, stack_trace_len);
 	CHECK(err, "compare_stack_ips stackmap vs. stack_amap",
 	      "err %d errno %d\n", err, errno);
 
-disable_pmu:
-	bpf_link__destroy(link);
-
-close_prog:
-	bpf_object__close(obj);
+cleanup:
+	test_stacktrace_build_id__destroy(skel);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c
index f62aa0eb959b..32fb03881a7b 100644
--- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c
+++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <test_progs.h>
+#include "test_stacktrace_build_id.skel.h"
 
 static __u64 read_perf_max_sample_freq(void)
 {
@@ -14,21 +15,19 @@ static __u64 read_perf_max_sample_freq(void)
 	return sample_freq;
 }
 
+BPF_EMBED_OBJ_DECLARE(stacktrace_build_id);
+
 void test_stacktrace_build_id_nmi(void)
 {
-	int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
-	const char *prog_name = "tracepoint/random/urandom_read";
-	const char *file = "./test_stacktrace_build_id.o";
-	int err, pmu_fd, prog_fd;
+	int control_map_fd, stackid_hmap_fd, stackmap_fd;
+	struct test_stacktrace_build_id *skel;
+	int err, pmu_fd;
 	struct perf_event_attr attr = {
 		.freq = 1,
 		.type = PERF_TYPE_HARDWARE,
 		.config = PERF_COUNT_HW_CPU_CYCLES,
 	};
 	__u32 key, previous_key, val, duration = 0;
-	struct bpf_program *prog;
-	struct bpf_object *obj;
-	struct bpf_link *link;
 	char buf[256];
 	int i, j;
 	struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH];
@@ -38,13 +37,16 @@ void test_stacktrace_build_id_nmi(void)
 	attr.sample_freq = read_perf_max_sample_freq();
 
 retry:
-	err = bpf_prog_load(file, BPF_PROG_TYPE_PERF_EVENT, &obj, &prog_fd);
-	if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
+	skel = test_stacktrace_build_id__open(&stacktrace_build_id_embed);
+	if (CHECK(!skel, "skel_open", "skeleton open failed\n"))
 		return;
 
-	prog = bpf_object__find_program_by_title(obj, prog_name);
-	if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name))
-		goto close_prog;
+	/* override program type */
+	bpf_program__set_perf_event(skel->progs.oncpu);
+
+	err = test_stacktrace_build_id__load(skel);
+	if (CHECK(err, "skel_load", "skeleton load failed: %d\n", err))
+		goto cleanup;
 
 	pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
 			 0 /* cpu 0 */, -1 /* group id */,
@@ -52,40 +54,25 @@ void test_stacktrace_build_id_nmi(void)
 	if (CHECK(pmu_fd < 0, "perf_event_open",
 		  "err %d errno %d. Does the test host support PERF_COUNT_HW_CPU_CYCLES?\n",
 		  pmu_fd, errno))
-		goto close_prog;
+		goto cleanup;
 
-	link = bpf_program__attach_perf_event(prog, pmu_fd);
-	if (CHECK(IS_ERR(link), "attach_perf_event",
-		  "err %ld\n", PTR_ERR(link))) {
+	skel->links.oncpu = bpf_program__attach_perf_event(skel->progs.oncpu,
+							   pmu_fd);
+	if (CHECK(IS_ERR(skel->links.oncpu), "attach_perf_event",
+		  "err %ld\n", PTR_ERR(skel->links.oncpu))) {
 		close(pmu_fd);
-		goto close_prog;
+		goto cleanup;
 	}
 
 	/* find map fds */
-	control_map_fd = bpf_find_map(__func__, obj, "control_map");
-	if (CHECK(control_map_fd < 0, "bpf_find_map control_map",
-		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
-
-	stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap");
-	if (CHECK(stackid_hmap_fd < 0, "bpf_find_map stackid_hmap",
-		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
-
-	stackmap_fd = bpf_find_map(__func__, obj, "stackmap");
-	if (CHECK(stackmap_fd < 0, "bpf_find_map stackmap", "err %d errno %d\n",
-		  err, errno))
-		goto disable_pmu;
-
-	stack_amap_fd = bpf_find_map(__func__, obj, "stack_amap");
-	if (CHECK(stack_amap_fd < 0, "bpf_find_map stack_amap",
-		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
+	control_map_fd = bpf_map__fd(skel->maps.control_map);
+	stackid_hmap_fd = bpf_map__fd(skel->maps.stackid_hmap);
+	stackmap_fd = bpf_map__fd(skel->maps.stackmap);
 
 	if (CHECK_FAIL(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")))
-		goto disable_pmu;
+		goto cleanup;
 	if (CHECK_FAIL(system("taskset 0x1 ./urandom_read 100000")))
-		goto disable_pmu;
+		goto cleanup;
 	/* disable stack trace collection */
 	key = 0;
 	val = 1;
@@ -97,23 +84,23 @@ void test_stacktrace_build_id_nmi(void)
 	err = compare_map_keys(stackid_hmap_fd, stackmap_fd);
 	if (CHECK(err, "compare_map_keys stackid_hmap vs. stackmap",
 		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
+		goto cleanup;
 
 	err = compare_map_keys(stackmap_fd, stackid_hmap_fd);
 	if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap",
 		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
+		goto cleanup;
 
 	err = extract_build_id(buf, 256);
 
 	if (CHECK(err, "get build_id with readelf",
 		  "err %d errno %d\n", err, errno))
-		goto disable_pmu;
+		goto cleanup;
 
 	err = bpf_map_get_next_key(stackmap_fd, NULL, &key);
 	if (CHECK(err, "get_next_key from stackmap",
 		  "err %d, errno %d\n", err, errno))
-		goto disable_pmu;
+		goto cleanup;
 
 	do {
 		char build_id[64];
@@ -121,7 +108,7 @@ void test_stacktrace_build_id_nmi(void)
 		err = bpf_map_lookup_elem(stackmap_fd, &key, id_offs);
 		if (CHECK(err, "lookup_elem from stackmap",
 			  "err %d, errno %d\n", err, errno))
-			goto disable_pmu;
+			goto cleanup;
 		for (i = 0; i < PERF_MAX_STACK_DEPTH; ++i)
 			if (id_offs[i].status == BPF_STACK_BUILD_ID_VALID &&
 			    id_offs[i].offset != 0) {
@@ -139,8 +126,7 @@ void test_stacktrace_build_id_nmi(void)
 	 * try it one more time.
 	 */
 	if (build_id_matches < 1 && retry--) {
-		bpf_link__destroy(link);
-		bpf_object__close(obj);
+		test_stacktrace_build_id__destroy(skel);
 		printf("%s:WARN:Didn't find expected build ID from the map, retrying\n",
 		       __func__);
 		goto retry;
@@ -148,7 +134,7 @@ void test_stacktrace_build_id_nmi(void)
 
 	if (CHECK(build_id_matches < 1, "build id match",
 		  "Didn't find expected build ID from the map\n"))
-		goto disable_pmu;
+		goto cleanup;
 
 	/*
 	 * We intentionally skip compare_stack_ips(). This is because we
@@ -157,8 +143,6 @@ void test_stacktrace_build_id_nmi(void)
 	 * BPF_STACK_BUILD_ID_IP;
 	 */
 
-disable_pmu:
-	bpf_link__destroy(link);
-close_prog:
-	bpf_object__close(obj);
+cleanup:
+	test_stacktrace_build_id__destroy(skel);
 }
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 14/15] selftests/bpf: add test validating data section to struct convertion layout
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (12 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 13/15] selftests/bpf: convert few more selftest to skeletons Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-10  1:14 ` [PATCH bpf-next 15/15] bpftool: add `gen skeleton` BASH completions Andrii Nakryiko
  2019-12-11 22:55 ` [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Martin Lau
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Add a simple selftests validating datasection-to-struct layour dumping. Global
variables are constructed in such a way as to cause both natural and
artificial padding (through custom alignment requirement).

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 .../selftests/bpf/prog_tests/skeleton.c       | 47 +++++++++++++++++++
 .../selftests/bpf/progs/test_skeleton.c       | 36 ++++++++++++++
 2 files changed, 83 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/skeleton.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_skeleton.c

diff --git a/tools/testing/selftests/bpf/prog_tests/skeleton.c b/tools/testing/selftests/bpf/prog_tests/skeleton.c
new file mode 100644
index 000000000000..d65a0203e1df
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/skeleton.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+
+struct s {
+	int a;
+	long long b;
+} __attribute__((packed));
+
+#include "test_skeleton.skel.h"
+
+BPF_EMBED_OBJ(skeleton, "test_skeleton.o");
+
+void test_skeleton(void)
+{
+	int duration = 0, err;
+	struct test_skeleton* skel;
+	struct test_skeleton__bss *bss;
+
+	skel = test_skeleton__open_and_load(&skeleton_embed);
+	if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
+		return;
+
+	bss = skel->bss;
+	bss->in1 = 1;
+	bss->in2 = 2;
+	bss->in3 = 3;
+	bss->in4 = 4;
+	bss->in5.a = 5;
+	bss->in5.b = 6;
+
+	err = test_skeleton__attach(skel);
+	if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
+		goto cleanup;
+
+	/* trigger tracepoint */
+	usleep(1);
+
+	CHECK(bss->out1 != 1, "res1", "got %d != exp %d\n", bss->out1, 1);
+	CHECK(bss->out2 != 2, "res2", "got %lld != exp %d\n", bss->out2, 2);
+	CHECK(bss->out3 != 3, "res3", "got %d != exp %d\n", (int)bss->out3, 3);
+	CHECK(bss->out4 != 4, "res4", "got %lld != exp %d\n", bss->out4, 4);
+	CHECK(bss->out5.a != 5, "res5", "got %d != exp %d\n", bss->out5.a, 5);
+	CHECK(bss->out5.b != 6, "res6", "got %lld != exp %d\n", bss->out5.b, 6);
+
+cleanup:
+	test_skeleton__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_skeleton.c b/tools/testing/selftests/bpf/progs/test_skeleton.c
new file mode 100644
index 000000000000..303a841c4d1c
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_skeleton.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017 Facebook
+
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+struct s {
+	int a;
+	long long b;
+} __attribute__((packed));
+
+int in1 = 0;
+long long in2 = 0;
+char in3 = '\0';
+long long in4 __attribute__((aligned(64))) = 0;
+struct s in5 = {};
+
+long long out2 = 0;
+struct s out5 = {};
+char out3 = 0;
+long long out4 = 0;
+int out1 = 0;
+
+
+SEC("raw_tp/sys_enter")
+int handler(const void *ctx)
+{
+	out1 = in1;
+	out2 = in2;
+	out3 = in3;
+	out4 = in4;
+	out5 = in5;
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH bpf-next 15/15] bpftool: add `gen skeleton` BASH completions
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (13 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 14/15] selftests/bpf: add test validating data section to struct convertion layout Andrii Nakryiko
@ 2019-12-10  1:14 ` Andrii Nakryiko
  2019-12-11 22:55 ` [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Martin Lau
  15 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10  1:14 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel
  Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko, Quentin Monnet

Add BASH completions for gen sub-command.

Cc: Quentin Monnet <quentin.monnet@netronome.com>
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/bpf/bpftool/bash-completion/bpftool | 11 +++++++++++
 tools/bpf/bpftool/main.c                  |  2 +-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index 70493a6da206..986519cc58d1 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -716,6 +716,17 @@ _bpftool()
                     ;;
             esac
             ;;
+        gen)
+            case $command in
+                skeleton)
+                    _filedir
+		    ;;
+                *)
+                    [[ $prev == $object ]] && \
+                        COMPREPLY=( $( compgen -W 'skeleton help' -- "$cur" ) )
+                    ;;
+            esac
+            ;;
         cgroup)
             case $command in
                 show|list|tree)
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index 758b294e8a7d..1fe91c558508 100644
--- a/tools/bpf/bpftool/main.c
+++ b/tools/bpf/bpftool/main.c
@@ -58,7 +58,7 @@ static int do_help(int argc, char **argv)
 		"       %s batch file FILE\n"
 		"       %s version\n"
 		"\n"
-		"       OBJECT := { prog | map | cgroup | perf | net | feature | btf }\n"
+		"       OBJECT := { prog | map | cgroup | perf | net | feature | btf | gen }\n"
 		"       " HELP_SPEC_OPTIONS "\n"
 		"",
 		bin_name, bin_name, bin_name);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 03/15] libbpf: move non-public APIs from libbpf.h to libbpf_internal.h
  2019-12-10  1:14 ` [PATCH bpf-next 03/15] libbpf: move non-public APIs from libbpf.h to libbpf_internal.h Andrii Nakryiko
@ 2019-12-10  1:33   ` Jakub Kicinski
  2019-12-10 17:04     ` Andrii Nakryiko
  0 siblings, 1 reply; 57+ messages in thread
From: Jakub Kicinski @ 2019-12-10  1:33 UTC (permalink / raw)
  To: Andrii Nakryiko; +Cc: bpf, netdev, ast, daniel, andrii.nakryiko, kernel-team

On Mon, 9 Dec 2019 17:14:26 -0800, Andrii Nakryiko wrote:
> Few libbpf APIs are not public but currently exposed through libbpf.h to be
> used by bpftool. Move them to libbpf_internal.h, where intent of being
> non-stable and non-public is much more obvious.
> 
> Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> ---
>  tools/bpf/bpftool/net.c         |  1 +
>  tools/lib/bpf/libbpf.h          | 17 -----------------
>  tools/lib/bpf/libbpf_internal.h | 17 +++++++++++++++++
>  3 files changed, 18 insertions(+), 17 deletions(-)
> 
> diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c
> index 4f52d3151616..d93bee298e54 100644
> --- a/tools/bpf/bpftool/net.c
> +++ b/tools/bpf/bpftool/net.c
> @@ -18,6 +18,7 @@
>  
>  #include <bpf.h>
>  #include <nlattr.h>
> +#include "libbpf_internal.h"
>  #include "main.h"
>  #include "netlink_dumper.h"

I thought this idea was unpopular when proposed?

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-10  1:14 ` [PATCH bpf-next 11/15] bpftool: add skeleton codegen command Andrii Nakryiko
@ 2019-12-10  1:57   ` Jakub Kicinski
  2019-12-10 17:11     ` Andrii Nakryiko
  2019-12-11 22:50   ` [Potential Spoof] " Martin Lau
  2019-12-16 14:16   ` Daniel Borkmann
  2 siblings, 1 reply; 57+ messages in thread
From: Jakub Kicinski @ 2019-12-10  1:57 UTC (permalink / raw)
  To: Andrii Nakryiko, LKML
  Cc: bpf, netdev, ast, daniel, andrii.nakryiko, kernel-team

On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> struct <object-name> {
> 	/* used by libbpf's skeleton API */
> 	struct bpf_object_skeleton *skeleton;
> 	/* bpf_object for libbpf APIs */
> 	struct bpf_object *obj;
> 	struct {
> 		/* for every defined map in BPF object: */
> 		struct bpf_map *<map-name>;
> 	} maps;
> 	struct {
> 		/* for every program in BPF object: */
> 		struct bpf_program *<program-name>;
> 	} progs;
> 	struct {
> 		/* for every program in BPF object: */
> 		struct bpf_link *<program-name>;
> 	} links;
> 	/* for every present global data section: */
> 	struct <object-name>__<one of bss, data, or rodata> {
> 		/* memory layout of corresponding data section,
> 		 * with every defined variable represented as a struct field
> 		 * with exactly the same type, but without const/volatile
> 		 * modifiers, e.g.:
> 		 */
> 		 int *my_var_1;
> 		 ...
> 	} *<one of bss, data, or rodata>;
> };

I think I understand how this is useful, but perhaps the problem here
is that we're using C for everything, and simple programs for which
loading the ELF is majority of the code would be better of being
written in a dynamic language like python?  Would it perhaps be a
better idea to work on some high-level language bindings than spend
time writing code gens and working around limitations of C?

> This provides great usability improvements:
> - no need to look up maps and programs by name, instead just
>   my_obj->maps.my_map or my_obj->progs.my_prog would give necessary
>   bpf_map/bpf_program pointers, which user can pass to existing libbpf APIs;
> - pre-defined places for bpf_links, which will be automatically populated for
>   program types that libbpf knows how to attach automatically (currently
>   tracepoints, kprobe/kretprobe, raw tracepoint and tracing programs). On
>   tearing down skeleton, all active bpf_links will be destroyed (meaning BPF
>   programs will be detached, if they are attached). For cases in which libbpf
>   doesn't know how to auto-attach BPF program, user can manually create link
>   after loading skeleton and they will be auto-detached on skeleton
>   destruction:
> 
> 	my_obj->links.my_fancy_prog = bpf_program__attach_cgroup_whatever(
> 		my_obj->progs.my_fancy_prog, <whatever extra param);
> 
> - it's extremely easy and convenient to work with global data from userspace
>   now. Both for read-only and read/write variables, it's possible to
>   pre-initialize them before skeleton is loaded:
> 
> 	skel = my_obj__open(raw_embed_data);
> 	my_obj->rodata->my_var = 123;
> 	my_obj__load(skel); /* 123 will be initialization value for my_var */
> 
>   After load, if kernel supports mmap() for BPF arrays, user can still read
>   (and write for .bss and .data) variables values, but at that point it will
>   be directly mmap()-ed to BPF array, backing global variables. This allows to
>   seamlessly exchange data with BPF side. From userspace program's POV, all
>   the pointers and memory contents stay the same, but mapped kernel memory
>   changes to point to created map.
>   If kernel doesn't yet support mmap() for BPF arrays, it's still possible to
>   use those data section structs to pre-initialize .bss, .data, and .rodata,
>   but after load their pointers will be reset to NULL, allowing user code to
>   gracefully handle this condition, if necessary.
> 
> Given a big surface area, skeleton is kept as an experimental non-public
> API for now, until more feedback and real-world experience is collected.

That makes no sense to me. bpftool has the same backward compat
requirements as libbpf. You're just pushing the requirements from
one component to the other. Feedback and real-world use cases have 
to be exercised before code is merged to any project with backward
compatibility requirements :(

Also please run checkpatch on your patches, and fix reverse xmas tree.
This is bpftool, not libbpf. Creating a separate tool for this codegen
stuff is also an option IMHO.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 03/15] libbpf: move non-public APIs from libbpf.h to libbpf_internal.h
  2019-12-10  1:33   ` Jakub Kicinski
@ 2019-12-10 17:04     ` Andrii Nakryiko
  2019-12-10 18:17       ` Jakub Kicinski
  0 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10 17:04 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrii Nakryiko, bpf, Networking, Alexei Starovoitov,
	Daniel Borkmann, Kernel Team

On Mon, Dec 9, 2019 at 5:33 PM Jakub Kicinski
<jakub.kicinski@netronome.com> wrote:
>
> On Mon, 9 Dec 2019 17:14:26 -0800, Andrii Nakryiko wrote:
> > Few libbpf APIs are not public but currently exposed through libbpf.h to be
> > used by bpftool. Move them to libbpf_internal.h, where intent of being
> > non-stable and non-public is much more obvious.
> >
> > Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> > ---
> >  tools/bpf/bpftool/net.c         |  1 +
> >  tools/lib/bpf/libbpf.h          | 17 -----------------
> >  tools/lib/bpf/libbpf_internal.h | 17 +++++++++++++++++
> >  3 files changed, 18 insertions(+), 17 deletions(-)
> >
> > diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c
> > index 4f52d3151616..d93bee298e54 100644
> > --- a/tools/bpf/bpftool/net.c
> > +++ b/tools/bpf/bpftool/net.c
> > @@ -18,6 +18,7 @@
> >
> >  #include <bpf.h>
> >  #include <nlattr.h>
> > +#include "libbpf_internal.h"
> >  #include "main.h"
> >  #include "netlink_dumper.h"
>
> I thought this idea was unpopular when proposed?

There was a recent discussion about the need for unstable APIs to be
exposed to bpftool and we concluded that libbpf_internal.h is the most
appropriate place to do this.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-10  1:57   ` Jakub Kicinski
@ 2019-12-10 17:11     ` Andrii Nakryiko
  2019-12-10 18:05       ` Jakub Kicinski
  0 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10 17:11 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrii Nakryiko, LKML, bpf, Networking, Alexei Starovoitov,
	Daniel Borkmann, Kernel Team

On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski
<jakub.kicinski@netronome.com> wrote:
>
> On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > struct <object-name> {
> >       /* used by libbpf's skeleton API */
> >       struct bpf_object_skeleton *skeleton;
> >       /* bpf_object for libbpf APIs */
> >       struct bpf_object *obj;
> >       struct {
> >               /* for every defined map in BPF object: */
> >               struct bpf_map *<map-name>;
> >       } maps;
> >       struct {
> >               /* for every program in BPF object: */
> >               struct bpf_program *<program-name>;
> >       } progs;
> >       struct {
> >               /* for every program in BPF object: */
> >               struct bpf_link *<program-name>;
> >       } links;
> >       /* for every present global data section: */
> >       struct <object-name>__<one of bss, data, or rodata> {
> >               /* memory layout of corresponding data section,
> >                * with every defined variable represented as a struct field
> >                * with exactly the same type, but without const/volatile
> >                * modifiers, e.g.:
> >                */
> >                int *my_var_1;
> >                ...
> >       } *<one of bss, data, or rodata>;
> > };
>
> I think I understand how this is useful, but perhaps the problem here
> is that we're using C for everything, and simple programs for which
> loading the ELF is majority of the code would be better of being
> written in a dynamic language like python?  Would it perhaps be a
> better idea to work on some high-level language bindings than spend
> time writing code gens and working around limitations of C?

None of this work prevents Python bindings and other improvements, is
it? Patches, as always, are greatly appreciated ;)

This skeleton stuff is not just to save code, but in general to
simplify and streamline working with BPF program from userspace side.
Fortunately or not, but there are a lot of real-world applications
written in C and C++ that could benefit from this, so this is still
immensely useful. selftests/bpf themselves benefit a lot from this
work, see few of the last patches in this series.

>
> > This provides great usability improvements:
> > - no need to look up maps and programs by name, instead just
> >   my_obj->maps.my_map or my_obj->progs.my_prog would give necessary
> >   bpf_map/bpf_program pointers, which user can pass to existing libbpf APIs;
> > - pre-defined places for bpf_links, which will be automatically populated for
> >   program types that libbpf knows how to attach automatically (currently
> >   tracepoints, kprobe/kretprobe, raw tracepoint and tracing programs). On
> >   tearing down skeleton, all active bpf_links will be destroyed (meaning BPF
> >   programs will be detached, if they are attached). For cases in which libbpf
> >   doesn't know how to auto-attach BPF program, user can manually create link
> >   after loading skeleton and they will be auto-detached on skeleton
> >   destruction:
> >
> >       my_obj->links.my_fancy_prog = bpf_program__attach_cgroup_whatever(
> >               my_obj->progs.my_fancy_prog, <whatever extra param);
> >
> > - it's extremely easy and convenient to work with global data from userspace
> >   now. Both for read-only and read/write variables, it's possible to
> >   pre-initialize them before skeleton is loaded:
> >
> >       skel = my_obj__open(raw_embed_data);
> >       my_obj->rodata->my_var = 123;
> >       my_obj__load(skel); /* 123 will be initialization value for my_var */
> >
> >   After load, if kernel supports mmap() for BPF arrays, user can still read
> >   (and write for .bss and .data) variables values, but at that point it will
> >   be directly mmap()-ed to BPF array, backing global variables. This allows to
> >   seamlessly exchange data with BPF side. From userspace program's POV, all
> >   the pointers and memory contents stay the same, but mapped kernel memory
> >   changes to point to created map.
> >   If kernel doesn't yet support mmap() for BPF arrays, it's still possible to
> >   use those data section structs to pre-initialize .bss, .data, and .rodata,
> >   but after load their pointers will be reset to NULL, allowing user code to
> >   gracefully handle this condition, if necessary.
> >
> > Given a big surface area, skeleton is kept as an experimental non-public
> > API for now, until more feedback and real-world experience is collected.
>
> That makes no sense to me. bpftool has the same backward compat
> requirements as libbpf. You're just pushing the requirements from
> one component to the other. Feedback and real-world use cases have
> to be exercised before code is merged to any project with backward
> compatibility requirements :(

To get this feedback we need to have this functionality adopted. To
have it adopted, we need it available in tool users already know,
have, and use. If you feel that "experimental" disclaimer is not
enough, I guess we can add extra flag to bpftool itself to enable
experimental functionality, something like:

bpftool --experimental gen skeleton <bla>

>
> Also please run checkpatch on your patches, and fix reverse xmas tree.
> This is bpftool, not libbpf. Creating a separate tool for this codegen
> stuff is also an option IMHO.

Sure, will fix few small things checkpatch detected. Will reverse
christmas-ize all the variables, of course :)

As for separate tool just for this, you are not serious, right? If
bpftool is not right tool for this, I don't know which one is.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-10 17:11     ` Andrii Nakryiko
@ 2019-12-10 18:05       ` Jakub Kicinski
  2019-12-10 18:56         ` Andrii Nakryiko
  2019-12-10 21:44         ` Stanislav Fomichev
  0 siblings, 2 replies; 57+ messages in thread
From: Jakub Kicinski @ 2019-12-10 18:05 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Andrii Nakryiko, LKML, bpf, Networking, Alexei Starovoitov,
	Daniel Borkmann, Kernel Team

On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:  
> > > struct <object-name> {
> > >       /* used by libbpf's skeleton API */
> > >       struct bpf_object_skeleton *skeleton;
> > >       /* bpf_object for libbpf APIs */
> > >       struct bpf_object *obj;
> > >       struct {
> > >               /* for every defined map in BPF object: */
> > >               struct bpf_map *<map-name>;
> > >       } maps;
> > >       struct {
> > >               /* for every program in BPF object: */
> > >               struct bpf_program *<program-name>;
> > >       } progs;
> > >       struct {
> > >               /* for every program in BPF object: */
> > >               struct bpf_link *<program-name>;
> > >       } links;
> > >       /* for every present global data section: */
> > >       struct <object-name>__<one of bss, data, or rodata> {
> > >               /* memory layout of corresponding data section,
> > >                * with every defined variable represented as a struct field
> > >                * with exactly the same type, but without const/volatile
> > >                * modifiers, e.g.:
> > >                */
> > >                int *my_var_1;
> > >                ...
> > >       } *<one of bss, data, or rodata>;
> > > };  
> >
> > I think I understand how this is useful, but perhaps the problem here
> > is that we're using C for everything, and simple programs for which
> > loading the ELF is majority of the code would be better of being
> > written in a dynamic language like python?  Would it perhaps be a
> > better idea to work on some high-level language bindings than spend
> > time writing code gens and working around limitations of C?  
> 
> None of this work prevents Python bindings and other improvements, is
> it? Patches, as always, are greatly appreciated ;)

This "do it yourself" shit is not really funny :/

I'll stop providing feedback on BPF patches if you guy keep saying 
that :/ Maybe that's what you want.

> This skeleton stuff is not just to save code, but in general to
> simplify and streamline working with BPF program from userspace side.
> Fortunately or not, but there are a lot of real-world applications
> written in C and C++ that could benefit from this, so this is still
> immensely useful. selftests/bpf themselves benefit a lot from this
> work, see few of the last patches in this series.

Maybe those applications are written in C and C++ _because_ there 
are no bindings for high level languages. I just wish BPF programming
was less weird and adding some funky codegen is not getting us closer
to that goal.

In my experience code gen is nothing more than a hack to work around
bad APIs, but experiences differ so that's not a solid argument.

> > > This provides great usability improvements:
> > > - no need to look up maps and programs by name, instead just
> > >   my_obj->maps.my_map or my_obj->progs.my_prog would give necessary
> > >   bpf_map/bpf_program pointers, which user can pass to existing libbpf APIs;
> > > - pre-defined places for bpf_links, which will be automatically populated for
> > >   program types that libbpf knows how to attach automatically (currently
> > >   tracepoints, kprobe/kretprobe, raw tracepoint and tracing programs). On
> > >   tearing down skeleton, all active bpf_links will be destroyed (meaning BPF
> > >   programs will be detached, if they are attached). For cases in which libbpf
> > >   doesn't know how to auto-attach BPF program, user can manually create link
> > >   after loading skeleton and they will be auto-detached on skeleton
> > >   destruction:
> > >
> > >       my_obj->links.my_fancy_prog = bpf_program__attach_cgroup_whatever(
> > >               my_obj->progs.my_fancy_prog, <whatever extra param);
> > >
> > > - it's extremely easy and convenient to work with global data from userspace
> > >   now. Both for read-only and read/write variables, it's possible to
> > >   pre-initialize them before skeleton is loaded:
> > >
> > >       skel = my_obj__open(raw_embed_data);
> > >       my_obj->rodata->my_var = 123;
> > >       my_obj__load(skel); /* 123 will be initialization value for my_var */
> > >
> > >   After load, if kernel supports mmap() for BPF arrays, user can still read
> > >   (and write for .bss and .data) variables values, but at that point it will
> > >   be directly mmap()-ed to BPF array, backing global variables. This allows to
> > >   seamlessly exchange data with BPF side. From userspace program's POV, all
> > >   the pointers and memory contents stay the same, but mapped kernel memory
> > >   changes to point to created map.
> > >   If kernel doesn't yet support mmap() for BPF arrays, it's still possible to
> > >   use those data section structs to pre-initialize .bss, .data, and .rodata,
> > >   but after load their pointers will be reset to NULL, allowing user code to
> > >   gracefully handle this condition, if necessary.
> > >
> > > Given a big surface area, skeleton is kept as an experimental non-public
> > > API for now, until more feedback and real-world experience is collected.  
> >
> > That makes no sense to me. bpftool has the same backward compat
> > requirements as libbpf. You're just pushing the requirements from
> > one component to the other. Feedback and real-world use cases have
> > to be exercised before code is merged to any project with backward
> > compatibility requirements :(  
> 
> To get this feedback we need to have this functionality adopted. To
> have it adopted, we need it available in tool users already know,
> have, and use. 

Well you claim you have users for it, just talk to them now. I don't
understand how this is not obvious. It's like saying "we can't test
this unless it's in the tree"..!?

> If you feel that "experimental" disclaimer is not enough, I guess we
> can add extra flag to bpftool itself to enable experimental
> functionality, something like:
> 
> bpftool --experimental gen skeleton <bla>

Yeah, world doesn't really work like that. Users start depending on 
a feature, it will break people's scripts/Makefiles if it disappears.
This codegen thing is made to be hard coded in Makefiles.. how do you
expect people not to immediately become dependent on it.

> > Also please run checkpatch on your patches, and fix reverse xmas tree.
> > This is bpftool, not libbpf. Creating a separate tool for this codegen
> > stuff is also an option IMHO.  
> 
> Sure, will fix few small things checkpatch detected.

Running checkpatch should be part of your upstreaming routine, you're
wasting people's time. So stop with the amused tone.

> Will reverse christmas-ize all the variables, of course :)
> 
> As for separate tool just for this, you are not serious, right? If
> bpftool is not right tool for this, I don't know which one is.

I am serious. There absolutely nothing this tool needs from BPF, no
JSON needed, no bpffs etc. It can be a separate tool like
libbpf-skel-gen or libbpf-c-skel or something, distributed with libbpf.
That way you can actually soften the backward compat. In case people
become dependent on it they can carry that little tool on their own.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 03/15] libbpf: move non-public APIs from libbpf.h to libbpf_internal.h
  2019-12-10 17:04     ` Andrii Nakryiko
@ 2019-12-10 18:17       ` Jakub Kicinski
  2019-12-10 18:47         ` Andrii Nakryiko
  0 siblings, 1 reply; 57+ messages in thread
From: Jakub Kicinski @ 2019-12-10 18:17 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Andrii Nakryiko, bpf, Networking, Alexei Starovoitov,
	Daniel Borkmann, Kernel Team

On Tue, 10 Dec 2019 09:04:58 -0800, Andrii Nakryiko wrote:
> > I thought this idea was unpopular when proposed?  
> 
> There was a recent discussion about the need for unstable APIs to be
> exposed to bpftool and we concluded that libbpf_internal.h is the most
> appropriate place to do this.

Mm. Do you happen to have lore link? Can't find now.

My recollection is that only you and Alexei thought it was
a good/workable idea.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 03/15] libbpf: move non-public APIs from libbpf.h to libbpf_internal.h
  2019-12-10 18:17       ` Jakub Kicinski
@ 2019-12-10 18:47         ` Andrii Nakryiko
  0 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10 18:47 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrii Nakryiko, bpf, Networking, Alexei Starovoitov,
	Daniel Borkmann, Kernel Team

On Tue, Dec 10, 2019 at 10:17 AM Jakub Kicinski
<jakub.kicinski@netronome.com> wrote:
>
> On Tue, 10 Dec 2019 09:04:58 -0800, Andrii Nakryiko wrote:
> > > I thought this idea was unpopular when proposed?
> >
> > There was a recent discussion about the need for unstable APIs to be
> > exposed to bpftool and we concluded that libbpf_internal.h is the most
> > appropriate place to do this.
>
> Mm. Do you happen to have lore link? Can't find now.
>

https://lkml.org/lkml/2019/11/27/1264

> My recollection is that only you and Alexei thought it was
> a good/workable idea.

Daniel and others didn't seem to mind either.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-10 18:05       ` Jakub Kicinski
@ 2019-12-10 18:56         ` Andrii Nakryiko
  2019-12-10 21:44         ` Stanislav Fomichev
  1 sibling, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10 18:56 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrii Nakryiko, LKML, bpf, Networking, Alexei Starovoitov,
	Daniel Borkmann, Kernel Team

On Tue, Dec 10, 2019 at 10:05 AM Jakub Kicinski
<jakub.kicinski@netronome.com> wrote:
>
> On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > struct <object-name> {
> > > >       /* used by libbpf's skeleton API */
> > > >       struct bpf_object_skeleton *skeleton;
> > > >       /* bpf_object for libbpf APIs */
> > > >       struct bpf_object *obj;
> > > >       struct {
> > > >               /* for every defined map in BPF object: */
> > > >               struct bpf_map *<map-name>;
> > > >       } maps;
> > > >       struct {
> > > >               /* for every program in BPF object: */
> > > >               struct bpf_program *<program-name>;
> > > >       } progs;
> > > >       struct {
> > > >               /* for every program in BPF object: */
> > > >               struct bpf_link *<program-name>;
> > > >       } links;
> > > >       /* for every present global data section: */
> > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > >               /* memory layout of corresponding data section,
> > > >                * with every defined variable represented as a struct field
> > > >                * with exactly the same type, but without const/volatile
> > > >                * modifiers, e.g.:
> > > >                */
> > > >                int *my_var_1;
> > > >                ...
> > > >       } *<one of bss, data, or rodata>;
> > > > };
> > >
> > > I think I understand how this is useful, but perhaps the problem here
> > > is that we're using C for everything, and simple programs for which
> > > loading the ELF is majority of the code would be better of being
> > > written in a dynamic language like python?  Would it perhaps be a
> > > better idea to work on some high-level language bindings than spend
> > > time writing code gens and working around limitations of C?
> >
> > None of this work prevents Python bindings and other improvements, is
> > it? Patches, as always, are greatly appreciated ;)
>
> This "do it yourself" shit is not really funny :/
>

Everyone has different priorities and limited time/resources. So
deciding for someone else where he/she needs to spend time is what's
not funny. As long as this work doesn't prevent any Python
improvements you'd hope (apparently) someone else to do, I don't see a
problem with addressing real needs for C/C++ applications. What am I
missing?

> I'll stop providing feedback on BPF patches if you guy keep saying
> that :/ Maybe that's what you want.

We do value feedback, of course, as long as it's constructive. Thanks.

>
> > This skeleton stuff is not just to save code, but in general to
> > simplify and streamline working with BPF program from userspace side.
> > Fortunately or not, but there are a lot of real-world applications
> > written in C and C++ that could benefit from this, so this is still
> > immensely useful. selftests/bpf themselves benefit a lot from this
> > work, see few of the last patches in this series.
>
> Maybe those applications are written in C and C++ _because_ there
> are no bindings for high level languages. I just wish BPF programming
> was less weird and adding some funky codegen is not getting us closer
> to that goal.
>
> In my experience code gen is nothing more than a hack to work around
> bad APIs, but experiences differ so that's not a solid argument.

Agreed about the last point. Look at all sorts of RPC frameworks
(e.g., grpc, Thrift) and tell people relying on them how codegen is a
bad idea.

>
> > > > This provides great usability improvements:
> > > > - no need to look up maps and programs by name, instead just
> > > >   my_obj->maps.my_map or my_obj->progs.my_prog would give necessary
> > > >   bpf_map/bpf_program pointers, which user can pass to existing libbpf APIs;
> > > > - pre-defined places for bpf_links, which will be automatically populated for
> > > >   program types that libbpf knows how to attach automatically (currently
> > > >   tracepoints, kprobe/kretprobe, raw tracepoint and tracing programs). On
> > > >   tearing down skeleton, all active bpf_links will be destroyed (meaning BPF
> > > >   programs will be detached, if they are attached). For cases in which libbpf
> > > >   doesn't know how to auto-attach BPF program, user can manually create link
> > > >   after loading skeleton and they will be auto-detached on skeleton
> > > >   destruction:
> > > >
> > > >       my_obj->links.my_fancy_prog = bpf_program__attach_cgroup_whatever(
> > > >               my_obj->progs.my_fancy_prog, <whatever extra param);
> > > >
> > > > - it's extremely easy and convenient to work with global data from userspace
> > > >   now. Both for read-only and read/write variables, it's possible to
> > > >   pre-initialize them before skeleton is loaded:
> > > >
> > > >       skel = my_obj__open(raw_embed_data);
> > > >       my_obj->rodata->my_var = 123;
> > > >       my_obj__load(skel); /* 123 will be initialization value for my_var */
> > > >
> > > >   After load, if kernel supports mmap() for BPF arrays, user can still read
> > > >   (and write for .bss and .data) variables values, but at that point it will
> > > >   be directly mmap()-ed to BPF array, backing global variables. This allows to
> > > >   seamlessly exchange data with BPF side. From userspace program's POV, all
> > > >   the pointers and memory contents stay the same, but mapped kernel memory
> > > >   changes to point to created map.
> > > >   If kernel doesn't yet support mmap() for BPF arrays, it's still possible to
> > > >   use those data section structs to pre-initialize .bss, .data, and .rodata,
> > > >   but after load their pointers will be reset to NULL, allowing user code to
> > > >   gracefully handle this condition, if necessary.
> > > >
> > > > Given a big surface area, skeleton is kept as an experimental non-public
> > > > API for now, until more feedback and real-world experience is collected.
> > >
> > > That makes no sense to me. bpftool has the same backward compat
> > > requirements as libbpf. You're just pushing the requirements from
> > > one component to the other. Feedback and real-world use cases have
> > > to be exercised before code is merged to any project with backward
> > > compatibility requirements :(
> >
> > To get this feedback we need to have this functionality adopted. To
> > have it adopted, we need it available in tool users already know,
> > have, and use.
>
> Well you claim you have users for it, just talk to them now. I don't
> understand how this is not obvious. It's like saying "we can't test
> this unless it's in the tree"..!?

It is useful right now as is, I never claimed I'm not sure if this
stuff is going to be used/useful. What I'm saying is that as we get
some more applications converted to this and used actively over
prolonged period of time, we might identify a bunch of tweaks we might
want to do. Talking with users without having a code for them to use
is not going to provide more insights beyond what we already
collected.

>
> > If you feel that "experimental" disclaimer is not enough, I guess we
> > can add extra flag to bpftool itself to enable experimental
> > functionality, something like:
> >
> > bpftool --experimental gen skeleton <bla>
>
> Yeah, world doesn't really work like that. Users start depending on
> a feature, it will break people's scripts/Makefiles if it disappears.
> This codegen thing is made to be hard coded in Makefiles.. how do you
> expect people not to immediately become dependent on it.

It's about managing expectations, isn't it? That's why I'm putting an
"experimental" disclaimer (or even extra --experimental flag as
described above) on this. If users is afraid of having a minor
breakage due to the need to add extra argument in source code and
recompile (if necessary), they shouldn't opt in.

>
> > > Also please run checkpatch on your patches, and fix reverse xmas tree.
> > > This is bpftool, not libbpf. Creating a separate tool for this codegen
> > > stuff is also an option IMHO.
> >
> > Sure, will fix few small things checkpatch detected.
>
> Running checkpatch should be part of your upstreaming routine, you're
> wasting people's time. So stop with the amused tone.
>
> > Will reverse christmas-ize all the variables, of course :)
> >
> > As for separate tool just for this, you are not serious, right? If
> > bpftool is not right tool for this, I don't know which one is.
>
> I am serious. There absolutely nothing this tool needs from BPF, no
> JSON needed, no bpffs etc. It can be a separate tool like
> libbpf-skel-gen or libbpf-c-skel or something, distributed with libbpf.
> That way you can actually soften the backward compat. In case people
> become dependent on it they can carry that little tool on their own.

We are trying to make users lives easier by having major distributions
distribute bpftool and libbpf properly. Adding extra binaries to
distribute around doesn't seem to be easing any of users pains.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-10 18:05       ` Jakub Kicinski
  2019-12-10 18:56         ` Andrii Nakryiko
@ 2019-12-10 21:44         ` Stanislav Fomichev
  2019-12-10 22:33           ` Andrii Nakryiko
  1 sibling, 1 reply; 57+ messages in thread
From: Stanislav Fomichev @ 2019-12-10 21:44 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrii Nakryiko, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On 12/10, Jakub Kicinski wrote:
> On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:  
> > > > struct <object-name> {
> > > >       /* used by libbpf's skeleton API */
> > > >       struct bpf_object_skeleton *skeleton;
> > > >       /* bpf_object for libbpf APIs */
> > > >       struct bpf_object *obj;
> > > >       struct {
> > > >               /* for every defined map in BPF object: */
> > > >               struct bpf_map *<map-name>;
> > > >       } maps;
> > > >       struct {
> > > >               /* for every program in BPF object: */
> > > >               struct bpf_program *<program-name>;
> > > >       } progs;
> > > >       struct {
> > > >               /* for every program in BPF object: */
> > > >               struct bpf_link *<program-name>;
> > > >       } links;
> > > >       /* for every present global data section: */
> > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > >               /* memory layout of corresponding data section,
> > > >                * with every defined variable represented as a struct field
> > > >                * with exactly the same type, but without const/volatile
> > > >                * modifiers, e.g.:
> > > >                */
> > > >                int *my_var_1;
> > > >                ...
> > > >       } *<one of bss, data, or rodata>;
> > > > };  
> > >
> > > I think I understand how this is useful, but perhaps the problem here
> > > is that we're using C for everything, and simple programs for which
> > > loading the ELF is majority of the code would be better of being
> > > written in a dynamic language like python?  Would it perhaps be a
> > > better idea to work on some high-level language bindings than spend
> > > time writing code gens and working around limitations of C?  
> > 
> > None of this work prevents Python bindings and other improvements, is
> > it? Patches, as always, are greatly appreciated ;)
> 
> This "do it yourself" shit is not really funny :/
> 
> I'll stop providing feedback on BPF patches if you guy keep saying 
> that :/ Maybe that's what you want.
> 
> > This skeleton stuff is not just to save code, but in general to
> > simplify and streamline working with BPF program from userspace side.
> > Fortunately or not, but there are a lot of real-world applications
> > written in C and C++ that could benefit from this, so this is still
> > immensely useful. selftests/bpf themselves benefit a lot from this
> > work, see few of the last patches in this series.
> 
> Maybe those applications are written in C and C++ _because_ there 
> are no bindings for high level languages. I just wish BPF programming
> was less weird and adding some funky codegen is not getting us closer
> to that goal.
> 
> In my experience code gen is nothing more than a hack to work around
> bad APIs, but experiences differ so that's not a solid argument.
*nod*

We have a nice set of C++ wrappers around libbpf internally, so we can do
something like BpfMap<key type, value type> and get a much better interface
with type checking. Maybe we should focus on higher level languages instead?
We are open to open-sourcing our C++ bits if you want to collaborate.

(I assume most of the stuff you have at fb is also non-c and one of
c++/python/php/rust/go/whatver).

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-10 21:44         ` Stanislav Fomichev
@ 2019-12-10 22:33           ` Andrii Nakryiko
  2019-12-10 22:59             ` Stanislav Fomichev
  0 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-10 22:33 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
>
> On 12/10, Jakub Kicinski wrote:
> > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > struct <object-name> {
> > > > >       /* used by libbpf's skeleton API */
> > > > >       struct bpf_object_skeleton *skeleton;
> > > > >       /* bpf_object for libbpf APIs */
> > > > >       struct bpf_object *obj;
> > > > >       struct {
> > > > >               /* for every defined map in BPF object: */
> > > > >               struct bpf_map *<map-name>;
> > > > >       } maps;
> > > > >       struct {
> > > > >               /* for every program in BPF object: */
> > > > >               struct bpf_program *<program-name>;
> > > > >       } progs;
> > > > >       struct {
> > > > >               /* for every program in BPF object: */
> > > > >               struct bpf_link *<program-name>;
> > > > >       } links;
> > > > >       /* for every present global data section: */
> > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > >               /* memory layout of corresponding data section,
> > > > >                * with every defined variable represented as a struct field
> > > > >                * with exactly the same type, but without const/volatile
> > > > >                * modifiers, e.g.:
> > > > >                */
> > > > >                int *my_var_1;
> > > > >                ...
> > > > >       } *<one of bss, data, or rodata>;
> > > > > };
> > > >
> > > > I think I understand how this is useful, but perhaps the problem here
> > > > is that we're using C for everything, and simple programs for which
> > > > loading the ELF is majority of the code would be better of being
> > > > written in a dynamic language like python?  Would it perhaps be a
> > > > better idea to work on some high-level language bindings than spend
> > > > time writing code gens and working around limitations of C?
> > >
> > > None of this work prevents Python bindings and other improvements, is
> > > it? Patches, as always, are greatly appreciated ;)
> >
> > This "do it yourself" shit is not really funny :/
> >
> > I'll stop providing feedback on BPF patches if you guy keep saying
> > that :/ Maybe that's what you want.
> >
> > > This skeleton stuff is not just to save code, but in general to
> > > simplify and streamline working with BPF program from userspace side.
> > > Fortunately or not, but there are a lot of real-world applications
> > > written in C and C++ that could benefit from this, so this is still
> > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > work, see few of the last patches in this series.
> >
> > Maybe those applications are written in C and C++ _because_ there
> > are no bindings for high level languages. I just wish BPF programming
> > was less weird and adding some funky codegen is not getting us closer
> > to that goal.
> >
> > In my experience code gen is nothing more than a hack to work around
> > bad APIs, but experiences differ so that's not a solid argument.
> *nod*
>
> We have a nice set of C++ wrappers around libbpf internally, so we can do
> something like BpfMap<key type, value type> and get a much better interface
> with type checking. Maybe we should focus on higher level languages instead?
> We are open to open-sourcing our C++ bits if you want to collaborate.

Python/C++ bindings and API wrappers are an orthogonal concerns here.
I personally think it would be great to have both Python and C++
specific API that uses libbpf under the cover. The only debatable
thing is the logistics: where the source code lives, how it's kept in
sync with libbpf, how we avoid crippling libbpf itself because
something is hard or inconvenient to adapt w/ Python, etc.

The problem I'm trying to solve here is not really C-specific. I don't
think you can solve it without code generation for C++. How do you
"generate" BPF program-specific layout of .data, .bss, .rodata, etc
data sections in such a way, where it's type safe (to the degree that
language allows that, of course) and is not "stringly-based" API? This
skeleton stuff provides a natural, convenient and type-safe way to
work with global data from userspace pretty much at the same level of
performance and convenience, as from BPF side. How can you achieve
that w/ C++ without code generation? As for Python, sure you can do
dynamic lookups based on just the name of property/method, but amount
of overheads is not acceptable for all applications (and Python itself
is not acceptable for those applications). In addition to that, C is
the best way for other less popular languages (e.g., Rust) to leverage
libbpf without investing lots of effort in re-implementing libbpf in
Rust.

So while having nice high-level language-specific APIs is good, it's not enough.

>
> (I assume most of the stuff you have at fb is also non-c and one of
> c++/python/php/rust/go/whatver).

Yes, C++ using libbpf directly or through very thin wrappers. For
BCC-based stuff, obviously, we rely on C++ parts of BCC. This struct
I'm generating is extremely useful for C++ as well, as it gives very
natural way to access *and initialize* global variables.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-10 22:33           ` Andrii Nakryiko
@ 2019-12-10 22:59             ` Stanislav Fomichev
  2019-12-11  7:07               ` Andrii Nakryiko
  0 siblings, 1 reply; 57+ messages in thread
From: Stanislav Fomichev @ 2019-12-10 22:59 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On 12/10, Andrii Nakryiko wrote:
> On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> >
> > On 12/10, Jakub Kicinski wrote:
> > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > struct <object-name> {
> > > > > >       /* used by libbpf's skeleton API */
> > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > >       /* bpf_object for libbpf APIs */
> > > > > >       struct bpf_object *obj;
> > > > > >       struct {
> > > > > >               /* for every defined map in BPF object: */
> > > > > >               struct bpf_map *<map-name>;
> > > > > >       } maps;
> > > > > >       struct {
> > > > > >               /* for every program in BPF object: */
> > > > > >               struct bpf_program *<program-name>;
> > > > > >       } progs;
> > > > > >       struct {
> > > > > >               /* for every program in BPF object: */
> > > > > >               struct bpf_link *<program-name>;
> > > > > >       } links;
> > > > > >       /* for every present global data section: */
> > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > >               /* memory layout of corresponding data section,
> > > > > >                * with every defined variable represented as a struct field
> > > > > >                * with exactly the same type, but without const/volatile
> > > > > >                * modifiers, e.g.:
> > > > > >                */
> > > > > >                int *my_var_1;
> > > > > >                ...
> > > > > >       } *<one of bss, data, or rodata>;
> > > > > > };
> > > > >
> > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > is that we're using C for everything, and simple programs for which
> > > > > loading the ELF is majority of the code would be better of being
> > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > better idea to work on some high-level language bindings than spend
> > > > > time writing code gens and working around limitations of C?
> > > >
> > > > None of this work prevents Python bindings and other improvements, is
> > > > it? Patches, as always, are greatly appreciated ;)
> > >
> > > This "do it yourself" shit is not really funny :/
> > >
> > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > that :/ Maybe that's what you want.
> > >
> > > > This skeleton stuff is not just to save code, but in general to
> > > > simplify and streamline working with BPF program from userspace side.
> > > > Fortunately or not, but there are a lot of real-world applications
> > > > written in C and C++ that could benefit from this, so this is still
> > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > work, see few of the last patches in this series.
> > >
> > > Maybe those applications are written in C and C++ _because_ there
> > > are no bindings for high level languages. I just wish BPF programming
> > > was less weird and adding some funky codegen is not getting us closer
> > > to that goal.
> > >
> > > In my experience code gen is nothing more than a hack to work around
> > > bad APIs, but experiences differ so that's not a solid argument.
> > *nod*
> >
> > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > something like BpfMap<key type, value type> and get a much better interface
> > with type checking. Maybe we should focus on higher level languages instead?
> > We are open to open-sourcing our C++ bits if you want to collaborate.
> 
> Python/C++ bindings and API wrappers are an orthogonal concerns here.
> I personally think it would be great to have both Python and C++
> specific API that uses libbpf under the cover. The only debatable
> thing is the logistics: where the source code lives, how it's kept in
> sync with libbpf, how we avoid crippling libbpf itself because
> something is hard or inconvenient to adapt w/ Python, etc.

[..]
> The problem I'm trying to solve here is not really C-specific. I don't
> think you can solve it without code generation for C++. How do you
> "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> data sections in such a way, where it's type safe (to the degree that
> language allows that, of course) and is not "stringly-based" API? This
> skeleton stuff provides a natural, convenient and type-safe way to
> work with global data from userspace pretty much at the same level of
> performance and convenience, as from BPF side. How can you achieve
> that w/ C++ without code generation? As for Python, sure you can do
> dynamic lookups based on just the name of property/method, but amount
> of overheads is not acceptable for all applications (and Python itself
> is not acceptable for those applications). In addition to that, C is
> the best way for other less popular languages (e.g., Rust) to leverage
> libbpf without investing lots of effort in re-implementing libbpf in
> Rust.
I'd say that a libbpf API similar to dlopen/dlsym is a more
straightforward thing to do. Have a way to "open" a section and
a way to find a symbol in it. Yes, it's a string-based API,
but there is nothing wrong with it. IMO, this is easier to
use/understand and I suppose Python/C++ wrappers are trivial.

As for type-safety: it's C, forget about it :-)

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-10 22:59             ` Stanislav Fomichev
@ 2019-12-11  7:07               ` Andrii Nakryiko
  2019-12-11 17:24                 ` Stanislav Fomichev
  0 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-11  7:07 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Tue, Dec 10, 2019 at 2:59 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
>
> On 12/10, Andrii Nakryiko wrote:
> > On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > >
> > > On 12/10, Jakub Kicinski wrote:
> > > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > > struct <object-name> {
> > > > > > >       /* used by libbpf's skeleton API */
> > > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > > >       /* bpf_object for libbpf APIs */
> > > > > > >       struct bpf_object *obj;
> > > > > > >       struct {
> > > > > > >               /* for every defined map in BPF object: */
> > > > > > >               struct bpf_map *<map-name>;
> > > > > > >       } maps;
> > > > > > >       struct {
> > > > > > >               /* for every program in BPF object: */
> > > > > > >               struct bpf_program *<program-name>;
> > > > > > >       } progs;
> > > > > > >       struct {
> > > > > > >               /* for every program in BPF object: */
> > > > > > >               struct bpf_link *<program-name>;
> > > > > > >       } links;
> > > > > > >       /* for every present global data section: */
> > > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > > >               /* memory layout of corresponding data section,
> > > > > > >                * with every defined variable represented as a struct field
> > > > > > >                * with exactly the same type, but without const/volatile
> > > > > > >                * modifiers, e.g.:
> > > > > > >                */
> > > > > > >                int *my_var_1;
> > > > > > >                ...
> > > > > > >       } *<one of bss, data, or rodata>;
> > > > > > > };
> > > > > >
> > > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > > is that we're using C for everything, and simple programs for which
> > > > > > loading the ELF is majority of the code would be better of being
> > > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > > better idea to work on some high-level language bindings than spend
> > > > > > time writing code gens and working around limitations of C?
> > > > >
> > > > > None of this work prevents Python bindings and other improvements, is
> > > > > it? Patches, as always, are greatly appreciated ;)
> > > >
> > > > This "do it yourself" shit is not really funny :/
> > > >
> > > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > > that :/ Maybe that's what you want.
> > > >
> > > > > This skeleton stuff is not just to save code, but in general to
> > > > > simplify and streamline working with BPF program from userspace side.
> > > > > Fortunately or not, but there are a lot of real-world applications
> > > > > written in C and C++ that could benefit from this, so this is still
> > > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > > work, see few of the last patches in this series.
> > > >
> > > > Maybe those applications are written in C and C++ _because_ there
> > > > are no bindings for high level languages. I just wish BPF programming
> > > > was less weird and adding some funky codegen is not getting us closer
> > > > to that goal.
> > > >
> > > > In my experience code gen is nothing more than a hack to work around
> > > > bad APIs, but experiences differ so that's not a solid argument.
> > > *nod*
> > >
> > > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > > something like BpfMap<key type, value type> and get a much better interface
> > > with type checking. Maybe we should focus on higher level languages instead?
> > > We are open to open-sourcing our C++ bits if you want to collaborate.
> >
> > Python/C++ bindings and API wrappers are an orthogonal concerns here.
> > I personally think it would be great to have both Python and C++
> > specific API that uses libbpf under the cover. The only debatable
> > thing is the logistics: where the source code lives, how it's kept in
> > sync with libbpf, how we avoid crippling libbpf itself because
> > something is hard or inconvenient to adapt w/ Python, etc.
>
> [..]
> > The problem I'm trying to solve here is not really C-specific. I don't
> > think you can solve it without code generation for C++. How do you
> > "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> > data sections in such a way, where it's type safe (to the degree that
> > language allows that, of course) and is not "stringly-based" API? This
> > skeleton stuff provides a natural, convenient and type-safe way to
> > work with global data from userspace pretty much at the same level of
> > performance and convenience, as from BPF side. How can you achieve
> > that w/ C++ without code generation? As for Python, sure you can do
> > dynamic lookups based on just the name of property/method, but amount
> > of overheads is not acceptable for all applications (and Python itself
> > is not acceptable for those applications). In addition to that, C is
> > the best way for other less popular languages (e.g., Rust) to leverage
> > libbpf without investing lots of effort in re-implementing libbpf in
> > Rust.
> I'd say that a libbpf API similar to dlopen/dlsym is a more
> straightforward thing to do. Have a way to "open" a section and
> a way to find a symbol in it. Yes, it's a string-based API,
> but there is nothing wrong with it. IMO, this is easier to
> use/understand and I suppose Python/C++ wrappers are trivial.

Without digging through libbpf source code (or actually, look at code,
but don't run any test program), what's the name of the map
corresponding to .bss section, if object file is
some_bpf_object_file.o? If you got it right (congrats, btw, it took me
multiple attempts to memorize the pattern), how much time did you
spend looking it up? Now compare it to `skel->maps.bss`. Further, if
you use anonymous structs for your global vars, good luck maintaining
two copies of that: one for BPF side and one for userspace.

I never said there is anything wrong with current straightforward
libbpf API, but I also never said it's the easiest and most
user-friendly way to work with BPF either. So we'll have both
code-generated interface and existing API. Furthermore, they are
interoperable (you can pass skel->maps.whatever to any of the existing
libbpf APIs, same for progs, links, obj itself). But there isn't much
that can beat performance and usability of code-generated .data, .bss,
.rodata (and now .extern) layout.

>
> As for type-safety: it's C, forget about it :-)

C is weakly, but still typed language. There are types and they are
helpful. Yes, you can disregard them and re-interpret values as
anything, but that's beside the point.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-11  7:07               ` Andrii Nakryiko
@ 2019-12-11 17:24                 ` Stanislav Fomichev
  2019-12-11 18:26                   ` Andrii Nakryiko
  0 siblings, 1 reply; 57+ messages in thread
From: Stanislav Fomichev @ 2019-12-11 17:24 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On 12/10, Andrii Nakryiko wrote:
> On Tue, Dec 10, 2019 at 2:59 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> >
> > On 12/10, Andrii Nakryiko wrote:
> > > On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > >
> > > > On 12/10, Jakub Kicinski wrote:
> > > > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > > > struct <object-name> {
> > > > > > > >       /* used by libbpf's skeleton API */
> > > > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > > > >       /* bpf_object for libbpf APIs */
> > > > > > > >       struct bpf_object *obj;
> > > > > > > >       struct {
> > > > > > > >               /* for every defined map in BPF object: */
> > > > > > > >               struct bpf_map *<map-name>;
> > > > > > > >       } maps;
> > > > > > > >       struct {
> > > > > > > >               /* for every program in BPF object: */
> > > > > > > >               struct bpf_program *<program-name>;
> > > > > > > >       } progs;
> > > > > > > >       struct {
> > > > > > > >               /* for every program in BPF object: */
> > > > > > > >               struct bpf_link *<program-name>;
> > > > > > > >       } links;
> > > > > > > >       /* for every present global data section: */
> > > > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > > > >               /* memory layout of corresponding data section,
> > > > > > > >                * with every defined variable represented as a struct field
> > > > > > > >                * with exactly the same type, but without const/volatile
> > > > > > > >                * modifiers, e.g.:
> > > > > > > >                */
> > > > > > > >                int *my_var_1;
> > > > > > > >                ...
> > > > > > > >       } *<one of bss, data, or rodata>;
> > > > > > > > };
> > > > > > >
> > > > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > > > is that we're using C for everything, and simple programs for which
> > > > > > > loading the ELF is majority of the code would be better of being
> > > > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > > > better idea to work on some high-level language bindings than spend
> > > > > > > time writing code gens and working around limitations of C?
> > > > > >
> > > > > > None of this work prevents Python bindings and other improvements, is
> > > > > > it? Patches, as always, are greatly appreciated ;)
> > > > >
> > > > > This "do it yourself" shit is not really funny :/
> > > > >
> > > > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > > > that :/ Maybe that's what you want.
> > > > >
> > > > > > This skeleton stuff is not just to save code, but in general to
> > > > > > simplify and streamline working with BPF program from userspace side.
> > > > > > Fortunately or not, but there are a lot of real-world applications
> > > > > > written in C and C++ that could benefit from this, so this is still
> > > > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > > > work, see few of the last patches in this series.
> > > > >
> > > > > Maybe those applications are written in C and C++ _because_ there
> > > > > are no bindings for high level languages. I just wish BPF programming
> > > > > was less weird and adding some funky codegen is not getting us closer
> > > > > to that goal.
> > > > >
> > > > > In my experience code gen is nothing more than a hack to work around
> > > > > bad APIs, but experiences differ so that's not a solid argument.
> > > > *nod*
> > > >
> > > > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > > > something like BpfMap<key type, value type> and get a much better interface
> > > > with type checking. Maybe we should focus on higher level languages instead?
> > > > We are open to open-sourcing our C++ bits if you want to collaborate.
> > >
> > > Python/C++ bindings and API wrappers are an orthogonal concerns here.
> > > I personally think it would be great to have both Python and C++
> > > specific API that uses libbpf under the cover. The only debatable
> > > thing is the logistics: where the source code lives, how it's kept in
> > > sync with libbpf, how we avoid crippling libbpf itself because
> > > something is hard or inconvenient to adapt w/ Python, etc.
> >
> > [..]
> > > The problem I'm trying to solve here is not really C-specific. I don't
> > > think you can solve it without code generation for C++. How do you
> > > "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> > > data sections in such a way, where it's type safe (to the degree that
> > > language allows that, of course) and is not "stringly-based" API? This
> > > skeleton stuff provides a natural, convenient and type-safe way to
> > > work with global data from userspace pretty much at the same level of
> > > performance and convenience, as from BPF side. How can you achieve
> > > that w/ C++ without code generation? As for Python, sure you can do
> > > dynamic lookups based on just the name of property/method, but amount
> > > of overheads is not acceptable for all applications (and Python itself
> > > is not acceptable for those applications). In addition to that, C is
> > > the best way for other less popular languages (e.g., Rust) to leverage
> > > libbpf without investing lots of effort in re-implementing libbpf in
> > > Rust.
> > I'd say that a libbpf API similar to dlopen/dlsym is a more
> > straightforward thing to do. Have a way to "open" a section and
> > a way to find a symbol in it. Yes, it's a string-based API,
> > but there is nothing wrong with it. IMO, this is easier to
> > use/understand and I suppose Python/C++ wrappers are trivial.
> 
> Without digging through libbpf source code (or actually, look at code,
> but don't run any test program), what's the name of the map
> corresponding to .bss section, if object file is
> some_bpf_object_file.o? If you got it right (congrats, btw, it took me
> multiple attempts to memorize the pattern), how much time did you
> spend looking it up? Now compare it to `skel->maps.bss`. Further, if
> you use anonymous structs for your global vars, good luck maintaining
> two copies of that: one for BPF side and one for userspace.
As your average author of BPF programs I don't really care
which section my symbol ends up into. Just give me an api
to mmap all "global" sections (or a call per section which does all the
naming magic inside) and lookup symbol by name; I can cast it to a proper
type and set it.

RE anonymous structs: maybe don't use them if you want to share the data
between bpf and userspace?

> I never said there is anything wrong with current straightforward
> libbpf API, but I also never said it's the easiest and most
> user-friendly way to work with BPF either. So we'll have both
> code-generated interface and existing API. Furthermore, they are
> interoperable (you can pass skel->maps.whatever to any of the existing
> libbpf APIs, same for progs, links, obj itself). But there isn't much
> that can beat performance and usability of code-generated .data, .bss,
> .rodata (and now .extern) layout.
I haven't looked closely enough, but is there a libbpf api to get
an offset of a variable? Suppose I have the following in bpf.c:

	int a;
	int b;

Can I get an offset of 'b' in the .bss without manually parsing BTF?

TBH, I don't buy the performance argument for these global maps.
When you did the mmap patchset for the array, you said it yourself
that it's about convenience and not performance.

> > As for type-safety: it's C, forget about it :-)
> 
> C is weakly, but still typed language. There are types and they are
> helpful. Yes, you can disregard them and re-interpret values as
> anything, but that's beside the point.
My point was that there is a certain mental model when working
with this type of external symbols which feels "natural" for C
(dlopen/dlsym).

But I agree with you, that as long as code-gen is optional
and there is an alternative api in libbpf, we should be good.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-11 17:24                 ` Stanislav Fomichev
@ 2019-12-11 18:26                   ` Andrii Nakryiko
  2019-12-11 19:15                     ` Stanislav Fomichev
  0 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-11 18:26 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Wed, Dec 11, 2019 at 9:24 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
>
> On 12/10, Andrii Nakryiko wrote:
> > On Tue, Dec 10, 2019 at 2:59 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > >
> > > On 12/10, Andrii Nakryiko wrote:
> > > > On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > >
> > > > > On 12/10, Jakub Kicinski wrote:
> > > > > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > > > > struct <object-name> {
> > > > > > > > >       /* used by libbpf's skeleton API */
> > > > > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > > > > >       /* bpf_object for libbpf APIs */
> > > > > > > > >       struct bpf_object *obj;
> > > > > > > > >       struct {
> > > > > > > > >               /* for every defined map in BPF object: */
> > > > > > > > >               struct bpf_map *<map-name>;
> > > > > > > > >       } maps;
> > > > > > > > >       struct {
> > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > >               struct bpf_program *<program-name>;
> > > > > > > > >       } progs;
> > > > > > > > >       struct {
> > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > >               struct bpf_link *<program-name>;
> > > > > > > > >       } links;
> > > > > > > > >       /* for every present global data section: */
> > > > > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > > > > >               /* memory layout of corresponding data section,
> > > > > > > > >                * with every defined variable represented as a struct field
> > > > > > > > >                * with exactly the same type, but without const/volatile
> > > > > > > > >                * modifiers, e.g.:
> > > > > > > > >                */
> > > > > > > > >                int *my_var_1;
> > > > > > > > >                ...
> > > > > > > > >       } *<one of bss, data, or rodata>;
> > > > > > > > > };
> > > > > > > >
> > > > > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > > > > is that we're using C for everything, and simple programs for which
> > > > > > > > loading the ELF is majority of the code would be better of being
> > > > > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > > > > better idea to work on some high-level language bindings than spend
> > > > > > > > time writing code gens and working around limitations of C?
> > > > > > >
> > > > > > > None of this work prevents Python bindings and other improvements, is
> > > > > > > it? Patches, as always, are greatly appreciated ;)
> > > > > >
> > > > > > This "do it yourself" shit is not really funny :/
> > > > > >
> > > > > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > > > > that :/ Maybe that's what you want.
> > > > > >
> > > > > > > This skeleton stuff is not just to save code, but in general to
> > > > > > > simplify and streamline working with BPF program from userspace side.
> > > > > > > Fortunately or not, but there are a lot of real-world applications
> > > > > > > written in C and C++ that could benefit from this, so this is still
> > > > > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > > > > work, see few of the last patches in this series.
> > > > > >
> > > > > > Maybe those applications are written in C and C++ _because_ there
> > > > > > are no bindings for high level languages. I just wish BPF programming
> > > > > > was less weird and adding some funky codegen is not getting us closer
> > > > > > to that goal.
> > > > > >
> > > > > > In my experience code gen is nothing more than a hack to work around
> > > > > > bad APIs, but experiences differ so that's not a solid argument.
> > > > > *nod*
> > > > >
> > > > > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > > > > something like BpfMap<key type, value type> and get a much better interface
> > > > > with type checking. Maybe we should focus on higher level languages instead?
> > > > > We are open to open-sourcing our C++ bits if you want to collaborate.
> > > >
> > > > Python/C++ bindings and API wrappers are an orthogonal concerns here.
> > > > I personally think it would be great to have both Python and C++
> > > > specific API that uses libbpf under the cover. The only debatable
> > > > thing is the logistics: where the source code lives, how it's kept in
> > > > sync with libbpf, how we avoid crippling libbpf itself because
> > > > something is hard or inconvenient to adapt w/ Python, etc.
> > >
> > > [..]
> > > > The problem I'm trying to solve here is not really C-specific. I don't
> > > > think you can solve it without code generation for C++. How do you
> > > > "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> > > > data sections in such a way, where it's type safe (to the degree that
> > > > language allows that, of course) and is not "stringly-based" API? This
> > > > skeleton stuff provides a natural, convenient and type-safe way to
> > > > work with global data from userspace pretty much at the same level of
> > > > performance and convenience, as from BPF side. How can you achieve
> > > > that w/ C++ without code generation? As for Python, sure you can do
> > > > dynamic lookups based on just the name of property/method, but amount
> > > > of overheads is not acceptable for all applications (and Python itself
> > > > is not acceptable for those applications). In addition to that, C is
> > > > the best way for other less popular languages (e.g., Rust) to leverage
> > > > libbpf without investing lots of effort in re-implementing libbpf in
> > > > Rust.
> > > I'd say that a libbpf API similar to dlopen/dlsym is a more
> > > straightforward thing to do. Have a way to "open" a section and
> > > a way to find a symbol in it. Yes, it's a string-based API,
> > > but there is nothing wrong with it. IMO, this is easier to
> > > use/understand and I suppose Python/C++ wrappers are trivial.
> >
> > Without digging through libbpf source code (or actually, look at code,
> > but don't run any test program), what's the name of the map
> > corresponding to .bss section, if object file is
> > some_bpf_object_file.o? If you got it right (congrats, btw, it took me
> > multiple attempts to memorize the pattern), how much time did you
> > spend looking it up? Now compare it to `skel->maps.bss`. Further, if
> > you use anonymous structs for your global vars, good luck maintaining
> > two copies of that: one for BPF side and one for userspace.
> As your average author of BPF programs I don't really care
> which section my symbol ends up into. Just give me an api
> to mmap all "global" sections (or a call per section which does all the
> naming magic inside) and lookup symbol by name; I can cast it to a proper
> type and set it.

I'd like to not have to know about bss/rodata/data as well, but that's
how things are done for global variables. In skeleton we can try to
make an illusion like they are part of one big datasection/struct, but
that seems like a bit too much magic at this point. But then again,
one of the reasons I want this as an experimental feature, so that we
can actually judge from real experience how inconvenient some things
are, and not just based on "I think it would be ...".

re: "Just give me ...". Following the spirit of "C is hard" from your
previous arguments, you already have that API: mmap() syscall. C
programmers have to be able to figure out the rest ;) But on the
serious note, this auto-generated code in skeleton actually addresses
all concerns (and more) that you mentioned: mmaping, knowing offsets,
knowing names and types, etc. And it doesn't preclude adding more
"conventional" additional APIs to do everything more dynamically,
based on string names.

>
> RE anonymous structs: maybe don't use them if you want to share the data
> between bpf and userspace?

Alright.

>
> > I never said there is anything wrong with current straightforward
> > libbpf API, but I also never said it's the easiest and most
> > user-friendly way to work with BPF either. So we'll have both
> > code-generated interface and existing API. Furthermore, they are
> > interoperable (you can pass skel->maps.whatever to any of the existing
> > libbpf APIs, same for progs, links, obj itself). But there isn't much
> > that can beat performance and usability of code-generated .data, .bss,
> > .rodata (and now .extern) layout.
> I haven't looked closely enough, but is there a libbpf api to get
> an offset of a variable? Suppose I have the following in bpf.c:
>
>         int a;
>         int b;
>
> Can I get an offset of 'b' in the .bss without manually parsing BTF?

No there isn't right now. There isn't even an API to know that there
is such a variable called "b". Except for this skeleton, of course.

>
> TBH, I don't buy the performance argument for these global maps.
> When you did the mmap patchset for the array, you said it yourself
> that it's about convenience and not performance.

Yes, it's first and foremost about convenience, addressing exactly the
problems you mentioned above. But performance is critical for some use
cases, and nothing can beat memory-mapped view of BPF map for those.
Think about the case of frequently polling (or even atomically
exchanging) some stats from userspace, as one possible example. E.g.,
like some map statistics (number of filled elements, p50 of whatever
of those elements, etc). I'm not sure what's there to buy: doing
syscall to get **entire** global data map contents vs just fetching
single integer from memory-mapped region, guess which one is cheaper?

>
> > > As for type-safety: it's C, forget about it :-)
> >
> > C is weakly, but still typed language. There are types and they are
> > helpful. Yes, you can disregard them and re-interpret values as
> > anything, but that's beside the point.
> My point was that there is a certain mental model when working
> with this type of external symbols which feels "natural" for C
> (dlopen/dlsym).
>
> But I agree with you, that as long as code-gen is optional
> and there is an alternative api in libbpf, we should be good.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-11 18:26                   ` Andrii Nakryiko
@ 2019-12-11 19:15                     ` Stanislav Fomichev
  2019-12-11 19:41                       ` Andrii Nakryiko
  0 siblings, 1 reply; 57+ messages in thread
From: Stanislav Fomichev @ 2019-12-11 19:15 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On 12/11, Andrii Nakryiko wrote:
> On Wed, Dec 11, 2019 at 9:24 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> >
> > On 12/10, Andrii Nakryiko wrote:
> > > On Tue, Dec 10, 2019 at 2:59 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > >
> > > > On 12/10, Andrii Nakryiko wrote:
> > > > > On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > >
> > > > > > On 12/10, Jakub Kicinski wrote:
> > > > > > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > > > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > struct <object-name> {
> > > > > > > > > >       /* used by libbpf's skeleton API */
> > > > > > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > > > > > >       /* bpf_object for libbpf APIs */
> > > > > > > > > >       struct bpf_object *obj;
> > > > > > > > > >       struct {
> > > > > > > > > >               /* for every defined map in BPF object: */
> > > > > > > > > >               struct bpf_map *<map-name>;
> > > > > > > > > >       } maps;
> > > > > > > > > >       struct {
> > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > >               struct bpf_program *<program-name>;
> > > > > > > > > >       } progs;
> > > > > > > > > >       struct {
> > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > >               struct bpf_link *<program-name>;
> > > > > > > > > >       } links;
> > > > > > > > > >       /* for every present global data section: */
> > > > > > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > > > > > >               /* memory layout of corresponding data section,
> > > > > > > > > >                * with every defined variable represented as a struct field
> > > > > > > > > >                * with exactly the same type, but without const/volatile
> > > > > > > > > >                * modifiers, e.g.:
> > > > > > > > > >                */
> > > > > > > > > >                int *my_var_1;
> > > > > > > > > >                ...
> > > > > > > > > >       } *<one of bss, data, or rodata>;
> > > > > > > > > > };
> > > > > > > > >
> > > > > > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > > > > > is that we're using C for everything, and simple programs for which
> > > > > > > > > loading the ELF is majority of the code would be better of being
> > > > > > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > > > > > better idea to work on some high-level language bindings than spend
> > > > > > > > > time writing code gens and working around limitations of C?
> > > > > > > >
> > > > > > > > None of this work prevents Python bindings and other improvements, is
> > > > > > > > it? Patches, as always, are greatly appreciated ;)
> > > > > > >
> > > > > > > This "do it yourself" shit is not really funny :/
> > > > > > >
> > > > > > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > > > > > that :/ Maybe that's what you want.
> > > > > > >
> > > > > > > > This skeleton stuff is not just to save code, but in general to
> > > > > > > > simplify and streamline working with BPF program from userspace side.
> > > > > > > > Fortunately or not, but there are a lot of real-world applications
> > > > > > > > written in C and C++ that could benefit from this, so this is still
> > > > > > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > > > > > work, see few of the last patches in this series.
> > > > > > >
> > > > > > > Maybe those applications are written in C and C++ _because_ there
> > > > > > > are no bindings for high level languages. I just wish BPF programming
> > > > > > > was less weird and adding some funky codegen is not getting us closer
> > > > > > > to that goal.
> > > > > > >
> > > > > > > In my experience code gen is nothing more than a hack to work around
> > > > > > > bad APIs, but experiences differ so that's not a solid argument.
> > > > > > *nod*
> > > > > >
> > > > > > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > > > > > something like BpfMap<key type, value type> and get a much better interface
> > > > > > with type checking. Maybe we should focus on higher level languages instead?
> > > > > > We are open to open-sourcing our C++ bits if you want to collaborate.
> > > > >
> > > > > Python/C++ bindings and API wrappers are an orthogonal concerns here.
> > > > > I personally think it would be great to have both Python and C++
> > > > > specific API that uses libbpf under the cover. The only debatable
> > > > > thing is the logistics: where the source code lives, how it's kept in
> > > > > sync with libbpf, how we avoid crippling libbpf itself because
> > > > > something is hard or inconvenient to adapt w/ Python, etc.
> > > >
> > > > [..]
> > > > > The problem I'm trying to solve here is not really C-specific. I don't
> > > > > think you can solve it without code generation for C++. How do you
> > > > > "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> > > > > data sections in such a way, where it's type safe (to the degree that
> > > > > language allows that, of course) and is not "stringly-based" API? This
> > > > > skeleton stuff provides a natural, convenient and type-safe way to
> > > > > work with global data from userspace pretty much at the same level of
> > > > > performance and convenience, as from BPF side. How can you achieve
> > > > > that w/ C++ without code generation? As for Python, sure you can do
> > > > > dynamic lookups based on just the name of property/method, but amount
> > > > > of overheads is not acceptable for all applications (and Python itself
> > > > > is not acceptable for those applications). In addition to that, C is
> > > > > the best way for other less popular languages (e.g., Rust) to leverage
> > > > > libbpf without investing lots of effort in re-implementing libbpf in
> > > > > Rust.
> > > > I'd say that a libbpf API similar to dlopen/dlsym is a more
> > > > straightforward thing to do. Have a way to "open" a section and
> > > > a way to find a symbol in it. Yes, it's a string-based API,
> > > > but there is nothing wrong with it. IMO, this is easier to
> > > > use/understand and I suppose Python/C++ wrappers are trivial.
> > >
> > > Without digging through libbpf source code (or actually, look at code,
> > > but don't run any test program), what's the name of the map
> > > corresponding to .bss section, if object file is
> > > some_bpf_object_file.o? If you got it right (congrats, btw, it took me
> > > multiple attempts to memorize the pattern), how much time did you
> > > spend looking it up? Now compare it to `skel->maps.bss`. Further, if
> > > you use anonymous structs for your global vars, good luck maintaining
> > > two copies of that: one for BPF side and one for userspace.
> > As your average author of BPF programs I don't really care
> > which section my symbol ends up into. Just give me an api
> > to mmap all "global" sections (or a call per section which does all the
> > naming magic inside) and lookup symbol by name; I can cast it to a proper
> > type and set it.
> 
> I'd like to not have to know about bss/rodata/data as well, but that's
> how things are done for global variables. In skeleton we can try to
> make an illusion like they are part of one big datasection/struct, but
> that seems like a bit too much magic at this point. But then again,
> one of the reasons I want this as an experimental feature, so that we
> can actually judge from real experience how inconvenient some things
> are, and not just based on "I think it would be ...".
> 
> re: "Just give me ...". Following the spirit of "C is hard" from your
> previous arguments, you already have that API: mmap() syscall. C
> programmers have to be able to figure out the rest ;) But on the
> serious note, this auto-generated code in skeleton actually addresses
> all concerns (and more) that you mentioned: mmaping, knowing offsets,
> knowing names and types, etc. And it doesn't preclude adding more
> "conventional" additional APIs to do everything more dynamically,
> based on string names.
We have different understanding of what's difficult :-)

To me, doing transparent data/rodata/bss mmap in bpf_object__load and then
adding a single libbpf api call to lookup symbol by string name is simple
(both from user perspective and from libbpf code complexity). Because in
order to use the codegen I need to teach our build system to spit it
out (which means I need to add bpftool to it and keep it
updated/etc/etc). You can use it as an example of "real experience how
inconvenient some things are".

> > RE anonymous structs: maybe don't use them if you want to share the data
> > between bpf and userspace?
> 
> Alright.
> 
> >
> > > I never said there is anything wrong with current straightforward
> > > libbpf API, but I also never said it's the easiest and most
> > > user-friendly way to work with BPF either. So we'll have both
> > > code-generated interface and existing API. Furthermore, they are
> > > interoperable (you can pass skel->maps.whatever to any of the existing
> > > libbpf APIs, same for progs, links, obj itself). But there isn't much
> > > that can beat performance and usability of code-generated .data, .bss,
> > > .rodata (and now .extern) layout.
> > I haven't looked closely enough, but is there a libbpf api to get
> > an offset of a variable? Suppose I have the following in bpf.c:
> >
> >         int a;
> >         int b;
> >
> > Can I get an offset of 'b' in the .bss without manually parsing BTF?
> 
> No there isn't right now. There isn't even an API to know that there
> is such a variable called "b". Except for this skeleton, of course.
> 
> >
> > TBH, I don't buy the performance argument for these global maps.
> > When you did the mmap patchset for the array, you said it yourself
> > that it's about convenience and not performance.
> 
> Yes, it's first and foremost about convenience, addressing exactly the
> problems you mentioned above. But performance is critical for some use
> cases, and nothing can beat memory-mapped view of BPF map for those.
> Think about the case of frequently polling (or even atomically
> exchanging) some stats from userspace, as one possible example. E.g.,
> like some map statistics (number of filled elements, p50 of whatever
> of those elements, etc). I'm not sure what's there to buy: doing
> syscall to get **entire** global data map contents vs just fetching
> single integer from memory-mapped region, guess which one is cheaper?
My understanding was that when you were talking about performance, you
were talking about doing symbol offset lookup at runtime vs having a
generated struct with fixed offsets; not about mmap vs old api with copy
(this debate is settled since your patches are accepted).

But to your original reply: you do understand that if you have multiple
threads that write to this global data you have a bigger problem, right?

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Potential Spoof] [PATCH bpf-next 06/15] libbpf: expose BPF program's function name
  2019-12-10  1:14 ` [PATCH bpf-next 06/15] libbpf: expose BPF program's function name Andrii Nakryiko
@ 2019-12-11 19:38   ` Martin Lau
  2019-12-11 19:54     ` Andrii Nakryiko
  0 siblings, 1 reply; 57+ messages in thread
From: Martin Lau @ 2019-12-11 19:38 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, netdev, Alexei Starovoitov, daniel, andrii.nakryiko, Kernel Team

On Mon, Dec 09, 2019 at 05:14:29PM -0800, Andrii Nakryiko wrote:
> Add APIs to get BPF program function name, as opposed to bpf_program__title(),
> which returns BPF program function's section name. Function name has a benefit
> of being a valid C identifier and uniquely identifies a specific BPF program,
> while section name can be duplicated across multiple independent BPF programs.
> 
> Add also bpf_object__find_program_by_name(), similar to
> bpf_object__find_program_by_title(), to facilitate looking up BPF programs by
> their C function names.
> 
> Convert one of selftests to new API for look up.
> 
> Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> ---
>  tools/lib/bpf/libbpf.c                        | 28 +++++++++++++++----
>  tools/lib/bpf/libbpf.h                        |  9 ++++--
>  tools/lib/bpf/libbpf.map                      |  2 ++
>  .../selftests/bpf/prog_tests/rdonly_maps.c    | 11 +++-----
>  4 files changed, 36 insertions(+), 14 deletions(-)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index edfe1cf1e940..f13752c4d271 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -209,8 +209,8 @@ static const char * const libbpf_type_to_btf_name[] = {
>  };
>  
>  struct bpf_map {
> -	int fd;
>  	char *name;
> +	int fd;
This change, and

>  	int sec_idx;
>  	size_t sec_offset;
>  	int map_ifindex;
> @@ -1384,7 +1384,7 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
>  }
>  
>  static int bpf_object__init_maps(struct bpf_object *obj,
> -				 struct bpf_object_open_opts *opts)
> +				 const struct bpf_object_open_opts *opts)
here, and a few other const changes,

are all good changes.  If they are not in a separate patch, it will be useful
to the reviewer if there is commit messages mentioning there are some
unrelated cleanup changes.  I have been looking at where it may cause
compiler warning because of this change, or I missed something?

>  {
>  	const char *pin_root_path = OPTS_GET(opts, pin_root_path, NULL);
>  	bool strict = !OPTS_GET(opts, relaxed_maps, false);
> @@ -1748,6 +1748,19 @@ bpf_object__find_program_by_title(const struct bpf_object *obj,
>  	return NULL;
>  }
>  
> +struct bpf_program *
> +bpf_object__find_program_by_name(const struct bpf_object *obj,
> +				 const char *name)
> +{
> +	struct bpf_program *prog;
> +
> +	bpf_object__for_each_program(prog, obj) {
> +		if (!strcmp(prog->name, name))
> +			return prog;
> +	}
> +	return NULL;
> +}
> +
>  static bool bpf_object__shndx_is_data(const struct bpf_object *obj,
>  				      int shndx)
>  {
> @@ -3893,7 +3906,7 @@ static int libbpf_find_attach_btf_id(const char *name,
>  				     __u32 attach_prog_fd);
>  static struct bpf_object *
>  __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
> -		   struct bpf_object_open_opts *opts)
> +		   const struct bpf_object_open_opts *opts)
>  {
>  	struct bpf_program *prog;
>  	struct bpf_object *obj;
> @@ -4002,7 +4015,7 @@ struct bpf_object *bpf_object__open(const char *path)
>  }
>  
>  struct bpf_object *
> -bpf_object__open_file(const char *path, struct bpf_object_open_opts *opts)
> +bpf_object__open_file(const char *path, const struct bpf_object_open_opts *opts)
>  {
>  	if (!path)
>  		return ERR_PTR(-EINVAL);
> @@ -4014,7 +4027,7 @@ bpf_object__open_file(const char *path, struct bpf_object_open_opts *opts)
>  
>  struct bpf_object *
>  bpf_object__open_mem(const void *obj_buf, size_t obj_buf_sz,
> -		     struct bpf_object_open_opts *opts)
> +		     const struct bpf_object_open_opts *opts)
>  {
>  	if (!obj_buf || obj_buf_sz == 0)
>  		return ERR_PTR(-EINVAL);
> @@ -4819,6 +4832,11 @@ void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex)
>  	prog->prog_ifindex = ifindex;
>  }
>  
> +const char *bpf_program__name(const struct bpf_program *prog)
> +{
> +	return prog->name;
> +}
> +
>  const char *bpf_program__title(const struct bpf_program *prog, bool needs_copy)
>  {
>  	const char *title;
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index fa803dde1f46..7fa583ebe56f 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -114,10 +114,10 @@ struct bpf_object_open_opts {
>  
>  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);
> +bpf_object__open_file(const char *path, const 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);
> +		     const struct bpf_object_open_opts *opts);
>  
>  /* deprecated bpf_object__open variants */
>  LIBBPF_API struct bpf_object *
> @@ -156,6 +156,7 @@ struct bpf_object_load_attr {
>  LIBBPF_API int bpf_object__load(struct bpf_object *obj);
>  LIBBPF_API int bpf_object__load_xattr(struct bpf_object_load_attr *attr);
>  LIBBPF_API int bpf_object__unload(struct bpf_object *obj);
> +
>  LIBBPF_API const char *bpf_object__name(const struct bpf_object *obj);
>  LIBBPF_API unsigned int bpf_object__kversion(const struct bpf_object *obj);
>  
> @@ -166,6 +167,9 @@ LIBBPF_API int bpf_object__btf_fd(const struct bpf_object *obj);
>  LIBBPF_API struct bpf_program *
>  bpf_object__find_program_by_title(const struct bpf_object *obj,
>  				  const char *title);
> +LIBBPF_API struct bpf_program *
> +bpf_object__find_program_by_name(const struct bpf_object *obj,
> +				 const char *name);
>  
>  LIBBPF_API struct bpf_object *bpf_object__next(struct bpf_object *prev);
>  #define bpf_object__for_each_safe(pos, tmp)			\
> @@ -209,6 +213,7 @@ LIBBPF_API void *bpf_program__priv(const struct bpf_program *prog);
>  LIBBPF_API void bpf_program__set_ifindex(struct bpf_program *prog,
>  					 __u32 ifindex);
>  
> +LIBBPF_API const char *bpf_program__name(const struct bpf_program *prog);
>  LIBBPF_API const char *bpf_program__title(const struct bpf_program *prog,
>  					  bool needs_copy);
>  
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index 757a88f64b5a..f2b2fa0f5c2a 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -211,5 +211,7 @@ LIBBPF_0.0.6 {
>  
>  LIBBPF_0.0.7 {
>  	global:
> +		bpf_object__find_program_by_name;
>  		bpf_program__attach;
> +		bpf_program__name;
>  } LIBBPF_0.0.6;
> diff --git a/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c b/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c
> index d90acc13d1ec..563e12120e77 100644
> --- a/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c
> +++ b/tools/testing/selftests/bpf/prog_tests/rdonly_maps.c
> @@ -16,14 +16,11 @@ struct rdonly_map_subtest {
>  
>  void test_rdonly_maps(void)
>  {
> -	const char *prog_name_skip_loop = "raw_tracepoint/sys_enter:skip_loop";
> -	const char *prog_name_part_loop = "raw_tracepoint/sys_enter:part_loop";
> -	const char *prog_name_full_loop = "raw_tracepoint/sys_enter:full_loop";
>  	const char *file = "test_rdonly_maps.o";
>  	struct rdonly_map_subtest subtests[] = {
> -		{ "skip loop", prog_name_skip_loop, 0, 0 },
> -		{ "part loop", prog_name_part_loop, 3, 2 + 3 + 4 },
> -		{ "full loop", prog_name_full_loop, 4, 2 + 3 + 4 + 5 },
> +		{ "skip loop", "skip_loop", 0, 0 },
> +		{ "part loop", "part_loop", 3, 2 + 3 + 4 },
> +		{ "full loop", "full_loop", 4, 2 + 3 + 4 + 5 },
>  	};
>  	int i, err, zero = 0, duration = 0;
>  	struct bpf_link *link = NULL;
> @@ -50,7 +47,7 @@ void test_rdonly_maps(void)
>  		if (!test__start_subtest(t->subtest_name))
>  			continue;
>  
> -		prog = bpf_object__find_program_by_title(obj, t->prog_name);
> +		prog = bpf_object__find_program_by_name(obj, t->prog_name);
>  		if (CHECK(!prog, "find_prog", "prog '%s' not found\n",
>  			  t->prog_name))
>  			goto cleanup;
> -- 
> 2.17.1
> 

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-11 19:15                     ` Stanislav Fomichev
@ 2019-12-11 19:41                       ` Andrii Nakryiko
  2019-12-11 20:09                         ` Stanislav Fomichev
  0 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-11 19:41 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Wed, Dec 11, 2019 at 11:15 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
>
> On 12/11, Andrii Nakryiko wrote:
> > On Wed, Dec 11, 2019 at 9:24 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > >
> > > On 12/10, Andrii Nakryiko wrote:
> > > > On Tue, Dec 10, 2019 at 2:59 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > >
> > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > >
> > > > > > > On 12/10, Jakub Kicinski wrote:
> > > > > > > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > > > > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > > > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > struct <object-name> {
> > > > > > > > > > >       /* used by libbpf's skeleton API */
> > > > > > > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > > > > > > >       /* bpf_object for libbpf APIs */
> > > > > > > > > > >       struct bpf_object *obj;
> > > > > > > > > > >       struct {
> > > > > > > > > > >               /* for every defined map in BPF object: */
> > > > > > > > > > >               struct bpf_map *<map-name>;
> > > > > > > > > > >       } maps;
> > > > > > > > > > >       struct {
> > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > >               struct bpf_program *<program-name>;
> > > > > > > > > > >       } progs;
> > > > > > > > > > >       struct {
> > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > >               struct bpf_link *<program-name>;
> > > > > > > > > > >       } links;
> > > > > > > > > > >       /* for every present global data section: */
> > > > > > > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > > > > > > >               /* memory layout of corresponding data section,
> > > > > > > > > > >                * with every defined variable represented as a struct field
> > > > > > > > > > >                * with exactly the same type, but without const/volatile
> > > > > > > > > > >                * modifiers, e.g.:
> > > > > > > > > > >                */
> > > > > > > > > > >                int *my_var_1;
> > > > > > > > > > >                ...
> > > > > > > > > > >       } *<one of bss, data, or rodata>;
> > > > > > > > > > > };
> > > > > > > > > >
> > > > > > > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > > > > > > is that we're using C for everything, and simple programs for which
> > > > > > > > > > loading the ELF is majority of the code would be better of being
> > > > > > > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > > > > > > better idea to work on some high-level language bindings than spend
> > > > > > > > > > time writing code gens and working around limitations of C?
> > > > > > > > >
> > > > > > > > > None of this work prevents Python bindings and other improvements, is
> > > > > > > > > it? Patches, as always, are greatly appreciated ;)
> > > > > > > >
> > > > > > > > This "do it yourself" shit is not really funny :/
> > > > > > > >
> > > > > > > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > > > > > > that :/ Maybe that's what you want.
> > > > > > > >
> > > > > > > > > This skeleton stuff is not just to save code, but in general to
> > > > > > > > > simplify and streamline working with BPF program from userspace side.
> > > > > > > > > Fortunately or not, but there are a lot of real-world applications
> > > > > > > > > written in C and C++ that could benefit from this, so this is still
> > > > > > > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > > > > > > work, see few of the last patches in this series.
> > > > > > > >
> > > > > > > > Maybe those applications are written in C and C++ _because_ there
> > > > > > > > are no bindings for high level languages. I just wish BPF programming
> > > > > > > > was less weird and adding some funky codegen is not getting us closer
> > > > > > > > to that goal.
> > > > > > > >
> > > > > > > > In my experience code gen is nothing more than a hack to work around
> > > > > > > > bad APIs, but experiences differ so that's not a solid argument.
> > > > > > > *nod*
> > > > > > >
> > > > > > > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > > > > > > something like BpfMap<key type, value type> and get a much better interface
> > > > > > > with type checking. Maybe we should focus on higher level languages instead?
> > > > > > > We are open to open-sourcing our C++ bits if you want to collaborate.
> > > > > >
> > > > > > Python/C++ bindings and API wrappers are an orthogonal concerns here.
> > > > > > I personally think it would be great to have both Python and C++
> > > > > > specific API that uses libbpf under the cover. The only debatable
> > > > > > thing is the logistics: where the source code lives, how it's kept in
> > > > > > sync with libbpf, how we avoid crippling libbpf itself because
> > > > > > something is hard or inconvenient to adapt w/ Python, etc.
> > > > >
> > > > > [..]
> > > > > > The problem I'm trying to solve here is not really C-specific. I don't
> > > > > > think you can solve it without code generation for C++. How do you
> > > > > > "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> > > > > > data sections in such a way, where it's type safe (to the degree that
> > > > > > language allows that, of course) and is not "stringly-based" API? This
> > > > > > skeleton stuff provides a natural, convenient and type-safe way to
> > > > > > work with global data from userspace pretty much at the same level of
> > > > > > performance and convenience, as from BPF side. How can you achieve
> > > > > > that w/ C++ without code generation? As for Python, sure you can do
> > > > > > dynamic lookups based on just the name of property/method, but amount
> > > > > > of overheads is not acceptable for all applications (and Python itself
> > > > > > is not acceptable for those applications). In addition to that, C is
> > > > > > the best way for other less popular languages (e.g., Rust) to leverage
> > > > > > libbpf without investing lots of effort in re-implementing libbpf in
> > > > > > Rust.
> > > > > I'd say that a libbpf API similar to dlopen/dlsym is a more
> > > > > straightforward thing to do. Have a way to "open" a section and
> > > > > a way to find a symbol in it. Yes, it's a string-based API,
> > > > > but there is nothing wrong with it. IMO, this is easier to
> > > > > use/understand and I suppose Python/C++ wrappers are trivial.
> > > >
> > > > Without digging through libbpf source code (or actually, look at code,
> > > > but don't run any test program), what's the name of the map
> > > > corresponding to .bss section, if object file is
> > > > some_bpf_object_file.o? If you got it right (congrats, btw, it took me
> > > > multiple attempts to memorize the pattern), how much time did you
> > > > spend looking it up? Now compare it to `skel->maps.bss`. Further, if
> > > > you use anonymous structs for your global vars, good luck maintaining
> > > > two copies of that: one for BPF side and one for userspace.
> > > As your average author of BPF programs I don't really care
> > > which section my symbol ends up into. Just give me an api
> > > to mmap all "global" sections (or a call per section which does all the
> > > naming magic inside) and lookup symbol by name; I can cast it to a proper
> > > type and set it.
> >
> > I'd like to not have to know about bss/rodata/data as well, but that's
> > how things are done for global variables. In skeleton we can try to
> > make an illusion like they are part of one big datasection/struct, but
> > that seems like a bit too much magic at this point. But then again,
> > one of the reasons I want this as an experimental feature, so that we
> > can actually judge from real experience how inconvenient some things
> > are, and not just based on "I think it would be ...".
> >
> > re: "Just give me ...". Following the spirit of "C is hard" from your
> > previous arguments, you already have that API: mmap() syscall. C
> > programmers have to be able to figure out the rest ;) But on the
> > serious note, this auto-generated code in skeleton actually addresses
> > all concerns (and more) that you mentioned: mmaping, knowing offsets,
> > knowing names and types, etc. And it doesn't preclude adding more
> > "conventional" additional APIs to do everything more dynamically,
> > based on string names.
> We have different understanding of what's difficult :-)

Well, clearly... See below.

>
> To me, doing transparent data/rodata/bss mmap in bpf_object__load and then
> adding a single libbpf api call to lookup symbol by string name is simple
> (both from user perspective and from libbpf code complexity). Because in
> order to use the codegen I need to teach our build system to spit it
> out (which means I need to add bpftool to it and keep it
> updated/etc/etc). You can use it as an example of "real experience how
> inconvenient some things are".

Yes, you need to integrate bpftool in your build process. Which is
exactly what I'm doing internally for Facebook as well. But it's a
mostly one-time cost, which benefits lots of users who have much
better time with these changes, as opposed to make things simpler for
us, libbpf developers, at the expense of more convoluted user
experience for end users. I certainly prefer more complicated
libbpf/bpftool code, if the resulting user experience is simpler for
BPF application developers, no doubt about it.

>
> > > RE anonymous structs: maybe don't use them if you want to share the data
> > > between bpf and userspace?
> >
> > Alright.
> >
> > >
> > > > I never said there is anything wrong with current straightforward
> > > > libbpf API, but I also never said it's the easiest and most
> > > > user-friendly way to work with BPF either. So we'll have both
> > > > code-generated interface and existing API. Furthermore, they are
> > > > interoperable (you can pass skel->maps.whatever to any of the existing
> > > > libbpf APIs, same for progs, links, obj itself). But there isn't much
> > > > that can beat performance and usability of code-generated .data, .bss,
> > > > .rodata (and now .extern) layout.
> > > I haven't looked closely enough, but is there a libbpf api to get
> > > an offset of a variable? Suppose I have the following in bpf.c:
> > >
> > >         int a;
> > >         int b;
> > >
> > > Can I get an offset of 'b' in the .bss without manually parsing BTF?
> >
> > No there isn't right now. There isn't even an API to know that there
> > is such a variable called "b". Except for this skeleton, of course.
> >
> > >
> > > TBH, I don't buy the performance argument for these global maps.
> > > When you did the mmap patchset for the array, you said it yourself
> > > that it's about convenience and not performance.
> >
> > Yes, it's first and foremost about convenience, addressing exactly the
> > problems you mentioned above. But performance is critical for some use
> > cases, and nothing can beat memory-mapped view of BPF map for those.
> > Think about the case of frequently polling (or even atomically
> > exchanging) some stats from userspace, as one possible example. E.g.,
> > like some map statistics (number of filled elements, p50 of whatever
> > of those elements, etc). I'm not sure what's there to buy: doing
> > syscall to get **entire** global data map contents vs just fetching
> > single integer from memory-mapped region, guess which one is cheaper?
> My understanding was that when you were talking about performance, you
> were talking about doing symbol offset lookup at runtime vs having a
> generated struct with fixed offsets; not about mmap vs old api with copy
> (this debate is settled since your patches are accepted).

Oh, I see. No, I didn't intend to claim that performance of looking up
variable by name in BTF is a big performance concern. Settled then :)

>
> But to your original reply: you do understand that if you have multiple
> threads that write to this global data you have a bigger problem, right?

Not necessarily. BPF has atomic increment instruction, doesn't it? And
can't we still do atomic swap from user-space (it's just a memory,
after all), right? I haven't tried, tbh, but don't see why it wouldn't
work.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Potential Spoof] [PATCH bpf-next 06/15] libbpf: expose BPF program's function name
  2019-12-11 19:38   ` [Potential Spoof] " Martin Lau
@ 2019-12-11 19:54     ` Andrii Nakryiko
  0 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-11 19:54 UTC (permalink / raw)
  To: Martin Lau
  Cc: Andrii Nakryiko, bpf, netdev, Alexei Starovoitov, daniel, Kernel Team

On Wed, Dec 11, 2019 at 11:38 AM Martin Lau <kafai@fb.com> wrote:
>
> On Mon, Dec 09, 2019 at 05:14:29PM -0800, Andrii Nakryiko wrote:
> > Add APIs to get BPF program function name, as opposed to bpf_program__title(),
> > which returns BPF program function's section name. Function name has a benefit
> > of being a valid C identifier and uniquely identifies a specific BPF program,
> > while section name can be duplicated across multiple independent BPF programs.
> >
> > Add also bpf_object__find_program_by_name(), similar to
> > bpf_object__find_program_by_title(), to facilitate looking up BPF programs by
> > their C function names.
> >
> > Convert one of selftests to new API for look up.
> >
> > Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> > ---
> >  tools/lib/bpf/libbpf.c                        | 28 +++++++++++++++----
> >  tools/lib/bpf/libbpf.h                        |  9 ++++--
> >  tools/lib/bpf/libbpf.map                      |  2 ++
> >  .../selftests/bpf/prog_tests/rdonly_maps.c    | 11 +++-----
> >  4 files changed, 36 insertions(+), 14 deletions(-)
> >
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index edfe1cf1e940..f13752c4d271 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -209,8 +209,8 @@ static const char * const libbpf_type_to_btf_name[] = {
> >  };
> >
> >  struct bpf_map {
> > -     int fd;
> >       char *name;
> > +     int fd;
> This change, and

Oh, no reason beyond eliminating completely unnecessary 4 byte
padding. This one I didn't think is worth mentioning at all, it's just
an internal struct rearrangement.

>
> >       int sec_idx;
> >       size_t sec_offset;
> >       int map_ifindex;
> > @@ -1384,7 +1384,7 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
> >  }
> >
> >  static int bpf_object__init_maps(struct bpf_object *obj,
> > -                              struct bpf_object_open_opts *opts)
> > +                              const struct bpf_object_open_opts *opts)
> here, and a few other const changes,
>
> are all good changes.  If they are not in a separate patch, it will be useful
> to the reviewer if there is commit messages mentioning there are some
> unrelated cleanup changes.  I have been looking at where it may cause
> compiler warning because of this change, or I missed something?

My bad. I could split it out into a separate patch but didn't bother.
The reason for this change is that those opts were always supposed to
be passed as pointer to const struct and it was just an ommision on my
side to not declare them as such. But not doing it in this patch set
would mean that bpf_object__open_skeleton API would have to stick to
non-const opts as well, and I didn't want to proliferate this blunt.

>
> >  {
> >       const char *pin_root_path = OPTS_GET(opts, pin_root_path, NULL);
> >       bool strict = !OPTS_GET(opts, relaxed_maps, false);
> > @@ -1748,6 +1748,19 @@ bpf_object__find_program_by_title(const struct bpf_object *obj,
> >       return NULL;
> >  }
> >

trimming irrelevant parts ;)

[...]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-11 19:41                       ` Andrii Nakryiko
@ 2019-12-11 20:09                         ` Stanislav Fomichev
  2019-12-12  0:50                           ` Andrii Nakryiko
  0 siblings, 1 reply; 57+ messages in thread
From: Stanislav Fomichev @ 2019-12-11 20:09 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On 12/11, Andrii Nakryiko wrote:
> On Wed, Dec 11, 2019 at 11:15 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> >
> > On 12/11, Andrii Nakryiko wrote:
> > > On Wed, Dec 11, 2019 at 9:24 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > >
> > > > On 12/10, Andrii Nakryiko wrote:
> > > > > On Tue, Dec 10, 2019 at 2:59 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > >
> > > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > > On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > >
> > > > > > > > On 12/10, Jakub Kicinski wrote:
> > > > > > > > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > > > > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > > struct <object-name> {
> > > > > > > > > > > >       /* used by libbpf's skeleton API */
> > > > > > > > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > > > > > > > >       /* bpf_object for libbpf APIs */
> > > > > > > > > > > >       struct bpf_object *obj;
> > > > > > > > > > > >       struct {
> > > > > > > > > > > >               /* for every defined map in BPF object: */
> > > > > > > > > > > >               struct bpf_map *<map-name>;
> > > > > > > > > > > >       } maps;
> > > > > > > > > > > >       struct {
> > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > >               struct bpf_program *<program-name>;
> > > > > > > > > > > >       } progs;
> > > > > > > > > > > >       struct {
> > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > >               struct bpf_link *<program-name>;
> > > > > > > > > > > >       } links;
> > > > > > > > > > > >       /* for every present global data section: */
> > > > > > > > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > > > > > > > >               /* memory layout of corresponding data section,
> > > > > > > > > > > >                * with every defined variable represented as a struct field
> > > > > > > > > > > >                * with exactly the same type, but without const/volatile
> > > > > > > > > > > >                * modifiers, e.g.:
> > > > > > > > > > > >                */
> > > > > > > > > > > >                int *my_var_1;
> > > > > > > > > > > >                ...
> > > > > > > > > > > >       } *<one of bss, data, or rodata>;
> > > > > > > > > > > > };
> > > > > > > > > > >
> > > > > > > > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > > > > > > > is that we're using C for everything, and simple programs for which
> > > > > > > > > > > loading the ELF is majority of the code would be better of being
> > > > > > > > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > > > > > > > better idea to work on some high-level language bindings than spend
> > > > > > > > > > > time writing code gens and working around limitations of C?
> > > > > > > > > >
> > > > > > > > > > None of this work prevents Python bindings and other improvements, is
> > > > > > > > > > it? Patches, as always, are greatly appreciated ;)
> > > > > > > > >
> > > > > > > > > This "do it yourself" shit is not really funny :/
> > > > > > > > >
> > > > > > > > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > > > > > > > that :/ Maybe that's what you want.
> > > > > > > > >
> > > > > > > > > > This skeleton stuff is not just to save code, but in general to
> > > > > > > > > > simplify and streamline working with BPF program from userspace side.
> > > > > > > > > > Fortunately or not, but there are a lot of real-world applications
> > > > > > > > > > written in C and C++ that could benefit from this, so this is still
> > > > > > > > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > > > > > > > work, see few of the last patches in this series.
> > > > > > > > >
> > > > > > > > > Maybe those applications are written in C and C++ _because_ there
> > > > > > > > > are no bindings for high level languages. I just wish BPF programming
> > > > > > > > > was less weird and adding some funky codegen is not getting us closer
> > > > > > > > > to that goal.
> > > > > > > > >
> > > > > > > > > In my experience code gen is nothing more than a hack to work around
> > > > > > > > > bad APIs, but experiences differ so that's not a solid argument.
> > > > > > > > *nod*
> > > > > > > >
> > > > > > > > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > > > > > > > something like BpfMap<key type, value type> and get a much better interface
> > > > > > > > with type checking. Maybe we should focus on higher level languages instead?
> > > > > > > > We are open to open-sourcing our C++ bits if you want to collaborate.
> > > > > > >
> > > > > > > Python/C++ bindings and API wrappers are an orthogonal concerns here.
> > > > > > > I personally think it would be great to have both Python and C++
> > > > > > > specific API that uses libbpf under the cover. The only debatable
> > > > > > > thing is the logistics: where the source code lives, how it's kept in
> > > > > > > sync with libbpf, how we avoid crippling libbpf itself because
> > > > > > > something is hard or inconvenient to adapt w/ Python, etc.
> > > > > >
> > > > > > [..]
> > > > > > > The problem I'm trying to solve here is not really C-specific. I don't
> > > > > > > think you can solve it without code generation for C++. How do you
> > > > > > > "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> > > > > > > data sections in such a way, where it's type safe (to the degree that
> > > > > > > language allows that, of course) and is not "stringly-based" API? This
> > > > > > > skeleton stuff provides a natural, convenient and type-safe way to
> > > > > > > work with global data from userspace pretty much at the same level of
> > > > > > > performance and convenience, as from BPF side. How can you achieve
> > > > > > > that w/ C++ without code generation? As for Python, sure you can do
> > > > > > > dynamic lookups based on just the name of property/method, but amount
> > > > > > > of overheads is not acceptable for all applications (and Python itself
> > > > > > > is not acceptable for those applications). In addition to that, C is
> > > > > > > the best way for other less popular languages (e.g., Rust) to leverage
> > > > > > > libbpf without investing lots of effort in re-implementing libbpf in
> > > > > > > Rust.
> > > > > > I'd say that a libbpf API similar to dlopen/dlsym is a more
> > > > > > straightforward thing to do. Have a way to "open" a section and
> > > > > > a way to find a symbol in it. Yes, it's a string-based API,
> > > > > > but there is nothing wrong with it. IMO, this is easier to
> > > > > > use/understand and I suppose Python/C++ wrappers are trivial.
> > > > >
> > > > > Without digging through libbpf source code (or actually, look at code,
> > > > > but don't run any test program), what's the name of the map
> > > > > corresponding to .bss section, if object file is
> > > > > some_bpf_object_file.o? If you got it right (congrats, btw, it took me
> > > > > multiple attempts to memorize the pattern), how much time did you
> > > > > spend looking it up? Now compare it to `skel->maps.bss`. Further, if
> > > > > you use anonymous structs for your global vars, good luck maintaining
> > > > > two copies of that: one for BPF side and one for userspace.
> > > > As your average author of BPF programs I don't really care
> > > > which section my symbol ends up into. Just give me an api
> > > > to mmap all "global" sections (or a call per section which does all the
> > > > naming magic inside) and lookup symbol by name; I can cast it to a proper
> > > > type and set it.
> > >
> > > I'd like to not have to know about bss/rodata/data as well, but that's
> > > how things are done for global variables. In skeleton we can try to
> > > make an illusion like they are part of one big datasection/struct, but
> > > that seems like a bit too much magic at this point. But then again,
> > > one of the reasons I want this as an experimental feature, so that we
> > > can actually judge from real experience how inconvenient some things
> > > are, and not just based on "I think it would be ...".
> > >
> > > re: "Just give me ...". Following the spirit of "C is hard" from your
> > > previous arguments, you already have that API: mmap() syscall. C
> > > programmers have to be able to figure out the rest ;) But on the
> > > serious note, this auto-generated code in skeleton actually addresses
> > > all concerns (and more) that you mentioned: mmaping, knowing offsets,
> > > knowing names and types, etc. And it doesn't preclude adding more
> > > "conventional" additional APIs to do everything more dynamically,
> > > based on string names.
> > We have different understanding of what's difficult :-)
> 
> Well, clearly... See below.
> 
> >
> > To me, doing transparent data/rodata/bss mmap in bpf_object__load and then
> > adding a single libbpf api call to lookup symbol by string name is simple
> > (both from user perspective and from libbpf code complexity). Because in
> > order to use the codegen I need to teach our build system to spit it
> > out (which means I need to add bpftool to it and keep it
> > updated/etc/etc). You can use it as an example of "real experience how
> > inconvenient some things are".
> 
> Yes, you need to integrate bpftool in your build process. Which is
> exactly what I'm doing internally for Facebook as well. But it's a
> mostly one-time cost, which benefits lots of users who have much
> better time with these changes, as opposed to make things simpler for
> us, libbpf developers, at the expense of more convoluted user
> experience for end users. I certainly prefer more complicated
> libbpf/bpftool code, if the resulting user experience is simpler for
> BPF application developers, no doubt about it.
I'm in the process of going through this with pahole to get proper BTF.
I don't think I'm willing yet (without a good reason) to go through
this process again :-D (I saw that you've converted a bunch of tests
to it which means I might not be able to run them).

I just hope bpftool codegen doesn't become a requirement for
any new useful feature; same happened to BTF, which was optional
for a while and now I can't run a single selftest without it.
I can totally understand the BTF requirement though, but I don't buy the
"codegen makes user experience simple for bpf application developers",
sorry (I guess, at this point, it's all about preference).

> > > > RE anonymous structs: maybe don't use them if you want to share the data
> > > > between bpf and userspace?
> > >
> > > Alright.
> > >
> > > >
> > > > > I never said there is anything wrong with current straightforward
> > > > > libbpf API, but I also never said it's the easiest and most
> > > > > user-friendly way to work with BPF either. So we'll have both
> > > > > code-generated interface and existing API. Furthermore, they are
> > > > > interoperable (you can pass skel->maps.whatever to any of the existing
> > > > > libbpf APIs, same for progs, links, obj itself). But there isn't much
> > > > > that can beat performance and usability of code-generated .data, .bss,
> > > > > .rodata (and now .extern) layout.
> > > > I haven't looked closely enough, but is there a libbpf api to get
> > > > an offset of a variable? Suppose I have the following in bpf.c:
> > > >
> > > >         int a;
> > > >         int b;
> > > >
> > > > Can I get an offset of 'b' in the .bss without manually parsing BTF?
> > >
> > > No there isn't right now. There isn't even an API to know that there
> > > is such a variable called "b". Except for this skeleton, of course.
> > >
> > > >
> > > > TBH, I don't buy the performance argument for these global maps.
> > > > When you did the mmap patchset for the array, you said it yourself
> > > > that it's about convenience and not performance.
> > >
> > > Yes, it's first and foremost about convenience, addressing exactly the
> > > problems you mentioned above. But performance is critical for some use
> > > cases, and nothing can beat memory-mapped view of BPF map for those.
> > > Think about the case of frequently polling (or even atomically
> > > exchanging) some stats from userspace, as one possible example. E.g.,
> > > like some map statistics (number of filled elements, p50 of whatever
> > > of those elements, etc). I'm not sure what's there to buy: doing
> > > syscall to get **entire** global data map contents vs just fetching
> > > single integer from memory-mapped region, guess which one is cheaper?
> > My understanding was that when you were talking about performance, you
> > were talking about doing symbol offset lookup at runtime vs having a
> > generated struct with fixed offsets; not about mmap vs old api with copy
> > (this debate is settled since your patches are accepted).
> 
> Oh, I see. No, I didn't intend to claim that performance of looking up
> variable by name in BTF is a big performance concern. Settled then :)
> 
> >
> > But to your original reply: you do understand that if you have multiple
> > threads that write to this global data you have a bigger problem, right?
> 
> Not necessarily. BPF has atomic increment instruction, doesn't it? And
> can't we still do atomic swap from user-space (it's just a memory,
> after all), right? I haven't tried, tbh, but don't see why it wouldn't
> work.
Atomics are even worse because you get all these nice cache bouncing effects.
That's why I didn't understand initialy the argument about performance.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Potential Spoof] [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-10  1:14 ` [PATCH bpf-next 11/15] bpftool: add skeleton codegen command Andrii Nakryiko
  2019-12-10  1:57   ` Jakub Kicinski
@ 2019-12-11 22:50   ` Martin Lau
  2019-12-16 14:16   ` Daniel Borkmann
  2 siblings, 0 replies; 57+ messages in thread
From: Martin Lau @ 2019-12-11 22:50 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, netdev, Alexei Starovoitov, daniel, andrii.nakryiko, Kernel Team

On Mon, Dec 09, 2019 at 05:14:34PM -0800, Andrii Nakryiko wrote:
> Add `bpftool gen skeleton` command, which takes in compiled BPF .o object file
> and dumps a BPF skeleton struct and related code to work with that skeleton.
> Skeleton itself is tailored to a specific structure of provided BPF object
> file, containing accessors (just plain struct fields) for every map and
> program, as well as dedicated space for bpf_links. If BPF program is using
> global variables, corresponding structure definitions of compatible memory
> layout are emitted as well, making it possible to initialize and subsequently
> read/update global variables values using simple and clear C syntax for
> accessing fields. This skeleton majorly improves usability of
> opening/loading/attaching of BPF object, as well as interacting with it
> throughout the lifetime of loaded BPF object.
> 
> Generated skeleton struct has the following structure:
> 
> struct <object-name> {
> 	/* used by libbpf's skeleton API */
> 	struct bpf_object_skeleton *skeleton;
> 	/* bpf_object for libbpf APIs */
> 	struct bpf_object *obj;
> 	struct {
> 		/* for every defined map in BPF object: */
> 		struct bpf_map *<map-name>;
> 	} maps;
> 	struct {
> 		/* for every program in BPF object: */
> 		struct bpf_program *<program-name>;
> 	} progs;
> 	struct {
> 		/* for every program in BPF object: */
> 		struct bpf_link *<program-name>;
> 	} links;
> 	/* for every present global data section: */
> 	struct <object-name>__<one of bss, data, or rodata> {
> 		/* memory layout of corresponding data section,
> 		 * with every defined variable represented as a struct field
> 		 * with exactly the same type, but without const/volatile
> 		 * modifiers, e.g.:
> 		 */
> 		 int *my_var_1;
> 		 ...
> 	} *<one of bss, data, or rodata>;
> };
> 
> This provides great usability improvements:
> - no need to look up maps and programs by name, instead just
>   my_obj->maps.my_map or my_obj->progs.my_prog would give necessary
>   bpf_map/bpf_program pointers, which user can pass to existing libbpf APIs;
> - pre-defined places for bpf_links, which will be automatically populated for
>   program types that libbpf knows how to attach automatically (currently
>   tracepoints, kprobe/kretprobe, raw tracepoint and tracing programs). On
>   tearing down skeleton, all active bpf_links will be destroyed (meaning BPF
>   programs will be detached, if they are attached). For cases in which libbpf
>   doesn't know how to auto-attach BPF program, user can manually create link
>   after loading skeleton and they will be auto-detached on skeleton
>   destruction:
> 
> 	my_obj->links.my_fancy_prog = bpf_program__attach_cgroup_whatever(
> 		my_obj->progs.my_fancy_prog, <whatever extra param);
> 
> - it's extremely easy and convenient to work with global data from userspace
>   now. Both for read-only and read/write variables, it's possible to
>   pre-initialize them before skeleton is loaded:
> 
> 	skel = my_obj__open(raw_embed_data);
> 	my_obj->rodata->my_var = 123;
This will be very useful.  I can think of one immediate use on this
for my current TCP work.

> 	my_obj__load(skel); /* 123 will be initialization value for my_var */
> 

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support
  2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
                   ` (14 preceding siblings ...)
  2019-12-10  1:14 ` [PATCH bpf-next 15/15] bpftool: add `gen skeleton` BASH completions Andrii Nakryiko
@ 2019-12-11 22:55 ` Martin Lau
  15 siblings, 0 replies; 57+ messages in thread
From: Martin Lau @ 2019-12-11 22:55 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: bpf, netdev, Alexei Starovoitov, daniel, andrii.nakryiko, Kernel Team

On Mon, Dec 09, 2019 at 05:14:23PM -0800, Andrii Nakryiko wrote:
> This patch set introduces an alternative and complimentary to existing libbpf
> API interface for working with BPF objects, maps, programs, and global data
> from userspace side. This approach is relying on code generation. bpftool
> produces a struct (a.k.a. skeleton) tailored and specific to provided BPF
> object file. It includes hard-coded fields and data structures for every map,
> program, link, and global data present.
> 
> Altogether this approach significantly reduces amount of userspace boilerplate
> code required to open, load, attach, and work with BPF objects. It improves
> attach/detach story, by providing pre-allocated space for bpf_links, and
> ensuring they are properly detached on shutdown. It allows to do away with by
> name/title lookups of maps and programs, because libbpf's skeleton API, in
> conjunction with generated code from bpftool, is filling in hard-coded fields
> with actual pointers to corresponding struct bpf_map/bpf_program/bpf_link.
> 
> Also, thanks to BPF array mmap() support, working with global data (variables)
> from userspace is now as natural as it is from BPF side: each variable is just
> a struct field inside skeleton struct. Furthermore, this allows to have
> a natural way for userspace to pre-initialize global data (including
> previously impossible to initialize .rodata) by just assigning values to the
> same per-variable fields. Libbpf will carefully take into account this
> initialization image, will use it to pre-populate BPF maps at creation time,
> and will re-mmap() BPF map's contents at exactly the same userspace memory
> address such that it can continue working with all the same pointers without
> any interruptions. If kernel doesn't support mmap(), global data will still be
> successfully initialized, but after map creation global data structures inside
> skeleton will be NULL-ed out. This allows userspace application to gracefully
> handle lack of mmap() support, if necessary.
> 
> A bunch of selftests are also converted to using skeletons, demonstrating
> significant simplification of userspace part of test and reduction in amount
> of code necessary.
Changes look good to me.

Acked-by: Martin KaFai Lau <kafai@fb.com>

which should not stop the on-going discussion.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-11 20:09                         ` Stanislav Fomichev
@ 2019-12-12  0:50                           ` Andrii Nakryiko
  2019-12-12  2:57                             ` Stanislav Fomichev
  0 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-12  0:50 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Wed, Dec 11, 2019 at 12:09 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
>
> On 12/11, Andrii Nakryiko wrote:
> > On Wed, Dec 11, 2019 at 11:15 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > >
> > > On 12/11, Andrii Nakryiko wrote:
> > > > On Wed, Dec 11, 2019 at 9:24 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > >
> > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > On Tue, Dec 10, 2019 at 2:59 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > >
> > > > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > > > On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > > >
> > > > > > > > > On 12/10, Jakub Kicinski wrote:
> > > > > > > > > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > > > > > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > > > struct <object-name> {
> > > > > > > > > > > > >       /* used by libbpf's skeleton API */
> > > > > > > > > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > > > > > > > > >       /* bpf_object for libbpf APIs */
> > > > > > > > > > > > >       struct bpf_object *obj;
> > > > > > > > > > > > >       struct {
> > > > > > > > > > > > >               /* for every defined map in BPF object: */
> > > > > > > > > > > > >               struct bpf_map *<map-name>;
> > > > > > > > > > > > >       } maps;
> > > > > > > > > > > > >       struct {
> > > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > > >               struct bpf_program *<program-name>;
> > > > > > > > > > > > >       } progs;
> > > > > > > > > > > > >       struct {
> > > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > > >               struct bpf_link *<program-name>;
> > > > > > > > > > > > >       } links;
> > > > > > > > > > > > >       /* for every present global data section: */
> > > > > > > > > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > > > > > > > > >               /* memory layout of corresponding data section,
> > > > > > > > > > > > >                * with every defined variable represented as a struct field
> > > > > > > > > > > > >                * with exactly the same type, but without const/volatile
> > > > > > > > > > > > >                * modifiers, e.g.:
> > > > > > > > > > > > >                */
> > > > > > > > > > > > >                int *my_var_1;
> > > > > > > > > > > > >                ...
> > > > > > > > > > > > >       } *<one of bss, data, or rodata>;
> > > > > > > > > > > > > };
> > > > > > > > > > > >
> > > > > > > > > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > > > > > > > > is that we're using C for everything, and simple programs for which
> > > > > > > > > > > > loading the ELF is majority of the code would be better of being
> > > > > > > > > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > > > > > > > > better idea to work on some high-level language bindings than spend
> > > > > > > > > > > > time writing code gens and working around limitations of C?
> > > > > > > > > > >
> > > > > > > > > > > None of this work prevents Python bindings and other improvements, is
> > > > > > > > > > > it? Patches, as always, are greatly appreciated ;)
> > > > > > > > > >
> > > > > > > > > > This "do it yourself" shit is not really funny :/
> > > > > > > > > >
> > > > > > > > > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > > > > > > > > that :/ Maybe that's what you want.
> > > > > > > > > >
> > > > > > > > > > > This skeleton stuff is not just to save code, but in general to
> > > > > > > > > > > simplify and streamline working with BPF program from userspace side.
> > > > > > > > > > > Fortunately or not, but there are a lot of real-world applications
> > > > > > > > > > > written in C and C++ that could benefit from this, so this is still
> > > > > > > > > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > > > > > > > > work, see few of the last patches in this series.
> > > > > > > > > >
> > > > > > > > > > Maybe those applications are written in C and C++ _because_ there
> > > > > > > > > > are no bindings for high level languages. I just wish BPF programming
> > > > > > > > > > was less weird and adding some funky codegen is not getting us closer
> > > > > > > > > > to that goal.
> > > > > > > > > >
> > > > > > > > > > In my experience code gen is nothing more than a hack to work around
> > > > > > > > > > bad APIs, but experiences differ so that's not a solid argument.
> > > > > > > > > *nod*
> > > > > > > > >
> > > > > > > > > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > > > > > > > > something like BpfMap<key type, value type> and get a much better interface
> > > > > > > > > with type checking. Maybe we should focus on higher level languages instead?
> > > > > > > > > We are open to open-sourcing our C++ bits if you want to collaborate.
> > > > > > > >
> > > > > > > > Python/C++ bindings and API wrappers are an orthogonal concerns here.
> > > > > > > > I personally think it would be great to have both Python and C++
> > > > > > > > specific API that uses libbpf under the cover. The only debatable
> > > > > > > > thing is the logistics: where the source code lives, how it's kept in
> > > > > > > > sync with libbpf, how we avoid crippling libbpf itself because
> > > > > > > > something is hard or inconvenient to adapt w/ Python, etc.
> > > > > > >
> > > > > > > [..]
> > > > > > > > The problem I'm trying to solve here is not really C-specific. I don't
> > > > > > > > think you can solve it without code generation for C++. How do you
> > > > > > > > "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> > > > > > > > data sections in such a way, where it's type safe (to the degree that
> > > > > > > > language allows that, of course) and is not "stringly-based" API? This
> > > > > > > > skeleton stuff provides a natural, convenient and type-safe way to
> > > > > > > > work with global data from userspace pretty much at the same level of
> > > > > > > > performance and convenience, as from BPF side. How can you achieve
> > > > > > > > that w/ C++ without code generation? As for Python, sure you can do
> > > > > > > > dynamic lookups based on just the name of property/method, but amount
> > > > > > > > of overheads is not acceptable for all applications (and Python itself
> > > > > > > > is not acceptable for those applications). In addition to that, C is
> > > > > > > > the best way for other less popular languages (e.g., Rust) to leverage
> > > > > > > > libbpf without investing lots of effort in re-implementing libbpf in
> > > > > > > > Rust.
> > > > > > > I'd say that a libbpf API similar to dlopen/dlsym is a more
> > > > > > > straightforward thing to do. Have a way to "open" a section and
> > > > > > > a way to find a symbol in it. Yes, it's a string-based API,
> > > > > > > but there is nothing wrong with it. IMO, this is easier to
> > > > > > > use/understand and I suppose Python/C++ wrappers are trivial.
> > > > > >
> > > > > > Without digging through libbpf source code (or actually, look at code,
> > > > > > but don't run any test program), what's the name of the map
> > > > > > corresponding to .bss section, if object file is
> > > > > > some_bpf_object_file.o? If you got it right (congrats, btw, it took me
> > > > > > multiple attempts to memorize the pattern), how much time did you
> > > > > > spend looking it up? Now compare it to `skel->maps.bss`. Further, if
> > > > > > you use anonymous structs for your global vars, good luck maintaining
> > > > > > two copies of that: one for BPF side and one for userspace.
> > > > > As your average author of BPF programs I don't really care
> > > > > which section my symbol ends up into. Just give me an api
> > > > > to mmap all "global" sections (or a call per section which does all the
> > > > > naming magic inside) and lookup symbol by name; I can cast it to a proper
> > > > > type and set it.
> > > >
> > > > I'd like to not have to know about bss/rodata/data as well, but that's
> > > > how things are done for global variables. In skeleton we can try to
> > > > make an illusion like they are part of one big datasection/struct, but
> > > > that seems like a bit too much magic at this point. But then again,
> > > > one of the reasons I want this as an experimental feature, so that we
> > > > can actually judge from real experience how inconvenient some things
> > > > are, and not just based on "I think it would be ...".
> > > >
> > > > re: "Just give me ...". Following the spirit of "C is hard" from your
> > > > previous arguments, you already have that API: mmap() syscall. C
> > > > programmers have to be able to figure out the rest ;) But on the
> > > > serious note, this auto-generated code in skeleton actually addresses
> > > > all concerns (and more) that you mentioned: mmaping, knowing offsets,
> > > > knowing names and types, etc. And it doesn't preclude adding more
> > > > "conventional" additional APIs to do everything more dynamically,
> > > > based on string names.
> > > We have different understanding of what's difficult :-)
> >
> > Well, clearly... See below.
> >
> > >
> > > To me, doing transparent data/rodata/bss mmap in bpf_object__load and then
> > > adding a single libbpf api call to lookup symbol by string name is simple
> > > (both from user perspective and from libbpf code complexity). Because in
> > > order to use the codegen I need to teach our build system to spit it
> > > out (which means I need to add bpftool to it and keep it
> > > updated/etc/etc). You can use it as an example of "real experience how
> > > inconvenient some things are".
> >
> > Yes, you need to integrate bpftool in your build process. Which is
> > exactly what I'm doing internally for Facebook as well. But it's a
> > mostly one-time cost, which benefits lots of users who have much
> > better time with these changes, as opposed to make things simpler for
> > us, libbpf developers, at the expense of more convoluted user
> > experience for end users. I certainly prefer more complicated
> > libbpf/bpftool code, if the resulting user experience is simpler for
> > BPF application developers, no doubt about it.
> I'm in the process of going through this with pahole to get proper BTF.
> I don't think I'm willing yet (without a good reason) to go through
> this process again :-D (I saw that you've converted a bunch of tests
> to it which means I might not be able to run them).

A lot of new functionality is depending on BTF for a really good
reason (check Alexei's fentry/fexit/btf_tp stuff, allowing for safe
direct memory reads and extremely low overhead kretprobes). More stuff
is to come and is going to require in-kernel BTF, so even if it's
painful right now, it's worth it. Think long term and keep perspective
in mind.

>
> I just hope bpftool codegen doesn't become a requirement for
> any new useful feature; same happened to BTF, which was optional
> for a while and now I can't run a single selftest without it.
> I can totally understand the BTF requirement though, but I don't buy the
> "codegen makes user experience simple for bpf application developers",
> sorry (I guess, at this point, it's all about preference).

Bpftool is going to be a requirement for selftests. And it's a good
thing because it allows us to continuously test not just libbpf,
kernel, but now also related tooling. I haven't converted all of the
selftests to skeleton, but given enough time I'd do that, just for the
cleaner and shorter plumbing code it gives.

>
> > > > > RE anonymous structs: maybe don't use them if you want to share the data
> > > > > between bpf and userspace?
> > > >
> > > > Alright.
> > > >
> > > > >
> > > > > > I never said there is anything wrong with current straightforward
> > > > > > libbpf API, but I also never said it's the easiest and most
> > > > > > user-friendly way to work with BPF either. So we'll have both
> > > > > > code-generated interface and existing API. Furthermore, they are
> > > > > > interoperable (you can pass skel->maps.whatever to any of the existing
> > > > > > libbpf APIs, same for progs, links, obj itself). But there isn't much
> > > > > > that can beat performance and usability of code-generated .data, .bss,
> > > > > > .rodata (and now .extern) layout.
> > > > > I haven't looked closely enough, but is there a libbpf api to get
> > > > > an offset of a variable? Suppose I have the following in bpf.c:
> > > > >
> > > > >         int a;
> > > > >         int b;
> > > > >
> > > > > Can I get an offset of 'b' in the .bss without manually parsing BTF?
> > > >
> > > > No there isn't right now. There isn't even an API to know that there
> > > > is such a variable called "b". Except for this skeleton, of course.
> > > >
> > > > >
> > > > > TBH, I don't buy the performance argument for these global maps.
> > > > > When you did the mmap patchset for the array, you said it yourself
> > > > > that it's about convenience and not performance.
> > > >
> > > > Yes, it's first and foremost about convenience, addressing exactly the
> > > > problems you mentioned above. But performance is critical for some use
> > > > cases, and nothing can beat memory-mapped view of BPF map for those.
> > > > Think about the case of frequently polling (or even atomically
> > > > exchanging) some stats from userspace, as one possible example. E.g.,
> > > > like some map statistics (number of filled elements, p50 of whatever
> > > > of those elements, etc). I'm not sure what's there to buy: doing
> > > > syscall to get **entire** global data map contents vs just fetching
> > > > single integer from memory-mapped region, guess which one is cheaper?
> > > My understanding was that when you were talking about performance, you
> > > were talking about doing symbol offset lookup at runtime vs having a
> > > generated struct with fixed offsets; not about mmap vs old api with copy
> > > (this debate is settled since your patches are accepted).
> >
> > Oh, I see. No, I didn't intend to claim that performance of looking up
> > variable by name in BTF is a big performance concern. Settled then :)
> >
> > >
> > > But to your original reply: you do understand that if you have multiple
> > > threads that write to this global data you have a bigger problem, right?
> >
> > Not necessarily. BPF has atomic increment instruction, doesn't it? And
> > can't we still do atomic swap from user-space (it's just a memory,
> > after all), right? I haven't tried, tbh, but don't see why it wouldn't
> > work.
> Atomics are even worse because you get all these nice cache bouncing effects.
> That's why I didn't understand initialy the argument about performance.

Depends on problems you are trying to solve. I bet it's still cheaper
than doing map updates under lock, don't you think?

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12  0:50                           ` Andrii Nakryiko
@ 2019-12-12  2:57                             ` Stanislav Fomichev
  2019-12-12  7:27                               ` Andrii Nakryiko
  0 siblings, 1 reply; 57+ messages in thread
From: Stanislav Fomichev @ 2019-12-12  2:57 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On 12/11, Andrii Nakryiko wrote:
> On Wed, Dec 11, 2019 at 12:09 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> >
> > On 12/11, Andrii Nakryiko wrote:
> > > On Wed, Dec 11, 2019 at 11:15 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > >
> > > > On 12/11, Andrii Nakryiko wrote:
> > > > > On Wed, Dec 11, 2019 at 9:24 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > >
> > > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > > On Tue, Dec 10, 2019 at 2:59 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > >
> > > > > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > > > > On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > > > >
> > > > > > > > > > On 12/10, Jakub Kicinski wrote:
> > > > > > > > > > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > > > > > > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > > > > struct <object-name> {
> > > > > > > > > > > > > >       /* used by libbpf's skeleton API */
> > > > > > > > > > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > > > > > > > > > >       /* bpf_object for libbpf APIs */
> > > > > > > > > > > > > >       struct bpf_object *obj;
> > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > >               /* for every defined map in BPF object: */
> > > > > > > > > > > > > >               struct bpf_map *<map-name>;
> > > > > > > > > > > > > >       } maps;
> > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > > > >               struct bpf_program *<program-name>;
> > > > > > > > > > > > > >       } progs;
> > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > > > >               struct bpf_link *<program-name>;
> > > > > > > > > > > > > >       } links;
> > > > > > > > > > > > > >       /* for every present global data section: */
> > > > > > > > > > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > > > > > > > > > >               /* memory layout of corresponding data section,
> > > > > > > > > > > > > >                * with every defined variable represented as a struct field
> > > > > > > > > > > > > >                * with exactly the same type, but without const/volatile
> > > > > > > > > > > > > >                * modifiers, e.g.:
> > > > > > > > > > > > > >                */
> > > > > > > > > > > > > >                int *my_var_1;
> > > > > > > > > > > > > >                ...
> > > > > > > > > > > > > >       } *<one of bss, data, or rodata>;
> > > > > > > > > > > > > > };
> > > > > > > > > > > > >
> > > > > > > > > > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > > > > > > > > > is that we're using C for everything, and simple programs for which
> > > > > > > > > > > > > loading the ELF is majority of the code would be better of being
> > > > > > > > > > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > > > > > > > > > better idea to work on some high-level language bindings than spend
> > > > > > > > > > > > > time writing code gens and working around limitations of C?
> > > > > > > > > > > >
> > > > > > > > > > > > None of this work prevents Python bindings and other improvements, is
> > > > > > > > > > > > it? Patches, as always, are greatly appreciated ;)
> > > > > > > > > > >
> > > > > > > > > > > This "do it yourself" shit is not really funny :/
> > > > > > > > > > >
> > > > > > > > > > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > > > > > > > > > that :/ Maybe that's what you want.
> > > > > > > > > > >
> > > > > > > > > > > > This skeleton stuff is not just to save code, but in general to
> > > > > > > > > > > > simplify and streamline working with BPF program from userspace side.
> > > > > > > > > > > > Fortunately or not, but there are a lot of real-world applications
> > > > > > > > > > > > written in C and C++ that could benefit from this, so this is still
> > > > > > > > > > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > > > > > > > > > work, see few of the last patches in this series.
> > > > > > > > > > >
> > > > > > > > > > > Maybe those applications are written in C and C++ _because_ there
> > > > > > > > > > > are no bindings for high level languages. I just wish BPF programming
> > > > > > > > > > > was less weird and adding some funky codegen is not getting us closer
> > > > > > > > > > > to that goal.
> > > > > > > > > > >
> > > > > > > > > > > In my experience code gen is nothing more than a hack to work around
> > > > > > > > > > > bad APIs, but experiences differ so that's not a solid argument.
> > > > > > > > > > *nod*
> > > > > > > > > >
> > > > > > > > > > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > > > > > > > > > something like BpfMap<key type, value type> and get a much better interface
> > > > > > > > > > with type checking. Maybe we should focus on higher level languages instead?
> > > > > > > > > > We are open to open-sourcing our C++ bits if you want to collaborate.
> > > > > > > > >
> > > > > > > > > Python/C++ bindings and API wrappers are an orthogonal concerns here.
> > > > > > > > > I personally think it would be great to have both Python and C++
> > > > > > > > > specific API that uses libbpf under the cover. The only debatable
> > > > > > > > > thing is the logistics: where the source code lives, how it's kept in
> > > > > > > > > sync with libbpf, how we avoid crippling libbpf itself because
> > > > > > > > > something is hard or inconvenient to adapt w/ Python, etc.
> > > > > > > >
> > > > > > > > [..]
> > > > > > > > > The problem I'm trying to solve here is not really C-specific. I don't
> > > > > > > > > think you can solve it without code generation for C++. How do you
> > > > > > > > > "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> > > > > > > > > data sections in such a way, where it's type safe (to the degree that
> > > > > > > > > language allows that, of course) and is not "stringly-based" API? This
> > > > > > > > > skeleton stuff provides a natural, convenient and type-safe way to
> > > > > > > > > work with global data from userspace pretty much at the same level of
> > > > > > > > > performance and convenience, as from BPF side. How can you achieve
> > > > > > > > > that w/ C++ without code generation? As for Python, sure you can do
> > > > > > > > > dynamic lookups based on just the name of property/method, but amount
> > > > > > > > > of overheads is not acceptable for all applications (and Python itself
> > > > > > > > > is not acceptable for those applications). In addition to that, C is
> > > > > > > > > the best way for other less popular languages (e.g., Rust) to leverage
> > > > > > > > > libbpf without investing lots of effort in re-implementing libbpf in
> > > > > > > > > Rust.
> > > > > > > > I'd say that a libbpf API similar to dlopen/dlsym is a more
> > > > > > > > straightforward thing to do. Have a way to "open" a section and
> > > > > > > > a way to find a symbol in it. Yes, it's a string-based API,
> > > > > > > > but there is nothing wrong with it. IMO, this is easier to
> > > > > > > > use/understand and I suppose Python/C++ wrappers are trivial.
> > > > > > >
> > > > > > > Without digging through libbpf source code (or actually, look at code,
> > > > > > > but don't run any test program), what's the name of the map
> > > > > > > corresponding to .bss section, if object file is
> > > > > > > some_bpf_object_file.o? If you got it right (congrats, btw, it took me
> > > > > > > multiple attempts to memorize the pattern), how much time did you
> > > > > > > spend looking it up? Now compare it to `skel->maps.bss`. Further, if
> > > > > > > you use anonymous structs for your global vars, good luck maintaining
> > > > > > > two copies of that: one for BPF side and one for userspace.
> > > > > > As your average author of BPF programs I don't really care
> > > > > > which section my symbol ends up into. Just give me an api
> > > > > > to mmap all "global" sections (or a call per section which does all the
> > > > > > naming magic inside) and lookup symbol by name; I can cast it to a proper
> > > > > > type and set it.
> > > > >
> > > > > I'd like to not have to know about bss/rodata/data as well, but that's
> > > > > how things are done for global variables. In skeleton we can try to
> > > > > make an illusion like they are part of one big datasection/struct, but
> > > > > that seems like a bit too much magic at this point. But then again,
> > > > > one of the reasons I want this as an experimental feature, so that we
> > > > > can actually judge from real experience how inconvenient some things
> > > > > are, and not just based on "I think it would be ...".
> > > > >
> > > > > re: "Just give me ...". Following the spirit of "C is hard" from your
> > > > > previous arguments, you already have that API: mmap() syscall. C
> > > > > programmers have to be able to figure out the rest ;) But on the
> > > > > serious note, this auto-generated code in skeleton actually addresses
> > > > > all concerns (and more) that you mentioned: mmaping, knowing offsets,
> > > > > knowing names and types, etc. And it doesn't preclude adding more
> > > > > "conventional" additional APIs to do everything more dynamically,
> > > > > based on string names.
> > > > We have different understanding of what's difficult :-)
> > >
> > > Well, clearly... See below.
> > >
> > > >
> > > > To me, doing transparent data/rodata/bss mmap in bpf_object__load and then
> > > > adding a single libbpf api call to lookup symbol by string name is simple
> > > > (both from user perspective and from libbpf code complexity). Because in
> > > > order to use the codegen I need to teach our build system to spit it
> > > > out (which means I need to add bpftool to it and keep it
> > > > updated/etc/etc). You can use it as an example of "real experience how
> > > > inconvenient some things are".
> > >
> > > Yes, you need to integrate bpftool in your build process. Which is
> > > exactly what I'm doing internally for Facebook as well. But it's a
> > > mostly one-time cost, which benefits lots of users who have much
> > > better time with these changes, as opposed to make things simpler for
> > > us, libbpf developers, at the expense of more convoluted user
> > > experience for end users. I certainly prefer more complicated
> > > libbpf/bpftool code, if the resulting user experience is simpler for
> > > BPF application developers, no doubt about it.
> > I'm in the process of going through this with pahole to get proper BTF.
> > I don't think I'm willing yet (without a good reason) to go through
> > this process again :-D (I saw that you've converted a bunch of tests
> > to it which means I might not be able to run them).
> 
> A lot of new functionality is depending on BTF for a really good
> reason (check Alexei's fentry/fexit/btf_tp stuff, allowing for safe
> direct memory reads and extremely low overhead kretprobes). More stuff
> is to come and is going to require in-kernel BTF, so even if it's
> painful right now, it's worth it. Think long term and keep perspective
> in mind.
Oh yeah, that's totally understandable with BTF, that's why I just started
adding it to our build. Still was a bit surprising that one day most
our our testing went red.

> > I just hope bpftool codegen doesn't become a requirement for
> > any new useful feature; same happened to BTF, which was optional
> > for a while and now I can't run a single selftest without it.
> > I can totally understand the BTF requirement though, but I don't buy the
> > "codegen makes user experience simple for bpf application developers",
> > sorry (I guess, at this point, it's all about preference).
> 
> Bpftool is going to be a requirement for selftests. And it's a good
> thing because it allows us to continuously test not just libbpf,
> kernel, but now also related tooling. I haven't converted all of the
> selftests to skeleton, but given enough time I'd do that, just for the
> cleaner and shorter plumbing code it gives.
Then why all the talk about --experimantal flags if you've set up your
mind on converting everything already?

Btw, how hard it would be to do this generation with a new python
script instead of bpftool? Something along the lines of
scripts/bpf_helpers_doc.py that parses BTF and spits out this C header
(shouldn't be that hard to write custom BTF parser in python, right)?

Python might be easier to integrate with other projects (our build system,
cilium, etc).

> > > > > > RE anonymous structs: maybe don't use them if you want to share the data
> > > > > > between bpf and userspace?
> > > > >
> > > > > Alright.
> > > > >
> > > > > >
> > > > > > > I never said there is anything wrong with current straightforward
> > > > > > > libbpf API, but I also never said it's the easiest and most
> > > > > > > user-friendly way to work with BPF either. So we'll have both
> > > > > > > code-generated interface and existing API. Furthermore, they are
> > > > > > > interoperable (you can pass skel->maps.whatever to any of the existing
> > > > > > > libbpf APIs, same for progs, links, obj itself). But there isn't much
> > > > > > > that can beat performance and usability of code-generated .data, .bss,
> > > > > > > .rodata (and now .extern) layout.
> > > > > > I haven't looked closely enough, but is there a libbpf api to get
> > > > > > an offset of a variable? Suppose I have the following in bpf.c:
> > > > > >
> > > > > >         int a;
> > > > > >         int b;
> > > > > >
> > > > > > Can I get an offset of 'b' in the .bss without manually parsing BTF?
> > > > >
> > > > > No there isn't right now. There isn't even an API to know that there
> > > > > is such a variable called "b". Except for this skeleton, of course.
> > > > >
> > > > > >
> > > > > > TBH, I don't buy the performance argument for these global maps.
> > > > > > When you did the mmap patchset for the array, you said it yourself
> > > > > > that it's about convenience and not performance.
> > > > >
> > > > > Yes, it's first and foremost about convenience, addressing exactly the
> > > > > problems you mentioned above. But performance is critical for some use
> > > > > cases, and nothing can beat memory-mapped view of BPF map for those.
> > > > > Think about the case of frequently polling (or even atomically
> > > > > exchanging) some stats from userspace, as one possible example. E.g.,
> > > > > like some map statistics (number of filled elements, p50 of whatever
> > > > > of those elements, etc). I'm not sure what's there to buy: doing
> > > > > syscall to get **entire** global data map contents vs just fetching
> > > > > single integer from memory-mapped region, guess which one is cheaper?
> > > > My understanding was that when you were talking about performance, you
> > > > were talking about doing symbol offset lookup at runtime vs having a
> > > > generated struct with fixed offsets; not about mmap vs old api with copy
> > > > (this debate is settled since your patches are accepted).
> > >
> > > Oh, I see. No, I didn't intend to claim that performance of looking up
> > > variable by name in BTF is a big performance concern. Settled then :)
> > >
> > > >
> > > > But to your original reply: you do understand that if you have multiple
> > > > threads that write to this global data you have a bigger problem, right?
> > >
> > > Not necessarily. BPF has atomic increment instruction, doesn't it? And
> > > can't we still do atomic swap from user-space (it's just a memory,
> > > after all), right? I haven't tried, tbh, but don't see why it wouldn't
> > > work.
> > Atomics are even worse because you get all these nice cache bouncing effects.
> > That's why I didn't understand initialy the argument about performance.
> 
> Depends on problems you are trying to solve. I bet it's still cheaper
> than doing map updates under lock, don't you think?
Exactly, depends on the problem. But still, if you need frequent reads
of that global data, it means there are frequent writes, which is
a problem on its own.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12  2:57                             ` Stanislav Fomichev
@ 2019-12-12  7:27                               ` Andrii Nakryiko
  2019-12-12 16:29                                 ` Stanislav Fomichev
  0 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-12  7:27 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Wed, Dec 11, 2019 at 6:57 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
>
> On 12/11, Andrii Nakryiko wrote:
> > On Wed, Dec 11, 2019 at 12:09 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > >
> > > On 12/11, Andrii Nakryiko wrote:
> > > > On Wed, Dec 11, 2019 at 11:15 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > >
> > > > > On 12/11, Andrii Nakryiko wrote:
> > > > > > On Wed, Dec 11, 2019 at 9:24 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > >
> > > > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > > > On Tue, Dec 10, 2019 at 2:59 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > > >
> > > > > > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > > > > > On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > > > > >
> > > > > > > > > > > On 12/10, Jakub Kicinski wrote:
> > > > > > > > > > > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > > > > > > > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > > > > > struct <object-name> {
> > > > > > > > > > > > > > >       /* used by libbpf's skeleton API */
> > > > > > > > > > > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > > > > > > > > > > >       /* bpf_object for libbpf APIs */
> > > > > > > > > > > > > > >       struct bpf_object *obj;
> > > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > > >               /* for every defined map in BPF object: */
> > > > > > > > > > > > > > >               struct bpf_map *<map-name>;
> > > > > > > > > > > > > > >       } maps;
> > > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > > > > >               struct bpf_program *<program-name>;
> > > > > > > > > > > > > > >       } progs;
> > > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > > > > >               struct bpf_link *<program-name>;
> > > > > > > > > > > > > > >       } links;
> > > > > > > > > > > > > > >       /* for every present global data section: */
> > > > > > > > > > > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > > > > > > > > > > >               /* memory layout of corresponding data section,
> > > > > > > > > > > > > > >                * with every defined variable represented as a struct field
> > > > > > > > > > > > > > >                * with exactly the same type, but without const/volatile
> > > > > > > > > > > > > > >                * modifiers, e.g.:
> > > > > > > > > > > > > > >                */
> > > > > > > > > > > > > > >                int *my_var_1;
> > > > > > > > > > > > > > >                ...
> > > > > > > > > > > > > > >       } *<one of bss, data, or rodata>;
> > > > > > > > > > > > > > > };
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > > > > > > > > > > is that we're using C for everything, and simple programs for which
> > > > > > > > > > > > > > loading the ELF is majority of the code would be better of being
> > > > > > > > > > > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > > > > > > > > > > better idea to work on some high-level language bindings than spend
> > > > > > > > > > > > > > time writing code gens and working around limitations of C?
> > > > > > > > > > > > >
> > > > > > > > > > > > > None of this work prevents Python bindings and other improvements, is
> > > > > > > > > > > > > it? Patches, as always, are greatly appreciated ;)
> > > > > > > > > > > >
> > > > > > > > > > > > This "do it yourself" shit is not really funny :/
> > > > > > > > > > > >
> > > > > > > > > > > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > > > > > > > > > > that :/ Maybe that's what you want.
> > > > > > > > > > > >
> > > > > > > > > > > > > This skeleton stuff is not just to save code, but in general to
> > > > > > > > > > > > > simplify and streamline working with BPF program from userspace side.
> > > > > > > > > > > > > Fortunately or not, but there are a lot of real-world applications
> > > > > > > > > > > > > written in C and C++ that could benefit from this, so this is still
> > > > > > > > > > > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > > > > > > > > > > work, see few of the last patches in this series.
> > > > > > > > > > > >
> > > > > > > > > > > > Maybe those applications are written in C and C++ _because_ there
> > > > > > > > > > > > are no bindings for high level languages. I just wish BPF programming
> > > > > > > > > > > > was less weird and adding some funky codegen is not getting us closer
> > > > > > > > > > > > to that goal.
> > > > > > > > > > > >
> > > > > > > > > > > > In my experience code gen is nothing more than a hack to work around
> > > > > > > > > > > > bad APIs, but experiences differ so that's not a solid argument.
> > > > > > > > > > > *nod*
> > > > > > > > > > >
> > > > > > > > > > > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > > > > > > > > > > something like BpfMap<key type, value type> and get a much better interface
> > > > > > > > > > > with type checking. Maybe we should focus on higher level languages instead?
> > > > > > > > > > > We are open to open-sourcing our C++ bits if you want to collaborate.
> > > > > > > > > >
> > > > > > > > > > Python/C++ bindings and API wrappers are an orthogonal concerns here.
> > > > > > > > > > I personally think it would be great to have both Python and C++
> > > > > > > > > > specific API that uses libbpf under the cover. The only debatable
> > > > > > > > > > thing is the logistics: where the source code lives, how it's kept in
> > > > > > > > > > sync with libbpf, how we avoid crippling libbpf itself because
> > > > > > > > > > something is hard or inconvenient to adapt w/ Python, etc.
> > > > > > > > >
> > > > > > > > > [..]
> > > > > > > > > > The problem I'm trying to solve here is not really C-specific. I don't
> > > > > > > > > > think you can solve it without code generation for C++. How do you
> > > > > > > > > > "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> > > > > > > > > > data sections in such a way, where it's type safe (to the degree that
> > > > > > > > > > language allows that, of course) and is not "stringly-based" API? This
> > > > > > > > > > skeleton stuff provides a natural, convenient and type-safe way to
> > > > > > > > > > work with global data from userspace pretty much at the same level of
> > > > > > > > > > performance and convenience, as from BPF side. How can you achieve
> > > > > > > > > > that w/ C++ without code generation? As for Python, sure you can do
> > > > > > > > > > dynamic lookups based on just the name of property/method, but amount
> > > > > > > > > > of overheads is not acceptable for all applications (and Python itself
> > > > > > > > > > is not acceptable for those applications). In addition to that, C is
> > > > > > > > > > the best way for other less popular languages (e.g., Rust) to leverage
> > > > > > > > > > libbpf without investing lots of effort in re-implementing libbpf in
> > > > > > > > > > Rust.
> > > > > > > > > I'd say that a libbpf API similar to dlopen/dlsym is a more
> > > > > > > > > straightforward thing to do. Have a way to "open" a section and
> > > > > > > > > a way to find a symbol in it. Yes, it's a string-based API,
> > > > > > > > > but there is nothing wrong with it. IMO, this is easier to
> > > > > > > > > use/understand and I suppose Python/C++ wrappers are trivial.
> > > > > > > >
> > > > > > > > Without digging through libbpf source code (or actually, look at code,
> > > > > > > > but don't run any test program), what's the name of the map
> > > > > > > > corresponding to .bss section, if object file is
> > > > > > > > some_bpf_object_file.o? If you got it right (congrats, btw, it took me
> > > > > > > > multiple attempts to memorize the pattern), how much time did you
> > > > > > > > spend looking it up? Now compare it to `skel->maps.bss`. Further, if
> > > > > > > > you use anonymous structs for your global vars, good luck maintaining
> > > > > > > > two copies of that: one for BPF side and one for userspace.
> > > > > > > As your average author of BPF programs I don't really care
> > > > > > > which section my symbol ends up into. Just give me an api
> > > > > > > to mmap all "global" sections (or a call per section which does all the
> > > > > > > naming magic inside) and lookup symbol by name; I can cast it to a proper
> > > > > > > type and set it.
> > > > > >
> > > > > > I'd like to not have to know about bss/rodata/data as well, but that's
> > > > > > how things are done for global variables. In skeleton we can try to
> > > > > > make an illusion like they are part of one big datasection/struct, but
> > > > > > that seems like a bit too much magic at this point. But then again,
> > > > > > one of the reasons I want this as an experimental feature, so that we
> > > > > > can actually judge from real experience how inconvenient some things
> > > > > > are, and not just based on "I think it would be ...".
> > > > > >
> > > > > > re: "Just give me ...". Following the spirit of "C is hard" from your
> > > > > > previous arguments, you already have that API: mmap() syscall. C
> > > > > > programmers have to be able to figure out the rest ;) But on the
> > > > > > serious note, this auto-generated code in skeleton actually addresses
> > > > > > all concerns (and more) that you mentioned: mmaping, knowing offsets,
> > > > > > knowing names and types, etc. And it doesn't preclude adding more
> > > > > > "conventional" additional APIs to do everything more dynamically,
> > > > > > based on string names.
> > > > > We have different understanding of what's difficult :-)
> > > >
> > > > Well, clearly... See below.
> > > >
> > > > >
> > > > > To me, doing transparent data/rodata/bss mmap in bpf_object__load and then
> > > > > adding a single libbpf api call to lookup symbol by string name is simple
> > > > > (both from user perspective and from libbpf code complexity). Because in
> > > > > order to use the codegen I need to teach our build system to spit it
> > > > > out (which means I need to add bpftool to it and keep it
> > > > > updated/etc/etc). You can use it as an example of "real experience how
> > > > > inconvenient some things are".
> > > >
> > > > Yes, you need to integrate bpftool in your build process. Which is
> > > > exactly what I'm doing internally for Facebook as well. But it's a
> > > > mostly one-time cost, which benefits lots of users who have much
> > > > better time with these changes, as opposed to make things simpler for
> > > > us, libbpf developers, at the expense of more convoluted user
> > > > experience for end users. I certainly prefer more complicated
> > > > libbpf/bpftool code, if the resulting user experience is simpler for
> > > > BPF application developers, no doubt about it.
> > > I'm in the process of going through this with pahole to get proper BTF.
> > > I don't think I'm willing yet (without a good reason) to go through
> > > this process again :-D (I saw that you've converted a bunch of tests
> > > to it which means I might not be able to run them).
> >
> > A lot of new functionality is depending on BTF for a really good
> > reason (check Alexei's fentry/fexit/btf_tp stuff, allowing for safe
> > direct memory reads and extremely low overhead kretprobes). More stuff
> > is to come and is going to require in-kernel BTF, so even if it's
> > painful right now, it's worth it. Think long term and keep perspective
> > in mind.
> Oh yeah, that's totally understandable with BTF, that's why I just started
> adding it to our build. Still was a bit surprising that one day most
> our our testing went red.
>
> > > I just hope bpftool codegen doesn't become a requirement for
> > > any new useful feature; same happened to BTF, which was optional
> > > for a while and now I can't run a single selftest without it.
> > > I can totally understand the BTF requirement though, but I don't buy the
> > > "codegen makes user experience simple for bpf application developers",
> > > sorry (I guess, at this point, it's all about preference).
> >
> > Bpftool is going to be a requirement for selftests. And it's a good
> > thing because it allows us to continuously test not just libbpf,
> > kernel, but now also related tooling. I haven't converted all of the
> > selftests to skeleton, but given enough time I'd do that, just for the
> > cleaner and shorter plumbing code it gives.
> Then why all the talk about --experimantal flags if you've set up your
> mind on converting everything already?

Where's contradiction? I'm not converting everything right now, same
as I haven't converted everything into test_progs, right? But I do
think that we should work towards that. But all that is still besides
the point of experimental, because we are talking about selftests, we
can atomically fix them up with whatever changes we do to those
experimental APIs.

The only reason for this experimental disclaimer is for user code
outside of kernel source tree that is going to try and use skeleton.
If we need to change something up a little bit, I'd like to still have
a bit of a wiggle room to adjust things, even if that causes a small
and easily fixable source code breakage (even though I don't see it
happening yet, it might be necessary).

>
> Btw, how hard it would be to do this generation with a new python
> script instead of bpftool? Something along the lines of
> scripts/bpf_helpers_doc.py that parses BTF and spits out this C header
> (shouldn't be that hard to write custom BTF parser in python, right)?
>

Not impossible, but harder than I'd care to deal with. I certainly
don't want to re-implement a good chunk of ELF and BTF parsing (maps,
progs, in addition to datasec stuff). But "it's hard to use bpftool in
our build system" doesn't seem like good enough reason to do all that.

> Python might be easier to integrate with other projects (our build system,
> cilium, etc).
>
> > > > > > > RE anonymous structs: maybe don't use them if you want to share the data
> > > > > > > between bpf and userspace?
> > > > > >
> > > > > > Alright.
> > > > > >
> > > > > > >
> > > > > > > > I never said there is anything wrong with current straightforward
> > > > > > > > libbpf API, but I also never said it's the easiest and most
> > > > > > > > user-friendly way to work with BPF either. So we'll have both
> > > > > > > > code-generated interface and existing API. Furthermore, they are
> > > > > > > > interoperable (you can pass skel->maps.whatever to any of the existing
> > > > > > > > libbpf APIs, same for progs, links, obj itself). But there isn't much
> > > > > > > > that can beat performance and usability of code-generated .data, .bss,
> > > > > > > > .rodata (and now .extern) layout.
> > > > > > > I haven't looked closely enough, but is there a libbpf api to get
> > > > > > > an offset of a variable? Suppose I have the following in bpf.c:
> > > > > > >
> > > > > > >         int a;
> > > > > > >         int b;
> > > > > > >
> > > > > > > Can I get an offset of 'b' in the .bss without manually parsing BTF?
> > > > > >
> > > > > > No there isn't right now. There isn't even an API to know that there
> > > > > > is such a variable called "b". Except for this skeleton, of course.
> > > > > >
> > > > > > >
> > > > > > > TBH, I don't buy the performance argument for these global maps.
> > > > > > > When you did the mmap patchset for the array, you said it yourself
> > > > > > > that it's about convenience and not performance.
> > > > > >
> > > > > > Yes, it's first and foremost about convenience, addressing exactly the
> > > > > > problems you mentioned above. But performance is critical for some use
> > > > > > cases, and nothing can beat memory-mapped view of BPF map for those.
> > > > > > Think about the case of frequently polling (or even atomically
> > > > > > exchanging) some stats from userspace, as one possible example. E.g.,
> > > > > > like some map statistics (number of filled elements, p50 of whatever
> > > > > > of those elements, etc). I'm not sure what's there to buy: doing
> > > > > > syscall to get **entire** global data map contents vs just fetching
> > > > > > single integer from memory-mapped region, guess which one is cheaper?
> > > > > My understanding was that when you were talking about performance, you
> > > > > were talking about doing symbol offset lookup at runtime vs having a
> > > > > generated struct with fixed offsets; not about mmap vs old api with copy
> > > > > (this debate is settled since your patches are accepted).
> > > >
> > > > Oh, I see. No, I didn't intend to claim that performance of looking up
> > > > variable by name in BTF is a big performance concern. Settled then :)
> > > >
> > > > >
> > > > > But to your original reply: you do understand that if you have multiple
> > > > > threads that write to this global data you have a bigger problem, right?
> > > >
> > > > Not necessarily. BPF has atomic increment instruction, doesn't it? And
> > > > can't we still do atomic swap from user-space (it's just a memory,
> > > > after all), right? I haven't tried, tbh, but don't see why it wouldn't
> > > > work.
> > > Atomics are even worse because you get all these nice cache bouncing effects.
> > > That's why I didn't understand initialy the argument about performance.
> >
> > Depends on problems you are trying to solve. I bet it's still cheaper
> > than doing map updates under lock, don't you think?
> Exactly, depends on the problem. But still, if you need frequent reads
> of that global data, it means there are frequent writes, which is
> a problem on its own.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12  7:27                               ` Andrii Nakryiko
@ 2019-12-12 16:29                                 ` Stanislav Fomichev
  2019-12-12 16:53                                   ` Andrii Nakryiko
  0 siblings, 1 reply; 57+ messages in thread
From: Stanislav Fomichev @ 2019-12-12 16:29 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On 12/11, Andrii Nakryiko wrote:
> On Wed, Dec 11, 2019 at 6:57 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> >
> > On 12/11, Andrii Nakryiko wrote:
> > > On Wed, Dec 11, 2019 at 12:09 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > >
> > > > On 12/11, Andrii Nakryiko wrote:
> > > > > On Wed, Dec 11, 2019 at 11:15 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > >
> > > > > > On 12/11, Andrii Nakryiko wrote:
> > > > > > > On Wed, Dec 11, 2019 at 9:24 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > >
> > > > > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > > > > On Tue, Dec 10, 2019 at 2:59 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > > > >
> > > > > > > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > > > > > > On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > On 12/10, Jakub Kicinski wrote:
> > > > > > > > > > > > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > > > > > > > > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > > > > > > struct <object-name> {
> > > > > > > > > > > > > > > >       /* used by libbpf's skeleton API */
> > > > > > > > > > > > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > > > > > > > > > > > >       /* bpf_object for libbpf APIs */
> > > > > > > > > > > > > > > >       struct bpf_object *obj;
> > > > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > > > >               /* for every defined map in BPF object: */
> > > > > > > > > > > > > > > >               struct bpf_map *<map-name>;
> > > > > > > > > > > > > > > >       } maps;
> > > > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > > > > > >               struct bpf_program *<program-name>;
> > > > > > > > > > > > > > > >       } progs;
> > > > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > > > > > >               struct bpf_link *<program-name>;
> > > > > > > > > > > > > > > >       } links;
> > > > > > > > > > > > > > > >       /* for every present global data section: */
> > > > > > > > > > > > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > > > > > > > > > > > >               /* memory layout of corresponding data section,
> > > > > > > > > > > > > > > >                * with every defined variable represented as a struct field
> > > > > > > > > > > > > > > >                * with exactly the same type, but without const/volatile
> > > > > > > > > > > > > > > >                * modifiers, e.g.:
> > > > > > > > > > > > > > > >                */
> > > > > > > > > > > > > > > >                int *my_var_1;
> > > > > > > > > > > > > > > >                ...
> > > > > > > > > > > > > > > >       } *<one of bss, data, or rodata>;
> > > > > > > > > > > > > > > > };
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > > > > > > > > > > > is that we're using C for everything, and simple programs for which
> > > > > > > > > > > > > > > loading the ELF is majority of the code would be better of being
> > > > > > > > > > > > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > > > > > > > > > > > better idea to work on some high-level language bindings than spend
> > > > > > > > > > > > > > > time writing code gens and working around limitations of C?
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > None of this work prevents Python bindings and other improvements, is
> > > > > > > > > > > > > > it? Patches, as always, are greatly appreciated ;)
> > > > > > > > > > > > >
> > > > > > > > > > > > > This "do it yourself" shit is not really funny :/
> > > > > > > > > > > > >
> > > > > > > > > > > > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > > > > > > > > > > > that :/ Maybe that's what you want.
> > > > > > > > > > > > >
> > > > > > > > > > > > > > This skeleton stuff is not just to save code, but in general to
> > > > > > > > > > > > > > simplify and streamline working with BPF program from userspace side.
> > > > > > > > > > > > > > Fortunately or not, but there are a lot of real-world applications
> > > > > > > > > > > > > > written in C and C++ that could benefit from this, so this is still
> > > > > > > > > > > > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > > > > > > > > > > > work, see few of the last patches in this series.
> > > > > > > > > > > > >
> > > > > > > > > > > > > Maybe those applications are written in C and C++ _because_ there
> > > > > > > > > > > > > are no bindings for high level languages. I just wish BPF programming
> > > > > > > > > > > > > was less weird and adding some funky codegen is not getting us closer
> > > > > > > > > > > > > to that goal.
> > > > > > > > > > > > >
> > > > > > > > > > > > > In my experience code gen is nothing more than a hack to work around
> > > > > > > > > > > > > bad APIs, but experiences differ so that's not a solid argument.
> > > > > > > > > > > > *nod*
> > > > > > > > > > > >
> > > > > > > > > > > > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > > > > > > > > > > > something like BpfMap<key type, value type> and get a much better interface
> > > > > > > > > > > > with type checking. Maybe we should focus on higher level languages instead?
> > > > > > > > > > > > We are open to open-sourcing our C++ bits if you want to collaborate.
> > > > > > > > > > >
> > > > > > > > > > > Python/C++ bindings and API wrappers are an orthogonal concerns here.
> > > > > > > > > > > I personally think it would be great to have both Python and C++
> > > > > > > > > > > specific API that uses libbpf under the cover. The only debatable
> > > > > > > > > > > thing is the logistics: where the source code lives, how it's kept in
> > > > > > > > > > > sync with libbpf, how we avoid crippling libbpf itself because
> > > > > > > > > > > something is hard or inconvenient to adapt w/ Python, etc.
> > > > > > > > > >
> > > > > > > > > > [..]
> > > > > > > > > > > The problem I'm trying to solve here is not really C-specific. I don't
> > > > > > > > > > > think you can solve it without code generation for C++. How do you
> > > > > > > > > > > "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> > > > > > > > > > > data sections in such a way, where it's type safe (to the degree that
> > > > > > > > > > > language allows that, of course) and is not "stringly-based" API? This
> > > > > > > > > > > skeleton stuff provides a natural, convenient and type-safe way to
> > > > > > > > > > > work with global data from userspace pretty much at the same level of
> > > > > > > > > > > performance and convenience, as from BPF side. How can you achieve
> > > > > > > > > > > that w/ C++ without code generation? As for Python, sure you can do
> > > > > > > > > > > dynamic lookups based on just the name of property/method, but amount
> > > > > > > > > > > of overheads is not acceptable for all applications (and Python itself
> > > > > > > > > > > is not acceptable for those applications). In addition to that, C is
> > > > > > > > > > > the best way for other less popular languages (e.g., Rust) to leverage
> > > > > > > > > > > libbpf without investing lots of effort in re-implementing libbpf in
> > > > > > > > > > > Rust.
> > > > > > > > > > I'd say that a libbpf API similar to dlopen/dlsym is a more
> > > > > > > > > > straightforward thing to do. Have a way to "open" a section and
> > > > > > > > > > a way to find a symbol in it. Yes, it's a string-based API,
> > > > > > > > > > but there is nothing wrong with it. IMO, this is easier to
> > > > > > > > > > use/understand and I suppose Python/C++ wrappers are trivial.
> > > > > > > > >
> > > > > > > > > Without digging through libbpf source code (or actually, look at code,
> > > > > > > > > but don't run any test program), what's the name of the map
> > > > > > > > > corresponding to .bss section, if object file is
> > > > > > > > > some_bpf_object_file.o? If you got it right (congrats, btw, it took me
> > > > > > > > > multiple attempts to memorize the pattern), how much time did you
> > > > > > > > > spend looking it up? Now compare it to `skel->maps.bss`. Further, if
> > > > > > > > > you use anonymous structs for your global vars, good luck maintaining
> > > > > > > > > two copies of that: one for BPF side and one for userspace.
> > > > > > > > As your average author of BPF programs I don't really care
> > > > > > > > which section my symbol ends up into. Just give me an api
> > > > > > > > to mmap all "global" sections (or a call per section which does all the
> > > > > > > > naming magic inside) and lookup symbol by name; I can cast it to a proper
> > > > > > > > type and set it.
> > > > > > >
> > > > > > > I'd like to not have to know about bss/rodata/data as well, but that's
> > > > > > > how things are done for global variables. In skeleton we can try to
> > > > > > > make an illusion like they are part of one big datasection/struct, but
> > > > > > > that seems like a bit too much magic at this point. But then again,
> > > > > > > one of the reasons I want this as an experimental feature, so that we
> > > > > > > can actually judge from real experience how inconvenient some things
> > > > > > > are, and not just based on "I think it would be ...".
> > > > > > >
> > > > > > > re: "Just give me ...". Following the spirit of "C is hard" from your
> > > > > > > previous arguments, you already have that API: mmap() syscall. C
> > > > > > > programmers have to be able to figure out the rest ;) But on the
> > > > > > > serious note, this auto-generated code in skeleton actually addresses
> > > > > > > all concerns (and more) that you mentioned: mmaping, knowing offsets,
> > > > > > > knowing names and types, etc. And it doesn't preclude adding more
> > > > > > > "conventional" additional APIs to do everything more dynamically,
> > > > > > > based on string names.
> > > > > > We have different understanding of what's difficult :-)
> > > > >
> > > > > Well, clearly... See below.
> > > > >
> > > > > >
> > > > > > To me, doing transparent data/rodata/bss mmap in bpf_object__load and then
> > > > > > adding a single libbpf api call to lookup symbol by string name is simple
> > > > > > (both from user perspective and from libbpf code complexity). Because in
> > > > > > order to use the codegen I need to teach our build system to spit it
> > > > > > out (which means I need to add bpftool to it and keep it
> > > > > > updated/etc/etc). You can use it as an example of "real experience how
> > > > > > inconvenient some things are".
> > > > >
> > > > > Yes, you need to integrate bpftool in your build process. Which is
> > > > > exactly what I'm doing internally for Facebook as well. But it's a
> > > > > mostly one-time cost, which benefits lots of users who have much
> > > > > better time with these changes, as opposed to make things simpler for
> > > > > us, libbpf developers, at the expense of more convoluted user
> > > > > experience for end users. I certainly prefer more complicated
> > > > > libbpf/bpftool code, if the resulting user experience is simpler for
> > > > > BPF application developers, no doubt about it.
> > > > I'm in the process of going through this with pahole to get proper BTF.
> > > > I don't think I'm willing yet (without a good reason) to go through
> > > > this process again :-D (I saw that you've converted a bunch of tests
> > > > to it which means I might not be able to run them).
> > >
> > > A lot of new functionality is depending on BTF for a really good
> > > reason (check Alexei's fentry/fexit/btf_tp stuff, allowing for safe
> > > direct memory reads and extremely low overhead kretprobes). More stuff
> > > is to come and is going to require in-kernel BTF, so even if it's
> > > painful right now, it's worth it. Think long term and keep perspective
> > > in mind.
> > Oh yeah, that's totally understandable with BTF, that's why I just started
> > adding it to our build. Still was a bit surprising that one day most
> > our our testing went red.
> >
> > > > I just hope bpftool codegen doesn't become a requirement for
> > > > any new useful feature; same happened to BTF, which was optional
> > > > for a while and now I can't run a single selftest without it.
> > > > I can totally understand the BTF requirement though, but I don't buy the
> > > > "codegen makes user experience simple for bpf application developers",
> > > > sorry (I guess, at this point, it's all about preference).
> > >
> > > Bpftool is going to be a requirement for selftests. And it's a good
> > > thing because it allows us to continuously test not just libbpf,
> > > kernel, but now also related tooling. I haven't converted all of the
> > > selftests to skeleton, but given enough time I'd do that, just for the
> > > cleaner and shorter plumbing code it gives.
> > Then why all the talk about --experimantal flags if you've set up your
> > mind on converting everything already?
> 
> Where's contradiction? I'm not converting everything right now, same
> as I haven't converted everything into test_progs, right? But I do
> think that we should work towards that. But all that is still besides
> the point of experimental, because we are talking about selftests, we
> can atomically fix them up with whatever changes we do to those
> experimental APIs.
> 
> The only reason for this experimental disclaimer is for user code
> outside of kernel source tree that is going to try and use skeleton.
> If we need to change something up a little bit, I'd like to still have
> a bit of a wiggle room to adjust things, even if that causes a small
> and easily fixable source code breakage (even though I don't see it
> happening yet, it might be necessary).
In this case, I might have misunderstood your original intent.
I thought you wanted to try this experimental stuff out in
the wild, collect some feedback and then, based on that, decide
whether you want to commit or not. But it seems that it's not
the case.

> > Btw, how hard it would be to do this generation with a new python
> > script instead of bpftool? Something along the lines of
> > scripts/bpf_helpers_doc.py that parses BTF and spits out this C header
> > (shouldn't be that hard to write custom BTF parser in python, right)?
> >
> 
> Not impossible, but harder than I'd care to deal with. I certainly
> don't want to re-implement a good chunk of ELF and BTF parsing (maps,
> progs, in addition to datasec stuff). But "it's hard to use bpftool in
> our build system" doesn't seem like good enough reason to do all that.
You can replace "our build system" with some other project you care about,
like systemd. They'd have the same problem with vendoring in recent enough
bpftool or waiting for every distro to do it. And all this work is
because you think that doing:

	my_obj->rodata->my_var = 123;

Is easier / more type safe than doing:
	int *my_var = bpf_object__rodata_lookup(obj, "my_var");
	*my_var = 123;

I don't think we are going anywhere with this thread, so feel free
to ignore me and don't reply.

> > Python might be easier to integrate with other projects (our build system,
> > cilium, etc).
> >
> > > > > > > > RE anonymous structs: maybe don't use them if you want to share the data
> > > > > > > > between bpf and userspace?
> > > > > > >
> > > > > > > Alright.
> > > > > > >
> > > > > > > >
> > > > > > > > > I never said there is anything wrong with current straightforward
> > > > > > > > > libbpf API, but I also never said it's the easiest and most
> > > > > > > > > user-friendly way to work with BPF either. So we'll have both
> > > > > > > > > code-generated interface and existing API. Furthermore, they are
> > > > > > > > > interoperable (you can pass skel->maps.whatever to any of the existing
> > > > > > > > > libbpf APIs, same for progs, links, obj itself). But there isn't much
> > > > > > > > > that can beat performance and usability of code-generated .data, .bss,
> > > > > > > > > .rodata (and now .extern) layout.
> > > > > > > > I haven't looked closely enough, but is there a libbpf api to get
> > > > > > > > an offset of a variable? Suppose I have the following in bpf.c:
> > > > > > > >
> > > > > > > >         int a;
> > > > > > > >         int b;
> > > > > > > >
> > > > > > > > Can I get an offset of 'b' in the .bss without manually parsing BTF?
> > > > > > >
> > > > > > > No there isn't right now. There isn't even an API to know that there
> > > > > > > is such a variable called "b". Except for this skeleton, of course.
> > > > > > >
> > > > > > > >
> > > > > > > > TBH, I don't buy the performance argument for these global maps.
> > > > > > > > When you did the mmap patchset for the array, you said it yourself
> > > > > > > > that it's about convenience and not performance.
> > > > > > >
> > > > > > > Yes, it's first and foremost about convenience, addressing exactly the
> > > > > > > problems you mentioned above. But performance is critical for some use
> > > > > > > cases, and nothing can beat memory-mapped view of BPF map for those.
> > > > > > > Think about the case of frequently polling (or even atomically
> > > > > > > exchanging) some stats from userspace, as one possible example. E.g.,
> > > > > > > like some map statistics (number of filled elements, p50 of whatever
> > > > > > > of those elements, etc). I'm not sure what's there to buy: doing
> > > > > > > syscall to get **entire** global data map contents vs just fetching
> > > > > > > single integer from memory-mapped region, guess which one is cheaper?
> > > > > > My understanding was that when you were talking about performance, you
> > > > > > were talking about doing symbol offset lookup at runtime vs having a
> > > > > > generated struct with fixed offsets; not about mmap vs old api with copy
> > > > > > (this debate is settled since your patches are accepted).
> > > > >
> > > > > Oh, I see. No, I didn't intend to claim that performance of looking up
> > > > > variable by name in BTF is a big performance concern. Settled then :)
> > > > >
> > > > > >
> > > > > > But to your original reply: you do understand that if you have multiple
> > > > > > threads that write to this global data you have a bigger problem, right?
> > > > >
> > > > > Not necessarily. BPF has atomic increment instruction, doesn't it? And
> > > > > can't we still do atomic swap from user-space (it's just a memory,
> > > > > after all), right? I haven't tried, tbh, but don't see why it wouldn't
> > > > > work.
> > > > Atomics are even worse because you get all these nice cache bouncing effects.
> > > > That's why I didn't understand initialy the argument about performance.
> > >
> > > Depends on problems you are trying to solve. I bet it's still cheaper
> > > than doing map updates under lock, don't you think?
> > Exactly, depends on the problem. But still, if you need frequent reads
> > of that global data, it means there are frequent writes, which is
> > a problem on its own.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12 16:29                                 ` Stanislav Fomichev
@ 2019-12-12 16:53                                   ` Andrii Nakryiko
  2019-12-12 18:43                                     ` Jakub Kicinski
  0 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-12 16:53 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Jakub Kicinski, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Thu, Dec 12, 2019 at 8:29 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
>
> On 12/11, Andrii Nakryiko wrote:
> > On Wed, Dec 11, 2019 at 6:57 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > >
> > > On 12/11, Andrii Nakryiko wrote:
> > > > On Wed, Dec 11, 2019 at 12:09 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > >
> > > > > On 12/11, Andrii Nakryiko wrote:
> > > > > > On Wed, Dec 11, 2019 at 11:15 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > >
> > > > > > > On 12/11, Andrii Nakryiko wrote:
> > > > > > > > On Wed, Dec 11, 2019 at 9:24 AM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > > >
> > > > > > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > > > > > On Tue, Dec 10, 2019 at 2:59 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > > > > >
> > > > > > > > > > > On 12/10, Andrii Nakryiko wrote:
> > > > > > > > > > > > On Tue, Dec 10, 2019 at 1:44 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
> > > > > > > > > > > > >
> > > > > > > > > > > > > On 12/10, Jakub Kicinski wrote:
> > > > > > > > > > > > > > On Tue, 10 Dec 2019 09:11:31 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > > > > > On Mon, Dec 9, 2019 at 5:57 PM Jakub Kicinski wrote:
> > > > > > > > > > > > > > > > On Mon, 9 Dec 2019 17:14:34 -0800, Andrii Nakryiko wrote:
> > > > > > > > > > > > > > > > > struct <object-name> {
> > > > > > > > > > > > > > > > >       /* used by libbpf's skeleton API */
> > > > > > > > > > > > > > > > >       struct bpf_object_skeleton *skeleton;
> > > > > > > > > > > > > > > > >       /* bpf_object for libbpf APIs */
> > > > > > > > > > > > > > > > >       struct bpf_object *obj;
> > > > > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > > > > >               /* for every defined map in BPF object: */
> > > > > > > > > > > > > > > > >               struct bpf_map *<map-name>;
> > > > > > > > > > > > > > > > >       } maps;
> > > > > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > > > > > > >               struct bpf_program *<program-name>;
> > > > > > > > > > > > > > > > >       } progs;
> > > > > > > > > > > > > > > > >       struct {
> > > > > > > > > > > > > > > > >               /* for every program in BPF object: */
> > > > > > > > > > > > > > > > >               struct bpf_link *<program-name>;
> > > > > > > > > > > > > > > > >       } links;
> > > > > > > > > > > > > > > > >       /* for every present global data section: */
> > > > > > > > > > > > > > > > >       struct <object-name>__<one of bss, data, or rodata> {
> > > > > > > > > > > > > > > > >               /* memory layout of corresponding data section,
> > > > > > > > > > > > > > > > >                * with every defined variable represented as a struct field
> > > > > > > > > > > > > > > > >                * with exactly the same type, but without const/volatile
> > > > > > > > > > > > > > > > >                * modifiers, e.g.:
> > > > > > > > > > > > > > > > >                */
> > > > > > > > > > > > > > > > >                int *my_var_1;
> > > > > > > > > > > > > > > > >                ...
> > > > > > > > > > > > > > > > >       } *<one of bss, data, or rodata>;
> > > > > > > > > > > > > > > > > };
> > > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > > I think I understand how this is useful, but perhaps the problem here
> > > > > > > > > > > > > > > > is that we're using C for everything, and simple programs for which
> > > > > > > > > > > > > > > > loading the ELF is majority of the code would be better of being
> > > > > > > > > > > > > > > > written in a dynamic language like python?  Would it perhaps be a
> > > > > > > > > > > > > > > > better idea to work on some high-level language bindings than spend
> > > > > > > > > > > > > > > > time writing code gens and working around limitations of C?
> > > > > > > > > > > > > > >
> > > > > > > > > > > > > > > None of this work prevents Python bindings and other improvements, is
> > > > > > > > > > > > > > > it? Patches, as always, are greatly appreciated ;)
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > This "do it yourself" shit is not really funny :/
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > I'll stop providing feedback on BPF patches if you guy keep saying
> > > > > > > > > > > > > > that :/ Maybe that's what you want.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > > This skeleton stuff is not just to save code, but in general to
> > > > > > > > > > > > > > > simplify and streamline working with BPF program from userspace side.
> > > > > > > > > > > > > > > Fortunately or not, but there are a lot of real-world applications
> > > > > > > > > > > > > > > written in C and C++ that could benefit from this, so this is still
> > > > > > > > > > > > > > > immensely useful. selftests/bpf themselves benefit a lot from this
> > > > > > > > > > > > > > > work, see few of the last patches in this series.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Maybe those applications are written in C and C++ _because_ there
> > > > > > > > > > > > > > are no bindings for high level languages. I just wish BPF programming
> > > > > > > > > > > > > > was less weird and adding some funky codegen is not getting us closer
> > > > > > > > > > > > > > to that goal.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > In my experience code gen is nothing more than a hack to work around
> > > > > > > > > > > > > > bad APIs, but experiences differ so that's not a solid argument.
> > > > > > > > > > > > > *nod*
> > > > > > > > > > > > >
> > > > > > > > > > > > > We have a nice set of C++ wrappers around libbpf internally, so we can do
> > > > > > > > > > > > > something like BpfMap<key type, value type> and get a much better interface
> > > > > > > > > > > > > with type checking. Maybe we should focus on higher level languages instead?
> > > > > > > > > > > > > We are open to open-sourcing our C++ bits if you want to collaborate.
> > > > > > > > > > > >
> > > > > > > > > > > > Python/C++ bindings and API wrappers are an orthogonal concerns here.
> > > > > > > > > > > > I personally think it would be great to have both Python and C++
> > > > > > > > > > > > specific API that uses libbpf under the cover. The only debatable
> > > > > > > > > > > > thing is the logistics: where the source code lives, how it's kept in
> > > > > > > > > > > > sync with libbpf, how we avoid crippling libbpf itself because
> > > > > > > > > > > > something is hard or inconvenient to adapt w/ Python, etc.
> > > > > > > > > > >
> > > > > > > > > > > [..]
> > > > > > > > > > > > The problem I'm trying to solve here is not really C-specific. I don't
> > > > > > > > > > > > think you can solve it without code generation for C++. How do you
> > > > > > > > > > > > "generate" BPF program-specific layout of .data, .bss, .rodata, etc
> > > > > > > > > > > > data sections in such a way, where it's type safe (to the degree that
> > > > > > > > > > > > language allows that, of course) and is not "stringly-based" API? This
> > > > > > > > > > > > skeleton stuff provides a natural, convenient and type-safe way to
> > > > > > > > > > > > work with global data from userspace pretty much at the same level of
> > > > > > > > > > > > performance and convenience, as from BPF side. How can you achieve
> > > > > > > > > > > > that w/ C++ without code generation? As for Python, sure you can do
> > > > > > > > > > > > dynamic lookups based on just the name of property/method, but amount
> > > > > > > > > > > > of overheads is not acceptable for all applications (and Python itself
> > > > > > > > > > > > is not acceptable for those applications). In addition to that, C is
> > > > > > > > > > > > the best way for other less popular languages (e.g., Rust) to leverage
> > > > > > > > > > > > libbpf without investing lots of effort in re-implementing libbpf in
> > > > > > > > > > > > Rust.
> > > > > > > > > > > I'd say that a libbpf API similar to dlopen/dlsym is a more
> > > > > > > > > > > straightforward thing to do. Have a way to "open" a section and
> > > > > > > > > > > a way to find a symbol in it. Yes, it's a string-based API,
> > > > > > > > > > > but there is nothing wrong with it. IMO, this is easier to
> > > > > > > > > > > use/understand and I suppose Python/C++ wrappers are trivial.
> > > > > > > > > >
> > > > > > > > > > Without digging through libbpf source code (or actually, look at code,
> > > > > > > > > > but don't run any test program), what's the name of the map
> > > > > > > > > > corresponding to .bss section, if object file is
> > > > > > > > > > some_bpf_object_file.o? If you got it right (congrats, btw, it took me
> > > > > > > > > > multiple attempts to memorize the pattern), how much time did you
> > > > > > > > > > spend looking it up? Now compare it to `skel->maps.bss`. Further, if
> > > > > > > > > > you use anonymous structs for your global vars, good luck maintaining
> > > > > > > > > > two copies of that: one for BPF side and one for userspace.
> > > > > > > > > As your average author of BPF programs I don't really care
> > > > > > > > > which section my symbol ends up into. Just give me an api
> > > > > > > > > to mmap all "global" sections (or a call per section which does all the
> > > > > > > > > naming magic inside) and lookup symbol by name; I can cast it to a proper
> > > > > > > > > type and set it.
> > > > > > > >
> > > > > > > > I'd like to not have to know about bss/rodata/data as well, but that's
> > > > > > > > how things are done for global variables. In skeleton we can try to
> > > > > > > > make an illusion like they are part of one big datasection/struct, but
> > > > > > > > that seems like a bit too much magic at this point. But then again,
> > > > > > > > one of the reasons I want this as an experimental feature, so that we
> > > > > > > > can actually judge from real experience how inconvenient some things
> > > > > > > > are, and not just based on "I think it would be ...".
> > > > > > > >
> > > > > > > > re: "Just give me ...". Following the spirit of "C is hard" from your
> > > > > > > > previous arguments, you already have that API: mmap() syscall. C
> > > > > > > > programmers have to be able to figure out the rest ;) But on the
> > > > > > > > serious note, this auto-generated code in skeleton actually addresses
> > > > > > > > all concerns (and more) that you mentioned: mmaping, knowing offsets,
> > > > > > > > knowing names and types, etc. And it doesn't preclude adding more
> > > > > > > > "conventional" additional APIs to do everything more dynamically,
> > > > > > > > based on string names.
> > > > > > > We have different understanding of what's difficult :-)
> > > > > >
> > > > > > Well, clearly... See below.
> > > > > >
> > > > > > >
> > > > > > > To me, doing transparent data/rodata/bss mmap in bpf_object__load and then
> > > > > > > adding a single libbpf api call to lookup symbol by string name is simple
> > > > > > > (both from user perspective and from libbpf code complexity). Because in
> > > > > > > order to use the codegen I need to teach our build system to spit it
> > > > > > > out (which means I need to add bpftool to it and keep it
> > > > > > > updated/etc/etc). You can use it as an example of "real experience how
> > > > > > > inconvenient some things are".
> > > > > >
> > > > > > Yes, you need to integrate bpftool in your build process. Which is
> > > > > > exactly what I'm doing internally for Facebook as well. But it's a
> > > > > > mostly one-time cost, which benefits lots of users who have much
> > > > > > better time with these changes, as opposed to make things simpler for
> > > > > > us, libbpf developers, at the expense of more convoluted user
> > > > > > experience for end users. I certainly prefer more complicated
> > > > > > libbpf/bpftool code, if the resulting user experience is simpler for
> > > > > > BPF application developers, no doubt about it.
> > > > > I'm in the process of going through this with pahole to get proper BTF.
> > > > > I don't think I'm willing yet (without a good reason) to go through
> > > > > this process again :-D (I saw that you've converted a bunch of tests
> > > > > to it which means I might not be able to run them).
> > > >
> > > > A lot of new functionality is depending on BTF for a really good
> > > > reason (check Alexei's fentry/fexit/btf_tp stuff, allowing for safe
> > > > direct memory reads and extremely low overhead kretprobes). More stuff
> > > > is to come and is going to require in-kernel BTF, so even if it's
> > > > painful right now, it's worth it. Think long term and keep perspective
> > > > in mind.
> > > Oh yeah, that's totally understandable with BTF, that's why I just started
> > > adding it to our build. Still was a bit surprising that one day most
> > > our our testing went red.
> > >
> > > > > I just hope bpftool codegen doesn't become a requirement for
> > > > > any new useful feature; same happened to BTF, which was optional
> > > > > for a while and now I can't run a single selftest without it.
> > > > > I can totally understand the BTF requirement though, but I don't buy the
> > > > > "codegen makes user experience simple for bpf application developers",
> > > > > sorry (I guess, at this point, it's all about preference).
> > > >
> > > > Bpftool is going to be a requirement for selftests. And it's a good
> > > > thing because it allows us to continuously test not just libbpf,
> > > > kernel, but now also related tooling. I haven't converted all of the
> > > > selftests to skeleton, but given enough time I'd do that, just for the
> > > > cleaner and shorter plumbing code it gives.
> > > Then why all the talk about --experimantal flags if you've set up your
> > > mind on converting everything already?
> >
> > Where's contradiction? I'm not converting everything right now, same
> > as I haven't converted everything into test_progs, right? But I do
> > think that we should work towards that. But all that is still besides
> > the point of experimental, because we are talking about selftests, we
> > can atomically fix them up with whatever changes we do to those
> > experimental APIs.
> >
> > The only reason for this experimental disclaimer is for user code
> > outside of kernel source tree that is going to try and use skeleton.
> > If we need to change something up a little bit, I'd like to still have
> > a bit of a wiggle room to adjust things, even if that causes a small
> > and easily fixable source code breakage (even though I don't see it
> > happening yet, it might be necessary).
> In this case, I might have misunderstood your original intent.
> I thought you wanted to try this experimental stuff out in
> the wild, collect some feedback and then, based on that, decide
> whether you want to commit or not. But it seems that it's not
> the case.
>
> > > Btw, how hard it would be to do this generation with a new python
> > > script instead of bpftool? Something along the lines of
> > > scripts/bpf_helpers_doc.py that parses BTF and spits out this C header
> > > (shouldn't be that hard to write custom BTF parser in python, right)?
> > >
> >
> > Not impossible, but harder than I'd care to deal with. I certainly
> > don't want to re-implement a good chunk of ELF and BTF parsing (maps,
> > progs, in addition to datasec stuff). But "it's hard to use bpftool in
> > our build system" doesn't seem like good enough reason to do all that.
> You can replace "our build system" with some other project you care about,
> like systemd. They'd have the same problem with vendoring in recent enough
> bpftool or waiting for every distro to do it. And all this work is
> because you think that doing:
>
>         my_obj->rodata->my_var = 123;
>
> Is easier / more type safe than doing:
>         int *my_var = bpf_object__rodata_lookup(obj, "my_var");
>         *my_var = 123;

Your arguments are confusing me. Did I say that we shouldn't add this
type of "dynamic" interface to variables? Or did I say that every
single BPF application has to adopt skeleton and bpftool? I made no
such claims and it seems like discussion is just based around where I
have to apply my time and efforts... You think it's not useful - don't
integrate bpftool into your build system, simple as that. Skeleton is
used for selftests, but it's up to maintainers to decide whether to
keep this, similar to all the BTF decisions.

So in conclusion. Yes, I think skeleton way is better/safer/easier
than string-based interface you are proposing, but no I'm not claiming
it should be the only way. It just happens to be the way I chose to
implement (first?). If you think API you have in mind makes sense,
feel free to implement it and we'll have a separate discussion for
those.

>
> I don't think we are going anywhere with this thread, so feel free
> to ignore me and don't reply.
>
> > > Python might be easier to integrate with other projects (our build system,
> > > cilium, etc).
> > >
> > > > > > > > > RE anonymous structs: maybe don't use them if you want to share the data
> > > > > > > > > between bpf and userspace?
> > > > > > > >
> > > > > > > > Alright.
> > > > > > > >
> > > > > > > > >
> > > > > > > > > > I never said there is anything wrong with current straightforward
> > > > > > > > > > libbpf API, but I also never said it's the easiest and most
> > > > > > > > > > user-friendly way to work with BPF either. So we'll have both
> > > > > > > > > > code-generated interface and existing API. Furthermore, they are
> > > > > > > > > > interoperable (you can pass skel->maps.whatever to any of the existing
> > > > > > > > > > libbpf APIs, same for progs, links, obj itself). But there isn't much
> > > > > > > > > > that can beat performance and usability of code-generated .data, .bss,
> > > > > > > > > > .rodata (and now .extern) layout.
> > > > > > > > > I haven't looked closely enough, but is there a libbpf api to get
> > > > > > > > > an offset of a variable? Suppose I have the following in bpf.c:
> > > > > > > > >
> > > > > > > > >         int a;
> > > > > > > > >         int b;
> > > > > > > > >
> > > > > > > > > Can I get an offset of 'b' in the .bss without manually parsing BTF?
> > > > > > > >
> > > > > > > > No there isn't right now. There isn't even an API to know that there
> > > > > > > > is such a variable called "b". Except for this skeleton, of course.
> > > > > > > >
> > > > > > > > >
> > > > > > > > > TBH, I don't buy the performance argument for these global maps.
> > > > > > > > > When you did the mmap patchset for the array, you said it yourself
> > > > > > > > > that it's about convenience and not performance.
> > > > > > > >
> > > > > > > > Yes, it's first and foremost about convenience, addressing exactly the
> > > > > > > > problems you mentioned above. But performance is critical for some use
> > > > > > > > cases, and nothing can beat memory-mapped view of BPF map for those.
> > > > > > > > Think about the case of frequently polling (or even atomically
> > > > > > > > exchanging) some stats from userspace, as one possible example. E.g.,
> > > > > > > > like some map statistics (number of filled elements, p50 of whatever
> > > > > > > > of those elements, etc). I'm not sure what's there to buy: doing
> > > > > > > > syscall to get **entire** global data map contents vs just fetching
> > > > > > > > single integer from memory-mapped region, guess which one is cheaper?
> > > > > > > My understanding was that when you were talking about performance, you
> > > > > > > were talking about doing symbol offset lookup at runtime vs having a
> > > > > > > generated struct with fixed offsets; not about mmap vs old api with copy
> > > > > > > (this debate is settled since your patches are accepted).
> > > > > >
> > > > > > Oh, I see. No, I didn't intend to claim that performance of looking up
> > > > > > variable by name in BTF is a big performance concern. Settled then :)
> > > > > >
> > > > > > >
> > > > > > > But to your original reply: you do understand that if you have multiple
> > > > > > > threads that write to this global data you have a bigger problem, right?
> > > > > >
> > > > > > Not necessarily. BPF has atomic increment instruction, doesn't it? And
> > > > > > can't we still do atomic swap from user-space (it's just a memory,
> > > > > > after all), right? I haven't tried, tbh, but don't see why it wouldn't
> > > > > > work.
> > > > > Atomics are even worse because you get all these nice cache bouncing effects.
> > > > > That's why I didn't understand initialy the argument about performance.
> > > >
> > > > Depends on problems you are trying to solve. I bet it's still cheaper
> > > > than doing map updates under lock, don't you think?
> > > Exactly, depends on the problem. But still, if you need frequent reads
> > > of that global data, it means there are frequent writes, which is
> > > a problem on its own.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12 16:53                                   ` Andrii Nakryiko
@ 2019-12-12 18:43                                     ` Jakub Kicinski
  2019-12-12 18:58                                       ` Stanislav Fomichev
  2019-12-12 19:54                                       ` Alexei Starovoitov
  0 siblings, 2 replies; 57+ messages in thread
From: Jakub Kicinski @ 2019-12-12 18:43 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Stanislav Fomichev, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Thu, 12 Dec 2019 08:53:22 -0800, Andrii Nakryiko wrote:
> > > > Btw, how hard it would be to do this generation with a new python
> > > > script instead of bpftool? Something along the lines of
> > > > scripts/bpf_helpers_doc.py that parses BTF and spits out this C header
> > > > (shouldn't be that hard to write custom BTF parser in python, right)?
> > > >  
> > >
> > > Not impossible, but harder than I'd care to deal with. I certainly
> > > don't want to re-implement a good chunk of ELF and BTF parsing (maps,
> > > progs, in addition to datasec stuff). But "it's hard to use bpftool in
> > > our build system" doesn't seem like good enough reason to do all that.  
> > You can replace "our build system" with some other project you care about,
> > like systemd. They'd have the same problem with vendoring in recent enough
> > bpftool or waiting for every distro to do it. And all this work is
> > because you think that doing:
> >
> >         my_obj->rodata->my_var = 123;
> >
> > Is easier / more type safe than doing:
> >         int *my_var = bpf_object__rodata_lookup(obj, "my_var");
> >         *my_var = 123;  
> 
> Your arguments are confusing me. Did I say that we shouldn't add this
> type of "dynamic" interface to variables? Or did I say that every
> single BPF application has to adopt skeleton and bpftool? I made no
> such claims and it seems like discussion is just based around where I
> have to apply my time and efforts... You think it's not useful - don't
> integrate bpftool into your build system, simple as that. Skeleton is
> used for selftests, but it's up to maintainers to decide whether to
> keep this, similar to all the BTF decisions.

Since we have two people suggesting this functionality to be a separate
tool could you please reconsider my arguments from two days ago?

  There absolutely nothing this tool needs from [bpftool], no
  JSON needed, no bpffs etc. It can be a separate tool like
  libbpf-skel-gen or libbpf-c-skel or something, distributed with libbpf.
  That way you can actually soften the backward compat. In case people
  become dependent on it they can carry that little tool on their own.

I'd honestly leave the distro packaging problem for people who actually
work on that to complain about.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12 18:43                                     ` Jakub Kicinski
@ 2019-12-12 18:58                                       ` Stanislav Fomichev
  2019-12-12 19:23                                         ` Jakub Kicinski
  2019-12-12 19:54                                       ` Alexei Starovoitov
  1 sibling, 1 reply; 57+ messages in thread
From: Stanislav Fomichev @ 2019-12-12 18:58 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrii Nakryiko, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On 12/12, Jakub Kicinski wrote:
> On Thu, 12 Dec 2019 08:53:22 -0800, Andrii Nakryiko wrote:
> > > > > Btw, how hard it would be to do this generation with a new python
> > > > > script instead of bpftool? Something along the lines of
> > > > > scripts/bpf_helpers_doc.py that parses BTF and spits out this C header
> > > > > (shouldn't be that hard to write custom BTF parser in python, right)?
> > > > >  
> > > >
> > > > Not impossible, but harder than I'd care to deal with. I certainly
> > > > don't want to re-implement a good chunk of ELF and BTF parsing (maps,
> > > > progs, in addition to datasec stuff). But "it's hard to use bpftool in
> > > > our build system" doesn't seem like good enough reason to do all that.  
> > > You can replace "our build system" with some other project you care about,
> > > like systemd. They'd have the same problem with vendoring in recent enough
> > > bpftool or waiting for every distro to do it. And all this work is
> > > because you think that doing:
> > >
> > >         my_obj->rodata->my_var = 123;
> > >
> > > Is easier / more type safe than doing:
> > >         int *my_var = bpf_object__rodata_lookup(obj, "my_var");
> > >         *my_var = 123;  
> > 
> > Your arguments are confusing me. Did I say that we shouldn't add this
> > type of "dynamic" interface to variables? Or did I say that every
> > single BPF application has to adopt skeleton and bpftool? I made no
> > such claims and it seems like discussion is just based around where I
> > have to apply my time and efforts... You think it's not useful - don't
> > integrate bpftool into your build system, simple as that. Skeleton is
> > used for selftests, but it's up to maintainers to decide whether to
> > keep this, similar to all the BTF decisions.
> 
> Since we have two people suggesting this functionality to be a separate
> tool could you please reconsider my arguments from two days ago?
> 
>   There absolutely nothing this tool needs from [bpftool], no
>   JSON needed, no bpffs etc. It can be a separate tool like
>   libbpf-skel-gen or libbpf-c-skel or something, distributed with libbpf.
>   That way you can actually soften the backward compat. In case people
>   become dependent on it they can carry that little tool on their own.

[..]
> I'd honestly leave the distro packaging problem for people who actually
> work on that to complain about.
I'm representing a 'Google distro' :-D

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12 18:58                                       ` Stanislav Fomichev
@ 2019-12-12 19:23                                         ` Jakub Kicinski
  0 siblings, 0 replies; 57+ messages in thread
From: Jakub Kicinski @ 2019-12-12 19:23 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Andrii Nakryiko, Andrii Nakryiko, LKML, bpf, Networking,
	Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Thu, 12 Dec 2019 10:58:31 -0800, Stanislav Fomichev wrote:
> > I'd honestly leave the distro packaging problem for people who actually
> > work on that to complain about.  
> I'm representing a 'Google distro' :-D

Suits me :)

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12 18:43                                     ` Jakub Kicinski
  2019-12-12 18:58                                       ` Stanislav Fomichev
@ 2019-12-12 19:54                                       ` Alexei Starovoitov
  2019-12-12 20:21                                         ` Jakub Kicinski
  2019-12-12 21:45                                         ` Stanislav Fomichev
  1 sibling, 2 replies; 57+ messages in thread
From: Alexei Starovoitov @ 2019-12-12 19:54 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrii Nakryiko, Stanislav Fomichev, Andrii Nakryiko, LKML, bpf,
	Networking, Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Thu, Dec 12, 2019 at 10:43:34AM -0800, Jakub Kicinski wrote:
> On Thu, 12 Dec 2019 08:53:22 -0800, Andrii Nakryiko wrote:
> > > > > Btw, how hard it would be to do this generation with a new python
> > > > > script instead of bpftool? Something along the lines of
> > > > > scripts/bpf_helpers_doc.py that parses BTF and spits out this C header
> > > > > (shouldn't be that hard to write custom BTF parser in python, right)?
> > > > >  
> > > >
> > > > Not impossible, but harder than I'd care to deal with. I certainly
> > > > don't want to re-implement a good chunk of ELF and BTF parsing (maps,
> > > > progs, in addition to datasec stuff). But "it's hard to use bpftool in
> > > > our build system" doesn't seem like good enough reason to do all that.  
> > > You can replace "our build system" with some other project you care about,
> > > like systemd. They'd have the same problem with vendoring in recent enough
> > > bpftool or waiting for every distro to do it. And all this work is
> > > because you think that doing:
> > >
> > >         my_obj->rodata->my_var = 123;
> > >
> > > Is easier / more type safe than doing:
> > >         int *my_var = bpf_object__rodata_lookup(obj, "my_var");
> > >         *my_var = 123;  
> > 
> > Your arguments are confusing me. Did I say that we shouldn't add this
> > type of "dynamic" interface to variables? Or did I say that every
> > single BPF application has to adopt skeleton and bpftool? I made no
> > such claims and it seems like discussion is just based around where I
> > have to apply my time and efforts... You think it's not useful - don't
> > integrate bpftool into your build system, simple as that. Skeleton is
> > used for selftests, but it's up to maintainers to decide whether to
> > keep this, similar to all the BTF decisions.
> 
> Since we have two people suggesting this functionality to be a separate
> tool could you please reconsider my arguments from two days ago?
> 
>   There absolutely nothing this tool needs from [bpftool], no
>   JSON needed, no bpffs etc. 

To generate vmlinux.h bpftool doesn't need json and doesn't need bpffs.

> It can be a separate tool like
>   libbpf-skel-gen or libbpf-c-skel or something, distributed with libbpf.
>   That way you can actually soften the backward compat. In case people
>   become dependent on it they can carry that little tool on their own.

Jakub,

Could you please consider Andrii's reply to your comment from two days ago:
https://lore.kernel.org/bpf/CAEf4BzbeZbmCTOOo2uQXjm0GL0WDu7aLN6fdUk18Nv2g0kfwVg@mail.gmail.com/
"we are trying to make users lives easier by having major distributions
distribute bpftool and libbpf properly. Adding extra binaries to
distribute around doesn't seem to be easing any of users pains."

My opinion is the following.
bpftool is necessary to write bpf programs already. It's necessary to produce
vmlinux.h for bpf programs to include it. It's part of build process. I can
relate to Stan's complains that he needs to update clang and pahole. He missed
the fact that he needs to update bpftool too if he wants to use all features of
CO-RE. Same thing for skeleton generation. If people need to run the latest
selftest/bpf on the latest kernel they need to upgrade to the latest clang,
pahole, libbpf, bpftool. Nothing new here.

Backwards compat is the same concern for skeleton generation and for vmlinux.h
generation. Obviously no one wants to introduce something that will keep
changing. Is vmlinux.h generation stable? I like to believe so. Same with
skeleton. I wouldn't want to see it changing, but in both cases such chance
exists. We cannot and should not adopt kernel-like ABI guarantees to user space
code. It will paralyze the development.

Now consider if vmlinux.h and skeleton generation is split out of bpftool into
new tool. Effectively it would mean a fork of bpftool. Two binaries doing bpf
elf file processing without clear distinction between them is going to be very
confusing.

One more point from Stan's email:

> You can replace "our build system" with some other project you care about,
> like systemd. They'd have the same problem with vendoring in recent enough

we've been working with systemd folks for ~8 month to integrate libbpf into
their build that is using meson build system and their CI that is github based.
So we're well aware about systemd requirements for libbpf and friends.

> bpftool or waiting for every distro to do it. And all this work is
> because you think that doing:
>
>        my_obj->rodata->my_var = 123;
>
> Is easier / more type safe than doing:
>        int *my_var = bpf_object__rodata_lookup(obj, "my_var");
>        *my_var = 123;

Stan, you conveniently skipped error checking. It should have been:
    int *my_var = bpf_object__rodata_lookup(obj, "my_var");
    if (IS_ERROR_NULL(my_var))
        goto out_cleanup;
     *my_var = 123;

Now multiply this code by N variables and consider how cleanup code will look like.
Then multiply this lookup plus cleanup code for all maps, all program and all links.
Today this bpf_object__*lookup*("string") for programs, maps, links is a major
chunk of code not all only for all tests, but for all C, python, C++ services
that use BPF. It's error prone and verbose. Generated skeleton moves the tedious
job of writing lookup accessors from humans into bpftool.
Take a look at Andrii's patch 13/15:
5 files changed, 149 insertions(+), 249 deletions(-)
Those are simple selftests, yet code removal is huge. Bigger project benefits
even more.

bcc and bpftrace are successful projects because barrier of entry is low.
Both allow single line 'hello world' to get going with BPF. We're missing
this completely for networking. Some people suggest that bpftrace like
domain specific language needs to be invented for networking. I don't mind. Yet
I'd like C to be the first choice both for networking and for tracing. To
achieve that the barrier of entry need to be drastically reduced. 'Hello world'
in BPF C and corresponding user space C should fit on one slide. The existing
amount of boiler plate code in libbpf is such barrier. Skeleton generation
solves this usability problem. I do expect a lot more tools to be written in C
because skeleton exists. Can we teach skeleton to generate C++, go, rust,
python wrappers? Surely we can. One step at a time. If we make it work well for
"bpf C" plus "user C" it will work well when user side is written in a
different language.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12 19:54                                       ` Alexei Starovoitov
@ 2019-12-12 20:21                                         ` Jakub Kicinski
  2019-12-12 21:28                                           ` Alexei Starovoitov
  2019-12-13  6:48                                           ` Andrii Nakryiko
  2019-12-12 21:45                                         ` Stanislav Fomichev
  1 sibling, 2 replies; 57+ messages in thread
From: Jakub Kicinski @ 2019-12-12 20:21 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Andrii Nakryiko, Stanislav Fomichev, Andrii Nakryiko, LKML, bpf,
	Networking, Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Thu, 12 Dec 2019 11:54:16 -0800, Alexei Starovoitov wrote:
> On Thu, Dec 12, 2019 at 10:43:34AM -0800, Jakub Kicinski wrote:
> > On Thu, 12 Dec 2019 08:53:22 -0800, Andrii Nakryiko wrote:  
> > > > > > Btw, how hard it would be to do this generation with a new python
> > > > > > script instead of bpftool? Something along the lines of
> > > > > > scripts/bpf_helpers_doc.py that parses BTF and spits out this C header
> > > > > > (shouldn't be that hard to write custom BTF parser in python, right)?
> > > > > >    
> > > > >
> > > > > Not impossible, but harder than I'd care to deal with. I certainly
> > > > > don't want to re-implement a good chunk of ELF and BTF parsing (maps,
> > > > > progs, in addition to datasec stuff). But "it's hard to use bpftool in
> > > > > our build system" doesn't seem like good enough reason to do all that.    
> > > > You can replace "our build system" with some other project you care about,
> > > > like systemd. They'd have the same problem with vendoring in recent enough
> > > > bpftool or waiting for every distro to do it. And all this work is
> > > > because you think that doing:
> > > >
> > > >         my_obj->rodata->my_var = 123;
> > > >
> > > > Is easier / more type safe than doing:
> > > >         int *my_var = bpf_object__rodata_lookup(obj, "my_var");
> > > >         *my_var = 123;    
> > > 
> > > Your arguments are confusing me. Did I say that we shouldn't add this
> > > type of "dynamic" interface to variables? Or did I say that every
> > > single BPF application has to adopt skeleton and bpftool? I made no
> > > such claims and it seems like discussion is just based around where I
> > > have to apply my time and efforts... You think it's not useful - don't
> > > integrate bpftool into your build system, simple as that. Skeleton is
> > > used for selftests, but it's up to maintainers to decide whether to
> > > keep this, similar to all the BTF decisions.  
> > 
> > Since we have two people suggesting this functionality to be a separate
> > tool could you please reconsider my arguments from two days ago?
> > 
> >   There absolutely nothing this tool needs from [bpftool], no
> >   JSON needed, no bpffs etc.   
> 
> To generate vmlinux.h bpftool doesn't need json and doesn't need bpffs.

At least for header generation it pertains to the running system.
And bpftool was (and still is AFAICT) about interacting with the BPF
state on the running system.

> > It can be a separate tool like
> >   libbpf-skel-gen or libbpf-c-skel or something, distributed with libbpf.
> >   That way you can actually soften the backward compat. In case people
> >   become dependent on it they can carry that little tool on their own.  
> 
> Jakub,
> 
> Could you please consider Andrii's reply to your comment from two days ago:
> https://lore.kernel.org/bpf/CAEf4BzbeZbmCTOOo2uQXjm0GL0WDu7aLN6fdUk18Nv2g0kfwVg@mail.gmail.com/
> "we are trying to make users lives easier by having major distributions
> distribute bpftool and libbpf properly. Adding extra binaries to
> distribute around doesn't seem to be easing any of users pains."

Last time we argued I heard how GH makes libbpf packaging easier.
Only to have that dis-proven once the people in Europe who do distro
packaging woke up:

https://lkml.org/lkml/2019/12/5/101
https://lkml.org/lkml/2019/12/5/312

I feel I'm justified not to take your opinion on this as fact.

> My opinion is the following.
> bpftool is necessary to write bpf programs already. It's necessary to produce
> vmlinux.h for bpf programs to include it. It's part of build process. I can
> relate to Stan's complains that he needs to update clang and pahole. He missed
> the fact that he needs to update bpftool too if he wants to use all features of
> CO-RE. Same thing for skeleton generation. If people need to run the latest
> selftest/bpf on the latest kernel they need to upgrade to the latest clang,
> pahole, libbpf, bpftool. Nothing new here.

They have to update libbpf, so why can't the code gen tool be part of
libbpf? We don't need to build all BPF user space into one binary.

> Backwards compat is the same concern for skeleton generation and for vmlinux.h
> generation. Obviously no one wants to introduce something that will keep
> changing. Is vmlinux.h generation stable? I like to believe so. Same with
> skeleton. I wouldn't want to see it changing, but in both cases such chance
> exists. 

vmlinux.h is pretty stable, there isn't much wiggle room there.
It's more of a conversion tool, if you will.

Skeleton OTOH is supposed to make people's lives easier, so it's a
completely different beast. It should be malleable so that users can
improve and hack on it. Baking it into as system tool is counter
productive. Users should be able to grab the skel tool single-file
source and adjust for their project's needs. Distributing your own copy
of bpftool because you want to adjust skel is a heavy lift.

And maybe one day we do have Python/Go/whatever bindings, and we can
convert the skel tool to a higher level language with modern templating.

> We cannot and should not adopt kernel-like ABI guarantees to user space
> code. It will paralyze the development.

Discussion for another time :)

> Now consider if vmlinux.h and skeleton generation is split out of bpftool into
> new tool. Effectively it would mean a fork of bpftool. Two binaries doing bpf
> elf file processing without clear distinction between them is going to be very
> confusing.

To be clear I'm suggesting skel gen is a separate tool, vmlinux and
Quentin's header gen work on the running system, they are not pure
build env tools.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12 20:21                                         ` Jakub Kicinski
@ 2019-12-12 21:28                                           ` Alexei Starovoitov
  2019-12-12 21:59                                             ` Jakub Kicinski
  2019-12-13  6:48                                           ` Andrii Nakryiko
  1 sibling, 1 reply; 57+ messages in thread
From: Alexei Starovoitov @ 2019-12-12 21:28 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Andrii Nakryiko, Stanislav Fomichev, Andrii Nakryiko, LKML, bpf,
	Networking, Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Thu, Dec 12, 2019 at 12:21:15PM -0800, Jakub Kicinski wrote:
> > > 
> > >   There absolutely nothing this tool needs from [bpftool], no
> > >   JSON needed, no bpffs etc.   
> > 
> > To generate vmlinux.h bpftool doesn't need json and doesn't need bpffs.
> 
> At least for header generation it pertains to the running system.
> And bpftool was (and still is AFAICT) about interacting with the BPF
> state on the running system.

No. Reality is different. vmlinux.h generation doesn't need to touch
kernel on the running system. Part of its job is to generate multiple
vmlinux.h from a set of vmlinux elf files. Different .h for different kernels.
It can generate vmlinux.h from running kernel too, but its less relevant
to make use of CO-RE.
In the future bpftool will be used to merge such multiple .h-s.
Likely it will first merge BTFs from vmlinuxes and then will produce
merged vmlinux_4_x_and_5_x.h

> > > It can be a separate tool like
> > >   libbpf-skel-gen or libbpf-c-skel or something, distributed with libbpf.
> > >   That way you can actually soften the backward compat. In case people
> > >   become dependent on it they can carry that little tool on their own.  
> > 
> > Jakub,
> > 
> > Could you please consider Andrii's reply to your comment from two days ago:
> > https://lore.kernel.org/bpf/CAEf4BzbeZbmCTOOo2uQXjm0GL0WDu7aLN6fdUk18Nv2g0kfwVg@mail.gmail.com/
> > "we are trying to make users lives easier by having major distributions
> > distribute bpftool and libbpf properly. Adding extra binaries to
> > distribute around doesn't seem to be easing any of users pains."
> 
> Last time we argued I heard how GH makes libbpf packaging easier.
> Only to have that dis-proven once the people in Europe who do distro
> packaging woke up:
> 
> https://lkml.org/lkml/2019/12/5/101
> https://lkml.org/lkml/2019/12/5/312

I think you missed the point of these two comments. It was about packaging
bpftool and libbpf together. Regardless how bpftool is packaged. I still
strongly suggest to use github/libbpf to package libbpf. It's something that is
actually tested whereas libbpf in the kernel tree has unit test coverage only.

> 
> > My opinion is the following.
> > bpftool is necessary to write bpf programs already. It's necessary to produce
> > vmlinux.h for bpf programs to include it. It's part of build process. I can
> > relate to Stan's complains that he needs to update clang and pahole. He missed
> > the fact that he needs to update bpftool too if he wants to use all features of
> > CO-RE. Same thing for skeleton generation. If people need to run the latest
> > selftest/bpf on the latest kernel they need to upgrade to the latest clang,
> > pahole, libbpf, bpftool. Nothing new here.
> 
> They have to update libbpf, so why can't the code gen tool be part of
> libbpf? 

I'm not sure why two answers were not enough.
No idea how to answer this question differently for the third time.

> > Backwards compat is the same concern for skeleton generation and for vmlinux.h
> > generation. Obviously no one wants to introduce something that will keep
> > changing. Is vmlinux.h generation stable? I like to believe so. Same with
> > skeleton. I wouldn't want to see it changing, but in both cases such chance
> > exists. 
> 
> vmlinux.h is pretty stable, there isn't much wiggle room there.

Do you have experience working with vmlinux.h? I bet the answer is no.
While we have and identified few things that needs improvement.
They require vmlinux.h to be generated differently.

> It's more of a conversion tool, if you will.
> 
> Skeleton OTOH is supposed to make people's lives easier, so it's a
> completely different beast. It should be malleable so that users can
> improve and hack on it. Baking it into as system tool is counter
> productive. Users should be able to grab the skel tool single-file
> source and adjust for their project's needs. Distributing your own copy
> of bpftool because you want to adjust skel is a heavy lift.

Adjust generator for their custom needs? essentially fork it for
private use? I'd rather prevent such possibility.
When people start using it I'd prefer they come back to this mailing
list with patches than do 'easy fork'.

> > Now consider if vmlinux.h and skeleton generation is split out of bpftool into
> > new tool. Effectively it would mean a fork of bpftool. Two binaries doing bpf
> > elf file processing without clear distinction between them is going to be very
> > confusing.
> 
> To be clear I'm suggesting skel gen is a separate tool, vmlinux and
> Quentin's header gen work on the running system, they are not pure
> build env tools.

You meant to say Andrii's header generator that is based on Quentin's man page
generator. Its output bpf_helper_defs.h makes sense as a part of libbpf
package. The generator script itself doesn't need to be included with any package.
bpftool vmlinux gen consumes vmlinux elf files and is a part of the build.
bpftool skeleton gen consumes bpf elf files and is a part of the same build.


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12 19:54                                       ` Alexei Starovoitov
  2019-12-12 20:21                                         ` Jakub Kicinski
@ 2019-12-12 21:45                                         ` Stanislav Fomichev
  2019-12-13  6:23                                           ` Andrii Nakryiko
  1 sibling, 1 reply; 57+ messages in thread
From: Stanislav Fomichev @ 2019-12-12 21:45 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Jakub Kicinski, Andrii Nakryiko, Andrii Nakryiko, LKML, bpf,
	Networking, Alexei Starovoitov, Daniel Borkmann, Kernel Team

On 12/12, Alexei Starovoitov wrote:
> On Thu, Dec 12, 2019 at 10:43:34AM -0800, Jakub Kicinski wrote:
> One more point from Stan's email:
> 
> > You can replace "our build system" with some other project you care about,
> > like systemd. They'd have the same problem with vendoring in recent enough
> 
> we've been working with systemd folks for ~8 month to integrate libbpf into
> their build that is using meson build system and their CI that is github based.
> So we're well aware about systemd requirements for libbpf and friends.
Just curious (searching on systemd github for bpftool/libbpf doesn't
show up any code/issues): are you saying that there will be another ~8 months
to bring in bpftool or that it's already being worked on as part of
libbpf integration?

> > bpftool or waiting for every distro to do it. And all this work is
> > because you think that doing:
> >
> >        my_obj->rodata->my_var = 123;
> >
> > Is easier / more type safe than doing:
> >        int *my_var = bpf_object__rodata_lookup(obj, "my_var");
> >        *my_var = 123;
> 
> Stan, you conveniently skipped error checking. It should have been:
>     int *my_var = bpf_object__rodata_lookup(obj, "my_var");
>     if (IS_ERROR_NULL(my_var))
>         goto out_cleanup;
>      *my_var = 123;
Yeah, but you have a choice, right? You can choose to check the error
and support old programs that don't export some global var and a new
program that has it. Or you can skip the error checks and rely on null
deref crash which is sometimes an option.

(might be not relevant with the introduction of EMBED_FILE which you
seem to be using more and more; ideally, we still would like to be able to
distribute bpf.o and userspace binary separately).

> Take a look at Andrii's patch 13/15:
> 5 files changed, 149 insertions(+), 249 deletions(-)
> Those are simple selftests, yet code removal is huge. Bigger project benefits
> even more.
Excluding fentry/fexit tests (where new find_program_by_title+attach
helper and mmap might help), it looks like the majority of those gains come
from the fact that the patch in question doesn't do any error checking.
You can drop all the CHECK() stuff for existing
find_map_by_name/find_prog_by_name instead and get the same gains.

[as usual, feel free to ignore me, I don't want to keep flaming about
it, but it's hard not to reply]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12 21:28                                           ` Alexei Starovoitov
@ 2019-12-12 21:59                                             ` Jakub Kicinski
  0 siblings, 0 replies; 57+ messages in thread
From: Jakub Kicinski @ 2019-12-12 21:59 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Andrii Nakryiko, Stanislav Fomichev, Andrii Nakryiko, LKML, bpf,
	Networking, Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Thu, 12 Dec 2019 13:28:00 -0800, Alexei Starovoitov wrote:
> On Thu, Dec 12, 2019 at 12:21:15PM -0800, Jakub Kicinski wrote:
> > > > It can be a separate tool like
> > > >   libbpf-skel-gen or libbpf-c-skel or something, distributed with libbpf.
> > > >   That way you can actually soften the backward compat. In case people
> > > >   become dependent on it they can carry that little tool on their own.    
> > > 
> > > Jakub,
> > > 
> > > Could you please consider Andrii's reply to your comment from two days ago:
> > > https://lore.kernel.org/bpf/CAEf4BzbeZbmCTOOo2uQXjm0GL0WDu7aLN6fdUk18Nv2g0kfwVg@mail.gmail.com/
> > > "we are trying to make users lives easier by having major distributions
> > > distribute bpftool and libbpf properly. Adding extra binaries to
> > > distribute around doesn't seem to be easing any of users pains."  
> > 
> > Last time we argued I heard how GH makes libbpf packaging easier.
> > Only to have that dis-proven once the people in Europe who do distro
> > packaging woke up:
> > 
> > https://lkml.org/lkml/2019/12/5/101
> > https://lkml.org/lkml/2019/12/5/312  
> 
> I think you missed the point of these two comments. It was about packaging
> bpftool and libbpf together. Regardless how bpftool is packaged. I still
> strongly suggest to use github/libbpf to package libbpf. It's something that is
> actually tested whereas libbpf in the kernel tree has unit test coverage only.

I disagree.

> > > My opinion is the following.
> > > bpftool is necessary to write bpf programs already. It's necessary to produce
> > > vmlinux.h for bpf programs to include it. It's part of build process. I can
> > > relate to Stan's complains that he needs to update clang and pahole. He missed
> > > the fact that he needs to update bpftool too if he wants to use all features of
> > > CO-RE. Same thing for skeleton generation. If people need to run the latest
> > > selftest/bpf on the latest kernel they need to upgrade to the latest clang,
> > > pahole, libbpf, bpftool. Nothing new here.  
> > 
> > They have to update libbpf, so why can't the code gen tool be part of
> > libbpf?   
> 
> I'm not sure why two answers were not enough.
> No idea how to answer this question differently for the third time.

I'm just presenting what I consider to be a cleaner solution.

> > > Backwards compat is the same concern for skeleton generation and for vmlinux.h
> > > generation. Obviously no one wants to introduce something that will keep
> > > changing. Is vmlinux.h generation stable? I like to believe so. Same with
> > > skeleton. I wouldn't want to see it changing, but in both cases such chance
> > > exists.   
> > 
> > vmlinux.h is pretty stable, there isn't much wiggle room there.  
> 
> Do you have experience working with vmlinux.h? I bet the answer is no.
> While we have and identified few things that needs improvement.
> They require vmlinux.h to be generated differently.
> 
> > It's more of a conversion tool, if you will.
> > 
> > Skeleton OTOH is supposed to make people's lives easier, so it's a
> > completely different beast. It should be malleable so that users can
> > improve and hack on it. Baking it into as system tool is counter
> > productive. Users should be able to grab the skel tool single-file
> > source and adjust for their project's needs. Distributing your own copy
> > of bpftool because you want to adjust skel is a heavy lift.  
> 
> Adjust generator for their custom needs? essentially fork it for
> private use? I'd rather prevent such possibility.
> When people start using it I'd prefer they come back to this mailing
> list with patches than do 'easy fork'.
> 
> > > Now consider if vmlinux.h and skeleton generation is split out of bpftool into
> > > new tool. Effectively it would mean a fork of bpftool. Two binaries doing bpf
> > > elf file processing without clear distinction between them is going to be very
> > > confusing.  
> > 
> > To be clear I'm suggesting skel gen is a separate tool, vmlinux and
> > Quentin's header gen work on the running system, they are not pure
> > build env tools.  
> 
> You meant to say Andrii's header generator that is based on Quentin's man page
> generator. Its output bpf_helper_defs.h makes sense as a part of libbpf
> package. The generator script itself doesn't need to be included with any package.
> bpftool vmlinux gen consumes vmlinux elf files and is a part of the build.
> bpftool skeleton gen consumes bpf elf files and is a part of the same build.

I said what I meant to say tools/bpf/bpftool/feature.c

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12 21:45                                         ` Stanislav Fomichev
@ 2019-12-13  6:23                                           ` Andrii Nakryiko
  0 siblings, 0 replies; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-13  6:23 UTC (permalink / raw)
  To: Stanislav Fomichev
  Cc: Alexei Starovoitov, Jakub Kicinski, Andrii Nakryiko, LKML, bpf,
	Networking, Alexei Starovoitov, Daniel Borkmann, Kernel Team

On Thu, Dec 12, 2019 at 1:46 PM Stanislav Fomichev <sdf@fomichev.me> wrote:
>
> On 12/12, Alexei Starovoitov wrote:
> > On Thu, Dec 12, 2019 at 10:43:34AM -0800, Jakub Kicinski wrote:
> > One more point from Stan's email:
> >
> > > You can replace "our build system" with some other project you care about,
> > > like systemd. They'd have the same problem with vendoring in recent enough
> >
> > we've been working with systemd folks for ~8 month to integrate libbpf into
> > their build that is using meson build system and their CI that is github based.
> > So we're well aware about systemd requirements for libbpf and friends.
> Just curious (searching on systemd github for bpftool/libbpf doesn't
> show up any code/issues): are you saying that there will be another ~8 months
> to bring in bpftool or that it's already being worked on as part of
> libbpf integration?
>
> > > bpftool or waiting for every distro to do it. And all this work is
> > > because you think that doing:
> > >
> > >        my_obj->rodata->my_var = 123;
> > >
> > > Is easier / more type safe than doing:
> > >        int *my_var = bpf_object__rodata_lookup(obj, "my_var");
> > >        *my_var = 123;
> >
> > Stan, you conveniently skipped error checking. It should have been:
> >     int *my_var = bpf_object__rodata_lookup(obj, "my_var");
> >     if (IS_ERROR_NULL(my_var))
> >         goto out_cleanup;
> >      *my_var = 123;
> Yeah, but you have a choice, right? You can choose to check the error
> and support old programs that don't export some global var and a new
> program that has it. Or you can skip the error checks and rely on null
> deref crash which is sometimes an option.

So your point is that between

A. Skeleton way:

my_obj->rodata->my_var = 123;

and

B. look up way:

int *my_var = bpf_object__rodata_lookup(obj, "my_var");
*my_var = 123;

B is **better**, because **if you drop error checking** (which
apparently is a choice within "Google distro") then it comes really
close to succinctness of skeleton, is that right?

Let's follow this through to the end with two pretty typical situations.

1. Someone renames my_var into my_var1.
  A. Skeleton: compilation error.
  B. Lookup: NULL deref, core dump. If there is helpful in-program
infra, it will dump stack trace and it should be pretty easy to
pinpoint which variable look up failed (provided binary is built with
debug info). If not - gdb is your friend, you'll figure this out
pretty quickly.

2. Someone changes the type of my_var from u64 to u32.
  A. Skeleton: compiler will warn on too large constant assignment,
but otherwise it's C, baby. But at least we are only going to write
(potentially truncated) 4 bytes.
  B. Lookup: we are happily overwriting 4 bytes of some other variable
(best case -- just padding), without anyone realizing this. Good if we
have exhaustive correctness testing for program logic.

I do think there is a clear winner, it just seems like we disagree which one.

>
> (might be not relevant with the introduction of EMBED_FILE which you
> seem to be using more and more; ideally, we still would like to be able to
> distribute bpf.o and userspace binary separately).

I don't even know what BPF_EMBED_OBJ has to do with skeleton, tbh.
BPF_EMBED_OBJ is just one of the ways to get contents of BPF object
file. You can just as well instantiate skeleton from separate .o file,
if you read its content in memory and create struct bpf_embed_data
with pointer to it (or just mmap() it, which would be even easier). If
this is typical case, we can generate an extra function to instantiate
skeleton from file, of course.

>
> > Take a look at Andrii's patch 13/15:
> > 5 files changed, 149 insertions(+), 249 deletions(-)
> > Those are simple selftests, yet code removal is huge. Bigger project benefits
> > even more.
> Excluding fentry/fexit tests (where new find_program_by_title+attach
> helper and mmap might help), it looks like the majority of those gains come
> from the fact that the patch in question doesn't do any error checking.
> You can drop all the CHECK() stuff for existing
> find_map_by_name/find_prog_by_name instead and get the same gains.

See above. The fact we are not doing error checking with skeleton is
because it can't fail. If programmer screwed up name of variable,
program, or variable -- that's compilation error! That's the reason we
don't check errors, not because it's a cowboy-style "You can choose to
check the error" approach.

>
> [as usual, feel free to ignore me, I don't want to keep flaming about
> it, but it's hard not to reply]

likewise

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-12 20:21                                         ` Jakub Kicinski
  2019-12-12 21:28                                           ` Alexei Starovoitov
@ 2019-12-13  6:48                                           ` Andrii Nakryiko
  2019-12-13 17:47                                             ` Jakub Kicinski
  1 sibling, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-13  6:48 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Alexei Starovoitov, Stanislav Fomichev, Andrii Nakryiko, LKML,
	bpf, Networking, Alexei Starovoitov, Daniel Borkmann,
	Kernel Team

On Thu, Dec 12, 2019 at 12:21 PM Jakub Kicinski
<jakub.kicinski@netronome.com> wrote:
>
> On Thu, 12 Dec 2019 11:54:16 -0800, Alexei Starovoitov wrote:
> > On Thu, Dec 12, 2019 at 10:43:34AM -0800, Jakub Kicinski wrote:
> > > On Thu, 12 Dec 2019 08:53:22 -0800, Andrii Nakryiko wrote:
> > > > > > > Btw, how hard it would be to do this generation with a new python
> > > > > > > script instead of bpftool? Something along the lines of
> > > > > > > scripts/bpf_helpers_doc.py that parses BTF and spits out this C header
> > > > > > > (shouldn't be that hard to write custom BTF parser in python, right)?
> > > > > > >
> > > > > >
> > > > > > Not impossible, but harder than I'd care to deal with. I certainly
> > > > > > don't want to re-implement a good chunk of ELF and BTF parsing (maps,
> > > > > > progs, in addition to datasec stuff). But "it's hard to use bpftool in
> > > > > > our build system" doesn't seem like good enough reason to do all that.
> > > > > You can replace "our build system" with some other project you care about,
> > > > > like systemd. They'd have the same problem with vendoring in recent enough
> > > > > bpftool or waiting for every distro to do it. And all this work is
> > > > > because you think that doing:
> > > > >
> > > > >         my_obj->rodata->my_var = 123;
> > > > >
> > > > > Is easier / more type safe than doing:
> > > > >         int *my_var = bpf_object__rodata_lookup(obj, "my_var");
> > > > >         *my_var = 123;
> > > >
> > > > Your arguments are confusing me. Did I say that we shouldn't add this
> > > > type of "dynamic" interface to variables? Or did I say that every
> > > > single BPF application has to adopt skeleton and bpftool? I made no
> > > > such claims and it seems like discussion is just based around where I
> > > > have to apply my time and efforts... You think it's not useful - don't
> > > > integrate bpftool into your build system, simple as that. Skeleton is
> > > > used for selftests, but it's up to maintainers to decide whether to
> > > > keep this, similar to all the BTF decisions.
> > >
> > > Since we have two people suggesting this functionality to be a separate
> > > tool could you please reconsider my arguments from two days ago?
> > >
> > >   There absolutely nothing this tool needs from [bpftool], no
> > >   JSON needed, no bpffs etc.
> >
> > To generate vmlinux.h bpftool doesn't need json and doesn't need bpffs.
>
> At least for header generation it pertains to the running system.
> And bpftool was (and still is AFAICT) about interacting with the BPF
> state on the running system.

As Alexei already mentioned, `bpftool btf dump file
<elf-file-with-BTF-section>` has as much to do with running system,
as, say, `readelf -x .BTF <elf-file-with-BTF-section>`. So if
bpftool's only goal is to interact with BPF state of running system,
as opposed to be, you know, the "all things BPF" multitool, then that
ship has sailed when we added `bpftool btf` subcommand, which I don't
remember hearing too much objections about at that time (which
happened quite a while ago, btw).

>
> > > It can be a separate tool like
> > >   libbpf-skel-gen or libbpf-c-skel or something, distributed with libbpf.
> > >   That way you can actually soften the backward compat. In case people
> > >   become dependent on it they can carry that little tool on their own.
> >
> > Jakub,
> >
> > Could you please consider Andrii's reply to your comment from two days ago:
> > https://lore.kernel.org/bpf/CAEf4BzbeZbmCTOOo2uQXjm0GL0WDu7aLN6fdUk18Nv2g0kfwVg@mail.gmail.com/
> > "we are trying to make users lives easier by having major distributions
> > distribute bpftool and libbpf properly. Adding extra binaries to
> > distribute around doesn't seem to be easing any of users pains."
>
> Last time we argued I heard how GH makes libbpf packaging easier.
> Only to have that dis-proven once the people in Europe who do distro
> packaging woke up:
>
> https://lkml.org/lkml/2019/12/5/101
> https://lkml.org/lkml/2019/12/5/312
>
> I feel I'm justified not to take your opinion on this as fact.
>
> > My opinion is the following.
> > bpftool is necessary to write bpf programs already. It's necessary to produce
> > vmlinux.h for bpf programs to include it. It's part of build process. I can
> > relate to Stan's complains that he needs to update clang and pahole. He missed
> > the fact that he needs to update bpftool too if he wants to use all features of
> > CO-RE. Same thing for skeleton generation. If people need to run the latest
> > selftest/bpf on the latest kernel they need to upgrade to the latest clang,
> > pahole, libbpf, bpftool. Nothing new here.
>
> They have to update libbpf, so why can't the code gen tool be part of
> libbpf? We don't need to build all BPF user space into one binary.
>
> > Backwards compat is the same concern for skeleton generation and for vmlinux.h
> > generation. Obviously no one wants to introduce something that will keep
> > changing. Is vmlinux.h generation stable? I like to believe so. Same with
> > skeleton. I wouldn't want to see it changing, but in both cases such chance
> > exists.
>
> vmlinux.h is pretty stable, there isn't much wiggle room there.
> It's more of a conversion tool, if you will.
>
> Skeleton OTOH is supposed to make people's lives easier, so it's a
> completely different beast. It should be malleable so that users can

Skeleton is also a conversion tool. From compiled BPF object file to
it's "C representation". Just like BTF-to-C converter takes BTF and
creates its C representation. Both make people's lives easier. They
are in the same boat: productivity-enhancing tools, not a system
introspection tools.

> improve and hack on it. Baking it into as system tool is counter
> productive. Users should be able to grab the skel tool single-file
> source and adjust for their project's needs. Distributing your own copy
> of bpftool because you want to adjust skel is a heavy lift.

Skeleton is auto-generated code, it's not supposed to be tweaked or
"adjusted" by hand. Because next time you do tiny change to your BPF
source code (just swap order of two global variables), skeleton
changes. If someone is not satisfied with the way skeleton generation
looks like, they should propose changes and contribute to common
algorithm. Or, of course, they can just go and re-implement it on
their own, if struct bpf_object_skeleton suits them still (which is
what libbpf works with). Then they can do it in Python, Go, Scala,
Java, Perl, whatnot. But somehow I doubt anyone would want to do that.

>
> And maybe one day we do have Python/Go/whatever bindings, and we can
> convert the skel tool to a higher level language with modern templating.

Because high-level implementation is going to be so much simpler and
shorter, really? Is it that complicated in C right now? What's the
real benefit of waiting to be able to do it in "higher level" language
beyond being the contrarian? Apart from \n\ (which is mostly hidden
from view), I don't think high-level templates are going to be much
more clean.

>
> > We cannot and should not adopt kernel-like ABI guarantees to user space
> > code. It will paralyze the development.
>
> Discussion for another time :)

If this "experimental" disclaimer is a real blocker for all of this, I
don't mind making it a public API right now. bpf_object_skeleton is
already designed to be backward/forward compatible with size of struct
itself and all the sub-structs recorded during initialization. I
didn't mean to create impression like this whole approach is so raw
and untried that it will most certainly break and we are still unsure
about it. It's not and it certainly improves set up code for
real-world applications. We might need to add some extra option here
and there, but the stuff that's there already will stay as is.

Would moving all the skeleton-related stuff into libbpf.h and
"stabilizing" it make all this more tolerable for you?

>
> > Now consider if vmlinux.h and skeleton generation is split out of bpftool into
> > new tool. Effectively it would mean a fork of bpftool. Two binaries doing bpf
> > elf file processing without clear distinction between them is going to be very
> > confusing.
>
> To be clear I'm suggesting skel gen is a separate tool, vmlinux and
> Quentin's header gen work on the running system, they are not pure
> build env tools.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-13  6:48                                           ` Andrii Nakryiko
@ 2019-12-13 17:47                                             ` Jakub Kicinski
  0 siblings, 0 replies; 57+ messages in thread
From: Jakub Kicinski @ 2019-12-13 17:47 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Alexei Starovoitov, Stanislav Fomichev, Andrii Nakryiko, LKML,
	bpf, Networking, Alexei Starovoitov, Daniel Borkmann,
	Kernel Team

On Thu, 12 Dec 2019 22:48:10 -0800, Andrii Nakryiko wrote:
> > improve and hack on it. Baking it into as system tool is counter
> > productive. Users should be able to grab the skel tool single-file
> > source and adjust for their project's needs. Distributing your own copy
> > of bpftool because you want to adjust skel is a heavy lift.  
> 
> Skeleton is auto-generated code, it's not supposed to be tweaked or
> "adjusted" by hand. 

Obviously not, I said adjusting the codegen tool, not the output.

> Because next time you do tiny change to your BPF
> source code (just swap order of two global variables), skeleton
> changes. If someone is not satisfied with the way skeleton generation
> looks like, they should propose changes and contribute to common
> algorithm. Or, of course, they can just go and re-implement it on
> their own, if struct bpf_object_skeleton suits them still (which is
> what libbpf works with). Then they can do it in Python, Go, Scala,
> Java, Perl, whatnot. But somehow I doubt anyone would want to do that.
> 
> > And maybe one day we do have Python/Go/whatever bindings, and we can
> > convert the skel tool to a higher level language with modern templating.  
> 
> Because high-level implementation is going to be so much simpler and
> shorter, really? Is it that complicated in C right now? What's the
> real benefit of waiting to be able to do it in "higher level" language
> beyond being the contrarian? 

I did not say wait, I said do C and convert to something else once easy.
You really gotta read responses more carefully :/

> Apart from \n\ (which is mostly hidden
> from view), I don't think high-level templates are going to be much
> more clean.
> 
> > > We cannot and should not adopt kernel-like ABI guarantees to user space
> > > code. It will paralyze the development.  
> >
> > Discussion for another time :)  
> 
> If this "experimental" disclaimer is a real blocker for all of this, I
> don't mind making it a public API right now. bpf_object_skeleton is
> already designed to be backward/forward compatible with size of struct
> itself and all the sub-structs recorded during initialization. I
> didn't mean to create impression like this whole approach is so raw
> and untried that it will most certainly break and we are still unsure
> about it. It's not and it certainly improves set up code for
> real-world applications. We might need to add some extra option here
> and there, but the stuff that's there already will stay as is.

As explained the experimental disclaimer is fairly useless and it gives
people precedent for maybe not caring as hard as they should about
ironing the details out before sending code upstream.

I think we can just add a switch or option for improved generation when
needed. You already check there are not extra trailing arguments so we
should be good.

> Would moving all the skeleton-related stuff into libbpf.h and
> "stabilizing" it make all this more tolerable for you?

I think I'm too tired if this to have an option any more.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-10  1:14 ` [PATCH bpf-next 11/15] bpftool: add skeleton codegen command Andrii Nakryiko
  2019-12-10  1:57   ` Jakub Kicinski
  2019-12-11 22:50   ` [Potential Spoof] " Martin Lau
@ 2019-12-16 14:16   ` Daniel Borkmann
  2019-12-16 18:53     ` Andrii Nakryiko
  2 siblings, 1 reply; 57+ messages in thread
From: Daniel Borkmann @ 2019-12-16 14:16 UTC (permalink / raw)
  To: Andrii Nakryiko; +Cc: bpf, netdev, ast, andrii.nakryiko, kernel-team

On Mon, Dec 09, 2019 at 05:14:34PM -0800, Andrii Nakryiko wrote:
> Add `bpftool gen skeleton` command, which takes in compiled BPF .o object file
> and dumps a BPF skeleton struct and related code to work with that skeleton.
> Skeleton itself is tailored to a specific structure of provided BPF object
> file, containing accessors (just plain struct fields) for every map and
> program, as well as dedicated space for bpf_links. If BPF program is using
> global variables, corresponding structure definitions of compatible memory
> layout are emitted as well, making it possible to initialize and subsequently
> read/update global variables values using simple and clear C syntax for
> accessing fields. This skeleton majorly improves usability of
> opening/loading/attaching of BPF object, as well as interacting with it
> throughout the lifetime of loaded BPF object.
> 
> Generated skeleton struct has the following structure:
> 
> struct <object-name> {
> 	/* used by libbpf's skeleton API */
> 	struct bpf_object_skeleton *skeleton;
> 	/* bpf_object for libbpf APIs */
> 	struct bpf_object *obj;
> 	struct {
> 		/* for every defined map in BPF object: */
> 		struct bpf_map *<map-name>;
> 	} maps;
> 	struct {
> 		/* for every program in BPF object: */
> 		struct bpf_program *<program-name>;
> 	} progs;
> 	struct {
> 		/* for every program in BPF object: */
> 		struct bpf_link *<program-name>;
> 	} links;
> 	/* for every present global data section: */
> 	struct <object-name>__<one of bss, data, or rodata> {
> 		/* memory layout of corresponding data section,
> 		 * with every defined variable represented as a struct field
> 		 * with exactly the same type, but without const/volatile
> 		 * modifiers, e.g.:
> 		 */
> 		 int *my_var_1;
> 		 ...
> 	} *<one of bss, data, or rodata>;
> };
> 
> This provides great usability improvements:
> - no need to look up maps and programs by name, instead just
>   my_obj->maps.my_map or my_obj->progs.my_prog would give necessary
>   bpf_map/bpf_program pointers, which user can pass to existing libbpf APIs;
> - pre-defined places for bpf_links, which will be automatically populated for
>   program types that libbpf knows how to attach automatically (currently
>   tracepoints, kprobe/kretprobe, raw tracepoint and tracing programs). On
>   tearing down skeleton, all active bpf_links will be destroyed (meaning BPF
>   programs will be detached, if they are attached). For cases in which libbpf
>   doesn't know how to auto-attach BPF program, user can manually create link
>   after loading skeleton and they will be auto-detached on skeleton
>   destruction:
> 
> 	my_obj->links.my_fancy_prog = bpf_program__attach_cgroup_whatever(
> 		my_obj->progs.my_fancy_prog, <whatever extra param);
> 
> - it's extremely easy and convenient to work with global data from userspace
>   now. Both for read-only and read/write variables, it's possible to
>   pre-initialize them before skeleton is loaded:
> 
> 	skel = my_obj__open(raw_embed_data);
> 	my_obj->rodata->my_var = 123;
> 	my_obj__load(skel); /* 123 will be initialization value for my_var */
> 
>   After load, if kernel supports mmap() for BPF arrays, user can still read
>   (and write for .bss and .data) variables values, but at that point it will
>   be directly mmap()-ed to BPF array, backing global variables. This allows to
>   seamlessly exchange data with BPF side. From userspace program's POV, all
>   the pointers and memory contents stay the same, but mapped kernel memory
>   changes to point to created map.
>   If kernel doesn't yet support mmap() for BPF arrays, it's still possible to
>   use those data section structs to pre-initialize .bss, .data, and .rodata,
>   but after load their pointers will be reset to NULL, allowing user code to
>   gracefully handle this condition, if necessary.
> 
> Given a big surface area, skeleton is kept as an experimental non-public
> API for now, until more feedback and real-world experience is collected.

Can you elaborate on the plan here? This is until v5.6 is out and hence a new
bpftool release implicitly where this becomes frozen / non-experimental?

There is also tools/bpf/bpftool/Documentation/bpftool-gen.rst missing. Given
you aim to collect more feedback (?), it would be appropriate to document
everything in there so users have a clue how to use it for getting started.

Also, I think at least some more clarification is needed in such document on
the following topics:

- libbpf and bpftool is both 'GPL-2.0-only' or 'BSD-2-Clause'. Given this
  is a code generator, what license is the `bpftool gen skeleton` result?
  In any case, should there also be a header comment emitted via do_skeleton()?

- Clear statement that this codegen is an alternative to regular libbpf
  API usage but that both are always kept feature-complete and hence not
  disadvantaged in one way or another (to rule out any uncertainties for
  users e.g. whether they now need to start rewriting their existing code
  etc); with purpose of the former (codgen) to simplify loader interaction.

Thanks,
Daniel

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-16 14:16   ` Daniel Borkmann
@ 2019-12-16 18:53     ` Andrii Nakryiko
  2019-12-17 13:59       ` Daniel Borkmann
  0 siblings, 1 reply; 57+ messages in thread
From: Andrii Nakryiko @ 2019-12-16 18:53 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: Andrii Nakryiko, bpf, Networking, Alexei Starovoitov, Kernel Team

On Mon, Dec 16, 2019 at 6:16 AM Daniel Borkmann <daniel@iogearbox.net> wrote:
>
> On Mon, Dec 09, 2019 at 05:14:34PM -0800, Andrii Nakryiko wrote:
> > Add `bpftool gen skeleton` command, which takes in compiled BPF .o object file
> > and dumps a BPF skeleton struct and related code to work with that skeleton.
> > Skeleton itself is tailored to a specific structure of provided BPF object
> > file, containing accessors (just plain struct fields) for every map and
> > program, as well as dedicated space for bpf_links. If BPF program is using
> > global variables, corresponding structure definitions of compatible memory
> > layout are emitted as well, making it possible to initialize and subsequently
> > read/update global variables values using simple and clear C syntax for
> > accessing fields. This skeleton majorly improves usability of
> > opening/loading/attaching of BPF object, as well as interacting with it
> > throughout the lifetime of loaded BPF object.
> >
> > Generated skeleton struct has the following structure:
> >
> > struct <object-name> {
> >       /* used by libbpf's skeleton API */
> >       struct bpf_object_skeleton *skeleton;
> >       /* bpf_object for libbpf APIs */
> >       struct bpf_object *obj;
> >       struct {
> >               /* for every defined map in BPF object: */
> >               struct bpf_map *<map-name>;
> >       } maps;
> >       struct {
> >               /* for every program in BPF object: */
> >               struct bpf_program *<program-name>;
> >       } progs;
> >       struct {
> >               /* for every program in BPF object: */
> >               struct bpf_link *<program-name>;
> >       } links;
> >       /* for every present global data section: */
> >       struct <object-name>__<one of bss, data, or rodata> {
> >               /* memory layout of corresponding data section,
> >                * with every defined variable represented as a struct field
> >                * with exactly the same type, but without const/volatile
> >                * modifiers, e.g.:
> >                */
> >                int *my_var_1;
> >                ...
> >       } *<one of bss, data, or rodata>;
> > };
> >
> > This provides great usability improvements:
> > - no need to look up maps and programs by name, instead just
> >   my_obj->maps.my_map or my_obj->progs.my_prog would give necessary
> >   bpf_map/bpf_program pointers, which user can pass to existing libbpf APIs;
> > - pre-defined places for bpf_links, which will be automatically populated for
> >   program types that libbpf knows how to attach automatically (currently
> >   tracepoints, kprobe/kretprobe, raw tracepoint and tracing programs). On
> >   tearing down skeleton, all active bpf_links will be destroyed (meaning BPF
> >   programs will be detached, if they are attached). For cases in which libbpf
> >   doesn't know how to auto-attach BPF program, user can manually create link
> >   after loading skeleton and they will be auto-detached on skeleton
> >   destruction:
> >
> >       my_obj->links.my_fancy_prog = bpf_program__attach_cgroup_whatever(
> >               my_obj->progs.my_fancy_prog, <whatever extra param);
> >
> > - it's extremely easy and convenient to work with global data from userspace
> >   now. Both for read-only and read/write variables, it's possible to
> >   pre-initialize them before skeleton is loaded:
> >
> >       skel = my_obj__open(raw_embed_data);
> >       my_obj->rodata->my_var = 123;
> >       my_obj__load(skel); /* 123 will be initialization value for my_var */
> >
> >   After load, if kernel supports mmap() for BPF arrays, user can still read
> >   (and write for .bss and .data) variables values, but at that point it will
> >   be directly mmap()-ed to BPF array, backing global variables. This allows to
> >   seamlessly exchange data with BPF side. From userspace program's POV, all
> >   the pointers and memory contents stay the same, but mapped kernel memory
> >   changes to point to created map.
> >   If kernel doesn't yet support mmap() for BPF arrays, it's still possible to
> >   use those data section structs to pre-initialize .bss, .data, and .rodata,
> >   but after load their pointers will be reset to NULL, allowing user code to
> >   gracefully handle this condition, if necessary.
> >
> > Given a big surface area, skeleton is kept as an experimental non-public
> > API for now, until more feedback and real-world experience is collected.
>
> Can you elaborate on the plan here? This is until v5.6 is out and hence a new
> bpftool release implicitly where this becomes frozen / non-experimental?

Yes, I've exposed all those interfaces as public, thus they are going
to stabilize with new release of libbpf/bpftool. I've received some
good usability feedback from Alexei after he tried it out locally, so
I'm going to adjust auto-generated part a bit. Libbpf APIs were
designed with extensibility built in, so we can extend them as any
other APIs, if need be.

>
> There is also tools/bpf/bpftool/Documentation/bpftool-gen.rst missing. Given
> you aim to collect more feedback (?), it would be appropriate to document
> everything in there so users have a clue how to use it for getting started.

sure, will add it

>
> Also, I think at least some more clarification is needed in such document on
> the following topics:
>
> - libbpf and bpftool is both 'GPL-2.0-only' or 'BSD-2-Clause'. Given this
>   is a code generator, what license is the `bpftool gen skeleton` result?
>   In any case, should there also be a header comment emitted via do_skeleton()?

Not a lawyer here, I assumed auto-generated code isn't copyrighted,
but how about I just emit SPDX header with the same license as libbpf
itself:

SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)


>
> - Clear statement that this codegen is an alternative to regular libbpf
>   API usage but that both are always kept feature-complete and hence not
>   disadvantaged in one way or another (to rule out any uncertainties for
>   users e.g. whether they now need to start rewriting their existing code
>   etc); with purpose of the former (codgen) to simplify loader interaction.

ok, will add that as well

>
> Thanks,
> Daniel

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-16 18:53     ` Andrii Nakryiko
@ 2019-12-17 13:59       ` Daniel Borkmann
  2019-12-17 15:45         ` Alexei Starovoitov
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Borkmann @ 2019-12-17 13:59 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Andrii Nakryiko, bpf, Networking, Alexei Starovoitov, Kernel Team

On 12/16/19 7:53 PM, Andrii Nakryiko wrote:
> On Mon, Dec 16, 2019 at 6:16 AM Daniel Borkmann <daniel@iogearbox.net> wrote:
>> On Mon, Dec 09, 2019 at 05:14:34PM -0800, Andrii Nakryiko wrote:
>>> Add `bpftool gen skeleton` command, which takes in compiled BPF .o object file
>>> and dumps a BPF skeleton struct and related code to work with that skeleton.
>>> Skeleton itself is tailored to a specific structure of provided BPF object
>>> file, containing accessors (just plain struct fields) for every map and
>>> program, as well as dedicated space for bpf_links. If BPF program is using
>>> global variables, corresponding structure definitions of compatible memory
>>> layout are emitted as well, making it possible to initialize and subsequently
>>> read/update global variables values using simple and clear C syntax for
>>> accessing fields. This skeleton majorly improves usability of
>>> opening/loading/attaching of BPF object, as well as interacting with it
>>> throughout the lifetime of loaded BPF object.
>>>
>>> Generated skeleton struct has the following structure:
>>>
>>> struct <object-name> {
>>>        /* used by libbpf's skeleton API */
>>>        struct bpf_object_skeleton *skeleton;
>>>        /* bpf_object for libbpf APIs */
>>>        struct bpf_object *obj;
>>>        struct {
>>>                /* for every defined map in BPF object: */
>>>                struct bpf_map *<map-name>;
>>>        } maps;
>>>        struct {
>>>                /* for every program in BPF object: */
>>>                struct bpf_program *<program-name>;
>>>        } progs;
>>>        struct {
>>>                /* for every program in BPF object: */
>>>                struct bpf_link *<program-name>;
>>>        } links;
>>>        /* for every present global data section: */
>>>        struct <object-name>__<one of bss, data, or rodata> {
>>>                /* memory layout of corresponding data section,
>>>                 * with every defined variable represented as a struct field
>>>                 * with exactly the same type, but without const/volatile
>>>                 * modifiers, e.g.:
>>>                 */
>>>                 int *my_var_1;
>>>                 ...
>>>        } *<one of bss, data, or rodata>;
>>> };
>>>
>>> This provides great usability improvements:
>>> - no need to look up maps and programs by name, instead just
>>>    my_obj->maps.my_map or my_obj->progs.my_prog would give necessary
>>>    bpf_map/bpf_program pointers, which user can pass to existing libbpf APIs;
>>> - pre-defined places for bpf_links, which will be automatically populated for
>>>    program types that libbpf knows how to attach automatically (currently
>>>    tracepoints, kprobe/kretprobe, raw tracepoint and tracing programs). On
>>>    tearing down skeleton, all active bpf_links will be destroyed (meaning BPF
>>>    programs will be detached, if they are attached). For cases in which libbpf
>>>    doesn't know how to auto-attach BPF program, user can manually create link
>>>    after loading skeleton and they will be auto-detached on skeleton
>>>    destruction:
>>>
>>>        my_obj->links.my_fancy_prog = bpf_program__attach_cgroup_whatever(
>>>                my_obj->progs.my_fancy_prog, <whatever extra param);
>>>
>>> - it's extremely easy and convenient to work with global data from userspace
>>>    now. Both for read-only and read/write variables, it's possible to
>>>    pre-initialize them before skeleton is loaded:
>>>
>>>        skel = my_obj__open(raw_embed_data);
>>>        my_obj->rodata->my_var = 123;
>>>        my_obj__load(skel); /* 123 will be initialization value for my_var */
>>>
>>>    After load, if kernel supports mmap() for BPF arrays, user can still read
>>>    (and write for .bss and .data) variables values, but at that point it will
>>>    be directly mmap()-ed to BPF array, backing global variables. This allows to
>>>    seamlessly exchange data with BPF side. From userspace program's POV, all
>>>    the pointers and memory contents stay the same, but mapped kernel memory
>>>    changes to point to created map.
>>>    If kernel doesn't yet support mmap() for BPF arrays, it's still possible to
>>>    use those data section structs to pre-initialize .bss, .data, and .rodata,
>>>    but after load their pointers will be reset to NULL, allowing user code to
>>>    gracefully handle this condition, if necessary.
>>>
>>> Given a big surface area, skeleton is kept as an experimental non-public
>>> API for now, until more feedback and real-world experience is collected.
>>
>> Can you elaborate on the plan here? This is until v5.6 is out and hence a new
>> bpftool release implicitly where this becomes frozen / non-experimental?
> 
> Yes, I've exposed all those interfaces as public, thus they are going
> to stabilize with new release of libbpf/bpftool. I've received some
> good usability feedback from Alexei after he tried it out locally, so
> I'm going to adjust auto-generated part a bit. Libbpf APIs were
> designed with extensibility built in, so we can extend them as any
> other APIs, if need be.
> 
>> There is also tools/bpf/bpftool/Documentation/bpftool-gen.rst missing. Given
>> you aim to collect more feedback (?), it would be appropriate to document
>> everything in there so users have a clue how to use it for getting started.
> 
> sure, will add it

Thanks!

>> Also, I think at least some more clarification is needed in such document on
>> the following topics:
>>
>> - libbpf and bpftool is both 'GPL-2.0-only' or 'BSD-2-Clause'. Given this
>>    is a code generator, what license is the `bpftool gen skeleton` result?
>>    In any case, should there also be a header comment emitted via do_skeleton()?
> 
> Not a lawyer here, I assumed auto-generated code isn't copyrighted,
> but how about I just emit SPDX header with the same license as libbpf
> itself:
> 
> SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)

Given this is mere output of the program and not derivative work of bpftool
itself, as in bpftool copying chunks of its own code into the generated one,
this should not need any restriction, but then you'd still need linking
against libbpf itself to make everything work.

>> - Clear statement that this codegen is an alternative to regular libbpf
>>    API usage but that both are always kept feature-complete and hence not
>>    disadvantaged in one way or another (to rule out any uncertainties for
>>    users e.g. whether they now need to start rewriting their existing code
>>    etc); with purpose of the former (codgen) to simplify loader interaction.
> 
> ok, will add that as well
> 
>>
>> Thanks,
>> Daniel


^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH bpf-next 11/15] bpftool: add skeleton codegen command
  2019-12-17 13:59       ` Daniel Borkmann
@ 2019-12-17 15:45         ` Alexei Starovoitov
  0 siblings, 0 replies; 57+ messages in thread
From: Alexei Starovoitov @ 2019-12-17 15:45 UTC (permalink / raw)
  To: Daniel Borkmann, Andrii Nakryiko
  Cc: Andrii Nakryiko, bpf, Networking, Kernel Team

On 12/17/19 5:59 AM, Daniel Borkmann wrote:
> Given this is mere output of the program and not derivative work of bpftool
> itself, as in bpftool copying chunks of its own code into the generated 
> one,
> this should not need any restriction, but then you'd still need linking
> against libbpf itself to make everything work.

btw generating stuff is the same as compilation. The license of the
tool that is doing it has no bearing on the generated output.
Linking with libbpf is different though, since it's a dependency of the 
output. Hence matching it to libbpf's license is the best.

^ permalink raw reply	[flat|nested] 57+ messages in thread

end of thread, other threads:[~2019-12-17 15:46 UTC | newest]

Thread overview: 57+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-10  1:14 [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 01/15] libbpf: don't require root for bpf_object__open() Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 02/15] libbpf: add generic bpf_program__attach() Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 03/15] libbpf: move non-public APIs from libbpf.h to libbpf_internal.h Andrii Nakryiko
2019-12-10  1:33   ` Jakub Kicinski
2019-12-10 17:04     ` Andrii Nakryiko
2019-12-10 18:17       ` Jakub Kicinski
2019-12-10 18:47         ` Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 04/15] libbpf: add BPF_EMBED_OBJ macro for embedding BPF .o files Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 05/15] libbpf: expose field/var declaration emitting API internally Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 06/15] libbpf: expose BPF program's function name Andrii Nakryiko
2019-12-11 19:38   ` [Potential Spoof] " Martin Lau
2019-12-11 19:54     ` Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 07/15] libbpf: refactor global data map initialization Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 08/15] libbpf: postpone BTF ID finding for TRACING programs to load phase Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 09/15] libbpf: reduce log level of supported section names dump Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 10/15] libbpf: add experimental BPF object skeleton support Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 11/15] bpftool: add skeleton codegen command Andrii Nakryiko
2019-12-10  1:57   ` Jakub Kicinski
2019-12-10 17:11     ` Andrii Nakryiko
2019-12-10 18:05       ` Jakub Kicinski
2019-12-10 18:56         ` Andrii Nakryiko
2019-12-10 21:44         ` Stanislav Fomichev
2019-12-10 22:33           ` Andrii Nakryiko
2019-12-10 22:59             ` Stanislav Fomichev
2019-12-11  7:07               ` Andrii Nakryiko
2019-12-11 17:24                 ` Stanislav Fomichev
2019-12-11 18:26                   ` Andrii Nakryiko
2019-12-11 19:15                     ` Stanislav Fomichev
2019-12-11 19:41                       ` Andrii Nakryiko
2019-12-11 20:09                         ` Stanislav Fomichev
2019-12-12  0:50                           ` Andrii Nakryiko
2019-12-12  2:57                             ` Stanislav Fomichev
2019-12-12  7:27                               ` Andrii Nakryiko
2019-12-12 16:29                                 ` Stanislav Fomichev
2019-12-12 16:53                                   ` Andrii Nakryiko
2019-12-12 18:43                                     ` Jakub Kicinski
2019-12-12 18:58                                       ` Stanislav Fomichev
2019-12-12 19:23                                         ` Jakub Kicinski
2019-12-12 19:54                                       ` Alexei Starovoitov
2019-12-12 20:21                                         ` Jakub Kicinski
2019-12-12 21:28                                           ` Alexei Starovoitov
2019-12-12 21:59                                             ` Jakub Kicinski
2019-12-13  6:48                                           ` Andrii Nakryiko
2019-12-13 17:47                                             ` Jakub Kicinski
2019-12-12 21:45                                         ` Stanislav Fomichev
2019-12-13  6:23                                           ` Andrii Nakryiko
2019-12-11 22:50   ` [Potential Spoof] " Martin Lau
2019-12-16 14:16   ` Daniel Borkmann
2019-12-16 18:53     ` Andrii Nakryiko
2019-12-17 13:59       ` Daniel Borkmann
2019-12-17 15:45         ` Alexei Starovoitov
2019-12-10  1:14 ` [PATCH bpf-next 12/15] selftests/bpf: add BPF skeletons selftests and convert attach_probe.c Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 13/15] selftests/bpf: convert few more selftest to skeletons Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 14/15] selftests/bpf: add test validating data section to struct convertion layout Andrii Nakryiko
2019-12-10  1:14 ` [PATCH bpf-next 15/15] bpftool: add `gen skeleton` BASH completions Andrii Nakryiko
2019-12-11 22:55 ` [PATCH bpf-next 00/15] Add code-generated BPF object skeleton support Martin Lau

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).