第2章:RDMA / OCC / 分离式架构必要前置
看 AURA 论文之前要打牢的概念地基:RDMA verbs 与 atomic 语义、ConnectX 各代行为差异、OCC 三阶段、CN/MN 数据流、masked CAS
第 1 章把”为什么动态锁所有权是必须的”立住了。但要看懂 AURA 论文与所有相关工作,还需要一组更基础的概念:RDMA verbs 怎么调用、atomic 在不同 NIC 上有什么差异、masked CAS 是干嘛的、OCC 三阶段在 DM 架构里到底走哪几步。本章把这些”看 paper 之前必须会”的基础打牢——读完你应该能徒手画出 OCC commit 路径的 RDMA op 序列,并且看到 IBV_EXP_WR_EXT_MASKED_ATOMIC_CMP_AND_SWP 不会一头雾水。
📑 目录
- 1. RDMA verbs 速通:READ / WRITE / CAS / FAA
- 2. ConnectX 代际差异:从 mlx4 到 mlx5/6
- 3. masked CAS 与 extended atomic:什么是 CREST 的依赖
- 4. OCC 三阶段模型与 DM 架构对应
- 5. CN / MN 数据流图谱
- 6. 一次 commit 的完整 RDMA op 序列
- 7. 必须知道的 5 个失败模式
- 自我检验清单
- 参考资料
1. RDMA verbs 速通:READ / WRITE / CAS / FAA
1.1 verbs 的角色:用户态直接对网卡下指令
RDMA 把”发数据”这件事从内核态完全移到用户态。verbs(即 libibverbs 暴露的接口)是一组让用户进程直接对网卡(RNIC)下指令的 API。一次 RDMA op 不需要陷入内核、不需要拷贝、远端 CPU 也不参与——这是它和 TCP 最本质的差异。
用户态 内核态
────── ──────
ibv_post_send(qp, &wr, ...)
│
│ (mmap 过的 doorbell page)
▼
┌──────────────┐
│ RNIC TX ring │ 写一个 WQE(Work Queue Element)
└──────────────┘
│
│ 直接 DMA 到 NIC 寄存器
▼
网卡硬件
│
▼
RoCE / InfiniBand 包发出
🌟 核心结论:verbs 调用是 user-space → NIC 一步直达。这是为什么 RDMA 单 QP 能轻松跑 10M+ ops/sec——内核根本没参与。
1.2 四个最常用的原语
DM 事务系统几乎只用这四个 op:
| 原语 | 字节大小 | 远端 CPU 参与 | 在 OCC 中的角色 |
|---|---|---|---|
IBV_WR_RDMA_READ | 任意(最大 1GB) | 否 | 读 read set / version / lock 状态 |
IBV_WR_RDMA_WRITE | 任意(最大 1GB) | 否 | 写 write set / unlock |
IBV_WR_ATOMIC_CMP_AND_SWP(CAS) | 8B 固定 | 是(NIC 内部) | 取锁 / 推进 epoch |
IBV_WR_ATOMIC_FETCH_AND_ADD(FAA) | 8B 固定 | 是(NIC 内部) | 计数器 / 序列号 |
⭐ 重点观察:
- READ / WRITE 是 one-sided:发起方只要知道远端的
(addr, rkey),就能直接读写远端内存,远端 CPU 完全无感。 - CAS / FAA 也是 one-sided 但走特殊路径:远端 NIC 内部要做”读-比较-写”,必须仲裁。这就是第 1 章提到的 atomic IOPS 瓶颈来源。
- 8B 限制不是任意决定:RoCE/IB 协议头里 atomic 字段就是 8B 宽,硬件层面没有更宽的 atomic 支持。
1.3 一次 ibv_post_send 调用的完整结构
// 一次 RDMA CAS(取锁)的最简版
struct ibv_sge sge = {
.addr = (uintptr_t) local_buf, // 本地接收 original 8B 的 buffer
.length = 8,
.lkey = local_mr->lkey, // 本地 MR 的 lkey
};
struct ibv_send_wr wr = {
.wr_id = txn_id,
.sg_list = &sge,
.num_sge = 1,
.opcode = IBV_WR_ATOMIC_CMP_AND_SWP,
.send_flags = IBV_SEND_SIGNALED, // 完成后产生 CQE
.wr.atomic = {
.remote_addr = remote_lock_addr,
.compare_add = 0, // expected = 0(unlock)
.swap = my_tx_id, // 想换上的值
.rkey = remote_rkey,
},
};
struct ibv_send_wr *bad_wr = NULL;
int ret = ibv_post_send(qp, &wr, &bad_wr);
关键参数:
wr_id:用户自定义 64-bit ID,在 CQE(completion queue entry)里返回,用来识别”这次完成的是哪个 op”IBV_SEND_SIGNALED:要求完成后产生 CQE。不加这个 flag 就是”silent send”,看不到完成信号compare_add:CAS 的 expected 值swap:CAS 成功时写入的新值rkey:远端 MR 的访问 key(注册 MR 时返回,必须事先交换)
1.4 doorbell batch:一次写多个 WQE
把多个 op 串在一起 post,内核一次性 ring doorbell。这是 FORD / CREST 的高吞吐套路:
struct ibv_send_wr wr_array[8];
for (int i = 0; i < 8; i++) {
fill_wr(&wr_array[i], ...);
wr_array[i].next = (i == 7) ? NULL : &wr_array[i+1];
}
ibv_post_send(qp, &wr_array[0], &bad_wr); // 一次 doorbell
🌟 关键事实:doorbell 写代价高(PCIe 寄存器写),把多个 op 摊到一次 doorbell 是 RNIC 高吞吐的关键。但对 atomic 没有帮助——atomic 在 NIC 内部仍然串行化。
1.5 one-sided vs two-sided 的分水岭
| 维度 | one-sided(READ/WRITE/CAS/FAA) | two-sided(SEND/RECV) |
|---|---|---|
| 远端 CPU 参与 | 否 | 是(必须 post 一个 RECV) |
| 编程模型 | 类似共享内存 | 类似消息传递 |
| DM 系统选择 | 首选 | 仅在协议必需时使用 |
| 典型用例 | 读 record / 取锁 / 写数据 | 控制面 RPC / OwnerRpc |
⭕ 互补:RDMA SEND/RECV 不是”慢”,FaSST 论文(OSDI’16)甚至论证过 two-sided 在某些场景比 one-sided 快——因为远端 CPU 可以做更聪明的 batching。但 DM 事务的核心承诺就是”远端 CPU 不参与”,所以 90% 路径都要走 one-sided。
🧠 关键洞察:one-sided 让 MN 变成”被动内存”——这是 disaggregation 的本钱。每次你想引入一个需要 MN CPU 参与的协议,先问自己”能不能用 CAS + READ 替代”。
2. ConnectX 代际差异:从 mlx4 到 mlx5/6
2.1 三代 NIC 的关键参数对比
| 维度 | ConnectX-3 | ConnectX-4/5 | ConnectX-6 Dx |
|---|---|---|---|
| 协议 | IB / RoCE v1 | IB / RoCE v2 | IB / RoCE v2 |
| 链路速率 | 56 Gb FDR | 100 Gb EDR | 100/200 Gb HDR |
| 内核驱动 | mlx4_core | mlx5_core | mlx5_core |
| 用户态库 | libmlx4 | libmlx5 | libmlx5 |
| atomic CAS Mpps(单 QP) | ~1.0 | ~3.0 | ~3.0 |
| atomic CAS Mpps(多 QP 聚合) | ~1.0–2.6 | ~4–5 | ~5–10 |
max_atomic_arg | 0 / 8 | 32 | 32 |
extended verbs(ibv_exp_*) | 不支持(OFED 4.9 也只是模拟) | 支持 | 支持 |
IBV_EXP_QP_INIT_ATTR_CREATE_FLAGS | 不支持(errno=95) | 支持 | 支持 |
| masked CAS | 不支持 | 支持 | 支持 |
RoCEv2 gid_idx | 不适用(IB only) | 通常 3 | 通常 3 |
IB gid_idx | 0 | 0 | 0 |
数据来源:APT c6220 (ConnectX-3) + CloudLab c6525-25g (ConnectX-6 Dx) 实测,2026-04 / 2026-05。
2.2 mlx4 vs mlx5 驱动栈:分裂的代价
ConnectX-3 用 mlx4 驱动栈,从 ConnectX-4 起切到 mlx5——两个驱动几乎完全不兼容:
ConnectX-3 软件栈 ConnectX-5+ 软件栈
────────────────── ──────────────────
user app user app
│ │
libibverbs libibverbs
│ │
libmlx4 (provider) libmlx5 (provider)
│ │
/dev/infiniband/uverbs0 /dev/infiniband/uverbs0
│ │
mlx4_ib (kernel) mlx5_ib (kernel)
│ │
mlx4_core mlx5_core
🌟 工程后果:
- 同一个 RDMA 程序在 ConnectX-3 和 ConnectX-5 上跑,很多 verbs 调用结果不同(特别是 extended verbs)。
- APT 集群只有 ConnectX-3 → 上面只能跑 mlx4 驱动 → CREST/Motor 用
ibv_exp_*的部分会报错或行为异常。 - 装 OFED 4.9(最后一版兼容 ConnectX-3 的 MLNX OFED)也只能解决一部分问题,剩下的需要改代码。
2.3 atomic 在不同代上的真实行为
实测命令:
# server (任一节点)
ib_atomic_bw -d $DEV -i 1 -F -D 30 -A CMP_AND_SWAP
# client (另一节点)
ib_atomic_bw -d $DEV -i 1 -F -D 30 -A CMP_AND_SWAP $SERVER_IP
| 平台 | 设备 | 单 QP Mpps | 多 QP Mpps | 备注 |
|---|---|---|---|---|
| APT c6220 | mlx4_0 (CX-3 56Gb IB) | 2.59 | ~2.6(饱和) | 多 QP 不能聚合 |
| CloudLab c6525-25g | mlx5_2 (CX-6 Dx 100Gb RoCE) | ~3.0 | ~5.8 | 多 QP 部分聚合 |
🧠 关键洞察:CX-3 上 多 QP 不能聚合 atomic——因为 NIC 内部只有一个 atomic engine。CX-6 Dx 有多个 engine,能部分聚合。这意味着同样的 OCC 协议,CX-3 上 retry storm 出现得早得多——这是 AURA 在 APT 上做”跨硬件 portability + 放大 retry storm”对照实验的物理基础。
2.4 gid_idx:IB vs RoCEv2 的隐藏陷阱
gid_idx 是 GID 表项索引,决定了 QP 用什么”地址”做寻址。这个值代际之间不一样:
| 设备 | 协议 | 推荐 gid_idx | 命令验证 |
|---|---|---|---|
| ConnectX-3 mlx4 | IB | 0 | show_gids 或 cat /sys/class/infiniband/mlx4_0/ports/1/gids/* |
| ConnectX-6 Dx mlx5 | RoCEv2 (IPv4) | 3 | show_gids mlx5_2 1 看 RoCE v2 行 |
| ConnectX-6 Dx mlx5 | RoCEv2 (IPv6) | 通常 1 | 看 GID 类型 |
⭕ 常见踩坑:把 c6525 上的 gid_idx=3 配置直接搬到 APT,QP 建立成功但 send 后包根本到不了对端。原因是 APT 是真 IB 不是 RoCE。
🌟 首次连通排错口令:先 show_gids 看实际表项,确认 GID type 是 IB 还是 RoCE v2,再调 ib_dev_id 与 gid_idx。
2.5 OFED 4.9 / rdma-core 的选择策略
| 场景 | 选什么 | 命令 |
|---|---|---|
跑 CREST / Motor(需要 ibv_exp_*) | MLNX OFED 4.9 | ./mlnxofedinstall --user-space-only --skip-distro-check --without-depcheck |
| 跑 FORD(只用 standard verbs) | Ubuntu 22.04 inbox rdma-core | apt install librdmacm-dev libibverbs-dev |
| 跑 perftest | 任意 | OFED 自带或 apt install perftest |
⭕ APT 集群必踩的坑:
- MLNX OFED 4.9 不再发布 Ubuntu 22.04 包(只到 ubuntu20.04)→ 需
--skip-distro-check强装 - 它会想装
mstflint(依赖 libssl1.1,22.04 没有)→ 安装后dpkg -P --force-all mstflint,让 22.04 自己的 mstflint 上 - inbox 包
libfabric1libucx0libopenmpi3想拉回 inboxibverbs-providers→ 必须apt-mark hold住 OFED 包,再批量 purge
3. masked CAS 与 extended atomic:什么是 CREST 的依赖
3.1 为什么标准 CAS 不够用
标准 RDMA CAS 是”全 8B 比较 + 全 8B 替换”。但 OCC 的写锁字往往这样布局:
8 字节 lock word
┌─────────┬─────────┬───────────────┐
│ owner_id│ epoch │ flags / bits │
│ (16b) │ (16b) │ (32b) │
└─────────┴─────────┴───────────────┘
问题:你只想 CAS owner_id 字段,不关心 epoch 和 flags——但标准 CAS 会要求整个 8B 完全匹配,否则失败。这意味着如果有任何并发 op 改了 flags,你的 CAS 就误判失败了。
3.2 masked CAS:带掩码的原子比较交换
Mellanox extended verbs 提供:
IBV_EXP_WR_EXT_MASKED_ATOMIC_CMP_AND_SWP
它在标准 CAS 的基础上加了两个掩码:
compare_mask:哪些 bit 参与比较swap_mask:哪些 bit 实际被替换
语义:
原子地:
if (memory & compare_mask) == (compare_value & compare_mask):
memory = (memory & ~swap_mask) | (swap & swap_mask)
return (true, original_memory)
else:
return (false, original_memory)
🍎 直觉比喻:标准 CAS 是”必须整个房子都跟我描述的一致才换”,masked CAS 是”只要客厅的家具一致就换客厅的家具”。
3.3 CREST / Motor 怎么依赖它
CREST 的 OCC commit 路径要在一次 atomic 里同时:
- 检查”锁字段”未被占
- 检查”版本字段”是某个值
- 替换”锁字段”为自己的 tx_id
- 不动”版本字段”
四步合一,没有 masked CAS 就要拆成”普通 CAS + 单独 READ 验证”——多一次 RDMA RTT,且引入 race window。
3.4 ConnectX-3 不支持的工程影响
我们在 APT 上实测:
[Logger] ASSERTION FAILED: opcode IBV_EXP_WR_EXT_MASKED_ATOMIC_CMP_AND_SWP
not supported on this device
[Scheduler.cc:121] abort()
[bench_runner exit code 134 (SIGABRT)]
🌟 后果:CREST 和 Motor 完全无法在 ConnectX-3 上跑——这不是配置问题,是硬件能力缺失。FORD 只用 standard CAS,能跑通。
⭕ 应对策略:
- 跨硬件 portability claim 用 FORD 而不是 CREST
- AURA 主体实验留在 ConnectX-6 上(本来 atomic 就更紧)
- APT 上 retry storm 实验用 FORD + 自制 micro benchmark 验证趋势
4. OCC 三阶段模型与 DM 架构对应
4.1 模型回顾
OCC(Optimistic Concurrency Control)把事务分三阶段执行:
阶段 1:Read phase
─────────────────
读所有需要的 record,记下版本号 → read set
本地修改写集(不提交)→ write set
阶段 2:Validation
──────────────────
重新检查 read set 的版本号
- 一致 → 进入 commit
- 不一致 → abort + retry
阶段 3:Commit
──────────────
取所有写集的锁(CAS)
写入新数据 + 新版本
解锁
4.2 在 DM 架构上每阶段走什么 op
| 阶段 | 主要 op | 工作位置 | atomic 压力 |
|---|---|---|---|
| Read | RDMA READ ×N | CN | 0 |
| Validate | RDMA READ × | read_set | (再读版本) |
| Commit (acquire) | RDMA CAS × | write_set | |
| Commit (data) | RDMA WRITE × | write_set | |
| Commit (release) | RDMA WRITE × | write_set | (写回 unlock) |
🌟 关键观察:只有 commit 的 acquire 阶段消耗 atomic IOPS。read / write / release 都是 normal RDMA op。这就是为什么”优化 atomic”是切中要害的。
4.3 为什么 DM 系统不用 2PL
| 维度 | 2PL(两阶段锁) | OCC |
|---|---|---|
| 取锁时机 | 事务开始 | commit 阶段 |
| 冲突时 | 等待(阻塞) | abort + retry |
| 远端 CPU 参与 | 必须(锁状态机 + 唤醒) | 不必须(CAS) |
| 死锁 | 可能 | 不可能(无等待) |
🧠 关键洞察:2PL 的”等待”语义需要远端有线程持有锁、被唤醒——这违反 disaggregation 原则。OCC 用 CAS 把”取锁”做成 1 次原子写,远端无感知。所以所有 DM 事务系统都用 OCC。
4.4 SI / SR 隔离级别在 DM 上怎么实现
| 级别 | DM 实现 | 代表系统 |
|---|---|---|
| Read Committed | 读最新已提交版本 | (工业数据库默认,DM 论文不常用) |
| Snapshot Isolation (SI) | MVCC + 拉版本表 | Motor |
| Serializable (SR) | 单版本 + 严格 OCC | FORD |
⭕ 互补:FORD 单版本系统跑 SR;Motor 多版本系统支持 SI 也支持 SR(设 iso_level=2)。AURA 设计上不限定,但实验默认用 SR(最难,最能反映 atomic 压力)。
5. CN / MN 数据流图谱
5.1 CN 侧持有什么
CN (Compute Node)
──────────────────
┌─────────────────────────────────────┐
│ Transaction logic │
│ ↓ │
│ Local working set (read/write set) │
│ Local hot record cache │ ← 减少远端 READ
│ TxnRequest queue │
│ Coroutine scheduler │
├─────────────────────────────────────┤
│ RDMA QP / CQ pool │
│ MR for local buffers │
│ rkey 表(远端各 region 的 rkey) │
├─────────────────────────────────────┤
│ ★ AURA 增量: │
│ OwnerLockTable (本地锁表) │
│ AffinityRouter (路由表) │
│ AccessGraph snapshot │
└─────────────────────────────────────┘
5.2 MN 侧持有什么
MN (Memory Node)
──────────────────
┌─────────────────────────────────────┐
│ ❶ Data region (record bytes) │
│ - 按 partition 布局 │
│ - cache-line 对齐(FORD) │
│ - version table(Motor MVCC) │
├─────────────────────────────────────┤
│ ❷ Lock region (8B lock words) │
│ - 与 record 1:1 或 cache-line:1 │
│ - CREST 用 masked field │
├─────────────────────────────────────┤
│ ❸ Index region (hash / B+tree) │
│ - FORD 用 PoolHashIndex │
│ - 索引也是 RDMA 远端拉取 │
├─────────────────────────────────────┤
│ ❹ Persistent log (PMEM 时持久) │
├─────────────────────────────────────┤
│ MR + rkey 注册(CN 启动时拉取) │
├─────────────────────────────────────┤
│ ★ AURA 增量: │
│ FallbackLock 区域(继续兜底) │
│ OwnerMap snapshot 快照副本 │
└─────────────────────────────────────┘
5.3 关键观察:MN 是被动存储
MN 不主动做任何事:
- 不解析事务
- 不处理冲突
- 不维护锁状态机(只是被 CAS 覆写的 8B 字)
- 不参与版本回收
它的 RNIC 处理 RDMA 入站包,DMA 到/从主机内存,仅此而已。这是 disaggregation 的本钱也是天花板——天花板就是第 1 章讲的 atomic IOPS。
🍎 直觉比喻:MN 是一块”放在远端的 DRAM”,CN 通过 RDMA 网卡像访问本地内存一样访问它。CN 是 CPU,MN 是 RAM——这是模块十三第 1 章的核心比喻。
5.4 AURA 改了什么、没改什么
| 维度 | 原 CREST/Motor | AURA |
|---|---|---|
| MN 数据布局 | 不变 | 不变 |
| MN 锁字布局 | 8B masked field | 不变(fallback 路径仍用) |
| MN CPU 参与 | 不参与 | 不参与 |
| CN 路由 | 随机 / 哈希 | AffinityRouter |
| CN 锁表 | 无 | OwnerLockTable |
| CN 间 RPC | 无 | OwnerRpc |
| 控制面进程 | 无 | Profiler / Planner / Publisher |
🌟 关键事实:AURA 不改 MN 的物理布局,只在 CN 侧加新结构。这意味着可以渐进部署——同一个 MN 同时服务 AURA-aware 和 legacy 客户端。
6. 一次 commit 的完整 RDMA op 序列
6.1 TPC-C NewOrder 简化路径
NewOrder 事务在 TPC-C 里读 Warehouse / District / Customer,写 District(d_next_o_id) / NewOrder / Order / OrderLine ×N / Stock ×N。简化到 1 个 OrderLine + 1 个 Stock,op 序列如下:
Phase 1: Read
─────────────
① RDMA READ → W[wid] (warehouse, 读 w_tax, 不写)
② RDMA READ → D[wid,did] (district)
③ RDMA READ → C[wid,did,cid] (customer)
④ RDMA READ → S[wid,iid] (stock)
Phase 2: Validate(CREST 用版本字段 inline 在 record 里)
──────────────────────────────────────────────────────
⑤ RDMA READ → 重新读 ④ 的 version → 比对
Phase 3: Commit
───────────────
⑥ RDMA CAS → D.lock (acquire)
⑦ RDMA CAS → S.lock (acquire)
⑧ RDMA CAS → NO.lock (acquire, 新插入)
⑨ RDMA WRITE → D.data (新 d_next_o_id)
⑩ RDMA WRITE → S.data (新 stock 数量)
⑪ RDMA WRITE → NO.data (新行)
⑫ RDMA WRITE → D.lock = 0 (release)
⑬ RDMA WRITE → S.lock = 0 (release)
⑭ RDMA WRITE → NO.lock = 0 (release)
统计:
- READ:5 次
- CAS:3 次(消耗 atomic IOPS)
- WRITE:6 次
⭐ 关键观察:CAS 占总 op 的 21%,但消耗 100% 的 atomic 预算。这就是 atomic 优化的重要性来源——少量 op 决定整体上限。
6.2 doorbell batch 的优化空间
step ⑨⑩⑪ 的 3 次 WRITE 可以合成一次 doorbell:
struct ibv_send_wr writes[3];
writes[0].next = &writes[1];
writes[1].next = &writes[2];
writes[2].next = NULL;
ibv_post_send(qp, &writes[0], &bad_wr); // 一次 doorbell,发出 3 个 WRITE
CAS 也可以 batch(步骤 ⑥⑦⑧),但 NIC 内部仍然是串行执行。Batch 减少 CN 的 doorbell 写代价,不减少 MN 的 atomic 处理代价。
6.3 AURA 改了哪些步骤
| 步骤 | 原 CREST | AURA(owner CN 命中) |
|---|---|---|
| ① ② ③ ④ READ | RDMA READ → MN | 不变 |
| ⑤ Validate | RDMA READ → MN | 不变 |
| ⑥ ⑦ ⑧ CAS lock | RDMA CAS → MN(消耗 atomic) | OwnerLockTable 本地 lock(无 RDMA) |
| ⑨ ⑩ ⑪ WRITE data | RDMA WRITE → MN | 不变 |
| ⑫ ⑬ ⑭ release | RDMA WRITE → MN | OwnerLockTable 本地 release |
🌟 核心收益:AURA 把 ⑥⑦⑧⑫⑬⑭ 这 6 次 RDMA op 中的 atomic CAS(3 次)变成 0 次远端 atomic——整个 commit 只剩 normal READ/WRITE。这是 AURA 解决 atomic IOPS 上限的物理路径。
7. 必须知道的 5 个失败模式
OCC commit 路径上每一步都可能失败。学完它们才能看懂论文的 abort 分析章节。
7.1 失败模式 1:CAS abort
现象:步骤 ⑥ 的 CAS 返回 failure(original ≠ expected)。
原因:另一个事务先到、已经持有该锁。
处理:当前事务释放已经取得的锁,全部 abort,retry 整个事务。
if (atomic_cas_result.original != 0) {
release_already_acquired_locks();
return ABORT_LOCK_BUSY;
}
7.2 失败模式 2:version mismatch
现象:步骤 ⑤ 的 validate read 返回的 version 不等于 phase 1 时的 version。
原因:read set 在事务执行期间被改了。
处理:abort + retry。
🍎 直觉比喻:你在做菜时打开冰箱拿了 3 样食材,做到一半发现有人换了其中一样——你只能扔掉重做。
7.3 失败模式 3:RDMA timeout
现象:RDMA op 长时间没有 CQE。
原因:网络抖动 / 远端 RNIC 过载 / QP 状态错误。
处理:根据严重程度 retry op、retry 整个事务、或 reset QP。
7.4 失败模式 4:硬件不支持的 atomic 模式
现象:post 时返回 errno=95(EOPNOTSUPP)。
原因:NIC 不支持你用的 atomic 操作。最常见是 ConnectX-3 上跑 masked CAS。
处理:不可救——只能换 NIC 或换协议。
7.5 失败模式 5:MR 边界 / rkey 失效
现象:CQE 状态是 IBV_WC_REM_ACCESS_ERR 或 IBV_WC_LOC_PROT_ERR。
原因:
- 写远端地址超出 MR 注册区域
- 远端 MR 已经 dereg(rkey 失效)
- 本地 buffer 不在已注册的 MR 内
处理:检查地址计算、重连 QP、重新注册 MR。
⭕ 重要提示:MR 注册有数量上限(通常几千)。CREST/FORD 在 TPC-C 大规模 populate 时可能撞这个上限——APT 上踩过的坑。
✅ 自我检验清单
- verbs 速通:能不查文档写出
ibv_post_send一次 atomic CAS 的完整调用代码 - one-sided vs two-sided:能解释为什么 DM 系统几乎只用 one-sided
- doorbell batch:能解释为什么 batch 加速 WRITE 但不加速 atomic
- 代际差异:能说出 ConnectX-3 与 ConnectX-6 Dx 至少 4 个关键差异
- gid_idx 陷阱:能描述把 c6525 配置搬到 APT 时会发生什么
- masked CAS:能解释 CREST 为什么不能跑在 ConnectX-3 上
- OCC 三阶段:能徒手画 Read / Validate / Commit 三阶段在 DM 上的 RDMA op 序列
- 2PL vs OCC:能用一句话说清为什么 DM 系统不用 2PL
- CN / MN 数据流:能列出 CN side 与 MN side 各自持有的数据结构
- AURA 改动范围:能描述 AURA 改了 CN 侧什么、没改 MN 侧什么
- commit op 序列:能列出 TPC-C NewOrder 的 14 步 RDMA op
- 失败模式:能描述至少 4 种 OCC commit 路径上的失败模式
📚 参考资料
概念入门
- RDMA Aware Programming User Manual —— Mellanox 官方编程手册:Mellanox Docs
- libibverbs man pages ——
man ibv_post_send/man ibv_create_qp - 模块十三第 2 章 RDMA 通信原理与 verbs:本仓库
docs/guides/模块十三-新型互联与远程内存系统/第2章-RDMA通信原理与verbs.md
关键论文
- Design Guidelines for High Performance RDMA Systems (Kalia et al., USENIX ATC’16):USENIX 链接 —— 单边 vs 双边 RDMA 的系统级权衡
- FaSST (Kalia et al., OSDI’16):USENIX 链接 —— 反直觉论证:two-sided 在某些场景比 one-sided 更快
- FaRM (Dragojević et al., NSDI’14):USENIX 链接 —— 首篇 RDMA OCC,奠定 commit 路径范式
- FORD (Zhang et al., FAST’22):USENIX 链接 —— cache-line 对齐锁的实现,是理解 standard CAS 用法的最佳样本
- Motor (Wu et al., OSDI’24):USENIX 链接 —— MVCC + masked CAS 的 commit 路径
行业讨论
- NVIDIA ConnectX-6 Dx Programmer’s Reference Manual —— atomic 操作的硬件实现说明(NDA 许可)
- Mellanox Community: Show GIDs ——
show_gids工具用法
框架文档
- rdma-core 仓库:github.com/linux-rdma/rdma-core
- MLNX OFED 4.9 release notes:包含 ConnectX-3 兼容性最后状态
- CREST 仓库:本路线第 8/9 章实战载体