4、DMA与Cache一致性问题
在嵌入式Linux系统中,DMA(直接存储器访问)控制器可以在没有CPU干预的情况下,在外设和内存之间传输数据。然而,当系统中同时存在Cache和DMA时,就会产生经典的Cache一致性问题。本章将深入剖析DMA传输中的Cache Stale问题、硬件Cache一致性协议(MESI/MOESI)以及硬件一致性互联(CCI/ACE)的工程实践。
4.1 DMA传输中的Cache Stale问题
Cache Stale(缓存陈旧)问题是指:DMA直接访问物理内存,而CPU可能通过Cache持有同一块内存的副本。当DMA更新了物理内存内容后,CPU的Cache中仍然保存着旧数据,导致CPU读取到错误的数据。反之,如果CPU通过Cache写入了数据但尚未写回物理内存,DMA直接读取物理内存也会得到旧数据。
典型的Cache Stale场景分为两种:
- DMA写入,CPU读取(DMA-to-CPU):外设通过DMA将数据写入物理内存,但CPU的Cache中可能还保留着该地址的旧数据。CPU读取时命中Cache,得到陈旧数据。
- CPU写入,DMA读取(CPU-to-DMA):CPU将数据写入Cache(标记为Dirty),但尚未写回物理内存。DMA直接从物理内存读取,得到的是旧数据。
dma_alloc_coherent()(一致性映射)和 dma_map_single() + dma_sync_single_for_cpu/device()(流式映射)。
4.2 Cache Coherency协议(MESI / MOESI)
为了解决多核CPU以及DMA等总线主控设备之间的Cache一致性问题,硬件层面引入了Cache一致性协议。最经典的协议是MESI,以及其增强版MOESI。这些协议通过为每个Cache行维护一组状态,来协调多个Cache副本之间的数据一致性。
4.2.1 MESI协议
MESI协议定义了四种Cache行状态:
| 状态 | 全称 | 描述 | 该Cache行是否有效 | 数据是否与内存一致 |
|---|---|---|---|---|
| M | Modified | 该行已被CPU修改,数据与内存不一致;且该行在其他Cache中不存在副本 | 是 | 否(脏数据) |
| E | Exclusive | 该行数据与内存一致,且仅存在于当前Cache中 | 是 | 是 |
| S | Shared | 该行数据与内存一致,且可能存在于多个Cache中 | 是 | 是 |
| I | Invalid | 该行无效,不包含有效数据 | 否 | — |
4.2.2 MOESI协议
MOESI协议在MESI的基础上增加了一个Owned(O)状态,用于优化Modified状态的数据共享。在MESI中,如果CPU A修改了数据(M状态),CPU B读取该数据时,CPU A必须先将数据写回内存,然后两个CPU都进入S状态。而在MOESI中,CPU A可以直接将数据提供给CPU B,CPU A进入O状态,CPU B进入S状态,无需写回内存。
| 状态 | 全称 | 与MESI的区别 |
|---|---|---|
| O | Owned | 数据被修改,但允许其他Cache共享(S状态)。该Cache负责在必要时将数据写回内存。减少了不必要的内存写回操作。 |
4.3 硬件一致性(CCI / ACE)
对于复杂的多核SoC,仅仅依靠Cache一致性协议还不够,还需要硬件互联架构来广播一致性消息。ARM公司推出了CCI(Cache Coherent Interconnect)和ACE(AXI Coherency Extensions)来提供硬件级别的一致性支持。
4.3.1 CCI(Cache Coherent Interconnect)
CCI是一个硬件互联模块,它连接多个CPU集群、GPU、DMA控制器等总线主控设备,并维护它们之间的Cache一致性。CCI通过监听(Snoop)机制,确保所有主控设备看到的内存视图是一致的。
- ACE接口:支持完整Cache一致性协议(MOESI)的AXI接口。CPU集群通过ACE接口连接到CCI,可以发送和接收一致性事务。
- ACE-Lite接口:简化的ACE接口,不支持监听(Snoop),但支持一致性。通常用于DMA等设备,它们可以读取一致性的数据,但不会主动参与一致性协议。
- CHI接口:ARM新一代的高性能一致性接口,用于更复杂的SoC设计。
dma_alloc_coherent)或正确配置流式映射。
4.3.2 ACE与ACE-Lite对比
| 特性 | ACE | ACE-Lite |
|---|---|---|
| 支持Cache一致性 | 是(完整MOESI) | 是(仅读取一致性) |
| 支持Snoop(监听) | 是 | 否 |
| 支持Dirty数据共享 | 是 | 否 |
| 典型设备 | CPU集群、GPU | DMA控制器、网卡 |
| 硬件复杂度 | 高 | 低 |
1. 对于ACE-Lite设备(如DMA),写入操作不会自动使CPU Cache失效。如果DMA写入内存后CPU需要读取,驱动仍需调用
dma_sync_single_for_cpu() 来使Cache行无效。2. 对于ACE设备(如GPU),读写操作都会自动维护一致性,驱动通常无需手动刷新Cache。
3. 在编写驱动时,应查阅SoC参考手册,确认DMA控制器连接的是ACE还是ACE-Lite接口。
4.4 本章小结
- Cache Stale问题是DMA与Cache不一致的根本原因,驱动开发者必须通过正确的API管理DMA缓冲区。
- MESI/MOESI协议是硬件维护Cache一致性的基础,MOESI通过O状态优化了多核共享性能。
- CCI/ACE提供了硬件级别的Cache一致性互联,ACE-Lite设备(如DMA)仍需部分软件干预。
- 在实际项目中,理解硬件一致性能力可以显著减少不必要的Cache刷新操作,提升驱动性能。