PMU 基于事件的分支

基于事件的分支 (EBB) 是一项功能,它允许硬件在发生某些事件时直接分支到指定的用户空间地址。

完整的规范可在 Power ISA v2.07 中找到

可以配置 EBB 的一种事件类型是 PMU 异常。本文档介绍了使用 Linux perf_events API 配置 Power PMU 生成 EBB 的 API。

术语

在本文档中,我们将提及“EBB 事件”或“EBB 事件”。 这仅指在其 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 事件将优先于其他事件,除非它们也被固定。 如果 EBB 事件和常规事件都已固定,则首先启用的事件将被调度,而另一个事件将处于错误状态。 有关更多信息,请参见下面标题为“启用 EBB 事件”的部分。

创建 EBB 事件

要请求使用 EBB 对事件进行计数,事件代码应将第 63 位设置为 1。

必须使用一组特定的和限制性的属性来创建 EBB 事件 - 这是为了使它们与 perf_events 子系统的其余部分正确地互操作。

必须使用设置的 “pinned” 和 “exclusive” 属性来创建 EBB 事件。 请注意,如果要创建一组 EBB 事件,则只有领导者可以设置这些属性。

EBB 事件不得设置任何 “inherit”、“sample_period”、“freq” 或 “enable_on_exec” 属性。

EBB 事件必须附加到任务。 这是通过传递 pid 值(通常为 0,表示当前任务)来指定给 perf_event_open() 的。

组中的所有事件必须就它们是否需要 EBB 达成一致。 也就是说,所有事件都必须请求 EBB,或者都不得请求 EBB。

EBB 事件必须指定要在其上进行计数的 PMC。 这可确保用户空间能够可靠地确定事件在哪个 PMC 上调度。

启用 EBB 事件

成功打开 EBB 事件后,必须使用 perf_events API 启用它。 这可以通过 ioctl() 接口或 prctl() 接口来实现。

但是,由于 perf_events API 的设计,启用事件并不能保证它已在 PMU 上调度。 要确保 EBB 事件已在 PMU 上调度,必须对该事件执行 read()。 如果 read() 返回 EOF,则表示该事件尚未调度,并且未启用 EBB。

出现此行为的原因是 EBB 事件被固定且是独占的。 启用 EBB 事件时,它将强制所有其他未固定的事件退出 PMU。 在这种情况下,启用将成功。 但是,如果 PMU 上已有一个固定的事件,则启用将不会成功。

读取 EBB 事件

可以从 EBB 事件中 read()。 但是,结果毫无意义。 由于中断被传递到用户进程,因此内核无法计算该事件,因此将返回一个垃圾值。

关闭 EBB 事件

完成 EBB 事件后,可以像关闭任何常规事件一样使用 close() 关闭它。 如果这是最后一个 EBB 事件,则 PMU 将取消配置,并且不会再传递 PMU EBB。

EBB 处理程序

EBB 处理程序只是常规的用户空间代码,但是必须以中断处理程序的样式编写。 当进入处理程序时,所有寄存器都处于活动状态(可能),因此必须在处理程序调用其他代码之前以某种方式保存它们。

如何处理取决于程序。 对于 C 程序,一个相对简单的选择是在堆栈上创建一个中断帧并在其中保存寄存器。

Fork

EBB 事件不会在 fork 中继承。 如果子进程希望使用 EBB,它应该为自己打开一个新的事件。 类似地,BESCR/EBBHR/EBBRR 中的 EBB 状态在 fork() 中被清除。