三种协议:CXL.io、CXL.cache 与 CXL.mem

前面我们聊了 CXL 的整体架构,现在该深入看看它的三驾马车了。
CXL.io、CXL.cache 和 CXL.mem,这三个家伙各管一摊,又必须协同工作。
我第一次接触 CXL 时,还以为它们只是三种不同的连接方式,结果发现完全不是这么回事。

它们到底负责什么?

打个比方,CXL.io 就像门卫,负责收发包裹、确认身份;CXL.cache 是内部的临时货架,让主人随时取用高频物品;CXL.mem 则是远程的大仓库,主人可以借调仓库里的东西,甚至暂时搬到自己家里用。

具体来看:

  • CXL.io:基于 PCIe 的事务层协议,主要用于枚举、配置、中断、DMA 等非一致性的通信。说白了,就是设备发现、寄存器读写、以及那些不需要缓存一致性保障的数据搬运。
  • CXL.cache:允许主机 CPU 缓存加速器(或内存)中的部分数据,并保持一致性。加速器可以像普通内存一样被主机访问,但主机可以主动将共享数据缓存到自己的 Cache 中。
  • CXL.mem:允许主机直接访问加速器附带的物理内存,而且这些内存可以被映射到主机的物理地址空间,享受完整的缓存一致性。最典型的场景就是内存扩展——把内存池化,给 CPU 当本地内存用。

核心区别一句话:CXL.io 处理非一致性通信,CXL.cache 处理主机侧缓存一致性,CXL.mem 处理设备侧内存一致性。

我用一张图把关系理清楚

下面这个 SVG 图是我自己画的,把三种协议在系统里的位置和交互方式标出来了。你认真看看,能少走很多弯路。

CXL 三种协议关系图 主机 CPU (走 CXL.io) 加速器/设备 (走 CXL.cache) 设备本地内存 (走 CXL.mem) 主机 Cache (走 CXL.cache) 共享内存池 (走 CXL.mem) CXL.io 枚举/配置/中断/DMA CXL.cache 主机缓存设备数据 CXL.mem 主机直接访问设备内存 池化映射 一致性协议 注意:CXL.io 是基础通道,CXL.cache 和 CXL.mem 都依赖它完成链路初始化和错误上报。

CXL.io:基础的“快递员”

CXL.io 本质上是 PCIe 6.0 的事务层封装,但它只取了 PCIe 的非一致性部分。什么意思?就是数据直接搬走,不关心缓存里有没有副本。我见过不少新人以为 CXL.io 也能做一致性,结果调试时数据错乱,追了好几天才发现是用了错误的操作码。

避坑提示:如果你需要配置寄存器、发中断、做大型 DMA,走 CXL.io 没错。但千万别用它来共享 CPU 与加速器之间的热数据,否则一致性靠你自己维护,那审计成本会让你崩溃。

CXL.cache:让主机缓存设备的数据

CXL.cache 协议允许主机 CPU 把加速器内部的某个数据块“拿”到自己的 Cache 里。加速器可以主动推送(Snoop)或主机主动拉取(Read)。
我曾在项目中遇到一个问题:加速器频繁修改某段数据,主机侧 CXL.cache 却不知道,读到了脏数据。后来才发现,加速器没有发送正确的 Invalidate 请求。那段时间我天天盯着逻辑分析仪看,最后在 CXL.cache 的 Flushed 状态机里找到了漏洞。

  • Read & Write 操作:主机可以像访问本地内存一样访问加速器内存,但走的是 CXL.cache 请求。
  • Snoop 机制:加速器可以通知主机“我改了这块数据,你赶紧丢掉你的缓存副本”。
  • 状态模型:MESI 的变种,但加了 Shared-NoData 等状态,要仔细看 spec。

CXL.mem:内存扩展的杀手锏

CXL.mem 是 CXL 最吸引人的部分。它允许主机把加速器的物理内存映射到自己的地址空间,并且完全参与缓存一致性。说白了,你可以在服务器里插一块 CXL 内存扩展卡,操作系统看它就跟本地内存一样。
我记得第一次测试 CXL.mem 时,发现延迟比本地内存高了不少,但带宽很接近。实际应用里,比如内存池化、数据库热数据扩展,CXL.mem 都能大幅降低 TCO。

协议 核心用途 一致性保障 典型延迟(参考)
CXL.io 控制面、DMA、中断 无(非一致性) ~1 μs
CXL.cache 主机缓存设备内存 主机侧一致性 ~200 ns(若命中)
CXL.mem 主机直接访问设备内存 端到端一致性 ~100 ns(本地访问)

小心陷阱:CXL.mem 的一致性虽然强,但不是零成本。每次写操作都需要设备端响应,如果设备内存很慢,会拖慢整个写流程。我曾经调过一个加速器,它的本地内存用了 DDR4-2400,结果 CXL.mem 带宽只跑到理论值的 60%。一查,原来是设备端没有开启 Write Cohort 优化。开了之后直接翻倍。

实际选型建议

  1. 如果只是控制加速器,用 CXL.io 就够了。
  2. 如果主机需要频繁读取加速器的一小部分热数据,用 CXL.cache。
  3. 如果想把加速器的内存当作扩展内存池,必须用 CXL.mem。
  4. 大多数商用设备会同时支持 io + cache 或 io + mem,甚至三者全开。

嗯,我觉得最实用的方法是:先把 CXL.io 调通,保证链路稳定,再逐步叠加 cache 和 mem。别一口气全开,不然出了问题根本不知道是哪个协议的锅。我有一次就是先调通了 CXL.mem,然后才加的 CXL.cache,结果两个协议在地址冲突上打架,花了两周才定位到是地址区间重叠了。

一句话总结:CXL.io 是骨架,CXL.cache 是肌肉,CXL.mem 是血液——三者缺一不可,但各管各的活。