MSM 抢占

抢占允许 Adreno GPU 在有工作被推送时切换到更高优先级的环,从而减少高优先级提交的延迟。

启用抢占时,将初始化 4 个环,对应不同的优先级。拥有多个环纯粹是一个软件概念,因为 GPU 只有寄存器来跟踪一个图形环。内核能够通过请求抢占来切换当前正在处理的环。当满足特定条件时(取决于优先级),GPU 会将当前状态保存到一系列缓冲区中,然后从内核指定的一组类似缓冲区中恢复状态。之后,它会恢复执行并触发中断 (IRQ),通知内核上下文切换已完成。

内核可以使用此机制在环之间进行切换。每当发生提交时,内核会找到非空的最高优先级环,如果该环不是当前正在执行的环,则会抢占到该环。当高优先级环完成时,每次提交完成后也会执行此操作,以确保执行在较低优先级环上恢复。

抢占级别

抢占只能在特定边界发生。通过更改抢占级别可以配置精确条件,这允许在延迟(即内核请求抢占与 SQE 开始保存状态之间的时间)和开销(需要保存的状态量)之间进行权衡。

GPU 提供 3 个级别

级别 0

抢占只发生在提交级别。这需要保存最少量的状态,因为用户空间提交的 IB(指令缓冲区)的执行从未中断,但与不启用任何类型的抢占相比,其好处微乎其微。

级别 1

如果使用 GMEM 渲染,抢占发生在 bin 级别;如果使用 sysmem 渲染,则发生在 draw 级别。

级别 2

抢占发生在 draw 级别。

级别 1 是 msm 驱动程序使用的模式。

此外,GPU 允许指定一个 skip_save_restore 选项。这会禁用所有寄存器的保存和恢复,除了与 SQE 自身操作相关的寄存器,从而减少开销。仅在使用 GMEM 和级别 1 抢占时跳过保存和恢复。启用此功能时,用户空间需要设置抢占发生时未保留的状态,这通过指定前导码 (preamble) 和后导码 (postambles) 来完成。这些是在抢占前后执行的 IB。

抢占缓冲区

需要一系列缓冲区来存储环在未执行时的状态。有不同种类的抢占记录,其中大多数每个环需要一个缓冲区。这是因为抢占绝不会发生在同一环上的提交之间,当环活跃时,提交总是按顺序运行。这意味着每个环只有一个上下文是有效活跃的。

SMMU_INFO

此缓冲区包含有关当前 SMMU 配置的信息,例如 ttbr0 寄存器。SQE 固件实际上无法保存此记录。因此,在触发抢占之前,必须手动将 SMMU 信息从 CP 保存到缓冲区,并使用该缓冲区中的信息更新 SMMU 记录。

NON_SECURE

这是主要的抢占记录,大多数状态都保存在这里。除了必须由内核初始化的前几个字之外,它对内核来说大部分是不透明的。

SECURE

这保存了与 GPU 安全模式相关的状态。

NON_PRIV

此记录的预期用途未知。SQE 固件实际上忽略了它,因此 msm 不会处理它。

COUNTER

此记录用于保存和恢复性能计数器。

处理这些缓冲区的权限对于安全性至关重要。除了 NON_PRIV 记录外,所有记录都必须对用户空间不可访问,因此它们必须使用 MSM_BO_MAP_PRIV 标志映射到内核地址空间中。例如,如果允许从用户空间访问 NON_SECURE 记录,则任何进程都可以操纵已保存环的 RPTR,这可用于跳过环中某些数据包的执行并以更高权限执行用户命令。