BPF_MAP_TYPE_CGROUP_STORAGE

BPF_MAP_TYPE_CGROUP_STORAGE 映射类型表示一种本地固定大小的存储。它仅在启用了 CONFIG_CGROUP_BPF 且附加到 cgroup 的程序中可用;这些程序通过相同的 Kconfig 提供。该存储由程序所附加到的 cgroup 标识。

该映射在 BPF 程序所附加到的 cgroup 处提供本地存储。与通用哈希表相比,它提供了更快、更简单的访问方式,通用哈希表需要执行哈希表查找,并且要求用户自行跟踪活跃的 cgroup。

本文档描述了 BPF_MAP_TYPE_CGROUP_STORAGE 映射类型的用法和语义。它的某些行为在 Linux 5.9 中有所改变,本文档将描述这些差异。

用法

该映射使用类型为 __u64 cgroup_inode_idstruct bpf_cgroup_storage_key 的键,在 linux/bpf.h 中声明。

struct bpf_cgroup_storage_key {
        __u64 cgroup_inode_id;
        __u32 attach_type;
};

cgroup_inode_id 是 cgroup 目录的 inode ID。attach_type 是程序的附加类型。

Linux 5.9 添加了对将类型 __u64 cgroup_inode_id 作为键类型的支持。当使用此键类型时,特定 cgroup 和映射的所有附加类型将共享相同的存储。否则,如果类型是 struct bpf_cgroup_storage_key,则不同附加类型的程序将被隔离并看到不同的存储。

要在程序中访问存储,请使用 bpf_get_local_storage

void *bpf_get_local_storage(void *map, u64 flags)

flags 保留供将来使用,必须为 0。

没有隐式同步。BPF_MAP_TYPE_CGROUP_STORAGE 的存储可以由跨不同 CPU 的多个程序访问,用户应自行处理同步。BPF 基础设施提供 struct bpf_spin_lock 来同步存储。请参见 tools/testing/selftests/bpf/progs/test_spin_lock.c

示例

struct bpf_cgroup_storage_key 作为键类型的用法

#include <bpf/bpf.h>

struct {
        __uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
        __type(key, struct bpf_cgroup_storage_key);
        __type(value, __u32);
} cgroup_storage SEC(".maps");

int program(struct __sk_buff *skb)
{
        __u32 *ptr = bpf_get_local_storage(&cgroup_storage, 0);
        __sync_fetch_and_add(ptr, 1);

        return 0;
}

用户空间访问上面声明的映射

#include <linux/bpf.h>
#include <linux/libbpf.h>

__u32 map_lookup(struct bpf_map *map, __u64 cgrp, enum bpf_attach_type type)
{
        struct bpf_cgroup_storage_key = {
                .cgroup_inode_id = cgrp,
                .attach_type = type,
        };
        __u32 value;
        bpf_map_lookup_elem(bpf_map__fd(map), &key, &value);
        // error checking omitted
        return value;
}

或者,只使用 __u64 cgroup_inode_id 作为键类型

#include <bpf/bpf.h>

struct {
        __uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
        __type(key, __u64);
        __type(value, __u32);
} cgroup_storage SEC(".maps");

int program(struct __sk_buff *skb)
{
        __u32 *ptr = bpf_get_local_storage(&cgroup_storage, 0);
        __sync_fetch_and_add(ptr, 1);

        return 0;
}

以及用户空间

#include <linux/bpf.h>
#include <linux/libbpf.h>

__u32 map_lookup(struct bpf_map *map, __u64 cgrp, enum bpf_attach_type type)
{
        __u32 value;
        bpf_map_lookup_elem(bpf_map__fd(map), &cgrp, &value);
        // error checking omitted
        return value;
}

语义

BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE 是此映射类型的一个变体。这种每 CPU 变体将为每个存储的每个 CPU 拥有不同的内存区域。非每 CPU 变体将为每个存储拥有相同的内存区域。

在 Linux 5.9 之前,存储的生命周期精确到每次附加,对于单个 CGROUP_STORAGE 映射,最多只能有一个程序加载并使用该映射。一个程序可以附加到多个 cgroup 或具有多个附加类型,每次附加都会创建一个全新的、清零的存储。存储在分离时释放。

在加载验证时,每种类型(每 CPU 和非每 CPU)的映射与 BPF 程序之间存在一对一关联。因此,每个映射只能由一个 BPF 程序使用,每个 BPF 程序也只能使用每种类型的一个存储映射。由于映射只能由一个 BPF 程序使用,因此不可能与其他 BPF 程序共享此 cgroup 的存储。

自 Linux 5.9 起,存储可以由多个程序共享。当程序附加到 cgroup 时,只有当映射中尚不包含该 cgroup 和附加类型对的条目时,内核才会创建新的存储,否则将重用旧存储用于新的附加。如果映射是附加类型共享的,则在比较时会简单地忽略附加类型。存储仅在映射或所附加的 cgroup 被释放时才释放。分离不会直接释放存储,但可能导致对映射的引用达到零,从而间接释放映射中的所有存储。

该映射不与任何 BPF 程序关联,因此可以实现共享。但是,BPF 程序仍然只能关联每种类型(每 CPU 和非每 CPU)的一个映射。一个 BPF 程序不能使用多个 BPF_MAP_TYPE_CGROUP_STORAGE 或多个 BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE

在所有版本中,用户空间都可以使用 struct bpf_cgroup_storage_key 中 cgroup 和附加类型对的附加参数作为 BPF 映射 API 的键,以读取或更新给定附加的存储。对于 Linux 5.9 中附加类型共享的存储,在比较时仅使用结构中的第一个值,即 cgroup inode ID,因此用户空间可以直接指定一个 __u64

存储在附加时绑定。即使程序附加到父级并在子级中触发,存储仍然属于父级。

用户空间不能在映射中创建新条目或删除现有条目。程序测试运行总是使用临时存储。