1. DMA寻址基础:物理地址空间、总线地址、DMA引擎寻址能力概述
在深入PCI/PCIe设备的DMA寻址掩码配置之前,必须厘清三个核心概念:物理地址空间、总线地址与DMA引擎寻址能力。这三者之间的映射关系直接决定了驱动开发者应当如何调用pci_set_dma_mask()与pci_set_consistent_dma_mask()。
1.1 物理地址空间与总线地址的分离
现代CPU通过内存管理单元(MMU)访问物理内存,而PCI/PCIe设备通过总线事务访问内存。这两套地址体系在硬件上通过Host Bridge(主机桥)或IOMMU进行转换。
关键认知: 设备看到的“内存地址”是总线地址(Bus Address),而非CPU侧的物理地址。DMA操作中,设备驱动必须使用总线地址来编程DMA引擎。
| 地址类型 | 所属域 | 宽度示例 | 转换关系 |
|---|---|---|---|
| CPU虚拟地址 | 进程空间 | 64位 | 由MMU转换为物理地址 |
| CPU物理地址 | 系统内存总线 | 40位/48位 | 由Host Bridge转换为总线地址 |
| 总线地址 | PCI/PCIe总线域 | 32位/64位 | 设备DMA引擎直接使用 |
实战提示: 在x86平台上,物理地址与总线地址通常相等(无IOMMU时),但在ARM/PPC等嵌入式平台上,两者可能完全不同。切勿假设物理地址等于总线地址。
1.2 DMA引擎寻址能力:为什么需要掩码
每个PCI/PCIe设备的DMA引擎内部地址寄存器宽度是固定的。例如:
- 32位DMA引擎:地址寄存器仅32位宽,只能访问总线地址空间低4GB区域。
- 64位DMA引擎:地址寄存器64位宽,可访问整个64位总线地址空间。
- 高位寻址设备:指DMA引擎支持超过32位(如40位、48位、64位)寻址的设备。
驱动通过pci_set_dma_mask()向内核声明设备DMA引擎的实际寻址能力。内核据此决定:
- 是否可以从高于4GB的物理内存分配DMA缓冲区。
- 是否需要启用SWIOTLB或IOMMU进行地址翻译。
- 是否允许使用
dma_alloc_coherent()分配大块连续内存。
常见错误: 驱动开发者误以为“系统是64位CPU,所以设备也支持64位DMA”。实际上,设备DMA引擎的地址宽度由硬件设计决定,与CPU位数无关。必须查阅设备手册确认。
1.3 寻址掩码的数学本质
DMA寻址掩码是一个位掩码,表示设备DMA引擎能够处理的总线地址高位范围。其核心规则是:
| 掩码值 | 二进制表示(低32位) | 含义 | 典型设备 |
|---|---|---|---|
0xFFFFFFFF |
1111...1111 (32个1) | 仅支持32位寻址,地址必须 < 4GB | 旧式网卡、声卡 |
0xFFFFFFFFFF |
1111...1111 (40个1) | 支持40位寻址,地址必须 < 1TB | 部分RAID控制器 |
0xFFFFFFFFFFFF |
1111...1111 (48个1) | 支持48位寻址,地址必须 < 256TB | 高端NVMe SSD |
0xFFFFFFFFFFFFFFFF |
64个1 | 支持64位寻址,无高位限制 | 现代GPU、FPGA加速卡 |
内核在分配DMA缓冲区时,会检查分配的总线地址是否满足:
bus_addr & ~dma_mask == 0
即总线地址的高位(超出掩码覆盖的位)必须全部为0。
1.4 高位寻址设备的典型场景
在嵌入式系统(如ARM64、RISC-V)和大内存x86服务器中,物理内存可能远超过4GB。如果设备仅支持32位DMA,内核必须将DMA缓冲区限制在低4GB,这会导致:
- 内存碎片化:低4GB内存被大量占用。
- 性能下降:需要额外的SWIOTLB缓冲拷贝。
- 大块分配失败:无法从低4GB分配连续大页。
高位寻址设备的价值: 通过正确配置
pci_set_dma_mask(),驱动可以告知内核“我的设备能访问整个64位地址空间”,从而允许内核从任意物理地址分配DMA缓冲区,避免上述问题。
下表总结了不同寻址能力下的驱动配置策略:
| 设备寻址能力 | 推荐掩码 | 内核行为 | 适用场景 |
|---|---|---|---|
| 仅32位 | DMA_BIT_MASK(32) |
DMA缓冲区限制在ZONE_DMA | 旧设备、低内存系统 |
| 40位 | DMA_BIT_MASK(40) |
允许从1TB以下内存分配 | 中端存储控制器 |
| 48位 | DMA_BIT_MASK(48) |
允许从256TB以下内存分配 | NVMe、高性能网卡 |
| 64位 | DMA_BIT_MASK(64) |
无地址限制,全地址空间可用 | GPU、FPGA、智能网卡 |
1.5 本章小结
理解DMA寻址基础是配置pci_dma_set_mask的前提。核心要点:
- 物理地址 ≠ 总线地址:设备DMA使用总线地址,驱动必须通过
pci_map_*或dma_map_*接口进行转换。 - 寻址掩码反映硬件能力:掩码的位数等于设备DMA引擎地址寄存器的有效宽度。
- 高位寻址设备需要显式声明:驱动必须调用
pci_set_dma_mask()并传入正确的掩码,否则内核默认设备仅支持32位寻址。 - 错误配置的后果:掩码设置过小会导致DMA分配失败或性能下降;设置过大(超出设备实际能力)会导致数据损坏或系统崩溃。