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直接读取物理内存也会得到旧数据。

核心矛盾:DMA绕过Cache直接操作物理内存,而CPU通过Cache操作数据。两者看到的“内存视图”不一致。

典型的Cache Stale场景分为两种:

  • DMA写入,CPU读取(DMA-to-CPU):外设通过DMA将数据写入物理内存,但CPU的Cache中可能还保留着该地址的旧数据。CPU读取时命中Cache,得到陈旧数据。
  • CPU写入,DMA读取(CPU-to-DMA):CPU将数据写入Cache(标记为Dirty),但尚未写回物理内存。DMA直接从物理内存读取,得到的是旧数据。
工程经验:在驱动开发中,DMA缓冲区必须通过特定的API进行管理,确保在DMA传输前后执行必要的Cache刷新操作。常见的API包括 dma_alloc_coherent()(一致性映射)和 dma_map_single() + dma_sync_single_for_cpu/device()(流式映射)。
DMA与Cache Stale问题示意图 DMA与Cache Stale问题示意图 CPU Cache (陈旧数据) 物理内存 (新数据) DMA控制器 外设 DMA写入 Cache未更新 读取 问题说明: 1. 外设通过DMA将新数据写入物理内存(绿色节点)。 2. CPU的Cache(红色节点)中仍保留旧数据,CPU读取时命中Cache,得到陈旧数据。 3. 解决方案:在DMA传输前后执行Cache刷新/无效化操作。

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 该行无效,不包含有效数据 否 —
MESI协议核心思想:当CPU要写入一个Cache行时,如果该行处于Shared状态,必须先发送“读所有权”(Read For Ownership, RFO)信号,使其他Cache中的副本失效,然后才能写入。这保证了同一时刻只有一个Cache持有Modified状态的行。
MESI Cache一致性协议状态转换图 MESI Cache一致性协议状态转换图 M (Modified) E (Exclusive) S (Shared) I (Invalid) Writeback Local Write Read Miss (shared) Snoop Invalidate Write (RFO) Snoop Invalidate Read Miss (exclusive) 状态转换说明: • Read Miss:Cache未命中,从内存或其他Cache读取数据。若其他Cache有副本,则进入S;否则进入E。 • Write Miss:写入未命中,需要先获取所有权(RFO),使其他副本失效,然后写入。 • Local Write:在E或M状态下直接写入,无需通知其他Cache。 • Snoop Invalidate:监听到其他Cache对该行的写入请求,使本Cache行失效。 • Writeback:将M状态的行写回内存,状态变为E。 • RFO:Read For Ownership,读取并获取独占权限,用于写入操作。

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负责在必要时将数据写回内存。减少了不必要的内存写回操作。
MOESI vs MESI:MOESI通过O状态减少了内存访问次数,提高了多核共享数据的效率。在嵌入式多核SoC中,MOESI更为常见。例如,ARM的AMBA 4 ACE协议就支持MOESI一致性模型。

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控制器通过ACE-Lite接口连接到CCI时,DMA传输的数据会自动保持与CPU Cache的一致性。这意味着驱动开发者无需手动执行Cache刷新操作,硬件会自动处理。但前提是DMA缓冲区必须使用一致性映射(如 dma_alloc_coherent)或正确配置流式映射。
CCI/ACE硬件一致性架构图 CCI/ACE硬件一致性架构图 CPU Cluster 0 Cortex-A72 L1/L2 Cache CPU Cluster 1 Cortex-A53 L1/L2 Cache CCI-500/600 Cache Coherent Interconnect ACE / CHI 接口 DMA控制器 ACE-Lite 接口 无Cache GPU ACE 接口 支持一致性 DDR 内存控制器 ACE ACE ACE-Lite ACE AXI 硬件一致性优势: • CPU Cluster 0/1 通过ACE接口与CCI连接,自动维护Cache一致性。 • DMA通过ACE-Lite接口可以读取一致性的数据,无需软件干预。

4.3.2 ACE与ACE-Lite对比

特性 ACE ACE-Lite
支持Cache一致性 是(完整MOESI) 是(仅读取一致性)
支持Snoop(监听) 是 否
支持Dirty数据共享 是 否
典型设备 CPU集群、GPU DMA控制器、网卡
硬件复杂度 高 低
重要提示:即使硬件支持CCI/ACE一致性,驱动开发者仍需注意:
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刷新操作,提升驱动性能。