On Mon, Jun 22, 2026 at 7:37 AM Leon Hwang <[email protected]> wrote:
>
> Add support for global percpu data in libbpf by adding a new ".percpu"
> section, similar to ".data". It enables efficient handling of percpu
> global variables in bpf programs.
>
> When generating loader for lightweight skeleton, update the percpu_array
> map used for global percpu data using BPF_F_ALL_CPUS, in order to update
> values across all CPUs using one value slot.
>
> Unlike global data, the mmaped data for global percpu data will be marked
> as read-only after populating the percpu_array map. Thereafter, users can
> read those initialized percpu data after loading prog. If they want to
> update the percpu data after loading prog, they have to update the
> percpu_array map using key=0 instead.
>
> Signed-off-by: Leon Hwang <[email protected]>
> ---
> tools/lib/bpf/bpf_gen_internal.h | 3 +-
> tools/lib/bpf/gen_loader.c | 3 +-
> tools/lib/bpf/libbpf.c | 68 ++++++++++++++++++++++++++------
> 3 files changed, 60 insertions(+), 14 deletions(-)
>
[...]
> @@ -5353,6 +5387,13 @@ bpf_object__populate_internal_map(struct bpf_object
> *obj, struct bpf_map *map)
> return err;
> }
> map->mmaped = mmaped;
> + } else if (is_percpu) {
> + if (mprotect(map->mmaped, mmap_sz, PROT_READ)) {
> + err = -errno;
> + pr_warn("map '%s': failed to mprotect() contents:
> %s\n",
> + bpf_map__name(map), errstr(err));
> + return err;
> + }
hm... do we need to do this? what happens for LIBBPF_MAP_KCONFIG, do
we just null out the initial image? should we just do that here?
Basically what I am asking is how important it is to access initial
per-CPU image after load? What's the realistic use case for that?
> } else if (map->mmaped) {
> munmap(map->mmaped, mmap_sz);
> map->mmaped = NULL;
> @@ -10806,16 +10847,19 @@ int bpf_map__fd(const struct bpf_map *map)
>
> static bool map_uses_real_name(const struct bpf_map *map)
> {
> - /* Since libbpf started to support custom .data.* and .rodata.* maps,
> - * their user-visible name differs from kernel-visible name. Users see
> - * such map's corresponding ELF section name as a map name.
> - * This check distinguishes .data/.rodata from .data.* and .rodata.*
> - * maps to know which name has to be returned to the user.
> + /*
> + * Since libbpf started to support custom .data.*, .rodata.* and
> + * .percpu.* maps, their user-visible name differs from
> + * kernel-visible name. Users see such map's corresponding ELF section
> + * name as a map name. This check distinguishes plain
> .data/.rodata/.percpu
> + * from .data.*, .rodata.* and .percpu.* to choose which name to
> return.
> */
> if (map->libbpf_type == LIBBPF_MAP_DATA && strcmp(map->real_name,
> DATA_SEC) != 0)
> return true;
> if (map->libbpf_type == LIBBPF_MAP_RODATA && strcmp(map->real_name,
> RODATA_SEC) != 0)
> return true;
> + if (map->libbpf_type == LIBBPF_MAP_PERCPU && strcmp(map->real_name,
> PERCPU_SEC) != 0)
> + return true;
um... this is extra logic for DATA/RODATA is supposed to be backwards
compatible legacy stuff. We shouldn't need this for PERCPU_SEC. It
should actually be called just ".percpu", not "<object_name>.percpu".
> return false;
> }
>
> --
> 2.54.0
>