3. PCIe读写事务机制
PCIe 的性能问题,很少是“链路带宽不够”这么简单。真正决定一次读写快慢的,是事务层如何把请求拆成 TLP、如何等待 Completion、以及配置空间访问为何天然比内存访问更慢。理解这一层,才能解释为什么同样的链路宽度,不同驱动的吞吐能差出数倍。
一、核心原理:四类事务与 TLP 结构
PCIe 事务层定义了请求者(Requester)与完成者(Completer)之间的交互。按访问目标划分,最常用的是四类:
- Memory Read (MRd):读目标设备的内存或 BAR 空间,属于 Non-Posted 事务,必须等待 Completion 才能拿到数据。
- Memory Write (MWr):写目标内存,属于 Posted 事务,发出后不等待确认,因此写吞吐通常高于读。
- Configuration Read (CfgRd):读取配置空间,用于枚举和寄存器访问,同样是 Non-Posted。
- Configuration Write (CfgWr):写配置空间,也是 Non-Posted,必须等 Cpl 返回。
每个 TLP 由 Header、可选 Data Payload 和可选 ECRC 组成。Header 中最关键的是 Fmt/Type 字段(决定事务类型与是否带数据)、Requester ID、Tag、Length 和地址字段。Tag 是读请求的“身份标识”,Completion 靠它匹配回原请求;Length 决定一次能搬多少字节,直接影响效率。
二、方法流程:一次读事务的完整生命周期
以一次 Memory Read 为例,流程可以拆成五步:
- 驱动或 CPU 发起读请求,RC 生成 MRd TLP,填入 Requester ID、Tag、目标地址和 Length。
- TLP 经 Switch 路由到目标 EP,EP 解析 Header,判断是命中 BAR 空间还是内部寄存器。
- EP 准备数据,生成 CplD TLP,把原请求的 Requester ID 和 Tag 原样带回。
- Completion 沿原路径返回 RC,RC 用 Tag 匹配挂起的请求,把数据交给上层。
- 若数据量超过单次 Length,则拆成多个 TLP 或多次请求,形成流水线。
三、案例/场景:配置空间访问为何拖慢枚举
系统启动时,BIOS 或 OS 会遍历总线号、设备号、功能号,逐个读取 Vendor ID、Device ID、BAR 等配置寄存器。每次 CfgRd 都是 Non-Posted,必须等 Cpl 返回才能发下一个。若拓扑中有多层 Switch,每层都增加转发延迟,枚举时间会明显拉长。
# 查看某设备配置空间(每次读都产生 CfgRd + Cpl)
lspci -s 01:00.0 -xxx
# 输出示例
01:00.0 Ethernet controller: Intel Corporation ...
00: 86 80 12 15 07 04 10 00 ...
# 查看链路状态与协商速率
lspci -vv -s 01:00.0 | grep -i "LnkSta"
LnkSta: Speed 8GT/s, Width x4
优化枚举的常见手段是减少不必要的配置读、合并访问,或利用缓存机制避免重复读取同一寄存器。对于性能敏感路径,应尽量用 Memory Read/Write 替代配置空间访问。
四、误区与复盘:Tag 耗尽与 Completion 超时
很多性能问题并非带宽不足,而是 Tag 资源被耗尽。每个 Non-Posted 请求占用一个 Tag,若驱动同时发出大量读请求而不及时回收 Completion,Tag 用尽后新请求只能排队,吞吐骤降。典型现象是:链路利用率不高,但读延迟飙升。
| 事务类型 | 是否 Posted | 是否需 Completion | 典型延迟 | 优化方向 |
|---|---|---|---|---|
| Memory Write (MWr) | 是 | 否 | 低 | 合并写、增大 Length |
| Memory Read (MRd) | 否 | 是 | 高 | 预取、增大 Tag 深度 |
| Configuration Write (CfgWr) | 否 | 是 | 高 | 减少访问频次 |
| Configuration Read (CfgRd) | 否 | 是 | 高 | 缓存、批量枚举 |
复盘时建议按以下清单排查:
- 确认当前 MPS 与 MRRS 设置是否匹配设备能力。
- 检查是否有大量小 Length 读请求,考虑合并或预取。
- 观察 Completion 是否出现 UR/CA 等错误状态,避免重试放大延迟。
- 用性能计数器统计 Tag 使用率,判断是否成为瓶颈。
- 区分配置空间访问与内存访问,避免在热路径中混用。
理解事务层机制后,后续的 MPS、MRRS、Relaxed Ordering 等优化手段才有落脚点。读写性能的差异,本质上由 Posted/Non-Posted 语义和 Completion 匹配机制决定。