关注了就能看到更多这么棒的文章哦~
Relief for insomniac tracepoints
By Jonathan Corbet
October 29, 2020
DeepL assisted translation
https://lwn.net/Articles/835426/
内核的 tracing 设施,设计时就着重考虑要能快速执行,尽可能减少对系统正常运行的干扰。这实际上提出了一个要求:当一个 tracepoint 被触发时,运行的代码不能睡眠(sleep)。否则,执行 tracepoint 这个动作可能会给内核本来在做的事情引入一个不确定时长的延迟。不过有时候,人们觉得在 tracepoint 中睡眠还是挺有用处的,哪怕它会带来延迟。来自 Michael Jeanson 的 sleepable tracepoints patch set 就为(某些)tracepoint 处理程序在执行任务时提供了可以进入 sleep 的功能。但是目前这项工作尚未完成。
在内核中,tracing 机制不需要睡眠,因为它所做的事情主要是把跟此 tracepoint 相关的数据打包,并放到 ring buffer 里面,以备后续传送到用户空间。这个工作的过程不需要等待任何外部事件。因此,需要 tracepoint 可以睡眠的应用场景一定是来自其他地方的,尤其是来自于那些通过 user space 来挂载到这个 tracepoint 的 BPF program 中。这些 BPF program 目前仅仅是访问内核空间中的数据,不需要睡眠。不过,如果能在 tracepoint 处理程序中查看 user space 的数据的话,也是很有好处的。BPF program 试图访问 user space 数据的话,并不能保证这些数据是驻留在 RAM 中的,如果这些数据不在 RAM 就会导致 page fault。处理 page fault 所耗费的时间是不确定的,在此期间,触发 page fault 的进程必须进入睡眠状态。
当前的内核就是因为这个原因而禁止 tracepoint 处理程序访问用户空间的数据。具体来说,这意味着 tracer 分析工具不能解析从 user space 传入的指针。例如,在进入 openat2()系统调用时运行的 tracepoint 可以看到用户空间传递的 open_how 结构的指针,但无法检查这个结构里面的具体内容。
tracepoint 本身并没有需求说一定不可以睡眠,尤其是如果 kernel 触发 tracepoint 的时候并不在原子上下文中运行的话,睡眠并没有什么问题。但长期以来,BPF 子系统有自己的规则,即 BPF program 不能睡眠。不过在 5.10 内核中这种情况将发生改变,这要归功于 sleepable BPF program 的加入,从此不再有这种限制。只有某些类型的 BPF program 允许阻塞住当前的执行,在 5.10 中,追踪程序也属于这类 BPF program 之一。不过在 5.10 版本中并没有代码使用了这种功能。
Jeanson 的 patch set 就是为了后续能有人可以使用这个功能,它提供的基础架构可以支持让 sleepable BPF program 能够挂载到特定的 tracepoint 上。这种功能的实现必须很小心,因为如上所述,内核经常运行在一个不适合睡眠的环境中(原子上下文)。具体来说,sleepable BPF program 只能被挂载到位于一般来说允许睡眠的代码区域的 tracepoint 上。
我们无法自动确定某个 tracepoint 是否可以安全地睡眠,所以目前现有的 tracepoint 在没有专门修改过的情况下都不允许挂载 sleepable BPF program。tracepoint 都是通过 TRACE_EVENT()宏以及一些变体来添加到内核代码中的,感兴趣的话可以查看 include/linux/tracepoint.h 来了解这里用宏用到了多么可怕的程度。Jeanson 的 patch set 增加了一个名为 TRACE_EVENT_FN_MAYSLEEP()的新的宏,作为 TRACE_EVENT_FN()的一个变体。它定义的 tracepoint 具有相关的注册和取消注册功能。将一个现有的 tracepoint 切换到使用这个新的宏就表明可以安全地在此 tracepoint 挂载 sleepable BPF program。
这些宏里面最重要的变化是,如果一个 tracepoint 被标记为可以接受 sleepable program,那么当该 tracepoint 被触发时,被调用的 tracer 程序将在允许抢占(preemption enable)后运行。这是能够处理 page fault 的必要前提,但它也改变了这些 tracer 作者的预期行为。这些 tracer 本身需要修改,从而在启用抢占的情况下可以安全地运行。这部分工作尚未发布出来。patch set 中暂时针对这种情况的处理是对 ftrace、perf 和 BPF tracer 进行修改,明确禁止抢占,从而避免意外情况出现。
如上所述,这个需求主要来自想要分析从用户空间传递给系统调用的指针内容。因此,这个能力的第一个应用场景不出意料也就落在了系统调用跟踪这里。Jeanson 的 patch set 将系统调用的进入和退出这里的 tracepoint 改为了使用 TRACE_EVENT_FN_MAYSLEEP(),从而可以允许挂载 sleepable BPF program,这些 program 就在被系统调用所触发的时候就可以在用户空间内存中翻来覆去地进行检查了。
目前只缺一个环节了,也就是真正完成对那些 tracer 程序的修改,并使用这个新功能来挂载并运行 sleepable BPF program。正如 patch set 的首页介绍所说:
这组 patch 只实现了允许 trace 来处理 page fault 所需的 tracepoint 基础架构。至于修改每个 tracer 来处理这些 page fault,将是我们对这种做法达成一致之后的下一步动作了。
这可能看起比较奇怪,因为这个功能尚且不能正常工作起来,而 patch set 仅仅做到这一步就暂停了下来。但是,对这种改法的讨论和修改很可能会对后续的 patch 产生重大影响。
根据到目前为止的讨论,似乎没有对这组 patch 进行大改动的需要。大部分的评论都是跟一些边边角角的细节有关。如果这种情况持续下去,我们可以预期不久的将来能看到完整实现这个功能,支持挂载 sleepable tracepoint program。这很可能会给 Linux tracing infrastructure 再次带来能力的提升。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~