* [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