How Tech - Systems Programming

How Tech - Systems Programming

Implementing Custom Metrics Gathering Using BPF Ring Buffers

Jun 27, 2026
∙ Paid

Before BPF_MAP_TYPE_RINGBUF, you streamed events from kernel to userspace through perf_event_array — one ring buffer per CPU, each consuming its full allocation whether that CPU was idle or saturated. On a 96-core machine with 4MB per-CPU buffers, that is 384MB of pinned kernel memory before your tracer logs a single event. Worse, events from different CPUs arrived out of timestamp order, so any analysis requiring causality needed a reorder pass in userspace. And if your userspace consumer stalled for even a millisecond, a busy CPU could silently overwrite the oldest records with no indication that data was lost.

BPF_MAP_TYPE_RINGBUF landed in Linux 5.8. One shared buffer, all CPUs, in-order delivery by design, and a proper drop counter you can actually observe.


The Memory Layout

The kernel-side bpf_ringbuf object has three layers: a consumer meta-page, a producer meta-page, and a power-of-2 array of data pages. Each meta-page holds a single 64-bit position counter — that is the entirety of the synchronization state. No spinlocks. No mutexes protecting the positions. Just two counters and an atomic compare-and-swap.

When you create the map, the kernel mmaps this structure into a file descriptor you can poll. The consumer page is mapped read-only; your userspace process reads its own position directly from the shared mapping without any syscall. The data pages are mapped twice in the virtual address space — a classic ring buffer trick that lets ring_buffer__poll() read events spanning the physical wrap boundary without copying. The only kernel entry on the hot path is the initial epoll_wait.


User's avatar

Continue reading this post for free, courtesy of Systems.

Or purchase a paid subscription.
© 2026 Sumedh S · Privacy ∙ Terms ∙ Collection notice
Start your SubstackGet the app
Substack is the home for great culture