I was able to confirm from the documentation that bpf_map_update_elem is an atomic operation if done on HASH_MAPs. Source (https://man7.org/linux/man-pages/man2/bpf.2.html). [Cite: map_update_elem() replaces existing elements atomically]
My question is 2 folds.
What if the element does not exist, is the map_update_elem still atomic?
Is the XDP operation bpf_map_delete_elem thread safe from User space program?
The map is a HASH_MAP.
Atomic ops, race conditions and thread safety are sort of complex in eBPF, so I will make a broad answer since it is hard to judge from your question what your goals are.
Yes, both the
bpf_map_update_elemcommand via the syscall and the helper function update the maps 'atmomically', which in this case means that if we go from value 'A' to value 'B' that the program always sees either 'A' or 'B' not some combination of the two(first bytes of 'B' and last bytes of 'A' for example). This is true for all map types. This holds true for all map modifying syscall commands(includingbpf_map_delete_elem).This however doesn't make race conditions impossible since the value of the map may have changed between a
map_lookup_elemand the moment you update it.What is also good to keep in mind is that the
map_lookup_elemsyscall command(userspace) works differently from the helper function(kernelspace). The syscall will always return a copy of the data which isn't mutable. But the helper function will return a pointer to the location in kernel memory where the map value is stored, and you can directly update the map value this way without using themap_update_elemhelper. That is why you often see hash maps used like:Note that in this example,
__sync_fetch_and_addis used to update parts of the map value. We need to do this since updating it likevalue->packets++;orvalue->packets += 1would result in a race condition. The__sync_fetch_and_addemits a atomic CPU instruction which in this case fetches, adds and writes back all in one instruction.Also, in this example, the two struct fields are atomically updated, but not together, it is still possible for the
packetsto have incremented butbytesnot yet. If you want to avoid this you need to use a spinlock(using thebpf_spin_lockandbpf_spin_unlockhelpers).Another way to sidestep the issue entirely is to use the
_PER_CPUvariants of maps, where you trade-off congestion/speed and memory use.