给 XDP 新增了一个 tracepoint;历时一个月,终不负所望。

年份:2023 年。

需求来源

说来惭愧,需求不来自公司、也不来自我自己,而是来自 Cilium 社区的一个讨论:

有人在使用 go-ebpf 库时,遇到了一个问题:当 MTU 设置为 9000 时,调用 AttachXDP() 会返回 invalid argument 错误;但却没有更详细的错误信息。

最后,在 7 月 3 日,我回复:“我觉得像 bpf verifier 一样报告错误信息会比较好”。

接着,在 7 月 4 日,我便完成了 POC,将错误信息通过 BPF 系统调用传递给用户空间。

提交第一个 patch

这是第一次提交正式的 patch,有许多社区行为需要注意;幸好有同事的帮助,才没有犯大错。

在 patch 的基本要求没问题后,厚着脸皮,在 7 月 5 日向 kernel bpf 社区发出了第一个 patch。

幸好,该主意得到了社区大佬 Daniel Borkmann 的肯定,并指出了我的 patch 中的一些问题:

  1. 破坏了 UAPI。
  2. 使用 bpf_log_once() 封装一下啰嗦的代码。

提交第二个 patch

根据 Daniel 的建议,我修正了 UAPI 的问题,并使用 bpf_log_once() 封装了代码。

并在 7 月 8 日提交了第二个 patch。

不过,该 patch 存在不少问题,被社区大佬 Alexei Starovoitov 指出:

  1. 缺失 libbpf 的更新。
  2. 缺失 selftest。
  3. 使用 bpf_vlog_finalize() 会更好。
  4. 考虑一下 Daniel Xu 提出的 tracepoint 的主意。

提交第三个 patch

接下来,当然是接纳社区大佬的建议,改用 tracepoint 的方式,并添加 selftest。

不过,在实现 selftest 的时候,遇到了困难:如何使用 bpf trace 该 tracepoint?

因为该 tracepoint 的 ctx 如下:

1
2
3
4
5
struct xdp_attach_error_ctx {
    unsigned long unused;

    const char *msg;
};

在 bpf 里,是无法直接通过 ctx->msg 来获取字符串的。

请教了社区大佬 Daniel Xu 后,得到具体的解决办法,请看:eBPF Talk: tracepoint __data_loc

而后,在完成了基本的 selftest 后,便在 7 月 20 日提交了第三个 patch。

为什么该 patch 被拒绝呢?因为该 patch 的 selftest 在某些情况下 crashed 了:kernel-patches/bpf CI

其实,我在提交 patch 之前就知道这个问题,但我罔顾问题就直接提交 patch 了,被狠狠地打脸。

提交第四个 patch

只能硬着头皮,修复 selftest 的问题。

此时,VirtualBox VM 不给力了,我只能使用 QEMU VM 来进行测试。

但没有学会使用 QEMU 通过 bzImage 来创建 VM,只能直接编译内核了。

给 QEMU VM 开 6 个 CPU 来编译内核,编译了 1 个小时。

而后,使用 gdb 调试 selftest 程序,发现是在 perf event callback 里复制字符串时复制多了 4 个字节,导致栈上的变量被破坏了。

修复了这个问题后,便在 7 月 30 日提交了第四个 patch。

提交第五个 patch

在 8 月 1 日,kernel net 社区大佬 Jakub Kicinski 指出 extack 变量没初始化,而且变量声明的代码行存在风格问题,这些行需要从长到短排列。

还好,这些问题都很容易修复,便在 8 月 1 日晚上提交了第五个 patch。

终于,在 8 月 3 日迎来了好消息:

1
2
3
4
Hello:

This series was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:

小结

这是我第二次向 kernel bpf 社区提交 patch,历时一个月,终于达成了目标。第一次仅是修复了一个单词拼写问题,就略过吧。

向 kernel 社区提交 patch 时,有不少地方需要注意的:

即便如此,最好还是在有经验的同事的 review 下,再将 patch 提交到社区。

作为新人,厚着脸皮混个脸熟;但也要快速学习社区的行为约定,以免冒犯了社区大佬们。

祝好!