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

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>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Quentin Monnet <qmo@kernel.org>
Link: https://lore.kernel.org/bpf/20260915161450.96249-1-leon.hwang@linux.dev
This commit is contained in:
Leon Hwang authored and Andrii Nakryiko committed 2026-10-02 05:41:50 -07:00
1 parent acc90a4bb1
commit b68bed3090
1 file changed
+34 -14
+34 -14
View File
@@ -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 @@ out:
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;
}