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 后,如果 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 再恢复的完整状态机流程:
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 和唤醒过滤器。