READ/WRITE 操作和本地内存操作的 RDMA 内存语义

RDMA memory semantics for READ/WRITE operations and local memory operations

我看到 RDMA 读取的意外结果让我怀疑我对 RDMA 读写语义的理解。

我正在尝试以类似于 L5 的方式在 RMDA 中实现消息传递,但是 运行 遇到了看起来像内存撕裂的问题。但这不应该发生。

我有一个比 L5 更复杂的结构:

struct Header {
   std::atomic<uint8_t> mailbox = 1;
   std::atomic<uint32_t> length; 
   char data[128];
};

在写入方面,我执行 RDMA 读取,直到我在邮箱中看到值 1。然后我执行长度 + 数据的 RDMA 写入,将邮箱设置为 0,并使用第二个 RDMA 写入发送邮箱。 在阅读方面,我检查邮箱 == 0,读取数据,并将长度设置为 0,将邮箱设置为 1。

当我进行 RDMA 读取时,我偶尔会看到长度 <> 0 以及邮箱值 0。由于 RDMA 操作应该按顺序发生,我不明白这是怎么发生的。

一个可能的解释是,如果您针对整个 struct Header 执行 RDMA 读取,则无法保证目标 RDMA 适配器将从内存中读取的顺序以满足该读取。特别是因为你的结构没有与缓存行大小对齐(我猜你在 x86 上,缓存行是 64 字节),所以 mailboxlength 可能在不同的缓存行中。

我仍然不太明白为什么看到 length != 0 和 mailbox == 0 会令人惊讶 - 阅读方不就是这样吗?根本没有处理邮箱?根据您所写的内容,从写入端进行两次 RDMA 写入后,结构的最终状态正好是 length != 0, mailbox == 0.

在任何情况下,由于如上所述,RDMA 适配器完全可以自由读取 RDMA 以任何顺序读取的内存,因此 RDMA 可以读取 return 新旧混合的任何内容数据,无论 CPU 更新字段的顺序如何。例如,如果您有:

  1. RDMA 读取进入目标 struct Header
  2. RDMA 适配器使用 length 字段读取缓存行
  3. CPU 更新 length 到 0 然后 mailbox 到 1
  4. RDMA 适配器使用 mailbox 字段读取缓存行

然后 READ 读取将获取 length != 0,mailbox == 1。这是因为 RDMA 读取操作不参与任何内存屏障或其他排序,即使您将您的结构成员声明为原子的。