mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH bpf-next v2] bpftool: Compute map size of light skeletons at runtime
@ 2026-09-14 15:33 Leon Hwang
  2026-09-14 16:39 ` bot+bpf-ci
  0 siblings, 1 reply; 3+ messages in thread
From: Leon Hwang @ 2026-09-14 15:33 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>
---
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 | 43 +++++++++++++++++++++++------------
 2 files changed, 37 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..6f23ea9410eb 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,34 @@ 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);
+	return roundup(roundup(val_sz, 8) * max_entries, sysconf(_SC_PAGE_SIZE));
 }

-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 +233,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 v2] bpftool: Compute map size of light skeletons at runtime
  2026-09-14 15:33 [PATCH bpf-next v2] bpftool: Compute map size of light skeletons at runtime Leon Hwang
@ 2026-09-14 16:39 ` bot+bpf-ci
  2026-09-15  2:27   ` Leon Hwang
  0 siblings, 1 reply; 3+ messages in thread
From: bot+bpf-ci @ 2026-09-14 16:39 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: 3274 bytes --]

> diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h
> index 1f3f332dffbe..6f23ea9410eb 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>

[ ... ]

> @@ -193,25 +197,34 @@ 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);
> +	return roundup(roundup(val_sz, 8) * max_entries, sysconf(_SC_PAGE_SIZE));
>  }

This isn't a bug, but would it read closer to array_map_mmap_sz() to hoist
the page size into a local here? sysconf(_SC_PAGE_SIZE) is passed straight
into roundup()'s second argument, and roundup() is a macro that expands its
second argument several times in the non-constant case. The three nearby
equivalents all hoist it: the bpf_map_mmap_sz() this patch deletes from
gen.c used 'long page_sz = sysconf(_SC_PAGE_SIZE);', and libbpf's
array_map_mmap_sz() and bpf_map_mmap_sz() both open with 'const long page_sz
= sysconf(_SC_PAGE_SIZE);'. Hoisting it here would also make the helper
independent of which roundup() definition wins, and might let the new
<sys/param.h> include be dropped.

>
> -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 +233,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;
>  }


---
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/34864978845

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

* Re: [PATCH bpf-next v2] bpftool: Compute map size of light skeletons at runtime
  2026-09-14 16:39 ` bot+bpf-ci
@ 2026-09-15  2:27   ` Leon Hwang
  0 siblings, 0 replies; 3+ messages in thread
From: Leon Hwang @ 2026-09-15  2:27 UTC (permalink / raw)
  To: bot+bpf-ci, bpf
  Cc: qmo, ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, linux-kernel,
	martin.lau, mason

On 15/9/26 00:39, bot+bpf-ci@kernel.org wrote:
>> diff --git a/tools/lib/bpf/skel_internal.h b/tools/lib/bpf/skel_internal.h
>> index 1f3f332dffbe..6f23ea9410eb 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>
> 
> [ ... ]
> 
>> @@ -193,25 +197,34 @@ 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);
>> +	return roundup(roundup(val_sz, 8) * max_entries, sysconf(_SC_PAGE_SIZE));
>>  }
> 
> This isn't a bug, but would it read closer to array_map_mmap_sz() to hoist
> the page size into a local here? sysconf(_SC_PAGE_SIZE) is passed straight
> into roundup()'s second argument, and roundup() is a macro that expands its
> second argument several times in the non-constant case. The three nearby
> equivalents all hoist it: the bpf_map_mmap_sz() this patch deletes from
> gen.c used 'long page_sz = sysconf(_SC_PAGE_SIZE);', and libbpf's
> array_map_mmap_sz() and bpf_map_mmap_sz() both open with 'const long page_sz
> = sysconf(_SC_PAGE_SIZE);'. Hoisting it here would also make the helper
> independent of which roundup() definition wins, and might let the new
> <sys/param.h> include be dropped.

Good catch.

'sysconf(_SC_PAGE_SIZE)' will be calculated several times in
'roundup(roundup(val_sz, 8) * max_entries, sysconf(_SC_PAGE_SIZE))'.

printf '%s\n' \
  '#include <sys/param.h>' \
  'roundup(roundup(val_sz, 8) * max_entries, sysconf(_SC_PAGE_SIZE))' |
gcc -E -P -x c - |
tail -n 1

(__builtin_constant_p (sysconf(_SC_PAGESIZE)) &&
((((sysconf(_SC_PAGESIZE)) - 1) & (sysconf(_SC_PAGESIZE))) == 0) ?
((((__builtin_constant_p (8) && ((((8) - 1) & (8)) == 0) ? (((val_sz) +
(8) - 1) & ~((8) - 1)) : ((((val_sz) + ((8) - 1)) / (8)) * (8))) *
max_entries) + (sysconf(_SC_PAGESIZE)) - 1) & ~((sysconf(_SC_PAGESIZE))
- 1)) : (((((__builtin_constant_p (8) && ((((8) - 1) & (8)) == 0) ?
(((val_sz) + (8) - 1) & ~((8) - 1)) : ((((val_sz) + ((8) - 1)) / (8)) *
(8))) * max_entries) + ((sysconf(_SC_PAGESIZE)) - 1)) /
(sysconf(_SC_PAGESIZE))) * (sysconf(_SC_PAGESIZE))))

Will hoist 'sysconf(_SC_PAGE_SIZE);' and 'roundup(val_sz, 8) * max_entries'.

However, the new <sys/param.h> is needed for roundup().

Thanks,
Leon

> [...]


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

end of thread, other threads:[~2026-09-15  2:27 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-14 15:33 [PATCH bpf-next v2] bpftool: Compute map size of light skeletons at runtime Leon Hwang
2026-09-14 16:39 ` bot+bpf-ci
2026-09-15  2:27   ` Leon Hwang

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®