* [PATCH bpf-next v3] bpftool: Compute map size of light skeletons at runtime
@ 2026-09-15 16:14 Leon Hwang
2026-09-15 16:53 ` bot+bpf-ci
2026-09-17 11:24 ` Quentin Monnet
0 siblings, 2 replies; 3+ messages in thread
From: Leon Hwang @ 2026-09-15 16:14 UTC (permalink / raw)
To: bpf
Cc: Quentin Monnet, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, linux-kernel, Leon Hwang
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 and max entries to the generated code and
round mmap size 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@linux.dev>
---
v2 -> v3:
* Hoist 'sysconf(_SC_PAGE_SIZE)' and the inner 'roundup()' in
skel_map_mmap_sz(). (bot+bpf-ci)
* v2: https://lore.kernel.org/bpf/20260914153326.72498-1-leon.hwang@linux.dev/
v1 -> v2:
* Pass max entries alongside value size.
* Use the computed mmap size for mprotect() in userspace's
skel_protect_map_data().
* Round up the mmap size to page size with keeping the original
multiplying max_entries logic. (Andrii)
* v1: https://lore.kernel.org/bpf/20260911145914.23676-1-leon.hwang@linux.dev/
---
tools/bpf/bpftool/gen.c | 26 ++++++-------------
tools/lib/bpf/skel_internal.h | 48 +++++++++++++++++++++++++----------
2 files changed, 42 insertions(+), 32 deletions(-)
diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
index a50540ef6521..ee680f7c2116 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, %3$u);\n",
+ ident, bpf_map__value_size(map), bpf_map__max_entries(map));
codegen("\
\n\
skel_closenz(skel->maps.%1$s.map_fd); \n\
@@ -771,13 +761,13 @@ 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\
+ skel->%1$s = (__typeof__(skel->%1$s))skel_prep_map_data((void *)data, %2$u, %3$u,\n\
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, bpf_map__value_size(map), bpf_map__max_entries(map));
}
codegen("\
\n\
@@ -871,14 +861,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, %3$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), bpf_map__max_entries(map));
continue;
}
@@ -890,11 +880,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$u, %4$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), bpf_map__max_entries(map), mmap_flags);
}
codegen("\
\n\
diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h
index 1f3f332dffbe..9e16b8e8cb7c 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>
@@ -127,7 +128,7 @@ static inline void skel_free(const void *p)
* either bpf_probe_read_kernel() or bpf_copy_from_user() from initial_value
* depending on bpf_loader_ctx->flags.
*/
-static inline void skel_free_map_data(void *p, __u64 addr, size_t sz)
+static inline void skel_free_map_data(void *p, __u64 addr, size_t val_sz, __u32 max_entries)
{
if (addr != ~0ULL)
kvfree(p);
@@ -138,18 +139,20 @@ 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, __u32 max_entries,
+ size_t data_sz)
{
void *addr;
- addr = kvmalloc(val_sz, GFP_KERNEL);
+ addr = kvmalloc(data_sz, GFP_KERNEL);
if (!addr)
return NULL;
- memcpy(addr, val, val_sz);
+ memcpy(addr, val, data_sz);
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, __u32 max_entries,
+ int flags, int fd)
{
struct bpf_map *map;
void *addr = NULL;
@@ -172,9 +175,10 @@ static inline void *skel_finalize_map_data(__u64 *init_val, size_t mmap_sz, int
return addr;
}
-static inline int skel_protect_map_data(void *p, __u64 *init_val, size_t sz)
+static inline int skel_protect_map_data(void *p, __u64 *init_val, size_t val_sz, __u32 max_entries)
{
- (void)sz;
+ (void)val_sz;
+ (void)max_entries;
kvfree(p);
*init_val = ~0ULL;
@@ -193,25 +197,39 @@ static inline void skel_free(void *p)
free(p);
}
-static inline void skel_free_map_data(void *p, __u64 addr, size_t sz)
+static inline size_t skel_map_mmap_sz(size_t val_sz, __u32 max_entries)
{
- munmap(p, sz);
+ const long page_sz = sysconf(_SC_PAGE_SIZE);
+ size_t mmap_sz;
+
+ mmap_sz = roundup(val_sz, 8) * max_entries;
+ mmap_sz = roundup(mmap_sz, page_sz);
+ return mmap_sz;
}
-static inline void *skel_prep_map_data(const void *val, size_t mmap_sz, size_t val_sz)
+static inline void skel_free_map_data(void *p, __u64 addr, size_t val_sz, __u32 max_entries)
{
+ munmap(p, skel_map_mmap_sz(val_sz, max_entries));
+}
+
+static inline void *skel_prep_map_data(const void *val, size_t val_sz, __u32 max_entries,
+ size_t data_sz)
+{
+ size_t mmap_sz = skel_map_mmap_sz(val_sz, max_entries);
void *addr;
addr = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (addr == (void *) -1)
return NULL;
- memcpy(addr, val, val_sz);
+ memcpy(addr, val, data_sz);
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, __u32 max_entries,
+ int flags, int fd)
{
+ size_t mmap_sz = skel_map_mmap_sz(val_sz, max_entries);
void *addr;
addr = mmap((void *) (long) *init_val, mmap_sz, flags, MAP_SHARED | MAP_FIXED, fd, 0);
@@ -220,11 +238,13 @@ static inline void *skel_finalize_map_data(__u64 *init_val, size_t mmap_sz, int
return addr;
}
-static inline int skel_protect_map_data(void *p, __u64 *init_val, size_t sz)
+static inline int skel_protect_map_data(void *p, __u64 *init_val, size_t val_sz, __u32 max_entries)
{
+ size_t mmap_sz = skel_map_mmap_sz(val_sz, max_entries);
+
(void)init_val;
- if (mprotect(p, sz, PROT_READ))
+ if (mprotect(p, mmap_sz, PROT_READ))
return -errno;
return 0;
}
--
2.55.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH bpf-next v3] bpftool: Compute map size of light skeletons at runtime
2026-09-15 16:14 [PATCH bpf-next v3] bpftool: Compute map size of light skeletons at runtime Leon Hwang
@ 2026-09-15 16:53 ` bot+bpf-ci
2026-09-17 11:24 ` Quentin Monnet
1 sibling, 0 replies; 3+ messages in thread
From: bot+bpf-ci @ 2026-09-15 16:53 UTC (permalink / raw)
To: leon.hwang, bpf
Cc: qmo, ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, linux-kernel,
leon.hwang, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 4169 bytes --]
> diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
> index a50540ef6521..ee680f7c2116 100644
> --- a/tools/bpf/bpftool/gen.c
> +++ b/tools/bpf/bpftool/gen.c
[ ... ]
> diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h
> index 1f3f332dffbe..9e16b8e8cb7c 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>
> @@ -127,7 +128,7 @@ static inline void skel_free(const void *p)
> * either bpf_probe_read_kernel() or bpf_copy_from_user() from initial_value
> * depending on bpf_loader_ctx->flags.
> */
> -static inline void skel_free_map_data(void *p, __u64 addr, size_t sz)
> +static inline void skel_free_map_data(void *p, __u64 addr, size_t val_sz, __u32 max_entries)
> {
> if (addr != ~0ULL)
> kvfree(p);
> @@ -138,18 +139,20 @@ 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, __u32 max_entries,
> + size_t data_sz)
> {
> void *addr;
>
> - addr = kvmalloc(val_sz, GFP_KERNEL);
> + addr = kvmalloc(data_sz, GFP_KERNEL);
> if (!addr)
> return NULL;
> - memcpy(addr, val, val_sz);
> + memcpy(addr, val, data_sz);
> return addr;
> }
This isn't a bug, but would naming these something like value_sz and
init_sz make it harder to mix up the map value size and the embedded
blob length? The two names differ by three characters and describe
different quantities, and in the __KERNEL__ variant above val_sz is not
referenced at all while data_sz drives both the kvmalloc() and the
memcpy(). A future edit that reaches for the wrong one would size an
allocation or a copy incorrectly.
>
> -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, __u32 max_entries,
> + int flags, int fd)
[ ... ]
> @@ -193,25 +197,39 @@ static inline void skel_free(void *p)
> free(p);
> }
[ ... ]
>
> -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, __u32 max_entries,
> + size_t data_sz)
> {
> + size_t mmap_sz = skel_map_mmap_sz(val_sz, max_entries);
> void *addr;
>
> addr = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE,
> MAP_SHARED | MAP_ANONYMOUS, -1, 0);
> if (addr == (void *) -1)
> return NULL;
> - memcpy(addr, val, val_sz);
> + memcpy(addr, val, data_sz);
> return addr;
> }
[ ... ]
commit 417792b69014155cfa0ecc10cdffc4c2e54b9e60
Author: Leon Hwang <leon.hwang@linux.dev>
> bpftool: Compute map size of light skeletons at runtime
>
> 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
This isn't a bug, but could this sentence be reworded ("When running
64K-page selftests on a 4K-page VM ...") or dropped, since the patch
does not change the error message itself? The sentence is missing a
subject and describes a diagnostics shortcoming that this patch does not
address.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/34994524877
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH bpf-next v3] bpftool: Compute map size of light skeletons at runtime
2026-09-15 16:14 [PATCH bpf-next v3] bpftool: Compute map size of light skeletons at runtime Leon Hwang
2026-09-15 16:53 ` bot+bpf-ci
@ 2026-09-17 11:24 ` Quentin Monnet
1 sibling, 0 replies; 3+ messages in thread
From: Quentin Monnet @ 2026-09-17 11:24 UTC (permalink / raw)
To: Leon Hwang, bpf
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
Ihor Solodrai, linux-kernel
2026-09-16 00:14 UTC+0800 ~ Leon Hwang <leon.hwang@linux.dev>
> 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 and max entries to the generated code and
> round mmap size 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@linux.dev>
Looks good as far as I can tell, thank you!
Acked-by: Quentin Monnet <qmo@kernel.org>
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-17 11:24 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-15 16:14 [PATCH bpf-next v3] bpftool: Compute map size of light skeletons at runtime Leon Hwang
2026-09-15 16:53 ` bot+bpf-ci
2026-09-17 11:24 ` Quentin Monnet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®