RFC-0123:CPU 性能信息系统调用

RFC-0123:CPU 性能信息系统调用
状态已接受
领域
  • 内核
说明

用于就 CPU 性能与内核进行通信的接口

Gerrit 更改
作者
审核人
提交日期(年-月-日)2021-07-20
审核日期(年-月-日)2021-08-18

摘要

此 RFC 提出了一种机制,使用户空间代理可通过该机制 与内核有关 CPU 性能的信息,两者都旨在更新 并查询其状态。

设计初衷

为了在异构中跨 CPU 有效调度工作 等架构,Zircon 内核调度程序会对 CPU 的相对性能。在撰写本文时, 来表示这些相对表现的量表 静态,由 ZBI 中的数据提供。

对 big.LITTLE 系统执行热 CPU 节流时, 大核心和小核心通常不会按照相同的因素进行扩缩, 它们的相对表现会动态变化。与大多数其他操作系统不同 在 Fuchsia 中,需在用户空间中执行对核心频率的修改,并且 必须跨内核边界通知调度程序相对 CPU 性能。这种通信需要新的系统调用。

设计

效果规模

概念

在考虑提议的系统调用之前,最好先了解一下相关概念 性能规模,这已经存在于内核调度程序中。 性能规模描述的是当下运行时的 CPU 的性能 其当前速度达到依赖于系统的参考性能,其中性能 可以使用任何合适的指标进行衡量,例如 DMIPS。在撰写本文时,但 不一定是将来,参考效果是指 最强大的 CPU 都以最大速率运行,因此最高 CPU 级别为 1.0 效果规模值。通常情况下,供应商会为 每个 CPU 都以标称速度运行,而且我们假定性能会因 与 CPU 频率成线性关系。

例如,在 big.LITTLE 系统中,供应商可能会提供性能数据 这表明大核心在全速运行时, DMIPS 是 以自己的最大速度工作的小型核心。如果参考性能 相当于一个以最大速度运行的大型核心 条件对应的是性能量表 1.0, 那么最大速度的性能范围将为 0.5。减慢大核心的速度 指定 25%,则得到 0.75 的新性能标准 核心速度提高了 25%,将其性能范围更改为 0.375。

更准确地说,如果 fref 是已知频率的参考频率, 效果规模 sref,则频率 fnew 具有 性能规模 snew=sreffnew/fref.一般来说, 实例中每个不同的 CPU 架构都需要参考频率 系统。

通常情况下, 给定系统。例如,通常情况下,同一集群中的 CPU 频率相同,并且每个集群仅支持相对较小的 不同频率的个数。不过,这超出了内核的讨论范围, 跟踪哪些性能量表是有效的。因此,内核信任用户空间。 以提供切合实际的价值,它会使用通过提议 尽其所能。

定点数表示法

为避免使用浮点数,性能规模的表示方法为 由结构体指定的定点数

  typedef struct zx_cpu_performance_scale {
    uint32_t integral_part;
    uint32_t fractional_part;  // Increments of 2**-32
  } zx_cpu_performance_scale_t;

integral_partfractional_part 描述整数和小数部分, 其中 fractional_part 指定 2-32 的增量。 实点表征和定点表征之间的转换应根据 以下函数:

zx_status_t ToFixedPoint(double real, zx_cpu_performance_scale_t* scale) {
  double integer;
  double fraction = std::modf(real, &integer);

  // Converting from double to fixed point should fail if the input's integer
  // part is too large.
  if (integer > static_cast<double>(UINT32_MAX)) {
    return ZX_ERR_INVALID_ARGS;
  }

  scale->integral_part = static_cast<uint32_t>(integer);

  // Rounding down the fractional part is suggested but should not matter
  // much in practice. A difference of 1 in the output is a difference of only
  // 2**-32 in the corresponding real value.
  scale->fractional_part = static_cast<uint32_t>(std::ldexp(fraction, 32));

  return ZX_OK;
}

double FromFixedPoint(zx_cpu_performance_scale_t scale) {
  return static_cast<double>(scale.integral_part)
    + std::ldexp(scale.fractional_part, -32);
}

系统调用 1:zx_system_set_performance_info

第一个系统调用允许用户空间代理设置 内核调度程序:

zx_status_t zx_system_set_performance_info(
    zx_handle_t resource,
    uint32_t topic,
    const void* new_info,
    size_t info_count
);

其参数为:

  • resource:授予此调用权限的资源。必须为 ZX_RSRC_SYSTEM_CPU_BASE,这是专门针对此 否则调用将失败。

  • topic:此调用引用的效果类型。必须为 ZX_CPU_PERF_SCALE,该值将在提案实施时进行定义。

  • new_info:有效的 zx_cpu_performance_info_t[],其元素为 指定者

    typedef struct zx_cpu_performance_info {
        uint32_t logical_cpu_number;
        zx_cpu_performance_scale_t performance_scale;
    } zx_cpu_performance_info_t;
    

    其中 zx_cpu_performance_t定义的 部分

    logical_cpu_number 指定结构体描述其信息的 CPU, 采用与内核相同的编号方案每个 logical_cpu_number 必须是有效的 CPU 标识符。new_info 的元素 必须按严格递增的 logical_cpu_number 进行排序(并且 因此,每个 logical_cpu_number 可能仅出现一次)。

    performance_scale 表示 并且应与 CPU 的新频率相对应,如所述 之前。但是,内核不会验证 基于支持的 CPU 频率的输入;任何正值都可以作为 输入。

    输入比例 {.integral_part = 0, .fractional_part = 0} 无效,因此 不要与离线核心请求混淆 一种不同的机制 - 预计将来会有其他 API。

    内核可以在内部使用最接近的值替换有效输入, 调度器可以使用的 Pod例如,在写入时, 支持的性能范围是 1.0。因此,如果 performance_scale 代表大于 1.0 的值,则内核会在内部限制该值 至 {.integral_part = 1, .fractional_part = 0}

    如果对 zx_system_set_performance_info 的调用失败,内核会获取 无任何操作,且 new_info 无任何影响。

    如果调用成功,内核调度程序将利用修改后的 从下个开始,new_info 重新安排操作(通常在调用返回后的某个时间发生)。 对于未引用的 CPU,内核不会修改其性能规模。 new_info

    此调用所做的更改将持续到重新启动或直到它们重新启动 被进一步使用此 API 所覆盖。

  • info_countnew_info 中的元素数量。必须为正数 超过系统中的 CPU 数量

错误情况

ZX_ERR_BAD_HANDLE

  • resource”不是有效的句柄。

ZX_ERR_WRONG_TYPE

  • resource 不是有效的资源句柄或不是种类 ZX_RSRC_KIND_SYSTEM

ZX_ERR_INVALID_ARGS

  • topic 不是 ZX_CPU_PERF_SCALE
  • new_info 是无效指针。
  • new_info”不是按照 logical_cpu_number 严格递增进行排序。

ZX_ERR_OUT_OF_RANGE

  • resource 的种类为 ZX_RSRC_KIND_SYSTEM,但不等于 ZX_RSRC_SYSTEM_CPU_BASE
  • info_count0 或超过了 CPU 数量。
  • logical_cpu_number 无效。
  • 输入 performance_scale{.integral_part = 0, .fractional_part = 0}

预期用途

zx_system_set_performance_info 应该用于通知内核 随着 CPU 频率的改变,CPU 性能也会随之改变。此 API 支持 因为不同的性能规范 CPU 可以由不同的实体控制。

如果要降低 CPU 频率,建议 在频率更改生效之前调用 zx_system_set_performance_info 错误。这样,内核调度程序就有机会减少 CPU 在容量降低之前(调度程序应 足够快,无需进一步协调;这个期望值 请在实施支持后确认。)

反之,如果要提高 CPU 的频率,建议 频率更改后,系统会调用 zx_system_set_performance_info 并仅在有新容量可用时通知调度程序。

无论是哪种情况,如果 CPU 频率更新失败,调用方都必须更新 根据生成的 CPU 状态启动内核调度程序。调用方应尝试执行 确定发生故障后的 CPU 频率,并使用该频率来 对 zx_system_set_performance_info 的调用。如果无法确定频率 (例如,如果关联的司机直接出现故障),来电者应拨打 对生成的 CPU 速度持悲观(低)猜测。此建议可能会 会不断演变;查看示例 https://fxbug.dev/42165500.

新 API 最终将由待开发的“CPU 管理器”使用 该组件将负责 CPU 的用户空间管理。更确切地说 与 CPU 驱动程序、想要修改 CPU 的代理程序相比, 频率将向 CPU 管理器注册请求,后者将协调 如本方案所述,频率随内核的更新而变化。

CPU 管理器还将接管 CPU 的温控调频 (此提案的激励用例)。

系统调用 2:zx_system_get_performance_info

第二个系统调用可检索所有 CPU 的性能信息:

zx_status_t zx_system_get_performance_info(
    zx_handle_t resource,
    uint32_t topic,
    void* info,
    size_t info_count
    size_t* output_count
);

其参数为:

  • resource:授予此调用权限的资源。必须为 ZX_RSRC_SYSTEM_CPU_BASE

  • topicZX_CPU_PERF_SCALEZX_CPU_DEFAULT_PERF_SCALE, 。主题决定内容 写入 info,如下所述。

  • info:长度等于zx_cpu_performance_info_t[] 系统中的 CPU 数量。

    如果调用失败,则 info 不会修改。

    如果调用成功,则返回时,info 会分别为每个元素包含一个元素 CPU,按 logical_cpu_number 递增排序。每个元素的 系统会根据 topic 填充 performance_scale

    • ZX_CPU_PERF_SCALEperformance_scale 存储内核的当前 指定 CPU 的性能范围。提供的值反映的是 最近一次对 zx_system_set_performance_info 的调用,即使下一次调用也不例外 重新安排操作尚未执行。

    • ZX_CPU_DEFAULT_PERF_SCALEperformance_scale 存储默认值 指定 CPU 启动时内核使用的性能范围。

  • info_countinfo 数组的长度;必须等于 系统。

  • output_count:如果调用成功,其中将包含元素数量 已写入 info。如果调用失败,则其值未指定。

错误情况

ZX_ERR_BAD_HANDLE

  • resource”不是有效的句柄。

ZX_ERR_WRONG_TYPE

  • resource 不是有效的资源句柄或不是种类 ZX_RSRC_KIND_SYSTEM

ZX_ERR_INVALID_ARGS

  • topic 不是 ZX_CPU_PERF_SCALEZX_CPU_DEFAULT_PERF_SCALE
  • info 是无效指针。

ZX_ERR_OUT_OF_RANGE

  • resource 的种类为 ZX_RSRC_KIND_SYSTEM,但不等于 ZX_RSRC_SYSTEM_CPU_BASE
  • info_count 不等于系统中的 CPU 总数。

预期用途

ZX_CPU_PERF_SCALE 下的行为允许用户空间代理查询 以便进行诊断例如,对于 代理在首次启动时评估系统状态,或将其作为崩溃信号 报告。

ZX_CPU_DEFAULT_PERF_SCALE 下的行为允许代理 确认其配置的性能水平是否与这些 由内核使用。

实现

内核

  • 新系统调用必须实现,由新资源控制 ZX_RSRC_SYSTEM_CPU_BASE

  • 必须修改内核调度程序以支持动态性能规模, 更新它们以使用 zx_system_set_performance_info,还会公开其当前正在使用的 默认性能可扩展到 zx_system_get_performance_info

组件管理器

新协议 CpuResource 必须由 组件管理器,用于提供 ZX_RSRC_SYSTEM_CPU_BASE 资源。这个 遵循之前存在的控制系统调用的资源模式。

性能

新系统调用本身的执行时间微乎其微,因为 它们只需处理少量数据,这些数据与 CPU 数量成正比。

使用 zx_cpu_set_performance_info 会导致调度器分配工作 将工作转移到性能规模提升的核心上 相对于所有性能水平总和,不考虑那些 也会随之降低重新安排流程本身 会给调度器造成大量负载。

重新调度会导致系统性能出现预期的变化。正在测试 这些更改等同于对调度器进行功能正确性测试 问题可在测试中解决。

安全注意事项

两个新系统调用都受新资源句柄的控制 ZX_RSRC_SYSTEM_CPU_BASE。对于zx_system_set_performance_info,这项保护措施 解决了对调度器的恶意干扰。对于 zx_system_get_performance_info,则存在更细微的数据泄露问题; 不应该信任不可信实体,以了解内核性能 体重秤通常提供有关系统支持的 P 状态。

隐私注意事项

此方案对隐私保护没有任何实质性的影响。

测试

  • 我们将添加核心测试,以执行基本的成功和失败标准。
  • 将添加单元测试,以验证调度器对更新后的 性能规模。他们将验证截止时间线程是否已固定到 CPU,并且该 CPU 的性能规模已由 α 系数修改,那么 将分配给线程的实际时间乘以 1/α。

文档

Zircon 系统调用文档将会更新,以包含新的 API。

缺点、替代方案和未知问题

通用性

考虑了更通用的接口,例如 zx_set_cpu_properties 最终可以处理内核间额外交互的系统调用 和 CPU 等资源最终,我们选择了窄版界面 预计会很少出现此界面的客户, 对建议界面的更改相对较小。对 更通用的界面在这一阶段主要是凭空猜测。

其他调用结构

作为 zx_system_set_performance_info 的“仅集合”操作的替代方法, 考虑了返回之前性能的组合 get/set 操作 缩放。这是出于 确保调用方能够还原性能规模变更 执行失败。

不过,经过进一步的研究发现,如果简单还原更改, 是不够的。这就导致了一套更复杂的故障处理 建议,并引领了更简单的“仅设置”策略 操作。

最后,需要使用 zx_system_get_performance_info 来支持封闭测试。 在这种情况下,直接还原更改是恰当的,并且支持 诊断用例。

备用 CPU 索引

我们考虑过使用其他方案将 CPU 编入索引,例如引用 分配给它们不过,由于内核没有其他需要 与 Zircon 的有限范围限制使用 API, 使用内核的现有逻辑 CPU 编号。这些数字在 客户端可以维护一个静态的 来引用它们或可能访问其配置数据 来自 ZBI。

性能规模的替代方案

我们认为,新的 API 可能会利用“速度因素”调度器将应用于 特定 CPU 的性能扩缩。这样做可以减少 客户需要了解的特定情境信息;而非 了解 CPU 之间的相对性能,只需要知道 CPU 的新频率与其标称频率之间的比率。

我们选择不采用这种方法,因为使用效果规模的预期 是异构系统上的 CPU 温控降频的基本方式, 该 API 的预期客户无法从 速度因素。同时,定义合适的语言模型 并修改调度器以利用新概念。

最大性能规模

此提案最初使用 uint32_t 表示效果范围, 以 [0.0, 1.0] 表示的实值。特别要指出的是,这使 表示最大值 1.0。

虽然 1.0 是 Zircon 调度程序支持的最大性能规模, 于是,我们决定允许表示大于 1.0 来支持未来的用例,例如涡轮模式。此外, 之前的表示法不是定点的,因此会产生值 调度器无法直接使用的名称。

performance_scale 的表示法

performance_scale 原本是 uint64_t,高 32 位存储 整数部分和较低 32 位存储小数部分。这会 在 zx_cpu_performance_info_t 中的字段之间产生了 32 位的填充, 从而引入了潜在泄露矢量。新的表示形式避免了 错误。

允许 performance_scale 的值

仔细考虑了zx_system_set_performance_info 应允许作为 performance_scale 的输入。“0.0”的值是 已确定太容易与指令混淆,导致 CPU 离线 Zircon 目前不支持 使用其他 API因此,确定了表示 0.0 的值 返回错误。

非常小的值也应特别注意。例如, {.integral_part = 0, .fractional_part = 1} 表示 2-32, 该值可合理视为 0.0, 相应的核心脱机。虽然可以通过强制执行 允许的最小值,目前任何此类阈值都是任意的, 会使内核与用户空间之间的协定更加复杂。我们觉得 将新 API 视为提示机制是最简单的做法, 如果需要替换输入,内核可以自由替换输入 公开与此类选择相关的内部细节。

未来工作

配置管理

理想情况下,用户空间代理将使用 ZBI 共享完全相同的 CPU 内核调度程序使用的配置数据。尚不清楚 但目前非常实用

此外,必须注意确保内核和用户空间 将默认性能量表与相同的标称频率相关联。

性能规模的下限

原则上,调度器可以确定 系统应根据当前的截止时间线程和 CPU 负载进行维护。动态 这些边界版本将是用户空间代理的重要输入, 尝试利用较低的 CPU 频率来实现能效。额外 zx_system_get_performance_info选项自然地 公开它们。

CPU 归因

应该建立一些方式来关联线程归因的 CPU 时间 以及调度它所针对的 CPU 的性能。此类关联为 已经与建立可靠的效果指标 在大核心而不是小核心上进行调度,并且相关度 我们开发出与频率调整相关的机械, 提案。

节流代理的保证执行

执行温控降频时降低 CPU 频率可能会导致 CPU 这反过来又会降低节流代理进程 以便及时处理节流代理的执行应 以适当的方式确定优先级。

先验技术和参考资料

将 CPU 频率控制的责任委派给用户空间的情况很常见 操作系统,因此不提供关于此主题的现有技术。