tailcall 是 v4.2 内核便已引入的一个特性,它允许一个 BPF 程序调用另一个程序,而无需返回原程序。

然而,在 BPF 子系统不断地引入新特性的同时,tailcall 跟新特性组合使用时,可能会引发一些问题。

TL;DR 我整理出了 6 个 tailcall 问题,而且这些问题影响的内核版本范围比较大,在使用时需要注意。

1. 把 freplace 当作非法的 tailcall 目标

在 v5.6 内核引入 freplace 特性时,便一并引入了这个问题。

在 v6.2 内核里,commit 1c123c567fb1 ("bpf: Resolve fext program type when checking map compatibility") 在无意中修复了这个问题。

2. 错误的 tail_call_cnt 加载偏移

如果在 subprog 里使用 tailcall,则需要注意一下这个问题。

subprog 里使用 tailcall 的前提:bpf: allow for tailcalls in BPF subprograms for x64 JIT since v5.10 kernel。

然而,在引入 tailcall in bpf2bpf 特性时,因为代码问题从而引入了这个问题。

在 v5.19 内核里,commit ff672c67ee76 ("bpf, x86: Fix tail call count offset calculation on bpf2bpf call") 修复了这个问题。

更多细节请查看:eBPF Talk: 使用 fentry 调试 tailcall BUG

3. 由 fentry/fexit 在跟踪 subprog 时引起的 tailcall 无限循环问题

在引入 tailcall in bpf2bpf 特性时,因基于 rax 寄存器传递 tail_call_cnt 的方式被 fentry/fexit 底层的 trampoline 机制所破坏,从而引入了这个问题。

在 v6.7 内核里,commit 2b5dcb31a19a ("bpf, x64: Fix tailcall infinite loop") 修复了这个问题。

更多细节请查看:

4. tailcall 叠加 bpf2bpftailcall 层级问题

在引入 tailcall in bpf2bpf 特性时,因为 tail_call_cnt 是单向传递的,从而引入了这个问题。

在 v6.12 内核里,commit 116e04ba1459 ("bpf, x64: Fix tailcall hierarchy") 修复了这个问题。

更多细节请查看:eBPF Talk: 耗时 10 个月,修复了又一个 tailcall 的 bug

5. 将 attached freplace 程序加入到 prog_array 时内核崩溃问题

在 v6.2 内核里,commit 1c123c567fb1 ("bpf: Resolve fext program type when checking map compatibility") 引入了这个问题;因为没有考虑到 freplace 程序在 attach 之后会将 prog->aux->dst_prog 置空。

在 v6.11 内核里,commit fdad456cbcca ("bpf: Fix updating attached freplace prog in prog_array map") 修复了这个问题。

更多细节请查看:eBPF Talk: 又修了一个 tailcall 有关的 BUG

6. 由 freplace 叠加 tailcall 引起的 tailcall 无限循环问题

在 v6.2 内核里,commit 1c123c567fb1 ("bpf: Resolve fext program type when checking map compatibility") 引入了这个问题;因为在 freplace 程序执行的时候,tail_call_cnt 会被置零。

在 v6.13 内核里,commit d6083f040d5d ("bpf: Prevent tailcall infinite loop caused by freplace") 修复了这个问题。

tailcall 问题检测

根据内核的行为特征,可以识别出当前内核有没有这些 tailcall 问题、这些问题有没有被修复了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# uname -r
6.8.0-35-generic

# ./tailcall-issues
detection results:

issue:  invalid tailcallee
state:  fixed

issue:  invalid loading offset of tail_call_cnt for bpf2bpf
state:  fixed

issue:  tailcall infinite loop caused by trampoline
state:  fixed

issue:  tailcall hierarchy
state:  not fixed

issue:  panic caused by updating attached freplace prog to prog array
state:  cannot detect

issue:  tailcall infinite loop caused by freplace
state:  not fixed

tailcall-issues 源代码:tailcall issues

总结

tailcall 是一个非常强大的特性,但在使用时需要注意一些问题。这些问题的修复状态可以通过检测工具 tailcall-issues 来查看。