4. Linux pciehp 驱动分析:驱动初始化、中断处理、状态机迁移与电源管理交互
Linux 内核中的 pciehp(PCI Express Hot-Plug)驱动是原生热插拔控制的核心实现。它负责管理标准 PCIe 插槽的物理插入/拔出检测、链路训练、电源控制以及上层通知。本章深入分析其驱动初始化流程、中断处理机制、状态机迁移模型以及与系统电源管理的交互方式。
4.1 驱动初始化流程
pciehp 驱动通过 PCI 子系统标准驱动模型注册,其初始化入口为 pciehp_probe。该函数在 PCI 核心枚举到带有 Hot-Plug Capability 的 Root Port 或 Switch Downstream Port 时被调用。
| 步骤 | 函数/操作 | 说明 |
|---|---|---|
| 1 | pciehp_probe | 分配 struct controller,初始化互斥锁、工作队列、定时器 |
| 2 | pcie_init | 读取 Slot Capabilities/Control/Status 寄存器,确认热插拔能力 |
| 3 | pciehp_request_irq | 注册 MSI/MSI-X 中断处理函数 pciehp_isr |
| 4 | pciehp_enable_slot | 执行初始状态检测,若槽位已有设备则启动枚举 |
| 5 | pcie_init_notification | 使能 Presence Detect、Attention Button、Power Fault 等事件通知 |
4.2 中断处理机制
pciehp 使用 PCIe 原生热插拔事件中断。当槽位状态发生变化(如卡插入/拔出、注意力按钮按下)时,硬件通过 MSI/MSI-X 触发中断。中断处理分为上半部(ISR)和下半部(工作队列)。
4.2.1 中断处理流程
- 上半部(
pciehp_isr):读取 Slot Status 寄存器,判断事件类型(Presence Detect、Attention Button、Power Fault、Link State)。清除中断状态位,并将事件封装为struct slot_event提交到工作队列。 - 下半部(
pciehp_ist):在工作队列上下文中执行实际的状态机迁移。调用pciehp_handle_presence_change、pciehp_handle_attention_button等处理函数。
4.2.2 事件类型与处理函数映射
| Slot Status 位 | 事件类型 | 处理函数 |
|---|---|---|
| Presence Detect Changed | 卡插入/拔出 | pciehp_handle_presence_change |
| Attention Button Pressed | 用户按下按钮 | pciehp_handle_attention_button |
| Power Fault Detected | 电源故障 | pciehp_handle_power_fault |
| Link State Changed | 链路状态变化 | pciehp_handle_link_state_change |
4.3 状态机迁移模型
pciehp 驱动内部维护一个有限状态机,用于管理槽位的生命周期。状态定义在 hp_state 枚举中,核心状态包括 HP_STATE_ENABLE、HP_STATE_DISABLE、HP_STATE_POWER_ON、HP_STATE_POWER_OFF 等。
4.3.1 核心状态定义
| 状态 | 枚举值 | 描述 |
|---|---|---|
| HP_STATE_DISABLE | 0 | 槽位禁用,电源关闭,无设备存在 |
| HP_STATE_POWER_ON | 1 | 供电已开启,等待链路训练完成 |
| HP_STATE_ENABLE | 2 | 设备已启用,PCI 枚举完成,可正常使用 |
| HP_STATE_POWER_OFF | 3 | 正在执行断电序列,等待电源稳定 |
| HP_STATE_BLINK | 4 | 注意力按钮按下,LED 闪烁,等待用户确认 |
4.3.2 典型迁移场景:卡插入
- 初始状态 HP_STATE_DISABLE,槽位无电源。
- 用户插入 PCIe 卡,Presence Detect 状态变化触发中断。
- 中断处理调用
pciehp_handle_presence_change,状态迁移至 HP_STATE_POWER_ON。 - 驱动向槽位供电,等待链路训练(Link Training)。
- 链路训练成功(Link Up),状态迁移至 HP_STATE_ENABLE。
- PCI 核心执行设备枚举,分配资源,加载驱动。
4.4 电源管理交互
pciehp 驱动需要与系统电源管理框架(ACPI、PCI PM)协同工作,确保热插拔操作不会破坏电源状态一致性。
4.4.1 电源管理回调
| 回调函数 | 触发时机 | pciehp 行为 |
|---|---|---|
pciehp_suspend | 系统进入 S3/S4 睡眠 | 保存槽位状态,关闭电源,禁止中断 |
pciehp_resume | 系统从睡眠中恢复 | 重新初始化硬件,恢复中断,重新检测槽位状态 |
pciehp_runtime_suspend | 运行时电源管理(RTPM) | 若槽位为空,允许 Root Port 进入低功耗状态 |
pciehp_runtime_resume | 运行时唤醒 | 恢复槽位电源,重新使能事件通知 |
4.4.2 与 ACPI 的协作
在支持 ACPI 的平台上,pciehp 通过 acpi_pcihp 接口与 ACPI 交互。ACPI 提供 _EJ0(弹出设备)、_PS0/_PS3(电源状态控制)等方法。当 pciehp 执行断电操作时,会先调用 ACPI 方法确保平台级电源序列正确。
cat /sys/bus/pci/slots/<slot>/power 查看当前槽位电源状态。写入 0 或 1 可手动控制电源,用于测试状态机迁移是否正确。
4.4.3 运行时电源管理(RTPM)集成
pciehp 支持 PCIe 运行时电源管理。当槽位为空且无未处理事件时,驱动允许上游端口进入 D3cold 状态以节省功耗。关键实现点:
- 使用
pm_runtime_get_sync/pm_runtime_put_sync管理引用计数。 - 在
pciehp_ist处理事件前获取运行时引用,防止在事件处理过程中端口进入睡眠。 - 当槽位状态变为 HP_STATE_DISABLE 且无待处理事件时,释放引用,允许电源管理框架关闭端口电源。
/* pciehp 运行时电源管理示例代码片段 */
static irqreturn_t pciehp_isr(int irq, void *dev_id)
{
struct controller *ctrl = dev_id;
/* 获取运行时引用,防止端口在中断处理期间进入低功耗 */
pm_runtime_get_sync(&ctrl->pcie->port->dev);
/* ... 读取状态,提交工作 ... */
return IRQ_WAKE_THREAD;
}
static void pciehp_ist(struct work_struct *work)
{
struct controller *ctrl = container_of(work, struct controller, work);
/* 处理事件 */
/* ... */
/* 处理完成后释放引用 */
pm_runtime_put_sync(&ctrl->pcie->port->dev);
}
4.5 调试与验证
以下命令和接口可用于验证 pciehp 驱动状态:
| 调试接口 | 路径/命令 | 查看内容 |
|---|---|---|
| 槽位状态 | /sys/bus/pci/slots/<slot>/ | power、attention、latch、adapter 等属性 |
| 中断统计 | /proc/interrupts | pciehp 中断计数 |
| 驱动日志 | dmesg | grep pciehp | 状态迁移、错误信息 |
| 调试级别 | echo 'module pciehp +p' > /sys/kernel/debug/dynamic_debug/control | 启用动态调试输出 |
lspci -v 中对应 Root Port 的 HotPlug 能力标志是否出现。若未出现,可能是 BIOS 未使能热插拔或驱动未绑定。