CXL 协议基础:CXL.io、CXL.cache、CXL.mem 详解

CXL 协议不是单一路线,它内部其实拆成了三个子协议。我个人习惯把这三个协议看作一架飞机的三个引擎——各自负责不同任务,但一起飞。

为什么要分三个?说白了,因为连接的对象不一样。CPU 内部的缓存一致性、内存读写、以及传统的 I/O 设备,对延迟和语义的要求完全不同。CXL 干脆把每一种场景抽象成专用通道,而不是像 PCIe 那样一个通用协议包打天下。

核心要点:CXL 的三个子协议共享同一物理层(基于 PCIe 5.0/6.0),但逻辑上独立运作。你可以只实现 io,也可以全栈支持。

CXL.io —— 基础控制与发现通道

CXL.io 本质上是披着 PCIe 外衣的初始化通道。我在项目里第一次接触 CXL 时,最直观的感受就是:枚举、配置、中断这些老熟人全在 CXL.io 上跑。它负责的事情包括:

  • 设备发现与初始化——用标准 PCIe 配置空间告诉主机你是谁、有什么能力。
  • DMA 和 I/O 操作——传统的 MMIO、PIO 访问。
  • 中断传递——MSI/MSI-X 照样走这个通道。
  • 错误报告——AER 机制也挂在这里。

嗯,这里要注意:CXL.io 不负责缓存一致性。它就是 PCIe 的升级版握手通道。你想想看,如果 Type3 设备(比如内存池控制器)要报告链路错误,走的肯定是 CXL.io,而不是其他通道。

避坑指南:我曾经在一个 FPGA 实现中只配了 CXL.io 的全部能力,结果发现地址翻译服务(ATS)没开,导致后续 CXL.mem 的地址映射乱掉。一定要确认 CXL.io 中的 PCIe 功能集是否完整。

CXL.cache —— 缓存一致性守护者

CXL.cache 是让加速器或内存设备「假装自己是 CPU 核心」的关键。通过这个协议,设备可以维护自己的本地缓存,并且跟 CPU 的缓存保持一致性。

它工作起来像这样:设备通过 CXL.cache 发送监听请求(Snp)给 CPU,CPU 响应后给出数据或状态。反过来,设备也可以主动缓存写回(Back Invalidate)。

CXL.cache 消息类型 方向 作用
SnpData / SnpInv / SnpCur 设备→CPU 设备发起监听,读取或无效化 CPU 缓存行
WrCache / WrFill CPU→设备 CPU 将数据写回设备缓存
BackInvalidate 设备→CPU 设备主动告知 CPU 某缓存行已失效

我参与的一个项目里,Type3 设备用 CXL.cache 实现了页表缓存。思路是:将设备内部的页表项视为可缓存的,设备发 Snp 让 CPU 刷新对应的 TLB 条目。效果立竿见影,但也要小心——如果监听频率太高,CPU 侧的延迟会飙升。

警告:CXL.cache 只支持简单一致性模型(HDP 模式)。别指望能像多核 CPU 间那样做 MESI 协议的全拓扑监听。如果你的设备需要像普通 CPU 核心那样参与一致性全域,请用 CXL.mem + 缓存一致性引擎。

CXL.mem —— 内存语义的直通车

CXL.mem 才是 Type3 内存池化的灵魂。它提供处理器与设备之间的内存级访问语义,包括读(MemRd)、写(MemWr),并且自带一致性保障。

你想想看,当 CPU 访问 Type3 内存池中的地址时,它不需要经过传统的 PCIe 驱动层。CXL.mem 会直接在事务层插入缓存一致性逻辑,保证 CPU 发起的读写能被内存池控制器正确解析,并且保持与 CPU 自身缓存的协同。

我个人习惯把 CXL.mem 的操作模式分为两类:

  • HDP 模式(Host-managed Device-attached memory):主机管理一致性,设备只需处理简单的读写请求。适合低延迟场景。
  • Device Coherent 模式:设备有自己的缓存,并且需要主动参与一致性协议。更复杂,但能减少远端访问次数。

在 Type3 设备(比如 CXL 内存控制器)中,我们通常只用 HDP 模式——设备就是一个简单内存端,不缓存任何数据。这样设计最简单,也最容易验证。

项目经验:我调试过一个 CXL.mem 读延迟问题,发现 CPU 发 MemRd 后设备总是多等了几百纳秒。后来用逻辑分析仪抓包,原来设备把 MemRd 当普通 PCIe 读请求处理了,走了完整的 TLP 流量控制流程。正确做法是:CXL.mem 请求要使用固定缓存通道(FIC),绕过流量控制。这一改,延迟直接减半。

三大协议的工作层级

下面这张图把三个协议的关系画清楚了。底层 CXL.io 作为传输和发现骨架,上层 CXL.cache 和 CXL.mem 分别处理缓存一致性和内存访问。它们共享同一物理层,但逻辑上独立流水线。

CXL 三协议分层结构 CXL.mem 内存读写 · 一致性保障 · MemRd/MemWr CXL.cache 缓存一致性 · 监听/写回 · Snp/WrCache CXL.io 设备发现 · 配置空间 · DMA · 中断 PCIe 物理层 / 链路层 5.0 / 6.0 · 差分对 · CRC · 重传 三个子协议共享同一物理层,但在事务层分流处理 CXL.mem CXL.cache CXL.io 物理层

三个协议的协同工作

实际产品中,这三个协议不是孤立运行的。举个例子,当 CPU 发起一个 memcpy 到 CXL 内存池时:

  1. 首先通过 CXL.io 进行 DMA 描述符设置(配置空间、BAR 映射)。
  2. 发起 CXL.mem 写请求(MemWr),数据直接写入远端内存。
  3. 如果远端设备内部有缓存,则用 CXL.cache 做一致性维护(Snp 刷新 CPU 侧的陈旧副本)。

不用把次序背下来,你只要记住:io 负责管人和配置,mem 负责传数据,cache 负责刷缓存。我做过一个性能调优项目,把这三条路径的带宽分配理清了,吞吐量升了 30%。

一句话总结:遇到 CXL 调优问题,先确认是 CXL.io 的队列不足,还是 CXL.mem 的读/写比例不合理,或是 CXL.cache 的监听风暴——对症下药,不要乱改参数。