BPF List
 help / color / mirror / Atom feed
* [PATCH bpf-next 1/2] libbpf: add bpf_map__reuse_fd_from_loader_ctx() to allow shared maps
@ 2026-06-26 21:51 John Fastabend
  2026-06-26 21:51 ` [PATCH bpf-next 2/2] libbpf: selftest for bpf_map__reuse_fd_from_loader_ctx() John Fastabend
  0 siblings, 1 reply; 2+ messages in thread
From: John Fastabend @ 2026-06-26 21:51 UTC (permalink / raw)
  To: bpf; +Cc: john.fastabend, daniel

This lets the gen_loader use a map that may already be loaded/pinned on
the target system. When we are loading many signed programs on the target
we often need them to share maps to communicate/pass data between the
programs.

Note it is slightly complicated by needing to ensure on error/close that
we only emit_sys_close_blob() for map ids that are not these shared
type otherwise we could close a shared map unintentionally. To track this
we added a borrowed_fd_array.

Signed-off-by: John Fastabend <john.fastabend@gmail.com>
---
 tools/lib/bpf/bpf_gen_internal.h |  2 ++
 tools/lib/bpf/gen_loader.c       | 50 ++++++++++++++++++++++++++++++--
 tools/lib/bpf/libbpf.c           | 11 +++++++
 tools/lib/bpf/libbpf.h           |  1 +
 tools/lib/bpf/libbpf.map         |  1 +
 5 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/tools/lib/bpf/bpf_gen_internal.h b/tools/lib/bpf/bpf_gen_internal.h
index 49af4260b8e6..e555c3165105 100644
--- a/tools/lib/bpf/bpf_gen_internal.h
+++ b/tools/lib/bpf/bpf_gen_internal.h
@@ -50,6 +50,7 @@ struct bpf_gen {
 	struct ksym_desc *ksyms;
 	__u32 nr_ksyms;
 	int fd_array;
+	int borrowed_fd_array;
 	int nr_fd_array;
 	int hash_insn_offset[SHA256_DWORD_SIZE];
 };
@@ -62,6 +63,7 @@ void bpf_gen__map_create(struct bpf_gen *gen,
 			 enum bpf_map_type map_type, const char *map_name,
 			 __u32 key_size, __u32 value_size, __u32 max_entries,
 			 struct bpf_map_create_opts *map_attr, int map_idx);
+void bpf_gen__map_reuse_fd(struct bpf_gen *gen, int map_idx);
 void bpf_gen__prog_load(struct bpf_gen *gen,
 			enum bpf_prog_type prog_type, const char *prog_name,
 			const char *license, struct bpf_insn *insns, size_t insn_cnt,
diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
index c7f2d2ac7bb3..1cb00e0244b5 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c
@@ -47,6 +47,11 @@ static int blob_fd_array_off(struct bpf_gen *gen, int index)
 	return gen->fd_array + index * sizeof(int);
 }
 
+static int blob_borrowed_fd_array_off(struct bpf_gen *gen, int index)
+{
+	return gen->borrowed_fd_array + index * sizeof(int);
+}
+
 static int realloc_insn_buf(struct bpf_gen *gen, __u32 size)
 {
 	size_t off = gen->insn_cur - gen->insn_start;
@@ -111,6 +116,7 @@ static void emit2(struct bpf_gen *gen, struct bpf_insn insn1, struct bpf_insn in
 
 static int add_data(struct bpf_gen *gen, const void *data, __u32 size);
 static void emit_sys_close_blob(struct bpf_gen *gen, int blob_off);
+static void emit_sys_close_owned_blob(struct bpf_gen *gen, int map_idx);
 static void emit_signature_match(struct bpf_gen *gen);
 
 void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps)
@@ -119,6 +125,7 @@ void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps
 	int i;
 
 	gen->fd_array = add_data(gen, NULL, MAX_FD_ARRAY_SZ * sizeof(int));
+	gen->borrowed_fd_array = add_data(gen, NULL, nr_maps * sizeof(int));
 	gen->log_level = log_level;
 	/* save ctx pointer into R6 */
 	emit(gen, BPF_MOV64_REG(BPF_REG_6, BPF_REG_1));
@@ -137,9 +144,10 @@ void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps
 			      /* size of cleanup code below (including map fd cleanup) */
 			      (nr_progs_sz / 4) * 3 + 2 +
 			      /* 6 insns for emit_sys_close_blob,
+			       * 4 insns for borrowed fd check,
 			       * 6 insns for debug_regs in emit_sys_close_blob
 			       */
-			      nr_maps * (6 + (gen->log_level ? 6 : 0))));
+			      nr_maps * (10 + (gen->log_level ? 6 : 0))));
 
 	/* remember the label where all error branches will jump to */
 	gen->cleanup_label = gen->insn_cur - gen->insn_start;
@@ -150,7 +158,7 @@ void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps
 		emit(gen, BPF_EMIT_CALL(BPF_FUNC_sys_close));
 	}
 	for (i = 0; i < nr_maps; i++)
-		emit_sys_close_blob(gen, blob_fd_array_off(gen, i));
+		emit_sys_close_owned_blob(gen, i);
 	/* R7 contains the error code from sys_bpf. Copy it into R0 and exit. */
 	emit(gen, BPF_MOV64_REG(BPF_REG_0, BPF_REG_7));
 	emit(gen, BPF_EXIT_INSN());
@@ -377,6 +385,17 @@ static void emit_sys_close_blob(struct bpf_gen *gen, int blob_off)
 	__emit_sys_close(gen);
 }
 
+static void emit_sys_close_owned_blob(struct bpf_gen *gen, int map_idx)
+{
+	int close_insn_cnt = 6 + (gen->log_level ? 6 : 0);
+
+	emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE,
+					 0, 0, 0, blob_borrowed_fd_array_off(gen, map_idx)));
+	emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0));
+	emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, close_insn_cnt));
+	emit_sys_close_blob(gen, blob_fd_array_off(gen, map_idx));
+}
+
 static void compute_sha_update_offsets(struct bpf_gen *gen);
 
 int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps)
@@ -460,6 +479,11 @@ void bpf_gen__free(struct bpf_gen *gen)
 	_val;							\
 })
 
+static void set_blob_borrowed_fd_array_off(struct bpf_gen *gen, int index)
+{
+	*(__u32 *)(gen->data_start + blob_borrowed_fd_array_off(gen, index)) = tgt_endian(1);
+}
+
 static void compute_sha_update_offsets(struct bpf_gen *gen)
 {
 	__u64 sha[SHA256_DWORD_SIZE];
@@ -596,6 +620,28 @@ void bpf_gen__map_create(struct bpf_gen *gen,
 		emit_sys_close_stack(gen, stack_off(inner_map_fd));
 }
 
+void bpf_gen__map_reuse_fd(struct bpf_gen *gen, int map_idx)
+{
+	int idx;
+
+	if (map_idx != gen->nr_maps) {
+		gen->error = -EDOM;
+		return;
+	}
+
+	idx = add_map_fd(gen);
+	if (gen->error)
+		return;
+
+	set_blob_borrowed_fd_array_off(gen, idx);
+	move_ctx2blob(gen, blob_fd_array_off(gen, idx), 4,
+		      sizeof(struct bpf_loader_ctx) +
+		      sizeof(struct bpf_map_desc) * map_idx +
+		      offsetof(struct bpf_map_desc, map_fd),
+		      false);
+	pr_debug("gen: map_reuse_fd idx %d\n", map_idx);
+}
+
 static void emit_signature_match(struct bpf_gen *gen)
 {
 	__s64 off;
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 7162146280a8..af1553e9d4df 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -5085,6 +5085,15 @@ int bpf_map__reuse_fd(struct bpf_map *map, int fd)
 	return libbpf_err(err);
 }
 
+int bpf_map__reuse_fd_from_loader_ctx(struct bpf_map *map)
+{
+	if (map_is_created(map))
+		return libbpf_err(-EBUSY);
+
+	map->reused = true;
+	return 0;
+}
+
 __u32 bpf_map__max_entries(const struct bpf_map *map)
 {
 	return map->def.max_entries;
@@ -5659,6 +5668,8 @@ bpf_object__create_maps(struct bpf_object *obj)
 		}
 
 		if (map->reused) {
+			if (obj->gen_loader)
+				bpf_gen__map_reuse_fd(obj->gen_loader, map - obj->maps);
 			pr_debug("map '%s': skipping creation (preset fd=%d)\n",
 				 map->name, map->fd);
 		} else {
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index b965ad571540..4145a16c8963 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -1135,6 +1135,7 @@ LIBBPF_API bool bpf_map__autoattach(const struct bpf_map *map);
  */
 LIBBPF_API int bpf_map__fd(const struct bpf_map *map);
 LIBBPF_API int bpf_map__reuse_fd(struct bpf_map *map, int fd);
+LIBBPF_API int bpf_map__reuse_fd_from_loader_ctx(struct bpf_map *map);
 /* get map name */
 LIBBPF_API const char *bpf_map__name(const struct bpf_map *map);
 /* get/set map type */
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index b731df19ae69..d6c2f1d75cda 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -458,6 +458,7 @@ LIBBPF_1.7.0 {
 
 LIBBPF_1.8.0 {
 	global:
+		bpf_map__reuse_fd_from_loader_ctx;
 		bpf_program__attach_tracing_multi;
 		bpf_program__clone;
 		btf__new_empty_opts;
-- 
2.53.0


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

* [PATCH bpf-next 2/2] libbpf: selftest for bpf_map__reuse_fd_from_loader_ctx()
  2026-06-26 21:51 [PATCH bpf-next 1/2] libbpf: add bpf_map__reuse_fd_from_loader_ctx() to allow shared maps John Fastabend
@ 2026-06-26 21:51 ` John Fastabend
  0 siblings, 0 replies; 2+ messages in thread
From: John Fastabend @ 2026-06-26 21:51 UTC (permalink / raw)
  To: bpf; +Cc: john.fastabend, daniel

Extend signed loader test case to exercise __reuse_fd_from_loader_ctx().

Signed-off-by: John Fastabend <john.fastabend@gmail.com>
---
 .../selftests/bpf/prog_tests/signed_loader.c  | 115 ++++++++++++++++++
 1 file changed, 115 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/signed_loader.c b/tools/testing/selftests/bpf/prog_tests/signed_loader.c
index 5fc417e31fc6..0af33362ab1b 100644
--- a/tools/testing/selftests/bpf/prog_tests/signed_loader.c
+++ b/tools/testing/selftests/bpf/prog_tests/signed_loader.c
@@ -8,8 +8,10 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <unistd.h>
 #include <linux/keyctl.h>
 #include <linux/bpf.h>
+#include <linux/if_ether.h>
 
 #include "bpf/libbpf_internal.h" /* for libbpf_sha256() */
 #include "bpf/skel_internal.h"	 /* for loader ctx layout (bpf_loader_ctx etc) */
@@ -699,6 +701,117 @@ static void metadata_ctx_initial_value_ignored(void)
 	test_signed_loader_data__destroy(skel);
 }
 
+static void loader_ctx_reuse_fd_shared_map(void)
+{
+	LIBBPF_OPTS(gen_loader_opts, gopts, .gen_hash = true);
+	struct test_signed_loader_map *skel = NULL;
+	struct bpf_map_info shared_info, reused_info;
+	__u32 shared_info_len, reused_info_len;
+	__u8 excl[SHA256_DIGEST_LENGTH];
+	__u8 pkt[ETH_HLEN] = {};
+	__u32 key = 0, expected_retval = 42;
+	__u64 shared_value = expected_retval;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = pkt,
+		.data_size_in = sizeof(pkt),
+		.repeat = 1,
+	);
+	int nr_maps = 0, nr_progs = 0, ctx_fd = -1, shared_fd = -1;
+	int supplied_fd, r;
+	struct bpf_prog_desc *pd;
+	struct bpf_map_desc *md;
+	struct bpf_program *p;
+	struct bpf_map *m;
+	unsigned char *blob = NULL;
+	__u32 ctx_sz, data_sz;
+	void *ctx = NULL;
+	bool ran;
+
+	skel = test_signed_loader_map__open();
+	if (!ASSERT_OK_PTR(skel, "skel_open"))
+		goto cleanup;
+	if (!ASSERT_OK(bpf_map__reuse_fd_from_loader_ctx(skel->maps.amap),
+		       "reuse_fd_from_loader_ctx"))
+		goto cleanup;
+	if (!ASSERT_OK(bpf_object__gen_loader(skel->obj, &gopts), "gen_loader"))
+		goto cleanup;
+	if (!ASSERT_OK(bpf_object__load(skel->obj), "gen_load"))
+		goto cleanup;
+
+	bpf_object__for_each_program(p, skel->obj)
+		nr_progs++;
+	bpf_object__for_each_map(m, skel->obj)
+		nr_maps++;
+	if (!ASSERT_EQ(nr_maps, 1, "one map") ||
+	    !ASSERT_EQ(nr_progs, 1, "one prog"))
+		goto cleanup;
+
+	shared_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "ctx_reuse",
+				   sizeof(__u32), sizeof(__u64), 4, NULL);
+	if (!ASSERT_OK_FD(shared_fd, "shared_map"))
+		goto cleanup;
+	if (!ASSERT_OK(bpf_map_update_elem(shared_fd, &key, &shared_value, BPF_ANY),
+		       "shared_map_update"))
+		goto cleanup;
+
+	ctx_sz = sizeof(struct bpf_loader_ctx) +
+		 nr_maps * sizeof(struct bpf_map_desc) +
+		 nr_progs * sizeof(struct bpf_prog_desc);
+	ctx = calloc(1, ctx_sz);
+	if (!ASSERT_OK_PTR(ctx, "ctx_alloc"))
+		goto cleanup;
+	((struct bpf_loader_ctx *)ctx)->sz = ctx_sz;
+
+	md = (struct bpf_map_desc *)((char *)ctx + sizeof(struct bpf_loader_ctx));
+	pd = (struct bpf_prog_desc *)(md + nr_maps);
+	ctx_fd = dup(shared_fd);
+	if (!ASSERT_OK_FD(ctx_fd, "ctx_map_fd"))
+		goto cleanup;
+	md[0].map_fd = ctx_fd;
+	ctx_fd = -1;
+	supplied_fd = md[0].map_fd;
+
+	libbpf_sha256(gopts.insns, gopts.insns_sz, excl);
+	data_sz = gopts.data_sz;
+	blob = malloc(data_sz);
+	if (!ASSERT_OK_PTR(blob, "blob_alloc"))
+		goto cleanup;
+	memcpy(blob, gopts.data, data_sz);
+
+	r = run_gen_loader(gopts.insns, gopts.insns_sz, blob, data_sz,
+			   excl, sizeof(excl), NULL, 0, true, ctx, ctx_sz, &ran);
+	if (!ASSERT_TRUE(ran, "loader ran") ||
+	    !ASSERT_EQ(r, 0, "loader retval"))
+		goto cleanup;
+
+	ASSERT_EQ(md[0].map_fd, supplied_fd, "ctx map fd preserved");
+	ASSERT_GT(pd[0].prog_fd, 0, "prog fd populated");
+	if (ASSERT_OK(bpf_prog_test_run_opts(pd[0].prog_fd, &topts),
+		      "shared map prog run"))
+		ASSERT_EQ(topts.retval, expected_retval, "shared map value");
+
+	memset(&shared_info, 0, sizeof(shared_info));
+	memset(&reused_info, 0, sizeof(reused_info));
+	shared_info_len = sizeof(shared_info);
+	reused_info_len = sizeof(reused_info);
+	if (ASSERT_OK(bpf_map_get_info_by_fd(shared_fd, &shared_info, &shared_info_len),
+		      "shared map info") &&
+	    ASSERT_OK(bpf_map_get_info_by_fd(md[0].map_fd, &reused_info, &reused_info_len),
+		      "reused map info"))
+		ASSERT_EQ(reused_info.id, shared_info.id, "shared map reused");
+
+cleanup:
+	if (ctx)
+		close_loader_ctx_fds(ctx, nr_maps, nr_progs);
+	if (ctx_fd >= 0)
+		close(ctx_fd);
+	if (shared_fd >= 0)
+		close(shared_fd);
+	free(blob);
+	free(ctx);
+	test_signed_loader_map__destroy(skel);
+}
+
 /*
  * The load-time signature must authenticate the loader instructions: a valid
  * signature loads, and the very same signature over one-byte-tampered insns is
@@ -1112,6 +1225,8 @@ void test_signed_loader(void)
 		metadata_ctx_max_entries_ignored();
 	if (test__start_subtest("metadata_ctx_initial_value_ignored"))
 		metadata_ctx_initial_value_ignored();
+	if (test__start_subtest("loader_ctx_reuse_fd_shared_map"))
+		loader_ctx_reuse_fd_shared_map();
 	if (test__start_subtest("signature_authenticates_insns"))
 		signature_authenticates_insns();
 	if (test__start_subtest("hash_requires_frozen"))
-- 
2.53.0


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

end of thread, other threads:[~2026-06-26 21:51 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-26 21:51 [PATCH bpf-next 1/2] libbpf: add bpf_map__reuse_fd_from_loader_ctx() to allow shared maps John Fastabend
2026-06-26 21:51 ` [PATCH bpf-next 2/2] libbpf: selftest for bpf_map__reuse_fd_from_loader_ctx() John Fastabend

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox