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