命令提交

Execs 在 DRM 驱动程序中一直相当复杂(至少在 i915 中),因为一些事情:

  • 传入一个 BO 列表,该列表被读取/写入以创建隐式同步

  • 在执行时绑定

  • 在执行时流量控制环

在 XE 中,我们通过不允许将 BO 列表传递到 exec 中,使用 dma-buf 隐式同步 uAPI,将绑定作为单独的操作,并使用 DRM 调度程序来流量控制环,从而避免了所有这些复杂性。 让我们深入了解这些。

我们可以通过强制用户在每次 exec 上使用输入/输出 fences 而不是内核跟踪 BO 的依赖关系来避免 BO 列表(例如,如果用户知道一个 exec 写入一个 BO 并在下一个 exec 中从 BO 读取,则用户有责任在两个 exec 之间传递输入/输出 fence)。

我们不允许用户在执行时触发绑定,而是有一个 VM 绑定 IOCTL,它使用与 exec 相同的输入/输出 fence 接口。 从这个意义上讲,从用户的角度来看,VM 绑定基本上与 exec 操作相同。 例如,如果 exec 依赖于 VM 绑定,则使用输入/输出 fence 接口 (struct drm_xe_sync) 来同步,就像在两个依赖的 exec 之间同步一样。

虽然用户无法触发绑定,但我们仍然必须在自上次 exec 以来已失效的 VM 中重新绑定 userptrs,同样,我们还必须重新绑定已被内核驱逐的 BO。 我们在 VM 中任何外部 BO 上的任何挂起的内核操作之后,或者任何 VM 私有的 BO 之后,安排这些重新绑定。 这是通过重新绑定等待 BO 的 DMA_RESV_USAGE_KERNEL 插槽(内核操作)和内核操作等待所有 BO 的插槽(正在进行中的 exec 位于私有 BO 的 DMA_RESV_USAGE_BOOKKEEP 中,对于外部 BO)。

重新绑定/ dma-resv 使用仅适用于非计算模式 VM,因为对于计算模式 VM,我们使用抢占 fences 和重新绑定 worker(TODO:添加链接)。

无需在 exec 中流量控制环,因为我们在提交时写入环,并设置 DRM 调度程序最大作业限制 SIZE_OF_RING / MAX_JOB_SIZE。 然后,DRM 调度程序将保持所有作业,直到环中有可用空间。

所有这些都会导致一个相当简单的 exec 实现。

流程

Parse input arguments
Wait for any async VM bind passed as in-fences to start
<----------------------------------------------------------------------|
Lock global VM lock in read mode                                       |
Pin userptrs (also finds userptr invalidated since last exec)          |
Lock exec (VM dma-resv lock, external BOs dma-resv locks)              |
Validate BOs that have been evicted                                    |
Create job                                                             |
Rebind invalidated userptrs + evicted BOs (non-compute-mode)           |
Add rebind fence dependency to job                                     |
Add job VM dma-resv bookkeeping slot (non-compute mode)                |
Add job to external BOs dma-resv write slots (non-compute mode)        |
Check if any userptrs invalidated since pin ------ Drop locks ---------|
Install in / out fences for job
Submit job
Unlock all