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,并在释放对象的页面时刷新 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 不接受任何 execlist。因此,不支持隐式同步。预计以下工作将能够支持所有用例中的对象依赖关系设置要求

“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 也不支持许多旧功能,如 in/out/submit 栅栏、栅栏数组、默认 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 锁定层次结构

此处的锁定设计支持旧的(基于 execlist)execbuf 模式、新的 VM_BIND 模式、带 GPU 页面错误的 VM_BIND 模式以及可能的未来系统分配器支持(参见 共享虚拟内存 (SVM) 支持)。旧的 execbuf 模式和新的无页面错误的 VM_BIND 模式使用 dma_fence 管理后备存储的驻留。带页面错误的 VM_BIND 模式和系统分配器支持根本不使用任何 dma_fence。

VM_BIND 锁定顺序如下。

  1. Lock-A:vm_bind 互斥锁将保护 vm_bind 列表。此锁在 vm_bind/vm_unbind ioctl 调用、execbuf 路径以及释放映射时获取。

    将来,当支持 GPU 页面错误时,我们可以潜在地使用 rwsem 代替,以便多个页面错误处理程序可以获取读取侧锁以查找映射,因此可以并行运行。旧的 execbuf 绑定模式不需要此锁。

  2. Lock-B:对象的 dma-resv 锁将保护 i915_vma 状态,需要在异步工作程序中绑定/解绑 vma 时以及更新对象的 dma-resv 栅栏列表时持有。请注意,VM 的私有 BO 将共享一个 dma-resv 对象。

    未来的系统分配器支持将使用 HMM 规定的锁定。

  3. Lock-C:spinlock/s 用于保护某些 VM 的列表,如因驱逐和 userptr 失效而失效的 vma 列表等。

当支持 GPU 页面错误时,execbuf 路径不会获取任何这些锁。在那里,我们将简单地将新的批处理缓冲区地址粉碎到环中,然后告诉调度程序运行它。获取锁仅发生在页面错误处理程序中,我们在读取模式下获取 lock-A,无论我们需要哪个 lock-B 才能找到后备存储(gem 对象的 dma_resv 锁和系统分配器的 hmm/core mm)以及一些额外的锁 (lock-D) 用于处理页表竞争。页面错误模式不应需要操作 vm 列表,因此永远不需要 lock-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 apis 来确定对象活动性(参见 dma_resv_test_signaled()dma_resv_wait_timeout()),不要使用已弃用的旧 i915_vma 活动引用跟踪。这应该更容易与当前的 TTM 后端一起工作。

Mesa 用例

VM_BIND 可以潜在地减少 Mesa 中的 CPU 开销(Vulkan 和 Iris),从而提高 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。子 BO 级别的位置提示对于即将到来的 GPU 按需页面错误支持将更重要。

页面级别缓存/CLOS 设置

VM_BIND 允许每个映射设置缓存/CLOS,而不是每个 BO。

可驱逐的页表分配

使页表分配可驱逐,并以类似于 VM_BIND 映射对象的方式管理它们。页表页面类似于 VM 的持久映射(此处的区别在于页表页面将没有 i915_vma 结构,并且在交换页面回来后,需要更新父页面链接)。

共享虚拟内存 (SVM) 支持

VM_BIND 接口可用于使用 HMM 接口直接映射系统内存(无需 gem BO 抽象)。仅在启用 GPU 页面错误时才支持 SVM。

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 调用不会替换任何现有映射。

2:取消了对解绑部分或多个映射的限制

。同样,绑定将替换给定范围内的任何映射。

参见 struct drm_i915_gem_vm_bindstruct 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;
};

句柄

用户的 drm_syncobj 句柄,用于等待或发出信号。

标志

支持的标志是

I915_TIMELINE_FENCE_WAIT:在操作之前等待输入栅栏。

I915_TIMELINE_FENCE_SIGNAL:返回作为输出的操作完成栅栏。

时间线中的一个点。对于二进制 drm_syncobj,值必须为 0。时间线 drm_syncobj 的值为 0 无效,因为它将 drm_syncobj 转换为二进制。

描述

操作将等待输入栅栏发出信号。

返回的输出栅栏将在操作完成后发出信号。

struct drm_i915_gem_vm_bind

要绑定的 VA 到对象映射。

vm_id

成员:

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

对象句柄

用户的 drm_syncobj 句柄,用于等待或发出信号。

开始

要绑定的虚拟地址起始

偏移量

要绑定的对象中的偏移量

长度

要绑定的映射长度

I915_GEM_VM_BIND_CAPTURE:在 GPU 错误时捕获转储中的此映射。

支持的标志是

I915_TIMELINE_FENCE_WAIT:在操作之前等待输入栅栏。

请注意,栅栏 带有自己的标志。

栅栏

用于绑定完成信号的时间线栅栏。

时间线栅栏的格式为 struct drm_i915_gem_timeline_fence

它是一个输出栅栏,因此使用 I915_TIMELINE_FENCE_WAIT 标志无效,并且将返回错误。

如果未设置 I915_TIMELINE_FENCE_SIGNAL 标志,则不请求输出栅栏,并且同步完成绑定。

扩展

以零结尾的扩展链。

用于未来的扩展。参见 struct i915_user_extension

此结构传递给 VM_BIND ioctl,并指定 GPU 虚拟地址 (VA) 范围到对象部分的映射,该映射应绑定在指定地址空间 (VM) 的设备页表中。指定的 VA 范围必须是唯一的(即,当前未绑定),并且可以映射到整个对象或对象的一部分(部分绑定)。可以创建多个 VA 映射到对象的同一部分(别名)。

操作将等待输入栅栏发出信号。

开始偏移量长度 必须与 4K 页面对齐。但是,DG2 的设备本地内存的页面大小为 64K,并具有紧凑的页表。在该平台上,对于绑定设备本地内存对象,开始偏移量长度 必须与 64K 对齐。此外,UMD 不应在同一 2M 范围内混合本地内存 64K 页面和系统内存 4K 页面绑定。

如果 开始偏移量长度 未正确对齐,则将返回错误代码 -EINVAL。在版本 1 中(参见 I915_PARAM_VM_BIND_VERSION),如果无法保留指定的 VA 范围,则将返回错误代码 -ENOSPC。

在不同的 CPU 线程上并发执行的 VM_BIND/UNBIND ioctl 调用没有顺序。此外,如果指定了有效的 栅栏,则可以异步完成 VM_BIND 操作的部分。

struct drm_i915_gem_vm_unbind

要解绑的 VA 到对象映射。

rsvd

成员:

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

对象句柄

保留,MBZ

要解绑的虚拟地址起始

要绑定的虚拟地址起始

要解绑的映射长度

要绑定的映射长度

当前保留,MBZ。

支持的标志是

用于解绑完成信号的时间线栅栏。

栅栏

用于绑定完成信号的时间线栅栏。

如果未设置 I915_TIMELINE_FENCE_SIGNAL 标志,则不请求输出栅栏,并且同步完成解绑。

它是一个输出栅栏,因此使用 I915_TIMELINE_FENCE_WAIT 标志无效,并且将返回错误。

如果未设置 I915_TIMELINE_FENCE_SIGNAL 标志,则不请求输出栅栏,并且同步完成绑定。

此结构传递给 VM_UNBIND ioctl,并指定应从指定地址空间 (VM) 的设备页表中解绑的 GPU 虚拟地址 (VA) 范围。VM_UNBIND 将强制从设备页表中解绑指定的范围,而无需等待任何 GPU 作业完成。UMD 有责任确保在调用 VM_UNBIND 之前不再使用该映射。

以零结尾的扩展链。

用于未来的扩展。参见 struct i915_user_extension

此结构传递给 VM_BIND ioctl,并指定 GPU 虚拟地址 (VA) 范围到对象部分的映射,该映射应绑定在指定地址空间 (VM) 的设备页表中。指定的 VA 范围必须是唯一的(即,当前未绑定),并且可以映射到整个对象或对象的一部分(部分绑定)。可以创建多个 VA 映射到对象的同一部分(别名)。

操作将等待输入栅栏发出信号。

如果找不到指定的映射,ioctl 将简单地返回,而不产生任何错误。

在不同的 CPU 线程上并发执行的 VM_BIND/UNBIND ioctl 调用没有顺序。此外,如果指定了有效的 栅栏,则可以异步完成 VM_UNBIND 操作的部分。

struct drm_i915_gem_execbuffer3

DRM_I915_GEM_EXECBUFFER3 ioctl 的结构。

ctx_id

成员:

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;
};

句柄

上下文 id

仅允许具有用户引擎映射的上下文。

engine_idx

引擎索引

ctx_id 指定的上下文的用户引擎映射中的索引。

batch_address

批处理 gpu 虚拟地址/es。

对于正常提交,它是批处理缓冲区的 gpu 虚拟地址。对于并行提交,它是一个指向批处理缓冲区 gpu 虚拟地址数组的指针,数组大小等于参与该提交的(并行)引擎的数量(参见 struct i915_context_engines_parallel_submit)。

当前保留,MBZ

支持的标志是

rsvd1

fence_count

要解绑的虚拟地址起始

timeline_fences 数组中的栅栏数。

timeline_fences

指向时间线栅栏数组的指针。

时间线栅栏的格式为 struct drm_i915_gem_timeline_fence

rsvd2

DRM_I915_GEM_EXECBUFFER3 ioctl 仅在 VM_BIND 模式下工作,VM_BIND 模式仅适用于此 ioctl 进行提交。参见 I915_VM_CREATE_FLAGS_USE_VM_BIND。

要解绑的虚拟地址起始

以零结尾的扩展链。

用于未来的扩展。参见 struct i915_user_extension

此结构传递给 VM_BIND ioctl,并指定 GPU 虚拟地址 (VA) 范围到对象部分的映射,该映射应绑定在指定地址空间 (VM) 的设备页表中。指定的 VA 范围必须是唯一的(即,当前未绑定),并且可以映射到整个对象或对象的一部分(部分绑定)。可以创建多个 VA 映射到对象的同一部分(别名)。

操作将等待输入栅栏发出信号。

struct drm_i915_gem_create_ext_vm_private

使对象对指定 VM 私有的扩展。

base

成员:

struct drm_i915_gem_create_ext_vm_private {
#define I915_GEM_CREATE_EXT_VM_PRIVATE          2;
    struct i915_user_extension base;
    __u32 vm_id;
};

句柄

扩展链接。参见 struct i915_user_extension

VM 的 Id,对象是私有的

要绑定的 VM(地址空间)id

参见 struct drm_i915_gem_create_ext

操作将等待输入栅栏发出信号。

©内核开发社区。| 由 Sphinx 5.3.0 & Alabaster 0.7.16 提供支持 | 页面来源