XDP上的线程安全操作

发布于 2025-01-28 06:18:56 字数 427 浏览 2 评论 0原文

我可以从文档中确认BPF_MAP_UPDATE_ELEM如果在hash_maps上完成,则是原子操作。来源( https://man7.org/linux/man-man-pages/ man2/bpf.2.html )。 [cite:map_update_elem()在原子上替代了现有元素]

我的问题是2倍。

  1. 如果元素不存在,MAP_UPDATE_ELEM仍然是原子吗?

  2. XDP操作BPF_MAP_DELETE_ELEM线程免受用户空间程序的安全?

该地图是hash_map。

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.

  1. What if the element does not exist, is the map_update_elem still atomic?

  2. Is the XDP operation bpf_map_delete_elem thread safe from User space program?

The map is a HASH_MAP.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

心意如水 2025-02-04 06:18:56

Atomic OPS,种族条件和线程安全在EBPF中很复杂,因此我会做出广泛的答案,因为很难从您的问题中判断您的目标是什么。

是的,BPF_MAP_UPDATE_ELEM通过SYSCALL和辅助函数命令和辅助函数更新地图“ atmomomylines thimomomomomy”',在这种情况下,如果我们从value a'a'tore'tore'tore'b't'tore'b',请始终看到“ a”或“ b”不是两个组合(例如,“ b”的第一个字节和“ a”的最后一个字节)。所有地图类型都是如此。对于所有映射修改SYSCALL命令(包括BPF_MAP_DELETE_ELEM),这是正确的。

但是,这并不能使比赛条件变得不可能,因为地图的值可能在map_lookup_elem和更新时发生了变化。

请记住的是,map_lookup_elem syscall命令(用户空间)的工作方式与助手函数(kernelspace)的作用不同。 SYSCALL将始终返回不可变形的数据副本。但是,辅助功能将返回一个指针,转换为存储地图值的内核内存中的位置,您可以以这种方式直接更新地图值,而无需使用map_update_elem helper。这就是为什么您经常看到使用的哈希地图,例如:

value = bpf_map_lookup_elem(&hash_map, &key);
if (value) {
    __sync_fetch_and_add(&value->packets, 1);
    __sync_fetch_and_add(&value->bytes, skb->len);
} else {
    struct pair val = {1, skb->len};

    bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
}

请注意,在此示例__ sync_fetch_and_add用于更新地图值的部分。我们需要执行此操作,因为更新它,例如value-> packet ++;value-> packet+= 1将导致竞赛条件。 __ sync_fetch_and_add发出原子CPU指令,在这种情况下,该指令获取,添加和写回一个指令。

同样,在此示例中,两个结构字段已在原子上进行了更新,但不是在一起,数据包仍然可以增加,但是bytes尚未。如果要避免这种情况,则需要使用Spinlock(使用bpf_spin_lockbpf_spin_unlock helpers )。

完全避开问题的另一种方法是使用_PER_CPU地图的变体,在此您可以权衡交通拥堵/速度和内存使用。

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_elem command 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(including bpf_map_delete_elem).

This however doesn't make race conditions impossible since the value of the map may have changed between a map_lookup_elem and the moment you update it.

What is also good to keep in mind is that the map_lookup_elem syscall 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 the map_update_elem helper. That is why you often see hash maps used like:

value = bpf_map_lookup_elem(&hash_map, &key);
if (value) {
    __sync_fetch_and_add(&value->packets, 1);
    __sync_fetch_and_add(&value->bytes, skb->len);
} else {
    struct pair val = {1, skb->len};

    bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
}

Note that in this example, __sync_fetch_and_add is used to update parts of the map value. We need to do this since updating it like value->packets++; or value->packets += 1 would result in a race condition. The __sync_fetch_and_add emits 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 packets to have incremented but bytes not yet. If you want to avoid this you need to use a spinlock(using the bpf_spin_lock and bpf_spin_unlock helpers).

Another way to sidestep the issue entirely is to use the _PER_CPU variants of maps, where you trade-off congestion/speed and memory use.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文