4. D3cold状态详解

D3cold 是 PCIe 设备电源管理中最深层次的电源状态,也是网卡 NIC 驱动开发中需要特别关注的“硬关断”场景。与 D3hot 不同,D3cold 下设备的主电源(Vmain)被完全切断,设备仅可能通过 Vaux 辅助电源维持极低功耗的唤醒逻辑。本节将深入剖析 D3cold 的定义、电源拓扑、配置空间行为以及驱动应对策略。

4.1 D3cold 的核心定义与电源特征

D3cold 在 PCIe 规范中定义为“主电源被移除”的状态。当系统进入深度睡眠(如 S3/S4/S5)、热插拔槽断电或通过电源管理固件关闭设备供电时,设备会进入 D3cold。其关键特征如下:

特征维度 D3cold 状态描述 与 D3hot 的差异
主电源 Vmain 完全切断(0V) D3hot 下 Vmain 仍保持供电
辅助电源 Vaux 可选保留,用于唤醒逻辑 D3hot 不依赖 Vaux
PCIe 配置空间 完全丢失(寄存器内容清零) D3hot 下配置空间保持有效
设备上下文 全部丢失,需驱动重新初始化 D3hot 可保留部分上下文
唤醒能力 仅通过 PME# 或 WAKE# 信号(需 Vaux) D3hot 可通过 PME# 唤醒
恢复延迟 高(需重新枚举或复位) 低(无需复位)
驱动要点: D3cold 下配置空间丢失意味着驱动不能依赖任何之前保存的寄存器值。恢复时必须重新读取 Vendor ID / Device ID 确认设备身份,并完整重做硬件初始化序列。

4.2 电源拓扑:Vmain 切断与 Vaux 辅助电源

理解 D3cold 的电源拓扑对驱动开发至关重要。下图展示了典型网卡在 D3cold 下的电源分配结构:

D3cold 电源拓扑:Vmain 切断与 Vaux 辅助电源 D3cold 电源拓扑:Vmain 切断与 Vaux 辅助电源 系统电源管理 (ACPI / 固件) 主电源开关 Vmain 切断 辅助电源开关 Vaux 可选保留 NIC 网卡设备 配置空间丢失 内部状态清零 唤醒逻辑 (PME# / WAKE#) 控制 控制 Vmain (0V) Vaux (3.3V) Vaux ⚠ 关键说明: Vaux 仅当设备支持 PME# 唤醒且系统配置了辅助电源时才存在。多数桌面网卡在 D3cold 下 Vaux 也被切断。
硬件设计提示: 网卡进入 D3cold 后,如果 Vaux 存在,设备内部的唤醒逻辑(如 Magic Packet 检测、Link Change 检测)仍可工作。驱动需在进入 D3cold 前通过 PMCSR 寄存器正确设置唤醒使能位。

4.3 配置空间丢失的后果与驱动应对

D3cold 下配置空间丢失是驱动开发中最需要警惕的问题。当设备从 D3cold 恢复时,所有 PCIe 配置寄存器(包括 Vendor ID、Device ID、Command、Status、BAR、MSI-X Capability 等)都恢复为复位默认值。这意味着:

  • 设备识别失效: 驱动不能假设任何寄存器值仍然有效,必须重新读取 Vendor ID / Device ID 确认设备是否还在。
  • BAR 地址丢失: 之前分配的 MMIO 基地址全部清零,驱动必须等待 PCI 子系统重新分配资源。
  • 中断配置清零: MSI/MSI-X 使能位、中断向量表配置全部丢失,需要重新设置。
  • 链路状态重置: 可能触发 LTSSM 重新训练,驱动需等待链路恢复。
常见驱动 Bug: 在 D3cold 恢复路径中直接使用之前缓存的 BAR 地址或中断配置,会导致 MMIO 访问无效、中断无法触发,甚至系统崩溃。正确的做法是:在恢复回调中调用 pci_restore_state() 或手动重新初始化所有硬件资源。

4.4 D3cold 状态转换流程

下图展示了网卡从 D0 进入 D3cold 再恢复的完整状态机流程:

D3cold 状态转换流程 D3cold 状态转换流程 D0 (全速) Vmain 正常 D3hot Vmain 保持 D3cold Vmain 切断 复位/重新枚举 配置空间恢复 驱动重新初始化 完整硬件重配置 PMCSR 写 D3 主电源切断 电源恢复 PCI 子系统回调 驱动完成恢复 ⚠ 配置空间丢失点 D3cold 入口处

4.5 驱动中处理 D3cold 的代码模式

在 Linux 内核网卡驱动中,处理 D3cold 恢复的标准模式如下:

/* 网卡驱动恢复回调示例 */
static int nic_resume(struct device *dev)
{
    struct pci_dev *pdev = to_pci_dev(dev);
    struct nic_priv *priv = pci_get_drvdata(pdev);
    int ret;

    /* 1. 恢复 PCI 配置空间(关键步骤) */
    pci_restore_state(pdev);

    /* 2. 重新使能设备 */
    ret = pci_enable_device(pdev);
    if (ret)
        return ret;

    /* 3. 重新设置 DMA 掩码 */
    ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
    if (ret)
        goto err_disable;

    /* 4. 重新分配并映射 BAR 资源 */
    pci_set_master(pdev);

    /* 5. 重新初始化网卡硬件(完整序列) */
    ret = nic_hw_init(priv);
    if (ret)
        goto err_disable;

    /* 6. 重新注册中断 */
    ret = nic_request_irq(priv);
    if (ret)
        goto err_hw_deinit;

    /* 7. 启动网络设备 */
    netif_device_attach(priv->netdev);
    netif_start_queue(priv->netdev);

    return 0;

err_hw_deinit:
    nic_hw_deinit(priv);
err_disable:
    pci_disable_device(pdev);
    return ret;
}
关键点解析:
  • pci_restore_state() 必须最先调用,它从保存的缓存中恢复配置空间。
  • 如果设备在 D3cold 期间被物理移除(如热插拔),pci_enable_device() 会失败,驱动需处理此场景。
  • 网卡硬件初始化必须完整执行,不能跳过任何步骤。

4.6 D3cold 与 PME# 唤醒配置

如果网卡支持从 D3cold 唤醒系统,需要满足以下条件:

条件 说明 驱动配置
Vaux 存在 系统必须提供辅助电源 检查 ACPI _PRW 对象
PME 支持 设备必须支持 PME# 信号 读取 PM Capability 的 PME_Support 位
唤醒使能 PMCSR 的 PME_En 位必须置位 进入 D3cold 前设置
唤醒事件配置 网卡内部唤醒过滤器(如 Magic Packet) 通过私有寄存器配置
调试建议: 使用 lspci -vvv 查看设备的 PM Capability 字段,确认 PME_Support 是否包含 D3cold。如果 PME_Support 的 bit 4 为 1,表示设备支持从 D3cold 唤醒。

4.7 本章小结

  • D3cold 是 PCIe 最深电源状态,主电源完全切断,配置空间丢失。
  • Vaux 辅助电源可选保留,用于支持 PME# 唤醒逻辑。
  • 驱动从 D3cold 恢复时必须完整重新初始化硬件,不能依赖任何缓存值。
  • pci_restore_state() 是恢复流程的第一步,用于恢复配置空间。
  • 支持 D3cold 唤醒的设备需在进入前正确配置 PMCSR 和唤醒过滤器。