【问题标题】:who creates map in BPF谁在 BPF 中创建地图
【发布时间】:2018-06-12 12:35:39
【问题描述】:

在阅读man bpf 和其他一些文档来源后,我的印象是map 只能由用户进程创建。然而,以下小程序似乎神奇地创建了bpf 地图:

struct bpf_map_def SEC("maps") my_map = {
        .type = BPF_MAP_TYPE_ARRAY,
        .key_size = sizeof(u32),
        .value_size = sizeof(long),
        .max_entries = 10,
};

SEC("sockops")
int my_prog(struct bpf_sock_ops *skops)
{
   u32 key = 1;
   long *value;
   ...

   value = bpf_map_lookup_elem(&my_map, &key);
   ...
   return 1;
}

所以我使用内核的tools/bpf/bpftool 加载程序并验证程序是否已加载:

$ bpftool prog show
1: sock_ops  name my_prog  tag f3a3583cdd82ae8d
        loaded_at Jan 02/18:46  uid 0
        xlated 728B  not jited  memlock 4096B

$ bpftool map show
1: array  name my_map  flags 0x0
        key 4B  value 8B  max_entries 10  memlock 4096B

当然地图是空的。但是,从程序中删除 bpf_map_lookup_elem 会导致不会创建任何地图。

更新 我使用strace 对其进行了调试,发现在这两种情况下,即使用bpf_map_lookup_elem 并且没有它,bpftool 确实调用bpf(BPF_MAP_CREATE, ...) 并且它显然成功了。然后,如果 bpf_map_lookup_elem 被遗漏,我在bpftool map show 上搜索,bpf(BPF_MAP_GET_NEXT_ID, ..) 立即返回ENOENT,它永远不会转储地图。所以很明显有些东西没有完成地图的创建。

所以我想知道这是否是预期的行为?

谢谢。

【问题讨论】:

    标签: linux-kernel bpf ebpf


    【解决方案1】:

    我的印象是地图只能由用户进程创建。

    您完全正确 - 用户程序是调用 bpf 系统调用以加载 eBPF 程序并创建 eBPF 映射的程序。

    而你就是这样做的:

    所以我用 tools/bpf/bpftool 和 ...

    加载程序

    您的bpftool 程序是调用bpf 系统调用的用户进程,因此是创建eBPF 映射的用户进程。

    当创建 BPF 程序的用户程序退出时,不必卸载它 - bpftool 很可能使用这种机制。

    一些相关位from the man page 连接点:

    一个用户进程可以创建多个映射...并通过文件描述符访问它们。

    通常,eBPF 程序由用户进程加载,并在进程退出时自动卸载。在某些情况下......即使在加载程序的进程退出后,程序仍将继续在内核中保持活动状态。

    每个 eBPF 程序都是一组指令,可以安全运行直到完成。 ...在验证过程中,内核会增加 eBPF 程序使用的每个映射的引用计数,因此在卸载程序之前无法删除附加的映射。

    【讨论】:

    • 感谢您的评论,但它没有解释为什么将 bpf_map_lookup_elem 排除在 BPF 程序之外不会创建地图。
    【解决方案2】:

    正如antiduh 所解释的,并通过您的strace 检查确认,bpftool 是在这种情况下创建地图的用户空间程序。它从 libbpf(在tools/lib/bpf/ 下)调用函数bpf_prog_load(),最终执行系统调用。然后将程序固定在所需位置(在bpf 虚拟文件系统挂载点下),以便在 bpftool 返回时不会卸载它。地图未固定。

    关于地图的创建,魔法位也发生在 libbpf 中。当bpf_prog_load() 被调用时,libbpf 接收目标文件的名称作为参数。 bpftool 不要求加载 this 特定程序或 that 特定地图;相反,它提供了目标文件,而 libbpf 必须处理它。所以libbpf中的函数解析这个ELF对象文件,最终找到对应maps和programs的多个section。然后它会尝试加载第一个程序。

    加载此程序包括以下步骤:

    CHECK_ERR(bpf_object__create_maps(obj), err, out);
    CHECK_ERR(bpf_object__relocate(obj), err, out);
    CHECK_ERR(bpf_object__load_progs(obj), err, out);
    

    换句话说:首先创建我们在目标文件中找到的所有地图。然后执行映射重定位(即将映射索引与 eBPF 指令相关联),最后加载程序指令。

    所以关于您的问题:在这两种情况下,无论有无bpf_map_lookup_elem(),地图都是使用bpf(BPF_MAP_CREATE, ...) 系统调用创建的。之后,会发生重定位,如果需要,程序指令会适应指向新创建的地图。然后,一旦完成所有步骤并加载程序,bpftool 就会退出。 eBPF 程序应该是固定的,并且仍然加载到内核中。据我了解,如果它确实使用地图(如果使用了bpf_map_lookup_elem()),那么地图仍然被加载的程序引用,并保存在内核中。另一方面,如果程序确实使用映射,那么就没有什么可以阻止它们了,所以当bpftool持有的文件描述符被关闭时,映射被销毁,当@ 987654334@返回。

    所以最后,当bpftool 完成时,如果程序使用它,你就会在内核中加载一个映射,但如果没有程序依赖它,则没有映射。在我看来,这听起来像是预期的行为;但是,如果您使用bpftool 遇到奇怪的事情,请以一种或另一种方式进行 ping,我是该实用程序的工作人员之一。最后一个一般性观察:映射也可以固定并保留在内核中,即使没有程序使用它们,如果需要保留它们。

    【讨论】:

    • 感谢您的全面解答!我是否正确,为了完全卸载固定的 BPF 程序,umount /sys/fs/bpf/xxx 就足够了?这是否保证 bpf blob 将从内存中删除?
    • 您将 rm 固定条目和 umount eBPF 虚拟 FS。删除:取决于您的程序是否仍在使用中,我会说。如果您附加到例如 TC,然后固定程序,然后删除固定条目,则程序应该仍然存在并附加,直到您删除 TC 分类器。如果您已经分离了程序(或者如果它从未连接到任何地方,例如,如果您使用 bpftool 加载它)那么是的,删除 /sys/fs/bpf 下的条目就足够了。
    • 哦,我明白了,我猜如果程序已经附加到cgroup,我应该先用bpftool将它分离,然后删除/sys/fs/bpf/my_pin,最后删除umount /sys/fs/bpf
    • 是的,我想是的。尽管umount BPF 文件系统没有义务。请注意,分离与取消固定的顺序并不重要,只要有东西(固定或附加点)保持程序,程序就会保留,但您也可以先取消固定然后分离。
    猜你喜欢
    • 2018-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多