系统挂起代码流程

版权:

© 2020 英特尔公司

作者:

Rafael J. Wysocki <rafael.j.wysocki@intel.com>

至少需要执行一次全局系统范围的转换,系统才能从工作状态进入支持的睡眠状态。休眠需要执行多次转换才能达到此目的,但其他睡眠状态,通常称为系统范围挂起(或简称系统挂起)状态,只需要一次转换。

对于这些睡眠状态,系统从工作状态到目标睡眠状态的转换也称为系统挂起(在大多数情况下,这指的是转换还是系统的睡眠状态应该从上下文中清楚地看出),而从睡眠状态返回到工作状态的转换称为系统恢复

与系统不同睡眠状态的挂起和恢复转换相关的内核代码流程非常相似,但挂起到空闲代码流程与挂起到 RAM待机睡眠状态相关的代码流程之间存在一些显著差异。

挂起到 RAM待机睡眠状态的实现离不开平台支持,它们之间的区别归根结底是平台驱动程序需要提供的挂起和恢复钩子所执行的特定于平台的动作,以使它们可用。除此之外,这些睡眠状态的挂起和恢复代码流程几乎相同,因此在下文中它们将一起称为平台相关的挂起状态。

挂起到空闲的挂起代码流程

执行以下步骤,将系统从工作状态转换到挂起到空闲睡眠状态

  1. 调用系统范围的挂起通知器。

    内核子系统可以注册回调,以便在即将发生挂起转换以及恢复转换完成后调用。

    这允许它们为系统状态的更改做准备,并在返回工作状态后进行清理。

  2. 冻结任务。

    冻结任务主要是为了避免用户空间通过直接暴露给它的 MMIO 区域或 I/O 寄存器进行未经检查的硬件访问,并防止用户空间在转换的下一步进行时进入内核(由于各种原因,这可能会有问题)。

    所有用户空间任务都被拦截,就好像它们被发送了一个信号,并进入不可中断的睡眠,直到随后的系统恢复转换结束。

    由于特定原因而选择在系统挂起期间冻结的内核线程随后会被冻结,但不会被拦截。相反,它们应该定期检查是否需要冻结,并在需要时使自己进入不可中断的睡眠。[但是,请注意,内核线程可以使用内核空间中可用的锁定和其他并发控制来与系统挂起和恢复同步,这比冻结精确得多,因此后者不是内核线程的推荐选项。]

  3. 挂起设备并重新配置 IRQ。

    设备在四个阶段挂起,称为准备挂起延迟挂起无 irq 挂起(有关每个阶段中具体发生情况的更多信息,请参阅设备电源管理基础知识)。

    每个设备在每个阶段都会被访问,但通常在不超过两个阶段中进行物理访问。

    延迟挂起阶段,会禁用每个设备的运行时 PM API,并且在高层级(“操作”)中断处理程序在无 irq 挂起阶段之前被阻止调用。

    在此之后,中断仍然会被处理,但只会向中断控制器确认,而不会执行在系统工作状态下会触发的任何特定于设备的动作(这些动作会延迟到后续的系统恢复转换,如下文所述)。

    与系统唤醒设备关联的 IRQ 会“启动”,以便在其中一个 IRQ 发出事件信号时启动系统的恢复转换。

  4. 冻结调度器滴答声并挂起计时。

    当所有设备都已挂起时,CPU 将进入空闲循环并置于最深的可用空闲状态。在这样做时,每个 CPU 都会“冻结”自己的调度器滴答声,以便与滴答声关联的计时器事件在 CPU 被另一个中断源唤醒之前不会发生。

    最后一个进入空闲状态的 CPU 也会停止计时,这(除其他外)会阻止高分辨率计时器向前触发,直到第一个被唤醒的 CPU 重新启动计时。这允许 CPU 一次性在深度空闲状态中停留相对较长的时间。

    从此时开始,CPU 只能被非计时器硬件中断唤醒。如果发生这种情况,它们将返回空闲状态,除非唤醒其中一个 CPU 的中断来自已为系统唤醒启动的 IRQ,在这种情况下,系统恢复转换将开始。

挂起到空闲的恢复代码流程

执行以下步骤,将系统从挂起到空闲睡眠状态转换到工作状态

  1. 恢复计时并解冻调度器滴答声。

    当其中一个 CPU 被唤醒(通过非计时器硬件中断)时,它会离开在上一个挂起转换的最后一步中进入的空闲状态,重新启动计时(除非它已经被较早唤醒的另一个 CPU 重新启动),并且该 CPU 上的调度器滴答声被解冻。

    如果唤醒 CPU 的中断已为系统唤醒启动,则系统恢复转换开始。

  2. 恢复设备并恢复 IRQ 的工作状态配置。

    设备在四个阶段恢复,称为无 irq 恢复早期恢复恢复完成(有关每个阶段中具体发生情况的更多信息,请参阅设备电源管理基础知识)。

    每个设备在每个阶段都会被访问,但通常在不超过两个阶段中进行物理访问。

    IRQ 的工作状态配置在无 irq 恢复阶段后恢复,并且在早期恢复阶段,会为每个其驱动程序支持的设备重新启用运行时 PM API。

  3. 解冻任务。

    在前一个挂起转换的第 2 步中冻结的任务被“解冻”,这意味着它们会从当时进入的不可中断睡眠中唤醒,并且允许用户空间任务退出内核。

  4. 调用系统范围的恢复通知器。

    这类似于挂起转换的第 1 步,并且此时会调用同一组回调,但会向它们传递不同的“通知类型”参数值。

平台相关的挂起代码流程

执行以下步骤,将系统从工作状态转换到平台相关的挂起状态

  1. 调用系统范围的挂起通知器。

    此步骤与上述挂起转换的第 1 步相同。

  2. 冻结任务。

    此步骤与上述挂起转换的第 2 步相同。

  3. 挂起设备并重新配置 IRQ。

    此步骤类似于上述挂起转换的第 3 步,但为系统唤醒启动 IRQ 通常对平台没有任何影响。

    有些平台在所有 CPU 都处于足够深的空闲状态且所有 I/O 设备都进入低功耗状态时,可以在内部进入非常深的低功耗状态。在这些平台上,挂起到空闲 (suspend-to-idle) 可以非常有效地降低系统功耗。

    然而,在其他平台上,需要以特定于平台的方式(在平台驱动程序提供的钩子中实现)关闭低级组件(如中断控制器),才能实现可比的功耗降低。

    这通常会阻止带内硬件中断唤醒系统,而必须以特殊的平台相关方式进行唤醒。然后,系统唤醒源的配置通常在系统唤醒设备被挂起时开始,并在稍后由平台挂起钩子完成。

  4. 禁用非引导 CPU。

    在某些平台上,上述挂起钩子必须在系统的单 CPU 配置中运行(特别是,硬件不能被任何与平台挂起钩子并行运行的代码访问,而这些代码可能并且经常会陷入平台固件以完成挂起转换)。

    因此,CPU 脱机/联机(CPU 热插拔)框架用于使系统中除一个(引导 CPU)之外的所有 CPU 都脱机(通常,脱机的 CPU 进入深度空闲状态)。

    这意味着所有任务都从这些 CPU 迁移开,并且所有 IRQ 都被重定向到唯一保持联机的 CPU。

  5. 挂起核心系统组件。

    这为核心系统组件准备(可能)失去电源,并暂停计时。

  6. 平台特定的电源移除。

    预计这将移除除内存控制器和 RAM(为了保留后者的内容)以及一些指定用于系统唤醒的设备之外的所有系统组件的电源。

    在许多情况下,控制权被传递给平台固件,该固件被期望根据需要完成挂起转换。

平台相关的恢复代码流程

以下步骤用于将系统从平台相关的挂起状态转换为工作状态

  1. 平台特定的系统唤醒。

    平台被来自指定系统唤醒设备之一的信号(不必是带内硬件中断)唤醒,并将控制权传递回内核(平台的 工作配置可能需要在内核再次获得控制权之前由平台固件恢复)。

  2. 恢复核心系统组件。

    恢复核心系统组件的挂起时配置,并恢复计时。

  3. 重新启用非引导 CPU。

    在前面挂起转换的步骤 4 中禁用的 CPU 将重新联机,并恢复其挂起时配置。

  4. 恢复设备并恢复 IRQ 的工作状态配置。

    此步骤与上述 的挂起到空闲挂起转换的步骤 2 相同。

  5. 解冻任务。

    此步骤与上述 的挂起到空闲挂起转换的步骤 3 相同。

  6. 调用系统范围的恢复通知器。

    此步骤与上述 的挂起到空闲挂起转换的步骤 4 相同。