Search code examples
system-callsmmapebpfbcc-bpflibbpf

Reading from an eBPF map without paying for kernel-call


Is there a way to read from eBPF map without paying for overhead that kernel-calls read / ppoll add (due to them being kernel-calls). Maybe mmap with some weird parameters passed?


Solution

  • Yes, since kernel v5.5 [link] it is possible to mmap BPF_MAP_TYPE_ARRAY maps. This feature is mainly intended for global data which lives in array maps. Libbpf automatically mmaps global data into the current process, and uses this mapping when writing via bpf_map__set_initial_value. You can also get access to the whole region via bpf_map__initial_value.

    Beware that the kernel may impose some restrictions. Initially you were not allowed to have spinlocks or timers in the array map value for example. This restrictions seems to have been lifted somewhere between v5.5 and v6.10, so keep in mind to test on a variety of kernel versions before relying on this feature of anything other than global data.

    A newer feature, introduced in v6.9 [link] called "BPF arena"s that is a more generically useful version of this. In essence you create a new map of type BPF_MAP_TYPE_ARENA, userspace can mmap it into memory allowing for a region of memory that can be accessed by both userspace and BPF programs.

    A major difference is that an array map is pre-allocated and arenas are dynamically allocated. So when you create the map, no memory is reserved and every time you access a new page of memory via userspace that page gets allocated. From the BPF side you need to use a special kfunc( bpf_arena_alloc_pages) to allocate a page before you use it. The memory can also be freed by by using the munmap syscall or the bpf_arena_free_pages kfunc.

    Using this feature requires a recent kernel, clang and libbpf version. I recommend reading the linked patch set for more details.