命令提交¶
在 DRM 驱动程序中(至少在 i915 中),Execs 在历史上一直相当复杂,因为有以下几点:
传入一个 BO 列表,这些 BO 会被读取/写入,从而创建隐式同步
在执行时绑定
在执行时控制环的流量
在 XE 中,我们避免所有这些复杂性,方法是不允许将 BO 列表传递到 exec 中,使用 dma-buf 隐式同步 uAPI,将绑定作为单独的操作,并使用 DRM 调度程序来控制环的流量。 让我们深入了解其中的每一项。
我们可以通过强制用户在每次执行时都使用输入/输出栅栏,而不是内核跟踪 BO 的依赖关系(例如,如果用户知道一个执行写入一个 BO,并在下一个执行中从该 BO 读取,则用户有责任在两个执行之间传递输入/输出栅栏),从而摆脱 BO 列表。
我们不允许用户在执行时触发绑定,而是有一个 VM 绑定 IOCTL,它使用与执行相同的输入/输出栅栏接口。 从这个意义上讲,从用户的角度来看,VM 绑定基本上与执行操作相同。 例如,如果执行依赖于 VM 绑定,请使用输入/输出栅栏接口 (struct drm_xe_sync
) 进行同步,就像在两个依赖执行之间进行同步一样。
尽管用户不能触发绑定,但我们仍然必须重新绑定自上次执行以来已失效的 VM 中的 userptr,同样,我们也必须重新绑定已被内核逐出的 BO。 我们在 VM 中任何外部 BO 或 VM 私有的任何 BO 上任何挂起的内核操作之后安排这些重新绑定。 这是通过让重新绑定等待 BO 的 DMA_RESV_USAGE_KERNEL 插槽(内核操作),以及让内核操作等待所有 BO 插槽(对于私有 BO,正在进行的执行在 DMA_RESV_USAGE_BOOKKEEP 中,对于外部 BO,则在 DMA_RESV_USAGE_BOOKKEEP 中)来实现的。
重新绑定/dma-resv 使用仅适用于非计算模式 VM,因为对于计算模式 VM,我们使用抢占栅栏和重新绑定工作线程(TODO:添加链接)。
无需在执行中控制环的流量,因为我们在提交时写入环,并设置 DRM 调度程序的最大作业限制 SIZE_OF_RING / MAX_JOB_SIZE。 然后,DRM 调度程序将保持所有作业,直到环中有可用空间。
所有这些都导致一个相当简单的执行实现。
流程¶
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