Re: [PATCH bpf-next] bpftool: Compute map size of light skeletons at runtime
From: Andrii Nakryiko
Date: Fri Sep 11 2026 - 15:10:07 EST
On Fri, Sep 11, 2026 at 7:59 AM Leon Hwang <leon.hwang@xxxxxxxxx> wrote:
>
> bpftool rounds memory-mapped data map sizes to the host page size when
> generating a light skeleton. The generated code therefore uses a 64K
> mapping size when bpftool runs on a 64K-page host, even if the skeleton
> runs on a 4K-page target. The target rejects the oversized map mmap(),
> causing failure of loading the light skeleton.
>
> When try to run 64K-page selftests on 4K-page VM, the error message does
> not provide the reason about page size.
>
> test_atomics:PASS:atomics skeleton open 0 nsec
> test_atomics:FAIL:atomics skeleton load unexpected error: -12 (errno 22)
> #15 atomics:FAIL
>
> Pass the original map value size to the generated code and round it to the
> runtime page size in the user-space light skeleton helpers. This keeps
> generated light skeletons independent of the build host page size.
>
> Fixes: d510296d331a ("bpftool: Use syscall/loader program in "prog load" and "gen skeleton" command.")
> Signed-off-by: Leon Hwang <leon.hwang@xxxxxxxxx>
> ---
> tools/bpf/bpftool/gen.c | 27 ++++++++-------------------
> tools/lib/bpf/skel_internal.h | 18 +++++++++++++-----
> 2 files changed, 21 insertions(+), 24 deletions(-)
>
> diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
> index a50540ef6521..77f04c28fee9 100644
> --- a/tools/bpf/bpftool/gen.c
> +++ b/tools/bpf/bpftool/gen.c
> @@ -500,16 +500,6 @@ static void print_hex(const char *data, int data_sz)
> }
> }
>
> -static size_t bpf_map_mmap_sz(const struct bpf_map *map)
> -{
> - long page_sz = sysconf(_SC_PAGE_SIZE);
> - size_t map_sz;
> -
> - map_sz = (size_t)roundup(bpf_map__value_size(map), 8) * bpf_map__max_entries(map);
> - map_sz = roundup(map_sz, page_sz);
> - return map_sz;
> -}
> -
> /* Emit type size asserts for all top-level fields in memory-mapped internal maps. */
> static void codegen_asserts(struct bpf_object *obj, const char *obj_name)
> {
> @@ -686,8 +676,8 @@ static void codegen_destroy(struct bpf_object *obj, const char *obj_name)
> if (!get_map_ident(map, ident, sizeof(ident)))
> continue;
> if (is_skel_data(map, ident, sizeof(ident)))
> - printf("\tskel_free_map_data(skel->%1$s, skel->maps.%1$s.initial_value, %2$zu);\n",
> - ident, bpf_map_mmap_sz(map));
> + printf("\tskel_free_map_data(skel->%1$s, skel->maps.%1$s.initial_value, %2$u);\n",
> + ident, bpf_map__value_size(map));
> codegen("\
> \n\
> skel_closenz(skel->maps.%1$s.map_fd); \n\
> @@ -771,13 +761,12 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
> \n\
> \"; \n\
> \n\
> - skel->%1$s = (__typeof__(skel->%1$s))skel_prep_map_data((void *)data, %2$zd,\n\
> - sizeof(data) - 1);\n\
> + skel->%1$s = (__typeof__(skel->%1$s))skel_prep_map_data((void *)data, sizeof(data) - 1);\n\
> if (!skel->%1$s) \n\
> goto cleanup; \n\
> skel->maps.%1$s.initial_value = (__u64) (long) skel->%1$s;\n\
> } \n\
> - ", ident, bpf_map_mmap_sz(map));
> + ", ident);
> }
> codegen("\
> \n\
> @@ -871,14 +860,14 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
> if (bpf_map__type(map) == BPF_MAP_TYPE_PERCPU_ARRAY) {
> codegen("\
> \n\
> - err = skel_protect_map_data(skel->%1$s, &skel->maps.%1$s.initial_value, %2$zd);\n\
> + err = skel_protect_map_data(skel->%1$s, &skel->maps.%1$s.initial_value, %2$u);\n\
> if (err) \n\
> return err; \n\
> #ifdef __KERNEL__ \n\
> skel->%1$s = NULL; \n\
> #endif \n\
> ",
> - ident, bpf_map_mmap_sz(map));
> + ident, bpf_map__value_size(map));
> continue;
> }
>
> @@ -890,11 +879,11 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
> codegen("\
> \n\
> skel->%1$s = (__typeof__(skel->%1$s))skel_finalize_map_data(&skel->maps.%1$s.initial_value,\n\
> - %2$zd, %3$s, skel->maps.%1$s.map_fd);\n\
> + %2$u, %3$s, skel->maps.%1$s.map_fd);\n\
> if (!skel->%1$s) \n\
> return -ENOMEM; \n\
> ",
> - ident, bpf_map_mmap_sz(map), mmap_flags);
> + ident, bpf_map__value_size(map), mmap_flags);
> }
> codegen("\
> \n\
> diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h
> index 1f3f332dffbe..5787911f17f5 100644
> --- a/tools/lib/bpf/skel_internal.h
> +++ b/tools/lib/bpf/skel_internal.h
> @@ -11,6 +11,7 @@
> #include <linux/bpf.h>
> #else
> #include <unistd.h>
> +#include <sys/param.h>
> #include <sys/syscall.h>
> #include <sys/mman.h>
> #include <linux/keyctl.h>
> @@ -138,7 +139,7 @@ static inline void skel_free_map_data(void *p, __u64 addr, size_t sz)
> */
> }
>
> -static inline void *skel_prep_map_data(const void *val, size_t mmap_sz, size_t val_sz)
> +static inline void *skel_prep_map_data(const void *val, size_t val_sz)
> {
> void *addr;
>
> @@ -149,7 +150,7 @@ static inline void *skel_prep_map_data(const void *val, size_t mmap_sz, size_t v
> return addr;
> }
>
> -static inline void *skel_finalize_map_data(__u64 *init_val, size_t mmap_sz, int flags, int fd)
> +static inline void *skel_finalize_map_data(__u64 *init_val, size_t val_sz, int flags, int fd)
> {
> struct bpf_map *map;
> void *addr = NULL;
> @@ -193,13 +194,19 @@ static inline void skel_free(void *p)
> free(p);
> }
>
> +static inline size_t skel_map_mmap_sz(size_t sz)
> +{
> + return roundup(sz, sysconf(_SC_PAGE_SIZE));
original code had this logic:
map_sz = (size_t)roundup(bpf_map__value_size(map), 8) *
bpf_map__max_entries(map);
map_sz = roundup(map_sz, page_sz);
you completely ignore max_entries in a new code now, which works for
global data because that have max_entires 1, but at the very least we
should add some check or assert for that so that we don't miss a case
when max_entries is not 1
pw-bot: cr
> +}
> +
> static inline void skel_free_map_data(void *p, __u64 addr, size_t sz)
> {
> - munmap(p, sz);
> + munmap(p, skel_map_mmap_sz(sz));
> }
>
> -static inline void *skel_prep_map_data(const void *val, size_t mmap_sz, size_t val_sz)
> +static inline void *skel_prep_map_data(const void *val, size_t val_sz)
> {
> + size_t mmap_sz = skel_map_mmap_sz(val_sz);
> void *addr;
>
> addr = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE,
> @@ -210,8 +217,9 @@ static inline void *skel_prep_map_data(const void *val, size_t mmap_sz, size_t v
> return addr;
> }
>
> -static inline void *skel_finalize_map_data(__u64 *init_val, size_t mmap_sz, int flags, int fd)
> +static inline void *skel_finalize_map_data(__u64 *init_val, size_t val_sz, int flags, int fd)
> {
> + size_t mmap_sz = skel_map_mmap_sz(val_sz);
> void *addr;
>
> addr = mmap((void *) (long) *init_val, mmap_sz, flags, MAP_SHARED | MAP_FIXED, fd, 0);
> --
> 2.55.0
>