I915 VM_BIND 特性设计和用例¶
VM_BIND 特性¶
DRM_I915_GEM_VM_BIND/UNBIND ioctl 允许 UMD 在指定地址空间(VM)的指定 GPU 虚拟地址上绑定/解绑 GEM 缓冲区对象(BO)或 BO 的一部分。这些映射(也称为持久映射)将在 UMD 发出的多个 GPU 提交(execbuf 调用)中保持持久,而用户无需在每次提交期间提供所有必需的映射列表(如旧的 execbuf 模式所要求的)。
VM_BIND/UNBIND 调用允许 UMD 请求时间线输出栅栏,以指示绑定/解绑操作的完成。
VM_BIND 特性通过 I915_PARAM_VM_BIND_VERSION 向用户通告。用户必须在 VM 创建时通过 I915_VM_CREATE_FLAGS_USE_VM_BIND 扩展选择地址空间(VM)的 VM_BIND 绑定模式。
并发执行在不同 CPU 线程上的 VM_BIND/UNBIND ioctl 调用不排序。此外,当指定有效的输出栅栏时,VM_BIND/UNBIND 操作的部分可以异步完成。
VM_BIND 特性包括:
多个虚拟地址(VA)映射可以映射到对象的相同物理页面(别名)。
VA 映射可以映射到 BO 的部分区域(部分绑定)。
支持在 GPU 错误发生时捕获转储中的持久映射。
支持 userptr gem 对象(无需特殊的 uapi)。
TLB 刷新考虑¶
i915 驱动程序在每次提交时以及当对象的页面被释放时刷新 TLB。VM_BIND/UNBIND 操作不会执行任何额外的 TLB 刷新。任何添加的 VM_BIND 映射都将存在于该 VM 的后续提交的工作集中,并且不会存在于当前正在运行的批处理的工作集中(这将需要额外的 TLB 刷新,这是不支持的)。
VM_BIND 模式下的 Execbuf ioctl¶
处于 VM_BIND 模式的 VM 将不支持旧的 execbuf 绑定模式。VM_BIND 模式下的 execbuf ioctl 处理与旧的 execbuf2 ioctl 有很大不同(请参阅struct drm_i915_gem_execbuffer2
)。因此,添加了一个新的 execbuf3 ioctl 以支持 VM_BIND 模式。(请参阅struct drm_i915_gem_execbuffer3
)。execbuf3 ioctl 将不接受任何执行列表。因此,不支持隐式同步。预计以下工作将能够支持所有用例中的对象依赖关系设置要求:
“dma-buf:添加用于导出同步文件的 API”(https://lwn.net/Articles/859290/)
新的 execbuf3 ioctl 仅在 VM_BIND 模式下工作,而 VM_BIND 模式仅与用于提交的 execbuf3 ioctl 一起工作。在 execbuf3 调用时映射到该 VM 上的所有 BO(通过 VM_BIND 调用)都被视为该提交所必需的。
execbuf3 ioctl 直接指定批处理地址,而不是像 execbuf2 ioctl 中那样作为对象句柄。execbuf3 ioctl 也将不支持许多旧的功能,如输入/输出/提交栅栏、栅栏数组、默认 gem 上下文等等(请参阅struct drm_i915_gem_execbuffer3
)。
在 VM_BIND 模式下,VA 分配完全由用户管理,而不是由 i915 驱动程序管理。因此,所有 VA 分配和逐出都不适用于 VM_BIND 模式。此外,为了确定对象的活动性,VM_BIND 模式将不使用 i915_vma 活动引用跟踪。它将改为使用 dma-resv 对象(请参阅VM_BIND dma_resv 用法)。
因此,许多支持 execbuf2 ioctl 的现有代码,如重定位、VA 逐出、vma 查找表、隐式同步、vma 活动引用跟踪等,都不适用于 execbuf3 ioctl。因此,所有特定于 execbuf3 的处理都应放在单独的文件中,并且只有这些 ioctl 共有的功能才可以尽可能地成为共享代码。
VM_PRIVATE 对象¶
默认情况下,BO 可以映射到多个 VM 上,也可以导出 dma-buf。因此,这些 BO 被称为共享 BO。在每次 execbuf 提交期间,必须将请求栅栏添加到映射到 VM 上的所有共享 BO 的 dma-resv 栅栏列表中。
VM_BIND 特性引入了一种优化,用户可以通过在 BO 创建期间使用 I915_GEM_CREATE_EXT_VM_PRIVATE 标志来创建特定于指定 VM 的私有 BO。与共享 BO 不同,这些 VM 私有 BO 只能映射到它们所属的 VM 上,并且不能导出 dma-buf。VM 的所有私有 BO 共享 dma-resv 对象。因此,在每次 execbuf 提交期间,它们只需要更新一个 dma-resv 栅栏列表。因此,快速路径(其中所需的映射已经绑定)提交延迟相对于 VM 私有 BO 的数量为 O(1)。
VM_BIND 锁定层次结构¶
此处的锁定设计支持旧的(基于执行列表)execbuf 模式、较新的 VM_BIND 模式、具有 GPU 页面错误的 VM_BIND 模式以及未来可能的系统分配器支持(请参阅共享虚拟内存 (SVM) 支持)。旧的 execbuf 模式和没有页面错误的较新 VM_BIND 模式使用 dma_fence 管理后备存储的驻留。带有页面错误的 VM_BIND 模式和系统分配器支持根本不使用任何 dma_fence。
VM_BIND 锁定顺序如下。
锁-A:vm_bind 互斥锁将保护 vm_bind 列表。此锁在 vm_bind/vm_unbind ioctl 调用、execbuf 路径以及释放映射时获取。
将来,当支持 GPU 页面错误时,我们可能会使用 rwsem 代替,以便多个页面错误处理程序可以获取读取侧锁来查找映射,从而可以并行运行。旧的 execbuf 绑定模式不需要此锁。
锁-B:对象的 dma-resv 锁将保护 i915_vma 状态,并且在异步工作器中绑定/解绑 vma 时以及在更新对象的 dma-resv 栅栏列表时需要保持该锁。请注意,VM 的私有 BO 将共享一个 dma-resv 对象。
未来的系统分配器支持将改用 HMM 规定的锁定。
锁-C:自旋锁/s 用于保护 VM 的一些列表,例如无效的 vma 列表(由于逐出和 userptr 无效等)。
当支持 GPU 页面错误时,execbuf 路径不获取任何这些锁。在那里,我们只需将新的批处理缓冲区地址粉碎到环中,然后告诉调度程序运行它。锁获取仅发生在页面错误处理程序中,我们在其中以读取模式获取锁-A,无论我们需要哪个锁-B 来找到后备存储(gem 对象的 dma_resv 锁,以及系统分配器的 hmm/核心 mm)和一些额外的锁(锁-D)来处理页表竞争。页面错误模式应该永远不需要操作 vm 列表,因此永远不需要锁-C。
VM_BIND LRU 处理¶
我们需要确保 VM_BIND 映射的对象被正确地标记为 LRU,以避免性能下降。我们还需要支持 VM_BIND 对象的批量 LRU 移动,以避免在 execbuf 路径中产生额外的延迟。
页表页面类似于 VM_BIND 映射的对象(请参阅可逐出的页表分配),并且按 VM 维护,当 VM 被激活时(即,在对该 VM 进行 execbuf 调用时)需要在内存中固定。因此,还需要页表页面的批量 LRU 移动。
VM_BIND dma_resv 用法¶
栅栏需要添加到所有 VM_BIND 映射的对象中。在每次 execbuf 提交期间,它们都以 DMA_RESV_USAGE_BOOKKEEP 用法添加,以防止过度同步(请参阅enum dma_resv_usage
)。您可以在显式对象依赖关系设置期间使用 DMA_RESV_USAGE_READ 或 DMA_RESV_USAGE_WRITE 用法覆盖它。
请注意,DRM_I915_GEM_WAIT 和 DRM_I915_GEM_BUSY ioctl 不检查 DMA_RESV_USAGE_BOOKKEEP 用法,因此不应用于批处理结束检查。相反,execbuf3 输出栅栏应用于批处理结束检查(请参阅struct drm_i915_gem_execbuffer3
)。
此外,在 VM_BIND 模式下,使用 dma-resv api 来确定对象的活动性(请参阅dma_resv_test_signaled()
和 dma_resv_wait_timeout()
),并且不要使用已弃用的旧的 i915_vma 活动引用跟踪。这应该更容易使其与当前的 TTM 后端一起工作。
Mesa 用例¶
VM_BIND 可以潜在地减少 Mesa(包括 Vulkan 和 Iris)中的 CPU 开销,从而提高 CPU 密集型应用程序的性能。它还允许我们实现 Vulkan 的稀疏资源。随着 GPU 硬件性能的提高,降低 CPU 开销变得更有影响力。
其他 VM_BIND 用例¶
长时间运行的计算上下文¶
使用 dma-fence 期望它们在合理的时间内完成。另一方面,计算可以长时间运行。因此,计算适合使用用户/内存栅栏(请参阅用户/内存栅栏),并且 dma-fence 的使用必须仅限于内核内消耗。
在 GPU 页面错误不可用的情况下,内核驱动程序在缓冲区失效时将启动长时间运行上下文的挂起(抢占),完成失效,重新验证 BO,然后恢复计算上下文。这是通过为每个上下文设置一个抢占栅栏来实现的,当有人尝试等待它时,该栅栏会启用并触发上下文抢占。
用户/内存栅栏¶
用户/内存栅栏是一个 <地址, 值> 对。要发出用户栅栏信号,指定的值将被写入指定的虚拟地址,并唤醒等待的进程。用户栅栏可以由 GPU 或内核异步工作线程(如绑定完成时)发出信号。用户可以使用新的用户栅栏等待 ioctl 来等待用户栅栏。
以下是一些关于此的先前工作:https://patchwork.freedesktop.org/patch/349417/
低延迟提交¶
允许计算 UMD 直接提交 GPU 作业,而不是通过 execbuf ioctl。这得益于 VM_BIND 不与 execbuf 同步。VM_BIND 允许绑定/解绑直接提交作业所需的映射。
调试器¶
借助调试事件接口,用户空间进程(调试器)能够跟踪和处理由另一个进程(被调试的进程)创建并通过 vm_bind 接口附加到 GPU 的资源。
GPU 页面错误¶
GPU 页面错误(如果将来支持)将仅在 VM_BIND 模式下受支持。虽然较旧的 execbuf 模式和较新的 VM_BIND 模式的绑定都需要使用 dma-fence 来确保驻留,但 GPU 页面错误模式(如果支持)将不使用任何 dma-fence,因为驻留完全通过安装和删除/失效页表条目来管理。
页面级提示设置¶
VM_BIND 允许为每个映射设置任何提示,而不是为每个 BO 设置。可能的提示包括放置和原子性。随着即将到来的 GPU 按需页面错误支持,子 BO 级别的放置提示将更加相关。
页面级缓存/CLOS 设置¶
VM_BIND 允许为每个映射设置缓存/CLOS 设置,而不是为每个 BO 设置。
可驱逐的页表分配¶
使页表分配可驱逐,并以类似于 VM_BIND 映射对象的方式管理它们。页表页面类似于 VM 的持久映射(这里的区别在于页表页面将没有 i915_vma 结构,并且在将页面换入后,需要更新父页面链接)。
VM_BIND UAPI¶
I915_PARAM_VM_BIND_VERSION
支持的 VM_BIND 功能版本。请参阅 typedef drm_i915_getparam_t
参数。
指定支持的 VM_BIND 功能版本。已定义以下版本的 VM_BIND
0:不支持 VM_BIND。
- 1:在 VM_UNBIND 调用中,UMD 必须指定先前使用 VM_BIND 创建的确切映射,ioctl 将不支持解绑多个映射或拆分它们。同样,VM_BIND 调用将不会替换任何现有映射。
先前使用 VM_BIND 创建的确切映射,ioctl 将不支持解绑多个映射或拆分它们。同样,VM_BIND 调用将不会替换任何现有映射。
- 2:取消了对解绑部分或多个映射的限制,同样,绑定将替换给定范围内的任何映射。
取消了对解绑部分或多个映射的限制,同样,绑定将替换给定范围内的任何映射。
请参阅 struct drm_i915_gem_vm_bind
和 struct drm_i915_gem_vm_unbind
。
I915_VM_CREATE_FLAGS_USE_VM_BIND
在 VM 创建期间选择加入 VM_BIND 绑定模式的标志。请参阅 struct drm_i915_gem_vm_control
标志。
较旧的 execbuf2 ioctl 将不支持 VM_BIND 模式的操作。对于 VM_BIND 模式,我们有新的 execbuf3 ioctl,它将不接受任何 execlist(有关详细信息,请参阅 struct drm_i915_gem_execbuffer3
)。
-
struct drm_i915_gem_timeline_fence¶
输入或输出时间线栅栏。
定义:
struct drm_i915_gem_timeline_fence {
__u32 handle;
__u32 flags;
#define I915_TIMELINE_FENCE_WAIT (1 << 0);
#define I915_TIMELINE_FENCE_SIGNAL (1 << 1);
#define __I915_TIMELINE_FENCE_UNKNOWN_FLAGS (-(I915_TIMELINE_FENCE_SIGNAL << 1));
__u64 value;
};
成员
handle
用户用于等待或发出信号的 drm_syncobj 句柄。
flags
支持的标志是
I915_TIMELINE_FENCE_WAIT:在操作之前等待输入栅栏。
I915_TIMELINE_FENCE_SIGNAL:将操作完成栅栏作为输出返回。
value
时间线中的一个点。对于二进制 drm_syncobj,值必须为 0。对于时间线 drm_syncobj,值为 0 无效,因为它会将 drm_syncobj 转换为二进制 drm_syncobj。
描述
该操作将等待输入栅栏发出信号。
返回的输出栅栏将在操作完成后发出信号。
-
struct drm_i915_gem_vm_bind¶
要绑定的 VA 到对象的映射。
定义:
struct drm_i915_gem_vm_bind {
__u32 vm_id;
__u32 handle;
__u64 start;
__u64 offset;
__u64 length;
__u64 flags;
#define I915_GEM_VM_BIND_CAPTURE (1 << 0);
struct drm_i915_gem_timeline_fence fence;
__u64 extensions;
};
成员
vm_id
要绑定的 VM(地址空间)ID
handle
对象句柄
start
要绑定的虚拟地址起始位置
offset
要绑定的对象中的偏移量
length
要绑定的映射的长度
flags
支持的标志是
I915_GEM_VM_BIND_CAPTURE:在 GPU 错误时将此映射捕获到转储中。
请注意,栅栏带有其自己的标志。
fence
用于绑定完成信号的时间线栅栏。
时间线栅栏的格式为
struct drm_i915_gem_timeline_fence
。它是输出栅栏,因此使用 I915_TIMELINE_FENCE_WAIT 标志无效,并且将返回错误。
如果未设置 I915_TIMELINE_FENCE_SIGNAL 标志,则不请求输出栅栏,并且绑定同步完成。
extensions
以零结尾的扩展链。
用于未来的扩展。请参阅
struct i915_user_extension
。
描述
此结构被传递给 VM_BIND ioctl,并指定 GPU 虚拟地址 (VA) 范围到应绑定在指定地址空间 (VM) 的设备页表中的对象部分的映射。指定的 VA 范围必须是唯一的(即,当前未绑定),并且可以映射到整个对象或对象的一部分(部分绑定)。可以创建多个 VA 映射到对象的同一部分(别名)。
start、offset 和 length 必须是 4K 页对齐的。但是,DG2 对于设备本地内存具有 64K 页大小,并且具有紧凑的页表。在该平台上,对于绑定设备本地内存对象,start、offset 和 length 必须是 64K 对齐的。此外,UMD 不应在相同的 2M 范围内混合本地内存 64K 页面和系统内存 4K 页面绑定。
如果 start、offset 和 length 未正确对齐,则将返回错误代码 -EINVAL。在版本 1 中(请参阅 I915_PARAM_VM_BIND_VERSION),如果无法保留指定的 VA 范围,则将返回错误代码 -ENOSPC。
在不同的 CPU 线程上并发执行的 VM_BIND/UNBIND ioctl 调用是无序的。此外,如果指定了有效的 fence,则可以异步完成 VM_BIND 操作的一部分。
-
struct drm_i915_gem_vm_unbind¶
要解绑的 VA 到对象的映射。
定义:
struct drm_i915_gem_vm_unbind {
__u32 vm_id;
__u32 rsvd;
__u64 start;
__u64 length;
__u64 flags;
struct drm_i915_gem_timeline_fence fence;
__u64 extensions;
};
成员
vm_id
要绑定的 VM(地址空间)ID
rsvd
保留,MBZ
start
要解绑的虚拟地址起始位置
length
要解绑的映射的长度
flags
当前保留,MBZ。
请注意,栅栏带有其自己的标志。
fence
用于解绑完成信号的时间线栅栏。
时间线栅栏的格式为
struct drm_i915_gem_timeline_fence
。它是输出栅栏,因此使用 I915_TIMELINE_FENCE_WAIT 标志无效,并且将返回错误。
如果未设置 I915_TIMELINE_FENCE_SIGNAL 标志,则不请求输出栅栏,并且解绑同步完成。
extensions
以零结尾的扩展链。
用于未来的扩展。请参阅
struct i915_user_extension
。
描述
此结构被传递给 VM_UNBIND ioctl,并指定应从指定地址空间 (VM) 的设备页表中解绑的 GPU 虚拟地址 (VA) 范围。VM_UNBIND 将强制从设备页表中解绑指定的范围,而无需等待任何 GPU 作业完成。UMD 有责任确保在调用 VM_UNBIND 之前该映射不再使用。
如果未找到指定的映射,则 ioctl 将直接返回,而不返回任何错误。
在不同的 CPU 线程上并发执行的 VM_BIND/UNBIND ioctl 调用是无序的。此外,如果指定了有效的 fence,则可以异步完成 VM_UNBIND 操作的一部分。
-
struct drm_i915_gem_execbuffer3¶
用于 DRM_I915_GEM_EXECBUFFER3 ioctl 的结构。
定义:
struct drm_i915_gem_execbuffer3 {
__u32 ctx_id;
__u32 engine_idx;
__u64 batch_address;
__u64 flags;
__u32 rsvd1;
__u32 fence_count;
__u64 timeline_fences;
__u64 rsvd2;
__u64 extensions;
};
成员
ctx_id
上下文 ID
仅允许具有用户引擎映射的上下文。
engine_idx
引擎索引
由 ctx_id 指定的上下文的用户引擎映射中的索引。
batch_address
批处理 GPU 虚拟地址/们。
对于正常提交,它是批处理缓冲区的 GPU 虚拟地址。对于并行提交,它是指向批处理缓冲区 GPU 虚拟地址数组的指针,数组大小等于参与该提交的(并行)引擎的数量(请参阅
struct i915_context_engines_parallel_submit
)。flags
当前保留,MBZ
rsvd1
保留,MBZ
fence_count
timeline_fences 数组中的栅栏数。
timeline_fences
指向时间线栅栏数组的指针。
时间线栅栏的格式为
struct drm_i915_gem_timeline_fence
。rsvd2
保留,MBZ
extensions
以零结尾的扩展链。
用于未来的扩展。请参阅
struct i915_user_extension
。
描述
DRM_I915_GEM_EXECBUFFER3 ioctl 仅在 VM_BIND 模式下工作,而 VM_BIND 模式仅使用此 ioctl 进行提交。请参阅 I915_VM_CREATE_FLAGS_USE_VM_BIND。
-
struct drm_i915_gem_create_ext_vm_private¶
使对象对指定 VM 私有的扩展。
定义:
struct drm_i915_gem_create_ext_vm_private {
#define I915_GEM_CREATE_EXT_VM_PRIVATE 2;
struct i915_user_extension base;
__u32 vm_id;
};
成员
base
扩展链接。请参阅
struct i915_user_extension
。vm_id
对象对其私有的 VM 的 ID
描述