一、RDMA 技术出现之前
计算机网络通信中最重要两个衡量指标主要是 带宽 和 延迟 。 现实计算机网络中的通信场景中,主要是以发送小消息为主,因此处理延迟是提升性能的关键。 传统的TCP/IP网络通信,数据需要通过用户空间发送到远程机器的用户空间,在这个过程中需要经历若干次内存拷贝:

- 数据发送方需要讲数据从用户空间
Buffer复制到内核空间的Socket Buffer - 数据发送方要在内核空间中添加数据包头,进行数据封装
- 数据从内核空间的
Socket Buffer复制到NIC Buffer进行网络传输 - 数据接受方接收到从远程机器发送的数据包后,要将数据包从
NIC Buffer中复制到内核空间的Socket Buffer - 经过一系列的多层网络协议进行数据包的解析工作,解析后的数据从内核空间的
Socket Buffer被复制到用户空间Buffer - 这个时候再进行系统上下文切换,用户应用程序才被调用
在高速网络条件下,传统的TCP/IP网络在主机侧数据移动和复制操作带来的高开销限制了可以在机器之间发送的带宽。为了提高数据传输带宽,人们提出了多种解决方案,这里主要介绍下面三种:
- TCP Offloading Engine
- Direct Memory Access
Remote Direct Memory Access
二、TCP Offloading Engine
在主机通过网络进行通信的过程中,CPU 需要耗费大量资源进行多层网络协议的数据包处理工作,包括数据复制、协议处理和中断处理。当主机收到网络数据包时,会引发大量的网络 I/O 中断,CPU 需要对 I/O 中断信号进行响应和确认。为了将 CPU 从这些操作中解放出来,人们发明了**TOE(TCP/IP Offloading Engine)** 技术,将上述主机处理器的工作转移到网卡上。TOE 技术需要特定支持 Offloading 的网卡,这种特定网卡能够支持封装多层网络协议的数据包。

TOE技术将原来在协议栈中进行的IP分片、TCP分段、重组、checksum校验等操作,转移到网卡硬件中进行,降低系统CPU的消耗,提高服务器处理性能。- 普通网卡处理每个数据包都要触发一次中断,
TOE网卡则让每个应用程序完成一次完整的数据处理进程后才触发一次中断,显著减轻服务器对中断的响应负担。 TOE网卡在接收数据时,在网卡内进行协议处理,因此,它不必将数据复制到内核空间缓冲区,而是直接复制到用户空间的缓冲区,这种"零拷贝"方式避免了网卡和服务器间的不必要的数据
三、DMA(Direct Memory Access)
3.1 DMA技术与零拷贝
该技术用于I/O过程。众所周知,磁盘速度相对于内存以及CPU cache非常慢,因此有很多用于优化磁盘的技术比如零拷贝,异步I/O等等。DMA也是其中的技术之一。
3.2 DMA背景
DMA技术之前,I/O过程如下图:

- CPU 发出对应的指令给磁盘控制器,然后返回;
- 磁盘控制器收到指令后,于是就开始准备数据,会把数据放入到磁盘控制器的内部缓冲区中,然后产生一个中断;
- CPU 收到中断信号后,停下手头的工作,接着把磁盘控制器的缓冲区的数据一次一个字节地读进自己的寄存器,然后再把寄存器里的数据写入到内存,而在数据传输的期间 CPU 是无法执行其他任务的。
这种策略需要CPU亲自参与数据的搬运,在传输大量数据的时候用CPU搬运显然是开销过大的;因此开发了新设备DMA控制器负责数据的搬运。具体过程如下图:

- 用户进程调用 read 方法,向操作系统发出 I/O 请求,请求读取数据到自己的内存缓冲区中,进程进入阻塞状态;
- 操作系统收到请求后,进一步将 I/O 请求发送 DMA,然后让 CPU 执行其他任务;
- DMA 进一步将 I/O 请求发送给磁盘;
- 磁盘收到 DMA 的 I/O 请求,把数据从磁盘读取到磁盘控制器的缓冲区中,当磁盘控制器的缓冲区被读满后,向 DMA 发起中断信号,告知自己缓冲区已满;
- DMA 收到磁盘的信号,将磁盘控制器缓冲区中的数据拷贝到内核缓冲区中,此时不占用 CPU,CPU 可以执行其他任务;
- 当 DMA 读取了足够多的数据,就会发送中断信号给 CPU;
- CPU 收到 DMA 的信号,知道数据已经准备好,于是将数据从内核拷贝到用户空间,系统调用返回;
可以看出在数据传输的过程中,CPU不再参与数据搬运的工作,这部分由DMA做完了,CPU要做的是进行DMA的调用。DMA一开始是存在于主板,现在一般处于各个I/O设备中。
3.3 文件传输过程
如果服务端要提供文件传输的功能,我们能想到的最简单的方式是:将磁盘上的文件读取出来,然后通过网络协议发送给客户端。传统的方法需要两个系统调用:
read(file, tmp_buf, len);
write(socket, tmp_buf, len);

如上图发生了 4 次用户态与内核态的上下文切换,因为发生了两次系统调用,每一次调用都意味着:用户态保留现场,转换到内核态处理,转换到用户态这样的过程,每一次切换都需要耗时几十纳秒到几微秒,高并发情况下会影响系统性能。
另外发生了四次拷贝:
- 磁盘通过DMA拷贝到内核缓冲区
- 内核缓冲区数据拷贝到用户缓冲区
- 用户缓冲区拷贝到内核缓冲区
- 内核缓冲区通过DMA拷贝到网卡缓冲区
数据拷贝也会消耗CPU资源降低系统性能。
总共发生了四次上下文切换和四次数据拷贝。
3.4 零拷贝
为了优化文件传输的性能,主要从两个方面下手:减少用户态和内核态上下文的切换(也就是减少系统调用),减少数据拷贝的次数;
为了实现零拷贝,目前主要由两种方式:
- mmap+write
- sendfile
3.4.1 mmap+write
使用read()会把内核缓冲区的数据拷贝到用户缓冲区中,使用mmap()会直接把内核缓冲区映射到用户空间,减少了内核和用户空间之间的数据拷贝:
buf = mmap(file, len);
write(sockfd, buf, len);

如上图:调用了mmap()之后,DMA就会把磁盘的数据拷贝到内核的缓冲区里。接着,应用进程跟操作系统内核「共享」这个缓冲区;然后使用write()会让系统直接把内核缓冲区的数据拷贝到socket缓冲区中,最后DMA把socket缓冲区的数据拷贝到网卡缓冲区中。
因此mmap()代替read()可以减少一次数据拷贝。最后需要四次上下文切换,三次数据拷贝。
3.4.2 sendfile
在linux 2.1内核中出现了sendfile()系统调用:
#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
前两个参数分别是目的端和源端的文件描述符,后面两个参数是源端的偏移量和复制数据的长度,返回值是实际复制数据的长度。
它可以代替read(),write()系统调用,减少了一次系统调用(相当于减少了两次上下文切换);另外它可以直接把内核缓冲区的数据拷贝到socket缓冲区中不需要拷贝到用户态,最后只有两次上下文切换和三次数据拷贝。

如果网卡支持SG-DMA(The Scatter-Gather Direct Memory Access),那么可以进一步减少通过CPU把内核缓冲区拷贝到socket缓冲区的过程;
linux系统可以通过如下命令查看是否支持:
$ ethtool -k eth0 | grep scatter-gather
scatter-gather: on
如果支持该功能,并且linux内核>=2.4,那么sendfile()的过程会发生如下变化:
- 第一步,通过 DMA 将磁盘上的数据拷贝到内核缓冲区里;
- 第二步,缓冲区描述符和数据长度传到 socket 缓冲区,这样网卡的 SG-DMA 控制器就可以直接将内核缓存中的数据拷贝到网卡的缓冲区里,此过程不需要将数据从操作系统内核缓冲区拷贝到 socket 缓冲区中,这样就减少了一次数据拷贝;

这就是零拷贝,因为没有通过CPU来进行数据搬运的过程;
最后总共使用了两次上下文切换和两次数据拷贝,并且都不需要走CPU,都是DMA进行的拷贝;可以把传输文件的性能提高至少一倍以上
四、RDMA(Remote Direct Memory Access)
为了消除传统网络通信带给计算任务的瓶颈,我们希望更快和更轻量级的网络通信,由此提出了RDMA技术。RDMA利用Kernel Bypass和 Zero Copy技术提供了低延迟的特性,同时减少了CPU占用,减少了内存带宽瓶颈,提供了很高的带宽利用率。RDMA提供了给基于 IO 的通道,这种通道允许一个应用程序通过RDMA设备对远程的虚拟内存进行直接的读写。
RDMA 技术有以下几个特点:
- CPU Offload :无需
CPU干预,应用程序可以访问远程主机内存而不消耗远程主机中的任何CPU。远程主机内存能够被读取而不需要远程主机上的进程(或CPU)参与。远程主机的CPU的缓存(cache)不会被访问的内存内容所填充 - Kernel Bypass :
RDMA提供一个专有的 Verbs interface而不是传统的TCP/IP Socket interface。应用程序可以直接在用户态执行数据传输,不需要在内核态与用户态之间做上下文切换 - Zero Copy:每个应用程序都能直接访问集群中的设备的虚拟内存,这意味着应用程序能够直接执行数据传输,在不涉及到网络软件栈的情况下,数据能够被直接发送到缓冲区或者能够直接从缓冲区里接收,而不需要被复制到网络层。
下面是 RDMA 整体框架架构图,从图中可以看出,RDMA在应用程序用户空间,提供了一系列 Verbs 接口操作RDMA硬件。RDMA绕过内核直接从用户空间访问RDMA 网卡。RNIC网卡中包括 Cached Page Table Entry,用来将虚拟页面映射到相应的物理页面。

目前RDMA有三种不同的硬件架构实现,它们都可以使用同一套API来使用,但它们有着不同的物理层和链路层:
- Infiniband: 基于 InfiniBand 架构的
RDMA技术,由IBTA(InfiniBand Trade Association)提出。搭建基于IB技术的RDMA网络需要专用的IB网卡和IB交换机。从性能上,很明显Infiniband网络最好,但网卡和交换机是价格也很高,然而RoCEv2和iWARP仅需使用特殊的网卡就可以了,价格也相对便宜很多。 - iWARP:
Internet Wide Area RDMA Protocal,基于TCP/IP协议的RDMA技术,由IETF标 准定义。iWARP支持在标准以太网基础设施上使用RDMA技术,而不需要交换机支持无损以太网传输,但服务器需要使用支持iWARP的网卡。与此同时,受TCP影响,性能稍差。 - RoCE: 基于以太网的
RDMA技术,也是由IBTA提出。RoCE支持在标准以太网基础设施上使用RDMA技术,但是需要交换机支持无损以太网传输,需要服务器使用RoCE网卡,性能与IB相当。

4.1 架构组成
InfiniBand 架构为系统通信定义了多种设备:channel adapter、switch、router、subnet manager,它提供了一种基于通道的点对点消息队列转发模型,每个应用都可通过创建的虚拟通道直接获取本应用的数据消息,无需其他操作系统及协议栈的介入。

在一个子网中,必须有至少每个节点有一个channel adapter,并且有一个subnet manager 来管理Link。

4.1.1 Channel Adapters
可安装在主机或者其他任何系统(如存储设备)上的网络适配器 ,这种组件为数据包的始发地或者目的地,支持Infiniband 定义的所有软件 Verbs
Host Channel Adapter:HCATarget Channel Adapter:TCA
4.1.2 Switch
Switch 包含多个 InfiniBand 端口,它根据每个数据包 LRH 里面的 LID,负责将一个端口上收到的数据包发送到另一个端口。除了 Management Packets,Switch 不产生或者消费任何 Packets。它包含有 Subnet Manager 配置的转发表,能够响应 Subnet Manager 的 Management Packets。

4.1.3 Router
Router 根据 L3 中的 GRH,负责将 Packet 从一个子网转发到另一个子网,当被转到到另一子网时,Router 会重建数据包中的 LID。
4.1.4 Subnet Manager
Subnet Manager 负责配置本地子网,使其保持工作:
- 发现子网的物理拓扑
- 给子网中的每个端口分配
LIC和其他属性(如活动MTU、活动速度) - 给子网交换机配置转发表
- 检测拓扑变化(如子网中节点的增删)
- 处理子网中的各种错误
4.2 分层设计
InfiniBand 有着自己的协议栈,从上到下依次包括传输层、网络层、数据链路层和物理层:

对应着不同的层,数据包的封装如下,下面将对每一层的封装详细介绍:

4.2.1 Physical Layer
物理层定义了 InfiniBand 具有的电气和机械特性,InfiniBand 支持光纤和铜作为传输介质。在物理层支持不同的 Link 速度,每个 Link 由四根线组成(每个方向两条),Link 可以聚合以提高速率,目前绝大多数的系统采用 4 Link。

以QDR 为例,线上的 Signalling Rate 为 10 Gb/s,由于采用 8b/10b 编码,实际有效带宽单 Link 为 10 Gb/s * 8/10 = 8 Gb/s,如果是 4 Link,则带宽可以达到 32 Gb/s。因为是双向的,所以 4 Link 全双工的速率可以达到 64 Gb/s。

4.2.2 Link Layer
链路层是架构的核心,包含了如下部分:
-
Packets:链路层由两种类型的Packets,Data Packet 和 Management Packet,数据包最大可以为 4KB,数据包传输的类型包括两种类型
- Memory:RDMA read/write,atomic operation(其实就是内存操作)
- Channel:send/receive,multicast transmission
-
Switching:在子网中,Packet 的转发和交换是在链路层完成的
- 一个子网内的每个设备有一个由 subnet manager分配的 16 bit Local ID (LID)
- 每个 Packet 中有一个 Local Route Header (LRH) 指定了要发送的目标 LID
- 在一个子网中通过 LID 来负责寻址
-
Qos:链路层提供了 QoS 保证,不需要数据缓冲
- Virtual Lanes:一种在一条物理链路上创建多条虚拟链路的机制。虚拟通道表示端口的一组用于收发数据包的缓冲区。支持的 VL 数是端口的一个属性。
- 每个 Link 支持 15 个标准的 VL 和一个用于 Management 的 VL15,VL15 具有最高等级,VL0 具有最低等级
- Service Level:InfiniBand 支持多达 16 个服务等级,但是并没有指定每个等级的策略。InfiniBand 通过将 SL 和 VL 映射支持 QoS
-
Credit Based Flow Control:发送数据包之前,发送方和接收方需要协商数据量credit,接收方保证有足够的Buffer,之后传输才能进行。
-
Data Integrity:链路层通过 Packet 中的 CRC 字段来进行数据完整性校验,其组成包括 ICRC 和 VCRC。

4.2.3 Network Layer
网络层负责将 Packet 从一个子网路由到另一个子网:
在子网之间发送的数据包包含全局路由标头 (GRH)。GRH 包含数据包源和目标的 128 位 IPv6 地址。数据包通过基于每个设备的 64 位全局唯一 ID (GUID) 的路由器在子网之间转发。路由器在每个子网中使用正确的本地地址修改 LRH。因此,路径中的最后一个路由器将 LRH 中的 LID 替换为目标端口的 LID。
下面是 GRH 报头的格式,长40字节,可选,用于组播数据包以及需要穿越多个子网的数据包。它使用 GID 描述了源端口和目标端口,其格式与 IPv6 报头相同。

4.2.4 Transport Layer
传输层负责 Packet 的按序传输、根据 MTU 分段和很多传输层的服务(reliable connection, reliable datagram, unreliable connection, unreliable datagram, raw datagram)。InfiniBand 的传输层提供了一个巨大的提升,因为所有的函数都是在硬件中实现的。

按照连接和可靠两个标准,可以划分出下图四种不同的传输模式:

- 可靠连接(
RC)一个QP只和另一个QP相连,消息通过一个QP的发送队列可靠地传输到另一个QP的接收队列。数据包按序交付 ,RC连接很类似于TCP连接。 - 不可靠连接(
UC)一个QP只和另一个QP相连,连接是不可靠的,所以数据包可能有丢失。传输层出错的消息不会进行重传,错误处理必须由高层的协议来进行。 - 不可靠数据报(
UD)一个QP可以和其它任意的UD QP进行数据传输和单包数据的接收。不保证按序性和交付性。交付的数据包可能被接收端丢弃。支持多播消息(一对多),UD连接很类似于UDP连接。
每种模式中可用的操作如下表所示,目前的 RDMA 硬件提供一种数据报传输:不可靠的数据报(UD),并且不支持 memory verbs。

下面是传输层的 Base Transport Header 的结构,长度为 12 字节,指定了源 QP 和 目标 QP、操作、数据包序列号和分区。

Partition Key:InfiniBand中每个端口Device都有一个由SM配置P_Key表,每个QP都与这个表中的一个P_Key索引相关联。只有当两个QP相关联的P_Key键值相同时,它们才能互相收发数据包。Destination QP:24 bit的目标QP ID。
根据传输层的服务类别和操作,有不定长度的扩展传输报头(Extended Transport Header,ETH),比如下面是进行时候的 ETH:
下面是 RDMA ETH,面向于 RDMA 操作:

下面是 Datagram ETH,面向与 UD 和 RD 类型的服务:

Queue Key:仅当两个不可靠QP的Q_Key相同时,它们才能接受对方的单播或组播消息,用于授权访问目标QP的Queue。Source QP:24 bit的source QP ID,用于回复数据包的Destination QP
下面是 Reliable Datagram ETH,面向于 RC 类型的服务,其中有 End2End Context 字段:
