# Phase 0 · Day 1: PCIe 总线拓扑与 Config Space --- ## 一、BDF 地址:每个设备的唯一身份 ``` 0000 : 02 : 01 . 0 │ │ │ └── Function (0~7) — 一个物理芯片可以有多个功能 │ │ └────── Device (0~31) — 同一总线上最多 32 个设备 │ └─────────── Bus (0~255) — 256 条总线 └───────────────── Domain (0~65535) — 多 CPU socket 系统有多个 domain 单 CPU 系统 domain=0000。多路服务器每个 socket 一个 domain。 ``` DPDK 通过 BDF 地址接管网卡:`dpdk-devbind.py -b vfio-pci 02:01.0` ### 为什么需要三级? PCI 是树形拓扑。每个 Bridge 引入一条新 bus。256 条 bus × 32 设备 × 8 功能 = 65536 个可寻址功能。如果没有 bus 层级,只靠 5-bit device 最多 32 个设备——一张显卡+一张网卡就满了。 ## 二、拓扑中的四种角色 | 角色 | 你的实例 | 职责 | |------|---------|------| | **Root Complex** | `00:00.0` | CPU 和 PCIe 世界的桥梁,把 load/store 翻译成 TLP | | **PCI Bridge** | `00:11.0` → Bus 02 | 传统 PCI 桥,用 subtractive decode 转发未认领的访问 | | **PCIe Root Port** | `00:15.0` → Bus 03 | PCIe 原生桥,点对点链路,支持更高带宽 | | **Endpoint** | `02:01.0` `03:00.0` | 叶子节点,实际的网卡设备 | 关键差异:`02:01.0` (e1000) 是挂在传统 PCI Bridge 后面的老式设备,多条设备共享 Bus 02。`03:00.0` (VMXNET3) 是 PCIe Endpoint,独享 Bus 03 的整条链路(5GT/s × x32)。 ## 二、PCIe 拓扑 ``` [0000:00] ← Root Complex (Host Bridge) │ CPU 访问 PCIe 设备的唯一入口 │ ├── 11.0 → [02] ← PCI Bridge (老式总线) │ ├── 01.0 e1000 (Endpoint) │ └── 04.0 e1000 (Endpoint) │ └── 15.0 → [03] ← PCIe Root Port (真正的 PCI Express) └── 00.0 VMXNET3 (Endpoint) ``` 数据流:CPU → Root Complex → TLP 封装 → PCIe 链路 → Endpoint。 ## 三、Config Space 完整解码 e1000 (02:01.0) 的 256 字节 Config Space,逐字段解码: | Offset | 字段 | Hex 值 | 含义 | |--------|------|--------|------| | 0x00-01 | Vendor ID | 0x8086 | Intel | | 0x02-03 | Device ID | 0x100f | 82545EM | | 0x04-05 | Command | 0x0113 | IO+Mem enable, BusMaster OFF | | 0x0a-0b | Class Code | 0x020000 | Network Controller | | 0x0e | Header Type | 0x00 | Endpoint | | 0x10-13 | BAR0 | 0xfd5c0004 | Memory@0xfd5c0000, 64bit, 128KB, Non-Prefetchable | | 0x18-1b | BAR2 | 0xfdff0004 | Memory@0xfdff0000, 64KB | | 0x20-23 | BAR4 | 0x00002001 | IO Port@0x2000 | | 0x34 | Capability Ptr | 0xdc | 链表起点 | | 0x3c | Interrupt Line | 0x07 | IRQ 7 | | 0x3d | Interrupt Pin | 0x01 | INTA# | BAR flag bit 解码: bit0=Memory/IO, bit[2:1]=32b/64b, bit3=Prefetchable。`0xfd5c0004` 中的 `4` = 0100₂ → 64-bit Memory, Non-Prefetchable。真实基址 = 0xfd5c0004 & ~0xF = 0xfd5c0000。 Capability 链表: 0xdc→PM(ID=1)→0xe4→PCI-X(ID=7)→0x00(End)。 ## 四、VMXNET3 对比 (真正的 PCIe 设备) | | e1000 | VMXNET3 | |---|---|---| | 总线 | PCI-X (老) | PCIe v2.0 | | Capability | PM + PCI-X | PM + PCIe Express + MSI + MSI-X + Serial# | | MSI-X | 无 | 25 向量, BAR2 offset 0, PBA offset 0x1000 | ## 五、实战踩坑 ### 坑 1: Bus Master 没开 - **现象**: DPDK 启动后一个包都收不到 - **根因**: Command bit2=0 → 网卡不能 DMA - **定位**: `lspci -vvv | grep "BusMaster"` - **修复**: `pci_set_master(pdev)` 或 `setpci -s XX:XX.X COMMAND=0x117` ### 坑 2: 64-bit BAR 误读 - **现象**: 物理地址 >4GB 时 MMIO 地址错误 - **根因**: 只用 uint32_t 读 BAR0 低 32 位 - **修复**: 用 `resource_size_t pci_resource_start(pdev, 0)` ### 坑 3: MSI-X 向量不够 - **现象**: 多队列网卡性能不增长 - **根因**: pci_alloc_irq_vectors 返回数 < RX 队列数 - **定位**: `cat /proc/interrupts | grep PCI-MSI | wc -l` - **修复**: 增大内核 MSI-X 限制或 BIOS 调整 ### 坑 4: PCIe 链路降速 - **现象**: 100Gbps 网卡只能跑 25Gbps - **定位**: `lspci -vvv | grep -E "LnkCap:|LnkSta:"` → Speed 2.5GT/s - **修复**: 检查物理槽位宽度、PCIe 延长线、BIOS 设置 ### 坑 5: AER 静默数据损坏 - **现象**: 偶尔出现 mbuf 数据乱码但不 crash - **定位**: `dmesg | grep AER` → Uncorrectable Error - **修复**: 必须换硬件,链路已不可靠 ## 六、核心认知 CPU 操作网卡的全路径: ``` 读 Config Space → 匹配 Vendor/Device ID → 确定驱动 → pci_enable_device → ioremap(BAR0) → 拿到虚拟地址 → ioread32/iowrite32 操作寄存器 → 网卡开始工作 ``` BAR 是 MMIO 的入口,Config Space 是身份证,Capability 链表是扩展机制。 ## 七、检验标准 - [ ] 能解释 BDF 地址含义 - [ ] 能手写 Config Space 前 16 字节字段 - [ ] 能从 hex 值解码 BAR 的基址和大小 - [ ] 能画 Capability 链表结构 - [ ] 能对比 e1000(PCI-X) vs VMXNET3(PCIe v2) - [ ] 能解释为什么 Bus Master bit 不置位就没 DMA