netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions
@ 2019-06-17 19:26 Andrii Nakryiko
  2019-06-17 19:26 ` [PATCH v2 bpf-next 01/11] libbpf: add common min/max macro to libbpf_internal.h Andrii Nakryiko
                   ` (11 more replies)
  0 siblings, 12 replies; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:26 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

This patch set implements initial version (as discussed at LSF/MM2019
conference) of a new way to specify BPF maps, relying on BTF type information,
which allows for easy extensibility, preserving forward and backward
compatibility. See details and examples in description for patch #6.

[0] contains an outline of follow up extensions to be added after this basic
set of features lands. They are useful by itself, but also allows to bring
libbpf to feature-parity with iproute2 BPF loader. That should open a path
forward for BPF loaders unification.

Patch #1 centralizes commonly used min/max macro in libbpf_internal.h.
Patch #2 extracts .BTF and .BTF.ext loading loging from elf_collect().
Patch #3 simplifies elf_collect() error-handling logic.
Patch #4 refactors map initialization logic into user-provided maps and global
data maps, in preparation to adding another way (BTF-defined maps).
Patch #5 adds support for map definitions in multiple ELF sections and
deprecates bpf_object__find_map_by_offset() API which doesn't appear to be
used anymore and makes assumption that all map definitions reside in single
ELF section.
Patch #6 splits BTF intialization from sanitization/loading into kernel to
preserve original BTF at the time of map initialization.
Patch #7 adds support for BTF-defined maps.
Patch #8 adds new test for BTF-defined map definition.
Patches #9-11 convert test BPF map definitions to use BTF way.

[0] https://lore.kernel.org/bpf/CAEf4BzbfdG2ub7gCi0OYqBrUoChVHWsmOntWAkJt47=FE+km+A@mail.gmail.com/

v1->v2:
- more BTF-sanity checks in parsing map definitions (Song);
- removed confusing usage of "attribute", switched to "field;
- split off elf_collect() refactor from btf loading refactor (Song);
- split selftests conversion into 3 patches (Stanislav):
  1. test already relying on BTF;
  2. tests w/ custom types as key/value (so benefiting from BTF);
  3. all the rest tests (integers as key/value, special maps w/o BTF support).
- smaller code improvements (Song);

rfc->v1:
- error out on unknown field by default (Stanislav, Jakub, Lorenz);
 
Andrii Nakryiko (11):
  libbpf: add common min/max macro to libbpf_internal.h
  libbpf: extract BTF loading logic
  libbpf: streamline ELF parsing error-handling
  libbpf: refactor map initialization
  libbpf: identify maps by section index in addition to offset
  libbpf: split initialization and loading of BTF
  libbpf: allow specifying map definitions using BTF
  selftests/bpf: add test for BTF-defined maps
  selftests/bpf: switch BPF_ANNOTATE_KV_PAIR tests to BTF-defined maps
  selftests/bpf: convert tests w/ custom values to BTF-defined maps
  selftests/bpf: convert remaining selftests to BTF-defined maps

 tools/lib/bpf/bpf.c                           |   7 +-
 tools/lib/bpf/bpf_prog_linfo.c                |   5 +-
 tools/lib/bpf/btf.c                           |   3 -
 tools/lib/bpf/btf.h                           |   1 +
 tools/lib/bpf/btf_dump.c                      |   3 -
 tools/lib/bpf/libbpf.c                        | 781 +++++++++++++-----
 tools/lib/bpf/libbpf_internal.h               |   7 +
 tools/testing/selftests/bpf/progs/bpf_flow.c  |  18 +-
 .../selftests/bpf/progs/get_cgroup_id_kern.c  |  18 +-
 .../testing/selftests/bpf/progs/netcnt_prog.c |  22 +-
 .../selftests/bpf/progs/sample_map_ret0.c     |  18 +-
 .../selftests/bpf/progs/socket_cookie_prog.c  |  11 +-
 .../bpf/progs/sockmap_verdict_prog.c          |  36 +-
 .../selftests/bpf/progs/test_btf_newkv.c      |  73 ++
 .../bpf/progs/test_get_stack_rawtp.c          |  27 +-
 .../selftests/bpf/progs/test_global_data.c    |  27 +-
 tools/testing/selftests/bpf/progs/test_l4lb.c |  45 +-
 .../selftests/bpf/progs/test_l4lb_noinline.c  |  45 +-
 .../selftests/bpf/progs/test_map_in_map.c     |  20 +-
 .../selftests/bpf/progs/test_map_lock.c       |  22 +-
 .../testing/selftests/bpf/progs/test_obj_id.c |   9 +-
 .../bpf/progs/test_select_reuseport_kern.c    |  45 +-
 .../bpf/progs/test_send_signal_kern.c         |  22 +-
 .../bpf/progs/test_skb_cgroup_id_kern.c       |   9 +-
 .../bpf/progs/test_sock_fields_kern.c         |  60 +-
 .../selftests/bpf/progs/test_spin_lock.c      |  33 +-
 .../bpf/progs/test_stacktrace_build_id.c      |  44 +-
 .../selftests/bpf/progs/test_stacktrace_map.c |  40 +-
 .../testing/selftests/bpf/progs/test_tc_edt.c |   9 +-
 .../bpf/progs/test_tcp_check_syncookie_kern.c |   9 +-
 .../selftests/bpf/progs/test_tcp_estats.c     |   9 +-
 .../selftests/bpf/progs/test_tcpbpf_kern.c    |  18 +-
 .../selftests/bpf/progs/test_tcpnotify_kern.c |  18 +-
 tools/testing/selftests/bpf/progs/test_xdp.c  |  18 +-
 .../selftests/bpf/progs/test_xdp_noinline.c   |  60 +-
 tools/testing/selftests/bpf/test_btf.c        |  10 +-
 .../selftests/bpf/test_queue_stack_map.h      |  20 +-
 .../testing/selftests/bpf/test_sockmap_kern.h |  72 +-
 38 files changed, 1199 insertions(+), 495 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/test_btf_newkv.c

-- 
2.17.1


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

* [PATCH v2 bpf-next 01/11] libbpf: add common min/max macro to libbpf_internal.h
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
@ 2019-06-17 19:26 ` Andrii Nakryiko
  2019-06-17 19:26 ` [PATCH v2 bpf-next 02/11] libbpf: extract BTF loading logic Andrii Nakryiko
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:26 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

Multiple files in libbpf redefine their own definitions for min/max.
Let's define them in libbpf_internal.h and use those everywhere.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Acked-by: Song Liu <songliubraving@fb.com>
---
 tools/lib/bpf/bpf.c             | 7 ++-----
 tools/lib/bpf/bpf_prog_linfo.c  | 5 +----
 tools/lib/bpf/btf.c             | 3 ---
 tools/lib/bpf/btf_dump.c        | 3 ---
 tools/lib/bpf/libbpf_internal.h | 7 +++++++
 5 files changed, 10 insertions(+), 15 deletions(-)

diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index 0d4b4fe10a84..c7d7993c44bb 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -26,10 +26,11 @@
 #include <memory.h>
 #include <unistd.h>
 #include <asm/unistd.h>
+#include <errno.h>
 #include <linux/bpf.h>
 #include "bpf.h"
 #include "libbpf.h"
-#include <errno.h>
+#include "libbpf_internal.h"
 
 /*
  * When building perf, unistd.h is overridden. __NR_bpf is
@@ -53,10 +54,6 @@
 # endif
 #endif
 
-#ifndef min
-#define min(x, y) ((x) < (y) ? (x) : (y))
-#endif
-
 static inline __u64 ptr_to_u64(const void *ptr)
 {
 	return (__u64) (unsigned long) ptr;
diff --git a/tools/lib/bpf/bpf_prog_linfo.c b/tools/lib/bpf/bpf_prog_linfo.c
index 6978314ea7f6..8c67561c93b0 100644
--- a/tools/lib/bpf/bpf_prog_linfo.c
+++ b/tools/lib/bpf/bpf_prog_linfo.c
@@ -6,10 +6,7 @@
 #include <linux/err.h>
 #include <linux/bpf.h>
 #include "libbpf.h"
-
-#ifndef min
-#define min(x, y) ((x) < (y) ? (x) : (y))
-#endif
+#include "libbpf_internal.h"
 
 struct bpf_prog_linfo {
 	void *raw_linfo;
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index b2478e98c367..467224feb43b 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -16,9 +16,6 @@
 #include "libbpf_internal.h"
 #include "hashmap.h"
 
-#define max(a, b) ((a) > (b) ? (a) : (b))
-#define min(a, b) ((a) < (b) ? (a) : (b))
-
 #define BTF_MAX_NR_TYPES 0x7fffffff
 #define BTF_MAX_STR_OFFSET 0x7fffffff
 
diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
index 4b22db77e2cc..7065bb5b2752 100644
--- a/tools/lib/bpf/btf_dump.c
+++ b/tools/lib/bpf/btf_dump.c
@@ -18,9 +18,6 @@
 #include "libbpf.h"
 #include "libbpf_internal.h"
 
-#define min(x, y) ((x) < (y) ? (x) : (y))
-#define max(x, y) ((x) < (y) ? (y) : (x))
-
 static const char PREFIXES[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t";
 static const size_t PREFIX_CNT = sizeof(PREFIXES) - 1;
 
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index 850f7bdec5cb..554a7856dc2d 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -23,6 +23,13 @@
 #define BTF_PARAM_ENC(name, type) (name), (type)
 #define BTF_VAR_SECINFO_ENC(type, offset, size) (type), (offset), (size)
 
+#ifndef min
+# define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+#ifndef max
+# define max(x, y) ((x) < (y) ? (y) : (x))
+#endif
+
 extern void libbpf_print(enum libbpf_print_level level,
 			 const char *format, ...)
 	__attribute__((format(printf, 2, 3)));
-- 
2.17.1


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

* [PATCH v2 bpf-next 02/11] libbpf: extract BTF loading logic
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
  2019-06-17 19:26 ` [PATCH v2 bpf-next 01/11] libbpf: add common min/max macro to libbpf_internal.h Andrii Nakryiko
@ 2019-06-17 19:26 ` Andrii Nakryiko
  2019-06-17 19:40   ` Song Liu
  2019-06-17 19:26 ` [PATCH v2 bpf-next 03/11] libbpf: streamline ELF parsing error-handling Andrii Nakryiko
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:26 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

As a preparetion fro adding BTF-based BPF map loading, extract .BTF and
.BTF.ext loading logic.

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

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index e725fa86b189..49d3a808e754 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1078,6 +1078,58 @@ static void bpf_object__sanitize_btf_ext(struct bpf_object *obj)
 	}
 }
 
+static int bpf_object__load_btf(struct bpf_object *obj,
+				Elf_Data *btf_data,
+				Elf_Data *btf_ext_data)
+{
+	int err = 0;
+
+	if (btf_data) {
+		obj->btf = btf__new(btf_data->d_buf, btf_data->d_size);
+		if (IS_ERR(obj->btf)) {
+			pr_warning("Error loading ELF section %s: %d.\n",
+				   BTF_ELF_SEC, err);
+			goto out;
+		}
+		err = btf__finalize_data(obj, obj->btf);
+		if (err) {
+			pr_warning("Error finalizing %s: %d.\n",
+				   BTF_ELF_SEC, err);
+			goto out;
+		}
+		bpf_object__sanitize_btf(obj);
+		err = btf__load(obj->btf);
+		if (err) {
+			pr_warning("Error loading %s into kernel: %d.\n",
+				   BTF_ELF_SEC, err);
+			goto out;
+		}
+	}
+	if (btf_ext_data) {
+		if (!obj->btf) {
+			pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
+				 BTF_EXT_ELF_SEC, BTF_ELF_SEC);
+			goto out;
+		}
+		obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
+					    btf_ext_data->d_size);
+		if (IS_ERR(obj->btf_ext)) {
+			pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
+				   BTF_EXT_ELF_SEC, PTR_ERR(obj->btf_ext));
+			obj->btf_ext = NULL;
+			goto out;
+		}
+		bpf_object__sanitize_btf_ext(obj);
+	}
+out:
+	if (err || IS_ERR(obj->btf)) {
+		if (!IS_ERR_OR_NULL(obj->btf))
+			btf__free(obj->btf);
+		obj->btf = NULL;
+	}
+	return 0;
+}
+
 static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 {
 	Elf *elf = obj->efile.elf;
@@ -1212,44 +1264,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 		pr_warning("Corrupted ELF file: index of strtab invalid\n");
 		return -LIBBPF_ERRNO__FORMAT;
 	}
-	if (btf_data) {
-		obj->btf = btf__new(btf_data->d_buf, btf_data->d_size);
-		if (IS_ERR(obj->btf)) {
-			pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
-				   BTF_ELF_SEC, PTR_ERR(obj->btf));
-			obj->btf = NULL;
-		} else {
-			err = btf__finalize_data(obj, obj->btf);
-			if (!err) {
-				bpf_object__sanitize_btf(obj);
-				err = btf__load(obj->btf);
-			}
-			if (err) {
-				pr_warning("Error finalizing and loading %s into kernel: %d. Ignored and continue.\n",
-					   BTF_ELF_SEC, err);
-				btf__free(obj->btf);
-				obj->btf = NULL;
-				err = 0;
-			}
-		}
-	}
-	if (btf_ext_data) {
-		if (!obj->btf) {
-			pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
-				 BTF_EXT_ELF_SEC, BTF_ELF_SEC);
-		} else {
-			obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
-						    btf_ext_data->d_size);
-			if (IS_ERR(obj->btf_ext)) {
-				pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
-					   BTF_EXT_ELF_SEC,
-					   PTR_ERR(obj->btf_ext));
-				obj->btf_ext = NULL;
-			} else {
-				bpf_object__sanitize_btf_ext(obj);
-			}
-		}
-	}
+	err = bpf_object__load_btf(obj, btf_data, btf_ext_data);
+	if (err)
+		return err;
 	if (bpf_object__has_maps(obj)) {
 		err = bpf_object__init_maps(obj, flags);
 		if (err)
-- 
2.17.1


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

* [PATCH v2 bpf-next 03/11] libbpf: streamline ELF parsing error-handling
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
  2019-06-17 19:26 ` [PATCH v2 bpf-next 01/11] libbpf: add common min/max macro to libbpf_internal.h Andrii Nakryiko
  2019-06-17 19:26 ` [PATCH v2 bpf-next 02/11] libbpf: extract BTF loading logic Andrii Nakryiko
@ 2019-06-17 19:26 ` Andrii Nakryiko
  2019-06-17 19:46   ` Song Liu
  2019-06-17 19:26 ` [PATCH v2 bpf-next 04/11] libbpf: refactor map initialization Andrii Nakryiko
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:26 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

Simplify ELF parsing logic by exiting early, as there is no common clean
up path to execute. That makes it unnecessary to track when err was set
and when it was cleared. It also reduces nesting in some places.

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

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 49d3a808e754..7ee44d8877c5 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1154,24 +1154,21 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 		if (gelf_getshdr(scn, &sh) != &sh) {
 			pr_warning("failed to get section(%d) header from %s\n",
 				   idx, obj->path);
-			err = -LIBBPF_ERRNO__FORMAT;
-			goto out;
+			return -LIBBPF_ERRNO__FORMAT;
 		}
 
 		name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name);
 		if (!name) {
 			pr_warning("failed to get section(%d) name from %s\n",
 				   idx, obj->path);
-			err = -LIBBPF_ERRNO__FORMAT;
-			goto out;
+			return -LIBBPF_ERRNO__FORMAT;
 		}
 
 		data = elf_getdata(scn, 0);
 		if (!data) {
 			pr_warning("failed to get section(%d) data from %s(%s)\n",
 				   idx, name, obj->path);
-			err = -LIBBPF_ERRNO__FORMAT;
-			goto out;
+			return -LIBBPF_ERRNO__FORMAT;
 		}
 		pr_debug("section(%d) %s, size %ld, link %d, flags %lx, type=%d\n",
 			 idx, name, (unsigned long)data->d_size,
@@ -1182,10 +1179,14 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 			err = bpf_object__init_license(obj,
 						       data->d_buf,
 						       data->d_size);
+			if (err)
+				return err;
 		} else if (strcmp(name, "version") == 0) {
 			err = bpf_object__init_kversion(obj,
 							data->d_buf,
 							data->d_size);
+			if (err)
+				return err;
 		} else if (strcmp(name, "maps") == 0) {
 			obj->efile.maps_shndx = idx;
 		} else if (strcmp(name, BTF_ELF_SEC) == 0) {
@@ -1196,11 +1197,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 			if (obj->efile.symbols) {
 				pr_warning("bpf: multiple SYMTAB in %s\n",
 					   obj->path);
-				err = -LIBBPF_ERRNO__FORMAT;
-			} else {
-				obj->efile.symbols = data;
-				obj->efile.strtabidx = sh.sh_link;
+				return -LIBBPF_ERRNO__FORMAT;
 			}
+			obj->efile.symbols = data;
+			obj->efile.strtabidx = sh.sh_link;
 		} else if (sh.sh_type == SHT_PROGBITS && data->d_size > 0) {
 			if (sh.sh_flags & SHF_EXECINSTR) {
 				if (strcmp(name, ".text") == 0)
@@ -1214,6 +1214,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 
 					pr_warning("failed to alloc program %s (%s): %s",
 						   name, obj->path, cp);
+					return err;
 				}
 			} else if (strcmp(name, ".data") == 0) {
 				obj->efile.data = data;
@@ -1225,8 +1226,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 				pr_debug("skip section(%d) %s\n", idx, name);
 			}
 		} else if (sh.sh_type == SHT_REL) {
+			int nr_reloc = obj->efile.nr_reloc;
 			void *reloc = obj->efile.reloc;
-			int nr_reloc = obj->efile.nr_reloc + 1;
 			int sec = sh.sh_info; /* points to other section */
 
 			/* Only do relo for section with exec instructions */
@@ -1236,28 +1237,24 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 				continue;
 			}
 
-			reloc = reallocarray(reloc, nr_reloc,
+			reloc = reallocarray(reloc, nr_reloc + 1,
 					     sizeof(*obj->efile.reloc));
 			if (!reloc) {
 				pr_warning("realloc failed\n");
-				err = -ENOMEM;
-			} else {
-				int n = nr_reloc - 1;
+				return -ENOMEM;
+			}
 
-				obj->efile.reloc = reloc;
-				obj->efile.nr_reloc = nr_reloc;
+			obj->efile.reloc = reloc;
+			obj->efile.nr_reloc++;
 
-				obj->efile.reloc[n].shdr = sh;
-				obj->efile.reloc[n].data = data;
-			}
+			obj->efile.reloc[nr_reloc].shdr = sh;
+			obj->efile.reloc[nr_reloc].data = data;
 		} else if (sh.sh_type == SHT_NOBITS && strcmp(name, ".bss") == 0) {
 			obj->efile.bss = data;
 			obj->efile.bss_shndx = idx;
 		} else {
 			pr_debug("skip section(%d) %s\n", idx, name);
 		}
-		if (err)
-			goto out;
 	}
 
 	if (!obj->efile.strtabidx || obj->efile.strtabidx >= idx) {
@@ -1270,10 +1267,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 	if (bpf_object__has_maps(obj)) {
 		err = bpf_object__init_maps(obj, flags);
 		if (err)
-			goto out;
+			return err;
 	}
 	err = bpf_object__init_prog_names(obj);
-out:
 	return err;
 }
 
-- 
2.17.1


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

* [PATCH v2 bpf-next 04/11] libbpf: refactor map initialization
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
                   ` (2 preceding siblings ...)
  2019-06-17 19:26 ` [PATCH v2 bpf-next 03/11] libbpf: streamline ELF parsing error-handling Andrii Nakryiko
@ 2019-06-17 19:26 ` Andrii Nakryiko
  2019-06-17 19:39   ` Song Liu
  2019-06-26 14:48   ` Matt Hart
  2019-06-17 19:26 ` [PATCH v2 bpf-next 05/11] libbpf: identify maps by section index in addition to offset Andrii Nakryiko
                   ` (7 subsequent siblings)
  11 siblings, 2 replies; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:26 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

User and global data maps initialization has gotten pretty complicated
and unnecessarily convoluted. This patch splits out the logic for global
data map and user-defined map initialization. It also removes the
restriction of pre-calculating how many maps will be initialized,
instead allowing to keep adding new maps as they are discovered, which
will be used later for BTF-defined map definitions.

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

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 7ee44d8877c5..88609dca4f7d 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -234,6 +234,7 @@ struct bpf_object {
 	size_t nr_programs;
 	struct bpf_map *maps;
 	size_t nr_maps;
+	size_t maps_cap;
 	struct bpf_secdata sections;
 
 	bool loaded;
@@ -763,21 +764,51 @@ int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
 	return -ENOENT;
 }
 
-static bool bpf_object__has_maps(const struct bpf_object *obj)
+static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
 {
-	return obj->efile.maps_shndx >= 0 ||
-	       obj->efile.data_shndx >= 0 ||
-	       obj->efile.rodata_shndx >= 0 ||
-	       obj->efile.bss_shndx >= 0;
+	struct bpf_map *new_maps;
+	size_t new_cap;
+	int i;
+
+	if (obj->nr_maps < obj->maps_cap)
+		return &obj->maps[obj->nr_maps++];
+
+	new_cap = max(4ul, obj->maps_cap * 3 / 2);
+	new_maps = realloc(obj->maps, new_cap * sizeof(*obj->maps));
+	if (!new_maps) {
+		pr_warning("alloc maps for object failed\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	obj->maps_cap = new_cap;
+	obj->maps = new_maps;
+
+	/* zero out new maps */
+	memset(obj->maps + obj->nr_maps, 0,
+	       (obj->maps_cap - obj->nr_maps) * sizeof(*obj->maps));
+	/*
+	 * fill all fd with -1 so won't close incorrect fd (fd=0 is stdin)
+	 * when failure (zclose won't close negative fd)).
+	 */
+	for (i = obj->nr_maps; i < obj->maps_cap; i++) {
+		obj->maps[i].fd = -1;
+		obj->maps[i].inner_map_fd = -1;
+	}
+
+	return &obj->maps[obj->nr_maps++];
 }
 
 static int
-bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
-			      enum libbpf_map_type type, Elf_Data *data,
-			      void **data_buff)
+bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
+			      Elf_Data *data, void **data_buff)
 {
-	struct bpf_map_def *def = &map->def;
 	char map_name[BPF_OBJ_NAME_LEN];
+	struct bpf_map_def *def;
+	struct bpf_map *map;
+
+	map = bpf_object__add_map(obj);
+	if (IS_ERR(map))
+		return PTR_ERR(map);
 
 	map->libbpf_type = type;
 	map->offset = ~(typeof(map->offset))0;
@@ -789,6 +820,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
 		return -ENOMEM;
 	}
 
+	def = &map->def;
 	def->type = BPF_MAP_TYPE_ARRAY;
 	def->key_size = sizeof(int);
 	def->value_size = data->d_size;
@@ -808,29 +840,58 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
 	return 0;
 }
 
-static int bpf_object__init_maps(struct bpf_object *obj, int flags)
+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.
+	 */
+	if (obj->efile.data_shndx >= 0) {
+		err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA,
+						    obj->efile.data,
+						    &obj->sections.data);
+		if (err)
+			return err;
+	}
+	if (obj->efile.rodata_shndx >= 0) {
+		err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA,
+						    obj->efile.rodata,
+						    &obj->sections.rodata);
+		if (err)
+			return err;
+	}
+	if (obj->efile.bss_shndx >= 0) {
+		err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS,
+						    obj->efile.bss, NULL);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
 {
-	int i, map_idx, map_def_sz = 0, nr_syms, nr_maps = 0, nr_maps_glob = 0;
-	bool strict = !(flags & MAPS_RELAX_COMPAT);
 	Elf_Data *symbols = obj->efile.symbols;
+	int i, map_def_sz = 0, nr_maps = 0, nr_syms;
 	Elf_Data *data = NULL;
-	int ret = 0;
+	Elf_Scn *scn;
+
+	if (obj->efile.maps_shndx < 0)
+		return 0;
 
 	if (!symbols)
 		return -EINVAL;
-	nr_syms = symbols->d_size / sizeof(GElf_Sym);
-
-	if (obj->efile.maps_shndx >= 0) {
-		Elf_Scn *scn = elf_getscn(obj->efile.elf,
-					  obj->efile.maps_shndx);
 
-		if (scn)
-			data = elf_getdata(scn, NULL);
-		if (!scn || !data) {
-			pr_warning("failed to get Elf_Data from map section %d\n",
-				   obj->efile.maps_shndx);
-			return -EINVAL;
-		}
+	scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx);
+	if (scn)
+		data = elf_getdata(scn, NULL);
+	if (!scn || !data) {
+		pr_warning("failed to get Elf_Data from map section %d\n",
+			   obj->efile.maps_shndx);
+		return -EINVAL;
 	}
 
 	/*
@@ -840,16 +901,8 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
 	 *
 	 * TODO: Detect array of map and report error.
 	 */
-	if (obj->caps.global_data) {
-		if (obj->efile.data_shndx >= 0)
-			nr_maps_glob++;
-		if (obj->efile.rodata_shndx >= 0)
-			nr_maps_glob++;
-		if (obj->efile.bss_shndx >= 0)
-			nr_maps_glob++;
-	}
-
-	for (i = 0; data && i < nr_syms; i++) {
+	nr_syms = symbols->d_size / sizeof(GElf_Sym);
+	for (i = 0; i < nr_syms; i++) {
 		GElf_Sym sym;
 
 		if (!gelf_getsym(symbols, i, &sym))
@@ -858,79 +911,56 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
 			continue;
 		nr_maps++;
 	}
-
-	if (!nr_maps && !nr_maps_glob)
-		return 0;
-
 	/* Assume equally sized map definitions */
-	if (data) {
-		pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path,
-			 nr_maps, data->d_size);
-
-		map_def_sz = data->d_size / nr_maps;
-		if (!data->d_size || (data->d_size % nr_maps) != 0) {
-			pr_warning("unable to determine map definition size "
-				   "section %s, %d maps in %zd bytes\n",
-				   obj->path, nr_maps, data->d_size);
-			return -EINVAL;
-		}
-	}
-
-	nr_maps += nr_maps_glob;
-	obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
-	if (!obj->maps) {
-		pr_warning("alloc maps for object failed\n");
-		return -ENOMEM;
-	}
-	obj->nr_maps = nr_maps;
-
-	for (i = 0; i < nr_maps; i++) {
-		/*
-		 * fill all fd with -1 so won't close incorrect
-		 * fd (fd=0 is stdin) when failure (zclose won't close
-		 * negative fd)).
-		 */
-		obj->maps[i].fd = -1;
-		obj->maps[i].inner_map_fd = -1;
+	pr_debug("maps in %s: %d maps in %zd bytes\n",
+		 obj->path, nr_maps, data->d_size);
+
+	map_def_sz = data->d_size / nr_maps;
+	if (!data->d_size || (data->d_size % nr_maps) != 0) {
+		pr_warning("unable to determine map definition size "
+			   "section %s, %d maps in %zd bytes\n",
+			   obj->path, nr_maps, data->d_size);
+		return -EINVAL;
 	}
 
-	/*
-	 * Fill obj->maps using data in "maps" section.
-	 */
-	for (i = 0, map_idx = 0; data && i < nr_syms; i++) {
+	/* Fill obj->maps using data in "maps" section.  */
+	for (i = 0; i < nr_syms; i++) {
 		GElf_Sym sym;
 		const char *map_name;
 		struct bpf_map_def *def;
+		struct bpf_map *map;
 
 		if (!gelf_getsym(symbols, i, &sym))
 			continue;
 		if (sym.st_shndx != obj->efile.maps_shndx)
 			continue;
 
-		map_name = elf_strptr(obj->efile.elf,
-				      obj->efile.strtabidx,
+		map = bpf_object__add_map(obj);
+		if (IS_ERR(map))
+			return PTR_ERR(map);
+
+		map_name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
 				      sym.st_name);
 		if (!map_name) {
 			pr_warning("failed to get map #%d name sym string for obj %s\n",
-				   map_idx, obj->path);
+				   i, obj->path);
 			return -LIBBPF_ERRNO__FORMAT;
 		}
 
-		obj->maps[map_idx].libbpf_type = LIBBPF_MAP_UNSPEC;
-		obj->maps[map_idx].offset = sym.st_value;
+		map->libbpf_type = LIBBPF_MAP_UNSPEC;
+		map->offset = sym.st_value;
 		if (sym.st_value + map_def_sz > data->d_size) {
 			pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
 				   obj->path, map_name);
 			return -EINVAL;
 		}
 
-		obj->maps[map_idx].name = strdup(map_name);
-		if (!obj->maps[map_idx].name) {
+		map->name = strdup(map_name);
+		if (!map->name) {
 			pr_warning("failed to alloc map name\n");
 			return -ENOMEM;
 		}
-		pr_debug("map %d is \"%s\"\n", map_idx,
-			 obj->maps[map_idx].name);
+		pr_debug("map %d is \"%s\"\n", i, map->name);
 		def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
 		/*
 		 * If the definition of the map in the object file fits in
@@ -939,7 +969,7 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
 		 * calloc above.
 		 */
 		if (map_def_sz <= sizeof(struct bpf_map_def)) {
-			memcpy(&obj->maps[map_idx].def, def, map_def_sz);
+			memcpy(&map->def, def, map_def_sz);
 		} else {
 			/*
 			 * Here the map structure being read is bigger than what
@@ -959,37 +989,30 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
 						return -EINVAL;
 				}
 			}
-			memcpy(&obj->maps[map_idx].def, def,
-			       sizeof(struct bpf_map_def));
+			memcpy(&map->def, def, sizeof(struct bpf_map_def));
 		}
-		map_idx++;
 	}
+	return 0;
+}
 
-	if (!obj->caps.global_data)
-		goto finalize;
+static int bpf_object__init_maps(struct bpf_object *obj, int flags)
+{
+	bool strict = !(flags & MAPS_RELAX_COMPAT);
+	int err;
 
-	/*
-	 * Populate rest of obj->maps with libbpf internal maps.
-	 */
-	if (obj->efile.data_shndx >= 0)
-		ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
-						    LIBBPF_MAP_DATA,
-						    obj->efile.data,
-						    &obj->sections.data);
-	if (!ret && obj->efile.rodata_shndx >= 0)
-		ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
-						    LIBBPF_MAP_RODATA,
-						    obj->efile.rodata,
-						    &obj->sections.rodata);
-	if (!ret && obj->efile.bss_shndx >= 0)
-		ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
-						    LIBBPF_MAP_BSS,
-						    obj->efile.bss, NULL);
-finalize:
-	if (!ret)
+	err = bpf_object__init_user_maps(obj, strict);
+	if (err)
+		return err;
+
+	err = bpf_object__init_global_data_maps(obj);
+	if (err)
+		return err;
+
+	if (obj->nr_maps) {
 		qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]),
 		      compare_bpf_map);
-	return ret;
+	}
+	return 0;
 }
 
 static bool section_have_execinstr(struct bpf_object *obj, int idx)
@@ -1262,14 +1285,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 		return -LIBBPF_ERRNO__FORMAT;
 	}
 	err = bpf_object__load_btf(obj, btf_data, btf_ext_data);
-	if (err)
-		return err;
-	if (bpf_object__has_maps(obj)) {
+	if (!err)
 		err = bpf_object__init_maps(obj, flags);
-		if (err)
-			return err;
-	}
-	err = bpf_object__init_prog_names(obj);
+	if (!err)
+		err = bpf_object__init_prog_names(obj);
 	return err;
 }
 
-- 
2.17.1


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

* [PATCH v2 bpf-next 05/11] libbpf: identify maps by section index in addition to offset
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
                   ` (3 preceding siblings ...)
  2019-06-17 19:26 ` [PATCH v2 bpf-next 04/11] libbpf: refactor map initialization Andrii Nakryiko
@ 2019-06-17 19:26 ` Andrii Nakryiko
  2019-06-17 19:26 ` [PATCH v2 bpf-next 06/11] libbpf: split initialization and loading of BTF Andrii Nakryiko
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:26 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

To support maps to be defined in multiple sections, it's important to
identify map not just by offset within its section, but section index as
well. This patch adds tracking of section index.

For global data, we record section index of corresponding
.data/.bss/.rodata ELF section for uniformity, and thus don't need
a special value of offset for those maps.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Acked-by: Song Liu <songliubraving@fb.com>
---
 tools/lib/bpf/libbpf.c | 40 +++++++++++++++++++++++++---------------
 1 file changed, 25 insertions(+), 15 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 88609dca4f7d..b1f3ab4b39b3 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -207,7 +207,8 @@ static const char * const libbpf_type_to_btf_name[] = {
 struct bpf_map {
 	int fd;
 	char *name;
-	size_t offset;
+	int sec_idx;
+	size_t sec_offset;
 	int map_ifindex;
 	int inner_map_fd;
 	struct bpf_map_def def;
@@ -647,7 +648,9 @@ static int compare_bpf_map(const void *_a, const void *_b)
 	const struct bpf_map *a = _a;
 	const struct bpf_map *b = _b;
 
-	return a->offset - b->offset;
+	if (a->sec_idx != b->sec_idx)
+		return a->sec_idx - b->sec_idx;
+	return a->sec_offset - b->sec_offset;
 }
 
 static bool bpf_map_type__is_map_in_map(enum bpf_map_type type)
@@ -800,7 +803,7 @@ static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
 
 static int
 bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
-			      Elf_Data *data, void **data_buff)
+			      int sec_idx, Elf_Data *data, void **data_buff)
 {
 	char map_name[BPF_OBJ_NAME_LEN];
 	struct bpf_map_def *def;
@@ -811,7 +814,8 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
 		return PTR_ERR(map);
 
 	map->libbpf_type = type;
-	map->offset = ~(typeof(map->offset))0;
+	map->sec_idx = sec_idx;
+	map->sec_offset = 0;
 	snprintf(map_name, sizeof(map_name), "%.8s%.7s", obj->name,
 		 libbpf_type_to_btf_name[type]);
 	map->name = strdup(map_name);
@@ -819,6 +823,8 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
 		pr_warning("failed to alloc map name\n");
 		return -ENOMEM;
 	}
+	pr_debug("map '%s' (global data): at sec_idx %d, offset %zu.\n",
+		 map_name, map->sec_idx, map->sec_offset);
 
 	def = &map->def;
 	def->type = BPF_MAP_TYPE_ARRAY;
@@ -851,6 +857,7 @@ 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);
 		if (err)
@@ -858,6 +865,7 @@ static int bpf_object__init_global_data_maps(struct bpf_object *obj)
 	}
 	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);
 		if (err)
@@ -865,6 +873,7 @@ static int bpf_object__init_global_data_maps(struct bpf_object *obj)
 	}
 	if (obj->efile.bss_shndx >= 0) {
 		err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS,
+						    obj->efile.bss_shndx,
 						    obj->efile.bss, NULL);
 		if (err)
 			return err;
@@ -948,7 +957,10 @@ static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
 		}
 
 		map->libbpf_type = LIBBPF_MAP_UNSPEC;
-		map->offset = sym.st_value;
+		map->sec_idx = sym.st_shndx;
+		map->sec_offset = sym.st_value;
+		pr_debug("map '%s' (legacy): at sec_idx %d, offset %zu.\n",
+			 map_name, map->sec_idx, map->sec_offset);
 		if (sym.st_value + map_def_sz > data->d_size) {
 			pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
 				   obj->path, map_name);
@@ -1448,9 +1460,13 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
 				if (maps[map_idx].libbpf_type != type)
 					continue;
 				if (type != LIBBPF_MAP_UNSPEC ||
-				    maps[map_idx].offset == sym.st_value) {
-					pr_debug("relocation: find map %zd (%s) for insn %u\n",
-						 map_idx, maps[map_idx].name, insn_idx);
+				    (maps[map_idx].sec_idx == sym.st_shndx &&
+				     maps[map_idx].sec_offset == sym.st_value)) {
+					pr_debug("relocation: found map %zd (%s, sec_idx %d, offset %zu) for insn %u\n",
+						 map_idx, maps[map_idx].name,
+						 maps[map_idx].sec_idx,
+						 maps[map_idx].sec_offset,
+						 insn_idx);
 					break;
 				}
 			}
@@ -3468,13 +3484,7 @@ bpf_object__find_map_fd_by_name(struct bpf_object *obj, const char *name)
 struct bpf_map *
 bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset)
 {
-	int i;
-
-	for (i = 0; i < obj->nr_maps; i++) {
-		if (obj->maps[i].offset == offset)
-			return &obj->maps[i];
-	}
-	return ERR_PTR(-ENOENT);
+	return ERR_PTR(-ENOTSUP);
 }
 
 long libbpf_get_error(const void *ptr)
-- 
2.17.1


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

* [PATCH v2 bpf-next 06/11] libbpf: split initialization and loading of BTF
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
                   ` (4 preceding siblings ...)
  2019-06-17 19:26 ` [PATCH v2 bpf-next 05/11] libbpf: identify maps by section index in addition to offset Andrii Nakryiko
@ 2019-06-17 19:26 ` Andrii Nakryiko
  2019-06-17 19:26 ` [PATCH v2 bpf-next 07/11] libbpf: allow specifying map definitions using BTF Andrii Nakryiko
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:26 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

Libbpf does sanitization of BTF before loading it into kernel, if kernel
doesn't support some of newer BTF features. This removes some of the
important information from BTF (e.g., DATASEC and VAR description),
which will be used for map construction. This patch splits BTF
processing into initialization step, in which BTF is initialized from
ELF and all the original data is still preserved; and
sanitization/loading step, which ensures that BTF is safe to load into
kernel. This allows to use full BTF information to construct maps, while
still loading valid BTF into older kernels.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Acked-by: Song Liu <songliubraving@fb.com>
---
 tools/lib/bpf/libbpf.c | 34 ++++++++++++++++++++++++----------
 1 file changed, 24 insertions(+), 10 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index b1f3ab4b39b3..da942ab2f06a 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1113,7 +1113,7 @@ static void bpf_object__sanitize_btf_ext(struct bpf_object *obj)
 	}
 }
 
-static int bpf_object__load_btf(struct bpf_object *obj,
+static int bpf_object__init_btf(struct bpf_object *obj,
 				Elf_Data *btf_data,
 				Elf_Data *btf_ext_data)
 {
@@ -1132,13 +1132,6 @@ static int bpf_object__load_btf(struct bpf_object *obj,
 				   BTF_ELF_SEC, err);
 			goto out;
 		}
-		bpf_object__sanitize_btf(obj);
-		err = btf__load(obj->btf);
-		if (err) {
-			pr_warning("Error loading %s into kernel: %d.\n",
-				   BTF_ELF_SEC, err);
-			goto out;
-		}
 	}
 	if (btf_ext_data) {
 		if (!obj->btf) {
@@ -1154,7 +1147,6 @@ static int bpf_object__load_btf(struct bpf_object *obj,
 			obj->btf_ext = NULL;
 			goto out;
 		}
-		bpf_object__sanitize_btf_ext(obj);
 	}
 out:
 	if (err || IS_ERR(obj->btf)) {
@@ -1165,6 +1157,26 @@ static int bpf_object__load_btf(struct bpf_object *obj,
 	return 0;
 }
 
+static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)
+{
+	int err = 0;
+
+	if (!obj->btf)
+		return 0;
+
+	bpf_object__sanitize_btf(obj);
+	bpf_object__sanitize_btf_ext(obj);
+
+	err = btf__load(obj->btf);
+	if (err) {
+		pr_warning("Error loading %s into kernel: %d.\n",
+			   BTF_ELF_SEC, err);
+		btf__free(obj->btf);
+		obj->btf = NULL;
+	}
+	return 0;
+}
+
 static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 {
 	Elf *elf = obj->efile.elf;
@@ -1296,9 +1308,11 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 		pr_warning("Corrupted ELF file: index of strtab invalid\n");
 		return -LIBBPF_ERRNO__FORMAT;
 	}
-	err = bpf_object__load_btf(obj, btf_data, btf_ext_data);
+	err = bpf_object__init_btf(obj, btf_data, btf_ext_data);
 	if (!err)
 		err = bpf_object__init_maps(obj, flags);
+	if (!err)
+		err = bpf_object__sanitize_and_load_btf(obj);
 	if (!err)
 		err = bpf_object__init_prog_names(obj);
 	return err;
-- 
2.17.1


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

* [PATCH v2 bpf-next 07/11] libbpf: allow specifying map definitions using BTF
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
                   ` (5 preceding siblings ...)
  2019-06-17 19:26 ` [PATCH v2 bpf-next 06/11] libbpf: split initialization and loading of BTF Andrii Nakryiko
@ 2019-06-17 19:26 ` Andrii Nakryiko
  2019-06-17 19:43   ` Song Liu
  2019-06-17 19:26 ` [PATCH v2 bpf-next 08/11] selftests/bpf: add test for BTF-defined maps Andrii Nakryiko
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:26 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

This patch adds support for a new way to define BPF maps. It relies on
BTF to describe mandatory and optional attributes of a map, as well as
captures type information of key and value naturally. This eliminates
the need for BPF_ANNOTATE_KV_PAIR hack and ensures key/value sizes are
always in sync with the key/value type.

Relying on BTF, this approach allows for both forward and backward
compatibility w.r.t. extending supported map definition features. By
default, any unrecognized attributes are treated as an error, but it's
possible relax this using MAPS_RELAX_COMPAT flag. New attributes, added
in the future will need to be optional.

The outline of the new map definition (short, BTF-defined maps) is as follows:
1. All the maps should be defined in .maps ELF section. It's possible to
   have both "legacy" map definitions in `maps` sections and BTF-defined
   maps in .maps sections. Everything will still work transparently.
2. The map declaration and initialization is done through
   a global/static variable of a struct type with few mandatory and
   extra optional fields:
   - type field is mandatory and specified type of BPF map;
   - key/value fields are mandatory and capture key/value type/size information;
   - max_entries attribute is optional; if max_entries is not specified or
     initialized, it has to be provided in runtime through libbpf API
     before loading bpf_object;
   - map_flags is optional and if not defined, will be assumed to be 0.
3. Key/value fields should be **a pointer** to a type describing
   key/value. The pointee type is assumed (and will be recorded as such
   and used for size determination) to be a type describing key/value of
   the map. This is done to save excessive amounts of space allocated in
   corresponding ELF sections for key/value of big size.
4. As some maps disallow having BTF type ID associated with key/value,
   it's possible to specify key/value size explicitly without
   associating BTF type ID with it. Use key_size and value_size fields
   to do that (see example below).

Here's an example of simple ARRAY map defintion:

struct my_value { int x, y, z; };

struct {
	int type;
	int max_entries;
	int *key;
	struct my_value *value;
} btf_map SEC(".maps") = {
	.type = BPF_MAP_TYPE_ARRAY,
	.max_entries = 16,
};

This will define BPF ARRAY map 'btf_map' with 16 elements. The key will
be of type int and thus key size will be 4 bytes. The value is struct
my_value of size 12 bytes. This map can be used from C code exactly the
same as with existing maps defined through struct bpf_map_def.

Here's an example of STACKMAP definition (which currently disallows BTF type
IDs for key/value):

struct {
	__u32 type;
	__u32 max_entries;
	__u32 map_flags;
	__u32 key_size;
	__u32 value_size;
} stackmap SEC(".maps") = {
	.type = BPF_MAP_TYPE_STACK_TRACE,
	.max_entries = 128,
	.map_flags = BPF_F_STACK_BUILD_ID,
	.key_size = sizeof(__u32),
	.value_size = PERF_MAX_STACK_DEPTH * sizeof(struct bpf_stack_build_id),
};

This approach is naturally extended to support map-in-map, by making a value
field to be another struct that describes inner map. This feature is not
implemented yet. It's also possible to incrementally add features like pinning
with full backwards and forward compatibility. Support for static
initialization of BPF_MAP_TYPE_PROG_ARRAY using pointers to BPF programs
is also on the roadmap.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/lib/bpf/btf.h    |   1 +
 tools/lib/bpf/libbpf.c | 353 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 345 insertions(+), 9 deletions(-)

diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
index ba4ffa831aa4..88a52ae56fc6 100644
--- a/tools/lib/bpf/btf.h
+++ b/tools/lib/bpf/btf.h
@@ -17,6 +17,7 @@ extern "C" {
 
 #define BTF_ELF_SEC ".BTF"
 #define BTF_EXT_ELF_SEC ".BTF.ext"
+#define MAPS_ELF_SEC ".maps"
 
 struct btf;
 struct btf_ext;
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index da942ab2f06a..585e3a2f1eb4 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -262,6 +262,7 @@ struct bpf_object {
 		} *reloc;
 		int nr_reloc;
 		int maps_shndx;
+		int btf_maps_shndx;
 		int text_shndx;
 		int data_shndx;
 		int rodata_shndx;
@@ -514,6 +515,7 @@ static struct bpf_object *bpf_object__new(const char *path,
 	obj->efile.obj_buf = obj_buf;
 	obj->efile.obj_buf_sz = obj_buf_sz;
 	obj->efile.maps_shndx = -1;
+	obj->efile.btf_maps_shndx = -1;
 	obj->efile.data_shndx = -1;
 	obj->efile.rodata_shndx = -1;
 	obj->efile.bss_shndx = -1;
@@ -1007,6 +1009,312 @@ static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
 	return 0;
 }
 
+static const struct btf_type *skip_mods_and_typedefs(const struct btf *btf,
+						     __u32 id)
+{
+	const struct btf_type *t = btf__type_by_id(btf, id);
+
+	while (true) {
+		switch (BTF_INFO_KIND(t->info)) {
+		case BTF_KIND_VOLATILE:
+		case BTF_KIND_CONST:
+		case BTF_KIND_RESTRICT:
+		case BTF_KIND_TYPEDEF:
+			t = btf__type_by_id(btf, t->type);
+			break;
+		default:
+			return t;
+		}
+	}
+}
+
+static bool get_map_field_int(const char *map_name,
+			      const struct btf *btf,
+			      const struct btf_type *def,
+			      const struct btf_member *m,
+			      const void *data, __u32 *res) {
+	const struct btf_type *t = skip_mods_and_typedefs(btf, m->type);
+	const char *name = btf__name_by_offset(btf, m->name_off);
+	__u32 int_info = *(const __u32 *)(const void *)(t + 1);
+
+	if (BTF_INFO_KIND(t->info) != BTF_KIND_INT) {
+		pr_warning("map '%s': attr '%s': expected INT, got %u.\n",
+			   map_name, name, BTF_INFO_KIND(t->info));
+		return false;
+	}
+	if (t->size != 4 || BTF_INT_BITS(int_info) != 32 ||
+	    BTF_INT_OFFSET(int_info)) {
+		pr_warning("map '%s': attr '%s': expected 32-bit non-bitfield integer, "
+			   "got %u-byte (%d-bit) one with bit offset %d.\n",
+			   map_name, name, t->size, BTF_INT_BITS(int_info),
+			   BTF_INT_OFFSET(int_info));
+		return false;
+	}
+	if (BTF_INFO_KFLAG(def->info) && BTF_MEMBER_BITFIELD_SIZE(m->offset)) {
+		pr_warning("map '%s': attr '%s': bitfield is not supported.\n",
+			   map_name, name);
+		return false;
+	}
+	if (m->offset % 32) {
+		pr_warning("map '%s': attr '%s': unaligned fields are not supported.\n",
+			   map_name, name);
+		return false;
+	}
+
+	*res = *(const __u32 *)(data + m->offset / 8);
+	return true;
+}
+
+static int bpf_object__init_user_btf_map(struct bpf_object *obj,
+					 const struct btf_type *sec,
+					 int var_idx, int sec_idx,
+					 const Elf_Data *data, bool strict)
+{
+	const struct btf_type *var, *def, *t;
+	const struct btf_var_secinfo *vi;
+	const struct btf_var *var_extra;
+	const struct btf_member *m;
+	const void *def_data;
+	const char *map_name;
+	struct bpf_map *map;
+	int vlen, i;
+
+	vi = (const struct btf_var_secinfo *)(const void *)(sec + 1) + var_idx;
+	var = btf__type_by_id(obj->btf, vi->type);
+	var_extra = (const void *)(var + 1);
+	map_name = btf__name_by_offset(obj->btf, var->name_off);
+	vlen = BTF_INFO_VLEN(var->info);
+
+	if (map_name == NULL || map_name[0] == '\0') {
+		pr_warning("map #%d: empty name.\n", var_idx);
+		return -EINVAL;
+	}
+	if ((__u64)vi->offset + vi->size > data->d_size) {
+		pr_warning("map '%s' BTF data is corrupted.\n", map_name);
+		return -EINVAL;
+	}
+	if (BTF_INFO_KIND(var->info) != BTF_KIND_VAR) {
+		pr_warning("map '%s': unexpected var kind %u.\n",
+			   map_name, BTF_INFO_KIND(var->info));
+		return -EINVAL;
+	}
+	if (var_extra->linkage != BTF_VAR_GLOBAL_ALLOCATED &&
+	    var_extra->linkage != BTF_VAR_STATIC) {
+		pr_warning("map '%s': unsupported var linkage %u.\n",
+			   map_name, var_extra->linkage);
+		return -EOPNOTSUPP;
+	}
+
+	def = skip_mods_and_typedefs(obj->btf, var->type);
+	if (BTF_INFO_KIND(def->info) != BTF_KIND_STRUCT) {
+		pr_warning("map '%s': unexpected def kind %u.\n",
+			   map_name, BTF_INFO_KIND(var->info));
+		return -EINVAL;
+	}
+	if (def->size > vi->size) {
+		pr_warning("map '%s': invalid def size.\n", map_name);
+		return -EINVAL;
+	}
+
+	map = bpf_object__add_map(obj);
+	if (IS_ERR(map))
+		return PTR_ERR(map);
+	map->name = strdup(map_name);
+	if (!map->name) {
+		pr_warning("map '%s': failed to alloc map name.\n", map_name);
+		return -ENOMEM;
+	}
+	map->libbpf_type = LIBBPF_MAP_UNSPEC;
+	map->def.type = BPF_MAP_TYPE_UNSPEC;
+	map->sec_idx = sec_idx;
+	map->sec_offset = vi->offset;
+	pr_debug("map '%s': at sec_idx %d, offset %zu.\n",
+		 map_name, map->sec_idx, map->sec_offset);
+
+	def_data = data->d_buf + vi->offset;
+	vlen = BTF_INFO_VLEN(def->info);
+	m = (const void *)(def + 1);
+	for (i = 0; i < vlen; i++, m++) {
+		const char *name = btf__name_by_offset(obj->btf, m->name_off);
+
+		if (!name) {
+			pr_warning("map '%s': invalid field #%d.\n",
+				   map_name, i);
+			return -EINVAL;
+		}
+		if (strcmp(name, "type") == 0) {
+			if (!get_map_field_int(map_name, obj->btf, def, m,
+					       def_data, &map->def.type))
+				return -EINVAL;
+			pr_debug("map '%s': found type = %u.\n",
+				 map_name, map->def.type);
+		} else if (strcmp(name, "max_entries") == 0) {
+			if (!get_map_field_int(map_name, obj->btf, def, m,
+					       def_data, &map->def.max_entries))
+				return -EINVAL;
+			pr_debug("map '%s': found max_entries = %u.\n",
+				 map_name, map->def.max_entries);
+		} else if (strcmp(name, "map_flags") == 0) {
+			if (!get_map_field_int(map_name, obj->btf, def, m,
+					       def_data, &map->def.map_flags))
+				return -EINVAL;
+			pr_debug("map '%s': found map_flags = %u.\n",
+				 map_name, map->def.map_flags);
+		} else if (strcmp(name, "key_size") == 0) {
+			__u32 sz;
+
+			if (!get_map_field_int(map_name, obj->btf, def, m,
+					       def_data, &sz))
+				return -EINVAL;
+			pr_debug("map '%s': found key_size = %u.\n",
+				 map_name, sz);
+			if (map->def.key_size && map->def.key_size != sz) {
+				pr_warning("map '%s': conflictling key size %u != %u.\n",
+					   map_name, map->def.key_size, sz);
+				return -EINVAL;
+			}
+			map->def.key_size = sz;
+		} else if (strcmp(name, "key") == 0) {
+			__s64 sz;
+
+			t = btf__type_by_id(obj->btf, m->type);
+			if (!t) {
+				pr_warning("map '%s': key type [%d] not found.\n",
+					   map_name, m->type);
+				return -EINVAL;
+			}
+			if (BTF_INFO_KIND(t->info) != BTF_KIND_PTR) {
+				pr_warning("map '%s': key spec is not PTR: %u.\n",
+					   map_name, BTF_INFO_KIND(t->info));
+				return -EINVAL;
+			}
+			sz = btf__resolve_size(obj->btf, t->type);
+			if (sz < 0) {
+				pr_warning("map '%s': can't determine key size for type [%u]: %lld.\n",
+					   map_name, t->type, sz);
+				return sz;
+			}
+			pr_debug("map '%s': found key [%u], sz = %lld.\n",
+				 map_name, t->type, sz);
+			if (map->def.key_size && map->def.key_size != sz) {
+				pr_warning("map '%s': conflictling key size %u != %lld.\n",
+					   map_name, map->def.key_size, sz);
+				return -EINVAL;
+			}
+			map->def.key_size = sz;
+			map->btf_key_type_id = t->type;
+		} else if (strcmp(name, "value_size") == 0) {
+			__u32 sz;
+
+			if (!get_map_field_int(map_name, obj->btf, def, m,
+					       def_data, &sz))
+				return -EINVAL;
+			pr_debug("map '%s': found value_size = %u.\n",
+				 map_name, sz);
+			if (map->def.value_size && map->def.value_size != sz) {
+				pr_warning("map '%s': conflictling value size %u != %u.\n",
+					   map_name, map->def.value_size, sz);
+				return -EINVAL;
+			}
+			map->def.value_size = sz;
+		} else if (strcmp(name, "value") == 0) {
+			__s64 sz;
+
+			t = btf__type_by_id(obj->btf, m->type);
+			if (!t) {
+				pr_warning("map '%s': value type [%d] not found.\n",
+					   map_name, m->type);
+				return -EINVAL;
+			}
+			if (BTF_INFO_KIND(t->info) != BTF_KIND_PTR) {
+				pr_warning("map '%s': value spec is not PTR: %u.\n",
+					   map_name, BTF_INFO_KIND(t->info));
+				return -EINVAL;
+			}
+			sz = btf__resolve_size(obj->btf, t->type);
+			if (sz < 0) {
+				pr_warning("map '%s': can't determine value size for type [%u]: %lld.\n",
+					   map_name, t->type, sz);
+				return sz;
+			}
+			pr_debug("map '%s': found value [%u], sz = %lld.\n",
+				 map_name, t->type, sz);
+			if (map->def.value_size && map->def.value_size != sz) {
+				pr_warning("map '%s': conflictling value size %u != %lld.\n",
+					   map_name, map->def.value_size, sz);
+				return -EINVAL;
+			}
+			map->def.value_size = sz;
+			map->btf_value_type_id = t->type;
+		} else {
+			if (strict) {
+				pr_warning("map '%s': unknown field '%s'.\n",
+					   map_name, name);
+				return -ENOTSUP;
+			}
+			pr_debug("map '%s': ignoring unknown field '%s'.\n",
+				 map_name, name);
+		}
+	}
+
+	if (map->def.type == BPF_MAP_TYPE_UNSPEC) {
+		pr_warning("map '%s': map type isn't specified.\n", map_name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict)
+{
+	const struct btf_type *sec = NULL;
+	int nr_types, i, vlen, err;
+	const struct btf_type *t;
+	const char *name;
+	Elf_Data *data;
+	Elf_Scn *scn;
+
+	if (obj->efile.btf_maps_shndx < 0)
+		return 0;
+
+	scn = elf_getscn(obj->efile.elf, obj->efile.btf_maps_shndx);
+	if (scn)
+		data = elf_getdata(scn, NULL);
+	if (!scn || !data) {
+		pr_warning("failed to get Elf_Data from map section %d (%s)\n",
+			   obj->efile.maps_shndx, MAPS_ELF_SEC);
+		return -EINVAL;
+	}
+
+	nr_types = btf__get_nr_types(obj->btf);
+	for (i = 1; i <= nr_types; i++) {
+		t = btf__type_by_id(obj->btf, i);
+		if (BTF_INFO_KIND(t->info) != BTF_KIND_DATASEC)
+			continue;
+		name = btf__name_by_offset(obj->btf, t->name_off);
+		if (strcmp(name, MAPS_ELF_SEC) == 0) {
+			sec = t;
+			break;
+		}
+	}
+
+	if (!sec) {
+		pr_warning("DATASEC '%s' not found.\n", MAPS_ELF_SEC);
+		return -ENOENT;
+	}
+
+	vlen = BTF_INFO_VLEN(sec->info);
+	for (i = 0; i < vlen; i++) {
+		err = bpf_object__init_user_btf_map(obj, sec, i,
+						    obj->efile.btf_maps_shndx,
+						    data, strict);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
 static int bpf_object__init_maps(struct bpf_object *obj, int flags)
 {
 	bool strict = !(flags & MAPS_RELAX_COMPAT);
@@ -1016,6 +1324,10 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
 	if (err)
 		return err;
 
+	err = bpf_object__init_user_btf_maps(obj, strict);
+	if (err)
+		return err;
+
 	err = bpf_object__init_global_data_maps(obj);
 	if (err)
 		return err;
@@ -1113,10 +1425,16 @@ static void bpf_object__sanitize_btf_ext(struct bpf_object *obj)
 	}
 }
 
+static bool bpf_object__is_btf_mandatory(const struct bpf_object *obj)
+{
+	return obj->efile.btf_maps_shndx >= 0;
+}
+
 static int bpf_object__init_btf(struct bpf_object *obj,
 				Elf_Data *btf_data,
 				Elf_Data *btf_ext_data)
 {
+	bool btf_required = bpf_object__is_btf_mandatory(obj);
 	int err = 0;
 
 	if (btf_data) {
@@ -1150,10 +1468,18 @@ static int bpf_object__init_btf(struct bpf_object *obj,
 	}
 out:
 	if (err || IS_ERR(obj->btf)) {
+		if (btf_required)
+			err = err ? : PTR_ERR(obj->btf);
+		else
+			err = 0;
 		if (!IS_ERR_OR_NULL(obj->btf))
 			btf__free(obj->btf);
 		obj->btf = NULL;
 	}
+	if (btf_required && !obj->btf) {
+		pr_warning("BTF is required, but is missing or corrupted.\n");
+		return err == 0 ? -ENOENT : err;
+	}
 	return 0;
 }
 
@@ -1173,6 +1499,8 @@ static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)
 			   BTF_ELF_SEC, err);
 		btf__free(obj->btf);
 		obj->btf = NULL;
+		if (bpf_object__is_btf_mandatory(obj))
+			return err;
 	}
 	return 0;
 }
@@ -1236,6 +1564,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
 				return err;
 		} else if (strcmp(name, "maps") == 0) {
 			obj->efile.maps_shndx = idx;
+		} else if (strcmp(name, MAPS_ELF_SEC) == 0) {
+			obj->efile.btf_maps_shndx = idx;
 		} else if (strcmp(name, BTF_ELF_SEC) == 0) {
 			btf_data = data;
 		} else if (strcmp(name, BTF_EXT_ELF_SEC) == 0) {
@@ -1355,7 +1685,8 @@ static bool bpf_object__shndx_is_data(const struct bpf_object *obj,
 static bool bpf_object__shndx_is_maps(const struct bpf_object *obj,
 				      int shndx)
 {
-	return shndx == obj->efile.maps_shndx;
+	return shndx == obj->efile.maps_shndx ||
+	       shndx == obj->efile.btf_maps_shndx;
 }
 
 static bool bpf_object__relo_in_known_section(const struct bpf_object *obj,
@@ -1399,14 +1730,14 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
 	prog->nr_reloc = nrels;
 
 	for (i = 0; i < nrels; i++) {
-		GElf_Sym sym;
-		GElf_Rel rel;
-		unsigned int insn_idx;
-		unsigned int shdr_idx;
 		struct bpf_insn *insns = prog->insns;
 		enum libbpf_map_type type;
+		unsigned int insn_idx;
+		unsigned int shdr_idx;
 		const char *name;
 		size_t map_idx;
+		GElf_Sym sym;
+		GElf_Rel rel;
 
 		if (!gelf_getrel(data, i, &rel)) {
 			pr_warning("relocation: failed to get %d reloc\n", i);
@@ -1500,14 +1831,18 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
 	return 0;
 }
 
-static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
+static int bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map)
 {
 	struct bpf_map_def *def = &map->def;
 	__u32 key_type_id = 0, value_type_id = 0;
 	int ret;
 
+	/* if it's BTF-defined map, we don't need to search for type IDs */
+	if (map->sec_idx == obj->efile.btf_maps_shndx)
+		return 0;
+
 	if (!bpf_map__is_internal(map)) {
-		ret = btf__get_map_kv_tids(btf, map->name, def->key_size,
+		ret = btf__get_map_kv_tids(obj->btf, map->name, def->key_size,
 					   def->value_size, &key_type_id,
 					   &value_type_id);
 	} else {
@@ -1515,7 +1850,7 @@ static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
 		 * LLVM annotates global data differently in BTF, that is,
 		 * only as '.data', '.bss' or '.rodata'.
 		 */
-		ret = btf__find_by_name(btf,
+		ret = btf__find_by_name(obj->btf,
 				libbpf_type_to_btf_name[map->libbpf_type]);
 	}
 	if (ret < 0)
@@ -1805,7 +2140,7 @@ bpf_object__create_maps(struct bpf_object *obj)
 		    map->inner_map_fd >= 0)
 			create_attr.inner_map_fd = map->inner_map_fd;
 
-		if (obj->btf && !bpf_map_find_btf_info(map, obj->btf)) {
+		if (obj->btf && !bpf_map_find_btf_info(obj, map)) {
 			create_attr.btf_fd = btf__fd(obj->btf);
 			create_attr.btf_key_type_id = map->btf_key_type_id;
 			create_attr.btf_value_type_id = map->btf_value_type_id;
-- 
2.17.1


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

* [PATCH v2 bpf-next 08/11] selftests/bpf: add test for BTF-defined maps
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
                   ` (6 preceding siblings ...)
  2019-06-17 19:26 ` [PATCH v2 bpf-next 07/11] libbpf: allow specifying map definitions using BTF Andrii Nakryiko
@ 2019-06-17 19:26 ` Andrii Nakryiko
  2019-06-17 19:26 ` [PATCH v2 bpf-next 09/11] selftests/bpf: switch BPF_ANNOTATE_KV_PAIR tests to " Andrii Nakryiko
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:26 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

Add file test for BTF-defined map definition.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Acked-by: Song Liu <songliubraving@fb.com>
---
 .../selftests/bpf/progs/test_btf_newkv.c      | 73 +++++++++++++++++++
 tools/testing/selftests/bpf/test_btf.c        | 10 +--
 2 files changed, 76 insertions(+), 7 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/test_btf_newkv.c

diff --git a/tools/testing/selftests/bpf/progs/test_btf_newkv.c b/tools/testing/selftests/bpf/progs/test_btf_newkv.c
new file mode 100644
index 000000000000..28c16bb583b6
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_btf_newkv.c
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Facebook */
+#include <linux/bpf.h>
+#include "bpf_helpers.h"
+
+int _version SEC("version") = 1;
+
+struct ipv_counts {
+	unsigned int v4;
+	unsigned int v6;
+};
+
+/* just to validate we can handle maps in multiple sections */
+struct bpf_map_def SEC("maps") btf_map_legacy = {
+	.type = BPF_MAP_TYPE_ARRAY,
+	.key_size = sizeof(int),
+	.value_size = sizeof(long long),
+	.max_entries = 4,
+};
+
+BPF_ANNOTATE_KV_PAIR(btf_map_legacy, int, struct ipv_counts);
+
+struct {
+	int *key;
+	struct ipv_counts *value;
+	unsigned int type;
+	unsigned int max_entries;
+} btf_map SEC(".maps") = {
+	.type = BPF_MAP_TYPE_ARRAY,
+	.max_entries = 4,
+};
+
+struct dummy_tracepoint_args {
+	unsigned long long pad;
+	struct sock *sock;
+};
+
+__attribute__((noinline))
+static int test_long_fname_2(struct dummy_tracepoint_args *arg)
+{
+	struct ipv_counts *counts;
+	int key = 0;
+
+	if (!arg->sock)
+		return 0;
+
+	counts = bpf_map_lookup_elem(&btf_map, &key);
+	if (!counts)
+		return 0;
+
+	counts->v6++;
+
+	/* just verify we can reference both maps */
+	counts = bpf_map_lookup_elem(&btf_map_legacy, &key);
+	if (!counts)
+		return 0;
+
+	return 0;
+}
+
+__attribute__((noinline))
+static int test_long_fname_1(struct dummy_tracepoint_args *arg)
+{
+	return test_long_fname_2(arg);
+}
+
+SEC("dummy_tracepoint")
+int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+{
+	return test_long_fname_1(arg);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_btf.c b/tools/testing/selftests/bpf/test_btf.c
index 289daf54dec4..8351cb5f4a20 100644
--- a/tools/testing/selftests/bpf/test_btf.c
+++ b/tools/testing/selftests/bpf/test_btf.c
@@ -4016,13 +4016,9 @@ struct btf_file_test {
 };
 
 static struct btf_file_test file_tests[] = {
-{
-	.file = "test_btf_haskv.o",
-},
-{
-	.file = "test_btf_nokv.o",
-	.btf_kv_notfound = true,
-},
+	{ .file = "test_btf_haskv.o", },
+	{ .file = "test_btf_newkv.o", },
+	{ .file = "test_btf_nokv.o", .btf_kv_notfound = true, },
 };
 
 static int do_test_file(unsigned int test_num)
-- 
2.17.1


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

* [PATCH v2 bpf-next 09/11] selftests/bpf: switch BPF_ANNOTATE_KV_PAIR tests to BTF-defined maps
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
                   ` (7 preceding siblings ...)
  2019-06-17 19:26 ` [PATCH v2 bpf-next 08/11] selftests/bpf: add test for BTF-defined maps Andrii Nakryiko
@ 2019-06-17 19:26 ` Andrii Nakryiko
  2019-06-17 21:41   ` Song Liu
  2019-06-17 19:26 ` [PATCH v2 bpf-next 10/11] selftests/bpf: convert tests w/ custom values " Andrii Nakryiko
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:26 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

Switch tests that already rely on BTF to BTF-defined map definitions.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 .../testing/selftests/bpf/progs/netcnt_prog.c | 22 ++++---
 .../selftests/bpf/progs/test_map_lock.c       | 22 +++----
 .../bpf/progs/test_send_signal_kern.c         | 22 +++----
 .../bpf/progs/test_sock_fields_kern.c         | 60 +++++++++++--------
 .../selftests/bpf/progs/test_spin_lock.c      | 33 +++++-----
 5 files changed, 87 insertions(+), 72 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/netcnt_prog.c b/tools/testing/selftests/bpf/progs/netcnt_prog.c
index 9f741e69cebe..a25c82a5b7c8 100644
--- a/tools/testing/selftests/bpf/progs/netcnt_prog.c
+++ b/tools/testing/selftests/bpf/progs/netcnt_prog.c
@@ -10,24 +10,22 @@
 #define REFRESH_TIME_NS	100000000
 #define NS_PER_SEC	1000000000
 
-struct bpf_map_def SEC("maps") percpu_netcnt = {
+struct {
+	__u32 type;
+	struct bpf_cgroup_storage_key *key;
+	struct percpu_net_cnt *value;
+} percpu_netcnt SEC(".maps") = {
 	.type = BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
-	.key_size = sizeof(struct bpf_cgroup_storage_key),
-	.value_size = sizeof(struct percpu_net_cnt),
 };
 
-BPF_ANNOTATE_KV_PAIR(percpu_netcnt, struct bpf_cgroup_storage_key,
-		     struct percpu_net_cnt);
-
-struct bpf_map_def SEC("maps") netcnt = {
+struct {
+	__u32 type;
+	struct bpf_cgroup_storage_key *key;
+	struct net_cnt *value;
+} netcnt SEC(".maps") = {
 	.type = BPF_MAP_TYPE_CGROUP_STORAGE,
-	.key_size = sizeof(struct bpf_cgroup_storage_key),
-	.value_size = sizeof(struct net_cnt),
 };
 
-BPF_ANNOTATE_KV_PAIR(netcnt, struct bpf_cgroup_storage_key,
-		     struct net_cnt);
-
 SEC("cgroup/skb")
 int bpf_nextcnt(struct __sk_buff *skb)
 {
diff --git a/tools/testing/selftests/bpf/progs/test_map_lock.c b/tools/testing/selftests/bpf/progs/test_map_lock.c
index af8cc68ed2f9..40d9c2853393 100644
--- a/tools/testing/selftests/bpf/progs/test_map_lock.c
+++ b/tools/testing/selftests/bpf/progs/test_map_lock.c
@@ -11,29 +11,31 @@ struct hmap_elem {
 	int var[VAR_NUM];
 };
 
-struct bpf_map_def SEC("maps") hash_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct hmap_elem *value;
+} hash_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH,
-	.key_size = sizeof(int),
-	.value_size = sizeof(struct hmap_elem),
 	.max_entries = 1,
 };
 
-BPF_ANNOTATE_KV_PAIR(hash_map, int, struct hmap_elem);
-
 struct array_elem {
 	struct bpf_spin_lock lock;
 	int var[VAR_NUM];
 };
 
-struct bpf_map_def SEC("maps") array_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	int *key;
+	struct array_elem *value;
+} array_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(int),
-	.value_size = sizeof(struct array_elem),
 	.max_entries = 1,
 };
 
-BPF_ANNOTATE_KV_PAIR(array_map, int, struct array_elem);
-
 SEC("map_lock_demo")
 int bpf_map_lock_test(struct __sk_buff *skb)
 {
diff --git a/tools/testing/selftests/bpf/progs/test_send_signal_kern.c b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c
index 45a1a1a2c345..6ac68be5d68b 100644
--- a/tools/testing/selftests/bpf/progs/test_send_signal_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c
@@ -4,24 +4,26 @@
 #include <linux/version.h>
 #include "bpf_helpers.h"
 
-struct bpf_map_def SEC("maps") info_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u64 *value;
+} info_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u64),
 	.max_entries = 1,
 };
 
-BPF_ANNOTATE_KV_PAIR(info_map, __u32, __u64);
-
-struct bpf_map_def SEC("maps") status_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u64 *value;
+} status_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u64),
 	.max_entries = 1,
 };
 
-BPF_ANNOTATE_KV_PAIR(status_map, __u32, __u64);
-
 SEC("send_signal_demo")
 int bpf_send_signal_test(void *ctx)
 {
diff --git a/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c b/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c
index 1c39e4ccb7f1..c3d383d650cb 100644
--- a/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_sock_fields_kern.c
@@ -27,31 +27,43 @@ enum bpf_linum_array_idx {
 	__NR_BPF_LINUM_ARRAY_IDX,
 };
 
-struct bpf_map_def SEC("maps") addr_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct sockaddr_in6 *value;
+} addr_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct sockaddr_in6),
 	.max_entries = __NR_BPF_ADDR_ARRAY_IDX,
 };
 
-struct bpf_map_def SEC("maps") sock_result_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct bpf_sock *value;
+} sock_result_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct bpf_sock),
 	.max_entries = __NR_BPF_RESULT_ARRAY_IDX,
 };
 
-struct bpf_map_def SEC("maps") tcp_sock_result_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct bpf_tcp_sock *value;
+} tcp_sock_result_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct bpf_tcp_sock),
 	.max_entries = __NR_BPF_RESULT_ARRAY_IDX,
 };
 
-struct bpf_map_def SEC("maps") linum_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u32 *value;
+} linum_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u32),
 	.max_entries = __NR_BPF_LINUM_ARRAY_IDX,
 };
 
@@ -60,26 +72,26 @@ struct bpf_spinlock_cnt {
 	__u32 cnt;
 };
 
-struct bpf_map_def SEC("maps") sk_pkt_out_cnt = {
+struct {
+	__u32 type;
+	__u32 map_flags;
+	int *key;
+	struct bpf_spinlock_cnt *value;
+} sk_pkt_out_cnt SEC(".maps") = {
 	.type = BPF_MAP_TYPE_SK_STORAGE,
-	.key_size = sizeof(int),
-	.value_size = sizeof(struct bpf_spinlock_cnt),
-	.max_entries = 0,
 	.map_flags = BPF_F_NO_PREALLOC,
 };
 
-BPF_ANNOTATE_KV_PAIR(sk_pkt_out_cnt, int, struct bpf_spinlock_cnt);
-
-struct bpf_map_def SEC("maps") sk_pkt_out_cnt10 = {
+struct {
+	__u32 type;
+	__u32 map_flags;
+	int *key;
+	struct bpf_spinlock_cnt *value;
+} sk_pkt_out_cnt10 SEC(".maps") = {
 	.type = BPF_MAP_TYPE_SK_STORAGE,
-	.key_size = sizeof(int),
-	.value_size = sizeof(struct bpf_spinlock_cnt),
-	.max_entries = 0,
 	.map_flags = BPF_F_NO_PREALLOC,
 };
 
-BPF_ANNOTATE_KV_PAIR(sk_pkt_out_cnt10, int, struct bpf_spinlock_cnt);
-
 static bool is_loopback6(__u32 *a6)
 {
 	return !a6[0] && !a6[1] && !a6[2] && a6[3] == bpf_htonl(1);
diff --git a/tools/testing/selftests/bpf/progs/test_spin_lock.c b/tools/testing/selftests/bpf/progs/test_spin_lock.c
index 40f904312090..0a77ae36d981 100644
--- a/tools/testing/selftests/bpf/progs/test_spin_lock.c
+++ b/tools/testing/selftests/bpf/progs/test_spin_lock.c
@@ -10,30 +10,29 @@ struct hmap_elem {
 	int test_padding;
 };
 
-struct bpf_map_def SEC("maps") hmap = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	int *key;
+	struct hmap_elem *value;
+} hmap SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH,
-	.key_size = sizeof(int),
-	.value_size = sizeof(struct hmap_elem),
 	.max_entries = 1,
 };
 
-BPF_ANNOTATE_KV_PAIR(hmap, int, struct hmap_elem);
-
-
 struct cls_elem {
 	struct bpf_spin_lock lock;
 	volatile int cnt;
 };
 
-struct bpf_map_def SEC("maps") cls_map = {
+struct {
+	__u32 type;
+	struct bpf_cgroup_storage_key *key;
+	struct cls_elem *value;
+} cls_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_CGROUP_STORAGE,
-	.key_size = sizeof(struct bpf_cgroup_storage_key),
-	.value_size = sizeof(struct cls_elem),
 };
 
-BPF_ANNOTATE_KV_PAIR(cls_map, struct bpf_cgroup_storage_key,
-		     struct cls_elem);
-
 struct bpf_vqueue {
 	struct bpf_spin_lock lock;
 	/* 4 byte hole */
@@ -42,14 +41,16 @@ struct bpf_vqueue {
 	unsigned int rate;
 };
 
-struct bpf_map_def SEC("maps") vqueue = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	int *key;
+	struct bpf_vqueue *value;
+} vqueue SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(int),
-	.value_size = sizeof(struct bpf_vqueue),
 	.max_entries = 1,
 };
 
-BPF_ANNOTATE_KV_PAIR(vqueue, int, struct bpf_vqueue);
 #define CREDIT_PER_NS(delta, rate) (((delta) * rate) >> 20)
 
 SEC("spin_lock_demo")
-- 
2.17.1


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

* [PATCH v2 bpf-next 10/11] selftests/bpf: convert tests w/ custom values to BTF-defined maps
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
                   ` (8 preceding siblings ...)
  2019-06-17 19:26 ` [PATCH v2 bpf-next 09/11] selftests/bpf: switch BPF_ANNOTATE_KV_PAIR tests to " Andrii Nakryiko
@ 2019-06-17 19:26 ` Andrii Nakryiko
  2019-06-17 19:27 ` [PATCH v2 bpf-next 11/11] selftests/bpf: convert remaining selftests " Andrii Nakryiko
  2019-06-17 21:17 ` [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Daniel Borkmann
  11 siblings, 0 replies; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:26 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

Convert a bulk of selftests that have maps with custom (not integer) key
and/or value.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/testing/selftests/bpf/progs/bpf_flow.c  | 18 ++++--
 .../selftests/bpf/progs/socket_cookie_prog.c  | 11 ++--
 .../bpf/progs/test_get_stack_rawtp.c          | 27 ++++++---
 .../selftests/bpf/progs/test_global_data.c    | 27 ++++++---
 tools/testing/selftests/bpf/progs/test_l4lb.c | 45 +++++++++-----
 .../selftests/bpf/progs/test_l4lb_noinline.c  | 45 +++++++++-----
 .../bpf/progs/test_select_reuseport_kern.c    | 45 +++++++++-----
 .../bpf/progs/test_stacktrace_build_id.c      | 44 +++++++++-----
 .../selftests/bpf/progs/test_stacktrace_map.c | 40 +++++++++----
 .../selftests/bpf/progs/test_tcp_estats.c     |  9 ++-
 .../selftests/bpf/progs/test_tcpbpf_kern.c    | 18 ++++--
 .../selftests/bpf/progs/test_tcpnotify_kern.c | 18 ++++--
 tools/testing/selftests/bpf/progs/test_xdp.c  | 18 ++++--
 .../selftests/bpf/progs/test_xdp_noinline.c   | 60 ++++++++++++-------
 14 files changed, 285 insertions(+), 140 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/bpf_flow.c b/tools/testing/selftests/bpf/progs/bpf_flow.c
index 81ad9a0b29d0..849f42e548b5 100644
--- a/tools/testing/selftests/bpf/progs/bpf_flow.c
+++ b/tools/testing/selftests/bpf/progs/bpf_flow.c
@@ -57,17 +57,25 @@ struct frag_hdr {
 	__be32 identification;
 };
 
-struct bpf_map_def SEC("maps") jmp_table = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} jmp_table SEC(".maps") = {
 	.type = BPF_MAP_TYPE_PROG_ARRAY,
+	.max_entries = 8,
 	.key_size = sizeof(__u32),
 	.value_size = sizeof(__u32),
-	.max_entries = 8
 };
 
-struct bpf_map_def SEC("maps") last_dissection = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct bpf_flow_keys *value;
+} last_dissection SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct bpf_flow_keys),
 	.max_entries = 1,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/socket_cookie_prog.c b/tools/testing/selftests/bpf/progs/socket_cookie_prog.c
index 0db15c3210ad..6aabb681fb9a 100644
--- a/tools/testing/selftests/bpf/progs/socket_cookie_prog.c
+++ b/tools/testing/selftests/bpf/progs/socket_cookie_prog.c
@@ -12,15 +12,16 @@ struct socket_cookie {
 	__u32 cookie_value;
 };
 
-struct bpf_map_def SEC("maps") socket_cookies = {
+struct {
+	__u32 type;
+	__u32 map_flags;
+	int *key;
+	struct socket_cookie *value;
+} socket_cookies SEC(".maps") = {
 	.type = BPF_MAP_TYPE_SK_STORAGE,
-	.key_size = sizeof(int),
-	.value_size = sizeof(struct socket_cookie),
 	.map_flags = BPF_F_NO_PREALLOC,
 };
 
-BPF_ANNOTATE_KV_PAIR(socket_cookies, int, struct socket_cookie);
-
 SEC("cgroup/connect6")
 int set_cookie(struct bpf_sock_addr *ctx)
 {
diff --git a/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c b/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c
index f6d9f238e00a..aaa6ec250e15 100644
--- a/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c
+++ b/tools/testing/selftests/bpf/progs/test_get_stack_rawtp.c
@@ -15,17 +15,25 @@ struct stack_trace_t {
 	struct bpf_stack_build_id user_stack_buildid[MAX_STACK_RAWTP];
 };
 
-struct bpf_map_def SEC("maps") perfmap = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} perfmap SEC(".maps") = {
 	.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+	.max_entries = 2,
 	.key_size = sizeof(int),
 	.value_size = sizeof(__u32),
-	.max_entries = 2,
 };
 
-struct bpf_map_def SEC("maps") stackdata_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct stack_trace_t *value;
+} stackdata_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_PERCPU_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct stack_trace_t),
 	.max_entries = 1,
 };
 
@@ -47,10 +55,13 @@ struct bpf_map_def SEC("maps") stackdata_map = {
  * issue and avoid complicated C programming massaging.
  * This is an acceptable workaround since there is one entry here.
  */
-struct bpf_map_def SEC("maps") rawdata_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u64 (*value)[2 * MAX_STACK_RAWTP];
+} rawdata_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_PERCPU_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = MAX_STACK_RAWTP * sizeof(__u64) * 2,
 	.max_entries = 1,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_global_data.c b/tools/testing/selftests/bpf/progs/test_global_data.c
index 5ab14e941980..866cc7ddbe43 100644
--- a/tools/testing/selftests/bpf/progs/test_global_data.c
+++ b/tools/testing/selftests/bpf/progs/test_global_data.c
@@ -7,17 +7,23 @@
 
 #include "bpf_helpers.h"
 
-struct bpf_map_def SEC("maps") result_number = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u64 *value;
+} result_number SEC(".maps") = {
 	.type		= BPF_MAP_TYPE_ARRAY,
-	.key_size	= sizeof(__u32),
-	.value_size	= sizeof(__u64),
 	.max_entries	= 11,
 };
 
-struct bpf_map_def SEC("maps") result_string = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	const char (*value)[32];
+} result_string SEC(".maps") = {
 	.type		= BPF_MAP_TYPE_ARRAY,
-	.key_size	= sizeof(__u32),
-	.value_size	= 32,
 	.max_entries	= 5,
 };
 
@@ -27,10 +33,13 @@ struct foo {
 	__u64 c;
 };
 
-struct bpf_map_def SEC("maps") result_struct = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct foo *value;
+} result_struct SEC(".maps") = {
 	.type		= BPF_MAP_TYPE_ARRAY,
-	.key_size	= sizeof(__u32),
-	.value_size	= sizeof(struct foo),
 	.max_entries	= 5,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_l4lb.c b/tools/testing/selftests/bpf/progs/test_l4lb.c
index 1e10c9590991..848cbb90f581 100644
--- a/tools/testing/selftests/bpf/progs/test_l4lb.c
+++ b/tools/testing/selftests/bpf/progs/test_l4lb.c
@@ -169,38 +169,53 @@ struct eth_hdr {
 	unsigned short eth_proto;
 };
 
-struct bpf_map_def SEC("maps") vip_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	struct vip *key;
+	struct vip_meta *value;
+} vip_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH,
-	.key_size = sizeof(struct vip),
-	.value_size = sizeof(struct vip_meta),
 	.max_entries = MAX_VIPS,
 };
 
-struct bpf_map_def SEC("maps") ch_rings = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u32 *value;
+} ch_rings SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u32),
 	.max_entries = CH_RINGS_SIZE,
 };
 
-struct bpf_map_def SEC("maps") reals = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct real_definition *value;
+} reals SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct real_definition),
 	.max_entries = MAX_REALS,
 };
 
-struct bpf_map_def SEC("maps") stats = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct vip_stats *value;
+} stats SEC(".maps") = {
 	.type = BPF_MAP_TYPE_PERCPU_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct vip_stats),
 	.max_entries = MAX_VIPS,
 };
 
-struct bpf_map_def SEC("maps") ctl_array = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct ctl_value *value;
+} ctl_array SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct ctl_value),
 	.max_entries = CTL_MAP_SIZE,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c b/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c
index ba44a14e6dc4..c63ecf3ca573 100644
--- a/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c
+++ b/tools/testing/selftests/bpf/progs/test_l4lb_noinline.c
@@ -165,38 +165,53 @@ struct eth_hdr {
 	unsigned short eth_proto;
 };
 
-struct bpf_map_def SEC("maps") vip_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	struct vip *key;
+	struct vip_meta *value;
+} vip_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH,
-	.key_size = sizeof(struct vip),
-	.value_size = sizeof(struct vip_meta),
 	.max_entries = MAX_VIPS,
 };
 
-struct bpf_map_def SEC("maps") ch_rings = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u32 *value;
+} ch_rings SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u32),
 	.max_entries = CH_RINGS_SIZE,
 };
 
-struct bpf_map_def SEC("maps") reals = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct real_definition *value;
+} reals SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct real_definition),
 	.max_entries = MAX_REALS,
 };
 
-struct bpf_map_def SEC("maps") stats = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct vip_stats *value;
+} stats SEC(".maps") = {
 	.type = BPF_MAP_TYPE_PERCPU_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct vip_stats),
 	.max_entries = MAX_VIPS,
 };
 
-struct bpf_map_def SEC("maps") ctl_array = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct ctl_value *value;
+} ctl_array SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct ctl_value),
 	.max_entries = CTL_MAP_SIZE,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c b/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c
index 5b54ec637ada..435a9527733e 100644
--- a/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_select_reuseport_kern.c
@@ -21,38 +21,55 @@ int _version SEC("version") = 1;
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 #endif
 
-struct bpf_map_def SEC("maps") outer_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} outer_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
+	.max_entries = 1,
 	.key_size = sizeof(__u32),
 	.value_size = sizeof(__u32),
-	.max_entries = 1,
 };
 
-struct bpf_map_def SEC("maps") result_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u32 *value;
+} result_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u32),
 	.max_entries = NR_RESULTS,
 };
 
-struct bpf_map_def SEC("maps") tmp_index_ovr_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	int *value;
+} tmp_index_ovr_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(int),
 	.max_entries = 1,
 };
 
-struct bpf_map_def SEC("maps") linum_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u32 *value;
+} linum_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u32),
 	.max_entries = 1,
 };
 
-struct bpf_map_def SEC("maps") data_check_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct data_check *value;
+} data_check_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct data_check),
 	.max_entries = 1,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c b/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c
index d86c281e957f..fcf2280bb60c 100644
--- a/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c
+++ b/tools/testing/selftests/bpf/progs/test_stacktrace_build_id.c
@@ -8,34 +8,50 @@
 #define PERF_MAX_STACK_DEPTH         127
 #endif
 
-struct bpf_map_def SEC("maps") control_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u32 *value;
+} control_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u32),
 	.max_entries = 1,
 };
 
-struct bpf_map_def SEC("maps") stackid_hmap = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u32 *value;
+} stackid_hmap SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u32),
 	.max_entries = 16384,
 };
 
-struct bpf_map_def SEC("maps") stackmap = {
+typedef struct bpf_stack_build_id stack_trace_t[PERF_MAX_STACK_DEPTH];
+
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 map_flags;
+	__u32 key_size;
+	__u32 value_size;
+} stackmap SEC(".maps") = {
 	.type = BPF_MAP_TYPE_STACK_TRACE,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct bpf_stack_build_id)
-		* PERF_MAX_STACK_DEPTH,
 	.max_entries = 128,
 	.map_flags = BPF_F_STACK_BUILD_ID,
+	.key_size = sizeof(__u32),
+	.value_size = sizeof(stack_trace_t),
 };
 
-struct bpf_map_def SEC("maps") stack_amap = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	/* there seems to be a bug in kernel not handling typedef properly */
+	struct bpf_stack_build_id (*value)[PERF_MAX_STACK_DEPTH];
+} stack_amap SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct bpf_stack_build_id)
-		* PERF_MAX_STACK_DEPTH,
 	.max_entries = 128,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_stacktrace_map.c b/tools/testing/selftests/bpf/progs/test_stacktrace_map.c
index af111af7ca1a..7ad09adbf648 100644
--- a/tools/testing/selftests/bpf/progs/test_stacktrace_map.c
+++ b/tools/testing/selftests/bpf/progs/test_stacktrace_map.c
@@ -8,31 +8,47 @@
 #define PERF_MAX_STACK_DEPTH         127
 #endif
 
-struct bpf_map_def SEC("maps") control_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u32 *value;
+} control_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u32),
 	.max_entries = 1,
 };
 
-struct bpf_map_def SEC("maps") stackid_hmap = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u32 *value;
+} stackid_hmap SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u32),
 	.max_entries = 16384,
 };
 
-struct bpf_map_def SEC("maps") stackmap = {
+typedef __u64 stack_trace_t[PERF_MAX_STACK_DEPTH];
+
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} stackmap SEC(".maps") = {
 	.type = BPF_MAP_TYPE_STACK_TRACE,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u64) * PERF_MAX_STACK_DEPTH,
 	.max_entries = 16384,
+	.key_size = sizeof(__u32),
+	.value_size = sizeof(stack_trace_t),
 };
 
-struct bpf_map_def SEC("maps") stack_amap = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u64 (*value)[PERF_MAX_STACK_DEPTH];
+} stack_amap SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u64) * PERF_MAX_STACK_DEPTH,
 	.max_entries = 16384,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_tcp_estats.c b/tools/testing/selftests/bpf/progs/test_tcp_estats.c
index bee3bbecc0c4..df98f7e32832 100644
--- a/tools/testing/selftests/bpf/progs/test_tcp_estats.c
+++ b/tools/testing/selftests/bpf/progs/test_tcp_estats.c
@@ -148,10 +148,13 @@ struct tcp_estats_basic_event {
 	struct tcp_estats_conn_id conn_id;
 };
 
-struct bpf_map_def SEC("maps") ev_record_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct tcp_estats_basic_event *value;
+} ev_record_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct tcp_estats_basic_event),
 	.max_entries = 1024,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c b/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c
index c7c3240e0dd4..38e10c9fd996 100644
--- a/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_tcpbpf_kern.c
@@ -14,17 +14,23 @@
 #include "bpf_endian.h"
 #include "test_tcpbpf.h"
 
-struct bpf_map_def SEC("maps") global_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct tcpbpf_globals *value;
+} global_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct tcpbpf_globals),
 	.max_entries = 4,
 };
 
-struct bpf_map_def SEC("maps") sockopt_results = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	int *value;
+} sockopt_results SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(int),
 	.max_entries = 2,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c b/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c
index ec6db6e64c41..d073d37d4e27 100644
--- a/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_tcpnotify_kern.c
@@ -14,18 +14,26 @@
 #include "bpf_endian.h"
 #include "test_tcpnotify.h"
 
-struct bpf_map_def SEC("maps") global_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct tcpnotify_globals *value;
+} global_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct tcpnotify_globals),
 	.max_entries = 4,
 };
 
-struct bpf_map_def SEC("maps") perf_event_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} perf_event_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+	.max_entries = 2,
 	.key_size = sizeof(int),
 	.value_size = sizeof(__u32),
-	.max_entries = 2,
 };
 
 int _version SEC("version") = 1;
diff --git a/tools/testing/selftests/bpf/progs/test_xdp.c b/tools/testing/selftests/bpf/progs/test_xdp.c
index 5e7df8bb5b5d..ec3d2c1c8cf9 100644
--- a/tools/testing/selftests/bpf/progs/test_xdp.c
+++ b/tools/testing/selftests/bpf/progs/test_xdp.c
@@ -22,17 +22,23 @@
 
 int _version SEC("version") = 1;
 
-struct bpf_map_def SEC("maps") rxcnt = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u64 *value;
+} rxcnt SEC(".maps") = {
 	.type = BPF_MAP_TYPE_PERCPU_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u64),
 	.max_entries = 256,
 };
 
-struct bpf_map_def SEC("maps") vip2tnl = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	struct vip *key;
+	struct iptnl_info *value;
+} vip2tnl SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH,
-	.key_size = sizeof(struct vip),
-	.value_size = sizeof(struct iptnl_info),
 	.max_entries = MAX_IPTNL_ENTRIES,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_xdp_noinline.c b/tools/testing/selftests/bpf/progs/test_xdp_noinline.c
index 4fe6aaad22a4..d2eddb5553d1 100644
--- a/tools/testing/selftests/bpf/progs/test_xdp_noinline.c
+++ b/tools/testing/selftests/bpf/progs/test_xdp_noinline.c
@@ -163,52 +163,66 @@ struct lb_stats {
 	__u64 v1;
 };
 
-struct bpf_map_def __attribute__ ((section("maps"), used)) vip_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	struct vip_definition *key;
+	struct vip_meta *value;
+} vip_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH,
-	.key_size = sizeof(struct vip_definition),
-	.value_size = sizeof(struct vip_meta),
 	.max_entries = 512,
-	.map_flags = 0,
 };
 
-struct bpf_map_def __attribute__ ((section("maps"), used)) lru_cache = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 map_flags;
+	struct flow_key *key;
+	struct real_pos_lru *value;
+} lru_cache SEC(".maps") = {
 	.type = BPF_MAP_TYPE_LRU_HASH,
-	.key_size = sizeof(struct flow_key),
-	.value_size = sizeof(struct real_pos_lru),
 	.max_entries = 300,
 	.map_flags = 1U << 1,
 };
 
-struct bpf_map_def __attribute__ ((section("maps"), used)) ch_rings = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u32 *value;
+} ch_rings SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u32),
 	.max_entries = 12 * 655,
-	.map_flags = 0,
 };
 
-struct bpf_map_def __attribute__ ((section("maps"), used)) reals = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct real_definition *value;
+} reals SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct real_definition),
 	.max_entries = 40,
-	.map_flags = 0,
 };
 
-struct bpf_map_def __attribute__ ((section("maps"), used)) stats = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct lb_stats *value;
+} stats SEC(".maps") = {
 	.type = BPF_MAP_TYPE_PERCPU_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct lb_stats),
 	.max_entries = 515,
-	.map_flags = 0,
 };
 
-struct bpf_map_def __attribute__ ((section("maps"), used)) ctl_array = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	struct ctl_value *value;
+} ctl_array SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(struct ctl_value),
 	.max_entries = 16,
-	.map_flags = 0,
 };
 
 struct eth_hdr {
-- 
2.17.1


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

* [PATCH v2 bpf-next 11/11] selftests/bpf: convert remaining selftests to BTF-defined maps
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
                   ` (9 preceding siblings ...)
  2019-06-17 19:26 ` [PATCH v2 bpf-next 10/11] selftests/bpf: convert tests w/ custom values " Andrii Nakryiko
@ 2019-06-17 19:27 ` Andrii Nakryiko
  2019-06-17 21:17 ` [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Daniel Borkmann
  11 siblings, 0 replies; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-17 19:27 UTC (permalink / raw)
  To: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team; +Cc: Andrii Nakryiko

Convert all the rest of selftests that use BPF maps. These are either
maps with integer key/value or special types of maps that don't event
allow BTF type information for key/value.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 .../selftests/bpf/progs/get_cgroup_id_kern.c  | 18 +++--
 .../selftests/bpf/progs/sample_map_ret0.c     | 18 +++--
 .../bpf/progs/sockmap_verdict_prog.c          | 36 +++++++---
 .../selftests/bpf/progs/test_map_in_map.c     | 20 ++++--
 .../testing/selftests/bpf/progs/test_obj_id.c |  9 ++-
 .../bpf/progs/test_skb_cgroup_id_kern.c       |  9 ++-
 .../testing/selftests/bpf/progs/test_tc_edt.c |  9 ++-
 .../bpf/progs/test_tcp_check_syncookie_kern.c |  9 ++-
 .../selftests/bpf/test_queue_stack_map.h      | 20 ++++--
 .../testing/selftests/bpf/test_sockmap_kern.h | 72 +++++++++++++------
 10 files changed, 154 insertions(+), 66 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c b/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c
index 014dba10b8a5..87b202381088 100644
--- a/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c
+++ b/tools/testing/selftests/bpf/progs/get_cgroup_id_kern.c
@@ -4,17 +4,23 @@
 #include <linux/bpf.h>
 #include "bpf_helpers.h"
 
-struct bpf_map_def SEC("maps") cg_ids = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u64 *value;
+} cg_ids SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u64),
 	.max_entries = 1,
 };
 
-struct bpf_map_def SEC("maps") pidmap = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u32 *value;
+} pidmap SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u32),
 	.max_entries = 1,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/sample_map_ret0.c b/tools/testing/selftests/bpf/progs/sample_map_ret0.c
index 0756303676ac..0f4d47cecd4d 100644
--- a/tools/testing/selftests/bpf/progs/sample_map_ret0.c
+++ b/tools/testing/selftests/bpf/progs/sample_map_ret0.c
@@ -2,17 +2,23 @@
 #include <linux/bpf.h>
 #include "bpf_helpers.h"
 
-struct bpf_map_def SEC("maps") htab = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	long *value;
+} htab SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(long),
 	.max_entries = 2,
 };
 
-struct bpf_map_def SEC("maps") array = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	long *value;
+} array SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(long),
 	.max_entries = 2,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c b/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c
index d85c874ef25e..983c4f6e4fad 100644
--- a/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c
+++ b/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c
@@ -4,31 +4,49 @@
 
 int _version SEC("version") = 1;
 
-struct bpf_map_def SEC("maps") sock_map_rx = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} sock_map_rx SEC(".maps") = {
 	.type = BPF_MAP_TYPE_SOCKMAP,
+	.max_entries = 20,
 	.key_size = sizeof(int),
 	.value_size = sizeof(int),
-	.max_entries = 20,
 };
 
-struct bpf_map_def SEC("maps") sock_map_tx = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} sock_map_tx SEC(".maps") = {
 	.type = BPF_MAP_TYPE_SOCKMAP,
+	.max_entries = 20,
 	.key_size = sizeof(int),
 	.value_size = sizeof(int),
-	.max_entries = 20,
 };
 
-struct bpf_map_def SEC("maps") sock_map_msg = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} sock_map_msg SEC(".maps") = {
 	.type = BPF_MAP_TYPE_SOCKMAP,
+	.max_entries = 20,
 	.key_size = sizeof(int),
 	.value_size = sizeof(int),
-	.max_entries = 20,
 };
 
-struct bpf_map_def SEC("maps") sock_map_break = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	int *key;
+	int *value;
+} sock_map_break SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(int),
-	.value_size = sizeof(int),
 	.max_entries = 20,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_map_in_map.c b/tools/testing/selftests/bpf/progs/test_map_in_map.c
index 2985f262846e..7404bee7c26e 100644
--- a/tools/testing/selftests/bpf/progs/test_map_in_map.c
+++ b/tools/testing/selftests/bpf/progs/test_map_in_map.c
@@ -5,22 +5,30 @@
 #include <linux/types.h>
 #include "bpf_helpers.h"
 
-struct bpf_map_def SEC("maps") mim_array = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} mim_array SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
+	.max_entries = 1,
 	.key_size = sizeof(int),
 	/* must be sizeof(__u32) for map in map */
 	.value_size = sizeof(__u32),
-	.max_entries = 1,
-	.map_flags = 0,
 };
 
-struct bpf_map_def SEC("maps") mim_hash = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} mim_hash SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH_OF_MAPS,
+	.max_entries = 1,
 	.key_size = sizeof(int),
 	/* must be sizeof(__u32) for map in map */
 	.value_size = sizeof(__u32),
-	.max_entries = 1,
-	.map_flags = 0,
 };
 
 SEC("xdp_mimtest")
diff --git a/tools/testing/selftests/bpf/progs/test_obj_id.c b/tools/testing/selftests/bpf/progs/test_obj_id.c
index 880d2963b472..2b1c2efdeed4 100644
--- a/tools/testing/selftests/bpf/progs/test_obj_id.c
+++ b/tools/testing/selftests/bpf/progs/test_obj_id.c
@@ -16,10 +16,13 @@
 
 int _version SEC("version") = 1;
 
-struct bpf_map_def SEC("maps") test_map_id = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u64 *value;
+} test_map_id SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u64),
 	.max_entries = 1,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_skb_cgroup_id_kern.c b/tools/testing/selftests/bpf/progs/test_skb_cgroup_id_kern.c
index 68cf9829f5a7..af296b876156 100644
--- a/tools/testing/selftests/bpf/progs/test_skb_cgroup_id_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_skb_cgroup_id_kern.c
@@ -10,10 +10,13 @@
 
 #define NUM_CGROUP_LEVELS	4
 
-struct bpf_map_def SEC("maps") cgroup_ids = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u64 *value;
+} cgroup_ids SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u64),
 	.max_entries = NUM_CGROUP_LEVELS,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_tc_edt.c b/tools/testing/selftests/bpf/progs/test_tc_edt.c
index 3af64c470d64..c2781dd78617 100644
--- a/tools/testing/selftests/bpf/progs/test_tc_edt.c
+++ b/tools/testing/selftests/bpf/progs/test_tc_edt.c
@@ -16,10 +16,13 @@
 #define THROTTLE_RATE_BPS (5 * 1000 * 1000)
 
 /* flow_key => last_tstamp timestamp used */
-struct bpf_map_def SEC("maps") flow_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	uint32_t *key;
+	uint64_t *value;
+} flow_map SEC(".maps") = {
 	.type = BPF_MAP_TYPE_HASH,
-	.key_size = sizeof(uint32_t),
-	.value_size = sizeof(uint64_t),
 	.max_entries = 1,
 };
 
diff --git a/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c b/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c
index 1ab095bcacd8..0f1725e25c44 100644
--- a/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_tcp_check_syncookie_kern.c
@@ -16,10 +16,13 @@
 #include "bpf_helpers.h"
 #include "bpf_endian.h"
 
-struct bpf_map_def SEC("maps") results = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 *key;
+	__u64 *value;
+} results SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(__u32),
-	.value_size = sizeof(__u64),
 	.max_entries = 1,
 };
 
diff --git a/tools/testing/selftests/bpf/test_queue_stack_map.h b/tools/testing/selftests/bpf/test_queue_stack_map.h
index 295b9b3bc5c7..f284137a36c4 100644
--- a/tools/testing/selftests/bpf/test_queue_stack_map.h
+++ b/tools/testing/selftests/bpf/test_queue_stack_map.h
@@ -10,20 +10,28 @@
 
 int _version SEC("version") = 1;
 
-struct bpf_map_def __attribute__ ((section("maps"), used)) map_in = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} map_in SEC(".maps") = {
 	.type = MAP_TYPE,
+	.max_entries = 32,
 	.key_size = 0,
 	.value_size = sizeof(__u32),
-	.max_entries = 32,
-	.map_flags = 0,
 };
 
-struct bpf_map_def __attribute__ ((section("maps"), used)) map_out = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} map_out SEC(".maps") = {
 	.type = MAP_TYPE,
+	.max_entries = 32,
 	.key_size = 0,
 	.value_size = sizeof(__u32),
-	.max_entries = 32,
-	.map_flags = 0,
 };
 
 SEC("test")
diff --git a/tools/testing/selftests/bpf/test_sockmap_kern.h b/tools/testing/selftests/bpf/test_sockmap_kern.h
index 4e7d3da21357..70b9236cedb0 100644
--- a/tools/testing/selftests/bpf/test_sockmap_kern.h
+++ b/tools/testing/selftests/bpf/test_sockmap_kern.h
@@ -28,59 +28,89 @@
  * are established and verdicts are decided.
  */
 
-struct bpf_map_def SEC("maps") sock_map = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} sock_map SEC(".maps") = {
 	.type = TEST_MAP_TYPE,
+	.max_entries = 20,
 	.key_size = sizeof(int),
 	.value_size = sizeof(int),
-	.max_entries = 20,
 };
 
-struct bpf_map_def SEC("maps") sock_map_txmsg = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} sock_map_txmsg SEC(".maps") = {
 	.type = TEST_MAP_TYPE,
+	.max_entries = 20,
 	.key_size = sizeof(int),
 	.value_size = sizeof(int),
-	.max_entries = 20,
 };
 
-struct bpf_map_def SEC("maps") sock_map_redir = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	__u32 key_size;
+	__u32 value_size;
+} sock_map_redir SEC(".maps") = {
 	.type = TEST_MAP_TYPE,
+	.max_entries = 20,
 	.key_size = sizeof(int),
 	.value_size = sizeof(int),
-	.max_entries = 20,
 };
 
-struct bpf_map_def SEC("maps") sock_apply_bytes = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	int *key;
+	int *value;
+} sock_apply_bytes SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(int),
-	.value_size = sizeof(int),
 	.max_entries = 1
 };
 
-struct bpf_map_def SEC("maps") sock_cork_bytes = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	int *key;
+	int *value;
+} sock_cork_bytes SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(int),
-	.value_size = sizeof(int),
 	.max_entries = 1
 };
 
-struct bpf_map_def SEC("maps") sock_bytes = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	int *key;
+	int *value;
+} sock_bytes SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(int),
-	.value_size = sizeof(int),
 	.max_entries = 6
 };
 
-struct bpf_map_def SEC("maps") sock_redir_flags = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	int *key;
+	int *value;
+} sock_redir_flags SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(int),
-	.value_size = sizeof(int),
 	.max_entries = 1
 };
 
-struct bpf_map_def SEC("maps") sock_skb_opts = {
+struct {
+	__u32 type;
+	__u32 max_entries;
+	int *key;
+	int *value;
+} sock_skb_opts SEC(".maps") = {
 	.type = BPF_MAP_TYPE_ARRAY,
-	.key_size = sizeof(int),
-	.value_size = sizeof(int),
 	.max_entries = 1
 };
 
-- 
2.17.1


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

* Re: [PATCH v2 bpf-next 04/11] libbpf: refactor map initialization
  2019-06-17 19:26 ` [PATCH v2 bpf-next 04/11] libbpf: refactor map initialization Andrii Nakryiko
@ 2019-06-17 19:39   ` Song Liu
  2019-06-26 14:48   ` Matt Hart
  1 sibling, 0 replies; 29+ messages in thread
From: Song Liu @ 2019-06-17 19:39 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Andrii Nakryiko, Alexei Starovoitov, daniel, netdev, bpf, Kernel Team



> On Jun 17, 2019, at 12:26 PM, Andrii Nakryiko <andriin@fb.com> wrote:
> 
> User and global data maps initialization has gotten pretty complicated
> and unnecessarily convoluted. This patch splits out the logic for global
> data map and user-defined map initialization. It also removes the
> restriction of pre-calculating how many maps will be initialized,
> instead allowing to keep adding new maps as they are discovered, which
> will be used later for BTF-defined map definitions.
> 
> Signed-off-by: Andrii Nakryiko <andriin@fb.com>

Acked-by: Song Liu <songliubraving@fb.com>

> ---
> tools/lib/bpf/libbpf.c | 247 ++++++++++++++++++++++-------------------
> 1 file changed, 133 insertions(+), 114 deletions(-)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 7ee44d8877c5..88609dca4f7d 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -234,6 +234,7 @@ struct bpf_object {
> 	size_t nr_programs;
> 	struct bpf_map *maps;
> 	size_t nr_maps;
> +	size_t maps_cap;
> 	struct bpf_secdata sections;
> 
> 	bool loaded;
> @@ -763,21 +764,51 @@ int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
> 	return -ENOENT;
> }
> 
> -static bool bpf_object__has_maps(const struct bpf_object *obj)
> +static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
> {
> -	return obj->efile.maps_shndx >= 0 ||
> -	       obj->efile.data_shndx >= 0 ||
> -	       obj->efile.rodata_shndx >= 0 ||
> -	       obj->efile.bss_shndx >= 0;
> +	struct bpf_map *new_maps;
> +	size_t new_cap;
> +	int i;
> +
> +	if (obj->nr_maps < obj->maps_cap)
> +		return &obj->maps[obj->nr_maps++];
> +
> +	new_cap = max(4ul, obj->maps_cap * 3 / 2);
> +	new_maps = realloc(obj->maps, new_cap * sizeof(*obj->maps));
> +	if (!new_maps) {
> +		pr_warning("alloc maps for object failed\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	obj->maps_cap = new_cap;
> +	obj->maps = new_maps;
> +
> +	/* zero out new maps */
> +	memset(obj->maps + obj->nr_maps, 0,
> +	       (obj->maps_cap - obj->nr_maps) * sizeof(*obj->maps));
> +	/*
> +	 * fill all fd with -1 so won't close incorrect fd (fd=0 is stdin)
> +	 * when failure (zclose won't close negative fd)).
> +	 */
> +	for (i = obj->nr_maps; i < obj->maps_cap; i++) {
> +		obj->maps[i].fd = -1;
> +		obj->maps[i].inner_map_fd = -1;
> +	}
> +
> +	return &obj->maps[obj->nr_maps++];
> }
> 
> static int
> -bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
> -			      enum libbpf_map_type type, Elf_Data *data,
> -			      void **data_buff)
> +bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
> +			      Elf_Data *data, void **data_buff)
> {
> -	struct bpf_map_def *def = &map->def;
> 	char map_name[BPF_OBJ_NAME_LEN];
> +	struct bpf_map_def *def;
> +	struct bpf_map *map;
> +
> +	map = bpf_object__add_map(obj);
> +	if (IS_ERR(map))
> +		return PTR_ERR(map);
> 
> 	map->libbpf_type = type;
> 	map->offset = ~(typeof(map->offset))0;
> @@ -789,6 +820,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
> 		return -ENOMEM;
> 	}
> 
> +	def = &map->def;
> 	def->type = BPF_MAP_TYPE_ARRAY;
> 	def->key_size = sizeof(int);
> 	def->value_size = data->d_size;
> @@ -808,29 +840,58 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
> 	return 0;
> }
> 
> -static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> +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.
> +	 */
> +	if (obj->efile.data_shndx >= 0) {
> +		err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA,
> +						    obj->efile.data,
> +						    &obj->sections.data);
> +		if (err)
> +			return err;
> +	}
> +	if (obj->efile.rodata_shndx >= 0) {
> +		err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA,
> +						    obj->efile.rodata,
> +						    &obj->sections.rodata);
> +		if (err)
> +			return err;
> +	}
> +	if (obj->efile.bss_shndx >= 0) {
> +		err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS,
> +						    obj->efile.bss, NULL);
> +		if (err)
> +			return err;
> +	}
> +	return 0;
> +}
> +
> +static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
> {
> -	int i, map_idx, map_def_sz = 0, nr_syms, nr_maps = 0, nr_maps_glob = 0;
> -	bool strict = !(flags & MAPS_RELAX_COMPAT);
> 	Elf_Data *symbols = obj->efile.symbols;
> +	int i, map_def_sz = 0, nr_maps = 0, nr_syms;
> 	Elf_Data *data = NULL;
> -	int ret = 0;
> +	Elf_Scn *scn;
> +
> +	if (obj->efile.maps_shndx < 0)
> +		return 0;
> 
> 	if (!symbols)
> 		return -EINVAL;
> -	nr_syms = symbols->d_size / sizeof(GElf_Sym);
> -
> -	if (obj->efile.maps_shndx >= 0) {
> -		Elf_Scn *scn = elf_getscn(obj->efile.elf,
> -					  obj->efile.maps_shndx);
> 
> -		if (scn)
> -			data = elf_getdata(scn, NULL);
> -		if (!scn || !data) {
> -			pr_warning("failed to get Elf_Data from map section %d\n",
> -				   obj->efile.maps_shndx);
> -			return -EINVAL;
> -		}
> +	scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx);
> +	if (scn)
> +		data = elf_getdata(scn, NULL);
> +	if (!scn || !data) {
> +		pr_warning("failed to get Elf_Data from map section %d\n",
> +			   obj->efile.maps_shndx);
> +		return -EINVAL;
> 	}
> 
> 	/*
> @@ -840,16 +901,8 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> 	 *
> 	 * TODO: Detect array of map and report error.
> 	 */
> -	if (obj->caps.global_data) {
> -		if (obj->efile.data_shndx >= 0)
> -			nr_maps_glob++;
> -		if (obj->efile.rodata_shndx >= 0)
> -			nr_maps_glob++;
> -		if (obj->efile.bss_shndx >= 0)
> -			nr_maps_glob++;
> -	}
> -
> -	for (i = 0; data && i < nr_syms; i++) {
> +	nr_syms = symbols->d_size / sizeof(GElf_Sym);
> +	for (i = 0; i < nr_syms; i++) {
> 		GElf_Sym sym;
> 
> 		if (!gelf_getsym(symbols, i, &sym))
> @@ -858,79 +911,56 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> 			continue;
> 		nr_maps++;
> 	}
> -
> -	if (!nr_maps && !nr_maps_glob)
> -		return 0;
> -
> 	/* Assume equally sized map definitions */
> -	if (data) {
> -		pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path,
> -			 nr_maps, data->d_size);
> -
> -		map_def_sz = data->d_size / nr_maps;
> -		if (!data->d_size || (data->d_size % nr_maps) != 0) {
> -			pr_warning("unable to determine map definition size "
> -				   "section %s, %d maps in %zd bytes\n",
> -				   obj->path, nr_maps, data->d_size);
> -			return -EINVAL;
> -		}
> -	}
> -
> -	nr_maps += nr_maps_glob;
> -	obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
> -	if (!obj->maps) {
> -		pr_warning("alloc maps for object failed\n");
> -		return -ENOMEM;
> -	}
> -	obj->nr_maps = nr_maps;
> -
> -	for (i = 0; i < nr_maps; i++) {
> -		/*
> -		 * fill all fd with -1 so won't close incorrect
> -		 * fd (fd=0 is stdin) when failure (zclose won't close
> -		 * negative fd)).
> -		 */
> -		obj->maps[i].fd = -1;
> -		obj->maps[i].inner_map_fd = -1;
> +	pr_debug("maps in %s: %d maps in %zd bytes\n",
> +		 obj->path, nr_maps, data->d_size);
> +
> +	map_def_sz = data->d_size / nr_maps;
> +	if (!data->d_size || (data->d_size % nr_maps) != 0) {
> +		pr_warning("unable to determine map definition size "
> +			   "section %s, %d maps in %zd bytes\n",
> +			   obj->path, nr_maps, data->d_size);
> +		return -EINVAL;
> 	}
> 
> -	/*
> -	 * Fill obj->maps using data in "maps" section.
> -	 */
> -	for (i = 0, map_idx = 0; data && i < nr_syms; i++) {
> +	/* Fill obj->maps using data in "maps" section.  */
> +	for (i = 0; i < nr_syms; i++) {
> 		GElf_Sym sym;
> 		const char *map_name;
> 		struct bpf_map_def *def;
> +		struct bpf_map *map;
> 
> 		if (!gelf_getsym(symbols, i, &sym))
> 			continue;
> 		if (sym.st_shndx != obj->efile.maps_shndx)
> 			continue;
> 
> -		map_name = elf_strptr(obj->efile.elf,
> -				      obj->efile.strtabidx,
> +		map = bpf_object__add_map(obj);
> +		if (IS_ERR(map))
> +			return PTR_ERR(map);
> +
> +		map_name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
> 				      sym.st_name);
> 		if (!map_name) {
> 			pr_warning("failed to get map #%d name sym string for obj %s\n",
> -				   map_idx, obj->path);
> +				   i, obj->path);
> 			return -LIBBPF_ERRNO__FORMAT;
> 		}
> 
> -		obj->maps[map_idx].libbpf_type = LIBBPF_MAP_UNSPEC;
> -		obj->maps[map_idx].offset = sym.st_value;
> +		map->libbpf_type = LIBBPF_MAP_UNSPEC;
> +		map->offset = sym.st_value;
> 		if (sym.st_value + map_def_sz > data->d_size) {
> 			pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
> 				   obj->path, map_name);
> 			return -EINVAL;
> 		}
> 
> -		obj->maps[map_idx].name = strdup(map_name);
> -		if (!obj->maps[map_idx].name) {
> +		map->name = strdup(map_name);
> +		if (!map->name) {
> 			pr_warning("failed to alloc map name\n");
> 			return -ENOMEM;
> 		}
> -		pr_debug("map %d is \"%s\"\n", map_idx,
> -			 obj->maps[map_idx].name);
> +		pr_debug("map %d is \"%s\"\n", i, map->name);
> 		def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
> 		/*
> 		 * If the definition of the map in the object file fits in
> @@ -939,7 +969,7 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> 		 * calloc above.
> 		 */
> 		if (map_def_sz <= sizeof(struct bpf_map_def)) {
> -			memcpy(&obj->maps[map_idx].def, def, map_def_sz);
> +			memcpy(&map->def, def, map_def_sz);
> 		} else {
> 			/*
> 			 * Here the map structure being read is bigger than what
> @@ -959,37 +989,30 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> 						return -EINVAL;
> 				}
> 			}
> -			memcpy(&obj->maps[map_idx].def, def,
> -			       sizeof(struct bpf_map_def));
> +			memcpy(&map->def, def, sizeof(struct bpf_map_def));
> 		}
> -		map_idx++;
> 	}
> +	return 0;
> +}
> 
> -	if (!obj->caps.global_data)
> -		goto finalize;
> +static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> +{
> +	bool strict = !(flags & MAPS_RELAX_COMPAT);
> +	int err;
> 
> -	/*
> -	 * Populate rest of obj->maps with libbpf internal maps.
> -	 */
> -	if (obj->efile.data_shndx >= 0)
> -		ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> -						    LIBBPF_MAP_DATA,
> -						    obj->efile.data,
> -						    &obj->sections.data);
> -	if (!ret && obj->efile.rodata_shndx >= 0)
> -		ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> -						    LIBBPF_MAP_RODATA,
> -						    obj->efile.rodata,
> -						    &obj->sections.rodata);
> -	if (!ret && obj->efile.bss_shndx >= 0)
> -		ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> -						    LIBBPF_MAP_BSS,
> -						    obj->efile.bss, NULL);
> -finalize:
> -	if (!ret)
> +	err = bpf_object__init_user_maps(obj, strict);
> +	if (err)
> +		return err;
> +
> +	err = bpf_object__init_global_data_maps(obj);
> +	if (err)
> +		return err;
> +
> +	if (obj->nr_maps) {
> 		qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]),
> 		      compare_bpf_map);
> -	return ret;
> +	}
> +	return 0;
> }
> 
> static bool section_have_execinstr(struct bpf_object *obj, int idx)
> @@ -1262,14 +1285,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> 		return -LIBBPF_ERRNO__FORMAT;
> 	}
> 	err = bpf_object__load_btf(obj, btf_data, btf_ext_data);
> -	if (err)
> -		return err;
> -	if (bpf_object__has_maps(obj)) {
> +	if (!err)
> 		err = bpf_object__init_maps(obj, flags);
> -		if (err)
> -			return err;
> -	}
> -	err = bpf_object__init_prog_names(obj);
> +	if (!err)
> +		err = bpf_object__init_prog_names(obj);
> 	return err;
> }
> 
> -- 
> 2.17.1
> 


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

* Re: [PATCH v2 bpf-next 02/11] libbpf: extract BTF loading logic
  2019-06-17 19:26 ` [PATCH v2 bpf-next 02/11] libbpf: extract BTF loading logic Andrii Nakryiko
@ 2019-06-17 19:40   ` Song Liu
  0 siblings, 0 replies; 29+ messages in thread
From: Song Liu @ 2019-06-17 19:40 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Andrii Nakryiko, Alexei Starovoitov, daniel, netdev, bpf, Kernel Team



> On Jun 17, 2019, at 12:26 PM, Andrii Nakryiko <andriin@fb.com> wrote:
> 
> As a preparetion fro adding BTF-based BPF map loading, extract .BTF and
> .BTF.ext loading logic.
> 
> Signed-off-by: Andrii Nakryiko <andriin@fb.com>

Acked-by: Song Liu <songliubraving@fb.com>

> ---
> tools/lib/bpf/libbpf.c | 93 +++++++++++++++++++++++++-----------------
> 1 file changed, 55 insertions(+), 38 deletions(-)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index e725fa86b189..49d3a808e754 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -1078,6 +1078,58 @@ static void bpf_object__sanitize_btf_ext(struct bpf_object *obj)
> 	}
> }
> 
> +static int bpf_object__load_btf(struct bpf_object *obj,
> +				Elf_Data *btf_data,
> +				Elf_Data *btf_ext_data)
> +{
> +	int err = 0;
> +
> +	if (btf_data) {
> +		obj->btf = btf__new(btf_data->d_buf, btf_data->d_size);
> +		if (IS_ERR(obj->btf)) {
> +			pr_warning("Error loading ELF section %s: %d.\n",
> +				   BTF_ELF_SEC, err);
> +			goto out;
> +		}
> +		err = btf__finalize_data(obj, obj->btf);
> +		if (err) {
> +			pr_warning("Error finalizing %s: %d.\n",
> +				   BTF_ELF_SEC, err);
> +			goto out;
> +		}
> +		bpf_object__sanitize_btf(obj);
> +		err = btf__load(obj->btf);
> +		if (err) {
> +			pr_warning("Error loading %s into kernel: %d.\n",
> +				   BTF_ELF_SEC, err);
> +			goto out;
> +		}
> +	}
> +	if (btf_ext_data) {
> +		if (!obj->btf) {
> +			pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
> +				 BTF_EXT_ELF_SEC, BTF_ELF_SEC);
> +			goto out;
> +		}
> +		obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
> +					    btf_ext_data->d_size);
> +		if (IS_ERR(obj->btf_ext)) {
> +			pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
> +				   BTF_EXT_ELF_SEC, PTR_ERR(obj->btf_ext));
> +			obj->btf_ext = NULL;
> +			goto out;
> +		}
> +		bpf_object__sanitize_btf_ext(obj);
> +	}
> +out:
> +	if (err || IS_ERR(obj->btf)) {
> +		if (!IS_ERR_OR_NULL(obj->btf))
> +			btf__free(obj->btf);
> +		obj->btf = NULL;
> +	}
> +	return 0;
> +}
> +
> static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> {
> 	Elf *elf = obj->efile.elf;
> @@ -1212,44 +1264,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> 		pr_warning("Corrupted ELF file: index of strtab invalid\n");
> 		return -LIBBPF_ERRNO__FORMAT;
> 	}
> -	if (btf_data) {
> -		obj->btf = btf__new(btf_data->d_buf, btf_data->d_size);
> -		if (IS_ERR(obj->btf)) {
> -			pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
> -				   BTF_ELF_SEC, PTR_ERR(obj->btf));
> -			obj->btf = NULL;
> -		} else {
> -			err = btf__finalize_data(obj, obj->btf);
> -			if (!err) {
> -				bpf_object__sanitize_btf(obj);
> -				err = btf__load(obj->btf);
> -			}
> -			if (err) {
> -				pr_warning("Error finalizing and loading %s into kernel: %d. Ignored and continue.\n",
> -					   BTF_ELF_SEC, err);
> -				btf__free(obj->btf);
> -				obj->btf = NULL;
> -				err = 0;
> -			}
> -		}
> -	}
> -	if (btf_ext_data) {
> -		if (!obj->btf) {
> -			pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
> -				 BTF_EXT_ELF_SEC, BTF_ELF_SEC);
> -		} else {
> -			obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
> -						    btf_ext_data->d_size);
> -			if (IS_ERR(obj->btf_ext)) {
> -				pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
> -					   BTF_EXT_ELF_SEC,
> -					   PTR_ERR(obj->btf_ext));
> -				obj->btf_ext = NULL;
> -			} else {
> -				bpf_object__sanitize_btf_ext(obj);
> -			}
> -		}
> -	}
> +	err = bpf_object__load_btf(obj, btf_data, btf_ext_data);
> +	if (err)
> +		return err;
> 	if (bpf_object__has_maps(obj)) {
> 		err = bpf_object__init_maps(obj, flags);
> 		if (err)
> -- 
> 2.17.1
> 


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

* Re: [PATCH v2 bpf-next 07/11] libbpf: allow specifying map definitions using BTF
  2019-06-17 19:26 ` [PATCH v2 bpf-next 07/11] libbpf: allow specifying map definitions using BTF Andrii Nakryiko
@ 2019-06-17 19:43   ` Song Liu
  0 siblings, 0 replies; 29+ messages in thread
From: Song Liu @ 2019-06-17 19:43 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Andrii Nakryiko, Alexei Starovoitov, daniel, netdev, bpf, Kernel Team



> On Jun 17, 2019, at 12:26 PM, Andrii Nakryiko <andriin@fb.com> wrote:
> 
> This patch adds support for a new way to define BPF maps. It relies on
> BTF to describe mandatory and optional attributes of a map, as well as
> captures type information of key and value naturally. This eliminates
> the need for BPF_ANNOTATE_KV_PAIR hack and ensures key/value sizes are
> always in sync with the key/value type.
> 
> Relying on BTF, this approach allows for both forward and backward
> compatibility w.r.t. extending supported map definition features. By
> default, any unrecognized attributes are treated as an error, but it's
> possible relax this using MAPS_RELAX_COMPAT flag. New attributes, added
> in the future will need to be optional.
> 
> The outline of the new map definition (short, BTF-defined maps) is as follows:
> 1. All the maps should be defined in .maps ELF section. It's possible to
>   have both "legacy" map definitions in `maps` sections and BTF-defined
>   maps in .maps sections. Everything will still work transparently.
> 2. The map declaration and initialization is done through
>   a global/static variable of a struct type with few mandatory and
>   extra optional fields:
>   - type field is mandatory and specified type of BPF map;
>   - key/value fields are mandatory and capture key/value type/size information;
>   - max_entries attribute is optional; if max_entries is not specified or
>     initialized, it has to be provided in runtime through libbpf API
>     before loading bpf_object;
>   - map_flags is optional and if not defined, will be assumed to be 0.
> 3. Key/value fields should be **a pointer** to a type describing
>   key/value. The pointee type is assumed (and will be recorded as such
>   and used for size determination) to be a type describing key/value of
>   the map. This is done to save excessive amounts of space allocated in
>   corresponding ELF sections for key/value of big size.
> 4. As some maps disallow having BTF type ID associated with key/value,
>   it's possible to specify key/value size explicitly without
>   associating BTF type ID with it. Use key_size and value_size fields
>   to do that (see example below).
> 
> Here's an example of simple ARRAY map defintion:
> 
> struct my_value { int x, y, z; };
> 
> struct {
> 	int type;
> 	int max_entries;
> 	int *key;
> 	struct my_value *value;
> } btf_map SEC(".maps") = {
> 	.type = BPF_MAP_TYPE_ARRAY,
> 	.max_entries = 16,
> };
> 
> This will define BPF ARRAY map 'btf_map' with 16 elements. The key will
> be of type int and thus key size will be 4 bytes. The value is struct
> my_value of size 12 bytes. This map can be used from C code exactly the
> same as with existing maps defined through struct bpf_map_def.
> 
> Here's an example of STACKMAP definition (which currently disallows BTF type
> IDs for key/value):
> 
> struct {
> 	__u32 type;
> 	__u32 max_entries;
> 	__u32 map_flags;
> 	__u32 key_size;
> 	__u32 value_size;
> } stackmap SEC(".maps") = {
> 	.type = BPF_MAP_TYPE_STACK_TRACE,
> 	.max_entries = 128,
> 	.map_flags = BPF_F_STACK_BUILD_ID,
> 	.key_size = sizeof(__u32),
> 	.value_size = PERF_MAX_STACK_DEPTH * sizeof(struct bpf_stack_build_id),
> };
> 
> This approach is naturally extended to support map-in-map, by making a value
> field to be another struct that describes inner map. This feature is not
> implemented yet. It's also possible to incrementally add features like pinning
> with full backwards and forward compatibility. Support for static
> initialization of BPF_MAP_TYPE_PROG_ARRAY using pointers to BPF programs
> is also on the roadmap.
> 
> Signed-off-by: Andrii Nakryiko <andriin@fb.com>

Acked-by: Song Liu <songliubraving@fb.com>


> ---
> tools/lib/bpf/btf.h    |   1 +
> tools/lib/bpf/libbpf.c | 353 +++++++++++++++++++++++++++++++++++++++--
> 2 files changed, 345 insertions(+), 9 deletions(-)
> 
> diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
> index ba4ffa831aa4..88a52ae56fc6 100644
> --- a/tools/lib/bpf/btf.h
> +++ b/tools/lib/bpf/btf.h
> @@ -17,6 +17,7 @@ extern "C" {
> 
> #define BTF_ELF_SEC ".BTF"
> #define BTF_EXT_ELF_SEC ".BTF.ext"
> +#define MAPS_ELF_SEC ".maps"
> 
> struct btf;
> struct btf_ext;
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index da942ab2f06a..585e3a2f1eb4 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -262,6 +262,7 @@ struct bpf_object {
> 		} *reloc;
> 		int nr_reloc;
> 		int maps_shndx;
> +		int btf_maps_shndx;
> 		int text_shndx;
> 		int data_shndx;
> 		int rodata_shndx;
> @@ -514,6 +515,7 @@ static struct bpf_object *bpf_object__new(const char *path,
> 	obj->efile.obj_buf = obj_buf;
> 	obj->efile.obj_buf_sz = obj_buf_sz;
> 	obj->efile.maps_shndx = -1;
> +	obj->efile.btf_maps_shndx = -1;
> 	obj->efile.data_shndx = -1;
> 	obj->efile.rodata_shndx = -1;
> 	obj->efile.bss_shndx = -1;
> @@ -1007,6 +1009,312 @@ static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
> 	return 0;
> }
> 
> +static const struct btf_type *skip_mods_and_typedefs(const struct btf *btf,
> +						     __u32 id)
> +{
> +	const struct btf_type *t = btf__type_by_id(btf, id);
> +
> +	while (true) {
> +		switch (BTF_INFO_KIND(t->info)) {
> +		case BTF_KIND_VOLATILE:
> +		case BTF_KIND_CONST:
> +		case BTF_KIND_RESTRICT:
> +		case BTF_KIND_TYPEDEF:
> +			t = btf__type_by_id(btf, t->type);
> +			break;
> +		default:
> +			return t;
> +		}
> +	}
> +}
> +
> +static bool get_map_field_int(const char *map_name,
> +			      const struct btf *btf,
> +			      const struct btf_type *def,
> +			      const struct btf_member *m,
> +			      const void *data, __u32 *res) {
> +	const struct btf_type *t = skip_mods_and_typedefs(btf, m->type);
> +	const char *name = btf__name_by_offset(btf, m->name_off);
> +	__u32 int_info = *(const __u32 *)(const void *)(t + 1);
> +
> +	if (BTF_INFO_KIND(t->info) != BTF_KIND_INT) {
> +		pr_warning("map '%s': attr '%s': expected INT, got %u.\n",
> +			   map_name, name, BTF_INFO_KIND(t->info));
> +		return false;
> +	}
> +	if (t->size != 4 || BTF_INT_BITS(int_info) != 32 ||
> +	    BTF_INT_OFFSET(int_info)) {
> +		pr_warning("map '%s': attr '%s': expected 32-bit non-bitfield integer, "
> +			   "got %u-byte (%d-bit) one with bit offset %d.\n",
> +			   map_name, name, t->size, BTF_INT_BITS(int_info),
> +			   BTF_INT_OFFSET(int_info));
> +		return false;
> +	}
> +	if (BTF_INFO_KFLAG(def->info) && BTF_MEMBER_BITFIELD_SIZE(m->offset)) {
> +		pr_warning("map '%s': attr '%s': bitfield is not supported.\n",
> +			   map_name, name);
> +		return false;
> +	}
> +	if (m->offset % 32) {
> +		pr_warning("map '%s': attr '%s': unaligned fields are not supported.\n",
> +			   map_name, name);
> +		return false;
> +	}
> +
> +	*res = *(const __u32 *)(data + m->offset / 8);
> +	return true;
> +}
> +
> +static int bpf_object__init_user_btf_map(struct bpf_object *obj,
> +					 const struct btf_type *sec,
> +					 int var_idx, int sec_idx,
> +					 const Elf_Data *data, bool strict)
> +{
> +	const struct btf_type *var, *def, *t;
> +	const struct btf_var_secinfo *vi;
> +	const struct btf_var *var_extra;
> +	const struct btf_member *m;
> +	const void *def_data;
> +	const char *map_name;
> +	struct bpf_map *map;
> +	int vlen, i;
> +
> +	vi = (const struct btf_var_secinfo *)(const void *)(sec + 1) + var_idx;
> +	var = btf__type_by_id(obj->btf, vi->type);
> +	var_extra = (const void *)(var + 1);
> +	map_name = btf__name_by_offset(obj->btf, var->name_off);
> +	vlen = BTF_INFO_VLEN(var->info);
> +
> +	if (map_name == NULL || map_name[0] == '\0') {
> +		pr_warning("map #%d: empty name.\n", var_idx);
> +		return -EINVAL;
> +	}
> +	if ((__u64)vi->offset + vi->size > data->d_size) {
> +		pr_warning("map '%s' BTF data is corrupted.\n", map_name);
> +		return -EINVAL;
> +	}
> +	if (BTF_INFO_KIND(var->info) != BTF_KIND_VAR) {
> +		pr_warning("map '%s': unexpected var kind %u.\n",
> +			   map_name, BTF_INFO_KIND(var->info));
> +		return -EINVAL;
> +	}
> +	if (var_extra->linkage != BTF_VAR_GLOBAL_ALLOCATED &&
> +	    var_extra->linkage != BTF_VAR_STATIC) {
> +		pr_warning("map '%s': unsupported var linkage %u.\n",
> +			   map_name, var_extra->linkage);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	def = skip_mods_and_typedefs(obj->btf, var->type);
> +	if (BTF_INFO_KIND(def->info) != BTF_KIND_STRUCT) {
> +		pr_warning("map '%s': unexpected def kind %u.\n",
> +			   map_name, BTF_INFO_KIND(var->info));
> +		return -EINVAL;
> +	}
> +	if (def->size > vi->size) {
> +		pr_warning("map '%s': invalid def size.\n", map_name);
> +		return -EINVAL;
> +	}
> +
> +	map = bpf_object__add_map(obj);
> +	if (IS_ERR(map))
> +		return PTR_ERR(map);
> +	map->name = strdup(map_name);
> +	if (!map->name) {
> +		pr_warning("map '%s': failed to alloc map name.\n", map_name);
> +		return -ENOMEM;
> +	}
> +	map->libbpf_type = LIBBPF_MAP_UNSPEC;
> +	map->def.type = BPF_MAP_TYPE_UNSPEC;
> +	map->sec_idx = sec_idx;
> +	map->sec_offset = vi->offset;
> +	pr_debug("map '%s': at sec_idx %d, offset %zu.\n",
> +		 map_name, map->sec_idx, map->sec_offset);
> +
> +	def_data = data->d_buf + vi->offset;
> +	vlen = BTF_INFO_VLEN(def->info);
> +	m = (const void *)(def + 1);
> +	for (i = 0; i < vlen; i++, m++) {
> +		const char *name = btf__name_by_offset(obj->btf, m->name_off);
> +
> +		if (!name) {
> +			pr_warning("map '%s': invalid field #%d.\n",
> +				   map_name, i);
> +			return -EINVAL;
> +		}
> +		if (strcmp(name, "type") == 0) {
> +			if (!get_map_field_int(map_name, obj->btf, def, m,
> +					       def_data, &map->def.type))
> +				return -EINVAL;
> +			pr_debug("map '%s': found type = %u.\n",
> +				 map_name, map->def.type);
> +		} else if (strcmp(name, "max_entries") == 0) {
> +			if (!get_map_field_int(map_name, obj->btf, def, m,
> +					       def_data, &map->def.max_entries))
> +				return -EINVAL;
> +			pr_debug("map '%s': found max_entries = %u.\n",
> +				 map_name, map->def.max_entries);
> +		} else if (strcmp(name, "map_flags") == 0) {
> +			if (!get_map_field_int(map_name, obj->btf, def, m,
> +					       def_data, &map->def.map_flags))
> +				return -EINVAL;
> +			pr_debug("map '%s': found map_flags = %u.\n",
> +				 map_name, map->def.map_flags);
> +		} else if (strcmp(name, "key_size") == 0) {
> +			__u32 sz;
> +
> +			if (!get_map_field_int(map_name, obj->btf, def, m,
> +					       def_data, &sz))
> +				return -EINVAL;
> +			pr_debug("map '%s': found key_size = %u.\n",
> +				 map_name, sz);
> +			if (map->def.key_size && map->def.key_size != sz) {
> +				pr_warning("map '%s': conflictling key size %u != %u.\n",
> +					   map_name, map->def.key_size, sz);
> +				return -EINVAL;
> +			}
> +			map->def.key_size = sz;
> +		} else if (strcmp(name, "key") == 0) {
> +			__s64 sz;
> +
> +			t = btf__type_by_id(obj->btf, m->type);
> +			if (!t) {
> +				pr_warning("map '%s': key type [%d] not found.\n",
> +					   map_name, m->type);
> +				return -EINVAL;
> +			}
> +			if (BTF_INFO_KIND(t->info) != BTF_KIND_PTR) {
> +				pr_warning("map '%s': key spec is not PTR: %u.\n",
> +					   map_name, BTF_INFO_KIND(t->info));
> +				return -EINVAL;
> +			}
> +			sz = btf__resolve_size(obj->btf, t->type);
> +			if (sz < 0) {
> +				pr_warning("map '%s': can't determine key size for type [%u]: %lld.\n",
> +					   map_name, t->type, sz);
> +				return sz;
> +			}
> +			pr_debug("map '%s': found key [%u], sz = %lld.\n",
> +				 map_name, t->type, sz);
> +			if (map->def.key_size && map->def.key_size != sz) {
> +				pr_warning("map '%s': conflictling key size %u != %lld.\n",
> +					   map_name, map->def.key_size, sz);
> +				return -EINVAL;
> +			}
> +			map->def.key_size = sz;
> +			map->btf_key_type_id = t->type;
> +		} else if (strcmp(name, "value_size") == 0) {
> +			__u32 sz;
> +
> +			if (!get_map_field_int(map_name, obj->btf, def, m,
> +					       def_data, &sz))
> +				return -EINVAL;
> +			pr_debug("map '%s': found value_size = %u.\n",
> +				 map_name, sz);
> +			if (map->def.value_size && map->def.value_size != sz) {
> +				pr_warning("map '%s': conflictling value size %u != %u.\n",
> +					   map_name, map->def.value_size, sz);
> +				return -EINVAL;
> +			}
> +			map->def.value_size = sz;
> +		} else if (strcmp(name, "value") == 0) {
> +			__s64 sz;
> +
> +			t = btf__type_by_id(obj->btf, m->type);
> +			if (!t) {
> +				pr_warning("map '%s': value type [%d] not found.\n",
> +					   map_name, m->type);
> +				return -EINVAL;
> +			}
> +			if (BTF_INFO_KIND(t->info) != BTF_KIND_PTR) {
> +				pr_warning("map '%s': value spec is not PTR: %u.\n",
> +					   map_name, BTF_INFO_KIND(t->info));
> +				return -EINVAL;
> +			}
> +			sz = btf__resolve_size(obj->btf, t->type);
> +			if (sz < 0) {
> +				pr_warning("map '%s': can't determine value size for type [%u]: %lld.\n",
> +					   map_name, t->type, sz);
> +				return sz;
> +			}
> +			pr_debug("map '%s': found value [%u], sz = %lld.\n",
> +				 map_name, t->type, sz);
> +			if (map->def.value_size && map->def.value_size != sz) {
> +				pr_warning("map '%s': conflictling value size %u != %lld.\n",
> +					   map_name, map->def.value_size, sz);
> +				return -EINVAL;
> +			}
> +			map->def.value_size = sz;
> +			map->btf_value_type_id = t->type;
> +		} else {
> +			if (strict) {
> +				pr_warning("map '%s': unknown field '%s'.\n",
> +					   map_name, name);
> +				return -ENOTSUP;
> +			}
> +			pr_debug("map '%s': ignoring unknown field '%s'.\n",
> +				 map_name, name);
> +		}
> +	}
> +
> +	if (map->def.type == BPF_MAP_TYPE_UNSPEC) {
> +		pr_warning("map '%s': map type isn't specified.\n", map_name);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict)
> +{
> +	const struct btf_type *sec = NULL;
> +	int nr_types, i, vlen, err;
> +	const struct btf_type *t;
> +	const char *name;
> +	Elf_Data *data;
> +	Elf_Scn *scn;
> +
> +	if (obj->efile.btf_maps_shndx < 0)
> +		return 0;
> +
> +	scn = elf_getscn(obj->efile.elf, obj->efile.btf_maps_shndx);
> +	if (scn)
> +		data = elf_getdata(scn, NULL);
> +	if (!scn || !data) {
> +		pr_warning("failed to get Elf_Data from map section %d (%s)\n",
> +			   obj->efile.maps_shndx, MAPS_ELF_SEC);
> +		return -EINVAL;
> +	}
> +
> +	nr_types = btf__get_nr_types(obj->btf);
> +	for (i = 1; i <= nr_types; i++) {
> +		t = btf__type_by_id(obj->btf, i);
> +		if (BTF_INFO_KIND(t->info) != BTF_KIND_DATASEC)
> +			continue;
> +		name = btf__name_by_offset(obj->btf, t->name_off);
> +		if (strcmp(name, MAPS_ELF_SEC) == 0) {
> +			sec = t;
> +			break;
> +		}
> +	}
> +
> +	if (!sec) {
> +		pr_warning("DATASEC '%s' not found.\n", MAPS_ELF_SEC);
> +		return -ENOENT;
> +	}
> +
> +	vlen = BTF_INFO_VLEN(sec->info);
> +	for (i = 0; i < vlen; i++) {
> +		err = bpf_object__init_user_btf_map(obj, sec, i,
> +						    obj->efile.btf_maps_shndx,
> +						    data, strict);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> {
> 	bool strict = !(flags & MAPS_RELAX_COMPAT);
> @@ -1016,6 +1324,10 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> 	if (err)
> 		return err;
> 
> +	err = bpf_object__init_user_btf_maps(obj, strict);
> +	if (err)
> +		return err;
> +
> 	err = bpf_object__init_global_data_maps(obj);
> 	if (err)
> 		return err;
> @@ -1113,10 +1425,16 @@ static void bpf_object__sanitize_btf_ext(struct bpf_object *obj)
> 	}
> }
> 
> +static bool bpf_object__is_btf_mandatory(const struct bpf_object *obj)
> +{
> +	return obj->efile.btf_maps_shndx >= 0;
> +}
> +
> static int bpf_object__init_btf(struct bpf_object *obj,
> 				Elf_Data *btf_data,
> 				Elf_Data *btf_ext_data)
> {
> +	bool btf_required = bpf_object__is_btf_mandatory(obj);
> 	int err = 0;
> 
> 	if (btf_data) {
> @@ -1150,10 +1468,18 @@ static int bpf_object__init_btf(struct bpf_object *obj,
> 	}
> out:
> 	if (err || IS_ERR(obj->btf)) {
> +		if (btf_required)
> +			err = err ? : PTR_ERR(obj->btf);
> +		else
> +			err = 0;
> 		if (!IS_ERR_OR_NULL(obj->btf))
> 			btf__free(obj->btf);
> 		obj->btf = NULL;
> 	}
> +	if (btf_required && !obj->btf) {
> +		pr_warning("BTF is required, but is missing or corrupted.\n");
> +		return err == 0 ? -ENOENT : err;
> +	}
> 	return 0;
> }
> 
> @@ -1173,6 +1499,8 @@ static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)
> 			   BTF_ELF_SEC, err);
> 		btf__free(obj->btf);
> 		obj->btf = NULL;
> +		if (bpf_object__is_btf_mandatory(obj))
> +			return err;
> 	}
> 	return 0;
> }
> @@ -1236,6 +1564,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> 				return err;
> 		} else if (strcmp(name, "maps") == 0) {
> 			obj->efile.maps_shndx = idx;
> +		} else if (strcmp(name, MAPS_ELF_SEC) == 0) {
> +			obj->efile.btf_maps_shndx = idx;
> 		} else if (strcmp(name, BTF_ELF_SEC) == 0) {
> 			btf_data = data;
> 		} else if (strcmp(name, BTF_EXT_ELF_SEC) == 0) {
> @@ -1355,7 +1685,8 @@ static bool bpf_object__shndx_is_data(const struct bpf_object *obj,
> static bool bpf_object__shndx_is_maps(const struct bpf_object *obj,
> 				      int shndx)
> {
> -	return shndx == obj->efile.maps_shndx;
> +	return shndx == obj->efile.maps_shndx ||
> +	       shndx == obj->efile.btf_maps_shndx;
> }
> 
> static bool bpf_object__relo_in_known_section(const struct bpf_object *obj,
> @@ -1399,14 +1730,14 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
> 	prog->nr_reloc = nrels;
> 
> 	for (i = 0; i < nrels; i++) {
> -		GElf_Sym sym;
> -		GElf_Rel rel;
> -		unsigned int insn_idx;
> -		unsigned int shdr_idx;
> 		struct bpf_insn *insns = prog->insns;
> 		enum libbpf_map_type type;
> +		unsigned int insn_idx;
> +		unsigned int shdr_idx;
> 		const char *name;
> 		size_t map_idx;
> +		GElf_Sym sym;
> +		GElf_Rel rel;
> 
> 		if (!gelf_getrel(data, i, &rel)) {
> 			pr_warning("relocation: failed to get %d reloc\n", i);
> @@ -1500,14 +1831,18 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
> 	return 0;
> }
> 
> -static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
> +static int bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map)
> {
> 	struct bpf_map_def *def = &map->def;
> 	__u32 key_type_id = 0, value_type_id = 0;
> 	int ret;
> 
> +	/* if it's BTF-defined map, we don't need to search for type IDs */
> +	if (map->sec_idx == obj->efile.btf_maps_shndx)
> +		return 0;
> +
> 	if (!bpf_map__is_internal(map)) {
> -		ret = btf__get_map_kv_tids(btf, map->name, def->key_size,
> +		ret = btf__get_map_kv_tids(obj->btf, map->name, def->key_size,
> 					   def->value_size, &key_type_id,
> 					   &value_type_id);
> 	} else {
> @@ -1515,7 +1850,7 @@ static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
> 		 * LLVM annotates global data differently in BTF, that is,
> 		 * only as '.data', '.bss' or '.rodata'.
> 		 */
> -		ret = btf__find_by_name(btf,
> +		ret = btf__find_by_name(obj->btf,
> 				libbpf_type_to_btf_name[map->libbpf_type]);
> 	}
> 	if (ret < 0)
> @@ -1805,7 +2140,7 @@ bpf_object__create_maps(struct bpf_object *obj)
> 		    map->inner_map_fd >= 0)
> 			create_attr.inner_map_fd = map->inner_map_fd;
> 
> -		if (obj->btf && !bpf_map_find_btf_info(map, obj->btf)) {
> +		if (obj->btf && !bpf_map_find_btf_info(obj, map)) {
> 			create_attr.btf_fd = btf__fd(obj->btf);
> 			create_attr.btf_key_type_id = map->btf_key_type_id;
> 			create_attr.btf_value_type_id = map->btf_value_type_id;
> -- 
> 2.17.1
> 


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

* Re: [PATCH v2 bpf-next 03/11] libbpf: streamline ELF parsing error-handling
  2019-06-17 19:26 ` [PATCH v2 bpf-next 03/11] libbpf: streamline ELF parsing error-handling Andrii Nakryiko
@ 2019-06-17 19:46   ` Song Liu
  0 siblings, 0 replies; 29+ messages in thread
From: Song Liu @ 2019-06-17 19:46 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Andrii Nakryiko, Alexei Starovoitov, daniel, netdev, bpf, Kernel Team



> On Jun 17, 2019, at 12:26 PM, Andrii Nakryiko <andriin@fb.com> wrote:
> 
> Simplify ELF parsing logic by exiting early, as there is no common clean
> up path to execute. That makes it unnecessary to track when err was set
> and when it was cleared. It also reduces nesting in some places.
> 
> Signed-off-by: Andrii Nakryiko <andriin@fb.com>

Acked-by: Song Liu <songliubraving@fb.com>

<snip>

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

* Re: [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions
  2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
                   ` (10 preceding siblings ...)
  2019-06-17 19:27 ` [PATCH v2 bpf-next 11/11] selftests/bpf: convert remaining selftests " Andrii Nakryiko
@ 2019-06-17 21:17 ` Daniel Borkmann
  2019-06-17 22:17   ` Daniel Borkmann
  2019-06-18 21:37   ` Andrii Nakryiko
  11 siblings, 2 replies; 29+ messages in thread
From: Daniel Borkmann @ 2019-06-17 21:17 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: andrii.nakryiko, ast, netdev, bpf, kernel-team, jakub.kicinski, joe

On 06/17/2019 09:26 PM, Andrii Nakryiko wrote:
> This patch set implements initial version (as discussed at LSF/MM2019
> conference) of a new way to specify BPF maps, relying on BTF type information,
> which allows for easy extensibility, preserving forward and backward
> compatibility. See details and examples in description for patch #6.
> 
> [0] contains an outline of follow up extensions to be added after this basic
> set of features lands. They are useful by itself, but also allows to bring
> libbpf to feature-parity with iproute2 BPF loader. That should open a path
> forward for BPF loaders unification.
> 
> Patch #1 centralizes commonly used min/max macro in libbpf_internal.h.
> Patch #2 extracts .BTF and .BTF.ext loading loging from elf_collect().
> Patch #3 simplifies elf_collect() error-handling logic.
> Patch #4 refactors map initialization logic into user-provided maps and global
> data maps, in preparation to adding another way (BTF-defined maps).
> Patch #5 adds support for map definitions in multiple ELF sections and
> deprecates bpf_object__find_map_by_offset() API which doesn't appear to be
> used anymore and makes assumption that all map definitions reside in single
> ELF section.
> Patch #6 splits BTF intialization from sanitization/loading into kernel to
> preserve original BTF at the time of map initialization.
> Patch #7 adds support for BTF-defined maps.
> Patch #8 adds new test for BTF-defined map definition.
> Patches #9-11 convert test BPF map definitions to use BTF way.
> 
> [0] https://lore.kernel.org/bpf/CAEf4BzbfdG2ub7gCi0OYqBrUoChVHWsmOntWAkJt47=FE+km+A@mail.gmail.com/

Quoting above in here for some clarifications on the approach. Basically for
iproute2, we would add libbpf library support on top of the current loader,
this means existing object files keep working as-is, and users would have to
decide whether they want to go with the new format or stick to the old one;
incentive for the new format would be to get all the other libbpf features
from upstream automatically. Though it means that once they switch there is
no object file compatibility with older iproute2 versions anymore. For the
case of Cilium, the container image ships with its own iproute2 version as
we don't want to rely on distros that they keep iproute2<->kernel release in
sync (some really don't). Switch should be fine in our case. For people
upgrading, the 'external' behavior (e.g. bpf fs interaction etc) would need
to stay the same to not run into any service disruption when switching versions.

>1. Pinning. This one is simple:
>  - add pinning attribute, that will either be "no pinning", "global
>pinning", "object-scope pinning".
>  - by default pinning root will be "/sys/fs/bpf", but one will be
>able to override this per-object using extra options (so that
>"/sys/fs/bpf/tc" can be specified).

I would just drop the object-scope pinning. We avoided using it and I'm not
aware if anyone else make use. It also has the ugly side-effect that this
relies on AF_ALG which e.g. on some cloud provider shipped kernels is disabled.
The pinning attribute should be part of the standard set of map attributes for
libbpf though as it's generally useful for networking applications.

>2. Map-in-map declaration:
>
>As outlined at LSF/MM, we can extend value type to be another map
>definition, specifying a prototype for inner map:
>
>struct {
>        int type;
>        int max_entries;
>        struct outer_key *key;
>        struct { /* this is definition of inner map */
>               int type;
>               int max_entries;
>               struct inner_key *key;
>               struct inner_value *value;
>        } value;
>} my_hash_of_arrays BPF_MAP = {
>        .type = BPF_MAP_TYPE_HASH_OF_MAPS,
>        .max_entries = 1024,
>        .value = {
>                .type = BPF_MAP_TYPE_ARRAY,
>                .max_entries = 64,
>        },
>};
>
>This would declare a hash_of_maps, where inner maps are arrays of 64
>elements each. Notice, that struct defining inner map can be declared
>outside and shared with other maps:
>
>struct inner_map_t {
>        int type;
>        int max_entries;
>        struct inner_key *key;
>        struct inner_value *value;
>};
>
>struct {
>        int type;
>        int max_entries;
>        struct outer_key *key;
>        struct inner_map_t value;
>} my_hash_of_arrays BPF_MAP = {
>        .type = BPF_MAP_TYPE_HASH_OF_MAPS,
>        .max_entries = 1024,
>        .value = {
>                .type = BPF_MAP_TYPE_ARRAY,
>                .max_entries = 64,
>        },
>};

This one feels a bit weird to me. My expectation would have been something
around the following to make this work:

  struct my_inner_map {
         int type;
         int max_entries;
         int *key;
         struct my_value *value;
  } btf_inner SEC(".maps") = {
         .type = BPF_MAP_TYPE_ARRAY,
         .max_entries = 16,
  };

And:

  struct {
         int type;
         int max_entries;
         int *key;
         struct my_inner_map *value;
  } btf_outer SEC(".maps") = {
         .type = BPF_MAP_TYPE_ARRAY,
         .max_entries = 16,
         .value = &btf_inner,
  };

And the loader should figure this out and combine everything in the background.
Otherwise above 'struct inner_map_t value' would be mixing convention of using
pointer vs non-pointer which may be even more confusing.

>3. Initialization of prog array. Iproute2 supports a convention-driven
>initialization of BPF_MAP_TYPE_PROG_ARRAY using special section names
>(wrapped into __section_tail(ID, IDX)):
>
>struct bpf_elf_map SEC("maps") POLICY_CALL_MAP = {
>        .type = BPF_MAP_TYPE_PROG_ARRAY,
>        .id = MAP_ID,
>        .size_key = sizeof(__u32),
>        .size_value = sizeof(__u32),
>        .max_elem = 16,
>};
>
>__section_tail(MAP_ID, MAP_IDX) int handle_policy(struct __sk_buff *skb)
>{
>        ...
>}
>
>For each such program, iproute2 will put its FD (for later
>tail-calling) into a corresponding MAP with id == MAP_ID at index
>MAP_IDX.
>
>Here's how I see this supported in BTF-defined maps case.
>
>typedef int (* skbuff_tailcall_fn)(struct __sk_buff *);
>
>struct {
>        int type;
>        int max_entries;
>        int *key;
>        skbuff_tailcall_fb value[];
>} POLICY_CALL_MAP SEC(".maps") = {
>        .type = BPF_MAP_TYPE_PROG_ARRAY,
>        .max_entries = 16,
>        .value = {
>                &handle_policy,
>                NULL,
>                &handle_some_other_policy,
>        },
>};
>
>libbpf loader will greate BPF_MAP_TYPE_PROG_ARRAY map with 16 elements
>and will initialize first and third entries with FDs of handle_policy
>and handle_some_other_policy programs. As an added nice bonus,
>compiler should also warn on signature mismatch. ;)

Seems okay, I guess the explicit initialization could lead people to think
that /after/ loading completed the NULL entries really won't have anything in
that tail call slot. In our case, we share some of the tail call maps for different
programs and each a different __section_tail(, idx), but in the end it's just a
matter of initialization by index for the above. iproute2 today fetches the map
from bpf fs if present, and only updates slots with __section_tail() present in
the object file. Invocation would again be via index I presume (tail_call(skb,
&policy_map, skb->mark), for example). For the __section_tail(MAP_ID, MAP_IDX),
we do dynamically generate the MAP_IDX define in some cases, but that MAP_IDX
would then simply be used in above POLICY_CALL_MAP instead; seems fine.

>4. We can extend this idea into ARRAY_OF_MAPS initialization. This is
>currently implemented in iproute2 using .id, .inner_id, and .inner_idx
>fields.
>
>struct inner_map_t {
>        int type;
>        int max_entries;
>        struct inner_key *key;
>        struct inner_value *value;
>};
>
>struct inner_map_t map1 = {...};
>struct inner_map_t map2 = {...};
>
>struct {
>        int type;
>        int max_entries;
>        struct outer_key *key;
>        struct inner_map_t value[];
>} my_hash_of_arrays BPF_MAP = {
>        .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
>        .max_entries = 2,
>        .value = {
>                &map1,
>                &map2,
>        },
>};

Yeah, agree.

> v1->v2:
> - more BTF-sanity checks in parsing map definitions (Song);
> - removed confusing usage of "attribute", switched to "field;
> - split off elf_collect() refactor from btf loading refactor (Song);
> - split selftests conversion into 3 patches (Stanislav):
>   1. test already relying on BTF;
>   2. tests w/ custom types as key/value (so benefiting from BTF);
>   3. all the rest tests (integers as key/value, special maps w/o BTF support).
> - smaller code improvements (Song);
> 
> rfc->v1:
> - error out on unknown field by default (Stanislav, Jakub, Lorenz);
>  
> Andrii Nakryiko (11):
>   libbpf: add common min/max macro to libbpf_internal.h
>   libbpf: extract BTF loading logic
>   libbpf: streamline ELF parsing error-handling
>   libbpf: refactor map initialization
>   libbpf: identify maps by section index in addition to offset
>   libbpf: split initialization and loading of BTF
>   libbpf: allow specifying map definitions using BTF
>   selftests/bpf: add test for BTF-defined maps
>   selftests/bpf: switch BPF_ANNOTATE_KV_PAIR tests to BTF-defined maps
>   selftests/bpf: convert tests w/ custom values to BTF-defined maps
>   selftests/bpf: convert remaining selftests to BTF-defined maps
> 
>  tools/lib/bpf/bpf.c                           |   7 +-
>  tools/lib/bpf/bpf_prog_linfo.c                |   5 +-
>  tools/lib/bpf/btf.c                           |   3 -
>  tools/lib/bpf/btf.h                           |   1 +
>  tools/lib/bpf/btf_dump.c                      |   3 -
>  tools/lib/bpf/libbpf.c                        | 781 +++++++++++++-----
>  tools/lib/bpf/libbpf_internal.h               |   7 +
>  tools/testing/selftests/bpf/progs/bpf_flow.c  |  18 +-
>  .../selftests/bpf/progs/get_cgroup_id_kern.c  |  18 +-
>  .../testing/selftests/bpf/progs/netcnt_prog.c |  22 +-
>  .../selftests/bpf/progs/sample_map_ret0.c     |  18 +-
>  .../selftests/bpf/progs/socket_cookie_prog.c  |  11 +-
>  .../bpf/progs/sockmap_verdict_prog.c          |  36 +-
>  .../selftests/bpf/progs/test_btf_newkv.c      |  73 ++
>  .../bpf/progs/test_get_stack_rawtp.c          |  27 +-
>  .../selftests/bpf/progs/test_global_data.c    |  27 +-
>  tools/testing/selftests/bpf/progs/test_l4lb.c |  45 +-
>  .../selftests/bpf/progs/test_l4lb_noinline.c  |  45 +-
>  .../selftests/bpf/progs/test_map_in_map.c     |  20 +-
>  .../selftests/bpf/progs/test_map_lock.c       |  22 +-
>  .../testing/selftests/bpf/progs/test_obj_id.c |   9 +-
>  .../bpf/progs/test_select_reuseport_kern.c    |  45 +-
>  .../bpf/progs/test_send_signal_kern.c         |  22 +-
>  .../bpf/progs/test_skb_cgroup_id_kern.c       |   9 +-
>  .../bpf/progs/test_sock_fields_kern.c         |  60 +-
>  .../selftests/bpf/progs/test_spin_lock.c      |  33 +-
>  .../bpf/progs/test_stacktrace_build_id.c      |  44 +-
>  .../selftests/bpf/progs/test_stacktrace_map.c |  40 +-
>  .../testing/selftests/bpf/progs/test_tc_edt.c |   9 +-
>  .../bpf/progs/test_tcp_check_syncookie_kern.c |   9 +-
>  .../selftests/bpf/progs/test_tcp_estats.c     |   9 +-
>  .../selftests/bpf/progs/test_tcpbpf_kern.c    |  18 +-
>  .../selftests/bpf/progs/test_tcpnotify_kern.c |  18 +-
>  tools/testing/selftests/bpf/progs/test_xdp.c  |  18 +-
>  .../selftests/bpf/progs/test_xdp_noinline.c   |  60 +-
>  tools/testing/selftests/bpf/test_btf.c        |  10 +-
>  .../selftests/bpf/test_queue_stack_map.h      |  20 +-
>  .../testing/selftests/bpf/test_sockmap_kern.h |  72 +-
>  38 files changed, 1199 insertions(+), 495 deletions(-)
>  create mode 100644 tools/testing/selftests/bpf/progs/test_btf_newkv.c
> 


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

* Re: [PATCH v2 bpf-next 09/11] selftests/bpf: switch BPF_ANNOTATE_KV_PAIR tests to BTF-defined maps
  2019-06-17 19:26 ` [PATCH v2 bpf-next 09/11] selftests/bpf: switch BPF_ANNOTATE_KV_PAIR tests to " Andrii Nakryiko
@ 2019-06-17 21:41   ` Song Liu
  2019-06-17 22:20     ` Daniel Borkmann
  0 siblings, 1 reply; 29+ messages in thread
From: Song Liu @ 2019-06-17 21:41 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Andrii Nakryiko, Alexei Starovoitov, daniel, netdev, bpf, Kernel Team



> On Jun 17, 2019, at 12:26 PM, Andrii Nakryiko <andriin@fb.com> wrote:
> 
> Switch tests that already rely on BTF to BTF-defined map definitions.
> 
> Signed-off-by: Andrii Nakryiko <andriin@fb.com>


For 09 to 11:

Acked-by: Song Liu <songliubraving@fb.com>



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

* Re: [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions
  2019-06-17 21:17 ` [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Daniel Borkmann
@ 2019-06-17 22:17   ` Daniel Borkmann
  2019-06-18 21:37   ` Andrii Nakryiko
  1 sibling, 0 replies; 29+ messages in thread
From: Daniel Borkmann @ 2019-06-17 22:17 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: andrii.nakryiko, ast, netdev, bpf, kernel-team, jakub.kicinski, joe

On 06/17/2019 11:17 PM, Daniel Borkmann wrote:
> On 06/17/2019 09:26 PM, Andrii Nakryiko wrote:
>> This patch set implements initial version (as discussed at LSF/MM2019
>> conference) of a new way to specify BPF maps, relying on BTF type information,
>> which allows for easy extensibility, preserving forward and backward
>> compatibility. See details and examples in description for patch #6.
>>
>> [0] contains an outline of follow up extensions to be added after this basic
>> set of features lands. They are useful by itself, but also allows to bring
>> libbpf to feature-parity with iproute2 BPF loader. That should open a path
>> forward for BPF loaders unification.
>>
>> Patch #1 centralizes commonly used min/max macro in libbpf_internal.h.
>> Patch #2 extracts .BTF and .BTF.ext loading loging from elf_collect().
>> Patch #3 simplifies elf_collect() error-handling logic.
>> Patch #4 refactors map initialization logic into user-provided maps and global
>> data maps, in preparation to adding another way (BTF-defined maps).
>> Patch #5 adds support for map definitions in multiple ELF sections and
>> deprecates bpf_object__find_map_by_offset() API which doesn't appear to be
>> used anymore and makes assumption that all map definitions reside in single
>> ELF section.
>> Patch #6 splits BTF intialization from sanitization/loading into kernel to
>> preserve original BTF at the time of map initialization.
>> Patch #7 adds support for BTF-defined maps.
>> Patch #8 adds new test for BTF-defined map definition.
>> Patches #9-11 convert test BPF map definitions to use BTF way.

LGTM as a base, applied 1-10 as per Stanislav's concern, added Song's Ack to
patch 10, and fixed up typos in patch 2 while at it.

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

* Re: [PATCH v2 bpf-next 09/11] selftests/bpf: switch BPF_ANNOTATE_KV_PAIR tests to BTF-defined maps
  2019-06-17 21:41   ` Song Liu
@ 2019-06-17 22:20     ` Daniel Borkmann
  0 siblings, 0 replies; 29+ messages in thread
From: Daniel Borkmann @ 2019-06-17 22:20 UTC (permalink / raw)
  To: Song Liu, Andrii Nakryiko
  Cc: Andrii Nakryiko, Alexei Starovoitov, netdev, bpf, Kernel Team

On 06/17/2019 11:41 PM, Song Liu wrote:
>> On Jun 17, 2019, at 12:26 PM, Andrii Nakryiko <andriin@fb.com> wrote:
>>
>> Switch tests that already rely on BTF to BTF-defined map definitions.
>>
>> Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> 
> For 09 to 11:
> 
> Acked-by: Song Liu <songliubraving@fb.com>

I've added it to patch 10 by hand given the manual labor for dropping 11 anyway.
Please keep in mind that patchwork doesn't understand/propagate 'for 09 to 11',
and explicitly ack in future, thanks.

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

* Re: [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions
  2019-06-17 21:17 ` [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Daniel Borkmann
  2019-06-17 22:17   ` Daniel Borkmann
@ 2019-06-18 21:37   ` Andrii Nakryiko
  2019-06-20 14:49     ` Lorenz Bauer
  1 sibling, 1 reply; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-18 21:37 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: Andrii Nakryiko, Alexei Starovoitov, Networking, bpf,
	Kernel Team, Jakub Kicinski, Joe Stringer

On Mon, Jun 17, 2019 at 2:17 PM Daniel Borkmann <daniel@iogearbox.net> wrote:
>
> On 06/17/2019 09:26 PM, Andrii Nakryiko wrote:
> > This patch set implements initial version (as discussed at LSF/MM2019
> > conference) of a new way to specify BPF maps, relying on BTF type information,
> > which allows for easy extensibility, preserving forward and backward
> > compatibility. See details and examples in description for patch #6.

Thanks for applying! Sorry for a bit of delayed in replying.

> >
> > [0] contains an outline of follow up extensions to be added after this basic
> > set of features lands. They are useful by itself, but also allows to bring
> > libbpf to feature-parity with iproute2 BPF loader. That should open a path
> > forward for BPF loaders unification.
> >
> > Patch #1 centralizes commonly used min/max macro in libbpf_internal.h.
> > Patch #2 extracts .BTF and .BTF.ext loading loging from elf_collect().
> > Patch #3 simplifies elf_collect() error-handling logic.
> > Patch #4 refactors map initialization logic into user-provided maps and global
> > data maps, in preparation to adding another way (BTF-defined maps).
> > Patch #5 adds support for map definitions in multiple ELF sections and
> > deprecates bpf_object__find_map_by_offset() API which doesn't appear to be
> > used anymore and makes assumption that all map definitions reside in single
> > ELF section.
> > Patch #6 splits BTF intialization from sanitization/loading into kernel to
> > preserve original BTF at the time of map initialization.
> > Patch #7 adds support for BTF-defined maps.
> > Patch #8 adds new test for BTF-defined map definition.
> > Patches #9-11 convert test BPF map definitions to use BTF way.
> >
> > [0] https://lore.kernel.org/bpf/CAEf4BzbfdG2ub7gCi0OYqBrUoChVHWsmOntWAkJt47=FE+km+A@mail.gmail.com/
>
> Quoting above in here for some clarifications on the approach. Basically for
> iproute2, we would add libbpf library support on top of the current loader,
> this means existing object files keep working as-is, and users would have to
> decide whether they want to go with the new format or stick to the old one;
> incentive for the new format would be to get all the other libbpf features
> from upstream automatically. Though it means that once they switch there is
> no object file compatibility with older iproute2 versions anymore. For the
> case of Cilium, the container image ships with its own iproute2 version as
> we don't want to rely on distros that they keep iproute2<->kernel release in
> sync (some really don't). Switch should be fine in our case. For people
> upgrading, the 'external' behavior (e.g. bpf fs interaction etc) would need
> to stay the same to not run into any service disruption when switching versions.
>
> >1. Pinning. This one is simple:
> >  - add pinning attribute, that will either be "no pinning", "global
> >pinning", "object-scope pinning".
> >  - by default pinning root will be "/sys/fs/bpf", but one will be
> >able to override this per-object using extra options (so that
> >"/sys/fs/bpf/tc" can be specified).
>
> I would just drop the object-scope pinning. We avoided using it and I'm not
> aware if anyone else make use. It also has the ugly side-effect that this
> relies on AF_ALG which e.g. on some cloud provider shipped kernels is disabled.
> The pinning attribute should be part of the standard set of map attributes for
> libbpf though as it's generally useful for networking applications.

Sounds good. I'll do some more surveying of use cases inside FB to see
if anyone needs object-scope pinning, just to be sure we are not
short-cutting anyone.

>
> >2. Map-in-map declaration:
> >
> >As outlined at LSF/MM, we can extend value type to be another map
> >definition, specifying a prototype for inner map:
> >
> >struct {
> >        int type;
> >        int max_entries;
> >        struct outer_key *key;
> >        struct { /* this is definition of inner map */
> >               int type;
> >               int max_entries;
> >               struct inner_key *key;
> >               struct inner_value *value;
> >        } value;
> >} my_hash_of_arrays BPF_MAP = {
> >        .type = BPF_MAP_TYPE_HASH_OF_MAPS,
> >        .max_entries = 1024,
> >        .value = {
> >                .type = BPF_MAP_TYPE_ARRAY,
> >                .max_entries = 64,
> >        },
> >};
> >
> >This would declare a hash_of_maps, where inner maps are arrays of 64
> >elements each. Notice, that struct defining inner map can be declared
> >outside and shared with other maps:
> >
> >struct inner_map_t {
> >        int type;
> >        int max_entries;
> >        struct inner_key *key;
> >        struct inner_value *value;
> >};
> >
> >struct {
> >        int type;
> >        int max_entries;
> >        struct outer_key *key;
> >        struct inner_map_t value;
> >} my_hash_of_arrays BPF_MAP = {
> >        .type = BPF_MAP_TYPE_HASH_OF_MAPS,
> >        .max_entries = 1024,
> >        .value = {
> >                .type = BPF_MAP_TYPE_ARRAY,
> >                .max_entries = 64,
> >        },
> >};
>
> This one feels a bit weird to me. My expectation would have been something
> around the following to make this work:
>
>   struct my_inner_map {
>          int type;
>          int max_entries;
>          int *key;
>          struct my_value *value;
>   } btf_inner SEC(".maps") = {
>          .type = BPF_MAP_TYPE_ARRAY,
>          .max_entries = 16,
>   };
>
> And:
>
>   struct {
>          int type;
>          int max_entries;
>          int *key;
>          struct my_inner_map *value;
>   } btf_outer SEC(".maps") = {
>          .type = BPF_MAP_TYPE_ARRAY,
>          .max_entries = 16,
>          .value = &btf_inner,
>   };
>
> And the loader should figure this out and combine everything in the background.
> Otherwise above 'struct inner_map_t value' would be mixing convention of using
> pointer vs non-pointer which may be even more confusing.

There are two reasons I didn't want to go with that approach:

1. This syntax makes my_inner_map usable as a stand-alone map, while
it's purpose is to serve as a inner map prototype. While technically
it is ok to use my_inner_map as real map, it's kind of confusing and
feels unclean.
2. This approach doesn't play well with case where we want to
pre-initialize array-of-maps with links to other maps. E.g., compare
w/ this:

struct {
        int type;
        int max_entries;
        int *key;
        struct my_inner_map *values[];
} btf_outer_initialized SEC(".maps") = {
        .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
        .max_entries = 16,
        .values = {
            &my_inner_map1,
            &my_inner_map2,
        },
};

In your case inner_map is a template, in this case my_inner_map1 and
my_inner_map2 is assigned to slots 0 and 1, respectively. But they
look deceivingly similar.

But in any case, we got to discussing details of map-in-map
initialization with Alexei, and concluded that for map-in-map cases
this split of key/value types being defined in struct definition and
flags/sizes/type being a compile-time assigned values becomes too much
of error-prone approach.
So we came up with a way to "encode" integer constants as part of BTF
type information, so that *all* declarative information is part of BTF
type, w/o the need to compile-time initialization. We tried to go the
other way (what Jakub was pushing for), but we couldn't figure out
anything that would work w/o more compiler hacks. So here's the
updated proposal:

#define __int(name, val) int (*name)[val]
#define __type(name, val) val (*foo)

struct my_value {
        int a;
        int b;
};

struct my_inner_map {
        __int(type, BPF_MAP_TYPE_ARRAY);
        __int(max_entries, 1000);
        __type(key, int);
        __type(value, struct my_value);
};

struct my_inner_map imap1 SEC(".maps");
struct my_inner_map imap2 SEC(".maps");

static struct {
        __int(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
        __int(max_entries, 1000);
        __type(key, int);
        __type(value, struct my_inner_map);
} my_outer_map SEC(".maps");

static struct {
        __int(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
        __int(max_entries, 1000);
        __type(key, int);
        __type(value, struct my_inner_map);
        struct my_inner_map *values[];
} my_initialized_outer_map SEC(".maps") = {
        .values = {
                &imap1,
                [500] = &imap2,
        },
};

Here struct my_inner_map is complete definition of array map w/ 1000
elements w/ all the type info for k/v. That struct is used as a
template for my_outer_map map-in-map. my_initialized_outer_map is the
case of pre-initialization of array-of-maps w/ instances of existing
maps imap1 and imap2.

The idea is that we encode integer fields as array dimensions + use
pointer to an array to save space. Given that syntax in plain C is a
bit ugly and hard to remember, we hide that behind __int macro. Then
in line with __int, we also have __type macro, that hides that hateful
pointer for key/value types. This allows map definition to be
self-describing w/o having to look at initialized ELF data section at
all, except for special cases of explicitly initializing map-in-map or
prog_array.

What do you think?

>
> >3. Initialization of prog array. Iproute2 supports a convention-driven
> >initialization of BPF_MAP_TYPE_PROG_ARRAY using special section names
> >(wrapped into __section_tail(ID, IDX)):
> >
> >struct bpf_elf_map SEC("maps") POLICY_CALL_MAP = {
> >        .type = BPF_MAP_TYPE_PROG_ARRAY,
> >        .id = MAP_ID,
> >        .size_key = sizeof(__u32),
> >        .size_value = sizeof(__u32),
> >        .max_elem = 16,
> >};
> >
> >__section_tail(MAP_ID, MAP_IDX) int handle_policy(struct __sk_buff *skb)
> >{
> >        ...
> >}
> >
> >For each such program, iproute2 will put its FD (for later
> >tail-calling) into a corresponding MAP with id == MAP_ID at index
> >MAP_IDX.
> >
> >Here's how I see this supported in BTF-defined maps case.
> >
> >typedef int (* skbuff_tailcall_fn)(struct __sk_buff *);
> >
> >struct {
> >        int type;
> >        int max_entries;
> >        int *key;
> >        skbuff_tailcall_fb value[];
> >} POLICY_CALL_MAP SEC(".maps") = {
> >        .type = BPF_MAP_TYPE_PROG_ARRAY,
> >        .max_entries = 16,
> >        .value = {
> >                &handle_policy,
> >                NULL,
> >                &handle_some_other_policy,
> >        },
> >};
> >
> >libbpf loader will greate BPF_MAP_TYPE_PROG_ARRAY map with 16 elements
> >and will initialize first and third entries with FDs of handle_policy
> >and handle_some_other_policy programs. As an added nice bonus,
> >compiler should also warn on signature mismatch. ;)
>
> Seems okay, I guess the explicit initialization could lead people to think
> that /after/ loading completed the NULL entries really won't have anything in
> that tail call slot. In our case, we share some of the tail call maps for different
> programs and each a different __section_tail(, idx), but in the end it's just a
> matter of initialization by index for the above. iproute2 today fetches the map
> from bpf fs if present, and only updates slots with __section_tail() present in
> the object file. Invocation would again be via index I presume (tail_call(skb,
> &policy_map, skb->mark), for example). For the __section_tail(MAP_ID, MAP_IDX),
> we do dynamically generate the MAP_IDX define in some cases, but that MAP_IDX
> would then simply be used in above POLICY_CALL_MAP instead; seems fine.

Yeah I can definitely see some confusion here. But it seems like this
is more of a semantics of map sharing, and maybe it should be some
extra option for when we have automatic support for extern (shared)
maps. E.g., something like

__int(sharing, SHARE_STRATEGY_MERGE) vs __int(sharing, SHARE_STRATEGY_OVERWRITE)

Haven't though through exact syntax, naming, semantics, but it seems
doable to support both, depending on desired behavior.

Maybe we should also unify this w/ pinning? E.g., there are many
sensible ways to handle already existing pinned map:

1. Reject program (e.g., if BPF application is the source of truth for that map)
2. Use pinned as is (e.g., if BPF application wants to consume data
from source of truth app)
3. Merge (what you described above)
4. Replace/reset - not sure if useful/desirable.

I'll need to study existing use cases a bit more though...

>
> >4. We can extend this idea into ARRAY_OF_MAPS initialization. This is
> >currently implemented in iproute2 using .id, .inner_id, and .inner_idx
> >fields.
> >
> >struct inner_map_t {
> >        int type;
> >        int max_entries;
> >        struct inner_key *key;
> >        struct inner_value *value;
> >};
> >
> >struct inner_map_t map1 = {...};
> >struct inner_map_t map2 = {...};
> >
> >struct {
> >        int type;
> >        int max_entries;
> >        struct outer_key *key;
> >        struct inner_map_t value[];
> >} my_hash_of_arrays BPF_MAP = {
> >        .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
> >        .max_entries = 2,
> >        .value = {
> >                &map1,
> >                &map2,
> >        },
> >};
>
> Yeah, agree.

See above w/ updated proposal. For exactly the same outcome.

>
> > v1->v2:
> > - more BTF-sanity checks in parsing map definitions (Song);
> > - removed confusing usage of "attribute", switched to "field;
> > - split off elf_collect() refactor from btf loading refactor (Song);
> > - split selftests conversion into 3 patches (Stanislav):
> >   1. test already relying on BTF;
> >   2. tests w/ custom types as key/value (so benefiting from BTF);
> >   3. all the rest tests (integers as key/value, special maps w/o BTF support).
> > - smaller code improvements (Song);
> >
> > rfc->v1:
> > - error out on unknown field by default (Stanislav, Jakub, Lorenz);
> >
> > Andrii Nakryiko (11):
> >   libbpf: add common min/max macro to libbpf_internal.h
> >   libbpf: extract BTF loading logic
> >   libbpf: streamline ELF parsing error-handling
> >   libbpf: refactor map initialization
> >   libbpf: identify maps by section index in addition to offset
> >   libbpf: split initialization and loading of BTF
> >   libbpf: allow specifying map definitions using BTF
> >   selftests/bpf: add test for BTF-defined maps
> >   selftests/bpf: switch BPF_ANNOTATE_KV_PAIR tests to BTF-defined maps
> >   selftests/bpf: convert tests w/ custom values to BTF-defined maps
> >   selftests/bpf: convert remaining selftests to BTF-defined maps
> >
> >  tools/lib/bpf/bpf.c                           |   7 +-
> >  tools/lib/bpf/bpf_prog_linfo.c                |   5 +-
> >  tools/lib/bpf/btf.c                           |   3 -
> >  tools/lib/bpf/btf.h                           |   1 +
> >  tools/lib/bpf/btf_dump.c                      |   3 -
> >  tools/lib/bpf/libbpf.c                        | 781 +++++++++++++-----
> >  tools/lib/bpf/libbpf_internal.h               |   7 +
> >  tools/testing/selftests/bpf/progs/bpf_flow.c  |  18 +-
> >  .../selftests/bpf/progs/get_cgroup_id_kern.c  |  18 +-
> >  .../testing/selftests/bpf/progs/netcnt_prog.c |  22 +-
> >  .../selftests/bpf/progs/sample_map_ret0.c     |  18 +-
> >  .../selftests/bpf/progs/socket_cookie_prog.c  |  11 +-
> >  .../bpf/progs/sockmap_verdict_prog.c          |  36 +-
> >  .../selftests/bpf/progs/test_btf_newkv.c      |  73 ++
> >  .../bpf/progs/test_get_stack_rawtp.c          |  27 +-
> >  .../selftests/bpf/progs/test_global_data.c    |  27 +-
> >  tools/testing/selftests/bpf/progs/test_l4lb.c |  45 +-
> >  .../selftests/bpf/progs/test_l4lb_noinline.c  |  45 +-
> >  .../selftests/bpf/progs/test_map_in_map.c     |  20 +-
> >  .../selftests/bpf/progs/test_map_lock.c       |  22 +-
> >  .../testing/selftests/bpf/progs/test_obj_id.c |   9 +-
> >  .../bpf/progs/test_select_reuseport_kern.c    |  45 +-
> >  .../bpf/progs/test_send_signal_kern.c         |  22 +-
> >  .../bpf/progs/test_skb_cgroup_id_kern.c       |   9 +-
> >  .../bpf/progs/test_sock_fields_kern.c         |  60 +-
> >  .../selftests/bpf/progs/test_spin_lock.c      |  33 +-
> >  .../bpf/progs/test_stacktrace_build_id.c      |  44 +-
> >  .../selftests/bpf/progs/test_stacktrace_map.c |  40 +-
> >  .../testing/selftests/bpf/progs/test_tc_edt.c |   9 +-
> >  .../bpf/progs/test_tcp_check_syncookie_kern.c |   9 +-
> >  .../selftests/bpf/progs/test_tcp_estats.c     |   9 +-
> >  .../selftests/bpf/progs/test_tcpbpf_kern.c    |  18 +-
> >  .../selftests/bpf/progs/test_tcpnotify_kern.c |  18 +-
> >  tools/testing/selftests/bpf/progs/test_xdp.c  |  18 +-
> >  .../selftests/bpf/progs/test_xdp_noinline.c   |  60 +-
> >  tools/testing/selftests/bpf/test_btf.c        |  10 +-
> >  .../selftests/bpf/test_queue_stack_map.h      |  20 +-
> >  .../testing/selftests/bpf/test_sockmap_kern.h |  72 +-
> >  38 files changed, 1199 insertions(+), 495 deletions(-)
> >  create mode 100644 tools/testing/selftests/bpf/progs/test_btf_newkv.c
> >
>

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

* Re: [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions
  2019-06-18 21:37   ` Andrii Nakryiko
@ 2019-06-20 14:49     ` Lorenz Bauer
  2019-06-21  4:19       ` Andrii Nakryiko
  0 siblings, 1 reply; 29+ messages in thread
From: Lorenz Bauer @ 2019-06-20 14:49 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Daniel Borkmann, Andrii Nakryiko, Alexei Starovoitov, Networking,
	bpf, Kernel Team, Jakub Kicinski, Joe Stringer

On Tue, 18 Jun 2019 at 22:37, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:

> > I would just drop the object-scope pinning. We avoided using it and I'm not
> > aware if anyone else make use. It also has the ugly side-effect that this
> > relies on AF_ALG which e.g. on some cloud provider shipped kernels is disabled.
> > The pinning attribute should be part of the standard set of map attributes for
> > libbpf though as it's generally useful for networking applications.
>
> Sounds good. I'll do some more surveying of use cases inside FB to see
> if anyone needs object-scope pinning, just to be sure we are not
> short-cutting anyone.

I'm also curious what the use cases for declarative pinning are. From my
limited POV it doesn't seem that useful? There are a couple of factors:

* Systemd mounts the default location only accessible to root, so I have to
  used my own bpffs mount.
* Since I don't want to hard code that, I put it in a config file.
* After loading the ELF we pin maps from the daemon managing the XDP.

How do other people work around this? Hard coding it in the ELF seems
suboptimal.

> > And the loader should figure this out and combine everything in the background.
> > Otherwise above 'struct inner_map_t value' would be mixing convention of using
> > pointer vs non-pointer which may be even more confusing.
>
> There are two reasons I didn't want to go with that approach:
>
> 1. This syntax makes my_inner_map usable as a stand-alone map, while
> it's purpose is to serve as a inner map prototype. While technically
> it is ok to use my_inner_map as real map, it's kind of confusing and
> feels unclean.

I agree, avoiding this problem is good.

> So we came up with a way to "encode" integer constants as part of BTF
> type information, so that *all* declarative information is part of BTF
> type, w/o the need to compile-time initialization. We tried to go the
> other way (what Jakub was pushing for), but we couldn't figure out
> anything that would work w/o more compiler hacks. So here's the
> updated proposal:
>
> #define __int(name, val) int (*name)[val]

Consider my mind blown: https://cdecl.org/?q=int+%28*foo%29%5B10%5D

> #define __type(name, val) val (*foo)

Maybe it's enough to just hide the pointer-ness?

  #define __member(name) (*name)
  struct my_value __member(value);

> struct my_inner_map {
>         __int(type, BPF_MAP_TYPE_ARRAY);
>         __int(max_entries, 1000);
>         __type(key, int);
>         __type(value, struct my_value);

What if this did

  __type(value, struct my_value)[1000];
  struct my_value __member(value)[1000]; // alternative

instead, and skipped max_entries?

> static struct {
>         __int(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
>         __int(max_entries, 1000);
>         __type(key, int);
>         __type(value, struct my_inner_map);
>         struct my_inner_map *values[];
> } my_initialized_outer_map SEC(".maps") = {
>         .values = {
>                 &imap1,
>                 [500] = &imap2,
>         },
> };
>
> Here struct my_inner_map is complete definition of array map w/ 1000
> elements w/ all the type info for k/v. That struct is used as a
> template for my_outer_map map-in-map. my_initialized_outer_map is the
> case of pre-initialization of array-of-maps w/ instances of existing
> maps imap1 and imap2.

For my_initialized_outer_map, which section does .values end up in the
generated ELF? How much space is going to be allocated? 501 * 4 bytes?

> The idea is that we encode integer fields as array dimensions + use
> pointer to an array to save space. Given that syntax in plain C is a
> bit ugly and hard to remember, we hide that behind __int macro. Then
> in line with __int, we also have __type macro, that hides that hateful
> pointer for key/value types. This allows map definition to be
> self-describing w/o having to look at initialized ELF data section at
> all, except for special cases of explicitly initializing map-in-map or
> prog_array.
>
> What do you think?

I think this is an interesting approach. One thing I'm not sure of is handling
these types from C. For example:

  sizeof(my_outer_map.value)

This compiles, but doesn't produce the intended result. Correct would be:

  sizeof(my_outer_map.value[0])

At that point you have to understand that value is a pointer so all of
our efforts
are for naught. I suspect there is other weirdness like this, but I need to play
with it a little bit more.

> Yeah I can definitely see some confusion here. But it seems like this
> is more of a semantics of map sharing, and maybe it should be some
> extra option for when we have automatic support for extern (shared)
> maps. E.g., something like
>
> __int(sharing, SHARE_STRATEGY_MERGE) vs __int(sharing, SHARE_STRATEGY_OVERWRITE)
>
> Haven't though through exact syntax, naming, semantics, but it seems
> doable to support both, depending on desired behavior.
>
> Maybe we should also unify this w/ pinning? E.g., there are many
> sensible ways to handle already existing pinned map:
>
> 1. Reject program (e.g., if BPF application is the source of truth for that map)
> 2. Use pinned as is (e.g., if BPF application wants to consume data
> from source of truth app)
> 3. Merge (what you described above)
> 4. Replace/reset - not sure if useful/desirable.

From my experience, trying to support many use cases in a purely declarative
fashion ends up creating many edge cases, and quirky behaviour that is hard to
fix later on. It's a bit like merging dictionaries in $LANGUAGE,
which starts out simple and then gets complicated because sometimes you
want to override a key, but lists should be concatenated, except in
that one case...

I wonder: are there many use cases where writing some glue code isn't
possible? With libbpf getting more mature APIs that should become easier and
easier. We could probably support existing iproute2 features that way as well.

-- 
Lorenz Bauer  |  Systems Engineer
6th Floor, County Hall/The Riverside Building, SE1 7PB, UK

www.cloudflare.com

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

* Re: [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions
  2019-06-20 14:49     ` Lorenz Bauer
@ 2019-06-21  4:19       ` Andrii Nakryiko
  2019-06-21 10:29         ` Lorenz Bauer
  0 siblings, 1 reply; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-21  4:19 UTC (permalink / raw)
  To: Lorenz Bauer
  Cc: Daniel Borkmann, Andrii Nakryiko, Alexei Starovoitov, Networking,
	bpf, Kernel Team, Jakub Kicinski, Joe Stringer

On Thu, Jun 20, 2019 at 7:49 AM Lorenz Bauer <lmb@cloudflare.com> wrote:
>
> On Tue, 18 Jun 2019 at 22:37, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
>
> > > I would just drop the object-scope pinning. We avoided using it and I'm not
> > > aware if anyone else make use. It also has the ugly side-effect that this
> > > relies on AF_ALG which e.g. on some cloud provider shipped kernels is disabled.
> > > The pinning attribute should be part of the standard set of map attributes for
> > > libbpf though as it's generally useful for networking applications.
> >
> > Sounds good. I'll do some more surveying of use cases inside FB to see
> > if anyone needs object-scope pinning, just to be sure we are not
> > short-cutting anyone.
>
> I'm also curious what the use cases for declarative pinning are. From my
> limited POV it doesn't seem that useful? There are a couple of factors:

Cilium is using it pretty extensively, so there are clearly use cases.
The most straigtforward use case is using a map created and shared by
another BPF program (to communicate, read stats, what have you).

>
> * Systemd mounts the default location only accessible to root, so I have to
>   used my own bpffs mount.
> * Since I don't want to hard code that, I put it in a config file.
> * After loading the ELF we pin maps from the daemon managing the XDP.

So mounting root would be specified per bpf_object, before maps are
created, so user-land driving application will have an opportunity to
tune everything. Declarative is only the per-map decision of whether
that map should be exposed to outer world (for sharing) or not.

>
> How do other people work around this? Hard coding it in the ELF seems
> suboptimal.
>
> > > And the loader should figure this out and combine everything in the background.
> > > Otherwise above 'struct inner_map_t value' would be mixing convention of using
> > > pointer vs non-pointer which may be even more confusing.
> >
> > There are two reasons I didn't want to go with that approach:
> >
> > 1. This syntax makes my_inner_map usable as a stand-alone map, while
> > it's purpose is to serve as a inner map prototype. While technically
> > it is ok to use my_inner_map as real map, it's kind of confusing and
> > feels unclean.
>
> I agree, avoiding this problem is good.
>
> > So we came up with a way to "encode" integer constants as part of BTF
> > type information, so that *all* declarative information is part of BTF
> > type, w/o the need to compile-time initialization. We tried to go the
> > other way (what Jakub was pushing for), but we couldn't figure out
> > anything that would work w/o more compiler hacks. So here's the
> > updated proposal:
> >
> > #define __int(name, val) int (*name)[val]
>
> Consider my mind blown: https://cdecl.org/?q=int+%28*foo%29%5B10%5D

Then check tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
for more crazy syntax ;)

typedef char * (* const (* const fn_ptr_arr2_t[5])())(char * (*)(int));


>
> > #define __type(name, val) val (*foo)
>
> Maybe it's enough to just hide the pointer-ness?
>
>   #define __member(name) (*name)
>   struct my_value __member(value);
>
> > struct my_inner_map {
> >         __int(type, BPF_MAP_TYPE_ARRAY);
> >         __int(max_entries, 1000);
> >         __type(key, int);
> >         __type(value, struct my_value);
>
> What if this did
>
>   __type(value, struct my_value)[1000];
>   struct my_value __member(value)[1000]; // alternative
>
> instead, and skipped max_entries?

I considered that, but decided for now to keep all those attributes
orthogonal for more flexibility and uniformity. This syntax might be
considered a nice "syntax sugar" and can be added in the future, if
necessary.

>
> > static struct {
> >         __int(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
> >         __int(max_entries, 1000);
> >         __type(key, int);
> >         __type(value, struct my_inner_map);
> >         struct my_inner_map *values[];
> > } my_initialized_outer_map SEC(".maps") = {
> >         .values = {
> >                 &imap1,
> >                 [500] = &imap2,
> >         },
> > };
> >
> > Here struct my_inner_map is complete definition of array map w/ 1000
> > elements w/ all the type info for k/v. That struct is used as a
> > template for my_outer_map map-in-map. my_initialized_outer_map is the
> > case of pre-initialization of array-of-maps w/ instances of existing
> > maps imap1 and imap2.
>
> For my_initialized_outer_map, which section does .values end up in the
> generated ELF? How much space is going to be allocated? 501 * 4 bytes?

Yes, if you want to pre-initialize it with values, you'll use
sizeof(void *) * max_entries ELF space.

>
> > The idea is that we encode integer fields as array dimensions + use
> > pointer to an array to save space. Given that syntax in plain C is a
> > bit ugly and hard to remember, we hide that behind __int macro. Then
> > in line with __int, we also have __type macro, that hides that hateful
> > pointer for key/value types. This allows map definition to be
> > self-describing w/o having to look at initialized ELF data section at
> > all, except for special cases of explicitly initializing map-in-map or
> > prog_array.
> >
> > What do you think?
>
> I think this is an interesting approach. One thing I'm not sure of is handling
> these types from C. For example:
>
>   sizeof(my_outer_map.value)
>
> This compiles, but doesn't produce the intended result. Correct would be:
>
>   sizeof(my_outer_map.value[0])
>
> At that point you have to understand that value is a pointer so all of
> our efforts
> are for naught. I suspect there is other weirdness like this, but I need to play
> with it a little bit more.

Yes, C can let you do crazy stuff, if you wish, but I think that
shouldn't be a blocker for this proposal. I haven't seen any BPF
program doing that, usually you duplicate the type of inner value
inside your function anyway, so there is no point in taking
sizeof(map.value) from BPF program side. From outside, though, all the
types will make sense, as expected.

>
> > Yeah I can definitely see some confusion here. But it seems like this
> > is more of a semantics of map sharing, and maybe it should be some
> > extra option for when we have automatic support for extern (shared)
> > maps. E.g., something like
> >
> > __int(sharing, SHARE_STRATEGY_MERGE) vs __int(sharing, SHARE_STRATEGY_OVERWRITE)
> >
> > Haven't though through exact syntax, naming, semantics, but it seems
> > doable to support both, depending on desired behavior.
> >
> > Maybe we should also unify this w/ pinning? E.g., there are many
> > sensible ways to handle already existing pinned map:
> >
> > 1. Reject program (e.g., if BPF application is the source of truth for that map)
> > 2. Use pinned as is (e.g., if BPF application wants to consume data
> > from source of truth app)
> > 3. Merge (what you described above)
> > 4. Replace/reset - not sure if useful/desirable.
>
> From my experience, trying to support many use cases in a purely declarative
> fashion ends up creating many edge cases, and quirky behaviour that is hard to
> fix later on. It's a bit like merging dictionaries in $LANGUAGE,
> which starts out simple and then gets complicated because sometimes you
> want to override a key, but lists should be concatenated, except in
> that one case...

I think if we can identify few robust common-sense strategies,
supporting them declaratively would eliminate 90% of need to writing
custom glue code for real-world use cases, so I think it's worth it.
For the rest, you'll have to do it in user-land app with custom glue
code. It's a non-goal to support any possible quirky way of sharing
maps declaratively.

>
> I wonder: are there many use cases where writing some glue code isn't
> possible? With libbpf getting more mature APIs that should become easier and
> easier. We could probably support existing iproute2 features that way as well.
>
> --
> Lorenz Bauer  |  Systems Engineer
> 6th Floor, County Hall/The Riverside Building, SE1 7PB, UK
>
> www.cloudflare.com

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

* Re: [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions
  2019-06-21  4:19       ` Andrii Nakryiko
@ 2019-06-21 10:29         ` Lorenz Bauer
  2019-06-21 17:56           ` Andrii Nakryiko
  0 siblings, 1 reply; 29+ messages in thread
From: Lorenz Bauer @ 2019-06-21 10:29 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Daniel Borkmann, Andrii Nakryiko, Alexei Starovoitov, Networking,
	bpf, Kernel Team, Jakub Kicinski, Joe Stringer

On Fri, 21 Jun 2019 at 05:20, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
>
> On Thu, Jun 20, 2019 at 7:49 AM Lorenz Bauer <lmb@cloudflare.com> wrote:
> >
> > On Tue, 18 Jun 2019 at 22:37, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> >
> > > > I would just drop the object-scope pinning. We avoided using it and I'm not
> > > > aware if anyone else make use. It also has the ugly side-effect that this
> > > > relies on AF_ALG which e.g. on some cloud provider shipped kernels is disabled.
> > > > The pinning attribute should be part of the standard set of map attributes for
> > > > libbpf though as it's generally useful for networking applications.
> > >
> > > Sounds good. I'll do some more surveying of use cases inside FB to see
> > > if anyone needs object-scope pinning, just to be sure we are not
> > > short-cutting anyone.
> >
> > I'm also curious what the use cases for declarative pinning are. From my
> > limited POV it doesn't seem that useful? There are a couple of factors:
>
> Cilium is using it pretty extensively, so there are clearly use cases.
> The most straigtforward use case is using a map created and shared by
> another BPF program (to communicate, read stats, what have you).

I think Cilium is in the quirky position that it has a persistent daemon, but
shells out to tc for loading programs. They are probably also the most
advanced (open-source) users of BPF out there. If I understood their comments
correctly they want to move to using a library for loading their ELF. At that
point whether something is possible in a declarative way is less important,
because you have the much more powerful APIs at your disposal.

Maybe Daniel or someone else from the Cilium team can chime in here?

> > * Systemd mounts the default location only accessible to root, so I have to
> >   used my own bpffs mount.
> > * Since I don't want to hard code that, I put it in a config file.
> > * After loading the ELF we pin maps from the daemon managing the XDP.
>
> So mounting root would be specified per bpf_object, before maps are
> created, so user-land driving application will have an opportunity to
> tune everything. Declarative is only the per-map decision of whether
> that map should be exposed to outer world (for sharing) or not.

So `tc filter add bpf obj foo.elf pin-root /gobbledygook`?

> Then check tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
> for more crazy syntax ;)
>
> typedef char * (* const (* const fn_ptr_arr2_t[5])())(char * (*)(int));

Not on a Friday ;P

> > What if this did
> >
> >   __type(value, struct my_value)[1000];
> >   struct my_value __member(value)[1000]; // alternative
> >
> > instead, and skipped max_entries?
>
> I considered that, but decided for now to keep all those attributes
> orthogonal for more flexibility and uniformity. This syntax might be
> considered a nice "syntax sugar" and can be added in the future, if
> necessary.

Ack.

> > At that point you have to understand that value is a pointer so all of
> > our efforts
> > are for naught. I suspect there is other weirdness like this, but I need to play
> > with it a little bit more.
>
> Yes, C can let you do crazy stuff, if you wish, but I think that
> shouldn't be a blocker for this proposal. I haven't seen any BPF
> program doing that, usually you duplicate the type of inner value
> inside your function anyway, so there is no point in taking
> sizeof(map.value) from BPF program side. From outside, though, all the
> types will make sense, as expected.

Right, but in my mind that is a bit of a cop out. I like BTF map definitions,
and I want them to be as unsurprising as possible, so that they are
easy to use and adopt.

If a type encodes all the information we need via the array dimension hack,
couldn't we make the map variable itself a pointer, and drop the inner pointers?

struct my_map_def {
  int type[BPF_MAP_TYPE_HASH];
  int value;
  struct foo key;
  ...
}

struct my_map_def *my_map;

-- 
Lorenz Bauer  |  Systems Engineer
6th Floor, County Hall/The Riverside Building, SE1 7PB, UK

www.cloudflare.com

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

* Re: [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions
  2019-06-21 10:29         ` Lorenz Bauer
@ 2019-06-21 17:56           ` Andrii Nakryiko
  2019-06-25 18:14             ` Andrii Nakryiko
  0 siblings, 1 reply; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-21 17:56 UTC (permalink / raw)
  To: Lorenz Bauer
  Cc: Daniel Borkmann, Andrii Nakryiko, Alexei Starovoitov, Networking,
	bpf, Kernel Team, Jakub Kicinski, Joe Stringer

On Fri, Jun 21, 2019 at 3:29 AM Lorenz Bauer <lmb@cloudflare.com> wrote:
>
> On Fri, 21 Jun 2019 at 05:20, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> >
> > On Thu, Jun 20, 2019 at 7:49 AM Lorenz Bauer <lmb@cloudflare.com> wrote:
> > >
> > > On Tue, 18 Jun 2019 at 22:37, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> > >
> > > > > I would just drop the object-scope pinning. We avoided using it and I'm not
> > > > > aware if anyone else make use. It also has the ugly side-effect that this
> > > > > relies on AF_ALG which e.g. on some cloud provider shipped kernels is disabled.
> > > > > The pinning attribute should be part of the standard set of map attributes for
> > > > > libbpf though as it's generally useful for networking applications.
> > > >
> > > > Sounds good. I'll do some more surveying of use cases inside FB to see
> > > > if anyone needs object-scope pinning, just to be sure we are not
> > > > short-cutting anyone.
> > >
> > > I'm also curious what the use cases for declarative pinning are. From my
> > > limited POV it doesn't seem that useful? There are a couple of factors:
> >
> > Cilium is using it pretty extensively, so there are clearly use cases.
> > The most straigtforward use case is using a map created and shared by
> > another BPF program (to communicate, read stats, what have you).
>
> I think Cilium is in the quirky position that it has a persistent daemon, but
> shells out to tc for loading programs. They are probably also the most
> advanced (open-source) users of BPF out there. If I understood their comments
> correctly they want to move to using a library for loading their ELF. At that
> point whether something is possible in a declarative way is less important,
> because you have the much more powerful APIs at your disposal.
>
> Maybe Daniel or someone else from the Cilium team can chime in here?

Yep, curious about their perspective on that.

>
> > > * Systemd mounts the default location only accessible to root, so I have to
> > >   used my own bpffs mount.
> > > * Since I don't want to hard code that, I put it in a config file.
> > > * After loading the ELF we pin maps from the daemon managing the XDP.
> >
> > So mounting root would be specified per bpf_object, before maps are
> > created, so user-land driving application will have an opportunity to
> > tune everything. Declarative is only the per-map decision of whether
> > that map should be exposed to outer world (for sharing) or not.
>
> So `tc filter add bpf obj foo.elf pin-root /gobbledygook`?

I meant something like:

bpf_object_open_attr attr;
attr.file = "path/to/my/object.o";
attr.pin_root_path = "/my/fancy/bpffs/root";
bpf_object__open_xattr(&attr);

Then tools can adopt they when necessary.

>
> > Then check tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
> > for more crazy syntax ;)
> >
> > typedef char * (* const (* const fn_ptr_arr2_t[5])())(char * (*)(int));
>
> Not on a Friday ;P
>
> > > What if this did
> > >
> > >   __type(value, struct my_value)[1000];
> > >   struct my_value __member(value)[1000]; // alternative
> > >
> > > instead, and skipped max_entries?
> >
> > I considered that, but decided for now to keep all those attributes
> > orthogonal for more flexibility and uniformity. This syntax might be
> > considered a nice "syntax sugar" and can be added in the future, if
> > necessary.
>
> Ack.
>
> > > At that point you have to understand that value is a pointer so all of
> > > our efforts
> > > are for naught. I suspect there is other weirdness like this, but I need to play
> > > with it a little bit more.
> >
> > Yes, C can let you do crazy stuff, if you wish, but I think that
> > shouldn't be a blocker for this proposal. I haven't seen any BPF
> > program doing that, usually you duplicate the type of inner value
> > inside your function anyway, so there is no point in taking
> > sizeof(map.value) from BPF program side. From outside, though, all the
> > types will make sense, as expected.
>
> Right, but in my mind that is a bit of a cop out. I like BTF map definitions,
> and I want them to be as unsurprising as possible, so that they are
> easy to use and adopt.


Right, but there are limit on what you can do with C syntax and it's
type system. Having fancy extra features like you described (e.g,
sizeof(map.value), etc) is pretty low on a priority list.

>
> If a type encodes all the information we need via the array dimension hack,
> couldn't we make the map variable itself a pointer, and drop the inner pointers?
>
> struct my_map_def {
>   int type[BPF_MAP_TYPE_HASH];
>   int value;
>   struct foo key;

This is bad because it potentially uses lots of space. If `struct foo`
is big, if max_entries is big, even for type, it's still a bunch of
extra space wasted. That's why we have pointers everywhere, as they
allow to encode everything with fixed space overhead of 8 bytes for a
pointer.


>   ...
> }
>
> struct my_map_def *my_map;
>
> --
> Lorenz Bauer  |  Systems Engineer
> 6th Floor, County Hall/The Riverside Building, SE1 7PB, UK
>
> www.cloudflare.com

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

* Re: [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions
  2019-06-21 17:56           ` Andrii Nakryiko
@ 2019-06-25 18:14             ` Andrii Nakryiko
  0 siblings, 0 replies; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-25 18:14 UTC (permalink / raw)
  To: Lorenz Bauer
  Cc: Daniel Borkmann, Andrii Nakryiko, Alexei Starovoitov, Networking,
	bpf, Kernel Team, Jakub Kicinski, Joe Stringer

On Fri, Jun 21, 2019 at 10:56 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Fri, Jun 21, 2019 at 3:29 AM Lorenz Bauer <lmb@cloudflare.com> wrote:
> >
> > On Fri, 21 Jun 2019 at 05:20, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> > >
> > > On Thu, Jun 20, 2019 at 7:49 AM Lorenz Bauer <lmb@cloudflare.com> wrote:
> > > >
> > > > On Tue, 18 Jun 2019 at 22:37, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> > > >
> > > > > > I would just drop the object-scope pinning. We avoided using it and I'm not
> > > > > > aware if anyone else make use. It also has the ugly side-effect that this
> > > > > > relies on AF_ALG which e.g. on some cloud provider shipped kernels is disabled.
> > > > > > The pinning attribute should be part of the standard set of map attributes for
> > > > > > libbpf though as it's generally useful for networking applications.
> > > > >
> > > > > Sounds good. I'll do some more surveying of use cases inside FB to see
> > > > > if anyone needs object-scope pinning, just to be sure we are not
> > > > > short-cutting anyone.
> > > >
> > > > I'm also curious what the use cases for declarative pinning are. From my
> > > > limited POV it doesn't seem that useful? There are a couple of factors:
> > >
> > > Cilium is using it pretty extensively, so there are clearly use cases.
> > > The most straigtforward use case is using a map created and shared by
> > > another BPF program (to communicate, read stats, what have you).
> >
> > I think Cilium is in the quirky position that it has a persistent daemon, but
> > shells out to tc for loading programs. They are probably also the most
> > advanced (open-source) users of BPF out there. If I understood their comments
> > correctly they want to move to using a library for loading their ELF. At that
> > point whether something is possible in a declarative way is less important,
> > because you have the much more powerful APIs at your disposal.
> >
> > Maybe Daniel or someone else from the Cilium team can chime in here?
>
> Yep, curious about their perspective on that.
>
> >
> > > > * Systemd mounts the default location only accessible to root, so I have to
> > > >   used my own bpffs mount.
> > > > * Since I don't want to hard code that, I put it in a config file.
> > > > * After loading the ELF we pin maps from the daemon managing the XDP.
> > >
> > > So mounting root would be specified per bpf_object, before maps are
> > > created, so user-land driving application will have an opportunity to
> > > tune everything. Declarative is only the per-map decision of whether
> > > that map should be exposed to outer world (for sharing) or not.
> >
> > So `tc filter add bpf obj foo.elf pin-root /gobbledygook`?
>
> I meant something like:
>
> bpf_object_open_attr attr;
> attr.file = "path/to/my/object.o";
> attr.pin_root_path = "/my/fancy/bpffs/root";
> bpf_object__open_xattr(&attr);
>
> Then tools can adopt they when necessary.
>
> >
> > > Then check tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
> > > for more crazy syntax ;)
> > >
> > > typedef char * (* const (* const fn_ptr_arr2_t[5])())(char * (*)(int));
> >
> > Not on a Friday ;P
> >
> > > > What if this did
> > > >
> > > >   __type(value, struct my_value)[1000];
> > > >   struct my_value __member(value)[1000]; // alternative
> > > >
> > > > instead, and skipped max_entries?
> > >
> > > I considered that, but decided for now to keep all those attributes
> > > orthogonal for more flexibility and uniformity. This syntax might be
> > > considered a nice "syntax sugar" and can be added in the future, if
> > > necessary.
> >
> > Ack.
> >
> > > > At that point you have to understand that value is a pointer so all of
> > > > our efforts
> > > > are for naught. I suspect there is other weirdness like this, but I need to play
> > > > with it a little bit more.
> > >
> > > Yes, C can let you do crazy stuff, if you wish, but I think that
> > > shouldn't be a blocker for this proposal. I haven't seen any BPF
> > > program doing that, usually you duplicate the type of inner value
> > > inside your function anyway, so there is no point in taking
> > > sizeof(map.value) from BPF program side. From outside, though, all the
> > > types will make sense, as expected.
> >
> > Right, but in my mind that is a bit of a cop out. I like BTF map definitions,
> > and I want them to be as unsurprising as possible, so that they are
> > easy to use and adopt.
>
>
> Right, but there are limit on what you can do with C syntax and it's
> type system. Having fancy extra features like you described (e.g,
> sizeof(map.value), etc) is pretty low on a priority list.
>
> >
> > If a type encodes all the information we need via the array dimension hack,
> > couldn't we make the map variable itself a pointer, and drop the inner pointers?
> >
> > struct my_map_def {
> >   int type[BPF_MAP_TYPE_HASH];
> >   int value;
> >   struct foo key;
>
> This is bad because it potentially uses lots of space. If `struct foo`
> is big, if max_entries is big, even for type, it's still a bunch of
> extra space wasted. That's why we have pointers everywhere, as they
> allow to encode everything with fixed space overhead of 8 bytes for a
> pointer.
>
>
> >   ...
> > }
> >
> > struct my_map_def *my_map;

Oh, I missed this point completely, sorry about that.

This has very little advantage over my proposal, in that number
encoding is still cumbersome with array dimensions, so you'd want to
hide it anyway behind macro, probably.

But the main problem with that is when we are going to do prog_array
or map-in-map initialization. This will create potentially huge
anonymous variable to initialize this pointer. See example below:

$ cat test.c
typedef int(*func)(void);

int f1(void) {
        return 0;
}

int f2(void) {
        return 1;
}

struct my_map_def {
        int size[1000];
        func arr[1000];
} *map = &(struct my_map_def){
        .arr = {
                [500] = &f1,
                [999] = &f2,
        },
};
$ ~/local/llvm/build/bin/clang -g -target bpf -c test.c -o test.o
$ bpftool btf dump file test.o

<snip>

[6] VAR '.compoundliteral' type_id=0, linkage=static

<snip>

[15] DATASEC '.data' size=0 vlen=1
        type_id=6 offset=0 size=12000

Note how variable ".compoundliteral" of size 12000 bytes is added
here. Plus the syntax of initialization is cumbersome, and it requires
naming map definition struct just for that &(struct my_map_def) cast.

So I think this doesn't get as much, but makes more advanced use cases
much more cumbersome and prohibitively expensive in terms of storage
size.

> >
> > --
> > Lorenz Bauer  |  Systems Engineer
> > 6th Floor, County Hall/The Riverside Building, SE1 7PB, UK
> >
> > www.cloudflare.com

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

* Re: [PATCH v2 bpf-next 04/11] libbpf: refactor map initialization
  2019-06-17 19:26 ` [PATCH v2 bpf-next 04/11] libbpf: refactor map initialization Andrii Nakryiko
  2019-06-17 19:39   ` Song Liu
@ 2019-06-26 14:48   ` Matt Hart
  2019-06-26 18:28     ` Andrii Nakryiko
  1 sibling, 1 reply; 29+ messages in thread
From: Matt Hart @ 2019-06-26 14:48 UTC (permalink / raw)
  To: Andrii Nakryiko, linux-perf-users
  Cc: andrii.nakryiko, ast, daniel, netdev, bpf, kernel-team

Hi all,

I noticed perf fails to build for armv7 on linux next, due to this
compile error:
$ make -C tools/perf ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

  CC       libbpf_probes.o
In file included from libbpf.c:27:
libbpf.c: In function ‘bpf_object__add_map’:
/home/matt/git/linux-next/tools/include/linux/kernel.h:45:17: error:
comparison of distinct pointer types lacks a cast [-Werror]
  (void) (&_max1 == &_max2);  \
                 ^~
libbpf.c:776:12: note: in expansion of macro ‘max’
  new_cap = max(4ul, obj->maps_cap * 3 / 2);
            ^~~

So I bisected it and came down to this patch.
Commit bf82927125dd25003d76ed5541da704df21de57a

Full verbose bisect script https://hastebin.com/odoyujofav.coffeescript

Is this a case that perf code needs updating to match the change, or
is the change broken?



On Tue, 25 Jun 2019 at 16:53, Andrii Nakryiko <andriin@fb.com> wrote:
>
> User and global data maps initialization has gotten pretty complicated
> and unnecessarily convoluted. This patch splits out the logic for global
> data map and user-defined map initialization. It also removes the
> restriction of pre-calculating how many maps will be initialized,
> instead allowing to keep adding new maps as they are discovered, which
> will be used later for BTF-defined map definitions.
>
> Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> ---
>  tools/lib/bpf/libbpf.c | 247 ++++++++++++++++++++++-------------------
>  1 file changed, 133 insertions(+), 114 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 7ee44d8877c5..88609dca4f7d 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -234,6 +234,7 @@ struct bpf_object {
>         size_t nr_programs;
>         struct bpf_map *maps;
>         size_t nr_maps;
> +       size_t maps_cap;
>         struct bpf_secdata sections;
>
>         bool loaded;
> @@ -763,21 +764,51 @@ int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
>         return -ENOENT;
>  }
>
> -static bool bpf_object__has_maps(const struct bpf_object *obj)
> +static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
>  {
> -       return obj->efile.maps_shndx >= 0 ||
> -              obj->efile.data_shndx >= 0 ||
> -              obj->efile.rodata_shndx >= 0 ||
> -              obj->efile.bss_shndx >= 0;
> +       struct bpf_map *new_maps;
> +       size_t new_cap;
> +       int i;
> +
> +       if (obj->nr_maps < obj->maps_cap)
> +               return &obj->maps[obj->nr_maps++];
> +
> +       new_cap = max(4ul, obj->maps_cap * 3 / 2);
> +       new_maps = realloc(obj->maps, new_cap * sizeof(*obj->maps));
> +       if (!new_maps) {
> +               pr_warning("alloc maps for object failed\n");
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       obj->maps_cap = new_cap;
> +       obj->maps = new_maps;
> +
> +       /* zero out new maps */
> +       memset(obj->maps + obj->nr_maps, 0,
> +              (obj->maps_cap - obj->nr_maps) * sizeof(*obj->maps));
> +       /*
> +        * fill all fd with -1 so won't close incorrect fd (fd=0 is stdin)
> +        * when failure (zclose won't close negative fd)).
> +        */
> +       for (i = obj->nr_maps; i < obj->maps_cap; i++) {
> +               obj->maps[i].fd = -1;
> +               obj->maps[i].inner_map_fd = -1;
> +       }
> +
> +       return &obj->maps[obj->nr_maps++];
>  }
>
>  static int
> -bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
> -                             enum libbpf_map_type type, Elf_Data *data,
> -                             void **data_buff)
> +bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
> +                             Elf_Data *data, void **data_buff)
>  {
> -       struct bpf_map_def *def = &map->def;
>         char map_name[BPF_OBJ_NAME_LEN];
> +       struct bpf_map_def *def;
> +       struct bpf_map *map;
> +
> +       map = bpf_object__add_map(obj);
> +       if (IS_ERR(map))
> +               return PTR_ERR(map);
>
>         map->libbpf_type = type;
>         map->offset = ~(typeof(map->offset))0;
> @@ -789,6 +820,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
>                 return -ENOMEM;
>         }
>
> +       def = &map->def;
>         def->type = BPF_MAP_TYPE_ARRAY;
>         def->key_size = sizeof(int);
>         def->value_size = data->d_size;
> @@ -808,29 +840,58 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
>         return 0;
>  }
>
> -static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> +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.
> +        */
> +       if (obj->efile.data_shndx >= 0) {
> +               err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA,
> +                                                   obj->efile.data,
> +                                                   &obj->sections.data);
> +               if (err)
> +                       return err;
> +       }
> +       if (obj->efile.rodata_shndx >= 0) {
> +               err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA,
> +                                                   obj->efile.rodata,
> +                                                   &obj->sections.rodata);
> +               if (err)
> +                       return err;
> +       }
> +       if (obj->efile.bss_shndx >= 0) {
> +               err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS,
> +                                                   obj->efile.bss, NULL);
> +               if (err)
> +                       return err;
> +       }
> +       return 0;
> +}
> +
> +static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
>  {
> -       int i, map_idx, map_def_sz = 0, nr_syms, nr_maps = 0, nr_maps_glob = 0;
> -       bool strict = !(flags & MAPS_RELAX_COMPAT);
>         Elf_Data *symbols = obj->efile.symbols;
> +       int i, map_def_sz = 0, nr_maps = 0, nr_syms;
>         Elf_Data *data = NULL;
> -       int ret = 0;
> +       Elf_Scn *scn;
> +
> +       if (obj->efile.maps_shndx < 0)
> +               return 0;
>
>         if (!symbols)
>                 return -EINVAL;
> -       nr_syms = symbols->d_size / sizeof(GElf_Sym);
> -
> -       if (obj->efile.maps_shndx >= 0) {
> -               Elf_Scn *scn = elf_getscn(obj->efile.elf,
> -                                         obj->efile.maps_shndx);
>
> -               if (scn)
> -                       data = elf_getdata(scn, NULL);
> -               if (!scn || !data) {
> -                       pr_warning("failed to get Elf_Data from map section %d\n",
> -                                  obj->efile.maps_shndx);
> -                       return -EINVAL;
> -               }
> +       scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx);
> +       if (scn)
> +               data = elf_getdata(scn, NULL);
> +       if (!scn || !data) {
> +               pr_warning("failed to get Elf_Data from map section %d\n",
> +                          obj->efile.maps_shndx);
> +               return -EINVAL;
>         }
>
>         /*
> @@ -840,16 +901,8 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
>          *
>          * TODO: Detect array of map and report error.
>          */
> -       if (obj->caps.global_data) {
> -               if (obj->efile.data_shndx >= 0)
> -                       nr_maps_glob++;
> -               if (obj->efile.rodata_shndx >= 0)
> -                       nr_maps_glob++;
> -               if (obj->efile.bss_shndx >= 0)
> -                       nr_maps_glob++;
> -       }
> -
> -       for (i = 0; data && i < nr_syms; i++) {
> +       nr_syms = symbols->d_size / sizeof(GElf_Sym);
> +       for (i = 0; i < nr_syms; i++) {
>                 GElf_Sym sym;
>
>                 if (!gelf_getsym(symbols, i, &sym))
> @@ -858,79 +911,56 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
>                         continue;
>                 nr_maps++;
>         }
> -
> -       if (!nr_maps && !nr_maps_glob)
> -               return 0;
> -
>         /* Assume equally sized map definitions */
> -       if (data) {
> -               pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path,
> -                        nr_maps, data->d_size);
> -
> -               map_def_sz = data->d_size / nr_maps;
> -               if (!data->d_size || (data->d_size % nr_maps) != 0) {
> -                       pr_warning("unable to determine map definition size "
> -                                  "section %s, %d maps in %zd bytes\n",
> -                                  obj->path, nr_maps, data->d_size);
> -                       return -EINVAL;
> -               }
> -       }
> -
> -       nr_maps += nr_maps_glob;
> -       obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
> -       if (!obj->maps) {
> -               pr_warning("alloc maps for object failed\n");
> -               return -ENOMEM;
> -       }
> -       obj->nr_maps = nr_maps;
> -
> -       for (i = 0; i < nr_maps; i++) {
> -               /*
> -                * fill all fd with -1 so won't close incorrect
> -                * fd (fd=0 is stdin) when failure (zclose won't close
> -                * negative fd)).
> -                */
> -               obj->maps[i].fd = -1;
> -               obj->maps[i].inner_map_fd = -1;
> +       pr_debug("maps in %s: %d maps in %zd bytes\n",
> +                obj->path, nr_maps, data->d_size);
> +
> +       map_def_sz = data->d_size / nr_maps;
> +       if (!data->d_size || (data->d_size % nr_maps) != 0) {
> +               pr_warning("unable to determine map definition size "
> +                          "section %s, %d maps in %zd bytes\n",
> +                          obj->path, nr_maps, data->d_size);
> +               return -EINVAL;
>         }
>
> -       /*
> -        * Fill obj->maps using data in "maps" section.
> -        */
> -       for (i = 0, map_idx = 0; data && i < nr_syms; i++) {
> +       /* Fill obj->maps using data in "maps" section.  */
> +       for (i = 0; i < nr_syms; i++) {
>                 GElf_Sym sym;
>                 const char *map_name;
>                 struct bpf_map_def *def;
> +               struct bpf_map *map;
>
>                 if (!gelf_getsym(symbols, i, &sym))
>                         continue;
>                 if (sym.st_shndx != obj->efile.maps_shndx)
>                         continue;
>
> -               map_name = elf_strptr(obj->efile.elf,
> -                                     obj->efile.strtabidx,
> +               map = bpf_object__add_map(obj);
> +               if (IS_ERR(map))
> +                       return PTR_ERR(map);
> +
> +               map_name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
>                                       sym.st_name);
>                 if (!map_name) {
>                         pr_warning("failed to get map #%d name sym string for obj %s\n",
> -                                  map_idx, obj->path);
> +                                  i, obj->path);
>                         return -LIBBPF_ERRNO__FORMAT;
>                 }
>
> -               obj->maps[map_idx].libbpf_type = LIBBPF_MAP_UNSPEC;
> -               obj->maps[map_idx].offset = sym.st_value;
> +               map->libbpf_type = LIBBPF_MAP_UNSPEC;
> +               map->offset = sym.st_value;
>                 if (sym.st_value + map_def_sz > data->d_size) {
>                         pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
>                                    obj->path, map_name);
>                         return -EINVAL;
>                 }
>
> -               obj->maps[map_idx].name = strdup(map_name);
> -               if (!obj->maps[map_idx].name) {
> +               map->name = strdup(map_name);
> +               if (!map->name) {
>                         pr_warning("failed to alloc map name\n");
>                         return -ENOMEM;
>                 }
> -               pr_debug("map %d is \"%s\"\n", map_idx,
> -                        obj->maps[map_idx].name);
> +               pr_debug("map %d is \"%s\"\n", i, map->name);
>                 def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
>                 /*
>                  * If the definition of the map in the object file fits in
> @@ -939,7 +969,7 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
>                  * calloc above.
>                  */
>                 if (map_def_sz <= sizeof(struct bpf_map_def)) {
> -                       memcpy(&obj->maps[map_idx].def, def, map_def_sz);
> +                       memcpy(&map->def, def, map_def_sz);
>                 } else {
>                         /*
>                          * Here the map structure being read is bigger than what
> @@ -959,37 +989,30 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
>                                                 return -EINVAL;
>                                 }
>                         }
> -                       memcpy(&obj->maps[map_idx].def, def,
> -                              sizeof(struct bpf_map_def));
> +                       memcpy(&map->def, def, sizeof(struct bpf_map_def));
>                 }
> -               map_idx++;
>         }
> +       return 0;
> +}
>
> -       if (!obj->caps.global_data)
> -               goto finalize;
> +static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> +{
> +       bool strict = !(flags & MAPS_RELAX_COMPAT);
> +       int err;
>
> -       /*
> -        * Populate rest of obj->maps with libbpf internal maps.
> -        */
> -       if (obj->efile.data_shndx >= 0)
> -               ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> -                                                   LIBBPF_MAP_DATA,
> -                                                   obj->efile.data,
> -                                                   &obj->sections.data);
> -       if (!ret && obj->efile.rodata_shndx >= 0)
> -               ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> -                                                   LIBBPF_MAP_RODATA,
> -                                                   obj->efile.rodata,
> -                                                   &obj->sections.rodata);
> -       if (!ret && obj->efile.bss_shndx >= 0)
> -               ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> -                                                   LIBBPF_MAP_BSS,
> -                                                   obj->efile.bss, NULL);
> -finalize:
> -       if (!ret)
> +       err = bpf_object__init_user_maps(obj, strict);
> +       if (err)
> +               return err;
> +
> +       err = bpf_object__init_global_data_maps(obj);
> +       if (err)
> +               return err;
> +
> +       if (obj->nr_maps) {
>                 qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]),
>                       compare_bpf_map);
> -       return ret;
> +       }
> +       return 0;
>  }
>
>  static bool section_have_execinstr(struct bpf_object *obj, int idx)
> @@ -1262,14 +1285,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
>                 return -LIBBPF_ERRNO__FORMAT;
>         }
>         err = bpf_object__load_btf(obj, btf_data, btf_ext_data);
> -       if (err)
> -               return err;
> -       if (bpf_object__has_maps(obj)) {
> +       if (!err)
>                 err = bpf_object__init_maps(obj, flags);
> -               if (err)
> -                       return err;
> -       }
> -       err = bpf_object__init_prog_names(obj);
> +       if (!err)
> +               err = bpf_object__init_prog_names(obj);
>         return err;
>  }
>
> --
> 2.17.1
>

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

* Re: [PATCH v2 bpf-next 04/11] libbpf: refactor map initialization
  2019-06-26 14:48   ` Matt Hart
@ 2019-06-26 18:28     ` Andrii Nakryiko
  2019-06-27 15:11       ` Matt Hart
  0 siblings, 1 reply; 29+ messages in thread
From: Andrii Nakryiko @ 2019-06-26 18:28 UTC (permalink / raw)
  To: Matt Hart
  Cc: Andrii Nakryiko, linux-perf-users, Alexei Starovoitov,
	Daniel Borkmann, Networking, bpf, Kernel Team

On Wed, Jun 26, 2019 at 7:48 AM Matt Hart <matthew.hart@linaro.org> wrote:
>
> Hi all,
>
> I noticed perf fails to build for armv7 on linux next, due to this
> compile error:
> $ make -C tools/perf ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
>
>   CC       libbpf_probes.o
> In file included from libbpf.c:27:
> libbpf.c: In function ‘bpf_object__add_map’:
> /home/matt/git/linux-next/tools/include/linux/kernel.h:45:17: error:
> comparison of distinct pointer types lacks a cast [-Werror]
>   (void) (&_max1 == &_max2);  \
>                  ^~
> libbpf.c:776:12: note: in expansion of macro ‘max’
>   new_cap = max(4ul, obj->maps_cap * 3 / 2);
>             ^~~
>
> So I bisected it and came down to this patch.
> Commit bf82927125dd25003d76ed5541da704df21de57a
>
> Full verbose bisect script https://hastebin.com/odoyujofav.coffeescript
>
> Is this a case that perf code needs updating to match the change, or
> is the change broken?

Hi Matt,

Thanks for reporting. This issue was already fixed in
https://patchwork.ozlabs.org/patch/1122673/, so just pull latest
bpf-next.
>
>
>
> On Tue, 25 Jun 2019 at 16:53, Andrii Nakryiko <andriin@fb.com> wrote:
> >
> > User and global data maps initialization has gotten pretty complicated
> > and unnecessarily convoluted. This patch splits out the logic for global
> > data map and user-defined map initialization. It also removes the
> > restriction of pre-calculating how many maps will be initialized,
> > instead allowing to keep adding new maps as they are discovered, which
> > will be used later for BTF-defined map definitions.
> >
> > Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> > ---
> >  tools/lib/bpf/libbpf.c | 247 ++++++++++++++++++++++-------------------
> >  1 file changed, 133 insertions(+), 114 deletions(-)
> >
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index 7ee44d8877c5..88609dca4f7d 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -234,6 +234,7 @@ struct bpf_object {
> >         size_t nr_programs;
> >         struct bpf_map *maps;
> >         size_t nr_maps;
> > +       size_t maps_cap;
> >         struct bpf_secdata sections;
> >
> >         bool loaded;
> > @@ -763,21 +764,51 @@ int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
> >         return -ENOENT;
> >  }
> >
> > -static bool bpf_object__has_maps(const struct bpf_object *obj)
> > +static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
> >  {
> > -       return obj->efile.maps_shndx >= 0 ||
> > -              obj->efile.data_shndx >= 0 ||
> > -              obj->efile.rodata_shndx >= 0 ||
> > -              obj->efile.bss_shndx >= 0;
> > +       struct bpf_map *new_maps;
> > +       size_t new_cap;
> > +       int i;
> > +
> > +       if (obj->nr_maps < obj->maps_cap)
> > +               return &obj->maps[obj->nr_maps++];
> > +
> > +       new_cap = max(4ul, obj->maps_cap * 3 / 2);
> > +       new_maps = realloc(obj->maps, new_cap * sizeof(*obj->maps));
> > +       if (!new_maps) {
> > +               pr_warning("alloc maps for object failed\n");
> > +               return ERR_PTR(-ENOMEM);
> > +       }
> > +
> > +       obj->maps_cap = new_cap;
> > +       obj->maps = new_maps;
> > +
> > +       /* zero out new maps */
> > +       memset(obj->maps + obj->nr_maps, 0,
> > +              (obj->maps_cap - obj->nr_maps) * sizeof(*obj->maps));
> > +       /*
> > +        * fill all fd with -1 so won't close incorrect fd (fd=0 is stdin)
> > +        * when failure (zclose won't close negative fd)).
> > +        */
> > +       for (i = obj->nr_maps; i < obj->maps_cap; i++) {
> > +               obj->maps[i].fd = -1;
> > +               obj->maps[i].inner_map_fd = -1;
> > +       }
> > +
> > +       return &obj->maps[obj->nr_maps++];
> >  }
> >
> >  static int
> > -bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
> > -                             enum libbpf_map_type type, Elf_Data *data,
> > -                             void **data_buff)
> > +bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
> > +                             Elf_Data *data, void **data_buff)
> >  {
> > -       struct bpf_map_def *def = &map->def;
> >         char map_name[BPF_OBJ_NAME_LEN];
> > +       struct bpf_map_def *def;
> > +       struct bpf_map *map;
> > +
> > +       map = bpf_object__add_map(obj);
> > +       if (IS_ERR(map))
> > +               return PTR_ERR(map);
> >
> >         map->libbpf_type = type;
> >         map->offset = ~(typeof(map->offset))0;
> > @@ -789,6 +820,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
> >                 return -ENOMEM;
> >         }
> >
> > +       def = &map->def;
> >         def->type = BPF_MAP_TYPE_ARRAY;
> >         def->key_size = sizeof(int);
> >         def->value_size = data->d_size;
> > @@ -808,29 +840,58 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
> >         return 0;
> >  }
> >
> > -static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> > +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.
> > +        */
> > +       if (obj->efile.data_shndx >= 0) {
> > +               err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA,
> > +                                                   obj->efile.data,
> > +                                                   &obj->sections.data);
> > +               if (err)
> > +                       return err;
> > +       }
> > +       if (obj->efile.rodata_shndx >= 0) {
> > +               err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA,
> > +                                                   obj->efile.rodata,
> > +                                                   &obj->sections.rodata);
> > +               if (err)
> > +                       return err;
> > +       }
> > +       if (obj->efile.bss_shndx >= 0) {
> > +               err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS,
> > +                                                   obj->efile.bss, NULL);
> > +               if (err)
> > +                       return err;
> > +       }
> > +       return 0;
> > +}
> > +
> > +static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
> >  {
> > -       int i, map_idx, map_def_sz = 0, nr_syms, nr_maps = 0, nr_maps_glob = 0;
> > -       bool strict = !(flags & MAPS_RELAX_COMPAT);
> >         Elf_Data *symbols = obj->efile.symbols;
> > +       int i, map_def_sz = 0, nr_maps = 0, nr_syms;
> >         Elf_Data *data = NULL;
> > -       int ret = 0;
> > +       Elf_Scn *scn;
> > +
> > +       if (obj->efile.maps_shndx < 0)
> > +               return 0;
> >
> >         if (!symbols)
> >                 return -EINVAL;
> > -       nr_syms = symbols->d_size / sizeof(GElf_Sym);
> > -
> > -       if (obj->efile.maps_shndx >= 0) {
> > -               Elf_Scn *scn = elf_getscn(obj->efile.elf,
> > -                                         obj->efile.maps_shndx);
> >
> > -               if (scn)
> > -                       data = elf_getdata(scn, NULL);
> > -               if (!scn || !data) {
> > -                       pr_warning("failed to get Elf_Data from map section %d\n",
> > -                                  obj->efile.maps_shndx);
> > -                       return -EINVAL;
> > -               }
> > +       scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx);
> > +       if (scn)
> > +               data = elf_getdata(scn, NULL);
> > +       if (!scn || !data) {
> > +               pr_warning("failed to get Elf_Data from map section %d\n",
> > +                          obj->efile.maps_shndx);
> > +               return -EINVAL;
> >         }
> >
> >         /*
> > @@ -840,16 +901,8 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> >          *
> >          * TODO: Detect array of map and report error.
> >          */
> > -       if (obj->caps.global_data) {
> > -               if (obj->efile.data_shndx >= 0)
> > -                       nr_maps_glob++;
> > -               if (obj->efile.rodata_shndx >= 0)
> > -                       nr_maps_glob++;
> > -               if (obj->efile.bss_shndx >= 0)
> > -                       nr_maps_glob++;
> > -       }
> > -
> > -       for (i = 0; data && i < nr_syms; i++) {
> > +       nr_syms = symbols->d_size / sizeof(GElf_Sym);
> > +       for (i = 0; i < nr_syms; i++) {
> >                 GElf_Sym sym;
> >
> >                 if (!gelf_getsym(symbols, i, &sym))
> > @@ -858,79 +911,56 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> >                         continue;
> >                 nr_maps++;
> >         }
> > -
> > -       if (!nr_maps && !nr_maps_glob)
> > -               return 0;
> > -
> >         /* Assume equally sized map definitions */
> > -       if (data) {
> > -               pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path,
> > -                        nr_maps, data->d_size);
> > -
> > -               map_def_sz = data->d_size / nr_maps;
> > -               if (!data->d_size || (data->d_size % nr_maps) != 0) {
> > -                       pr_warning("unable to determine map definition size "
> > -                                  "section %s, %d maps in %zd bytes\n",
> > -                                  obj->path, nr_maps, data->d_size);
> > -                       return -EINVAL;
> > -               }
> > -       }
> > -
> > -       nr_maps += nr_maps_glob;
> > -       obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
> > -       if (!obj->maps) {
> > -               pr_warning("alloc maps for object failed\n");
> > -               return -ENOMEM;
> > -       }
> > -       obj->nr_maps = nr_maps;
> > -
> > -       for (i = 0; i < nr_maps; i++) {
> > -               /*
> > -                * fill all fd with -1 so won't close incorrect
> > -                * fd (fd=0 is stdin) when failure (zclose won't close
> > -                * negative fd)).
> > -                */
> > -               obj->maps[i].fd = -1;
> > -               obj->maps[i].inner_map_fd = -1;
> > +       pr_debug("maps in %s: %d maps in %zd bytes\n",
> > +                obj->path, nr_maps, data->d_size);
> > +
> > +       map_def_sz = data->d_size / nr_maps;
> > +       if (!data->d_size || (data->d_size % nr_maps) != 0) {
> > +               pr_warning("unable to determine map definition size "
> > +                          "section %s, %d maps in %zd bytes\n",
> > +                          obj->path, nr_maps, data->d_size);
> > +               return -EINVAL;
> >         }
> >
> > -       /*
> > -        * Fill obj->maps using data in "maps" section.
> > -        */
> > -       for (i = 0, map_idx = 0; data && i < nr_syms; i++) {
> > +       /* Fill obj->maps using data in "maps" section.  */
> > +       for (i = 0; i < nr_syms; i++) {
> >                 GElf_Sym sym;
> >                 const char *map_name;
> >                 struct bpf_map_def *def;
> > +               struct bpf_map *map;
> >
> >                 if (!gelf_getsym(symbols, i, &sym))
> >                         continue;
> >                 if (sym.st_shndx != obj->efile.maps_shndx)
> >                         continue;
> >
> > -               map_name = elf_strptr(obj->efile.elf,
> > -                                     obj->efile.strtabidx,
> > +               map = bpf_object__add_map(obj);
> > +               if (IS_ERR(map))
> > +                       return PTR_ERR(map);
> > +
> > +               map_name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
> >                                       sym.st_name);
> >                 if (!map_name) {
> >                         pr_warning("failed to get map #%d name sym string for obj %s\n",
> > -                                  map_idx, obj->path);
> > +                                  i, obj->path);
> >                         return -LIBBPF_ERRNO__FORMAT;
> >                 }
> >
> > -               obj->maps[map_idx].libbpf_type = LIBBPF_MAP_UNSPEC;
> > -               obj->maps[map_idx].offset = sym.st_value;
> > +               map->libbpf_type = LIBBPF_MAP_UNSPEC;
> > +               map->offset = sym.st_value;
> >                 if (sym.st_value + map_def_sz > data->d_size) {
> >                         pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
> >                                    obj->path, map_name);
> >                         return -EINVAL;
> >                 }
> >
> > -               obj->maps[map_idx].name = strdup(map_name);
> > -               if (!obj->maps[map_idx].name) {
> > +               map->name = strdup(map_name);
> > +               if (!map->name) {
> >                         pr_warning("failed to alloc map name\n");
> >                         return -ENOMEM;
> >                 }
> > -               pr_debug("map %d is \"%s\"\n", map_idx,
> > -                        obj->maps[map_idx].name);
> > +               pr_debug("map %d is \"%s\"\n", i, map->name);
> >                 def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
> >                 /*
> >                  * If the definition of the map in the object file fits in
> > @@ -939,7 +969,7 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> >                  * calloc above.
> >                  */
> >                 if (map_def_sz <= sizeof(struct bpf_map_def)) {
> > -                       memcpy(&obj->maps[map_idx].def, def, map_def_sz);
> > +                       memcpy(&map->def, def, map_def_sz);
> >                 } else {
> >                         /*
> >                          * Here the map structure being read is bigger than what
> > @@ -959,37 +989,30 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> >                                                 return -EINVAL;
> >                                 }
> >                         }
> > -                       memcpy(&obj->maps[map_idx].def, def,
> > -                              sizeof(struct bpf_map_def));
> > +                       memcpy(&map->def, def, sizeof(struct bpf_map_def));
> >                 }
> > -               map_idx++;
> >         }
> > +       return 0;
> > +}
> >
> > -       if (!obj->caps.global_data)
> > -               goto finalize;
> > +static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> > +{
> > +       bool strict = !(flags & MAPS_RELAX_COMPAT);
> > +       int err;
> >
> > -       /*
> > -        * Populate rest of obj->maps with libbpf internal maps.
> > -        */
> > -       if (obj->efile.data_shndx >= 0)
> > -               ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> > -                                                   LIBBPF_MAP_DATA,
> > -                                                   obj->efile.data,
> > -                                                   &obj->sections.data);
> > -       if (!ret && obj->efile.rodata_shndx >= 0)
> > -               ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> > -                                                   LIBBPF_MAP_RODATA,
> > -                                                   obj->efile.rodata,
> > -                                                   &obj->sections.rodata);
> > -       if (!ret && obj->efile.bss_shndx >= 0)
> > -               ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> > -                                                   LIBBPF_MAP_BSS,
> > -                                                   obj->efile.bss, NULL);
> > -finalize:
> > -       if (!ret)
> > +       err = bpf_object__init_user_maps(obj, strict);
> > +       if (err)
> > +               return err;
> > +
> > +       err = bpf_object__init_global_data_maps(obj);
> > +       if (err)
> > +               return err;
> > +
> > +       if (obj->nr_maps) {
> >                 qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]),
> >                       compare_bpf_map);
> > -       return ret;
> > +       }
> > +       return 0;
> >  }
> >
> >  static bool section_have_execinstr(struct bpf_object *obj, int idx)
> > @@ -1262,14 +1285,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> >                 return -LIBBPF_ERRNO__FORMAT;
> >         }
> >         err = bpf_object__load_btf(obj, btf_data, btf_ext_data);
> > -       if (err)
> > -               return err;
> > -       if (bpf_object__has_maps(obj)) {
> > +       if (!err)
> >                 err = bpf_object__init_maps(obj, flags);
> > -               if (err)
> > -                       return err;
> > -       }
> > -       err = bpf_object__init_prog_names(obj);
> > +       if (!err)
> > +               err = bpf_object__init_prog_names(obj);
> >         return err;
> >  }
> >
> > --
> > 2.17.1
> >

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

* Re: [PATCH v2 bpf-next 04/11] libbpf: refactor map initialization
  2019-06-26 18:28     ` Andrii Nakryiko
@ 2019-06-27 15:11       ` Matt Hart
  0 siblings, 0 replies; 29+ messages in thread
From: Matt Hart @ 2019-06-27 15:11 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Andrii Nakryiko, linux-perf-users, Alexei Starovoitov,
	Daniel Borkmann, Networking, bpf, Kernel Team

On Wed, 26 Jun 2019 at 19:29, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
>
> On Wed, Jun 26, 2019 at 7:48 AM Matt Hart <matthew.hart@linaro.org> wrote:
> >
> > Hi all,
> >
> > I noticed perf fails to build for armv7 on linux next, due to this
> > compile error:
> > $ make -C tools/perf ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
> >
> >   CC       libbpf_probes.o
> > In file included from libbpf.c:27:
> > libbpf.c: In function ‘bpf_object__add_map’:
> > /home/matt/git/linux-next/tools/include/linux/kernel.h:45:17: error:
> > comparison of distinct pointer types lacks a cast [-Werror]
> >   (void) (&_max1 == &_max2);  \
> >                  ^~
> > libbpf.c:776:12: note: in expansion of macro ‘max’
> >   new_cap = max(4ul, obj->maps_cap * 3 / 2);
> >             ^~~
> >
> > So I bisected it and came down to this patch.
> > Commit bf82927125dd25003d76ed5541da704df21de57a
> >
> > Full verbose bisect script https://hastebin.com/odoyujofav.coffeescript
> >
> > Is this a case that perf code needs updating to match the change, or
> > is the change broken?
>
> Hi Matt,
>
> Thanks for reporting. This issue was already fixed in
> https://patchwork.ozlabs.org/patch/1122673/, so just pull latest
> bpf-next.

Thanks, I see that patch has hit linux-next so perf is building again.

> >
> >
> >
> > On Tue, 25 Jun 2019 at 16:53, Andrii Nakryiko <andriin@fb.com> wrote:
> > >
> > > User and global data maps initialization has gotten pretty complicated
> > > and unnecessarily convoluted. This patch splits out the logic for global
> > > data map and user-defined map initialization. It also removes the
> > > restriction of pre-calculating how many maps will be initialized,
> > > instead allowing to keep adding new maps as they are discovered, which
> > > will be used later for BTF-defined map definitions.
> > >
> > > Signed-off-by: Andrii Nakryiko <andriin@fb.com>
> > > ---
> > >  tools/lib/bpf/libbpf.c | 247 ++++++++++++++++++++++-------------------
> > >  1 file changed, 133 insertions(+), 114 deletions(-)
> > >
> > > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > > index 7ee44d8877c5..88609dca4f7d 100644
> > > --- a/tools/lib/bpf/libbpf.c
> > > +++ b/tools/lib/bpf/libbpf.c
> > > @@ -234,6 +234,7 @@ struct bpf_object {
> > >         size_t nr_programs;
> > >         struct bpf_map *maps;
> > >         size_t nr_maps;
> > > +       size_t maps_cap;
> > >         struct bpf_secdata sections;
> > >
> > >         bool loaded;
> > > @@ -763,21 +764,51 @@ int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
> > >         return -ENOENT;
> > >  }
> > >
> > > -static bool bpf_object__has_maps(const struct bpf_object *obj)
> > > +static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
> > >  {
> > > -       return obj->efile.maps_shndx >= 0 ||
> > > -              obj->efile.data_shndx >= 0 ||
> > > -              obj->efile.rodata_shndx >= 0 ||
> > > -              obj->efile.bss_shndx >= 0;
> > > +       struct bpf_map *new_maps;
> > > +       size_t new_cap;
> > > +       int i;
> > > +
> > > +       if (obj->nr_maps < obj->maps_cap)
> > > +               return &obj->maps[obj->nr_maps++];
> > > +
> > > +       new_cap = max(4ul, obj->maps_cap * 3 / 2);
> > > +       new_maps = realloc(obj->maps, new_cap * sizeof(*obj->maps));
> > > +       if (!new_maps) {
> > > +               pr_warning("alloc maps for object failed\n");
> > > +               return ERR_PTR(-ENOMEM);
> > > +       }
> > > +
> > > +       obj->maps_cap = new_cap;
> > > +       obj->maps = new_maps;
> > > +
> > > +       /* zero out new maps */
> > > +       memset(obj->maps + obj->nr_maps, 0,
> > > +              (obj->maps_cap - obj->nr_maps) * sizeof(*obj->maps));
> > > +       /*
> > > +        * fill all fd with -1 so won't close incorrect fd (fd=0 is stdin)
> > > +        * when failure (zclose won't close negative fd)).
> > > +        */
> > > +       for (i = obj->nr_maps; i < obj->maps_cap; i++) {
> > > +               obj->maps[i].fd = -1;
> > > +               obj->maps[i].inner_map_fd = -1;
> > > +       }
> > > +
> > > +       return &obj->maps[obj->nr_maps++];
> > >  }
> > >
> > >  static int
> > > -bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
> > > -                             enum libbpf_map_type type, Elf_Data *data,
> > > -                             void **data_buff)
> > > +bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
> > > +                             Elf_Data *data, void **data_buff)
> > >  {
> > > -       struct bpf_map_def *def = &map->def;
> > >         char map_name[BPF_OBJ_NAME_LEN];
> > > +       struct bpf_map_def *def;
> > > +       struct bpf_map *map;
> > > +
> > > +       map = bpf_object__add_map(obj);
> > > +       if (IS_ERR(map))
> > > +               return PTR_ERR(map);
> > >
> > >         map->libbpf_type = type;
> > >         map->offset = ~(typeof(map->offset))0;
> > > @@ -789,6 +820,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
> > >                 return -ENOMEM;
> > >         }
> > >
> > > +       def = &map->def;
> > >         def->type = BPF_MAP_TYPE_ARRAY;
> > >         def->key_size = sizeof(int);
> > >         def->value_size = data->d_size;
> > > @@ -808,29 +840,58 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map,
> > >         return 0;
> > >  }
> > >
> > > -static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> > > +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.
> > > +        */
> > > +       if (obj->efile.data_shndx >= 0) {
> > > +               err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA,
> > > +                                                   obj->efile.data,
> > > +                                                   &obj->sections.data);
> > > +               if (err)
> > > +                       return err;
> > > +       }
> > > +       if (obj->efile.rodata_shndx >= 0) {
> > > +               err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA,
> > > +                                                   obj->efile.rodata,
> > > +                                                   &obj->sections.rodata);
> > > +               if (err)
> > > +                       return err;
> > > +       }
> > > +       if (obj->efile.bss_shndx >= 0) {
> > > +               err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS,
> > > +                                                   obj->efile.bss, NULL);
> > > +               if (err)
> > > +                       return err;
> > > +       }
> > > +       return 0;
> > > +}
> > > +
> > > +static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
> > >  {
> > > -       int i, map_idx, map_def_sz = 0, nr_syms, nr_maps = 0, nr_maps_glob = 0;
> > > -       bool strict = !(flags & MAPS_RELAX_COMPAT);
> > >         Elf_Data *symbols = obj->efile.symbols;
> > > +       int i, map_def_sz = 0, nr_maps = 0, nr_syms;
> > >         Elf_Data *data = NULL;
> > > -       int ret = 0;
> > > +       Elf_Scn *scn;
> > > +
> > > +       if (obj->efile.maps_shndx < 0)
> > > +               return 0;
> > >
> > >         if (!symbols)
> > >                 return -EINVAL;
> > > -       nr_syms = symbols->d_size / sizeof(GElf_Sym);
> > > -
> > > -       if (obj->efile.maps_shndx >= 0) {
> > > -               Elf_Scn *scn = elf_getscn(obj->efile.elf,
> > > -                                         obj->efile.maps_shndx);
> > >
> > > -               if (scn)
> > > -                       data = elf_getdata(scn, NULL);
> > > -               if (!scn || !data) {
> > > -                       pr_warning("failed to get Elf_Data from map section %d\n",
> > > -                                  obj->efile.maps_shndx);
> > > -                       return -EINVAL;
> > > -               }
> > > +       scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx);
> > > +       if (scn)
> > > +               data = elf_getdata(scn, NULL);
> > > +       if (!scn || !data) {
> > > +               pr_warning("failed to get Elf_Data from map section %d\n",
> > > +                          obj->efile.maps_shndx);
> > > +               return -EINVAL;
> > >         }
> > >
> > >         /*
> > > @@ -840,16 +901,8 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> > >          *
> > >          * TODO: Detect array of map and report error.
> > >          */
> > > -       if (obj->caps.global_data) {
> > > -               if (obj->efile.data_shndx >= 0)
> > > -                       nr_maps_glob++;
> > > -               if (obj->efile.rodata_shndx >= 0)
> > > -                       nr_maps_glob++;
> > > -               if (obj->efile.bss_shndx >= 0)
> > > -                       nr_maps_glob++;
> > > -       }
> > > -
> > > -       for (i = 0; data && i < nr_syms; i++) {
> > > +       nr_syms = symbols->d_size / sizeof(GElf_Sym);
> > > +       for (i = 0; i < nr_syms; i++) {
> > >                 GElf_Sym sym;
> > >
> > >                 if (!gelf_getsym(symbols, i, &sym))
> > > @@ -858,79 +911,56 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> > >                         continue;
> > >                 nr_maps++;
> > >         }
> > > -
> > > -       if (!nr_maps && !nr_maps_glob)
> > > -               return 0;
> > > -
> > >         /* Assume equally sized map definitions */
> > > -       if (data) {
> > > -               pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path,
> > > -                        nr_maps, data->d_size);
> > > -
> > > -               map_def_sz = data->d_size / nr_maps;
> > > -               if (!data->d_size || (data->d_size % nr_maps) != 0) {
> > > -                       pr_warning("unable to determine map definition size "
> > > -                                  "section %s, %d maps in %zd bytes\n",
> > > -                                  obj->path, nr_maps, data->d_size);
> > > -                       return -EINVAL;
> > > -               }
> > > -       }
> > > -
> > > -       nr_maps += nr_maps_glob;
> > > -       obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
> > > -       if (!obj->maps) {
> > > -               pr_warning("alloc maps for object failed\n");
> > > -               return -ENOMEM;
> > > -       }
> > > -       obj->nr_maps = nr_maps;
> > > -
> > > -       for (i = 0; i < nr_maps; i++) {
> > > -               /*
> > > -                * fill all fd with -1 so won't close incorrect
> > > -                * fd (fd=0 is stdin) when failure (zclose won't close
> > > -                * negative fd)).
> > > -                */
> > > -               obj->maps[i].fd = -1;
> > > -               obj->maps[i].inner_map_fd = -1;
> > > +       pr_debug("maps in %s: %d maps in %zd bytes\n",
> > > +                obj->path, nr_maps, data->d_size);
> > > +
> > > +       map_def_sz = data->d_size / nr_maps;
> > > +       if (!data->d_size || (data->d_size % nr_maps) != 0) {
> > > +               pr_warning("unable to determine map definition size "
> > > +                          "section %s, %d maps in %zd bytes\n",
> > > +                          obj->path, nr_maps, data->d_size);
> > > +               return -EINVAL;
> > >         }
> > >
> > > -       /*
> > > -        * Fill obj->maps using data in "maps" section.
> > > -        */
> > > -       for (i = 0, map_idx = 0; data && i < nr_syms; i++) {
> > > +       /* Fill obj->maps using data in "maps" section.  */
> > > +       for (i = 0; i < nr_syms; i++) {
> > >                 GElf_Sym sym;
> > >                 const char *map_name;
> > >                 struct bpf_map_def *def;
> > > +               struct bpf_map *map;
> > >
> > >                 if (!gelf_getsym(symbols, i, &sym))
> > >                         continue;
> > >                 if (sym.st_shndx != obj->efile.maps_shndx)
> > >                         continue;
> > >
> > > -               map_name = elf_strptr(obj->efile.elf,
> > > -                                     obj->efile.strtabidx,
> > > +               map = bpf_object__add_map(obj);
> > > +               if (IS_ERR(map))
> > > +                       return PTR_ERR(map);
> > > +
> > > +               map_name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
> > >                                       sym.st_name);
> > >                 if (!map_name) {
> > >                         pr_warning("failed to get map #%d name sym string for obj %s\n",
> > > -                                  map_idx, obj->path);
> > > +                                  i, obj->path);
> > >                         return -LIBBPF_ERRNO__FORMAT;
> > >                 }
> > >
> > > -               obj->maps[map_idx].libbpf_type = LIBBPF_MAP_UNSPEC;
> > > -               obj->maps[map_idx].offset = sym.st_value;
> > > +               map->libbpf_type = LIBBPF_MAP_UNSPEC;
> > > +               map->offset = sym.st_value;
> > >                 if (sym.st_value + map_def_sz > data->d_size) {
> > >                         pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
> > >                                    obj->path, map_name);
> > >                         return -EINVAL;
> > >                 }
> > >
> > > -               obj->maps[map_idx].name = strdup(map_name);
> > > -               if (!obj->maps[map_idx].name) {
> > > +               map->name = strdup(map_name);
> > > +               if (!map->name) {
> > >                         pr_warning("failed to alloc map name\n");
> > >                         return -ENOMEM;
> > >                 }
> > > -               pr_debug("map %d is \"%s\"\n", map_idx,
> > > -                        obj->maps[map_idx].name);
> > > +               pr_debug("map %d is \"%s\"\n", i, map->name);
> > >                 def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
> > >                 /*
> > >                  * If the definition of the map in the object file fits in
> > > @@ -939,7 +969,7 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> > >                  * calloc above.
> > >                  */
> > >                 if (map_def_sz <= sizeof(struct bpf_map_def)) {
> > > -                       memcpy(&obj->maps[map_idx].def, def, map_def_sz);
> > > +                       memcpy(&map->def, def, map_def_sz);
> > >                 } else {
> > >                         /*
> > >                          * Here the map structure being read is bigger than what
> > > @@ -959,37 +989,30 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> > >                                                 return -EINVAL;
> > >                                 }
> > >                         }
> > > -                       memcpy(&obj->maps[map_idx].def, def,
> > > -                              sizeof(struct bpf_map_def));
> > > +                       memcpy(&map->def, def, sizeof(struct bpf_map_def));
> > >                 }
> > > -               map_idx++;
> > >         }
> > > +       return 0;
> > > +}
> > >
> > > -       if (!obj->caps.global_data)
> > > -               goto finalize;
> > > +static int bpf_object__init_maps(struct bpf_object *obj, int flags)
> > > +{
> > > +       bool strict = !(flags & MAPS_RELAX_COMPAT);
> > > +       int err;
> > >
> > > -       /*
> > > -        * Populate rest of obj->maps with libbpf internal maps.
> > > -        */
> > > -       if (obj->efile.data_shndx >= 0)
> > > -               ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> > > -                                                   LIBBPF_MAP_DATA,
> > > -                                                   obj->efile.data,
> > > -                                                   &obj->sections.data);
> > > -       if (!ret && obj->efile.rodata_shndx >= 0)
> > > -               ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> > > -                                                   LIBBPF_MAP_RODATA,
> > > -                                                   obj->efile.rodata,
> > > -                                                   &obj->sections.rodata);
> > > -       if (!ret && obj->efile.bss_shndx >= 0)
> > > -               ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++],
> > > -                                                   LIBBPF_MAP_BSS,
> > > -                                                   obj->efile.bss, NULL);
> > > -finalize:
> > > -       if (!ret)
> > > +       err = bpf_object__init_user_maps(obj, strict);
> > > +       if (err)
> > > +               return err;
> > > +
> > > +       err = bpf_object__init_global_data_maps(obj);
> > > +       if (err)
> > > +               return err;
> > > +
> > > +       if (obj->nr_maps) {
> > >                 qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]),
> > >                       compare_bpf_map);
> > > -       return ret;
> > > +       }
> > > +       return 0;
> > >  }
> > >
> > >  static bool section_have_execinstr(struct bpf_object *obj, int idx)
> > > @@ -1262,14 +1285,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
> > >                 return -LIBBPF_ERRNO__FORMAT;
> > >         }
> > >         err = bpf_object__load_btf(obj, btf_data, btf_ext_data);
> > > -       if (err)
> > > -               return err;
> > > -       if (bpf_object__has_maps(obj)) {
> > > +       if (!err)
> > >                 err = bpf_object__init_maps(obj, flags);
> > > -               if (err)
> > > -                       return err;
> > > -       }
> > > -       err = bpf_object__init_prog_names(obj);
> > > +       if (!err)
> > > +               err = bpf_object__init_prog_names(obj);
> > >         return err;
> > >  }
> > >
> > > --
> > > 2.17.1
> > >

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

end of thread, other threads:[~2019-06-27 15:11 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-17 19:26 [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Andrii Nakryiko
2019-06-17 19:26 ` [PATCH v2 bpf-next 01/11] libbpf: add common min/max macro to libbpf_internal.h Andrii Nakryiko
2019-06-17 19:26 ` [PATCH v2 bpf-next 02/11] libbpf: extract BTF loading logic Andrii Nakryiko
2019-06-17 19:40   ` Song Liu
2019-06-17 19:26 ` [PATCH v2 bpf-next 03/11] libbpf: streamline ELF parsing error-handling Andrii Nakryiko
2019-06-17 19:46   ` Song Liu
2019-06-17 19:26 ` [PATCH v2 bpf-next 04/11] libbpf: refactor map initialization Andrii Nakryiko
2019-06-17 19:39   ` Song Liu
2019-06-26 14:48   ` Matt Hart
2019-06-26 18:28     ` Andrii Nakryiko
2019-06-27 15:11       ` Matt Hart
2019-06-17 19:26 ` [PATCH v2 bpf-next 05/11] libbpf: identify maps by section index in addition to offset Andrii Nakryiko
2019-06-17 19:26 ` [PATCH v2 bpf-next 06/11] libbpf: split initialization and loading of BTF Andrii Nakryiko
2019-06-17 19:26 ` [PATCH v2 bpf-next 07/11] libbpf: allow specifying map definitions using BTF Andrii Nakryiko
2019-06-17 19:43   ` Song Liu
2019-06-17 19:26 ` [PATCH v2 bpf-next 08/11] selftests/bpf: add test for BTF-defined maps Andrii Nakryiko
2019-06-17 19:26 ` [PATCH v2 bpf-next 09/11] selftests/bpf: switch BPF_ANNOTATE_KV_PAIR tests to " Andrii Nakryiko
2019-06-17 21:41   ` Song Liu
2019-06-17 22:20     ` Daniel Borkmann
2019-06-17 19:26 ` [PATCH v2 bpf-next 10/11] selftests/bpf: convert tests w/ custom values " Andrii Nakryiko
2019-06-17 19:27 ` [PATCH v2 bpf-next 11/11] selftests/bpf: convert remaining selftests " Andrii Nakryiko
2019-06-17 21:17 ` [PATCH v2 bpf-next 00/11] BTF-defined BPF map definitions Daniel Borkmann
2019-06-17 22:17   ` Daniel Borkmann
2019-06-18 21:37   ` Andrii Nakryiko
2019-06-20 14:49     ` Lorenz Bauer
2019-06-21  4:19       ` Andrii Nakryiko
2019-06-21 10:29         ` Lorenz Bauer
2019-06-21 17:56           ` Andrii Nakryiko
2019-06-25 18:14             ` Andrii Nakryiko

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).