In this lesson, you will learn how to maintain data integrity when multiple CPU cores attempt to update shared metrics in BPF maps simultaneously.
When you run BPF programs, they often execute in parallel across all available CPU cores. If your program aggregates metrics (like counting packets or tracking latency) using a shared map, you face a race condition: two CPUs might read the same value, increment it, and write it back, causing "lost updates."
The Per-CPU Pattern
The most efficient way to handle this in BPF is to avoid cross-CPU contention entirely by using BPF_MAP_TYPE_PERCPU_HASH or BPF_MAP_TYPE_PERCPU_ARRAY. In these maps, each CPU gets its own private instance of the data. Since no two CPUs write to the same memory address, you eliminate the need for locking or atomic operations. You aggregate the total only in user-space when reading the map.
Atomic Operations
Sometimes, you cannot use per-CPU maps, such as when you need a globally consistent counter that every CPU must read and update synchronously. For these cases, we use BPF atomic built-ins, which rely on the underlying CPU architecture to ensure the operation completes as a single, uninterruptible transaction.
// Example: Atomically incrementing a global counter
u64 *value = bpf_map_lookup_elem(&my_global_map, &key);
if (value) {
// __sync_fetch_and_add ensures the operation is atomic
__sync_fetch_and_add(value, 1);
}
When to use Locking
eBPF introduced bpf_spin_lock to handle more complex data structures (like maps inside maps). By defining a struct bpf_spin_lock inside your map value structure, you can serialize access to that specific entry. Use this sparingly, as holding a lock blocks other CPUs from accessing that map entry, which can lead to significant performance degradation or helper function errors if the lock is held for too long.
Always prefer per-CPU structures for high-frequency observability tasks to keep your tracing overhead low and your concurrency model clean.
Key Takeaway: Prioritize per-CPU maps to eliminate contention; use atomic operations or spinlocks only when global state consistency is strictly required.