将晦涩难懂的技术讲的通俗易懂
分类: LINUX
2020-11-01 17:07:18
“RDMA for VPC”方案之MasQ分析
RDMA技术使得应用程序可以bypass kernel直接将数据拷贝到远端主机内存,所以可以提供较高的网络性能(低延时,高带宽),当前已经广泛应用于HPC等业务。而RDMA技术也从最开始的InfiniBand发展出了RoCE/RoCEv2,iWARP等基于不同底层网络的技术。
由于目前公有云网络大多提供的是VPC(Virtual Private Cloud)网络,而VPC网络大多是需要vswitch提供网络服务的,但是这对于RDMA却成了困难,因为RNIC(RDMA network interface controller)是直接将RDMA网络协议卸载到网卡硬件上,从而bypass了vswitch。所以如何在VPC网络中提供类似RDMA的低延时网络是HPC等业务上云的一个关键需求。各大云厂商近些年也在纷纷探索类似方案,AWS推出了基于其Nitro网卡的EFA方案,但从延时性能上看和传统物理(underlay)网络有一定差距。华为在2020年的SIGCOMM会议上发表了一篇名为《MasQ: RDMA for Virtual Private Cloud》的论文,也提供了一种VPC RDMA的方案,这里对这个论文中描述的方案做一下大致介绍。
Remote Direct Memory Access(RDMA)是一种远程内存管理能力,允许不同服务器上应用的内存之间直接移动数据,不需要CPU的干预。RoCE(RDMA over Converged Ethernet)是一种机制,它提供了在无损以太网络上极低延迟的高效数据传输。
RoCE将IB传输的流量封装到下面两种以太网帧中:
l RoCE v1 - RoCE v1协议被定义为带有以太网头部的IB帧。 它使用以太网类型0x8915并且可以选择启用或者关闭VLAN标签。常规的以太网MTU也适用于RoCE帧。
l RoCE v2 - RoCE协议的直接扩展使得流量能在3层IP环境下运行。将RoCE中的GRH头部替换成IP头部,使用UDP类型和专用的目的UDP端口(4791)。UDP源端口域用来携带一个模糊的流标识符,使得网络设备能够实现包的转发优化(例如ECMP),同时对于协议头部的格式保持无关性。
一般解决VPC RDMA问题有两类方案:软件方案和硬件方案。但是软件方案通常无法达到较高性能,而硬件方案通常又缺少灵活性。虽然当前一些SmartNiC方案将一些网络虚拟化能力卸载到了硬件上,如vxlan的封装/解封装,但是这类方案依然缺少可扩展性,例如作为RNIC必须要存放虚拟交换机下发的配置(如流表信息),而硬件资源通常是十分有限的。
为此华为提出了一种名为MasQ(queue masquerade)的方案,MasQ提供了一种VPC RoCE能力, MasQ的核心想法是每个RDMA通信都应该关联一个QP(queue pair),并且 QP context (QPC)维护了所有必要的发送或接收信息,如果QPC能够正确的被“虚拟化”,那么就相应的虚拟化了RDMA网络。
Related Work部分提到了目前一些VPC RDMA技术的实现和缺陷,倒是为我们收集相关资料提供了不错的资料。例如VMware的vRDMA方案采用的是paravirtualization方案,为了完成IO操作,前端驱动先将IO操作传递给后端,再由后端传递给物理网卡,这无疑带来了很大的IO虚拟化开销,为了解决这个问题HyV和virtio-RDMA技术利用RDMA的控制路径和数据路径分离的特点,使用paravirtualization方案实现控制路径,但是对数据路径采用 zero-copy方案。MasQ采用了类似IO虚拟化方案,但是更专注于解决在VPC网络中使用RDMA的新挑战(如网络的隔离性和安全组应用方面)。
FreeFlow提出了一种基于paravirtualization的RDMA虚拟化方案,利用FreeFlow router (FFR)操作主机内和主机间的RDMA flow,类似vRDMA方案,FreeFlow也牺牲性能换取管理的灵活性。
Azure提出的AccelNet基于FPGA的SmartNIC实现了RNIC网络虚拟化,基本可以满足我们的所有需求,但是需要专有的硬件。此外由于网络虚拟化是通过硬件实现的,由于片上资源有限也限制了其可扩展性。
近些年很多其他方面的工作也为RDMA的广泛应用铺平了道路。一方面的工作重点是利用RDMA提高关键应用程序的性能,如延时敏感型的的socket应用,基于RDMA的HDFS, FaRM,FaSST, HERD。尽管以上以上工作并非和MasQ直接相关,但是他们证明了RDMA是实现高性能应用程序的关键技术。另一方面的工作是RDMA的大规模部署相关的研究工作,这方面的工作也可以应用于MasQ中。
如下图所示,整个RDMA的通信其实可用分为三个阶段:首先是setup阶段,client和server通过调用图中红色部分的Verbs接口进行通信前的一些设置,包括创建资源,交互通信配置信息,设置QP状态。第一个阶段的特点是所有的操作对于应用程序都是一次性的,如QP一旦创建就会被用来收发数据,直到程序通信终止。第二个阶段是数据交互阶段,这个阶段的操作将会重复多次直到数据交互完成。最后是cleanup阶段,用于在数据交互完成后清理释放资源。
通过以上分析可以将RDMA的Verbs操作分为两类,一类是第一阶段和第三阶段使用的控制面Verbs,这类操作仅需要执行一次;而第二阶段中数据交互使用的称之为数据面Verbs,这种类型的Verbs会被执行多次。而MasQ正式利用这种特性,将控制面的Verbs采用IO虚拟化方式实现,而对数据面的Verbs操作采用了直通(zero-copy)方式实现。
MasQ采用了hybrid I/O虚拟化的方法,如下图所示,控制面通过virtio半虚拟化实现,而数据面通过直接内存mmap实现。因此数据面RNIC可以直接和VM中的应用相互访问。例如,数据面的两种常见资源访问:一种是VM访问HOST上的RNIC硬件寄存器(如Doorbell),通过MMIO的方式可以将寄存器映射到VM的guest物理内存中,进而可以被VM中的应用程序直接访问 ;另一种是RNIC访问VM的内存,如QPs和用户注册的 memory regions (MRs),由于这些内存在HOST的物理地址空间是可见的,所以可以将guest virtual address(GVA) 映射到host physical address (HPA),这样RNIC就可以通过DMA直接访问这些用户内存了。
由于以上方案在HyV和virtio-RDMA中都用使用,所以并不是MasQ首先提出的,MasQ只是直接使用了这个技术,所以这个不是论文的重点,论文也没有过多描述。不过我个人而言还是对这一块的细节比较感兴趣,这部分在本分析的最后进一部分展开。
将I/O虚拟化具体应用到RDMA的网络虚拟化上,其整体架构就变为如下图所示。一个RoCE RNIC设备会在虚拟机中呈现出两个interface,一个正常的虚拟以太网interface,另一个是虚拟的RDMA interface。进一步来讲MasQ重用了vhost_net来实现以太网interface,并利用了上述I/O虚拟化技术虚拟化了一个RDMA interface,以下将从四个方面介绍MasQ的实现。
租户隔离方面MasQ采用了链接粒度的虚拟化取代传统的报文粒度的虚拟化,即RConnrename(RDMA connection rename)。传统网络虚拟化方式,如vxlan,通常是对每个报文进行封装来达到租户隔离。而这里粒度的虚拟化是将租户(VM)的链接在HOST上进行映射,进而进行rename,由于不同租户的连接经过HOST重映射,所以可以在HOST上确保不同租户的连接不冲突,从而达到组合隔离的效果。可以看到MasQ的RDMA网络虚拟化并没有采用overlay方式,而是采用连接rename的方式进行隔离。
在详细阐述RConnrename实现前首先看一下MasQ设备管理面临的问题。上文提到在VM内部会有以太网interface和RDMA interface,一个VM可以像支持多网卡一样拥有多个RDMA interface,那么当应用程序使用某个RDMA interface时该使用哪个IP呢?在传统物理设备实现RoCE是不存在这个问题的,因为通常以太网interface和RDMA interface是一个PCI设备,所以应用确定RDMA interface时也就确定了以太网的interface,就可以直接使用以太网interface的IP地址。但是在MasQ方案中以太网和RDMA被虚拟化为了两个interface。为了解决这个问题MasQ引入了vBond的概念。
如上图所示,通过vbond将以太网和RDMA interface动态绑定在一起。为此vbond首先获取RDMA interface对应的以太网interface的MAC地址,通过初始化时查询后端驱动,如果以太网分配了IP地址,则vbond将立即初始化生成一个GID( global identifier),并将对应RDMA和以太网interface绑定在一起;随后vbond注册一个回调函数来监听IP地址变化事件,一旦以太网设备的IP地址发生了变化vbond就会根据IP重新生成GID,注意GID的作用是在RDMA网络中标识一个RDMA interface。总结起来就是通过vbond将以太网设备和RDMA设备进行了一对一绑定,由于RDMA设备的GID是根据以太网设备的IP生成的,所以GID也就和以太网设备对应起来了。
有了vbond的基础,实现RConnrename就相对容易了。如果没有RConnrename则VM将会使用VM的IP/MAC地址和远端进行RDMA通信,这里面有两个问题,首先VM的IP/MAC直接进入到underlay网络是没有意义的,其次由于VPC技术,不同租户的VM IP地址可能是相同的,这就会导致其对应RDMA的GID是一样的。为此MasQ Backend通过拦截前端的Verbs命令,将虚拟机的RDMA虚拟地址连接信息重新映射到了HOST的物理地址。其具体过程如下图所示:
在进行RDMA通信前client和server都要创建QP以及注册memory region,其控制路径流程如下:
(1) VM前端发起的“create_qp”命令被转发到HOST,HOST上的后端驱动将QP的GVA和HPA进行映射,然后通过调用物理设备驱动创建QPC。一旦QPC在HOST上创建成功,VM应用程序即可进行GID的查询;
(2) vbond在前端维护对应的virtual GID(vGID),此时应用进行连接信息的查询,如QP number(QPN)和vGID,可由前端驱动直接返回;
(3) 使用预先创建好的TCP连接,通过以太网设备和远端进行连接信息的交换;
在完成连接信息交换后,应用程序将要使用远端的vGID作为目的地址进行QPC的配置,但是如前所述,RConnrename需要将远端的vGID替换为远端的GID才能映射到物理网络正常通信。而通过(3)的TCP连接只能获取到远端的vGID,无法获取到远端的GID,由于vGID仅和VM的IP和MAC有关,和物理GID并没有关联关系,要想根据vGID获取到对应的GID就需要维护一个vGID到GID的映射。又因为不同租户的IP地址可能相同,所以vGID也可能相同,所以为了确定一个VM的vGID对应的GID,MasQ维护了VPC tunnelid+vGID和GID的映射表。一旦vGID创建或者更新就会同步更新这个映射表。而这个映射表通常是在一个Remote Controler中维护的,为了减低vGID到GID转换每次需要查询远端的性能开销,RConnrename引入了本地cache的思想,将一些常用映射关系缓存在本地来降低性能开销。
(4) 本地查询远端GID的命令被后端截获,进而向Remote Controler发起查询从而获取到远端的物理GID返回给应用;
至此两端的QPC都已经配置完成,这样整个链接将使用真实的GID进行通信,且使用HOST地址,从而完成了connection的重映射。
前面的RConnrename解决了租户的隔离问题,这部分要说一下安全问题的解决。在VPC RDMA网络中的安全问题包括网络安全和内存安全两方面。
网络安全
通常在VPC网络中有两类安全策略,一类是网络ACL,一类是安全组功能,前者作用在一个子网上而后者作用在某个VM或网卡上。而通常这样的安全测试是通过类似iptable的机制在vswitch中实现,但是RDMA流量由于完全bypass了vswitch,所以传统安全策略方式将不再适用。
为了解决这个问题MasQ实现了RConntrack功能,即RDMA的链接跟踪功能。首先,RDMA的链接新建是要经过MasQ后端软件处理的,这样对于新建的RDMA链接就有机会在MasQ的后端通过查询安全策略决定是否允许链接建立成功。
对于存量的链接,MasQ利用RConntrack跟踪记录RDMA的所有连接信息,从而可以结合安全策略的配置识别出需要禁止的RDMA连接。识别出要禁止的连接不是问题,而关键问题是如何禁止已经建立的连接,因为此时数据连接已经脱离的软件路径。这里MasQ利用了QP的状态机特性。如下图所示:
和TCP状态机类似,RDMA连接从建立到终止其状态是要多次变化的,例如一个QP要想发送数据那么他的状态必须要是RTS(Ready To Send)。根据上图可知,当QP的状态变为ERROR时,将立刻停止数据处理并将错误上报给应用层,另外很重要一点就是从其他任何状态都可以直接变为ERROR状态。所以就可以利用改变QP的状态为ERROR来中断已有链接。
以下图为例说明安全策略的实现流程:
两个VM分别位于两个subnet,一开始安全策略都是放行的,VM A发送一个到VM B的连接建立请求(1),MasQ后端截获后检查安全规则发现没有被禁止,就继续练创建连接过程。连接创建成功后用将连接信息记录在了RConntrack模块,随后户修改了安全策略,禁止了VM A到VM B两个网络的访问,这个时候RConntrack就找到了刚才符合禁止条件的连接,并将对应的QP的状态改为ERROR来中断连接。然后VM A又向VM B发送了一个创建连接请求(2),同样被MasQ后端截获,检查发现不满足安全策略的条件就直接终止连接过程。
内存安全
MasQ依靠RDMA原生的安全机制实现用户内存的保护。首先是对RDMA资源的保护,如QP,MR,以及通过MasQ创建的 PDs(protection domains);其次,和远端主机的连接建立需要可靠连接(即RC)或在UD模式中带有Q-key,这样就可以很好的进行识别保证连接安全;最后,为了能够正确的访问远端的内存,访问内存时需要提供memory key,此外RNIC也会对内存访问的边界做校验。综上MasQ在解决内存完全问题上没有引入额外的开销。
Qos对于VPC网络是至关重要的,为了实现Qos能力,同时尽肯能的引入性能开销,MasQ采用了基于硬件的限速。MasQ实现的限速是基于QP的,将不同的QP映射到对应的限速meter,同时也提供QP group的限速,即将一些QP加入到一个QP group使用一个meter来限速。MasQ的默认限速策略是将某个用户的QP对应到一个QP group,然后对这个QP group进行限速。
当前MasQ是通过SR-IOV的方式实现上述Qos能力的,并不是将SR-IOV透传给VM,而是在MasQ后端维护每个VF设备处理对应不同VM的请求(例如create_qp)。然后通过VF来下发限速配置。
这篇文章主要关注的是基于链接的传输,如RC。但是,众所周知,RC在面临RDMA可扩展性方面有着较大挑战,因此对基于报文(UD)传输的支持也十分重要。通过上面流程阐述可以发现将MasQ应用于UD模式也比较简单。由于报文的传递信息是放在WQE(work queue element)中的,这种情况下可以将WEQ实现为非zero-copy方式,这样MasQ就有机会介入了,利用RConnection替换虚拟网络的WEQ配置为物理网络配置,进而完成通信。
最后文章对MasQ进行了性能评估,对比物理网络(HOST)的RDMA,以及使用SR-IOV模式的RDMA,还有FreeFlow。得出了以下结论:
(1)MasQ的性能无论在benchmark场景还是应用场景都和HOST RDMA性能几乎相当。MasQ的主要性能开销在控制面建立连接的过程,但是对于多数应用建立连接不是关键路径,因此性能影响并不明显;
(2)MasQ的灵活性使其非常有效的实现所有VPC网络的需求,如支持大规格实例并提供高性能和安全性。
MasQ实现了在VPC网络中支持RoCEv2的能力,虽然RoCEv2依赖PFC功能实现lossless网络,而PFC虽然可以减少丢包但是逐级反压又容易产生PFC storm。不过目前已经有其他一些解决方案,如DCDCN,NDP,HPCC等。
此外,对于支持RDMA的VM进行热迁移是一个比较大的挑战,因为RDMA直接bypass了内核,无法记录传输过程中产生的内存脏页。如果目前有一些绕过的方案,比如在热迁移的时候先将RDMA回退到TCP/IP再进行迁移。
前文说过Masq正是利用Hybrid I/O虚拟化,实现了数据面的zero-copy,那么这里我们就详细看一下zero-copy是如何实现的。我们从两个方向的访问分别分析:VM中的应用访问RNIC和RNIC(HOST上的物理设备)访问VM内存。
在HOST场景下,应用程序访问RNIC通常是通过MMIO的方式。为了让VM中的应用也可以这样访问,则需要引入一次额外的内存映射,即将RNIC的寄存器(如Doorbell)映射到VM中应用程序的虚拟地址空间。我们以在设备初始化过程中映射一个Doorbell为例说明这个过程:
(1)前端应用程序启动调用Verbs接口”open_device”来初始化device context;
(2)这一步将上述初始化调用分为三个部分,如下图所示:
(2a) 虚拟化后端收到前端请求后在RNIC上申请出一个Doorbell,这样后端驱动就获取了Doorbell的HPA(HOST物理地址),我们将这个地址叫做doorbell_hpa;
(2b) 前端驱动申请出一块Doorbell大小的内存然后将其mmap到应用的虚拟地址空间,我们称这块内存称之为vDoorbell,它对应在QEMU中的虚拟地址空间我们称作doorbell_hva;
(2c) 后端驱动通过修改QEMU的页表创建doorbell_hva和doorbell_hpa之间的映射,这样应用程序访问vDoorBell就会被定向到RNIC上的真实Doorbell了;
至此,VM中的应用就可以通过MMIO方式直接访问RNIC上的Doorbell了。
为了实现RNIC能够通过DMA的方式访问vm的用户空间,如QPs和MRs,vm用户空间的虚拟地址和HOST的物理地址直接的映射需要被配置到RNIC的内存转换表(MTT)中。对于VM中的应用来说,可以通过调用一次内存pin和内存转换来实现。这里我们使用创建一个QP来描述这个过程。
(1) 为了创建一个QP,应用程序需要调用Verbs的”create_qp”接口;
(2) 对于的api首先为QP分配一块内存,我们将这块内存称地址称作qp_gva,然后将这个内存信息和请求传递给前端驱动;
(3) 前端驱动首先将这块内存pin住,然后通过应用程序的进程页表将qp_gva转换为qp_gpa,然后将内存信息和请求传递给后端驱动,包括qp_gva和qp_gpa内存映射信息;
(4) 后端驱动收到请求后将qp_gpa转换为qp_hva,然后再次pin住相应内存,通过QEMU的页表将qp_hva转换为qp_hpa。
这样后端驱动就有了QP的qp_gva和qp_hpa,只需要将它们写入RNIC的MTT中即可(这里qp_gva相当于IOMMU的iova)。这样就可以实现RNIC对QP的DMA访问,MRs的映射过程也类似,这里不再展开。