RFC-0259:单调时钟挂起和启动时间轴

RFC-0259:单调时钟暂停和启动时间轴
状态已接受
区域
  • 系统
说明

详细说明了在 S2Idle 期间暂停单调时钟,并介绍了启动时间轴。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2024-08-16
审核日期(年-月-日)2024-09-11

问题陈述

“挂起到空闲”RFC 规定,在挂起到空闲期间,单调时钟应暂停。不过,在 Fuchsia 上运行的某些程序可能需要了解在暂停期间经过的时间。如果我们暂停时钟,而不为这些程序提供其他跟踪时间的方式,这些程序就会中断。

摘要

此 RFC 提出了一种解决方案,即添加一个新时间轴(以下称为“启动时间轴”),该时间轴在挂起到空闲期间会继续推进,从而解决上述问题。我们还将介绍时钟、计时器以及与此时间轴互动的其他相关对象。

需要了解暂停期间经过时间的现有代码需要进行修改,以使用启动时间轴,然后我们才能更改为暂停单调时钟。

利益相关方

Facilitator: jamesr@google.com

审核者

  • adamperry@google.com
  • alizhang@google.com
  • brunodalbo@google.com
  • crjohns@google.com
  • fmil@google.com
  • gmtr@google.com
  • johngro@google.com
  • maniscalco@google.com
  • miguelfrde@google.com
  • tgales@google.com
  • tmandry@google.com

已咨询

此 RFC 的内容咨询了 Fuchsia 内的许多团队。这包括但不限于以下团队的成员:

  • 取证
  • 诊断
  • 内核
  • 性能
  • 安全
  • Starnix
  • 计时

共同化

此 RFC 的内容已在多份单独的文档中发布,这些文档已发送到涵盖 Fuchsia 组织大部分成员的各个邮件列表。

要求

此 RFC 对现有单调时间轴和新的启动时间轴提出了多项要求:

  • 引入启动时间轴并在挂起期间暂停单调时钟不得阻止应用实现其现有功能。
  • 启动时钟 必须报告自系统启动以来经过的总时间,包括在挂起到空闲状态下花费的任何时间。
  • 单调时钟不得包含在挂起到空闲状态下花费的时间。在 Suspend-To-Idle RFC 之前,单调时钟在系统挂起期间的行为未定义,因此我们在此处明确定义该行为。
  • 单调时钟和启动时钟必须单调递增。
  • 当内核初始化计时器硬件时,单调时钟和启动时钟都必须设置为零。
    • 计时器硬件初始化必须在用户空间启动之前进行。

设计

此提案描述了新的启动时间轴的行为,以及单调时间轴的行为变化。本文档还概述了 Fuchsia 中可能需要使用启动时间轴来适应暂停单调时钟的部分。未来 RFC 将提供有关所需 API 更改的更详细说明。

命名注意事项

虽然每个主要操作系统都有一个在系统挂起时不会暂停的时间轴,但每个操作系统的时间轴名称都不同。下表显示了 Fuchsia 中的两个时间轴以及它们在其他操作系统中的名称:

紫红色 Linux BSD Windows OS X Android
单调时间 CLOCK_MONOTONIC_RAW CLOCK_UPTIME 无偏见时间 绝对时间 正常运行时间
启动时间 CLOCK_BOOTTIME CLOCK_BOOTTIME 实时或时间 连续时间 ElapsedRealtime

之所以选择“启动时间轴”这个名称,是为了与 Linux 和 BSD 使用的现有名称保持一致,尤其是因为 POSIX 兼容性(通过 Starnix)是我们向 Zircon 内核引入这个新时间轴的主要原因之一。

时间轴语义

在挂起到空闲状态期间,启动时间轴保证会继续推进。相比之下,如 RFC-230 所述,单调时间轴保证在暂停期间暂停。不过,当系统未处于暂停状态时,这两个时间轴将以相同的速率计时。当内核首次初始化计时器硬件时,这两个时间轴也会设置为零,目前这种情况发生在内核启动的早期阶段。

受影响的 Fuchsia 部分

需要更改 Fuchsia 的以下部分,以适应暂停单调时钟。此 RFC 的目标是概略说明需要更改的方面。未来的 RFC 和设计文档将明确说明这些更改的具体内容以及更改方式。

请注意,此处列出的每个领域还应审核其提供的 FIDL 服务,以确保任何时间戳字段都记录了其运行的时间线。时间戳类型本身不会发生变化,但注释和变量名称应清楚指明它们所基于的时间线。这些类型未来可能会发生变化,如需了解详情,请参阅“未来工作”部分。

  1. Kernel:许多内核 API 会采用或返回单调时间戳。可能需要修改其中一些 API 以同时支持这两个时间轴,或者完全改用启动时间。
  2. Timekeeper:UTC 时间的计算应切换为使用启动时间。
  3. 诊断:诊断堆栈在许多地方(例如归档器、日志、检查等)都使用时间戳。必须审核每种用法是否正确。
  4. 跟踪:跟踪目前会使用单调时间戳来注释样本,因此在暂停期间将不再起作用。在这些时间间隔内获取轨迹有很强的应用场景,因此我们需要一种方法来重建它们。
  5. Starnix:Starnix 中所有 CLOCK_BOOTTIME 的用法都需要切换为使用启动时间轴。
  6. 取证:崩溃报告和 Cobalt 都包含单调时间戳;这些时间戳可能需要更改为使用启动时间戳,也可能不需要。
  7. 开发者工具:FFX 可能需要添加功能来获取当前启动时间,并且可能需要在检索日志时审核其单调时间使用情况。
  8. 系统库:某些特定于语言的功能(例如标准库中的异步执行器或时间 API)可能需要考虑新的时间轴。
  9. 软件交付:Omaha 客户端(以及 SWD 堆栈中可能存在的其他组件)依赖于单调时间,可能需要切换时间轴。
  10. Netstack:Netstack3 使用的计时器可能需要切换为使用启动时间轴,也可能不需要。

实现

实施将分几个阶段进行。

阶段 1:启动时的内核支持

启动时间轴将引入到 Zircon 内核中,并且内部结构将进行修改以与新的时间轴配合使用。然后,启动时间轴将公开给用户空间。这需要进行一些 API 更改,因此需要另一份 RFC 来记录这些更改。

第 2 阶段:Starnix 集成

一旦 Zircon 支持启动时间轴,Starnix 就可以使用它来支持 CLOCK_BOOTTIME 的实现以及所有相关的下游功能。由于 Starnix 使用 @next vDSO,因此可以在与其他系统部分集成之前先进行集成,这可能允许在获得第 1 阶段中提及的 RFC 批准后进行并行化。

第 3 阶段:在支持挂起的设备上保持功能

单调时钟仅在系统暂停期间暂停。 因此,我们必须确保在支持挂起的设备上运行的所有软件都切换为使用阶段 1 中引入的启动时间轴。这包括系统库、诊断、跟踪、取证和时间管理器的更改。

只要有可能,此阶段就可以与第 2 阶段并行进行。

第 4 阶段:在挂起到空闲期间暂停单调时钟

修复了所有在启用挂起的设备上运行的代码后,我们可以在挂起期间暂停单调时钟。这可能会突出显示其他问题(如需了解详情,请参阅“未知”部分)。如果这些问题会中断关键用户历程,则会回滚暂停时钟的 CL 并修复相应问题。如果关键用户体验未中断,则时钟将保持暂停状态,并且问题将向前修复。

第 5 阶段:在暂停功能已停用的设备上保持功能

仅在暂停功能已停用的设备上运行且必须了解暂停时间的软件应进行修改,以便在必要时使用启动时间。由于在这些设备上,对挂起到空闲状态的支持优先级较低,因此时间压力较小。此阶段的变化包括软件交付和开发者工具。

Netstack3 也将在此阶段更新;即使它在启用暂停的设备上运行,单调时钟暂停时,其对计时器的使用也不会中断,因此我们无需阻止它。

未来工作

唤醒向量

我们可能希望支持在启动时间轴上的给定点恢复系统。为此,我们需要在系统中正式确定唤醒向量的概念,并为创建基于时间的唤醒向量建立语义。这将在未来的 RFC 中解决。

时间轴感知型 FIDL 时间戳

FIDL 目前使用 zx.Time 表示所有时间戳,该类型别名为 int64。最终,这种类型将受益于:

  1. 存储或以其他方式记录其参考时间轴,或者
  2. 将替换为每个参考时间轴的不同时间类型。

我们将在未来的 RFC 中决定采用哪种方法,因为推出新时间轴并了解常见的使用模式将有助于我们做出更好的决策。

性能

引入第二个时间轴并暂停单调时间轴本身不会影响性能。不过,未来 RFC 中围绕各种 API 如何与启动时间交互的设计决策可能会对性能产生影响。相关 RFC 和设计文档中应讨论这些影响。

工效学设计

此更改将使 Fuchsia 中的时间推理更加复杂。代码作者需要考虑,在使用时间 API 之前,是否需要让代码了解暂停间隔。不过,大多数程序应该能够使用单调时间轴并忽略暂停时间段,因为在暂停期间不会运行任何用户空间线程。

向后兼容性

如设计和实现部分中所述,有些代码目前使用单调时钟,在挂起到空闲期间暂停该时钟后,这些代码可能无法正常运行。在暂停时钟之前,此代码将迁移到启动时间轴,如实现部分中所述。

安全注意事项

有几项对安全性至关重要的操作需要可信的时间源。这些措施包括限制、验证令牌和验证证书。如今,节流和令牌验证需要可信执行环境 (TEE) 和/或云,因此对于作为高级别操作系统的 Fuchsia 而言,这不是主要问题。

Fuchsia 各处都使用证书验证来验证 HTTPS、SSH、Omaha、TUF 等的安全传输 (TLS)。这要求时间源具有高度可信度,即时间源必须在几分钟的误差范围内反映时间的物理现实。目前,Timekeeper 可满足此需求,它专门调整时间客户端从中获取时间的 UTC 时钟。因此,如 RFC 前面所述,Timekeeper 必须切换为使用启动时间。请注意,在建立可信度和精确度之前,Timekeeper 已经阻止客户端检索时间,因此无需额外工作即可确保此操作安全。

请注意,除 Timekeeper 及其客户端之外的任何启动时间使用情况均不得处理关键安全应用场景;这些应用场景应通过 TEE 或使用 Timekeeper 提供的 UTC 时钟。

隐私注意事项

此提案不会影响系统隐私权。

测试

有两类不同的代码必须进行测试:

  1. 我们必须验证,使用单调时间轴的代码在暂停期间不受暂停的影响。我们计划使用 CQ 和广泛的本地测试(包括自动挂起)来测试此功能。
  2. 我们必须验证需要切换到启动时间轴的所有代码区域是否正常运行。这些领域的测试计划将由相应代码所有者制定,并将在未来的文档和/或 RFC 中指定。

文档

从宏观层面来看,我们将在 fuchsia.dev 中添加一个页面,介绍新的启动时间轴及其行为。我们还将修改现有的单调文档,以反映在挂起到空闲期间,单调时钟会暂停。

然后,设计部分中提及的每个代码区域都负责以其认为合适的方式更新其文档。

缺点、替代方案和未知因素

替代方案

可以设想其他解决方案,在挂起期间不暂停时钟,从而无需新的启动时间轴。很遗憾,该内容不符合我们的平台要求:

  1. Starnix 中的预期:Linux 将 CLOCK_MONOTONIC 定义为“不计算系统暂停的时间”。因此,Starnix 必须提供相同的保证。我们可以使用用户空间时钟来实现这一点,但这会带来严重的复杂性,因为 Starnix 和 Fuchsia 的时钟行为会有所不同,这意味着任何涉及时间和时间戳的 IPC 或操作都必须考虑这种差异。
  2. 部分现有代码中的预期:部分代码在编写时假设单调时间不会暂停。部分内容是假设会这样而撰写的。根据暂停到空闲状态 RFC,我们决定暂停单调时钟是两害相权取其轻。
  3. 避免计时器“唤醒风暴”:假设系统长时间处于挂起状态,而单调时钟在此期间继续推进。系统中的程序可以设置任意数量的计时器,但系统在从挂起状态恢复之前不会执行这些计时器的回调。这可能会导致在恢复时花费大量时间来运行计时器回调。

未知

在 Fuchsia 上运行的几乎每个程序都会使用单调时间轴。如上一部分中所述,部分代码在编写时假设单调时钟在暂停期间会继续运行。一旦我们暂停时钟,这种平衡就会被打破,而且很可能会以微妙的方式打破。

我们已尽最大努力识别并列出所有需要切换到新时间轴的代码区域。为此,我们不仅查看了代码,还与组织中的许多利益相关者进行了广泛的交流。我们相信,我们已经发现了大部分潜在的破坏性更改。

不过,我们可能遗漏了一些调用点,或者错误地假设使用单调时间轴的调用点没问题。遗憾的是,我们无法轻松识别这些问题;它们是未知未知问题,只有在暂停时钟(在挂起到空闲期间)并对系统挂起进行广泛测试后才会显现出来。

在先技术和参考资料

RFC-0230:挂起到空闲 RFC