lspci命令输出的一些解释

最近一段时间在折腾虚拟化,想把SR-IOV硬件直通给用起来,所以免不了要利用lspci这个工具,用来查看当前系统连接的所有PCI/PCIe设备。其实之前也有用到过,也有一些不理解的地方,只是当时无脑跟着文档设置,也就没多关心了,这次需要好好的理解一下相关的概念什么的。

首先很简单,看看不加参数直接调用lspci命令的输出结果,下面的是我笔记本上的输出:

00:00.0 Host bridge: Intel Corporation Broadwell-U Host Bridge -OPI (rev 09)
00:02.0 VGA compatible controller: Intel Corporation HD Graphics 5500 (rev 09)
00:03.0 Audio device: Intel Corporation Broadwell-U Audio Controller (rev 09)
00:04.0 Signal processing controller: Intel Corporation Broadwell-U Processor Thermal Subsystem (rev 09)
00:14.0 USB controller: Intel Corporation Wildcat Point-LP USB xHCI Controller (rev 03)
00:16.0 Communication controller: Intel Corporation Wildcat Point-LP MEI Controller #1 (rev 03)
00:19.0 Ethernet controller: Intel Corporation Ethernet Connection (3) I218-LM (rev 03)
00:1b.0 Audio device: Intel Corporation Wildcat Point-LP High Definition Audio Controller (rev 03)
00:1c.0 PCI bridge: Intel Corporation Wildcat Point-LP PCI Express Root Port #1 (rev e3)
00:1c.3 PCI bridge: Intel Corporation Wildcat Point-LP PCI Express Root Port #4 (rev e3)
00:1c.4 PCI bridge: Intel Corporation Wildcat Point-LP PCI Express Root Port #5 (rev e3)
00:1d.0 USB controller: Intel Corporation Wildcat Point-LP USB EHCI Controller (rev 03)
00:1f.0 ISA bridge: Intel Corporation Wildcat Point-LP LPC Controller (rev 03)
00:1f.2 SATA controller: Intel Corporation Wildcat Point-LP SATA Controller [AHCI Mode] (rev 03)
00:1f.3 SMBus: Intel Corporation Wildcat Point-LP SMBus Controller (rev 03)
01:00.0 SD Host controller: O2 Micro, Inc. SD/MMC Card Reader Controller (rev 01)
02:00.0 Network controller: Intel Corporation Wireless 7265 (rev 59)

首先让人不好理解的地方就是最左边的一系列编号类似00:19.0这些,毕竟右边的设备名字供应商什么的还是很容易看懂的。那么最左边的这些编号代表什么意思呢?

在PCI或者说PCIe里,每个设备有三个编号:总线编号(Bus Number)、设备编号(Device Number)和功能编号(Function Number),那么对应上面的00:19.0来说:这个设备的BusNumber是00,DeviceNumber是19,FunctionNumber是0,需要说明的是这三个都是16进制表示,有些配置里需要填十进制数的,需要做下转换。

其实呢,在linux里还有一个编号,叫做DomainNumber,不过上面的输出中没有,因为都是0,所以就忽略了,理论上,一个Segment可以有256个Bus,每个Bus可以有32个Device,每个Device可以有8个Function,在一些场景下,比如设备特别多,是会有多个Domain的,在硬件层面对应多个PCI Segment,在这种情况下,或者使用lspci -D命令,输出就会带上DomainNumber,比如我们一台服务器的输出:

0002:e8:00.0 PCI bridge: ----- Co., Ltd. Device 1610 (rev 01)
0004:48:00.0 PCI bridge: ----- Co., Ltd. Device 1610 (rev 01)
0004:49:00.0 Serial Attached SCSI controller: LSI Logic / Symbios Logic SAS3008 PCI-Express Fusion-MPT SAS-3 (rev 02)
0005:00:00.0 PCI bridge: ----- Co., Ltd. Device 1610 (rev 01)
0006:08:00.0 PCI bridge: ----- Co., Ltd. Device 1610 (rev 01)
0007:40:00.0 PCI bridge: ----- Co., Ltd. Device 1610 (rev 01)
0007:41:00.0 VGA compatible controller: ----- Co., Ltd. [iBMC Intelligent Management system chip w/VGA support] (rev 01)
000a:10:00.0 PCI bridge: ----- Co., Ltd. Device 1610 (rev 01)
000c:20:00.0 PCI bridge: ----- Co., Ltd. Device 1610 (rev 01)
000d:30:00.0 PCI bridge: ----- Co., Ltd. Device 1610 (rev 01)

在前面多了一列,多出来的就是DomainNumber了。

明白了前面的编号之后,还有一个疑问,就是lspci命令是怎么能知道00:19.0这个设备就是个Ethernet controller,并且是Intel Corporation Ethernet Connection (3) I218-LM (rev 03)的呢?
这时可以使用lspci -nn命令,附加上原始的信息:

...
00:19.0 Ethernet controller [0200]: Intel Corporation Ethernet Connection (3) I218-LM [8086:15a2] (rev 03)
...

这里只用我笔记本的网卡举例了,可以看到和上面不同的是多了两个方括号括起来的编号,其中前面的[0200]表示了当前设备的DeviceClass,也就是设备类型,而后面的[8086:15a2]代表的就是设备的VendorID和DeviceID,lspci命令会默认尝试读取/usr/share/hwdata/pci.ids.gz/usr/share/hwdata/pci.ids文件,利用DeviceClass、VendorID:DeviceID去匹配相应的设备,这个设备清单是由https://pci-ids.ucw.cz/维护的,我们也可以直接去网站上去查询。

直接登录网站,可以查询到DeviceClass前两位02是一个Network controller,而后两位00表示这个设备是一个Ethernet controller,而8086这个VendorID,代码供应商是Intel Corporation(话说还真是应景啊,作为8086的创始者用8086这个ID),而DeviceID为15a2的设备为Ethernet Connection (3) I218-LM,组合一下,就和lspci的输出一致了。

具体到如何读取DeviceClass和VendorID:DeviceID,这里就不说了,可以参考一下后面的链接,简单来说,PCI总线上每个设备,都会对应一段内存地址,这个地址可以根据设备的编号直接计算出来,加上偏移量,就可以读到这些数据了。

参考:

  1. Interpreting the output of lspci: https://diego.assencio.com/?index=649b7a71b35fc7ad41e03b6d0e825f07
  2. 深入PCI与PCIe之一:硬件篇: https://zhuanlan.zhihu.com/p/26172972
  3. 深入PCI与PCIe之二:软件篇: https://zhuanlan.zhihu.com/p/26244141