PCI

概述

参考:

PCI device resources(PCI 设备资源) 由 Kernel 注册在 sysfs/sys/devices/pci${DOMAIN:BUS}/ 目录。每个 PCI 设备资源在该目录下都有一个以 唯一标识符(有的时候也称为 PCI Address) 命名的目录,格式为: DOMAIN:BUS:SLOT.FUNC(e.g. 0000:17:00.0)

  • DOMAIN(域) # 表示 PCI 域编号。用于识别主机系统中的不同 PCI 主机桥。在较早期的系统中,只有一个域编号为 0。随着系统规模扩大,可能存在多个 PCI 域。
  • BUS(总线) # 表示 PCI 总线编号(16 进制)。一个 PCI 域中可能包含多条 PCI 总线,每条总线都有一个唯一编号。
  • SLOT(插槽) # 表示 PCI 插槽编号。每条 PCI 总线上可以连接多个 PCI 设备,每个设备对应一个插槽编号。
    • 有的源码中也描述为 DEVICE。比如这里
  • FUNC(功能) # 表示 PCI 功能编号。一些 PCI 设备可能包含多个独立的功能,每个功能都有一个编号,用于区分和访问。对于单功能设备,该编号通常为 0。
    • 有的源码中也描述为 FUNCTION

通常来说,目录可能是像这样的:

/sys/devices/pci0000:17
|-- 0000:17:00.0
|   |-- class
|   |-- config
|   |-- device
|   |-- enable
|   |-- irq
|   |-- local_cpus
|   |-- remove
|   |-- resource
|   |-- resource0
|   |-- resource1
|   |-- resource2
|   |-- revision
|   |-- rom
|   |-- subsystem_device
|   |-- subsystem_vendor
|   `-- vendor
`-- ...

上面这个目录的例子描述的 PCI 设备在 Domain(域) 编号为 0000,Bus(总线) 编号为 17(HEX)。该 Bus 在 Slot(插槽) 0 中包含了 1 个单一功能的 device(设备)。

PCI 设备资源信息

参考:

PCI device resouorces(PCI 设备的资源) 数据以文件形式保存。PCI 设备资源信息 的储存目录中,通常包含如下文件

文件名功能
classPCI 设备的类型 ID (ascii, ro)。比如 网卡、USB、etc.
devicePCI 设备的型号 ID (ascii, ro)。类似设备型号,比如 I350 Gigabit Network 这种。设备 ID 基于供应商 ID,是在供应商 ID 之下的。
vendorPCI 设备的供应商 ID (ascii, ro)。各种厂家,比如 Intel、AMD、etc.
subsystem_devicePCI 设备的型号子系统 ID (ascii, ro)
subsystem_vendorPCI 设备的供应商子系统 ID (ascii, ro)
configPCI config space (binary, rw)
enableWhether the device is enabled (ascii, rw)
irqIRQ number (ascii, ro)
local_cpusnearby CPU mask (cpumask, ro)
removeremove device from kernel’s list (ascii, wo)
resourcePCI resource host addresses (ascii, ro)
resource0..NPCI resource N, if present (binary, mmap, rw)
resource0_wc..N_wcPCI WC map resource N, if prefetchable (binary, mmap)
revisionPCI revision (ascii, ro)
romPCI ROM resource, if present (binary, ro)

该目录中主要由如下几类文件

  • ro(只读) 类型文件 # 是信息性的,对它们的写入将被忽略,“rom”文件除外。可写文件可用于在设备上执行操作(例如更改配置空间、分离设备)。 mmapable 文件可通过偏移量 0 处的文件 mmap 获得,并可用于从用户空间进行实际设备编程。请注意,某些平台不支持某些资源的映射,因此请务必检查任何尝试的映射的返回值。其中最值得注意的是 I/O 端口资源,它也提供读/写访问
    • class、vendor、device、subsystem_vendor、subsystem_device # PCI 设备信息文件,文件内容是 HEX(16 进制) 的数字(i.e. 0x8086,0x 是 16 进制标识,具体数值是 8086),数字对应的含义可以参考 GitHub 项目,torvalds/linux - include/linux/pci_ids.h
      • 这些文件中的值分别对应 CLASS_ID、VENDOR_ID、DEVICE_ID、SBUVENDOR_ID、SUBDEVICE_ID
      • Tips: DEVICE_ID 都是包含在 VENDOR_ID 下的,理解为某个供应商下的某个设备比如 1521 这个 DEVICE_ID,可以在 8086 这个 VENDOR_ID 下找到,表示英特尔公司的 1350 Gigabit Network Connection 型号的网卡
  • enable 文件 # 提供了一个计数器,指示设备已启用的次数。如果“enable”文件当前返回“4”,并且回显“1”,则它将返回“5”。回显“0”会减少计数。但是,即使返回到 0,某些初始化也可能无法逆转。
  • rom 文件 # 的特殊之处在于它提供对设备 ROM 文件(如果可用)的只读访问。但默认情况下它是禁用的,因此应用程序应在尝试读取调用之前将字符串“1”写入文件以启用它,并在访问后通过将“0”写入文件来禁用它。请注意,必须启用设备才能读取 ROM 才能成功返回数据。如果驱动程序未绑定到设备,则可以使用上面记录的“enable”文件来启用它。
  • remove 文件 # 用于通过向文件写入非零整数来删除 PCI 设备。这不涉及任何类型的热插拔功能,例如关闭设备电源。该设备将从内核的 PCI 设备列表中删除,其 sysfs 目录也将被删除,并且该设备将从附加到它的任何驱动程序中删除。不允许移除 PCI 根总线。

目录中的其他文件

uevent # userspace event(用户空间事件,简称 uevent)

PCI 关联文件

[!Question] 如果有 pci.ids 文件,为啥还要自己定义 class、device、vendor、etc. 这些文件中的数字对应关系呢?是通过代码中的定义运行更效率?但是代码中的定义好像少。

看过下文之后,假如 DEVICE_ID 是 1521,在 pid.ids 文件中 https://admin.pci-ids.ucw.cz/read/PC/8086/1521, 但是 Linux 源码 https://github.com/torvalds/linux/blob/master/include/linux/pci_ids.h 这里找不到对应 DEVICE_ID 是 1521。也就是说有部分 PCI 设备信息在 pci.ids 文件中有,但是 Linxu 代码中没有

pci.ids # pci.ids 文件,包含所有已知 PCI ID(vendors(供应商)、devices(设备)、classes(类别)、subclasses(子类别))列表。可以称为 PCI ID 存储库

  • 不同的 Linux 发行版该文件所在位置不同,通常都是 /usr/share/XXX/pci.ids

.pciids-cache # 通过 DNS 查询模式获取到的所有 PCI ID 缓存文件

pci_ids.h # Linux 源码中的 PCI ID 与 名称 的对应关系

/sys/devices/pci${DOMAIN:BUS}/${DOMAIN:BUS:SLOT.FUNC}/ # PCI 设备资源信息储存目录。包含该 PCI 设备的各种信息,e.g. 供应商、PCI 类型、etc.

  • Note: 由于 PCI 是有层级的(e.g. /sys/devices/pci0000:00/0000:00:1c.1/0000:05:00.0/0000:06:00.0/0000:07:00.0/0000:08:00.0),所以每个层级的目录都有可能存在下面这些内容
  • /sys/bus/pci/devices/ # 可以在这里找到所有 PCI 设备的列表,该目录下的文件是以 PCI Addr 命名的软链接,指向 /sys/devices/pci${DOMAIN:BUS}/ 目录下的某个子目录。
    • Notes: 有的 PCI 层级可能比较多,比如这种 0000:08:00.0 -> ../../../devices/pci0000:00/0000:00:1c.1/0000:05:00.0/0000:06:00.0/0000:07:00.0/0000:08:00.0

pci.ids 文件

参考:

pci.ids 是 PCI 设备中使用的所有已知 ID 的公共存储库:供应商、设备、子系统和设备类别的 ID。它在各种程序(例如 PCI 实用程序)中用于显示完整的人类可读名称,而不是神秘的数字代码。

Note: pci.ids 数字都是 HEX(16 进制)

该文件由 pciutils 维护。在官方网站中可以在线查询,主要分为两大块查询

  • 查询 PCI Device ID
    • 每个 Device 都在某个 Vendor 下,比如 1521 这个 DEVICE_ID,可以在 8086 这个 VENDOR_ID 下找到,表示英特尔公司的 1350 Gigabit Network Connection 型号的网卡
    • 使用在线库查询时,先找到 VENDOR ID 点进去,再找 DEVICE ID,即可找到 设备的型号命名。
  • 查询 PCI Device 类型 ID

[!Question] 该文件的内容如何生成的?比如 /sys/devices/pci0000:00/0000:00:00.0/vendor 文件的值怎么来的?从 include/linux/pci.h 文件的提示中 For more information, please consult the following manuals (look at http://www.pcisig.com/ for how to get them) 合理猜测这些数据都是从 PCI SIG 定义的规范中获取到的?

下面的维护和更新由 AI 回答,待验证

维护和更新

维护机构

  • pci.ids 文件由 pciutils 项目维护,该项目的开发者和社区成员定期更新这个文件。
  • pciutils 项目由 Martin Mares 维护,托管在 https://github.com/pciutils/pciids

数据来源

  • 新的供应商 ID 和设备 ID 通常由硬件制造商提供。
  • 制造商向 PCI-SIG 注册他们的 ID。PCI-SIG 是管理和分配 PCI ID 的机构。
  • 社区成员和开发者通过手动添加新设备的信息到 pci.ids 文件中。

更新过程

  • 新的设备信息通过提交补丁的方式添加到 pciutils 项目的仓库中。
  • 开发者在 GitHub 上提交 Pull Request 或直接发送补丁给维护者。
  • 维护者审核并合并这些更改。
  • 合并后的更新会在 pciutils 项目的新版本中发布。

PCI I/O 虚拟化

https://github.com/torvalds/linux/blob/master/Documentation/PCI/pci-iov-howto.rst

DPDK 举例

~]# lshw -c net -businfo
Bus info          Device     Class          Description
=======================================================
pci@0000:02:00.0             network        Ethernet 10G 2P X520 Adapter
pci@0000:02:00.1             network        Ethernet 10G 2P X520 Adapter
pci@0000:01:00.0  eno3       network        I350 Gigabit Network Connection
pci@0000:01:00.1  eno4       network        I350 Gigabit Network Connection
~]#
~]# dpdk-devbind.py -s

Network devices using DPDK-compatible driver
============================================
0000:02:00.0 'Ethernet 10G 2P X520 Adapter 154d' drv=igb_uio unused=ixgbe,vfio-pci
0000:02:00.1 'Ethernet 10G 2P X520 Adapter 154d' drv=igb_uio unused=ixgbe,vfio-pci

Network devices using kernel driver
===================================
0000:01:00.0 'I350 Gigabit Network Connection 1521' if=eno3 drv=igb unused=igb_uio,vfio-pci
0000:01:00.1 'I350 Gigabit Network Connection 1521' if=eno4 drv=igb unused=igb_uio,vfio-pci
~]#
~]# find / -name 'sriov_numvfs'
/sys/devices/pci0000:00/0000:00:02.0/0000:02:00.0/sriov_numvfs
/sys/devices/pci0000:00/0000:00:02.0/0000:02:00.1/sriov_numvfs

通过 DPDK 管理的 PCI 设备可以找到 sriov_numvfs 文件

PCI 源码与实现规范

https://github.com/torvalds/linux/blob/v6.9/include/linux/pci.h#L322 - struct pci_dev 是存储 PCI 信息的结构体

include/linux/pci.h 文件的开头注释中有这么一段

 *	For more information, please consult the following manuals (look at
 *	http://www.pcisig.com/ for how to get them):
 *
 *	PCI BIOS Specification
 *	PCI Local Bus Specification
 *	PCI to PCI Bridge Specification
 *	PCI Express Specification
 *	PCI System Design Guide

可以查查这些规范里都有什么

  • PCI BIOS Specification
  • PCI Local Bus Specification
  • PCI to PCI Bridge Specification
  • PCI Express Specification
  • PCI System Design Guide

/sys/bus/ 下的 PCI

参考:

/sys/bus/pci/drivers/ # 管理 PCI 驱动。详见 Driver 的 PCI 章节

/sys/bus/pci/devices/ # 目录下是所有 PCI 设备指向 /sys/devices/ 的 Symbolic link

/sys/bus/pci/drivers_autoprobe #

/sys/bus/pci/drivers_probe #

/sys/bus/pci/rescan # 向该文件写入非零值将强制重新扫描系统中的所有 PCI 总线,并重新发现以前删除的设备。

/sys/bus/pci/resource_alignment #

/sys/bus/pci/slots/ #

/sys/bus/pci/uevent #

TODO: WSL 中的 网卡 PCI 总结


最后修改 August 28, 2024: pci, driver, bus (57bf1c0b)