I915 GuC 提交/DRM 调度器章节

上游计划

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

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

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

    • 需要进行大量返工才能与 DRM 调度器集成,因此无需挑剔代码中的所有内容,它应该只是功能性的,没有主要的编码风格/分层错误,并且不会回归 execlists

    • 根据需要更新 IGT/自测以与 GuC 提交一起使用

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

    • 根据需要进行返工/使 CI 在 GuC 提交到位的情况下保持健康

  • 合并新的并行提交 uAPI
    • Bonding 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 调度器中重置/取消挂钩

      • Watchdog 挂钩到 DRM 调度器

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

    • Execlists 后端将最低限度地需要挂钩到 DRM 调度器
      • 传统接口

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

      • 完全集成到 DRM 调度器的 ROI 很低

      • 完全集成会给 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

现有的 bonding uAPI 与 GuC 提交完全中断,因为在通过 I915_SUBMIT_FENCE 激活的 execbuf 时间之前,不知道提交是单上下文提交还是并行提交。为了使用 GuC 并行提交多个上下文,必须显式地用 N 个上下文注册该上下文,并且必须在单个命令中将所有 N 个上下文提交给 GuC。GuC 接口不支持像 bonding uAPI 那样动态地在 N 个上下文之间切换。因此需要一个新的并行提交接口。此外,传统的 bonding uAPI 非常令人困惑,并且根本不直观。此外,根据设计,I915_SUBMIT_FENCE 是一个未来的 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

2-d 引擎实例数组,用于配置并行引擎

长度 = 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 是最后 N 个缓冲区对象,如果设置了 I915_EXEC_BATCH_FIRST,则为前 N 个。

默认的放置行为是在每个上下文映射到超过 1 个物理引擎(例如,上下文是虚拟引擎)时,在每个上下文之间创建隐式绑定。此外,我们只允许相同引擎类别的上下文,并且这些上下文必须按逻辑连续的顺序排列。下面描述了放置行为的示例。最后,默认是不允许 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 更改。