Linux PCIe设备枚举与资源分配调试 - 第4节
4.1 PCI 子系统核心数据结构总览
Linux 内核 PCI 子系统围绕三个核心结构体构建:pci_bus(总线)、pci_dev(设备)和 pci_driver(驱动)。它们共同构成了 PCI 设备枚举、资源分配和驱动绑定的基础框架。理解这三个结构体之间的关系,是掌握 PCIe 驱动开发的关键。
4.2 pci_bus 结构体:总线的抽象
pci_bus 是 Linux 内核中对一条 PCI 总线的软件抽象。它既代表物理上的 PCI 总线(如 Bus 0、Bus 1),也代表通过 PCIe 桥接器扩展出的逻辑总线。每个 pci_bus 实例维护了该总线的拓扑信息、资源分配状态以及子设备链表。
4.2.1 关键字段
| 字段名 | 类型 | 说明 |
|---|---|---|
number |
unsigned char | 总线号(Bus Number),由 PCI 配置空间分配 |
primary |
unsigned char | 主总线号(仅对桥设备有意义) |
secondary |
unsigned char | 从属总线号(桥下游总线) |
subordinate |
unsigned char | 最大从属总线号(桥可访问的总线范围上限) |
devices |
struct list_head | 挂接在该总线上的所有 pci_dev 链表头 |
children |
struct list_head | 子总线链表(通过 PCI 桥连接的下级总线) |
self |
struct pci_dev * | 指向代表该总线自身的桥设备(若为根总线则为 NULL) |
parent |
struct pci_bus * | 指向父总线(上级总线) |
resource |
struct resource * | 总线级别的资源(IO、MEM 地址空间) |
ops |
struct pci_ops * | 总线访问操作函数集(配置空间读写) |
number、secondary、subordinate 三个字段共同定义了该总线的地址范围。例如,一个 PCI 桥的 secondary=1、subordinate=3,表示桥下游管理 Bus 1~3 共三条总线。
4.2.2 总线树状拓扑
PCI 总线系统是一个树状结构:根总线(Root Bus)通常是 Bus 0,通过 PCI 桥(PCI-to-PCI Bridge)扩展出子总线。每个 pci_bus 通过 children 链表链接子总线,通过 devices 链表链接直接挂接的设备。
4.3 pci_dev 结构体:设备的抽象
pci_dev 是内核中对一个 PCI 功能(Function)的完整描述。每个 PCI 设备(无论是物理设备还是多功能设备中的某个功能)都对应一个 pci_dev 实例。该结构体包含了设备的配置空间信息、资源信息、电源管理状态以及驱动绑定状态。
4.3.1 关键字段
| 字段名 | 类型 | 说明 |
|---|---|---|
bus |
struct pci_bus * | 指向该设备所属的 pci_bus |
devfn |
unsigned int | 设备号(高 5 位 Device Number + 低 3 位 Function Number) |
vendor |
unsigned short | 厂商 ID(Vendor ID) |
device |
unsigned short | 设备 ID(Device ID) |
subsystem_vendor |
unsigned short | 子系统厂商 ID |
subsystem_device |
unsigned short | 子系统设备 ID |
class |
unsigned int | 类别代码(Class Code),包含 Base Class / Sub Class / Interface |
irq |
unsigned int | 中断号(INTx 或 MSI/MSI-X 分配后的中断号) |
resource[] |
struct resource[DEVICE_COUNT_RESOURCE] | 设备的 BAR 资源数组(最多 6 个 BAR + 扩展 ROM) |
driver |
struct pci_driver * | 指向当前绑定的 pci_driver(若未绑定则为 NULL) |
dev |
struct device | 内嵌的通用 device 结构体,用于驱动模型和 sysfs |
4.3.2 BDF 地址与 devfn
每个 PCI 设备在系统中的唯一标识是 BDF(Bus:Device.Function)地址。内核通过 pci_dev 的 bus->number 和 devfn 字段组合得到 BDF:
// BDF 地址计算
bus_number = pdev->bus->number; // 总线号
device_number = PCI_SLOT(pdev->devfn); // 设备号(高 5 位)
function_number = PCI_FUNC(pdev->devfn); // 功能号(低 3 位)
// 示例:BDF = 00:1f.3 表示 Bus 0, Device 31, Function 3
// devfn = (31 << 3) | 3 = 0xFB
4.4 pci_driver 结构体:驱动的抽象
pci_driver 是内核 PCI 子系统与具体设备驱动之间的接口。每个 PCI 设备驱动(如网卡驱动、NVMe 驱动)都需要注册一个 pci_driver 实例,通过 id_table 声明支持的设备,并通过 probe / remove 回调实现设备初始化和卸载。
4.4.1 关键字段
| 字段名 | 类型 | 说明 |
|---|---|---|
name |
const char * | 驱动名称,用于 sysfs 和调试 |
id_table |
const struct pci_device_id * | 驱动支持的设备 ID 列表(匹配 Vendor/Device/Class) |
probe |
int (*probe)(struct pci_dev *, const struct pci_device_id *) | 设备匹配成功后的初始化回调 |
remove |
void (*remove)(struct pci_dev *) | 设备移除或驱动卸载时的清理回调 |
suspend |
int (*suspend)(struct pci_dev *, pm_message_t) | 电源管理挂起回调(可选) |
resume |
int (*resume)(struct pci_dev *) | 电源管理恢复回调(可选) |
shutdown |
void (*shutdown)(struct pci_dev *) | 系统关机时的回调(可选) |
driver |
struct device_driver | 内嵌的通用 device_driver,用于驱动模型 |
4.4.2 pci_device_id 匹配表
pci_device_id 结构体定义了驱动与设备之间的匹配规则。内核在枚举设备时,会遍历所有已注册的 pci_driver,将设备的 Vendor ID、Device ID、Subsystem ID、Class Code 与 id_table 中的条目进行比对。
// pci_device_id 结构体定义
struct pci_device_id {
__u32 vendor, device; // 厂商 ID 和设备 ID
__u32 subvendor, subdevice; // 子系统厂商 ID 和设备 ID
__u32 class, class_mask; // 类别代码及掩码
unsigned long driver_data; // 驱动私有数据
};
// 示例:Intel I350 网卡驱动匹配表
static const struct pci_device_id igb_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x1521), 0 }, // I350 千兆网卡
{ PCI_VDEVICE(INTEL, 0x1522), 0 }, // I350 双口
{ PCI_VDEVICE(INTEL, 0x1523), 0 }, // I350 四口
{ 0, } // 结束标志
};
4.5 三者的协作流程
在 PCI 设备枚举过程中,三个结构体按照以下流程协同工作:
- 总线扫描:内核从根总线(pci_bus 0)开始,递归扫描所有 PCI 总线,发现每个设备/功能时创建
pci_dev实例。 - 设备注册:新创建的
pci_dev被添加到所属pci_bus的devices链表中,同时通过device_add()注册到内核驱动模型。 - 驱动匹配:内核遍历所有已注册的
pci_driver,调用pci_match_device()检查id_table是否匹配当前pci_dev。 - 驱动绑定:匹配成功后,内核调用
pci_driver的probe回调,并将pci_dev的driver指针指向该pci_driver。 - 资源分配:在 probe 过程中,驱动通过
pci_request_regions()等 API 获取并启用设备的 BAR 资源。
4.6 数据结构关系总结
下表从不同维度对比三个核心结构体的职责和关联:
| 对比维度 | pci_bus | pci_dev | pci_driver |
|---|---|---|---|
| 抽象对象 | PCI 总线(物理/逻辑) | PCI 功能(Function) | 设备驱动模块 |
| 生命周期 | 系统启动时创建,关机时销毁 | 枚举时创建,移除时销毁 | 模块加载时注册,卸载时注销 |
| 核心链表 | devices(设备链表) children(子总线链表) |
global_list(全局设备链表) bus_list(总线设备链表) |
driver_list(全局驱动链表) |
| 匹配机制 | 无(被动管理) | 通过 vendor/device/class 匹配 | 通过 id_table 声明支持 |
| sysfs 路径 | /sys/devices/pciXXXX:XX/ | /sys/devices/pciXXXX:XX/XXXX:XX:XX.X/ | /sys/bus/pci/drivers/<name>/ |
| 关键回调 | 无(由 PCI 核心管理) | 无(被动数据载体) | probe / remove / suspend / resume |
pci_driver 的注册和 probe 回调实现。内核的 PCI 核心层自动完成 pci_bus 的扫描和 pci_dev 的创建,驱动开发者无需手动管理总线拓扑。