bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH bpf-next 0/3] Libbpf extern support improvements
@ 2019-12-19  0:28 Andrii Nakryiko
  2019-12-19  0:28 ` [PATCH bpf-next 1/3] libbpf: put Kconfig externs into .kconfig section Andrii Nakryiko
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Andrii Nakryiko @ 2019-12-19  0:28 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Based on latest feedback and discussions, this patch set implements the
following changes:

- Kconfig-provided externs have to be in .kconfig section, for which
  bpf_helpers.h provides convenient __kconfig macro (Daniel);
- instead of allowing to override Kconfig file path, switch this to ability to
  extend and override system Kconfig with user-provided custom values (Alexei);
- BTF is required when externs are used.

Andrii Nakryiko (3):
  libbpf: put Kconfig externs into .kconfig section
  libbpf: allow to augment system Kconfig through extra optional config
  libbpf: BTF is required when externs are present

 tools/bpf/bpftool/gen.c                       |   8 +-
 tools/lib/bpf/bpf_helpers.h                   |   2 +
 tools/lib/bpf/libbpf.c                        | 265 +++++++++++-------
 tools/lib/bpf/libbpf.h                        |   8 +-
 .../selftests/bpf/prog_tests/core_extern.c    |  32 +--
 .../selftests/bpf/prog_tests/skeleton.c       |  16 +-
 .../selftests/bpf/progs/test_core_extern.c    |  20 +-
 .../selftests/bpf/progs/test_skeleton.c       |   4 +-
 8 files changed, 194 insertions(+), 161 deletions(-)

-- 
2.17.1


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

* [PATCH bpf-next 1/3] libbpf: put Kconfig externs into .kconfig section
  2019-12-19  0:28 [PATCH bpf-next 0/3] Libbpf extern support improvements Andrii Nakryiko
@ 2019-12-19  0:28 ` Andrii Nakryiko
  2019-12-19  0:28 ` [PATCH bpf-next 2/3] libbpf: allow to augment system Kconfig through extra optional config Andrii Nakryiko
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Andrii Nakryiko @ 2019-12-19  0:28 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Move Kconfig-provided externs into custom .kconfig section. Add __kconfig into
bpf_helpers.h for user convenience. Update selftests accordingly.

Suggested-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/bpf/bpftool/gen.c                       |  8 +--
 tools/lib/bpf/bpf_helpers.h                   |  2 +
 tools/lib/bpf/libbpf.c                        | 58 +++++++++++--------
 .../selftests/bpf/prog_tests/skeleton.c       | 16 +++--
 .../selftests/bpf/progs/test_core_extern.c    | 20 +++----
 .../selftests/bpf/progs/test_skeleton.c       |  4 +-
 6 files changed, 60 insertions(+), 48 deletions(-)

diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
index 8d93c8f90f82..87ef7c17c61c 100644
--- a/tools/bpf/bpftool/gen.c
+++ b/tools/bpf/bpftool/gen.c
@@ -83,8 +83,8 @@ static const char *get_map_ident(const struct bpf_map *map)
 		return "rodata";
 	else if (str_has_suffix(name, ".bss"))
 		return "bss";
-	else if (str_has_suffix(name, ".extern"))
-		return "externs"; /* extern is a C keyword */
+	else if (str_has_suffix(name, ".kconfig"))
+		return "kconfig";
 	else
 		return NULL;
 }
@@ -112,8 +112,8 @@ static int codegen_datasec_def(struct bpf_object *obj,
 		sec_ident = "bss";
 	else if (strcmp(sec_name, ".rodata") == 0)
 		sec_ident = "rodata";
-	else if (strcmp(sec_name, ".extern") == 0)
-		sec_ident = "externs"; /* extern is a C keyword */
+	else if (strcmp(sec_name, ".kconfig") == 0)
+		sec_ident = "kconfig";
 	else
 		return 0;
 
diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h
index aa46700075e1..f69cc208778a 100644
--- a/tools/lib/bpf/bpf_helpers.h
+++ b/tools/lib/bpf/bpf_helpers.h
@@ -53,4 +53,6 @@ enum libbpf_tristate {
 	TRI_MODULE = 2,
 };
 
+#define __kconfig __attribute__((section(".kconfig")))
+
 #endif
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 906bbbf7b2e4..ed54a6a7f6f2 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -231,21 +231,21 @@ struct bpf_program {
 #define DATA_SEC ".data"
 #define BSS_SEC ".bss"
 #define RODATA_SEC ".rodata"
-#define EXTERN_SEC ".extern"
+#define KCONFIG_SEC ".kconfig"
 
 enum libbpf_map_type {
 	LIBBPF_MAP_UNSPEC,
 	LIBBPF_MAP_DATA,
 	LIBBPF_MAP_BSS,
 	LIBBPF_MAP_RODATA,
-	LIBBPF_MAP_EXTERN,
+	LIBBPF_MAP_KCONFIG,
 };
 
 static const char * const libbpf_type_to_btf_name[] = {
 	[LIBBPF_MAP_DATA]	= DATA_SEC,
 	[LIBBPF_MAP_BSS]	= BSS_SEC,
 	[LIBBPF_MAP_RODATA]	= RODATA_SEC,
-	[LIBBPF_MAP_EXTERN]	= EXTERN_SEC,
+	[LIBBPF_MAP_KCONFIG]	= KCONFIG_SEC,
 };
 
 struct bpf_map {
@@ -305,7 +305,7 @@ struct bpf_object {
 	char *kconfig_path;
 	struct extern_desc *externs;
 	int nr_extern;
-	int extern_map_idx;
+	int kconfig_map_idx;
 
 	bool loaded;
 	bool has_pseudo_calls;
@@ -606,7 +606,7 @@ static struct bpf_object *bpf_object__new(const char *path,
 	obj->efile.data_shndx = -1;
 	obj->efile.rodata_shndx = -1;
 	obj->efile.bss_shndx = -1;
-	obj->extern_map_idx = -1;
+	obj->kconfig_map_idx = -1;
 
 	obj->kern_version = get_kernel_version();
 	obj->loaded = false;
@@ -902,11 +902,25 @@ static size_t bpf_map_mmap_sz(const struct bpf_map *map)
 	return map_sz;
 }
 
+static char *internal_map_name(struct bpf_object *obj,
+			       enum libbpf_map_type type)
+{
+	char map_name[BPF_OBJ_NAME_LEN];
+	const char *sfx = libbpf_type_to_btf_name[type];
+	int sfx_len = max((size_t)7, strlen(sfx));
+	int pfx_len = min((size_t)BPF_OBJ_NAME_LEN - sfx_len - 1,
+			  strlen(obj->name));
+
+	snprintf(map_name, sizeof(map_name), "%.*s%.*s", pfx_len, obj->name,
+		 sfx_len, libbpf_type_to_btf_name[type]);
+
+	return strdup(map_name);
+}
+
 static int
 bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
 			      int sec_idx, void *data, size_t data_sz)
 {
-	char map_name[BPF_OBJ_NAME_LEN];
 	struct bpf_map_def *def;
 	struct bpf_map *map;
 	int err;
@@ -918,9 +932,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
 	map->libbpf_type = type;
 	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);
+	map->name = internal_map_name(obj, type);
 	if (!map->name) {
 		pr_warn("failed to alloc map name\n");
 		return -ENOMEM;
@@ -931,12 +943,12 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
 	def->key_size = sizeof(int);
 	def->value_size = data_sz;
 	def->max_entries = 1;
-	def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_EXTERN
+	def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_KCONFIG
 			 ? BPF_F_RDONLY_PROG : 0;
 	def->map_flags |= BPF_F_MMAPABLE;
 
 	pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n",
-		 map_name, map->sec_idx, map->sec_offset, def->map_flags);
+		 map->name, map->sec_idx, map->sec_offset, def->map_flags);
 
 	map->mmaped = mmap(NULL, bpf_map_mmap_sz(map), PROT_READ | PROT_WRITE,
 			   MAP_SHARED | MAP_ANONYMOUS, -1, 0);
@@ -1232,7 +1244,7 @@ static int bpf_object__read_kernel_config(struct bpf_object *obj,
 	return err;
 }
 
-static int bpf_object__init_extern_map(struct bpf_object *obj)
+static int bpf_object__init_kconfig_map(struct bpf_object *obj)
 {
 	struct extern_desc *last_ext;
 	size_t map_sz;
@@ -1244,13 +1256,13 @@ static int bpf_object__init_extern_map(struct bpf_object *obj)
 	last_ext = &obj->externs[obj->nr_extern - 1];
 	map_sz = last_ext->data_off + last_ext->sz;
 
-	err = bpf_object__init_internal_map(obj, LIBBPF_MAP_EXTERN,
+	err = bpf_object__init_internal_map(obj, LIBBPF_MAP_KCONFIG,
 					    obj->efile.symbols_shndx,
 					    NULL, map_sz);
 	if (err)
 		return err;
 
-	obj->extern_map_idx = obj->nr_maps - 1;
+	obj->kconfig_map_idx = obj->nr_maps - 1;
 
 	return 0;
 }
@@ -1742,7 +1754,7 @@ static int bpf_object__init_maps(struct bpf_object *obj,
 	err = bpf_object__init_user_maps(obj, strict);
 	err = err ?: bpf_object__init_user_btf_maps(obj, strict, pin_root_path);
 	err = err ?: bpf_object__init_global_data_maps(obj);
-	err = err ?: bpf_object__init_extern_map(obj);
+	err = err ?: bpf_object__init_kconfig_map(obj);
 	if (err)
 		return err;
 
@@ -2269,9 +2281,9 @@ static int bpf_object__collect_externs(struct bpf_object *obj)
 			 i, ext->sym_idx, ext->data_off, ext->name);
 	}
 
-	btf_id = btf__find_by_name(obj->btf, EXTERN_SEC);
+	btf_id = btf__find_by_name(obj->btf, KCONFIG_SEC);
 	if (btf_id <= 0) {
-		pr_warn("no BTF info found for '%s' datasec\n", EXTERN_SEC);
+		pr_warn("no BTF info found for '%s' datasec\n", KCONFIG_SEC);
 		return -ESRCH;
 	}
 
@@ -2361,7 +2373,7 @@ bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx)
 	else if (shndx == obj->efile.rodata_shndx)
 		return LIBBPF_MAP_RODATA;
 	else if (shndx == obj->efile.symbols_shndx)
-		return LIBBPF_MAP_EXTERN;
+		return LIBBPF_MAP_KCONFIG;
 	else
 		return LIBBPF_MAP_UNSPEC;
 }
@@ -2908,8 +2920,8 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map)
 		return err;
 	}
 
-	/* Freeze .rodata and .extern map as read-only from syscall side. */
-	if (map_type == LIBBPF_MAP_RODATA || map_type == LIBBPF_MAP_EXTERN) {
+	/* Freeze .rodata and .kconfig map as read-only from syscall side. */
+	if (map_type == LIBBPF_MAP_RODATA || map_type == LIBBPF_MAP_KCONFIG) {
 		err = bpf_map_freeze(map->fd);
 		if (err) {
 			err = -errno;
@@ -4264,7 +4276,7 @@ bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
 			break;
 		case RELO_EXTERN:
 			insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
-			insn[0].imm = obj->maps[obj->extern_map_idx].fd;
+			insn[0].imm = obj->maps[obj->kconfig_map_idx].fd;
 			insn[1].imm = relo->sym_off;
 			break;
 		case RELO_CALL:
@@ -4743,7 +4755,7 @@ static int bpf_object__resolve_externs(struct bpf_object *obj,
 	if (obj->nr_extern == 0)
 		return 0;
 
-	data = obj->maps[obj->extern_map_idx].mmaped;
+	data = obj->maps[obj->kconfig_map_idx].mmaped;
 
 	for (i = 0; i < obj->nr_extern; i++) {
 		ext = &obj->externs[i];
@@ -7526,7 +7538,7 @@ int bpf_object__open_skeleton(struct bpf_object_skeleton *s,
 		}
 
 		/* externs shouldn't be pre-setup from user code */
-		if (mmaped && (*map)->libbpf_type != LIBBPF_MAP_EXTERN)
+		if (mmaped && (*map)->libbpf_type != LIBBPF_MAP_KCONFIG)
 			*mmaped = (*map)->mmaped;
 	}
 
diff --git a/tools/testing/selftests/bpf/prog_tests/skeleton.c b/tools/testing/selftests/bpf/prog_tests/skeleton.c
index ec6f2aec3853..9264a2736018 100644
--- a/tools/testing/selftests/bpf/prog_tests/skeleton.c
+++ b/tools/testing/selftests/bpf/prog_tests/skeleton.c
@@ -15,20 +15,18 @@ void test_skeleton(void)
 	int duration = 0, err;
 	struct test_skeleton* skel;
 	struct test_skeleton__bss *bss;
-	struct test_skeleton__externs *exts;
+	struct test_skeleton__kconfig *kcfg;
 
 	skel = test_skeleton__open();
 	if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
 		return;
 
-	printf("EXTERNS BEFORE: %p\n", skel->externs);
-	if (CHECK(skel->externs, "skel_externs", "externs are mmaped()!\n"))
+	if (CHECK(skel->kconfig, "skel_kconfig", "kconfig is mmaped()!\n"))
 		goto cleanup;
 
 	err = test_skeleton__load(skel);
 	if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err))
 		goto cleanup;
-	printf("EXTERNS AFTER: %p\n", skel->externs);
 
 	bss = skel->bss;
 	bss->in1 = 1;
@@ -37,7 +35,7 @@ void test_skeleton(void)
 	bss->in4 = 4;
 	bss->in5.a = 5;
 	bss->in5.b = 6;
-	exts = skel->externs;
+	kcfg = skel->kconfig;
 
 	err = test_skeleton__attach(skel);
 	if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
@@ -55,10 +53,10 @@ void test_skeleton(void)
 	CHECK(bss->handler_out5.b != 6, "res6", "got %lld != exp %d\n",
 	      bss->handler_out5.b, 6);
 
-	CHECK(bss->bpf_syscall != exts->CONFIG_BPF_SYSCALL, "ext1",
-	      "got %d != exp %d\n", bss->bpf_syscall, exts->CONFIG_BPF_SYSCALL);
-	CHECK(bss->kern_ver != exts->LINUX_KERNEL_VERSION, "ext2",
-	      "got %d != exp %d\n", bss->kern_ver, exts->LINUX_KERNEL_VERSION);
+	CHECK(bss->bpf_syscall != kcfg->CONFIG_BPF_SYSCALL, "ext1",
+	      "got %d != exp %d\n", bss->bpf_syscall, kcfg->CONFIG_BPF_SYSCALL);
+	CHECK(bss->kern_ver != kcfg->LINUX_KERNEL_VERSION, "ext2",
+	      "got %d != exp %d\n", bss->kern_ver, kcfg->LINUX_KERNEL_VERSION);
 
 cleanup:
 	test_skeleton__destroy(skel);
diff --git a/tools/testing/selftests/bpf/progs/test_core_extern.c b/tools/testing/selftests/bpf/progs/test_core_extern.c
index e12f09f9e881..9bfc91d9d004 100644
--- a/tools/testing/selftests/bpf/progs/test_core_extern.c
+++ b/tools/testing/selftests/bpf/progs/test_core_extern.c
@@ -10,16 +10,16 @@
 /* non-existing BPF helper, to test dead code elimination */
 static int (*bpf_missing_helper)(const void *arg1, int arg2) = (void *) 999;
 
-extern int LINUX_KERNEL_VERSION;
-extern bool CONFIG_BPF_SYSCALL; /* strong */
-extern enum libbpf_tristate CONFIG_TRISTATE __weak;
-extern bool CONFIG_BOOL __weak;
-extern char CONFIG_CHAR __weak;
-extern uint16_t CONFIG_USHORT __weak;
-extern int CONFIG_INT __weak;
-extern uint64_t CONFIG_ULONG __weak;
-extern const char CONFIG_STR[8] __weak;
-extern uint64_t CONFIG_MISSING __weak;
+extern int LINUX_KERNEL_VERSION __kconfig;
+extern bool CONFIG_BPF_SYSCALL __kconfig; /* strong */
+extern enum libbpf_tristate CONFIG_TRISTATE __kconfig __weak;
+extern bool CONFIG_BOOL __kconfig __weak;
+extern char CONFIG_CHAR __kconfig __weak;
+extern uint16_t CONFIG_USHORT __kconfig __weak;
+extern int CONFIG_INT __kconfig __weak;
+extern uint64_t CONFIG_ULONG __kconfig __weak;
+extern const char CONFIG_STR[8] __kconfig __weak;
+extern uint64_t CONFIG_MISSING __kconfig __weak;
 
 uint64_t kern_ver = -1;
 uint64_t bpf_syscall = -1;
diff --git a/tools/testing/selftests/bpf/progs/test_skeleton.c b/tools/testing/selftests/bpf/progs/test_skeleton.c
index 9caa44758ea2..4f69aac5635f 100644
--- a/tools/testing/selftests/bpf/progs/test_skeleton.c
+++ b/tools/testing/selftests/bpf/progs/test_skeleton.c
@@ -21,8 +21,8 @@ char out3 = 0;
 long long out4 = 0;
 int out1 = 0;
 
-extern bool CONFIG_BPF_SYSCALL;
-extern int LINUX_KERNEL_VERSION;
+extern bool CONFIG_BPF_SYSCALL __kconfig;
+extern int LINUX_KERNEL_VERSION __kconfig;
 bool bpf_syscall = 0;
 int kern_ver = 0;
 
-- 
2.17.1


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

* [PATCH bpf-next 2/3] libbpf: allow to augment system Kconfig through extra optional config
  2019-12-19  0:28 [PATCH bpf-next 0/3] Libbpf extern support improvements Andrii Nakryiko
  2019-12-19  0:28 ` [PATCH bpf-next 1/3] libbpf: put Kconfig externs into .kconfig section Andrii Nakryiko
@ 2019-12-19  0:28 ` Andrii Nakryiko
  2019-12-19  0:28 ` [PATCH bpf-next 3/3] libbpf: BTF is required when externs are present Andrii Nakryiko
  2019-12-19  1:35 ` [PATCH bpf-next 0/3] Libbpf extern support improvements Alexei Starovoitov
  3 siblings, 0 replies; 5+ messages in thread
From: Andrii Nakryiko @ 2019-12-19  0:28 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

Instead of all or nothing approach of overriding Kconfig file location, allow
to extend it with extra values and override chosen subset of values though
optional user-provided extra config, passed as a string through open options'
.kconfig option. If same config key is present in both user-supplied config
and Kconfig, user-supplied one wins. This allows applications to more easily
test various conditions despite host kernel's real configuration. If all of
BPF object's __kconfig externs are satisfied from user-supplied config, system
Kconfig won't be read at all.

Simplify selftests by not needing to create temporary Kconfig files.

Suggested-by: Alexei Starovoitov <ast@fb.com>
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 tools/lib/bpf/libbpf.c                        | 204 +++++++++++-------
 tools/lib/bpf/libbpf.h                        |   8 +-
 .../selftests/bpf/prog_tests/core_extern.c    |  32 +--
 3 files changed, 132 insertions(+), 112 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index ed54a6a7f6f2..f90db2b18e4b 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -302,7 +302,7 @@ struct bpf_object {
 	size_t nr_maps;
 	size_t maps_cap;
 
-	char *kconfig_path;
+	char *kconfig;
 	struct extern_desc *externs;
 	int nr_extern;
 	int kconfig_map_idx;
@@ -1149,98 +1149,129 @@ static int set_ext_value_num(struct extern_desc *ext, void *ext_val,
 	return 0;
 }
 
-static int bpf_object__read_kernel_config(struct bpf_object *obj,
-					  const char *config_path,
-					  void *data)
+static int bpf_object__process_kconfig_line(struct bpf_object *obj,
+					    char *buf, void *data)
 {
-	char buf[PATH_MAX], *sep, *value;
 	struct extern_desc *ext;
+	char *sep, *value;
 	int len, err = 0;
 	void *ext_val;
 	__u64 num;
-	gzFile file;
 
-	if (config_path) {
-		file = gzopen(config_path, "r");
-	} else {
-		struct utsname uts;
+	if (strncmp(buf, "CONFIG_", 7))
+		return 0;
 
-		uname(&uts);
-		len = snprintf(buf, PATH_MAX, "/boot/config-%s", uts.release);
-		if (len < 0)
-			return -EINVAL;
-		else if (len >= PATH_MAX)
-			return -ENAMETOOLONG;
-		/* gzopen also accepts uncompressed files. */
-		file = gzopen(buf, "r");
-		if (!file)
-			file = gzopen("/proc/config.gz", "r");
+	sep = strchr(buf, '=');
+	if (!sep) {
+		pr_warn("failed to parse '%s': no separator\n", buf);
+		return -EINVAL;
+	}
+
+	/* Trim ending '\n' */
+	len = strlen(buf);
+	if (buf[len - 1] == '\n')
+		buf[len - 1] = '\0';
+	/* Split on '=' and ensure that a value is present. */
+	*sep = '\0';
+	if (!sep[1]) {
+		*sep = '=';
+		pr_warn("failed to parse '%s': no value\n", buf);
+		return -EINVAL;
+	}
+
+	ext = find_extern_by_name(obj, buf);
+	if (!ext || ext->is_set)
+		return 0;
+
+	ext_val = data + ext->data_off;
+	value = sep + 1;
+
+	switch (*value) {
+	case 'y': case 'n': case 'm':
+		err = set_ext_value_tri(ext, ext_val, *value);
+		break;
+	case '"':
+		err = set_ext_value_str(ext, ext_val, value);
+		break;
+	default:
+		/* assume integer */
+		err = parse_u64(value, &num);
+		if (err) {
+			pr_warn("extern %s=%s should be integer\n",
+				ext->name, value);
+			return err;
+		}
+		err = set_ext_value_num(ext, ext_val, num);
+		break;
 	}
+	if (err)
+		return err;
+	pr_debug("extern %s=%s\n", ext->name, value);
+	return 0;
+}
+
+static int bpf_object__read_kconfig_file(struct bpf_object *obj, void *data)
+{
+	char buf[PATH_MAX];
+	struct utsname uts;
+	int len, err = 0;
+	gzFile file;
+
+	uname(&uts);
+	len = snprintf(buf, PATH_MAX, "/boot/config-%s", uts.release);
+	if (len < 0)
+		return -EINVAL;
+	else if (len >= PATH_MAX)
+		return -ENAMETOOLONG;
+
+	/* gzopen also accepts uncompressed files. */
+	file = gzopen(buf, "r");
+	if (!file)
+		file = gzopen("/proc/config.gz", "r");
+
 	if (!file) {
-		pr_warn("failed to read kernel config at '%s'\n", config_path);
+		pr_warn("failed to open system Kconfig\n");
 		return -ENOENT;
 	}
 
 	while (gzgets(file, buf, sizeof(buf))) {
-		if (strncmp(buf, "CONFIG_", 7))
-			continue;
-
-		sep = strchr(buf, '=');
-		if (!sep) {
-			err = -EINVAL;
-			pr_warn("failed to parse '%s': no separator\n", buf);
-			goto out;
-		}
-		/* Trim ending '\n' */
-		len = strlen(buf);
-		if (buf[len - 1] == '\n')
-			buf[len - 1] = '\0';
-		/* Split on '=' and ensure that a value is present. */
-		*sep = '\0';
-		if (!sep[1]) {
-			err = -EINVAL;
-			*sep = '=';
-			pr_warn("failed to parse '%s': no value\n", buf);
+		err = bpf_object__process_kconfig_line(obj, buf, data);
+		if (err) {
+			pr_warn("error parsing system Kconfig line '%s': %d\n",
+				buf, err);
 			goto out;
 		}
+	}
 
-		ext = find_extern_by_name(obj, buf);
-		if (!ext)
-			continue;
-		if (ext->is_set) {
-			err = -EINVAL;
-			pr_warn("re-defining extern '%s' not allowed\n", buf);
-			goto out;
-		}
+out:
+	gzclose(file);
+	return err;
+}
 
-		ext_val = data + ext->data_off;
-		value = sep + 1;
+static int bpf_object__read_kconfig_mem(struct bpf_object *obj,
+					const char *config, void *data)
+{
+	char buf[PATH_MAX];
+	int err = 0;
+	FILE *file;
 
-		switch (*value) {
-		case 'y': case 'n': case 'm':
-			err = set_ext_value_tri(ext, ext_val, *value);
-			break;
-		case '"':
-			err = set_ext_value_str(ext, ext_val, value);
-			break;
-		default:
-			/* assume integer */
-			err = parse_u64(value, &num);
-			if (err) {
-				pr_warn("extern %s=%s should be integer\n",
-					ext->name, value);
-				goto out;
-			}
-			err = set_ext_value_num(ext, ext_val, num);
+	file = fmemopen((void *)config, strlen(config), "r");
+	if (!file) {
+		err = -errno;
+		pr_warn("failed to open in-memory Kconfig: %d\n", err);
+		return err;
+	}
+
+	while (fgets(buf, sizeof(buf), file)) {
+		err = bpf_object__process_kconfig_line(obj, buf, data);
+		if (err) {
+			pr_warn("error parsing in-memory Kconfig line '%s': %d\n",
+				buf, err);
 			break;
 		}
-		if (err)
-			goto out;
-		pr_debug("extern %s=%s\n", ext->name, value);
 	}
 
-out:
-	gzclose(file);
+	fclose(file);
 	return err;
 }
 
@@ -4567,7 +4598,7 @@ static struct bpf_object *
 __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
 		   const struct bpf_object_open_opts *opts)
 {
-	const char *obj_name, *kconfig_path;
+	const char *obj_name, *kconfig;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
 	char tmp_name[64];
@@ -4599,10 +4630,10 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
 		return obj;
 
 	obj->relaxed_core_relocs = OPTS_GET(opts, relaxed_core_relocs, false);
-	kconfig_path = OPTS_GET(opts, kconfig_path, NULL);
-	if (kconfig_path) {
-		obj->kconfig_path = strdup(kconfig_path);
-		if (!obj->kconfig_path)
+	kconfig = OPTS_GET(opts, kconfig, NULL);
+	if (kconfig) {
+		obj->kconfig = strdup(kconfig);
+		if (!obj->kconfig)
 			return ERR_PTR(-ENOMEM);
 	}
 
@@ -4745,7 +4776,7 @@ static int bpf_object__sanitize_maps(struct bpf_object *obj)
 }
 
 static int bpf_object__resolve_externs(struct bpf_object *obj,
-				       const char *config_path)
+				       const char *extra_kconfig)
 {
 	bool need_config = false;
 	struct extern_desc *ext;
@@ -4779,8 +4810,21 @@ static int bpf_object__resolve_externs(struct bpf_object *obj,
 			return -EINVAL;
 		}
 	}
+	if (need_config && extra_kconfig) {
+		err = bpf_object__read_kconfig_mem(obj, extra_kconfig, data);
+		if (err)
+			return -EINVAL;
+		need_config = false;
+		for (i = 0; i < obj->nr_extern; i++) {
+			ext = &obj->externs[i];
+			if (!ext->is_set) {
+				need_config = true;
+				break;
+			}
+		}
+	}
 	if (need_config) {
-		err = bpf_object__read_kernel_config(obj, config_path, data);
+		err = bpf_object__read_kconfig_file(obj, data);
 		if (err)
 			return -EINVAL;
 	}
@@ -4818,7 +4862,7 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
 	obj->loaded = true;
 
 	err = bpf_object__probe_caps(obj);
-	err = err ? : bpf_object__resolve_externs(obj, obj->kconfig_path);
+	err = err ? : bpf_object__resolve_externs(obj, obj->kconfig);
 	err = err ? : bpf_object__sanitize_and_load_btf(obj);
 	err = err ? : bpf_object__sanitize_maps(obj);
 	err = err ? : bpf_object__create_maps(obj);
@@ -5412,7 +5456,7 @@ void bpf_object__close(struct bpf_object *obj)
 		zfree(&map->pin_path);
 	}
 
-	zfree(&obj->kconfig_path);
+	zfree(&obj->kconfig);
 	zfree(&obj->externs);
 	obj->nr_extern = 0;
 
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index f7084235bae9..ffe62e3baffd 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -85,12 +85,12 @@ struct bpf_object_open_opts {
 	 */
 	const char *pin_root_path;
 	__u32 attach_prog_fd;
-	/* kernel config file path override (for CONFIG_ externs); can point
-	 * to either uncompressed text file or .gz file
+	/* Additional kernel config content that augments and overrides
+	 * system Kconfig for CONFIG_xxx externs.
 	 */
-	const char *kconfig_path;
+	const char *kconfig;
 };
-#define bpf_object_open_opts__last_field kconfig_path
+#define bpf_object_open_opts__last_field kconfig
 
 LIBBPF_API struct bpf_object *bpf_object__open(const char *path);
 LIBBPF_API struct bpf_object *
diff --git a/tools/testing/selftests/bpf/prog_tests/core_extern.c b/tools/testing/selftests/bpf/prog_tests/core_extern.c
index 5f03dc1de29e..b093787e9448 100644
--- a/tools/testing/selftests/bpf/prog_tests/core_extern.c
+++ b/tools/testing/selftests/bpf/prog_tests/core_extern.c
@@ -23,19 +23,13 @@ static uint32_t get_kernel_version(void)
 static struct test_case {
 	const char *name;
 	const char *cfg;
-	const char *cfg_path;
 	bool fails;
 	struct test_core_extern__data data;
 } test_cases[] = {
-	{ .name = "default search path", .cfg_path = NULL,
-	  .data = { .bpf_syscall = true } },
-	{ .name = "/proc/config.gz", .cfg_path = "/proc/config.gz",
-	  .data = { .bpf_syscall = true } },
-	{ .name = "missing config", .fails = true,
-	  .cfg_path = "/proc/invalid-config.gz" },
+	{ .name = "default search path", .data = { .bpf_syscall = true } },
 	{
 		.name = "custom values",
-		.cfg = "CONFIG_BPF_SYSCALL=y\n"
+		.cfg = "CONFIG_BPF_SYSCALL=n\n"
 		       "CONFIG_TRISTATE=m\n"
 		       "CONFIG_BOOL=y\n"
 		       "CONFIG_CHAR=100\n"
@@ -45,7 +39,7 @@ static struct test_case {
 		       "CONFIG_STR=\"abracad\"\n"
 		       "CONFIG_MISSING=0",
 		.data = {
-			.bpf_syscall = true,
+			.bpf_syscall = false,
 			.tristate_val = TRI_MODULE,
 			.bool_val = true,
 			.char_val = 100,
@@ -133,30 +127,14 @@ void test_core_extern(void)
 	int n = sizeof(*skel->data) / sizeof(uint64_t);
 
 	for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
-		char tmp_cfg_path[] = "/tmp/test_core_extern_cfg.XXXXXX";
 		struct test_case *t = &test_cases[i];
 		DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
-			.kconfig_path = t->cfg_path,
+			.kconfig = t->cfg,
 		);
 
 		if (!test__start_subtest(t->name))
 			continue;
 
-		if (t->cfg) {
-			size_t n = strlen(t->cfg) + 1;
-			int fd = mkstemp(tmp_cfg_path);
-			int written;
-
-			if (CHECK(fd < 0, "mkstemp", "errno: %d\n", errno))
-				continue;
-			printf("using '%s' as config file\n", tmp_cfg_path);
-			written = write(fd, t->cfg, n);
-			close(fd);
-			if (CHECK_FAIL(written != n))
-				goto cleanup;
-			opts.kconfig_path = tmp_cfg_path;
-		}
-
 		skel = test_core_extern__open_opts(&opts);
 		if (CHECK(!skel, "skel_open", "skeleton open failed\n"))
 			goto cleanup;
@@ -185,8 +163,6 @@ void test_core_extern(void)
 			       j, exp[j], got[j]);
 		}
 cleanup:
-		if (t->cfg)
-			unlink(tmp_cfg_path);
 		test_core_extern__destroy(skel);
 		skel = NULL;
 	}
-- 
2.17.1


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

* [PATCH bpf-next 3/3] libbpf: BTF is required when externs are present
  2019-12-19  0:28 [PATCH bpf-next 0/3] Libbpf extern support improvements Andrii Nakryiko
  2019-12-19  0:28 ` [PATCH bpf-next 1/3] libbpf: put Kconfig externs into .kconfig section Andrii Nakryiko
  2019-12-19  0:28 ` [PATCH bpf-next 2/3] libbpf: allow to augment system Kconfig through extra optional config Andrii Nakryiko
@ 2019-12-19  0:28 ` Andrii Nakryiko
  2019-12-19  1:35 ` [PATCH bpf-next 0/3] Libbpf extern support improvements Alexei Starovoitov
  3 siblings, 0 replies; 5+ messages in thread
From: Andrii Nakryiko @ 2019-12-19  0:28 UTC (permalink / raw)
  To: bpf, netdev, ast, daniel; +Cc: andrii.nakryiko, kernel-team, Andrii Nakryiko

BTF is required to get type information about extern variables.

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

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index f90db2b18e4b..e1d91c64e093 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1887,7 +1887,8 @@ 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;
+	return obj->efile.btf_maps_shndx >= 0 ||
+	       obj->nr_extern > 0;
 }
 
 static int bpf_object__init_btf(struct bpf_object *obj,
-- 
2.17.1


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

* Re: [PATCH bpf-next 0/3] Libbpf extern support improvements
  2019-12-19  0:28 [PATCH bpf-next 0/3] Libbpf extern support improvements Andrii Nakryiko
                   ` (2 preceding siblings ...)
  2019-12-19  0:28 ` [PATCH bpf-next 3/3] libbpf: BTF is required when externs are present Andrii Nakryiko
@ 2019-12-19  1:35 ` Alexei Starovoitov
  3 siblings, 0 replies; 5+ messages in thread
From: Alexei Starovoitov @ 2019-12-19  1:35 UTC (permalink / raw)
  To: Andrii Nakryiko; +Cc: bpf, netdev, ast, daniel, andrii.nakryiko, kernel-team

On Wed, Dec 18, 2019 at 04:28:33PM -0800, Andrii Nakryiko wrote:
> Based on latest feedback and discussions, this patch set implements the
> following changes:
> 
> - Kconfig-provided externs have to be in .kconfig section, for which
>   bpf_helpers.h provides convenient __kconfig macro (Daniel);
> - instead of allowing to override Kconfig file path, switch this to ability to
>   extend and override system Kconfig with user-provided custom values (Alexei);
> - BTF is required when externs are used.

Applied, Thanks

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

end of thread, other threads:[~2019-12-19  1:35 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-19  0:28 [PATCH bpf-next 0/3] Libbpf extern support improvements Andrii Nakryiko
2019-12-19  0:28 ` [PATCH bpf-next 1/3] libbpf: put Kconfig externs into .kconfig section Andrii Nakryiko
2019-12-19  0:28 ` [PATCH bpf-next 2/3] libbpf: allow to augment system Kconfig through extra optional config Andrii Nakryiko
2019-12-19  0:28 ` [PATCH bpf-next 3/3] libbpf: BTF is required when externs are present Andrii Nakryiko
2019-12-19  1:35 ` [PATCH bpf-next 0/3] Libbpf extern support improvements Alexei Starovoitov

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