I915 GuC 提交/DRM 调度器章节

上游计划

对于上游来说,GuC 提交和将 i915 与 DRM 调度器集成的总体计划是:

  • 合并基本 GuC 提交
    • 所有 gen11+ 平台的基本提交支持

    • 在任何当前平台上默认不启用,但可以通过 modparam enable_guc 启用

    • 需要进行大量重构才能与 DRM 调度器集成,因此无需挑剔代码中的所有内容,它只需功能正常,没有主要的编码风格/分层错误,并且不会使 execlists 回退

    • 根据需要更新 IGT/自检以使用 GuC 提交

    • 在支持的平台上为基线启用 CI

    • 根据需要重构/使 CI 适应 GuC 提交

  • 合并新的并行提交 uAPI
    • 绑定 uAPI 与 GuC 提交完全不兼容,而且它在总体上存在严重的设计问题,这就是我们无论如何都想将其淘汰的原因

    • 新的 uAPI 添加了 I915_CONTEXT_ENGINES_EXT_PARALLEL 上下文设置步骤,该步骤使用 N 个上下文配置一个插槽

    • 在 I915_CONTEXT_ENGINES_EXT_PARALLEL 之后,用户可以在单个 execbuf IOCTL 中向插槽提交 N 个批处理,并且这些批处理在 GPU 上并行运行

    • 最初仅用于 GuC 提交,但如果需要,可以支持 execlists

  • 转换 i915 以使用 DRM 调度器
    • GuC 提交后端与 DRM 调度器完全集成
      • 所有请求队列都从后端删除(例如,所有反压力都在 DRM 调度器中处理)

      • 在 DRM 调度器中重置/取消挂钩

      • 看门狗挂钩到 DRM 调度器中

      • 一旦与 DRM 调度器集成,GuC 后端的许多复杂性就可以被移除(例如,状态机变得更简单,锁定变得更简单等)

    • Execlists 后端将最小化所需的功能以挂钩到 DRM 调度器中
      • 旧接口

      • 诸如时间片/抢占/虚拟引擎之类的功能很难与 DRM 调度器集成,并且这些功能对于 GuC 提交不是必需的,因为 GuC 为我们完成了这些事情

      • 完全集成到 DRM 调度器的投资回报率较低

      • 完全集成会增加 DRM 调度器的复杂性

    • 在 DRM 调度器中移植 i915 优先级继承/提升功能
      • 用于 i915 页面翻转,也可能对其他 DRM 驱动程序有用

      • 这将是 DRM 调度器中的一个可选功能

    • 从 DRM 调度器中删除按顺序完成的假设
      • 即使使用 DRM 调度器,后端也会处理抢占、时间片等,因此作业可能以无序的方式完成

    • 移除 i915 优先级级别并使用 DRM 优先级级别

    • 根据需要优化 DRM 调度器

GuC 提交上游的 TODO

  • 需要更新 GuC 固件/i915 以启用错误状态捕获

  • 用于解码 GuC 日志的开源工具

  • 公开 GuC 规范

用于基本 GuC 提交的新 uAPI

基本 GuC 提交的 uAPI 不需要进行重大更改。唯一的更改是新的调度器属性:I915_SCHEDULER_CAP_STATIC_PRIORITY_MAP。此属性指示 2k i915 用户优先级级别静态映射到 3 个级别,如下所示

  • -1k 到 -1 低优先级

  • 0 中优先级

  • 1 到 1k 高优先级

这是必需的,因为 GuC 只有 4 个优先级频段。最高优先级频段保留给内核。这也与 DRM 调度器优先级级别对齐。

规范参考:

新的并行提交 uAPI

现有的绑定 uAPI 与 GuC 提交完全中断,因为在通过 I915_SUBMIT_FENCE 激活的 execbuf 时间之前,不知道提交是单个上下文提交还是并行提交。为了使用 GuC 并行提交多个上下文,必须使用 N 个上下文显式注册上下文,并且必须在单个命令中将所有 N 个上下文提交给 GuC。GuC 接口不支持像绑定 uAPI 那样动态更改 N 个上下文。因此,需要一个新的并行提交接口。此外,旧的绑定 uAPI 非常令人困惑且完全不直观。此外,I915_SUBMIT_FENCE 在设计上是未来的栅栏,所以我们真的不应该继续支持它。

新的并行提交 uAPI 由 3 个部分组成

  • 导出引擎逻辑映射

  • 一个“set_parallel”扩展,用于配置用于并行提交的上下文

  • 扩展 execbuf2 IOCTL 以支持在单个 IOCTL 中提交 N 个 BB

导出引擎逻辑映射

某些用例要求将 BB 按照逻辑顺序放置在引擎实例上(例如,gen11+ 上的分帧)。引擎实例的逻辑映射可以根据熔断而变化。与其让 UMD 感知熔断,不如简单地使用现有的查询引擎信息 IOCTL 公开逻辑映射。此外,与 execlists 相比,GuC 提交接口当前仅支持按照逻辑顺序将多个上下文提交到引擎,这是一个新的要求。最后,所有当前平台最多有 2 个引擎实例,并且逻辑顺序与 uAPI 顺序相同。这将在具有超过 2 个引擎实例的平台上发生变化。

将在 drm_i915_engine_info.flags 中添加一个位,指示已返回逻辑实例,并且新字段 drm_i915_engine_info.logical_instance 返回逻辑实例。

一个“set_parallel”扩展,用于配置用于并行提交的上下文

“set_parallel”扩展配置一个插槽,用于并行提交 N 个 BB。这是一个必须在使用任何上下文之前调用的设置步骤。有关类似的现有示例,请参见 I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE 或 I915_CONTEXT_ENGINES_EXT_BOND。一旦为并行提交配置了插槽,就可以调用 execbuf2 IOCTL 在单个 IOCTL 中提交 N 个 BB。最初仅支持 GuC 提交。如果需要,可以稍后添加 Execlists 支持。

将 I915_CONTEXT_ENGINES_EXT_PARALLEL_SUBMIT 和 drm_i915_context_engines_parallel_submit 添加到 uAPI 以实现此扩展。

struct i915_context_engines_parallel_submit

配置引擎用于并行提交。

定义:

struct i915_context_engines_parallel_submit {
    struct i915_user_extension base;
    __u16 engine_index;
    __u16 width;
    __u16 num_siblings;
    __u16 mbz16;
    __u64 flags;
    __u64 mbz64[3];
    struct i915_engine_class_instance engines[];
};

成员

base

基本用户扩展。

engine_index

并行引擎的插槽

width

每个并行引擎的上下文数,换句话说,每次提交中的批次数

num_siblings

每个上下文的同级数,换句话说,每次提交的可能放置数

mbz16

保留供将来使用;必须为零

flags

所有未定义的标志都必须为零,当前未定义的标志

mbz64

保留供将来使用;必须为零

engines

用于配置并行引擎的引擎实例的二维数组

长度 = width (i) * num_siblings (j) 索引 = j + i * num_siblings

描述

在上下文引擎映射中设置一个插槽,以允许在单个 execbuf IOCTL 中提交多个 BB。然后,这些 BB 将被调度为在 GPU 上并行运行。在 i915 中内部创建多个硬件上下文以运行这些 BB。一旦为 N 个 BB 配置了一个插槽,则只能在每个 execbuf IOCTL 中提交 N 个 BB,这是隐式行为,例如,用户不会告诉 execbuf IOCTL 有 N 个 BB,execbuf IOCTL 知道有多少个 BB 基于插槽的配置。N 个 BB 是 drm_i915_gem_exec_object2 列表中的最后 N 个缓冲区对象,如果设置了 I915_EXEC_BATCH_FIRST,则为前 N 个。

默认的放置行为是在每个上下文映射到多个物理引擎(例如,上下文是一个虚拟引擎)的情况下,在每个上下文之间创建隐式绑定。此外,我们只允许相同引擎类的上下文,并且这些上下文必须按逻辑连续顺序排列。下面描述了放置行为的示例。最后,默认情况下不允许在批处理中间抢占 BB。而是在每组 BB 之间在所有硬件上下文上插入协调的抢占点。将来可以添加标志以更改这两个默认行为。

如果硬件上下文放置配置无效,或者平台/提交接口不支持放置配置,则返回 -EINVAL。如果平台/提交接口不支持扩展,则返回 -ENODEV。

Examples syntax:
CS[X] = generic engine of same class, logical instance X
INVALID = I915_ENGINE_CLASS_INVALID, I915_ENGINE_CLASS_INVALID_NONE

Example 1 pseudo code:
set_engines(INVALID)
set_parallel(engine_index=0, width=2, num_siblings=1,
             engines=CS[0],CS[1])

Results in the following valid placement:
CS[0], CS[1]

Example 2 pseudo code:
set_engines(INVALID)
set_parallel(engine_index=0, width=2, num_siblings=2,
             engines=CS[0],CS[2],CS[1],CS[3])

Results in the following valid placements:
CS[0], CS[1]
CS[2], CS[3]

This can be thought of as two virtual engines, each containing two
engines thereby making a 2D array. However, there are bonds tying the
entries together and placing restrictions on how they can be scheduled.
Specifically, the scheduler can choose only vertical columns from the 2D
array. That is, CS[0] is bonded to CS[1] and CS[2] to CS[3]. So if the
scheduler wants to submit to CS[0], it must also choose CS[1] and vice
versa. Same for CS[2] requires also using CS[3].
VE[0] = CS[0], CS[2]
VE[1] = CS[1], CS[3]

Example 3 pseudo code:
set_engines(INVALID)
set_parallel(engine_index=0, width=2, num_siblings=2,
             engines=CS[0],CS[1],CS[1],CS[3])

Results in the following valid and invalid placements:
CS[0], CS[1]
CS[1], CS[3] - Not logically contiguous, return -EINVAL

扩展 execbuf2 IOCTL 以支持在单个 IOCTL 中提交 N 个 BB

已使用“set_parallel”扩展配置的上下文只能在单个 execbuf2 IOCTL 中提交 N 个 BB。BB 要么是 drm_i915_gem_exec_object2 列表中的最后 N 个对象,要么是如果设置了 I915_EXEC_BATCH_FIRST,则为前 N 个。BB 的数量是隐式的,基于提交的插槽以及它是如何通过“set_parallel”或其他扩展配置的。execbuf2 IOCTL 不需要 uAPI 更改。