lspci 详解 pci 拓扑结构 与 pci 树形结构

lspci 详解 pci 拓扑结构 与 pci 树形结构

一、PCIE 拓扑结构

硬盘是大家都很熟悉的设备,一路走来,从HDD到SSD,从SATA到NVMe,作为NVMe SSD的前端接口,PCIe再次进入我们的视野。作为x86体系关键的一环,PCIe标准历经PCI,PCI-X和PCIe,走过近30年时光。其中Host发现与查找设备的方式却一脉沿袭,今天我们先来聊一聊PCIe设备在一个系统中是如何发现与访问的。

首先我们来看一下在x86系统中,PCIe是什么样的一个体系架构。下图是一个PCIe的拓扑结构示例,PCIe协议支持256个Bus, 每条Bus最多支持32个Device,每个Device最多支持8个Function,所以由BDF(Bus,device,function)构成了每个PCIe设备节点的身份证号。

image-20240126145217327

PCIe体系架构一般由root complex,switch,endpoint等类型的PCIe设备组成,在root complex和switch中通常会有一些embeded endpoint(这种设备对外不出PCIe接口)。这么多的设备,CPU启动后要怎么去找到并认出它们呢? Host对PCIe设备扫描是采用了深度优先算法,其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。我们一般称这个过程为PCIe设备枚举。枚举过程中host通过配置读事物包来获取下游设备的信息,通过配置写事物包对下游设备进行设置。

二、PCIE 设备的遍历过程

第一步

PCI Host主桥扫描Bus 0上的设备(在一个处理器系统中,一般将Root complex中与Host Bridge相连接的PCI总线命名为PCI Bus 0),系统首先会忽略Bus 0上的embedded EP等不会挂接PCI桥的设备,主桥发现Bridge 1后,将Bridge1 下面的PCI Bus定为 Bus 1,系统将初始化Bridge 1的配置空间,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成0和1,以表明Bridge1 的上游总线是0,下游总线是1,由于还无法确定Bridge1下挂载设备的具体情况,系统先暂时将Subordinate Bus Number设为0xFF。
image-20240126150343936

第二步
系统开始扫描Bus 1,将会发现Bridge 3,并发现这是一个switch设备。系统将Bridge 3下面的PCI Bus定为Bus 2,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成1和2,和上一步一样暂时把Bridge 3 的Subordinate Bus Number设为0xFF。
image-20240126151400431

第三步
系统继续扫描Bus 2,将会发现Bridge 4。继续扫描,系统会发现Bridge下面挂载的NVMe SSD设备,系统将Bridge 4下面的PCI Bus定为Bus 3,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成2和3,因为Bus3下面挂的是端点设备(叶子节点),下面不会再有下游总线了,因此Bridge 4的Subordinate Bus Number的值可以确定为3。
image-20240126151454790

第四步
完成Bus 3的扫描后,系统返回到Bus 2继续扫描,会发现Bridge 5。继续扫描,系统会发现下面挂载的NIC设备,系统将Bridge 5下面的PCI Bus设置为Bus 4,并将该桥的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成2和4,因为NIC同样是端点设备,Bridge 5的Subordinate Bus Number的值可以确定为4。
image-20240126151522247

第五步
除了Bridge 4和Bridge 5以外,Bus2下面没有其他设备了,因此返回到Bridge 3,Bus 4是找到的挂载在这个Bridge下的最后一个bus号,因此将Bridge 3的Subordinate Bus Number设置为4。Bridge 3的下游设备都已经扫描完毕,继续向上返回到Bridge 1,同样将Bridge 1的Subordinate Bus Number设置为4。

image-20240126151548774

第六步
系统返回到Bus0继续扫描,会发现Bridge 2,系统将Bridge 2下面的PCI Bus定为Bus 5。并将Bridge 2的Primary Bus Number 和 Secondary Bus Number寄存器分别设置成0和5, Graphics card也是端点设备,因此Bridge 2 的Subordinate Bus Number的值可以确定为5。

至此,挂在PCIe总线上的所有设备都被扫描到,枚举过程结束,Host通过这一过程获得了一个完整的PCIe设备拓扑结构。

image-20240126151619817

系统上电以后,host会自动完成上述的设备枚举过程。除一些专有系统外,普通系统只会在开机阶段进行设备的扫描,启动成功后(枚举过程结束),即使插入一个PCIe设备,系统也不会再去识别它。

image-20240126151647492

PCI 总线域由 PCI 设备所能直接访问的地址空间组成。在一个处理器系统中,可能存在多个 HOST 主桥,因此也存在多个 PCI 总线域。在上图所示的处理器系统中,具有两个 HOST 主桥,因而在这个处理器系统中存在 PCI 总线 x 和 y 域。

在多数处理器系统中,分属于两个 PCI 总线域的 PCI 设备并不能直接进行数据交换,而需要通过 FSB 进行数据交换。值得注意的是,如果某些处理器的 HOST 主桥支持 Peer-to-Peer 数据传送,那么这个 HOST 主桥可以支持不同 PCI 总线域间的数据传送。

lspci 用于查看 linux 系统下面的pci设备。详细帮助可以通过命令 man lspci 获取详情。

[root@localhost ~]# lspci 
00:00.0 Host bridge: Intel Corporation 4th Gen Core Processor DRAM Controller (rev 06)
00:01.0 PCI bridge: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor PCI Express x16 Controller (rev 06)
00:02.0 VGA compatible controller: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller (rev 06)
00:03.0 Audio device: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller (rev 06)
00:14.0 USB controller: Intel Corporation 8 Series/C220 Series Chipset Family USB xHCI (rev 05)
00:16.0 Communication controller: Intel Corporation 8 Series/C220 Series Chipset Family MEI Controller #1 (rev 04)
00:19.0 Ethernet controller: Intel Corporation Ethernet Connection I217-LM (rev 05)
....

通过 lspci 命令,我们可以得知当前系统上所有的 pci 设备信息。当需要选择特定一个 pci 设备的时候,可以通过 lspci -s bdf 来指定,这个 bdf 就是 lspci 输出的前三列的内容。bdf 的全称其实就是 bus : device . func。

[root@nsa ~]# man lspci 
....
 Options for selection of devices
       -s [[[[<domain>]:]<bus>]:][<device>][.[<func>]]
....

根据本小节前面的内容,我们知道 bdf 最前面应该是该 pci 设备所在的 pci 总线域。由于大部分情况下一个系统只有一个 HOST 主桥,所以只有唯一的一个 pci 总线域,此时不需要区分总线域。

四、linux 系统下的 pci 设备目录

由第一、二小节我们知道,pci 的拓扑结构为:pci 桥(包括 HOST 主桥)下面可以延伸出一条 pci 总线,pci 总线上面挂着 pci 设备和 pci 桥。

我们可以通过 /sys/class/pci_bus/ 目录查看 linux 系统下有多少条 pci 总线。

[root@localhost ~]# ls /sys/class/pci_bus/
0000:00  0000:01  0000:02  0000:03  0000:04  0000:05  0000:06  0000:07  0000:08  0000:09

我们也可以通过 /sys/bus/pci/devices/ 目录查看 linux 系统下有多少个 pci 设备(以及相应的设备号 bdf)。

[root@localhost ~]# ls /sys/bus/pci/devices/
0000:00:00.0  0000:00:03.0  0000:00:19.0  0000:00:1c.0  0000:00:1f.0  0000:01:00.0  0000:02:0c.0  0000:04:00.0
0000:00:01.0  0000:00:14.0  0000:00:1a.0  0000:00:1c.1  0000:00:1f.3  0000:02:04.0  0000:02:10.0  0000:09:00.0
0000:00:02.0  0000:00:16.0  0000:00:1b.0  0000:00:1d.0  0000:00:1f.5  0000:02:08.0  0000:02:14.0

系统上电以后,host会自动完成上述的设备枚举过程。除一些专有系统外,普通系统只会在开机阶段进行设备的扫描。当我们安装一个 pci 设备驱动后,可以在 /sys/bus/pci/drivers 目录下找到我们安装的 pci 设备驱动模块。

[root@localhost ~]#  ls /sys/bus/pci/drivers
agpgart-intel  ata_generic  ehci-pci    i915    lpc_ich    ohci-pci   pci-stub  snd_hda_intel     xhci_hcd
agpgart-sis    ata_piix     hsw_uncore  igb     mei_me     pata_acpi  serial    uhci_hcd
agpgart-via    e1000e       i801_smbus  ioapic  mlx5_core  pcieport   shpchp    xen-platform-pci
[root@localhost ~]# 
[root@localhost ~]# ls /sys/bus/pci/devices/
0000:00:00.0  0000:00:03.0  0000:00:19.0  0000:00:1c.0  0000:00:1f.0  0000:01:00.0  0000:02:0c.0  0000:04:00.0
0000:00:01.0  0000:00:14.0  0000:00:1a.0  0000:00:1c.1  0000:00:1f.3  0000:02:04.0  0000:02:10.0  0000:09:00.0
0000:00:02.0  0000:00:16.0  0000:00:1b.0  0000:00:1d.0  0000:00:1f.5  0000:02:08.0  0000:02:14.0
[root@localhost platform]# insmod  nsa_dma.ko # 安装驱动模块
[root@localhost platform]# 
[root@localhost platform]# ls /sys/bus/pci/drivers  # 多出了一个 nsa_dma 驱动模块
agpgart-intel  ata_generic  ehci-pci    i915    lpc_ich    nsa_dma    pcieport  shpchp         xen-platform-pci
agpgart-sis    ata_piix     hsw_uncore  igb     mei_me     ohci-pci   pci-stub  snd_hda_intel  xhci_hcd
agpgart-via    e1000e       i801_smbus  ioapic  mlx5_core  pata_acpi  serial    uhci_hcd
[root@localhost platform]# ls /sys/bus/pci/devices/ # pci 设备数量不变,因为只会在开机阶段进行 pci 设备扫描
0000:00:00.0  0000:00:03.0  0000:00:19.0  0000:00:1c.0  0000:00:1f.0  0000:01:00.0  0000:02:0c.0  0000:04:00.0
0000:00:01.0  0000:00:14.0  0000:00:1a.0  0000:00:1c.1  0000:00:1f.3  0000:02:04.0  0000:02:10.0  0000:09:00.0
0000:00:02.0  0000:00:16.0  0000:00:1b.0  0000:00:1d.0  0000:00:1f.5  0000:02:08.0  0000:02:14.0
[root@localhost platform]# 

五、linux 系统下的 pci 树形结构

通过 lspci -tv 命令,我们可以获得当前 linux 系统下的 pci 设备树。

[root@localhost ~]# lspci -tv
-[0000:00]-+-00.0  Intel Corporation 4th Gen Core Processor DRAM Controller
           +-01.0-[01-07]----00.0-[02-07]--+-04.0-[03]--
           |                               +-08.0-[04]----00.0  Device 1ded:1020
           |                               +-0c.0-[05]--
           |                               +-10.0-[06]--
           |                               \-14.0-[07]--
           +-02.0  Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller
           +-03.0  Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller
           +-14.0  Intel Corporation 8 Series/C220 Series Chipset Family USB xHCI
           +-16.0  Intel Corporation 8 Series/C220 Series Chipset Family MEI Controller #1
           +-19.0  Intel Corporation Ethernet Connection I217-LM
           +-1a.0  Intel Corporation 8 Series/C220 Series Chipset Family USB EHCI #2
           +-1b.0  Intel Corporation 8 Series/C220 Series Chipset High Definition Audio Controller
           +-1c.0-[08]--
           +-1c.1-[09]----00.0  Intel Corporation I210 Gigabit Network Connection
           +-1d.0  Intel Corporation 8 Series/C220 Series Chipset Family USB EHCI #1
           +-1f.0  Intel Corporation C220 Series Chipset Family H81 Express LPC Controller
           +-1f.3  Intel Corporation 8 Series/C220 Series Chipset Family SMBus Controller
           \-1f.5  Intel Corporation 8 Series/C220 Series Chipset Family 2-port SATA Controller 2 [IDE mode]
[root@localhost ~]# 

面我们将结合第一、二小节的内容,解释一下 pci 设备树的结构。

1)
我们知道,只有 pci 桥(包含 HOST 主桥)下,可以新开一条 pci bus 总线。因此,pci 树形结构是以 pci 桥为连接点的。
我们可以通过下面的命令,获取当前 linux 系统下的 pci bus 总线情况:

[root@localhost platform]# ls /sys/class/pci_bus/
0000:00  0000:01  0000:02  0000:03  0000:04  0000:05  0000:06  0000:07  0000:08  0000:09
[root@localhost platform]# 

一共有10条 pci bus 总线,开头的 0000:00 就是 HOST主桥下的第一条总线了。

同理,还可以通过命令,获取当前 linux 系统下的 pci 设备号的情况:

[root@localhost platform]# ls /sys/bus/pci/devices/
0000:00:00.0  0000:00:03.0  0000:00:19.0  0000:00:1c.0  0000:00:1f.0  0000:01:00.0  0000:02:0c.0  0000:04:00.0
0000:00:01.0  0000:00:14.0  0000:00:1a.0  0000:00:1c.1  0000:00:1f.3  0000:02:04.0  0000:02:10.0  0000:09:00.0
0000:00:02.0  0000:00:16.0  0000:00:1b.0  0000:00:1d.0  0000:00:1f.5  0000:02:08.0  0000:02:14.0
[root@localhost platform]#

2)
如下图,红色方框中的就是 HOST主桥 bus 0 上的 pci 设备,包括 pci 终端设备和 pci 桥设备。可以看到,绿色方框中的 [00~03].0 ,其中 01.0 之后还有树形结构,所以 01.0 就是 pci 桥设备了;同时可以知道,pci 桥下面会有新开的 pci bus 总线,±01.0-[01-07] 后面的 [01-07] 就是 01.0 pci 桥下面挂接的 pci bus 总线了。 而 [00~03].0 另外三个设备下面没有树形结构,也没有后接 [ ] ,因此就是普通的 pci 终端设备。
image-20240126155651667

接下来,通过下图,可以得知 [00~03].0 对应的设备号,就是 0000:00:00.0,0000:00:01.0,0000:00:02.0,0000:00:03.0。

image-20240126155750306

为了验证我们的结论,我们通过命令查看 0000:00:00.0,0000:00:01.0,0000:00:02.0,0000:00:03.0 对应的设备信息:

[root@localhost platform]# ls /sys/bus/pci/devices/
0000:00:00.0  0000:00:03.0  0000:00:19.0  0000:00:1c.0  0000:00:1f.0  0000:01:00.0  0000:02:0c.0  0000:04:00.0
0000:00:01.0  0000:00:14.0  0000:00:1a.0  0000:00:1c.1  0000:00:1f.3  0000:02:04.0  0000:02:10.0  0000:09:00.0
0000:00:02.0  0000:00:16.0  0000:00:1b.0  0000:00:1d.0  0000:00:1f.5  0000:02:08.0  0000:02:14.0          
[root@localhost platform]# ls /sys/class/pci_bus/
0000:00  0000:01  0000:02  0000:03  0000:04  0000:05  0000:06  0000:07  0000:08  0000:09
[root@localhost platform]#
[root@localhost platform]# lspci -s 0000:00:00.0 -v # 0000:00:00.0 为 DRAM 控制器
00:00.0 Host bridge: Intel Corporation 4th Gen Core Processor DRAM Controller (rev 06)
        Subsystem: Intel Corporation 4th Gen Core Processor DRAM Controller
        Flags: bus master, fast devsel, latency 0
        Capabilities: [e0] Vendor Specific Information: Len=0c <?>
        Kernel driver in use: hsw_uncore

[root@localhost platform]# lspci -s 0000:00:01.0 -v  # 0000:00:01.0 为 pci 桥设备
00:01.0 PCI bridge: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor PCI Express x16 Controller (rev 06) (prog-if 00 [Normal decode])
        Flags: bus master, fast devsel, latency 0, IRQ 24
        Bus: primary=00, secondary=01, subordinate=07, sec-latency=0
        Memory behind bridge: f0000000-f41fffff
        Capabilities: [88] Subsystem: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor PCI Express x16 Controller
        Capabilities: [80] Power Management version 3
        Capabilities: [90] MSI: Enable+ Count=1/1 Maskable- 64bit-
        Capabilities: [a0] Express Root Port (Slot+), MSI 00
        Capabilities: [100] Virtual Channel
        Capabilities: [140] Root Complex Link
        Kernel driver in use: pcieport
        Kernel modules: shpchp

[root@localhost platform]# lspci -s 0000:00:02.0 -v  # 0000:00:02.0 为 VGA控制器
00:02.0 VGA compatible controller: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller (rev 06) (prog-if 00 [VGA controller])
        Subsystem: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller
        Flags: bus master, fast devsel, latency 0, IRQ 40
        Memory at f4400000 (64-bit, non-prefetchable) [size=4M]
        Memory at e0000000 (64-bit, prefetchable) [size=256M]
        I/O ports at f000 [size=64]
        Expansion ROM at <unassigned> [disabled]
        Capabilities: [90] MSI: Enable+ Count=1/1 Maskable- 64bit-
        Capabilities: [d0] Power Management version 2
        Capabilities: [a4] PCI Advanced Features
        Kernel driver in use: i915
        Kernel modules: i915

[root@localhost platform]# lspci -s 0000:00:03.0 -v  # 0000:00:03.0 为音频设备
00:03.0 Audio device: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller (rev 06)
        Subsystem: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller
        Flags: bus master, fast devsel, latency 0, IRQ 43
        Memory at f4934000 (64-bit, non-prefetchable) [size=16K]
        Capabilities: [50] Power Management version 2
        Capabilities: [60] MSI: Enable+ Count=1/1 Maskable- 64bit-
        Capabilities: [70] Express Root Complex Integrated Endpoint, MSI 00
        Kernel driver in use: snd_hda_intel
        Kernel modules: snd_hda_intel

[root@localhost platform]# 

可以看到,确实是 0000:00:01.0 为 pci 桥设备,其他三个为普通 pci 设备。

3)
我们可以进一步验证,1c.0 和 1c.1 是 pci 桥设备,其中 1c.0 下面挂接 08 总线, 1c.1 下面挂接 09 总线, 09 总线下面还有一个 pci 设备:0000:09:00.0。
image-20240126155928102

image-20240126155943598

命令验证 pci 设备信息:

[root@localhost platform]# lspci  -s 0000:00:1c.0 -v #  0000:00:1c.0 为 pci 桥设备
00:1c.0 PCI bridge: Intel Corporation 8 Series/C220 Series Chipset Family PCI Express Root Port #1 (rev d5) (prog-if 00 [Normal decode])
        Flags: bus master, fast devsel, latency 0, IRQ 25
        Bus: primary=00, secondary=08, subordinate=08, sec-latency=0
        I/O behind bridge: 00002000-00002fff
        Memory behind bridge: df200000-df3fffff
        Prefetchable memory behind bridge: 00000000df400000-00000000df5fffff
        Capabilities: [40] Express Root Port (Slot+), MSI 00
        Capabilities: [80] MSI: Enable+ Count=1/1 Maskable- 64bit-
        Capabilities: [90] Subsystem: Intel Corporation 8 Series/C220 Series Chipset Family PCI Express Root Port #1
        Capabilities: [a0] Power Management version 3
        Kernel driver in use: pcieport
        Kernel modules: shpchp

[root@localhost platform]# lspci  -s 0000:00:1c.1 -v  #  0000:00:1c.1 为 pci 桥设备
00:1c.1 PCI bridge: Intel Corporation 8 Series/C220 Series Chipset Family PCI Express Root Port #2 (rev d5) (prog-if 00 [Normal decode])
        Flags: bus master, fast devsel, latency 0, IRQ 26
        Bus: primary=00, secondary=09, subordinate=09, sec-latency=0
        I/O behind bridge: 0000e000-0000efff
        Memory behind bridge: f4800000-f48fffff
        Capabilities: [40] Express Root Port (Slot+), MSI 00
        Capabilities: [80] MSI: Enable+ Count=1/1 Maskable- 64bit-
        Capabilities: [90] Subsystem: Intel Corporation 8 Series/C220 Series Chipset Family PCI Express Root Port #2
        Capabilities: [a0] Power Management version 3
        Kernel driver in use: pcieport
        Kernel modules: shpchp

[root@localhost platform]# lspci  -s 0000:09:00.0 -v  # 0000:09:00.0 为 以太网控制器
09:00.0 Ethernet controller: Intel Corporation I210 Gigabit Network Connection (rev 03)
        Subsystem: DFI Inc Device 100a
        Flags: bus master, fast devsel, latency 0, IRQ 17
        Memory at f4800000 (32-bit, non-prefetchable) [size=512K]
        I/O ports at e000 [size=32]
        Memory at f4880000 (32-bit, non-prefetchable) [size=16K]
        Capabilities: [40] Power Management version 3
        Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+
        Capabilities: [70] MSI-X: Enable+ Count=5 Masked-
        Capabilities: [a0] Express Endpoint, MSI 00
        Capabilities: [100] Advanced Error Reporting
        Capabilities: [140] Device Serial Number 00-01-29-ff-ff-97-f7-48
        Capabilities: [1a0] Transaction Processing Hints
        Kernel driver in use: igb
        Kernel modules: igb

[root@localhost platform]# 

4)
我们可以留意到,±01.0-[01-07]----00.0-[02-07]–±04.0-[03]-- 这一长串,其实结合 pci 拓扑结构很容易理解。

一个 pci 桥设备只能新开一条 pci bus 总线,pci bus 总线上又可以连接 pci 桥设备,继而又新开 pci bus 总线。同时,pci 扫描采用深度优先算法,因此先扫描到的 pci bus 总线,就优先编号。

所以有如下结论:
±01.0-[01-07] :代表 0000:00 总线上的 0000:00:01.0 设备挂接 [01-07] 七条总线,其中 01 总线是与 0000:00:01.0 pci设备相连接的总线;

[01-07]----00.0-[02-07]: 01 总线下的设备 0000:01:00.0 又是一个 pci 桥设备,下面挂接 [02-07] 六条总线,其中 02 总线是与 0000:01:00.0 pci设备相连接的总线;

[02-07]–±04.0-[03]–
-----------±08.0-[04]----00.0 Device 1ded:1020
-----------±0c.0-[05]–
-----------±10.0-[06]–
------------14.0-[07]–
同理,02 总线下面挂接的都是pci 桥设备,pci 桥设备再新开新的 pci 总线。可以看到,02 总线上的设备 0000:02:08.0 挂接着 04 总线,04 总线上还挂接着 0000:04:00.0 pci 设备。
image-20240126160220372

[root@localhost platform]# lspci  -s  0000:04:00.0  -v
04:00.0 Serial controller: Device 1ded:1020 (prog-if 01 [16450])
        Subsystem: Xilinx Corporation Device 0007
        Flags: bus master, fast devsel, latency 0, IRQ 16
        Memory at f0000000 (32-bit, non-prefetchable) [size=64M]
        Memory at f4000000 (32-bit, non-prefetchable) [size=64K]
        Capabilities: [40] Power Management version 3
        Capabilities: [48] MSI: Enable- Count=1/32 Maskable- 64bit+
        Capabilities: [60] MSI-X: Enable+ Count=33 Masked-
        Capabilities: [70] Express Endpoint, MSI 00
        Capabilities: [100] Advanced Error Reporting
        Kernel driver in use: nsa_dma

[root@localhost platform]# 

posted @ 2024-01-26 16:03  寻梦99  阅读(1167)  评论(0编辑  收藏  举报