PMU 基于事件的分支¶
基于事件的分支 (EBB) 是一项允许硬件在发生某些事件时直接跳转到指定的 用户空间地址的功能。
完整规范可在 Power ISA v2.07 中查阅
可以为 EBB 配置的一种事件是 PMU 异常。本文档介绍了使用 Linux perf_events API 配置 Power PMU 以生成 EBB 的 API。
术语¶
在本文档中,我们将使用“EBB 事件”这一称呼。这仅指在其 attr.config 中设置了“EBB”标志的 struct perf_event。所有可以在硬件 PMU 上配置的事件都有可能是“EBB 事件”。
背景¶
当发生 PMU EBB 时,它会被传递给当前正在运行的进程。因此,EBB 只能由用于自我监控的程序合理使用。
perf_events API 的一个特性是可以在其他进程上创建事件(受标准权限检查的约束)。EBB 事件也是如此,但除非目标进程启用了 EBB(通过 mtspr(BESCR)),否则永远不会传递任何 EBB。
这使得进程可以为自己启用 EBB,但实际上并不配置任何事件。随后,另一个进程可以向该进程附加一个 EBB 事件,从而导致 EBB 被传递给第一个进程。目前尚不清楚这是否有实际用处。
当为 EBB 配置 PMU 时,所有 PMU 中断都会传递给用户进程。这意味着一旦在 PMU 上调度了 EBB 事件,就无法配置非 EBB 事件。这意味着 EBB 事件不能与常规的“perf”命令或任何其他 perf 事件并发运行。
不过,在一个正在使用 EBB 的进程上运行“perf”命令是安全的。通常内核会调度 EBB 事件,并且 perf 会收到通知,告知其事件无法运行。
EBB 事件与常规事件之间的排他性是通过 perf_events 现有的“pinned”(锁定)和“exclusive”(排他)属性实现的。这意味着 EBB 事件将优先于其他事件,除非其他事件也被锁定。如果 EBB 事件和常规事件都被锁定,那么首先启用的事件将被调度,另一个事件将进入错误状态。有关更多信息,请参阅下文“启用 EBB 事件”一节。
创建 EBB 事件¶
要请求使用 EBB 计数的事件,事件代码必须设置第 63 位。
必须使用一组特定且限制严格的属性来创建 EBB 事件——这样它们才能与 perf_events 子系统的其余部分正确互操作。
必须在设置了“pinned”和“exclusive”属性的情况下创建 EBB 事件。请注意,如果您正在创建一组 EBB 事件,则只有领导者(leader)可以设置这些属性。
EBB 事件绝不能设置“inherit”、“sample_period”、“freq”或“enable_on_exec”属性中的任何一个。
EBB 事件必须附加到一个任务上。这可以通过向 perf_event_open() 传递一个 pid 值来指定,通常为 0,表示当前任务。
组中的所有事件必须就它们是否需要 EBB 达成一致。也就是说,要么所有事件都请求 EBB,要么所有事件都不请求 EBB。
EBB 事件必须指定在其上进行计数的 PMC。这确保了用户空间能够可靠地确定事件被调度到了哪个 PMC 上。
启用 EBB 事件¶
成功打开 EBB 事件后,必须使用 perf_events API 启用它。这可以通过 ioctl() 接口或 prctl() 接口来实现。
然而,由于 perf_events API 的设计,启用事件并不能保证它已被调度到 PMU 上。为确保 EBB 事件已调度到 PMU 上,必须对该事件执行 read() 操作。如果 read() 返回 EOF,则说明事件尚未调度,且 EBB 未启用。
此行为发生的原因是 EBB 事件是 pinned 和 exclusive 的。当启用 EBB 事件时,它会强制所有其他未锁定的事件退出 PMU。在这种情况下,启用将成功。但是,如果 PMU 上已经锁定了一个事件,则启用将不成功。
读取 EBB 事件¶
可以对 EBB 事件进行 read() 操作。但结果毫无意义。由于中断正在传递给用户进程,内核无法对事件进行计数,因此会返回垃圾值。
关闭 EBB 事件¶
当 EBB 事件使用完毕后,你可以像对待任何常规事件一样使用 close() 将其关闭。如果这是最后一个 EBB 事件,PMU 将被解除配置,并且不会再传递任何 PMU EBB。
EBB 处理程序¶
EBB 处理程序只是常规的用户空间代码,但它必须以中断处理程序的风格编写。进入处理程序时,所有寄存器可能都处于活动(live)状态,因此在处理程序调用其他代码之前,必须以某种方式保存它们。
程序如何处理这一点由其自身决定。对于 C 程序来说,一个相对简单的选择是在栈上创建一个中断帧,并将寄存器保存在那里。
Fork¶
EBB 事件不会在 fork 过程中被继承。如果子进程希望使用 EBB,它应该为自己打开一个新的事件。类似地,BESCR/EBBHR/EBBRR 中的 EBB 状态在 fork() 时会被清除。