〇、全景: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_probe里PCI_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_match(pci-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_capability(portdrv.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_native 或 native_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 位 | 无额外权限要求 |
几个值得注意的点:
- AER / PME 只挂在 RP 和 RCEC 上------因为只有 Root Port 才会"汇总"整条链路的错误/电源事件(Switch 的 Upstream/Downstream Port 不承担这个汇总职责)。
- HP 要求 RP 或 DN (
is_hotplug_bridge只在这两类 Port 上有意义),且还要"native"放权。 - 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。 - 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_numbers(portdrv.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, ®16);
*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, ®32);
*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, ®16);
*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_vec(portdrv.c:112)先尝试用 MSI-X 或 MSI 分配最多 32 个向量(PCIE_PORT_MAX_MSI_ENTRIES),如果失败,pcie_init_service_irqs(portdrv.c:176)回退到 legacy INTx 。注意 flags 是 PCI_IRQ_MSIX | PCI_IRQ_MSI(portdrv.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 的特殊 case :pcie_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_init(portdrv.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_id(portdrv.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_register(portdrv.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_service,portdrv.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_init 是 device_initcall,晚于 ACPI/PCI 枚举。它先 pcie_init_services 注册所有 service driver (此时还没有任何 pcie_device),再 pci_register_driver(&pcie_portdriver) 注册 pcieport 这个 pci_driver。此时 Port 早已被枚举成 pci_dev,所以 pci_register_driver 会立即对每个已枚举的 Port 调 pcie_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_fn(pme.c:213)循环读 Root Status,找到具体是哪个设备发的 PME(pcie_pme_handle_request,pme.c:129 会解析 Requester ID、遍历 bus 找设备、调 pm_request_resume 唤醒它),处理完清 PME 状态、重新开中断。
6.4 涉及到的 Root 寄存器(RC 专用,之前没展开)
PME 用到的三个寄存器是 Root Port 专用的(普通 Endpoint 没有),在 PCIe Capability 的末尾:
| 寄存器 | 偏移 | 关键位 | 含义 |
|---|---|---|---|
Root Control (PCI_EXP_RTCTL) |
0x1c | PMEIE (0x0008, bit3) |
使能 PME 中断 |
SEFEE/SENFEE/SECEE (bits 2:0) |
错误上报给 system error 的开关 | ||
Root Capabilities (PCI_EXP_RTCAP) |
0x1e | CRSVIS (0x0001) |
是否支持 CRS Software Visibility |
Root Status (PCI_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_probe 里 PCI_EXP_TYPE_RC_EC 是合法匹配),并在 get_port_device_capability 里给 RCEC 开 AER 和 PME service (见 3.1 表:AER/PME 的 Port 条件都是 RP 或 RCEC)。
7.2 pcie_link_rcec:建立 RCEC ↔ RCiEP 的关联
probe RCEC 时,portdrv 调 pcie_link_rcec(rcec.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->bitmap,rcec_assoc_rciep,rcec.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_iter。pcie_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_ops(portdrv.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 等) |
记住三点
-
portdrv 是"二级总线"桥接层 :一个物理 Port(
pci_dev,挂pci_bus_type)被拆成最多 5 个逻辑服务(pcie_device,挂pcie_port_bus_type),各配一个 service driver。服务不是枚举出来的,是pcie_device_init软件构造的。 -
两个关键判断 :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)。 -
native vs 固件是 _OSC 决定的 :AER/PME/HP 这些"高级功能"默认固件管,Linux 要管必须先经
_OSC协商拿到放权,否则不碰(热插拔交给 acpiphp)。pcie_ports=native可强制绕过。