PCIE那些事之linux枚举流程

本文详细介绍了PCIe设备在Linux系统中的枚举流程,基于深度优先DFS算法,从HOST主桥开始,逐级扫描并配置PCIe桥片,直至找到所有设备。枚举过程涉及配置空间更新、设备资源分配等关键步骤,适用于系统初始化、热插拔和应用层调用等情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

枚举流程原理

采用的是深度优先DFS遍历算法,中心规则是发现是桥设备继续遍历,直到遍历到最大的PCI总线,然后返回上级总线,继续遍历。
根据DFS算法首先走向红色路径遍历PCI桥片1->PCI桥片2->PCI桥片3
在每次遍历桥片的时候需要更新所属配置空间和设备的配置空间
当红色第一路桥片遍历完成之后发现蓝色2号路径有桥片则进行遍历更新其配置空间属性
在这里插入图片描述
1.HOST主桥扫描PCIe总线 0上的设备。系统软件首先忽略所有这条总线上的 PCIe设备,因为在这些设备之下不会挂接新的 PCIe总线。例如 PCIe设备 01下不可能挂接新的 PCIe总线。
2.HOST主桥首先发现PCIe桥1,并将 PCIe桥 1的 SecondaryBus命名为PCIe总线1。 系统软件将初始化 PCIe桥1的配置空间,将 PCIe桥1的 PrimaryBus Number寄存器赋值为 0,而将 Secondary Bus Number寄存器赋值为1,即 PCIe桥 1的上游 PCIe总线号为 0,而下游PCIe总线号为1。
3. 扫描 PCIe总线 1,发现 PCIe桥2,并将 PCIe桥 2的 SecondaryBus命名为PCIe总线2。系统软件将初 始化PCIe桥 2的配置空间,将 PCIe桥 2的 Primary Bus Number寄存器赋值为 1,而将SecondaryBusNumber寄存器赋值为2。
4.扫描 PCIe总线 2,发现 PCIe桥3,并将 PCIe桥 3的 SecondaryBus命名为PCIe总线3。系统软件将初始化PCIe桥 3的配置空间,将 PCIe桥 3的 Primary Bus Number寄存器赋值为 2,而将SecondaryBusNumber寄存器赋值为3
5.扫描PCIe总线3,没有发现任何PCIe桥,这表示PCIe总线3下不可能有新的总线,此时系统软件将PCIe桥3的SubordinateBusnumber寄存器赋值为3。系统软件在完成PCIe总线的扫描后,将回退到PCIe总线3的上一级总线,即PCIe总线2,继续进行扫描。
6.在重新扫描PCIe总线2时, 系统软件发现PCIe总线2上除了PCIe桥3之外没有发现新的PCIe桥,而PCIe桥 3之下的所有设备已经完成了扫描过程,此时系统软件将 PCIe桥2的 SubordinateBusnumber寄存器赋值为3。继续回退到PCIe总线1。 PCIe总线1上除了PCIe桥2外, 没有其他桥片, 于是继续回退到PCIe总线0, 并将PCIe桥 1的Subordinate Busnumber寄存器赋值为3。
7.在PCIe总线0上,系统软件扫描到PCIe桥4,则首先将PCIe桥4的PrimaryBusNumber寄存器赋值为0,而将SecondaryBusNumber寄存器赋值为4,即PCIe桥1的上游PCIe总线号为0,而下游PCIe总线号为4。
8.系统软件发现PCIe总线4上没有任何PCIe桥,将结束对PCIe总线4的扫描,并将PCIe桥 4的SubordinateBusnumber寄存器赋值为4, 之后回退到PCIe总线4的上游总线, 即PCIe总线0继续进行扫描。
9.系统软件发现在PCIe总线0上的两个桥片PCIe总线0和PCIe总线4都已完成扫描后,将结束对PCIe的扫描

linux枚举流程

linux 枚举的口函数为pci_rescan_bus,主要功能就是扫描设备,分配资源,并且使能设备
枚举操作发生在以下几种环境
1.host初始化的时候
2.热插拔的时候
3.应用层调用rescan的时候
echo 1 > rescan

通过hdr_type来判断是否是桥设备

static inline bool pci_is_bridge(struct pci_dev *dev)
{
   
	return dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
		dev->hdr_type == PCI_HEADER_TYPE_CARDBUS;
}

看一下该函数的实现方式主要调用pci_scan_child_bus扫描设备
然后调用pci_bus_add_devices运行PCI设备

 * pci_rescan_bus - scan a PCI bus for devices.
 * @bus: PCI bus to scan
 *
 * Scan a PCI bus and child buses for new devices, adds them,
 * and enables them.
 *
 * Returns the max number of subordinate bus discovered.
 */
unsigned int pci_rescan_bus(struct pci_bus *bus)
{
   
	unsigned int max;

	max = pci_scan_child_bus(bus);//扫描设备和总线 建立树状图
	pci_assign_unassigned_bus_resources(bus);
	pci_bus_add_devices(bus);//添加bus上的设备并且运行他们

	return max;
}
unsigned int pci_scan_child_bus(struct pci_bus *bus)
{
   
	unsigned int devfn, pass, max = bus->busn_res.start;
	struct pci_dev *dev;

	dev_dbg(&bus->dev, "scanning bus\n");

	/* Go find them, Rover! */ //遍历一条总线上的所有子总线,一条总线有32个接口,一个接口有8个子功能,所以这里只能以8递增*/
	for (devfn = 0; devfn < 0x100; devfn += 8)
		pci_scan_slot(bus, devfn);
       /*在遍历每一个接口,这里一个接口最多有八个function*/
    /*在这里,就把总线上的每一个设备都探测过了并加入到了bus对应的设备链表中,后面遍历还要用到*/		

//加上预留的总线号的数
	/* Reserve buses for SR-IOV capability. */
	max += pci_iov_bus_range(bus);

	/*
	 * After performing arch-dependent fixup of the bus, look behind
	 * all PCI-to-PCI bridges on this bus.
	 */
	 //PCI bus 是否需要补丁 进行修复 不重要
	if (!bus->is_added) {
   
		dev_dbg(&bus->dev, "fixups for bus\n");
		pcibios_fixup_bus(bus);
		bus->is_added = 1;
	}
//需要调用两次pci_scan_bridge 首先遍历被BOIS配置的总线设备 然后在遍历BOIS下面新增的总线设备*/
	for (pass = 0; pass < 2; pass++)
		list_for_each_entry(dev, &bus->devices, bus_list) {
   
			if (pci_is_bridge(dev))
				max = pci_scan_bridge(bus, dev, max, pass); /*遍历PCI桥*/
		}

	/*
	 * Make sure a hotplug bridge has at least the minimum requested
	 * number of buses.
	 */
	if (bus
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值