Linux 6.6内核 PCIe 深度解析(十):PCIe Port Driver(portdrv)— 一个物理 Port,拆出五个逻辑服务

〇、全景:portdrv 是"一个物理设备拆成多个逻辑设备"的桥接层

前面几篇里反复出现的 AER(#3)、pciehp(#5)、DPC、PME------它们都有一个共同的宿主:PCIe Port(Root Port / Switch Port) 。这些功能不是各自独立的设备,而是同一个物理 Port 身上承担的多种职责。但 Linux 驱动模型是"一个设备配一个驱动",一个 Port 身上要同时跑"错误上报 + 热插拔 + 电源事件 + 带宽通知 + DPC 隔离"五件事,怎么拆?

答案是 PCIe Port Driver(portdrv) :它不枚举新硬件,而是把一个已有的 Port pci_dev,按它支持的 capability,拆成最多 5 个"软件构造"的逻辑设备(struct pcie_device,每个挂一个 service driver。
#mermaid-svg-Z1L4WTW1cO3Rkjcj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .error-icon{fill:#552222;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .marker.cross{stroke:#333333;}#mermaid-svg-Z1L4WTW1cO3Rkjcj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Z1L4WTW1cO3Rkjcj p{margin:0;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .cluster-label text{fill:#333;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .cluster-label span{color:#333;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .cluster-label span p{background-color:transparent;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .label text,#mermaid-svg-Z1L4WTW1cO3Rkjcj span{fill:#333;color:#333;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .node rect,#mermaid-svg-Z1L4WTW1cO3Rkjcj .node circle,#mermaid-svg-Z1L4WTW1cO3Rkjcj .node ellipse,#mermaid-svg-Z1L4WTW1cO3Rkjcj .node polygon,#mermaid-svg-Z1L4WTW1cO3Rkjcj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .rough-node .label text,#mermaid-svg-Z1L4WTW1cO3Rkjcj .node .label text,#mermaid-svg-Z1L4WTW1cO3Rkjcj .image-shape .label,#mermaid-svg-Z1L4WTW1cO3Rkjcj .icon-shape .label{text-anchor:middle;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .rough-node .label,#mermaid-svg-Z1L4WTW1cO3Rkjcj .node .label,#mermaid-svg-Z1L4WTW1cO3Rkjcj .image-shape .label,#mermaid-svg-Z1L4WTW1cO3Rkjcj .icon-shape .label{text-align:center;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .node.clickable{cursor:pointer;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .arrowheadPath{fill:#333333;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Z1L4WTW1cO3Rkjcj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Z1L4WTW1cO3Rkjcj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Z1L4WTW1cO3Rkjcj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .cluster text{fill:#333;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .cluster span{color:#333;}#mermaid-svg-Z1L4WTW1cO3Rkjcj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Z1L4WTW1cO3Rkjcj rect.text{fill:none;stroke-width:0;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .icon-shape,#mermaid-svg-Z1L4WTW1cO3Rkjcj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .icon-shape p,#mermaid-svg-Z1L4WTW1cO3Rkjcj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .icon-shape .label rect,#mermaid-svg-Z1L4WTW1cO3Rkjcj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Z1L4WTW1cO3Rkjcj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Z1L4WTW1cO3Rkjcj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Z1L4WTW1cO3Rkjcj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} portdrv 二级总线模型(本片重点)
PCIe 物理拓扑(配置空间枚举)
probe 时被 portdrv 接管
Root Complex(RC)
Root Port(RP)
Switch
Upstream Port
Downstream Port(DN)
Endpoint(EP)
struct pci_dev

(物理 Port,挂在 pci_bus_type)
portdrv 拆出最多 5 个

struct pcie_device(挂在 pcie_port_bus_type)
PME 服务
AER 服务
Hotplug 服务
DPC 服务
Bandwidth 服务

一句话主线:portdrv 是一个 pci_driver(名字就叫 pcieport),它 match 所有 PCIe Port(Root Port / Switch Port / RCEC),probe 时读配置空间探测出这个 Port 支持哪些 service → 为每个 service 分配中断 → 用 pcie_device_init 造一个逻辑设备挂到 pcie_port_bus_type 上 → 再由各 service driver(pcie_port_service_register 注册)绑定 probe


一、概念先行:为什么需要 portdrv

1.1 PCIe Port 是什么

在 PCIe 拓扑里,除了 Endpoint(终端设备),还有一类"中间设备"------Port 。它们本身是 PCI-to-PCI bridge(class 0x0604),负责把 bus 连成树。Port 的类型由 PCIe Capability(PCI_EXP_FLAGS,偏移 0x02)里的 Device/Port Type 字段PCI_EXP_FLAGS_TYPE,bits 7:4)决定:

c 复制代码
// include/uapi/linux/pci_regs.h (v6.6, line 473-484)
#define PCI_EXP_FLAGS		0x02	/* Capabilities register */
#define  PCI_EXP_FLAGS_TYPE	0x00f0	/* Device/Port type */
#define   PCI_EXP_TYPE_ENDPOINT	   0x0	/* Express Endpoint */
#define   PCI_EXP_TYPE_LEG_END	   0x1	/* Legacy Endpoint */
#define   PCI_EXP_TYPE_ROOT_PORT   0x4	/* Root Port */
#define   PCI_EXP_TYPE_UPSTREAM	   0x5	/* Upstream Port */
#define   PCI_EXP_TYPE_DOWNSTREAM  0x6	/* Downstream Port */
#define   PCI_EXP_TYPE_PCI_BRIDGE  0x7	/* PCIe to PCI/PCI-X Bridge */
#define   PCI_EXP_TYPE_PCIE_BRIDGE 0x8	/* PCI/PCI-X to PCIe Bridge */
#define   PCI_EXP_TYPE_RC_END	   0x9	/* Root Complex Integrated Endpoint */
#define   PCI_EXP_TYPE_RC_EC	   0xa	/* Root Complex Event Collector */

内核读这个字段的入口是 pci_pcie_type()

c 复制代码
// include/linux/pci.h (v6.6, line 2427-2430)
static inline int pci_pcie_type(const struct pci_dev *dev)
{
	return (pcie_caps_reg(dev) & PCI_EXP_FLAGS_TYPE) >> 4;
}

portdrv 只关心这四类 Port(其余类型不是 portdrv 的菜):

Port 类型 拓扑位置 portdrv 是否接管
Root Port(RP) 0x4 RC 直连,树根到第一层 ✅ 核心对象
Upstream Port(UP) 0x5 Switch 朝上(朝 RC)的一侧 ✅ 接管
Downstream Port(DN) 0x6 Switch 朝下(朝 EP)的一侧 ✅ 接管
RCEC 0xa RC 内部的事件收集器(非 bridge) ✅ 特殊接管
Endpoint / Legacy EP 0x0/0x1 终端设备 ❌ 不是 portdrv 对象
PCIe-to-PCI Bridge 0x7 桥接传统 PCI ❌ 不接管

注意:Upstream Port 也被 portdrv 接管pcie_portdrv_probePCI_EXP_TYPE_UPSTREAM 是合法匹配),但它身上能提供的 service 极少------见第三节,五个 service 的 Port 类型条件(RP/DN/RCEC)UP 都不满足,所以 get_port_device_capability 对 UP 通常返回 0,一个 service 设备都不创建。别把 UP 和 RP 搞混。

1.2 一个 Port 的多种职责,怎么装进"一设备一驱动"的模型

一个 Root Port 身兼多职:

职责 硬件载体 对应 service
电源事件上报(PME) Root Status 寄存器的 PME 位 + PMEIE 使能 PCIE_PORT_SERVICE_PME
高级错误上报(AER) AER Capability 的 Root Status PCIE_PORT_SERVICE_AER
热插拔 Slot Capability(SLTCAP 的 HPC 位) PCIE_PORT_SERVICE_HP
错误隔离(DPC) DPC Extended Capability PCIE_PORT_SERVICE_DPC
带宽变化通知 Link Capability 的 LBNC 位 PCIE_PORT_SERVICE_BWNOTIF

这些都是同一个物理设备 的不同侧面。如果按传统方式给这个 Port 写一个巨大的驱动,内部就得自己管理"哪部分能力存在、各自的中断是什么"------又乱又难维护。portdrv 的思路是把 Port 抽象成一条总线,让每个 service 变成总线上的一个独立设备,各自写一个独立、干净的 service driver。

1.3 二级总线模型:pci_bus_type → pcie_port_bus_type

这是 portdrv 的灵魂。内核里出现了两层总线

复制代码
pci_bus_type(物理总线,driver core 原生)
   └── struct pci_dev            ← 物理 Port(Root Port / Switch Port),被 pcieport 驱动绑定
            │
            └── pcie_port_bus_type(逻辑总线,portdrv 自定义)
                     └── struct pcie_device  ← 逻辑 service 设备,被各 service driver 绑定
                     └── struct pcie_device  ← 另一个 service ...

pcie_port_bus_type 声明在 portdrv.h:99,真正定义在 pci-driver.c:1717.name = "pci_express"),它和 pci_bus_type两条完全不同的总线 。service driver 注册到 pcie_port_bus_type(而不是 pci_bus_type),所以它们的 probe 拿到的是 struct pcie_device(而不是 struct pci_dev)------这正是 #5 pciehp 篇里"pciehp 是 portdrv 的 service driver,挂 pcie_port_bus_type 而非 pci_bus_type"那句话的由来。


二、两大核心结构

portdrv 就靠两个结构支撑整个框架:一个描述"逻辑服务设备"(pcie_device),一个描述"服务驱动"(pcie_port_service_driver)。

2.1 struct pcie_device --- 软件构造的"伪设备"

c 复制代码
// drivers/pci/pcie/portdrv.h (v6.6, line 57-64)
struct pcie_device {
	int		irq;	    /* Service IRQ/MSI/MSI-X Vector */
	struct pci_dev *port;	    /* Root/Upstream/Downstream Port */
	u32		service;    /* Port service this device represents */
	void		*priv_data; /* Service Private Data */
	struct device	device;     /* Generic Device Interface */
};
#define to_pcie_device(d) container_of(d, struct pcie_device, device)
  • port回指物理 Port 。service driver 所有对硬件的操作,最终都是通过 srv->port 这个 pci_dev 去读配置空间。
  • irq:这个 service 分到的中断向量(不同 service 可能不同,见第四节)。
  • service:一个 bit,标识这是哪个 service(PCIE_PORT_SERVICE_PME 等)。
  • device:内嵌的通用 struct device,用来挂到 pcie_port_bus_type 上、参与 driver core 的 match/probe。
  • priv_data:service driver 的私有数据指针(set_service_data / get_service_data 存取),典型用来存 struct pcie_pme_service_data 这类驱动私有结构。

2.2 struct pcie_port_service_driver --- 服务驱动的"外壳"

c 复制代码
// drivers/pci/pcie/portdrv.h (v6.6, line 76-94)
struct pcie_port_service_driver {
	const char *name;
	int (*probe)(struct pcie_device *dev);
	void (*remove)(struct pcie_device *dev);
	int (*suspend)(struct pcie_device *dev);
	int (*resume_noirq)(struct pcie_device *dev);
	int (*resume)(struct pcie_device *dev);
	int (*runtime_suspend)(struct pcie_device *dev);
	int (*runtime_resume)(struct pcie_device *dev);

	int (*slot_reset)(struct pcie_device *dev);

	int port_type;  /* Type of the port this driver can handle */
	u32 service;    /* Port service this device represents */

	struct device_driver driver;
};
#define to_service_driver(d) \
	container_of(d, struct pcie_port_service_driver, driver)

两个关键字段:

  • service :这个驱动服务哪个 service(例如 PCIE_PORT_SERVICE_AER)。这是 match 的关键------portdrv 靠它把某个 pcie_device(service= AER)和 aerdriver(service= AER)配对。
  • port_type :这个驱动只处理哪种 Port。多数 service driver 填 PCIE_ANY_PORT(~0,任何 Port 都行),然后在 probe 里再自己判断;少数会限定类型。

port_type 别和 1.1 的 PCI_EXP_TYPE_* 混淆:它在这里是可选的 Port 类型过滤条件 (填 PCIE_ANY_PORT 表示不限制),真正的 match 主键是 service。具体 match 逻辑在 pcie_port_bus_matchpci-driver.c:1696):

c 复制代码
// drivers/pci/pci-driver.c (v6.6, line 1696-1715)
static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
{
	struct pcie_device *pciedev;
	struct pcie_port_service_driver *driver;

	if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
		return 0;

	pciedev = to_pcie_device(dev);
	driver = to_service_driver(drv);

	if (driver->service != pciedev->service)   /* ① service 必须相等 */
		return 0;

	if (driver->port_type != PCIE_ANY_PORT &&  /* ② port_type 可选过滤 */
	    driver->port_type != pci_pcie_type(pciedev->port))
		return 0;

	return 1;
}

struct bus_type pcie_port_bus_type = {   /* 定义在 pci-driver.c:1717 */
	.name		= "pci_express",
	.match		= pcie_port_bus_match,
};

match 就两个条件:service 必须相等 (主匹配键);② 若驱动填了具体的 port_type,则要求 Port 类型也一致PCIE_ANY_PORT 表示跳过这条)。

2.3 对比:pcie_device/pcie_port_service_driver vs pci_dev/pci_driver

物理层 逻辑层(portdrv)
设备结构 struct pci_dev struct pcie_device
驱动结构 struct pci_driver struct pcie_port_service_driver
所在总线 pci_bus_type pcie_port_bus_type
probe 入参 struct pci_dev * struct pcie_device *
注册函数 pci_register_driver pcie_port_service_register
谁创建设备 PCI 枚举(真实硬件) portdrv(pcie_device_init 软件构造)

三、服务发现:get_port_device_capability

portdrv probe 一个 Port 后,第一步是读配置空间,判断这个 Port 到底支持哪些 service 。这个函数是 get_port_device_capabilityportdrv.c:217),返回一个 bitmask。

3.1 五个 service 的启用条件(核心对比表)

c 复制代码
// drivers/pci/pcie/portdrv.c (v6.6, line 217-277)  摘要
static int get_port_device_capability(struct pci_dev *dev)
{
	struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
	int services = 0;

	/* HP:hotplug bridge + (RP 或 DN) + native */
	if (dev->is_hotplug_bridge &&
	    (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
	     pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) &&
	    (pcie_ports_native || host->native_pcie_hotplug))
		services |= PCIE_PORT_SERVICE_HP;

	/* AER:RP 或 RCEC,有 aer_cap,AER 可用,native */
	if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
	     pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) &&
	    dev->aer_cap && pci_aer_available() &&
	    (pcie_ports_native || host->native_aer))
		services |= PCIE_PORT_SERVICE_AER;

	/* PME:RP 或 RCEC,native */
	if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
	     pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) &&
	    (pcie_ports_native || host->native_pme))
		services |= PCIE_PORT_SERVICE_PME;

	/* DPC:有 DPC cap,AER 可用,dpc-native 或已有 AER */
	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) &&
	    pci_aer_available() &&
	    (pcie_ports_dpc_native || (services & PCIE_PORT_SERVICE_AER)))
		services |= PCIE_PORT_SERVICE_DPC;

	/* BWNOTIF:RP 或 DN,Link Cap 有 LBNC 位 */
	if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
	    pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
		u32 linkcap;
		pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &linkcap);
		if (linkcap & PCI_EXP_LNKCAP_LBNC)
			services |= PCIE_PORT_SERVICE_BWNOTIF;
	}
	return services;
}

把五个 service 的启用条件抽出来对比,能看清 portdrv 的"门禁"逻辑:

service 需要的 Port 类型 硬件前提 权限前提
HP(热插拔) RP 或 DN is_hotplug_bridge(SLTCAP 的 HPC 位) pcie_ports_nativenative_pcie_hotplug
AER RP 或 RCEC 有 AER cap(dev->aer_cap native_aer + pci_aer_available()
PME RP 或 RCEC (Root Status 天然有) native_pme
DPC 任意 Port(有 DPC cap 即可) PCI_EXT_CAP_ID_DPC(0x1D) pcie_ports_dpc_native 或「已有 AER」
BWNOTIF RP 或 DN LNKCAP 的 LBNC 位 无额外权限要求

几个值得注意的点:

  1. AER / PME 只挂在 RP 和 RCEC 上------因为只有 Root Port 才会"汇总"整条链路的错误/电源事件(Switch 的 Upstream/Downstream Port 不承担这个汇总职责)。
  2. HP 要求 RP 或 DNis_hotplug_bridge 只在这两类 Port 上有意义),且还要"native"放权。
  3. DPC 最特殊 :它的启用条件是 pcie_ports_dpc_native services & PCIE_PORT_SERVICE_AER」,即只要 AER 已经启用,DPC 就自动跟上 (源码注释:With dpc-native, allow Linux to use DPC even if it doesn't have permission to use AER)。因为 AER 和 DPC 常配合使用------AER 负责"报告错误",DPC 负责"隔离错误"。另外 DPC 全称 Downstream Port Containment,规范上就是给 Downstream Port 的机制,所以代码虽不显式检查 Port 类型,实际只有 Downstream Port 才带 DPC capability。
  4. BWNOTIF 无权限门:只要硬件支持(LBNC 位)就启用,不需要 native 授权。

3.2 native vs 固件:_OSC 协商决定"谁拥有这个功能"

上面的"权限前提"列反复出现 native_pcie_hotplug / native_aer / native_pme,这些 flag 来自 ACPI 的 _OSC 协商

Linux 启动时,ACPI PCI root 驱动会通过 _OSC(Operating System Capabilities)方法跟固件谈判:"这些 PCIe 功能我想自己管,你放不放权?":

c 复制代码
// drivers/acpi/pci_root.c (v6.6, line 134-140)
{ OSC_PCI_EXPRESS_NATIVE_HP_CONTROL, "PCIeHotplug" },
{ OSC_PCI_EXPRESS_PME_CONTROL, "PME" },
{ OSC_PCI_EXPRESS_AER_CONTROL, "AER" },
{ OSC_PCI_EXPRESS_CAPABILITY_CONTROL, "PCIeCapability" },
{ OSC_PCI_EXPRESS_LTR_CONTROL, "LTR" },
{ OSC_PCI_EXPRESS_DPC_CONTROL, "DPC" },

协商结果记在 root->osc_control_set,然后落到 host bridge 的 native flag:

c 复制代码
// drivers/acpi/pci_root.c (v6.6, line 1037-1048)
if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
	host_bridge->native_pcie_hotplug = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL))
	host_bridge->native_aer = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL))
	host_bridge->native_pme = 0;
...

为什么要有这道门 :PCIe 的这些高级功能(热插拔、AER、PME)历史上常由固件(BIOS/UEFI)通过 SMM/ACPI 方法管理,而不是 OS。如果 OS 和固件同时 管同一个功能,会打架(两边都去清同一个状态位、都去响应同一个中断)。所以规范定了 _OSC 来协商所有权 :固件放权给 OS,OS 才启用 native service;否则 Linux 就不碰这些功能(热插拔交给固件的 acpiphp,AER/PME 也不注册 native service)。

如果用户想强制 native(不管 _OSC 结果),可以用内核启动参数 pcie_ports=native

c 复制代码
// drivers/pci/pcie/portdrv.c (v6.6, line 616-627)
static int __init pcie_port_setup(char *str)
{
	if (!strncmp(str, "compat", 6))
		pcie_ports_disabled = true;   /* 完全禁用 native service */
	else if (!strncmp(str, "native", 6))
		pcie_ports_native = true;     /* 强制 native,忽略 _OSC */
	else if (!strncmp(str, "dpc-native", 10))
		pcie_ports_dpc_native = true; /* 只强制 DPC native */
	return 1;
}
__setup("pcie_ports=", pcie_port_setup);

三个 flag 的作用域:pcie_ports_disabled = 完全不加载 portdrv;pcie_ports_native = 绕过 _OSC 强制 native(所有 service);pcie_ports_dpc_native = 只绕过 DPC 那一项。

3.3 pci_aer_available() --- AER 的额外门槛

AER 和 DPC 的启用条件里还有个 pci_aer_available()

c 复制代码
// drivers/pci/pcie/aer.c (v6.6, line 117-120)
bool pci_aer_available(void)
{
	return !pcie_aer_disable && pci_msi_enabled();
}

它要求两个条件:① 没被 pci=noaer 禁用;② MSI 可用。为什么 AER 依赖 MSI?因为 AER 的中断必须用 MSI/MSI-X 报(PCIe 规范里 AER 的 Interrupt Message Number 机制依赖 MSI),legacy INTx 下 AER 无法正常工作。


四、中断分配:三个 Message Number + 降级链

发现 service 后,portdrv 要为每个 service 分配中断 。这不是"每个 service 一个独立中断"那么简单------不同 service 的中断编号来源不同 ,而且有几个 service 要共享一个向量。

4.1 Interrupt Message Number 从哪读(对比表)

核心函数 pcie_message_numbersportdrv.c:56)读三个来源,各返回一个 "Message Number"(即用第几个 MSI/MSI-X 向量):

c 复制代码
// drivers/pci/pcie/portdrv.c (v6.6, line 56-101)  摘要
static int pcie_message_numbers(struct pci_dev *dev, int mask,
				u32 *pme, u32 *aer, u32 *dpc)
{
	u32 nvec = 0, pos;
	u16 reg16;

	/* PME/HP/BWNOTIF 共享:读 PCIe Capability 的 Interrupt Message Number */
	if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP |
		    PCIE_PORT_SERVICE_BWNOTIF)) {
		pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
		*pme = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;   /* bits 13:9 */
		nvec = *pme + 1;
	}

	/* AER:读 AER Root Status 的 AER IRQ 字段 */
	if (mask & PCIE_PORT_SERVICE_AER) {
		u32 reg32;

		pos = dev->aer_cap;
		if (pos) {
			pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
			*aer = (reg32 & PCI_ERR_ROOT_AER_IRQ) >> 27;  /* bits 31:27 */
			nvec = max(nvec, *aer + 1);
		}
	}

	/* DPC:读 DPC Capability 的 IRQ 字段 */
	if (mask & PCIE_PORT_SERVICE_DPC) {
		pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
		if (pos) {
			pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP, &reg16);
			*dpc = reg16 & PCI_EXP_DPC_IRQ;               /* bits 4:0 */
			nvec = max(nvec, *dpc + 1);
		}
	}
	return nvec;
}

三个来源的对比:

service 组 Message Number 来源 寄存器 位域
PME + HP + BWNOTIF PCIe Capability(每个 PCIe 设备都有) PCI_EXP_FLAGS (0x02) PCI_EXP_FLAGS_IRQ (0x3e00),bits 13:9
AER AER Extended Capability PCI_ERR_ROOT_STATUS (0x30) PCI_ERR_ROOT_AER_IRQ (0xf8000000),bits 31:27
DPC DPC Extended Capability PCI_EXP_DPC_CAP (0x04) PCI_EXP_DPC_IRQ (0x1F),bits 4:0

4.2 共享 vs 独占向量

关键结论:PME、HP、BWNOTIF 三个 service 共享同一个 MSI/MSI-X 向量 (都用 PCI_EXP_FLAGS 里的那个 Message Number),而 AER、DPC 各自独占一个向量。

c 复制代码
// drivers/pci/pcie/portdrv.c (v6.6, line 150-163)
/* PME, hotplug and bandwidth notification share an MSI/MSI-X vector */
if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP |
	    PCIE_PORT_SERVICE_BWNOTIF)) {
	pcie_irq = pci_irq_vector(dev, pme);
	irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pcie_irq;
	irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pcie_irq;
	irqs[PCIE_PORT_SERVICE_BWNOTIF_SHIFT] = pcie_irq;
}
if (mask & PCIE_PORT_SERVICE_AER)
	irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, aer);
if (mask & PCIE_PORT_SERVICE_DPC)
	irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, dpc);

为什么 PME/HP/BWNOTIF 要共享一个向量? 因为它们在硬件上本来就是同一个中断来源 ------都挂在 PCIe Capability 的 Interrupt Message Number 字段上(规范只给了这一个字段给 Port 的"杂项"事件)。所以这三个 service 的 ISR 必须注册成 IRQF_SHARED,中断来了各自读自己的状态位判断是不是自己该处理(PME 读 Root Status 的 PME 位、HP 读 Slot Status、BWNOTIF 读 Link Status 的 LBMS/LABS 位)。

而 AER 和 DPC 各有自己独立的 capability + 独立的 Interrupt Message Number 字段,所以能独占向量,不用共享。

4.3 中断模式降级:MSI-X/MSI → INTx

pcie_port_enable_irq_vecportdrv.c:112)先尝试用 MSI-X 或 MSI 分配最多 32 个向量(PCIE_PORT_MAX_MSI_ENTRIES),如果失败,pcie_init_service_irqsportdrv.c:176)回退到 legacy INTx 。注意 flags 是 PCI_IRQ_MSIX | PCI_IRQ_MSIportdrv.c:119)------让内核优先选 MSI-X、退而求其次选 MSI ,两者在同一次 pci_alloc_irq_vectors 里自动决定;只有两个都失败才走到 legacy_irq 分支:

c 复制代码
// drivers/pci/pcie/portdrv.c (v6.6, line 176-205)  摘要
static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{
	int ret, i;

	for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
		irqs[i] = -1;

	/* PME 不能 MSI 时,回退 INTx(有个别平台 PME 用 MSI 有 bug) */
	if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi())
		goto legacy_irq;

	/* 优先 MSI-X 或 MSI */
	if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0)
		return 0;

legacy_irq:
	/* 回退 legacy INTx */
	ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY);
	...
	for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
		irqs[i] = pci_irq_vector(dev, 0);   /* 所有 service 共用同一个 INTx */
}

降级链:

复制代码
MSI-X / MSI(最多 32 向量,各 service 分不同向量)
        ↓ 分配失败 或 PME 不能 MSI(pcie_pme_no_msi)
legacy INTx(只有 1 个向量,所有 service 共享,全部 IRQF_SHARED)

PME 不能 MSI 的特殊 casepcie_pme_msi_disabled flag(pme.c:30,可用 pcie_pme=nomsi 参数设置,或 DMI 匹配到有问题的机型自动设置)。原因见 pme.c 的注释------有些平台用 MSI 做 PME 时,和系统睡眠唤醒(PME-based wakeup)冲突,所以对这些平台强制 PME 走 INTx。


五、设备创建与驱动绑定

5.1 pcie_device_init --- 造一个逻辑设备

拿到 service 和 irq 后,pcie_device_initportdrv.c:285)为每个 service 造一个 struct pcie_device

c 复制代码
// drivers/pci/pcie/portdrv.c (v6.6, line 285-317)  摘要
static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
{
	int retval;
	struct pcie_device *pcie;
	struct device *device;

	pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
	pcie->port = pdev;
	pcie->irq = irq;
	pcie->service = service;

	device = &pcie->device;
	device->bus = &pcie_port_bus_type;      /* 挂到 portdrv 的逻辑总线 */
	device->release = release_pcie_device;
	dev_set_name(device, "%s:pcie%03x",
		     pci_name(pdev),
		     get_descriptor_id(pci_pcie_type(pdev), service));
	device->parent = &pdev->dev;            /* 父设备是物理 Port */

	retval = device_register(device);       /* 触发 driver core 的 match */
	...
}

设备命名规则 get_descriptor_idportdrv.c:31):

c 复制代码
#define get_descriptor_id(type, service) (((type - 4) << 8) | service)

type 是 PCIe Port Type(ROOT_PORT=4 → 减 4 后 0;UPSTREAM=5 → 0x100;DOWNSTREAM=6 → 0x200;RCEC=10 → 0x600),service 是低 8 位的 bit。所以一个 Root Port 的 HP 服务设备名是 0000:00:1c.0:pcie004(type 4-4=0,service HP=0x04),PME 是 pcie001,AER 是 pcie002,DPC 是 pcie008,BWNOTIF 是 pcie010。用 lspci 看不到这些(lspci 只枚举 pci_bus_type 上的真实设备),但 ls /sys/bus/pci_express/devices/ 能看到(pcie_port_bus_type 的 name 是 pci_express)。

5.2 pcie_port_service_register --- service driver 的注册入口

service driver 通过 pcie_port_service_registerportdrv.c:577)注册,它做的事很简单:把驱动挂到 pcie_port_bus_type,并把 probe/remove 包装成 portdrv 自己的:

c 复制代码
// drivers/pci/pcie/portdrv.c (v6.6, line 577-589)
int pcie_port_service_register(struct pcie_port_service_driver *new)
{
	if (pcie_ports_disabled)
		return -ENODEV;

	new->driver.name = new->name;
	new->driver.bus = &pcie_port_bus_type;         /* 挂逻辑总线 */
	new->driver.probe = pcie_port_probe_service;   /* portdrv 统一 probe */
	new->driver.remove = pcie_port_remove_service;
	new->driver.shutdown = pcie_port_shutdown_service;

	return driver_register(&new->driver);
}

统一 probe 包装(pcie_port_probe_serviceportdrv.c:514)------driver core 匹配成功后,portdrv 再调 service driver 自己的 probe

c 复制代码
// drivers/pci/pcie/portdrv.c (v6.6, line 514-534)  摘要
static int pcie_port_probe_service(struct device *dev)
{
	struct pcie_device *pciedev;
	struct pcie_port_service_driver *driver;
	int status;

	driver = to_service_driver(dev->driver);
	pciedev = to_pcie_device(dev);
	status = driver->probe(pciedev);   /* 调 service driver 的 probe */
	...
}

5.3 完整流程:从 init 到 service probe

把整条链路串起来(函数层调用链图,带行号):

复制代码
pcie_portdrv_init (portdrv.c:833)                    ← device_initcall
  ├─ pcie_init_services (portdrv.c:825)
  │    ├─ pcie_aer_init (aer.c:1409)  → pcie_port_service_register(aerdriver)
  │    ├─ pcie_pme_init (pme.c:468)   → pcie_port_service_register(pcie_pme_driver)
  │    ├─ pcie_dpc_init (dpc.c:405)   → pcie_port_service_register(dpcdriver)
  │    └─ pcie_hp_init (pciehp_core.c:358) → pcie_port_service_register(pciehp_driver)
  └─ pci_register_driver(&pcie_portdriver) (portdrv.c:841)
        │  ← driver core 把 pcieport 和所有 PCIe Port 匹配
        └─ pcie_portdrv_probe (portdrv.c:679)          ← 对每个 Port 调用
             └─ pcie_port_device_register (portdrv.c:326)
                  ├─ get_port_device_capability (portdrv.c:217)   ← 探测 service
                  ├─ pcie_init_service_irqs (portdrv.c:176)       ← 分配中断
                  └─ pcie_device_init (portdrv.c:285)  × N        ← 每个 service 造设备
                       └─ device_register → driver core match
                            └─ pcie_port_probe_service (portdrv.c:514)
                                 └─ service driver 的 probe(如 pcie_pme_probe)

时间线pcie_portdrv_initdevice_initcall,晚于 ACPI/PCI 枚举。它先 pcie_init_services 注册所有 service driver (此时还没有任何 pcie_device),再 pci_register_driver(&pcie_portdriver) 注册 pcieport 这个 pci_driver。此时 Port 早已被枚举成 pci_dev,所以 pci_register_driver立即对每个已枚举的 Portpcie_portdrv_probe(运行时热插拔新增的 Port 也由 driver core 自动匹配),portdrv 才现场 为这个 Port 造 service 设备------造出来的 pcie_device 又触发 driver core 去 match 之前注册好的 service driver。先注册 service driver,后造 service 设备,driver core 自动配对


六、实例走读:PME service(最简洁的完整范例)

五个 service driver 里,PME 是最干净、最能讲清"service driver 长什么样"的drivers/pci/pcie/pme.c)。它的完整流程:注册 → probe(申请中断)→ isr(快速响应)→ work(慢处理)→ suspend/resume。

6.1 注册

c 复制代码
// drivers/pci/pcie/pme.c (v6.6, line 454-471)  摘要
static struct pcie_port_service_driver pcie_pme_driver = {
	.name		= "pcie_pme",
	.port_type	= PCIE_ANY_PORT,
	.service	= PCIE_PORT_SERVICE_PME,
	.probe		= pcie_pme_probe,
	.suspend	= pcie_pme_suspend,
	.resume		= pcie_pme_resume,
	.remove		= pcie_pme_remove,
};

int __init pcie_pme_init(void)
{
	return pcie_port_service_register(&pcie_pme_driver);
}

6.2 probe:申请中断 + 初始化

c 复制代码
// drivers/pci/pcie/pme.c (v6.6, line 324-359)  摘要
static int pcie_pme_probe(struct pcie_device *srv)
{
	struct pci_dev *port = srv->port;
	struct pcie_pme_service_data *data;
	int type = pci_pcie_type(port);
	int ret;

	/* 只服务 RP 或 RCEC */
	if (type != PCI_EXP_TYPE_RC_EC && type != PCI_EXP_TYPE_ROOT_PORT)
		return -ENODEV;

	data = kzalloc(sizeof(*data), GFP_KERNEL);
	spin_lock_init(&data->lock);
	INIT_WORK(&data->work, pcie_pme_work_fn);
	data->srv = srv;
	set_service_data(srv, data);          /* 把私有数据挂到 pcie_device */

	pcie_pme_interrupt_enable(port, false);   /* 先关,避免 BIOS 遗留 */
	pcie_clear_root_pme_status(port);

	ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
	...
	pcie_pme_interrupt_enable(port, true);    /* 再开 */
}

注意 request_irq 用的是 IRQF_SHARED------这正是 4.2 节说的:PME 和 HP、BWNOTIF 共享同一个向量,所以必须 shared。

6.3 isr:读 Root Status,快速清场,慢活交给 work

PME 的中断来自 Root Status 寄存器PCI_EXP_RTSTA,偏移 0x20)的 PME 位PCI_EXP_RTSTA_PME,bit16)。isr 只做三件事:读状态 → 确认是 PME → 关中断 + 调度 work:

c 复制代码
// drivers/pci/pcie/pme.c (v6.6, line 264-289)  摘要
static irqreturn_t pcie_pme_irq(int irq, void *context)
{
	struct pci_dev *port;
	struct pcie_pme_service_data *data;
	u32 rtsta;
	unsigned long flags;

	port = ((struct pcie_device *)context)->port;
	data = get_service_data((struct pcie_device *)context);

	spin_lock_irqsave(&data->lock, flags);
	pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);

	/* 不是 PME,也不是本 service 的活 → 让给共享的中断处理器 */
	if (PCI_POSSIBLE_ERROR(rtsta) || !(rtsta & PCI_EXP_RTSTA_PME)) {
		spin_unlock_irqrestore(&data->lock, flags);
		return IRQ_NONE;    /* ← 共享中断的关键:不关我的事就返回 NONE */
	}

	pcie_pme_interrupt_enable(port, false);   /* 关 PME 中断,防止重入 */
	spin_unlock_irqrestore(&data->lock, flags);

	schedule_work(&data->work);               /* 慢活丢给 workqueue */
	return IRQ_HANDLED;
}

work 函数 pcie_pme_work_fnpme.c:213)循环读 Root Status,找到具体是哪个设备发的 PME(pcie_pme_handle_requestpme.c:129 会解析 Requester ID、遍历 bus 找设备、调 pm_request_resume 唤醒它),处理完清 PME 状态、重新开中断。

6.4 涉及到的 Root 寄存器(RC 专用,之前没展开)

PME 用到的三个寄存器是 Root Port 专用的(普通 Endpoint 没有),在 PCIe Capability 的末尾:

寄存器 偏移 关键位 含义
Root ControlPCI_EXP_RTCTL 0x1c PMEIE (0x0008, bit3) 使能 PME 中断
SEFEE/SENFEE/SECEE (bits 2:0) 错误上报给 system error 的开关
Root CapabilitiesPCI_EXP_RTCAP 0x1e CRSVIS (0x0001) 是否支持 CRS Software Visibility
Root StatusPCI_EXP_RTSTA 0x20 PME (0x00010000, bit16) PME 状态(有设备请求电源事件)
PME_PENDING (0x00020000, bit17) 还有 PME 在排队

pcie_pme_interrupt_enable 就是写 Root Control 的 PMEIE 位:

c 复制代码
// drivers/pci/pcie/pme.c (v6.6, line 53-61)
void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
{
	if (enable)
		pcie_capability_set_word(dev, PCI_EXP_RTCTL, PCI_EXP_RTCTL_PMEIE);
	else
		pcie_capability_clear_word(dev, PCI_EXP_RTCTL, PCI_EXP_RTCTL_PMEIE);
}

呼应 #6 电源管理篇:PME 是 runtime PM 唤醒的硬件基础。设备进 D3 后要能唤醒,靠的就是这根 PME 链------设备拉 PME → Root Port 的 Root Status PME 位置位 → PME service 的 isr 触发 → work 里 pm_request_resume 唤醒设备。


七、RCEC:不是 bridge 的"特殊 Port"

7.1 为什么需要 RCEC

Root Complex Event Collector(RCEC)是个特殊存在:它的 Device/Port Type 是 0xa,但它不是 bridge (没有 subordinate bus),而是一个独立 function ,专职替 RC 内部的集成设备(RCiEP,Root Complex Integrated Endpoint)收集和上报错误/PME 事件

问题来了:一个 RC 集成的设备(比如内置显卡、内置网卡)没有经过 Root Port,它的 AER 错误怎么报?答案就是 RCEC------这些 RCiEP 的 AER 错误统一由 RCEC 汇总上报,RCEC 自己带 AER capability。

所以 portdrv 要接管 RCEC(pcie_portdrv_probePCI_EXP_TYPE_RC_EC 是合法匹配),并在 get_port_device_capability给 RCEC 开 AER 和 PME service (见 3.1 表:AER/PME 的 Port 条件都是 RP 或 RCEC)。

probe RCEC 时,portdrv 调 pcie_link_rcecrcec.c:110)把 RCEC 和它负责的 RCiEP 设备关联起来:

c 复制代码
// drivers/pci/pcie/portdrv.c (v6.6, line 692-693)
if (type == PCI_EXP_TYPE_RC_EC)
	pcie_link_rcec(dev);

关联的依据是 RCEC 的 RCEC Associated Endpoint 位图rcec->rcec_ea->bitmaprcec_assoc_rcieprcec.c:24)------RCEC 的扩展 capability 里记录了一张"我负责哪些 RCiEP"的位图,portdrv 据此把每个 RCiEP 的 dev->rcec 指回这个 RCEC。这样 AER service 处理错误时,能从出错设备反查到它的 RCEC,正确地读 RCEC 的 AER Root Status。

小结:Root Port 通过"拓扑上级"天然知道自己管哪些下游设备;RCEC 则靠"关联位图"显式记录自己管哪些 RCiEP。两种"汇总"机制,殊途同归------都是为了让"收集者"能找到"产生者"。


八、PM 回调的 offsetof 分发技巧

portdrv 有个很精巧的设计:电源管理回调怎么分发给所有 service driver?

Port 要 suspend 了,5 个 service driver 各有各的 suspend 回调要调。portdrv 没有写死"调 pme 的 suspend、再调 aer 的 suspend......",而是用 device_for_each_child + offsetof 泛型地遍历:

c 复制代码
// drivers/pci/pcie/portdrv.c (v6.6, line 380-393)
static int pcie_port_device_iter(struct device *dev, void *data)
{
	struct pcie_port_service_driver *service_driver;
	size_t offset = *(size_t *)data;
	pcie_callback_t cb;

	/* 只处理挂在 pcie_port_bus_type 上的、且已绑定的 service */
	if ((dev->bus == &pcie_port_bus_type) && dev->driver) {
		service_driver = to_service_driver(dev->driver);
		cb = *(pcie_callback_t *)((void *)service_driver + offset);
		if (cb)
			return cb(to_pcie_device(dev));
	}
	return 0;
}

// drivers/pci/pcie/portdrv.c (v6.6, line 400-404)
static int pcie_port_device_suspend(struct device *dev)
{
	size_t off = offsetof(struct pcie_port_service_driver, suspend);
	return device_for_each_child(dev, &off, pcie_port_device_iter);
}

机制device_for_each_child 遍历 Port 的每个子设备(就是那些 pcie_device),对每个子设备调 pcie_port_device_iterpcie_port_device_iter 拿到子设备绑定的 service driver,然后用 offsetof(struct pcie_port_service_driver, suspend) 算出 suspend 回调在结构体里的字节偏移,直接按偏移取出函数指针调用。

为什么用 offsetof 而不是写死一个枚举 :因为 struct pcie_port_service_driver 里有 5 个 PM 回调(suspend/resume/resume_noirq/runtime_suspend/runtime_resume)要分发,如果写 5 个几乎一样的遍历函数,全是复制粘贴。用 offsetof 把"遍历 + 取回调"变成一个通用函数 pcie_port_device_iter,5 个 PM 回调各传一个 offset 就行,代码量骤减。

这 5 个分发函数通过 pcie_portdrv_pm_opsportdrv.c:650)挂到 pcieport 驱动的 driver.pm 上;其中 dev_pm_ops 的 freeze/thaw/poweroff/restore 入口直接复用 suspend/resume 的 handler(因为它们"保存/恢复状态"的逻辑相同),再由 driver core 在 suspend/resume 时调用。


九、关键函数 / 文件索引

9.1 关键函数

函数 文件:行号 作用
pcie_portdrv_init portdrv.c:833 portdrv 入口(device_initcall):注册 service driver + pcieport
pcie_init_services portdrv.c:825 依次注册 AER/PME/DPC/HP 四个 service driver
pcie_portdrv_probe portdrv.c:679 probe 一个 PCIe Port,调 pcie_port_device_register
pcie_port_device_register portdrv.c:326 探测能力 + 分配中断 + 造 service 设备的总入口
get_port_device_capability portdrv.c:217 读配置空间,返回支持哪些 service 的 bitmask
pcie_init_service_irqs portdrv.c:176 分配 service 中断(含 INTx 回退)
pcie_port_enable_irq_vec portdrv.c:112 用 MSI-X/MSI 分配向量
pcie_message_numbers portdrv.c:56 读 PME/AER/DPC 三组 Interrupt Message Number
pcie_device_init portdrv.c:285 造一个 struct pcie_device 并 device_register
pcie_port_service_register portdrv.c:577 注册 service driver 到 pcie_port_bus_type
pcie_port_bus_match pci-driver.c:1696 service driver 与 pcie_device 的 match(service 主键 + port_type 过滤)
pcie_port_probe_service portdrv.c:514 统一 probe 包装,调 service driver 的 probe
pcie_port_find_device portdrv.c:478 按 service 查找某个 Port 的 service device
pcie_port_device_iter portdrv.c:380 offsetof 泛型遍历,分发 PM 回调
pcie_portdrv_slot_reset portdrv.c:752 AER slot reset 时分发 slot_reset 回调给 service
pci_pcie_type pci.h:2427 读 PCIe Capability 的 Device/Port Type 字段
pci_aer_available aer.c:117 AER 是否可用(未禁用 + MSI 可用)
pcie_pme_irq pme.c:264 PME 中断处理(isr)
pcie_pme_work_fn pme.c:213 PME 中断的慢处理(work)
pcie_pme_probe pme.c:324 PME service 的 probe(申请共享中断)
pcie_link_rcec rcec.c:110 建立 RCEC 与 RCiEP 的关联
acpi_pci_osc_control_set pci_root.c:355 执行 _OSC 协商
negotiate_os_control pci_root.c:564 _OSC 协商入口,决定 native flag

9.2 关键文件

文件 内容
drivers/pci/pcie/portdrv.c portdrv 主框架(服务发现/中断/设备创建/PM 分发)
drivers/pci/pcie/portdrv.h struct pcie_device / struct pcie_port_service_driver 定义
drivers/pci/pcie/pme.c PME service driver(最简洁范例)
drivers/pci/pcie/aer.c AER service driver + pci_aer_available
drivers/pci/pcie/dpc.c DPC service driver
drivers/pci/pcie/rcec.c RCEC 关联逻辑
drivers/acpi/pci_root.c OSC 协商 → native* flag
include/uapi/linux/pci_regs.h PCIe Capability 寄存器定义(Root Control/Status 等)

记住三点

  1. portdrv 是"二级总线"桥接层 :一个物理 Port(pci_dev,挂 pci_bus_type)被拆成最多 5 个逻辑服务(pcie_device,挂 pcie_port_bus_type),各配一个 service driver。服务不是枚举出来的,是 pcie_device_init 软件构造的。

  2. 两个关键判断 :service 启不启用看 get_port_device_capability(Port 类型 + 硬件 capability + native/_OSC 放权);中断怎么分看 pcie_message_numbers(PME/HP/BWNOTIF 共享 PCI_EXP_FLAGS_IRQ,AER 用 AER cap,DPC 用 DPC cap,失败降级 INTx)。

  3. native vs 固件是 _OSC 决定的 :AER/PME/HP 这些"高级功能"默认固件管,Linux 要管必须先经 _OSC 协商拿到放权,否则不碰(热插拔交给 acpiphp)。pcie_ports=native 可强制绕过。

相关推荐
天天喝旺仔2 小时前
Python asyncio 异步编程实战:用协程与事件循环构建高性能网络服务
服务器·网络·python·性能优化·fastapi
运维行者_3 小时前
ISP 企业级带宽计费怎么做?网络流量计费的 6 大核心能力
运维·服务器·网络·数据库·支持向量机·接口隔离原则
数安旭说3 小时前
企业终端行为审计建设:本地审计 + 网络审计构建完整溯源取证体系
运维·数据安全·软件需求·溯源·审计·网络审计
码农小韩3 小时前
Linux应用开发(二)——Linux的文件IO
linux·嵌入式软件·嵌入式操作系统·linux应用
Elastic 中国社区官方博客4 小时前
在 Elasticsearch 中回填时间序列数据:通过批量 API 加载数月的历史指标数据
大数据·运维·数据库·人工智能·elasticsearch·搜索引擎·全文检索
Horn Still Sounds4 小时前
Linux网络并发服务器模型与SQLite数据库学习笔记
linux·服务器·数据库
10mAh4 小时前
【Linux】CPU 100% 怎么排查?——top、pidstat、jstack 到线程定位实战
linux·运维·服务器
深念Y4 小时前
开机启动优化记录
linux·开机
!chen4 小时前
客户环境 Nginx 配置流式报表与超时排查
运维·nginx