BPF_MAP_TYPE_BLOOM_FILTER

注意

  • BPF_MAP_TYPE_BLOOM_FILTER 是在内核版本 5.16 中引入的

BPF_MAP_TYPE_BLOOM_FILTER 提供了一个 BPF Bloom 过滤器映射。Bloom 过滤器是一种空间效率高的概率数据结构,用于快速测试元素是否存在于集合中。在 Bloom 过滤器中,可能会出现假阳性,但不会出现假阴性。

Bloom 过滤器映射没有键,只有值。创建 Bloom 过滤器映射时,必须使用 key_size 为 0 创建。Bloom 过滤器映射支持两个操作

  • push:将元素添加到映射

  • peek:确定元素是否存在于映射中

BPF 程序必须使用 bpf_map_push_elem 将元素添加到 Bloom 过滤器映射,并使用 bpf_map_peek_elem 查询映射。这些操作通过以下方式使用现有的 bpf 系统调用暴露给用户空间应用程序

  • BPF_MAP_UPDATE_ELEM -> push

  • BPF_MAP_LOOKUP_ELEM -> peek

在创建映射时指定的 max_entries 大小用于近似 Bloom 过滤器的合理位图大小,否则不会严格强制执行。如果用户希望在 Bloom 过滤器中插入比 max_entries 更多的条目,这可能会导致更高的假阳性率。

用于 Bloom 过滤器的哈希数可以使用创建映射时 union bpf_attrmap_extra 的低 4 位进行配置。如果未指定数字,则默认使用 5 个哈希函数。通常,使用更多哈希会降低假阳性率和查找速度。

无法从 Bloom 过滤器映射中删除元素。Bloom 过滤器映射可以用作内部映射。用户负责同步并发更新和查找,以确保不会发生假阴性查找。

用法

内核 BPF

bpf_map_push_elem()

long bpf_map_push_elem(struct bpf_map *map, const void *value, u64 flags)

可以使用 bpf_map_push_elem() 辅助函数将 value 添加到 Bloom 过滤器。flags 参数在向 Bloom 过滤器添加条目时必须设置为 BPF_ANY。此辅助函数在成功时返回 0,如果失败则返回负错误。

bpf_map_peek_elem()

long bpf_map_peek_elem(struct bpf_map *map, void *value)

bpf_map_peek_elem() 辅助函数用于确定 value 是否存在于 Bloom 过滤器映射中。如果 value 可能存在于映射中,此辅助函数返回 0,如果 value 肯定不存在于映射中,则返回 -ENOENT

用户空间

bpf_map_update_elem()

int bpf_map_update_elem (int fd, const void *key, const void *value, __u64 flags)

用户空间程序可以使用 libbpf 的 bpf_map_update_elem 函数将 value 添加到 Bloom 过滤器。key 参数必须设置为 NULLflags 必须设置为 BPF_ANY。成功时返回 0,如果失败则返回负错误。

bpf_map_lookup_elem()

int bpf_map_lookup_elem (int fd, const void *key, void *value)

用户空间程序可以使用 libbpf 的 bpf_map_lookup_elem 函数确定 Bloom 过滤器中是否存在 valuekey 参数必须设置为 NULL。如果 value 可能存在于映射中,则返回 0,如果 value 肯定不存在于映射中,则返回 -ENOENT

示例

内核 BPF

此代码段显示如何在 BPF 程序中声明 Bloom 过滤器

struct {
        __uint(type, BPF_MAP_TYPE_BLOOM_FILTER);
        __type(value, __u32);
        __uint(max_entries, 1000);
        __uint(map_extra, 3);
} bloom_filter SEC(".maps");

此代码段显示如何在 BPF 程序中确定 Bloom 过滤器中是否存在值

void *lookup(__u32 key)
{
        if (bpf_map_peek_elem(&bloom_filter, &key) == 0) {
                /* Verify not a false positive and fetch an associated
                 * value using a secondary lookup, e.g. in a hash table
                 */
                return bpf_map_lookup_elem(&hash_table, &key);
        }
        return 0;
}

用户空间

此代码段显示如何使用 libbpf 从用户空间创建 Bloom 过滤器映射

int create_bloom()
{
        LIBBPF_OPTS(bpf_map_create_opts, opts,
                    .map_extra = 3);             /* number of hashes */

        return bpf_map_create(BPF_MAP_TYPE_BLOOM_FILTER,
                              "ipv6_bloom",      /* name */
                              0,                 /* key size, must be zero */
                              sizeof(ipv6_addr), /* value size */
                              10000,             /* max entries */
                              &opts);            /* create options */
}

此代码段显示如何从用户空间向 Bloom 过滤器添加元素

int add_element(struct bpf_map *bloom_map, __u32 value)
{
        int bloom_fd = bpf_map__fd(bloom_map);
        return bpf_map_update_elem(bloom_fd, NULL, &value, BPF_ANY);
}

参考

https://lwn.net/ml/bpf/20210831225005.2762202-1-joannekoong@fb.com/