fsession 是我跟 Menglong 大佬合作的杰作,已合入 bpf 社区,将在 v7.0 合入 Linux 主线。

目前,fsession 已支持 x86_64 和 arm64 架构。

TL;DR fsession 指的是一个新的 tracing attach type: BPF_TRACE_FSESSION,提供了 fentry + fexit 的能力;并提供 bpf_session_is_return() kfunc 用来区分 entry/exit。 同时,提供 bpf_session_cookie() kfunc 用来 entry -> exit 传递信息。

用法

类似于 fentry/fexit,直接使用 SEC("fsession") 即可:

1
2
3
4
5
6
7
8
9
SEC("fsession/icmp_rcv")
int BPF_PROG(fsession__icmp_rcv, struct sk_buff *skb)
{
    if (bpf_session_is_return(ctx))
        bpf_printk("fsession exit.\n");
    else
        bpf_printk("fsession entry.\n");
    return BPF_OK;
}

类似于 fentry + fexit,该 bpf prog 在 icmp_rcv 运行前后各运行一次。

bpf_session_cookie() 用法如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
SEC("fsession/icmp_rcv")
int BPF_PROG(fsession__icmp_rcv, struct sk_buff *skb)
{
    u64 *cookie = bpf_session_cookie(ctx);

    if (!bpf_session_is_return(ctx))
        *cookie = 0xDEADBEEF;
    else
        bpf_printk("fsession cookie: 0x%llx\n", *cookie);
    return BPF_OK;
}

实现

底层使用的是 BPF 版本的 trampoline:

不过,fsession 没有直接在 trampoline 里新增自己的实现代码,而是将 fsession 拆分成 fentry + fexit 这样的 2 个 trampoline link,从而复用 trampolinefentryfexit 的实现逻辑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// kernel/bpf/trampoline.c

static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link,
                                      struct bpf_trampoline *tr,
                                      struct bpf_prog *tgt_prog)
{
    // ...
    if (kind == BPF_TRAMP_FSESSION) {
        prog_list = &tr->progs_hlist[BPF_TRAMP_FENTRY];
        cnt++;
    } else {
        prog_list = &tr->progs_hlist[kind];
    }
    // ...

    hlist_add_head(&link->tramp_hlist, prog_list);
    if (kind == BPF_TRAMP_FSESSION) {
        tr->progs_cnt[BPF_TRAMP_FENTRY]++;
        fslink = container_of(link, struct bpf_fsession_link, link.link);
        hlist_add_head(&fslink->fexit.tramp_hlist, &tr->progs_hlist[BPF_TRAMP_FEXIT]);
        tr->progs_cnt[BPF_TRAMP_FEXIT]++;
    } else {
        tr->progs_cnt[kind]++;
    }
    err = bpf_trampoline_update(tr, true /* lock_direct_mutex */);
    // ...
    return err;
}

题外

在 Linux v7.0 合并窗口前,我给 arm64 新增 fsession 支持的 patch 合入了 bpf 社区; 该 patch 带上了 fsession 的第一个 bug fix: Menglong 大佬在迭代 fsession 时 漏掉了对其它架构的检查。

小记

该需求来源于 bpfsnoop

bpfsnoop 里,bpfsnoop --output-fgraph 需要使用一个 bpf prog 对大量内核函数同时 进行 fentryfexit,就想着在 trampoline 里复用一个 bpf prog 对内核函数同时进行 fentry + fexit 处理;即新增一个 tracing attach type farount

在 2025 年上半年便完成了 PoC,但忙着推进 eBPF Talk: 新增 BPF_F_CPU 和 BPF_F_ALL_CPUS flagbpf: Extend BPF syscall with common attributes support,就没有精力同时将 faround 推向社区。

在 2025 年下半年,跟 Menglong 大佬讨论时,一拍即合:由 Menglong 大佬基于 faround PoC 代码实现 fsession 并推向社区。

耗时接近 3 个月,历经 13 个版本,fsession 终于合入了 bpf 社区。

小结

当需要对内核函数、bpf prog 既要 fentry 也要 fexit 时,推荐使用 fsession,可以节省 一个 bpf prog + 一个 bpf link。

fsession 进阶用法,请参考 bpfsnoop: Add fsession support