【经验分享】面试必问的 PCIe 拓扑与 Bus 分配,今天用一张图给你讲得明明白白!
2026-06-18 17:58:41

近期有用户在使用我们的PCIe 6.0 switch卡连接Nvidia Mellanox CX系列网卡的时候问到一个关于PCIe 拓扑树(topology tree)显示的问题。我们知道,在 PCIe 系统和服务器硬件运维中, lspci 输出的树状图(Topology)是排查设备链路、拓扑结构和 Bus 号分配问题的核心工具。今天就来结合我们用户的这个实际问题,来详细拆解如何阅读 PCIe Tree、解答关于 Bus 号范围(Upstream/Downstream)看起来好像不一致的疑问,并盘点全球工程师最常用的 lspci 参数与经典实战案例。

一、 图解:如何阅读 lspci-t 拓扑树?

首先,我们用一个标准的、干净的 lspci-t 拓扑图范本来做教学:

拓扑图的基本阅读逻辑:

  1. 左侧根节点 -[0000:80]-:表示 PCIe Domain : Root Bus (根总线)。这里代表 Domain 为 0000,Root Bus 号是 80。通常对应 CPU 内部的 Host Bridge。
  2. 分支节点 +-04.1:代表在 Bus 80 上的 Device 04, Function 1(即 80:04.1)。这是一个 Root Port(根端口),本质上是一个 PCI-to-PCI 桥。
  3. 括号区间 -[81-94]:紧跟在桥设备后面的方括号,表示该桥所能控制/预留的子总线范围(Subordinate Bus Range)
  4. 横线连接 ----00.0:表示下一级总线上的设备。例如 -[81-94]----00.0 意味着,在进入次级总线后,遇到的第一个设备是 81:00.0

二、 核心疑问:为什么 Upstream 和 Downstream 的 Bus 号范围不一致?

在随附的截图中,有两处画红线的 Bus 范围区间:

  • 根端口下挂的范围是: -[81-94]
  • 后面紧跟的交换芯片/子桥下挂的范围是: -[82-93]

1. 为什么它们不一致?

这属于非常经典的 PCIe 桥的 Bus 资源分配与嵌套(Nested Bridges)

在 PCIe 规范中,每一个 PCI-to-PCI Bridge(包括 Root Port, Switch Upstream Port, Switch Downstream Port)在配置空间里都有三个关于 Bus 的寄存器:

  • Primary Bus Number:桥本身所处的上级总线号。
  • Secondary Bus Number:桥直接导出的下一级总线号。
  • Subordinate Bus Number:该桥下方所能延伸达到的最大(最后一级)总线号。

2. 对照原图的拓扑级联关系拆解:

  • 第一层(Root Port) 80:04.1
  • 它直接导出的下一级 Bus 是 81
  • 它能管辖的整个庞大子系统的最大范围是 81 到 94(共 20 个 Bus 号空间)。
  • 第二层(PCIe Switch 的 Upstream Port) 81:00.0
  • 它是由 Bus 81 上的设备导出的。
  • 它自身的次级 Bus 是 82
  • 重点: 它并没有把上级给的 94 全吞掉,它向上汇报自己管辖的最大范围是 82 到 93
  • 第三层(PCIe Switch 的 Downstream Ports)
  • 在 82-93 的内部,Switch 拆出了非常多的下游端口(Downstream Ports),例如 00.001.002.0 直到 10.0
  • 其中的 00.0 端口直接分配了 Bus 83 给网卡(Mellanox ConnectX-6 Dx),而网卡拥有两个 Function( 00.0 和 00.1)。
  • 剩下的 01.0 分配了 Bus 84, 02.0 分配了 Bus 85……以此类推,直到 10.0 分配了 Bus 93

3. 为什么空出了 Bus 94?

你会发现 Root Port 批了 81-94,但底下的 Switch 只用了 82-9394 消失了

这是因为 BIOS/系统内核在进行 PCIe 热插拔预留(Hotplug Reservation)。系统为了防止用户以后在这个 Root Port 附近插入新的扩展卡或切分设备,故意在最外层(Root Port)多留了一个 94 号总线。如果未来有新硬件插入,可以直接将 94 分配给新硬件,而不需要重新打乱、刷新整个 81-93 已经分配好的现有拓扑结构。

三、 全球最常用 lspci 命令与实战案例

以下是根据全球系统工程师和内核开发者的使用频度,整理出的最常用 lspci 参数及其实战解决的问题:

常用命令组合全球频度核心作用描述
lspci
⭐⭐⭐⭐⭐
快速列出所有 PCI 设备简要信息。
lspci-tv
⭐⭐⭐⭐☆
带有设备详细名称的树状拓扑图显示。
lspci-vvv
⭐⭐⭐⭐⭐
打印极其详细的硬件底层配置空间及链路状态。
lspci-k
⭐⭐⭐⭐☆
显示每个 PCI 设备当前绑定的内核驱动和可选模块。
lspci-nn
⭐⭐⭐⭐☆
同时显示设备的文字名称和数字化的 Vendor ID / Device ID。
lspci-s<BDF>
⭐⭐⭐⭐⭐
精准过滤并只查看指定 BDF 地址的设备。

经典实战案例佐证

案例一:排查设备是否降速(物理链路劣化)
  • 命令: lspci-vvv-s83:00.0|grep-E"LnkCap|LnkSta"
  • 为什么要用:

    当你在测试 Mellanox 网卡或 NVMe 盘时,发现性能死活跑不满(例如原本 100G 的网卡只能跑 50G)。

  • 解决什么问题:

    -vvv 可以 dump 出 PCIe 能力寄存器。 LnkCap(Link Capability)代表硬件设计最高支持的能力(如 Speed16GT/s,Widthx16,即 PCIe 4.0 x16)。而 LnkSta(Link Status)代表当前实际运行的状态。如果实际状态显示 Speed8GT/s,Widthx8,说明硬件可能由于金手指脏污、机箱震动或信号干扰,在物理层发生了解宽和降速。使用该命令能一秒定位物理链路故障。

案例二:硬件无法识别,需要找驱动或提报 Bug
  • 命令: lspci-nn|grep-i mellanox
  • 为什么要用:

    当系统里装了一块新网卡或加速卡,操作系统无法识别它(显示为 Unknowndevice),或者你在写自动化脚本、需要精确匹配芯片型号。

  • 解决什么问题:

    标准的 lspci 只输出人能看懂的文本(依赖本地的 pci.ids 数据库)。如果数据库没更新,就看不到名字。加上 -nn 后,会强制输出十六进制的数字,例如 [15b3:101d](15b3 是 Mellanox 厂商 ID,101d 是设备 ID)。有了这个绝对唯一的数字 ID,工程师可以直接去谷歌查到该芯片的官方型号,或者直接写进内核驱动的 match_table 里去加载驱动。

案例三:排查网卡、GPU 到底用的是哪个驱动
  • 命令: lspci-k-s83:00.0
  • 为什么要用:

    在配置网络 DPDK 高性能转发或者安装 NVIDIA/AMD 显卡驱动时,经常需要确认当前设备是被内核自带的开源驱动(如 nouveau、 mlx5_core)接管,还是被用户态驱动(如 vfio-pci)接管。

  • 解决什么问题:

    使用 -k 参数会明确输出两行:

    Kerneldriverinuse:mlx5_core (当前正在生效的驱动)

    Kernelmodules:mlx5_core (系统里可供选择的驱动模块)

    这能让装驱动、切驱动的运维过程变得极其透明,防止“驱动没装上”或“驱动冲突”导致的各种玄学罢工。

更多PCIe5&6.0, CXL, NVMe SSD, SAS/SATA, NVMe over Fabric (NVMoF), NAND,新型存储技术NVM(RRAM/ReRAM, FRAM/FeRAM, MRAM, PCM, 3D-NOR, SRAM/DRAM等) DDR5/LPDDR5以及UFS测试方面的问题想咨询,可以查看Saniffer公司2026.2.24最新更新的测试工具白皮书15.1版本,我们已经整理收录在Saniffer公众号的【白皮书】菜单中。

欢迎关注Saniffer公众号,点击底部菜单栏即可免费获取。如有任何技术问题,也可直接在公众号内留言交流。