Featured image of post RDMA 之 Memory Window

RDMA 之 Memory Window

本文转载于知乎专栏:14. RDMA 之 Memory Window,作者:Savir。为了更灵活方便的控制内存访问权限,IB 协议设计了 MW。本文主要介绍 MW 的作用、和 MR 的关系、接口以及分类。另外相比 MR 一文,对 L_Key 和 R_Key 进行了更深入的介绍。

# RDMA 之 Memory Window

本文欢迎非商业转载,转载请注明出处。

声明:仅用于收藏,便于阅读

Savir, 知乎专栏:14. RDMA 之 Memory Window

本文是“RDMA 杂谈”专栏文章的第 14 篇,欢迎转载,转载请注明出处

我们在 【RDMA 之 Memory Region】 一文中介绍过 Memory Region,它是一片由用户注册的特殊的内存区域:一方面其中的内容不会被换页到硬盘中,另一方面 RDMA 网卡中记录了它的地址转换关系,使得硬件拿到用户指定在 WR 中的虚拟地址之后找到对应的物理地址。

本文我们来讲解 Memory Window 的概念,它是一种基于 Memory Region 的、更灵活的内存管理单元。除了 MW 的概念之外,本文也会更详细的介绍一些 RDMA 领域的内存相关概念,比如 L_Key/R_Key 等。本文配合 【RDMA 之 Memory Region】 阅读效果更佳,建议先读者温习一下。

# Memory Window 是什么

Memory Window 简称 MW,中文就翻译成内存窗口吧。是一种由用户申请的,用于让远端节点访问本端内存区域的 RDMA 资源。每个 MW 都会绑定(称为 bind)在一个已经注册的 MR 上,但是它相比于 MR 可以提供更灵活的权限控制。MW 可以粗略理解为是 MR 的子集,一个 MR 上可以划分出很多 MW,每个 MW 都可以设置自己的权限。MW 和 MR 的关系如下图所示:

2024-06-28_12_1

MR 与 MW 的关系

# 内存的访问权限控制

为了后文说明为何设计 MW,我们先来把 MR 和 MW 都涉及的权限控制讲解一下。

# MR/MW 的权限配置

这里的权限,指的是本端/对端节点,对于本端内存的读/写权限,它们两两组合形成了四种权限:

本端对端
Local ReadRemote Read
Local WriteRemote Write

除了这四种权限之外,还有 Atomic 权限等,不在本文讨论范围内。

上表中这四种权限中最低的是本地读(Local Read),是用户必须赋予 MR/MW 的权限,因为如果一块内存本地的用户都无法访问的话,那就失去意义了;另外还有个限制,如果某个 MR 需要配置远端写(Remote Write)或者还没介绍的远端原子操作权限(Remote Atomic),那么也一定要配置本地写(Local Write)权限。在此约束之下,每个 MR 或者 MW 都可以按需配置权限,比如我们注册的一个 MR 需要允许远端节点写入数据,而不允许读,那么我们就打开 Remote Write 权限,关闭 Remote Read 权限。这样 HCA(网卡)收到对端发起的对这个 MR 范围内的某个地址的 WRITE 请求之后,就可以予以放行;而 HCA 收到对端对这个 MR 的 READ 操作时,就会拒绝这个请求,并返回错误信息给对端。

# Memory Key

上述的访问权限配置,并不能杜绝恶意用户对于本地或者远端内存的访问。比如某个节点给了一块内存区域的 Remote Write 权限,那么岂不是任意远端节点(进程)只要传入了合法的地址信息,都可以对这片区域进行写入了?因此,IB 规范设计了 Memory Key,简单理解它就是访问 MR 的钥匙机制,只有持有正确的钥匙,才能打开 MR/MW 的大门。

Key 是一串数字,由两部分组成:24bit 的 Index 以及 8bit 的 Key:

2024-06-28_12_2

L_Key/R_Key 的组成

其中,Index 用于 HCA 快速索引到本地的虚拟-物理地址转换表等 MR 相关的信息,而 Key 用于校验整个字段的合法性,以防止未授权的用户任意传递 Index。

Memory Key 按照用途分为两种,Local Key 和 Remote Key:

# L_Key

即 Local Key,关联到一个 MR 上,用于 HCA 访问本端内存。当本端的某个进程试图使用一个已经注册的 MR 的内存时,HCA 会校验其传递的 L_Key。并且利用 L_Key 中的索引查找地址转换表,把虚拟地址翻译成物理地址然后访问内存。

我们在 【RDMA 之 Shared Receive Queue】 一文中描述过 sge,sge 由起始地址、长度和秘钥组成。用户在填写 WR 时,如果需要 HCA 访问本端内存,那么就需要通过一个 sge 的链表(sgl)来描述内存块,这里 sge 的秘钥填的就是 L_Key,也就是下图中的 key1 和 key3,他们分别是 MR1 的 L_Key 和 MR2 的 L_Key。如果没有 L_Key,那么任何一个本地用户进程都可以指挥硬件访问其他本地用户注册的 MR 的内容,硬件也难以高效的将虚拟地址翻译成物理地址。

2024-06-28_12_3

L_Key 的作用

# R_Key

即 Remote Key,关联到一个 MR 或者 MW 上,用于远端节点访问本端内存。当远端节点试图访问本端的内存时,一方面本端的 HCA 会校验 R_Key 是否合法,另一方面会利用 R_Key 中的索引查地址转换表,把虚拟地址翻译成物理地址然后访问内存。

凡是 RDMA 操作(即 Write/Read/Atomic),用户都要在 WR 中携带远端内存区域的 R_Key。

2024-06-28_12_4

R_Key 的作用

IB 规范通过上述两种机制,来确保 MR 可以按照用户的期望被正确且安全的访问。我们用一个比喻来总结下 MR/MW 权限控制相关的内容:

A 给自己的房间(MR)配了两把钥匙(Memory Key),一把留作自用(L_Key),另一把钥匙(R_Key)邮寄(可以是任何通信方式)给了 B。B 可以在 A 不在家的时候(本端 CPU 不感知远端节点对本地内存的 RDMA 操作),通过钥匙(R_Key)打开门。打开门之后,可能 B 只能隔着玻璃查看房间的摆设(A 只给了这个 MR 远程读权限),或者进入房间内发现漆黑一片什么也看不到,但是可以向房间里放物品(A 只给了这个 MR 远程写权限),当然也有可能没有玻璃也开了灯(同时给了远程读写权限)。

# 为什么要有 MW

简而言之,设计 MW 的目的就是想更灵活的控制内存的远程访问权限。

【RDMA 之 Memory Region】 一文中我们介绍过用户注册 MR 的过程,需要从用户态陷入内核态,调用内核提供的函数 pin 住内存(防止换页),然后制作虚拟-物理地址映射表并下发给硬件。

因为 MR 是由内核管理的,如果用户想修改一个已经存在的 MR 的信息,比如我想收回某个 MR 的远端写权限,只保留远端读权限;或者想要使一个之前已经授权给远端节点的 R_Key 失效,那么用户需要通过重注册 MR(Reregister MR)接口来进行修改,该接口等价于先取消注册 MR(Deregister MR),然后注册 MR(Register MR)。上述流程需要陷入内核态来完成,而这个过程是耗时较长的

不同于需要通过控制路径修改权限的 MR,MW 在创建好之后,可以通过数据路径(即通过用户态直接下发 WR 到硬件的方式)动态的绑定到一个已经注册的 MR 上,并同时设置或者更改其访问权限,这个过程的速度远远超过重新注册 MR 的过程

那么现在为了使一片内存能够被远端节点进行 RDMA WRITE/READ 操作,我们就拥有了注册 MR 以及注册 MW 然后绑定到一个已注册的 MR 两种方式,它们都会产生一个 R_Key 来提供给远端节点。前一种方式准备阶段的步骤简单,但是不够灵活,一旦注册之后修改起来会比较麻烦;后一种方式相比前一种多了注册 MW 和绑定 MW 到 MR 两种操作,但是可以方便迅速的控制远端访问权限。

# MW 和 MR 权限的关系

也许有的读者会想到,MR 申请时配置了自己的权限,MW 绑定到 MR 时也会配置自己的权限,这两者的权限是什么关系呢?IB 规范在 10.6.7.2.2 节有专门介绍:

When binding a Memory Window, a Consumer can request any combination of remote access rights for the Window. However, if the associated Region does not have local write access enabled and the Consumer requests remote write or remote atomic access for the Window, the Channel Interface must return an error either at bind time or access time.

总结来说,如果想要给 MW 配置远程写或者远程原子操作(Atomic)权限,那么它绑定到的 MR 必须有本地写权限,其他情况下两者权限互不干扰:远端用户用 MW,就要遵循 MW 的权限配置;远端用户用 MR,就要遵循 MR 的权限配置。

# 用户接口

老样子,用户接口时我们按照控制路径和数据路径来分类:

# 控制路径

MW 支持增、删和查,不能直接修改:

# 创建——Allocate MW

申请 MW,主要是创建 MW 相关的软件结构和让硬件做好准备,用户需要指定后文中介绍的 MW 的类型。这个接口会产生一个 Memory Window 的句柄,用户以后可以用这个句柄指代这个 MW。

注意此时 MW 没有绑定到 MR 上,处于不可从远端访问的状态。

# 删除——Deallocate MW

取消注册 MW。很好理解,就是销毁相关资源。

# 查询——Query MW

查询 MW 的信息,包括 R_Key 及其状态、MW 类型以及 PD 等。

需要再次强调的是,虽然这个 Verbs 在 IB 规范中有描述,但是并没有在 RDMA 软件栈中实现相关的 API。类似情况的 Verbs 接口还有不少,RDMA 软件栈以实用为原则,没有用户需求的接口一般都没有实现。

# 数据路径

MW 在数据路径有一套独特的接口,分为 Bind 和 Invalidate 两类:

# 绑定——Bind

Bind(ing)意为“绑定”,指的是将一个 MW“关联”到一个已经注册的 MR 的指定范围上,并配置一定的读写权限。绑定的结果会产生一个 R_key,用户可以把这个 R_Key 传递给远端节点用于远程访问。注意一个 MW 可以被多次绑定,一个 MR 上也可以绑定多个 MW。如果一个 MR 还有被绑定的 MW,那么这个 MR 是不能被取消注册的。

2024-06-28_12_5

Bind 的软硬件交互

Bind 有两种方式,一种是调用 Post Send 接口下发 Bind MW WR,一种是调用 Bind MW 接口。

  • Post Send Bind MW WR

前文我们讲过,相比于 MR,MW 最大的优势就是可以从数据路径快速的配置权限。Post Send Bind MW WR 操作,指的就是用户通过 post send 接口(比如 ibv_post_send())下发一个 WR 到 SQ 中,这个 WR 的操作类型(比如 SEND/RDMA WRITE/RDMA READ)被指定为 BIND MW,此外 WR 中还携带有权限和要绑定到的 MR 的范围信息。与其他 WR 不同,下发 Bind MW 的 WR 之后,硬件并不会发送任何数据包,而是将 MW 绑定到了指定 MR 上。

这种方式仅适用于后文介绍的 Type 2 的 MW。

  • Bind MW

虽然这是一个独立的接口,但是实际是在 Post Send Bind MW WR 外面又封装了一层。用户传入 MW 绑定的相关信息,包括权限及要绑定的 MR 的信息,驱动程序负责组装和下发 WR 到硬件中。该接口成功后,会将新生成的 R_Key 返回给用户。

这种方式仅适用于后文介绍的 Type 1 的 MW。

上述两种操作的关系是这样的:

2024-06-28_12_6

两种 Bind 操作的关系

# 无效化——Invalidate

Invalidate 意为无效化,指的是用户通过下发一个带有 Invalidate 操作码的 WR 到硬件而使一个 R_Key 无效的操作。

需要强调的是,Invalidate 操作的对象是 R_Key 而不是 MW 本身,即 Invalidate 之后的效果是:远端用户无法再使用这个 R_Key 访问对应的 MW,而 MW 资源仍然存在,以后仍然可以生成新的 R_Key 给远端使用。

Invalidate 操作只能用于下文介绍的 Type 2 的 MW。

按照 Invalidate 操作的发起方不同,又可以进一步分成两种:

  • Local Invalidate

本地无效操作。上层用户如果想在不回收 MW 资源的情况下,收回某个远端的用户的 R_Key 的权限。那么就可以下发一个 Local Invalidate 操作到 SQ 中,硬件收到之后会对相应的 MR 的配置进行修改。成功执行之后,如果持有这个 R_Key 的远端用户想要对 MW 进行 RDMA 操作,将会被本地的硬件拒绝并返回错误。

因为是本地操作,所以硬件收到这个 WR 之后也不会发送消息到链路上。

2024-06-28_12_7

Local Invalidate 操作的软硬件交互

  • Remote Invalidate

远端无效操作。当一个远端用户不再使用一个 R_Key 之后,可以主动发送消息,让本端回收这个 R_Key。远端用户下发一个带有此操作码的 WR 到 SQ 中,其硬件收到后,将会组装一个报文并发送到本端。本端硬件收到远端的 Remote Invalidate 操作之后,将会把对应的 R_Key 置为不可用状态。同 Local Invalidate 一样,此后对端将无法使用这个 R_Key 对对应的 MW 进行 RDMA 操作。

2024-06-28_12_8

Remote Invalidate 操作的软硬件交互

# MW 的类型

根据实现和应用场景的不同,IB 规范对 MW 进行了分类:

# Type 1

Type 1 的 MW 通过 PD 和一个 QP 关联,不会绑定到一个 QP 上,所以也不会影响销毁同一个 PD 下的 QP。

Type 1 的 MW 的 R_Key 的 key 域段由驱动和硬件掌握,这里“掌握”的意思是,由驱动和硬件分配 key,而不是上层用户。这也是前文中说 Type 1 的 MW 不能被执行 Invalidate 操作的原因。如果 Type 1 MW 的用户想要使一个 R_Key 失效,那么重新通过 Bind MW 接口绑定一次这个 MW,硬件或者驱动就回自动分配一个新的 R_Key 的 key 域段,原有的 R_Key 也就失效了。

此外,如果用户暂时想要使一个 MW 不再绑定到任何 MR,但是又想保留相关的资源而不是销毁这个 MW,那么可以通过调用 Bind MW 接口,并将 MW 长度设置为 0 来实现。

IB 规范允许多个 Type 1 MW 绑定到同一个 MR 上,范围可以相互覆盖。

# Type 2

Type 2 的 MW 赋予了用户更大的自由度,其 R_Key 的 key 域段由用户掌握,即用户想怎么分配就怎么分配。前文已经讲过,用户通过 Post Send Bind MW WR 操作来进行绑定,这个过程并不会返回 R_Key。用户必须记住 Allocate MW 时的 index,并且和其选择的 8 bit key 组成 R_Key 并发送给对端。

用户可以通过前文介绍过的 Invalidate 操作来使一个 R_Key 无效,如果想要分配一个新的 R_Key 到 MW 上,必须先通过 Invalidate 操作无效之前的 R_Key。

与 Type 1 不同,Type 2 的 MW 不支持 0 长度的绑定。

IB 规范同样也允许多个 Type 2 绑定到同一个 MR 上,范围可以相互覆盖。

此外,根据绑定关系不同,Type 2 还可以分为两种实现方式,它们的差异仅在于和 QP 的绑定关系上。

# Type 2A

通过 QPN 和一个 QP 关联,也就是说远端访问这个 MW 范围内的内存时候,除了 R_Key 之外,还必须指定正确的 QPN。如果一个 QP 上还有绑定的 Type 2A 的 MW,那么这个 QP 不可以被销毁。

# Type 2B

通过 QPN 和 PD 与一个 QP 关联,比 Type 2A 多了个 PD 的校验,即远端通过 RDMA 操作访问 MW 的内存时,除了 QPN 要正确之外,其指定的本端 QP 的 PD 要与绑定这个 MW 时的 PD 相同。另外,与 Type 2A 不同,QP 如果还有 Type 2B MW 绑定关系时是可以被销毁的。

这里 IB 规范中原有的介绍就比较分散,我们来简单总结一下几种 MW 的异同:

Type 1Type 2AType 2B
关联关系PDQPPD + QP
R_Key 的 key 域归属驱动+硬件用户用户
绑定方式Bind MW 绑定后之前的 R_Key 自动失效Post Send Bind MWWR 绑定前需要先使之前的 R_Key 无效化Post Send Bind MWWR 绑定前需要先使之前的 R_Key 无效化
是否支持零长度
是否支持 Invalidate
关联的 QP 是否可以被销毁-

此外,IB 规范中对上述几种类型还有如下描述:HCA 必须实现 Type 1 的 MW,另外可以仅选择实现 Type 2A 和 2B 中的一种。Type 1 和 Type 2 的 MW 可以同时关联到同一个 MR 上。因为我了解到的应用程序中使用 MW 的情况不多,所以具体在什么场景下应该使用哪种 MW 也说不出所以然来,如果读者有对这方面的了解欢迎一起交流。

好了,MW 就讲到这里,到此为止 RDMA 技术中常见的资源就都介绍完了。

鉴于一般支持 RDMA 的设备都比较昂贵,下一篇我将介绍如何通过软件模拟设备的方式——即 Soft-RoCE 进行一些编程实验。

# IB 规范相关章节

  • 3.5.3 Memory Keys 介绍

  • 9.4.1.1 Invalidate 操作

  • 10.6.7 权限管理

  • 11.2.10.9~12 相关 Verbs 介绍

# 参考文档

[1] IB Specification Vol 1-Release-1.4

[2] Linux Kernel Networking - Implementation and Theory. Chapter 13

本博客已稳定运行
总访客数: Loading
总访问量: Loading
发表了 73 篇文章 · 总计 323.73k

使用 Hugo 构建
主题 StackJimmy 设计
基于 v3.27.0 分支版本修改