最近在用 eBPF 造轮子的时候,发现其中一个处理函数;而后,自己有样学样地封装了另外一个函数。

bpf_map_lookup_or_try_init

函数名称有点长。

该函数是用来查询某个 key 对应的 value, 或者尝试去将 key 对应的 value 更新为提供的初始值,最后返回这个 key 对应的 value。

该函数适用于需要为 key 设置初始值的情况,譬如 hash 类的 map(也能够无差别地处理 array 类的 map)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
static __always_inline void *
bpf_map_lookup_or_try_init(void *map, const void *key, const void *init)
{
    void *val;
    long err;

    val = bpf_map_lookup_elem(map, key);
    if (val)
        return val;

    err = bpf_map_update_elem(map, key, init, BPF_NOEXIST);
    if (err && err != -EEXIST)
        return 0;

    return bpf_map_lookup_elem(map, key);
}

bpf_map_lookup_and_delete

该函数是用来查询某个 key 对应的 value 后,删除该 key/value,然后返回 value。

该函数就是用于“阅后即焚”的场景,譬如时间戳的处理。

为什么需要这么做呢?用户应用程序不是有 LookupAndDelete 吗?

没错,从内核 5.14 版本开始,对于 hash map,内核提供了 LOOKUP_AND_DELETE_ELEM map 处理 API;参考 BPF Features by Linux Kernel Version

可是,查看一下 bpf-helpers,里面并没有 bpf_map_lookup_and_delete_elem() 的函数。 所以,就自己封装一下 bpf_map_lookup_elem()bpf_map_delete_elem() 这两个函数吧。如下:

1
2
3
4
5
6
7
8
9
static __always_inline void *
bpf_map_lookup_and_delete(void *map, const void *key)
{
    void *val = bpf_map_lookup_elem(map, key);
    if (val)
        bpf_map_delete_elem(map, key);

    return val;
}

小结

bpf_map_lookup_or_try_init() 是从宝藏 libbpf-tools 中挖掘出来的宝贝。

libbpf-tools 宝藏有待进一步挖掘。