将晦涩难懂的技术讲的通俗易懂
分类: LINUX
2024-06-09 20:48:05
在AI和HPC场景,GPU间需要大量的交换数据,GPU通信性能成为了非常重要的指标。NVIDIA推出的GPUDirect就是一组提升GPU通信性能的技术。
传统上,当数据需要在 GPU 和另一个设备之间传输时,数据必须通过 CPU,从而导致潜在的瓶颈并增加延迟。使用 GPUDirect,网络适配器和存储驱动器可以直接读写 GPU 内存,减少不必要的内存消耗,减少 CPU 开销并降低延迟,从而显著提高性能。GPU Direct 技术包括 GPUDirect Storage、GPUDirect RDMA、GPUDirect P2P 和 GPUDirect 视频,这里我们重点说一下GPUDirect P2P特性。
某些工作负载需要位于同一服务器中的两个或多个 GPU 之间进行数据交换,在没有 GPUDirect P2P 技术的情况下,来自 GPU 的数据将首先通过 CPU 和 PCIe 总线复制到主机固定的共享内存。然后,数据将通过 CPU 和 PCIe 总线从主机固定的共享内存复制到目标 GPU,数据在到达目的地之前需要被复制两次。
2011年,GPUDirect增加了相同PCI Express root complex 下的GPU之间的Peer to Peer(P2P) Direct Access和Direct Transers的支持。有了 GPUDirect P2P 通信技术后,将数据从源 GPU 复制到同一节点中的另一个 GPU 不再需要将数据临时暂存到主机内存中。如果两个 GPU 连接到同一 PCIe 总线,GPUDirect P2P 允许访问其相应的内存,而无需 CPU 参与。前者将执行相同任务所需的复制操作数量减半。
目前云厂商大多是通过虚拟机(或安全容器)提供主机服务的,这就涉及到P2P技术在虚拟化场景的应用。但在虚拟化场景下使用基于PCIe的GPUDirect P2P却存在一些限制。
首先就是不在同一个Intel IOH(或PCH)芯片组(相当于RC)下面PCI-e P2P通信是不支持的,因为Intel CPU之间是QPI协议通信,PCIe P2P通信是无法跨QPI协议的。所以GPU driver必须要知道GPU的PCI拓信息,同一个IOH芯片组下面的GPU才能使能GPUDiret P2P。
其次在bare-metal中,NVIDIA 驱动程序能够检测此类拓扑,并将 GPU 组织成互斥的“cliques”即能够进行点对点通信的 GPU 组。 然而在虚拟机中,PCIe拓扑由hypervisor模拟,QEMU会将拓扑flattened,呈现给虚拟机内的拓扑结构不再是实际物理拓扑。但是NVIDIA 会限定特定芯片组和 PCI Express switches,并通过 PCI Express 拓扑来确定硬件是否能够支持P2P,这就导致GPU P2P能力在虚拟机中是默认关闭的。
为了让GPU driver获取到真实的GPU拓扑结构,hypervisor需要为Guest中的GPU 驱动提供额外的信息,让驱动感知GPU的Clique。NV为此提供了一个解决方案,即在hypervisor层模拟一个虚拟的capability用于向GPU驱动提供peer-to-peer信息,NV driver在初始化时会读取该值。同时NV对这个capability的格式和编码进行了规定,并且要求在虚拟机中当GPU设备被NV驱动接管时该capability需要保持静态不变。
如下图所示,我们通过QEMU向虚拟机中GPU capability 链表插入P2P Approval Capability。之后NV驱动通过读取Peer Clique ID的值来更新peer-to-peer 拓扑,允许具有相同Clique ID的GPU建立P2P映射关系。
此外PCIe的P2P和IOMMU配合存在一些问题,我们知道IOMMU是为了支持设备硬件辅助虚拟化而引入的技术,intel称为VT-d,AMD称为AMD-Vi,ARM称为SMMU,它们的实现细节不同,但原理相同。IOMMU是集成在RC中的一个部件,当设备访问主机内存时,它将设备访问时使用的地址转换为主机内存的地址,在虚拟化场景下可以将设备访问的GPA转换为HPA。而PCIe P2P是两个设备的直接通信,自然不在经过RC,也就无法经过IOMMU的地址翻译。那会有什么问题呢?考虑如下图场景,假设网卡1 + GPU1被直通给VM1,网卡2 + GPU2被直通给VM2,虚拟机的一个原则就是不感知自己是虚拟机,所以VM1和VM2独立配置GPU内存的PCIe域的地址范围,如果这两个值是相交(甚至是相同)的,那么网卡1对GPU1内存做读写请求时,当TLP到达PCIe switch 2时,PCIe switch 2根据地址查表,它无法判断出应该转发到port3还是port4。这就出现了设备的隔离安全问题,肯定是不行的。
那是不是PCIe P2P就不能在虚拟化场景使用了呢?当然不是,这里就要引入一个新的特性,ACS(Access Control Services)。ACS是指PCIe(Peripheral Component Interconnect Express)规范中引入的一项技术特性——Access Control Services。在PCIe架构中,ACS是为了增强I/O虚拟化安全性而引入的。在虚拟化环境中,多个虚拟机(VM)可能共享同一物理主机上的PCIe设备。如果没有适当的隔离措施,不同VM之间的直接内存访问(DMA)可能会导致数据泄露或安全漏洞。ACS机制通过提供一种方法来限制和控制PCIe设备间的直接通信(Peer-to-Peer,P2P)以及设备对系统内存的访问,从而增强了虚拟化环境下的I/O设备隔离能力。如果没有ACS,那么你的P2P程序将在虚拟化下无法运行。
ACS是怎么解决设备隔离的问题呢?答案是ACS的开启会影响到PCIe的TLP的路由。如果设备开启了ACS,那么这个设备的PCIe数据包都会强制走其upstream port,{BANNED}最佳佳终走到CPU的RC。而我们知道RC的一个功能就是带IOMMU的地址翻译。VM内部的数据都是GPA(guest physical address),VM内驱动送给设备运行的数据都必须进过一次IOMMU的地址翻译才能{BANNED}最佳佳终访问到正真的物理内存。ACS正式通过强制将P2P的PCIe TLP路由到RC IOMMU才保证了设备的安全隔离。所以开启了ACS control以后,在物理层面,其实就是禁止了任何PCIe endpoint之间的物理P2P连接,因为所有的数据都会强制到RC中转。
但也正是由于开启ACS PCIe P2P会强制绕行到RC IOMMU,因此在这种情况下GPUDirect的性能相对正常的P2P也会有较大下降。
这里再补充一下ACS和IOMMU的关系。ACS(Access Control Services)和IOMMU(Input/Output Memory Management Unit)在PCIe系统共同协作,以增强对输入输出设备访问系统内存的控制和管理,但它们扮演着不同的角色。
IOMMU的作用
● 地址转换:IOMMU负责将设备发起的DMA(Direct Memory Access)请求中的总线物理地址转换为主机物理地址或虚拟机内存地址,这在虚拟化环境中尤为关键,因为它允许直接内存访问的同时保持虚拟机内存的隔离。
● 访问权限控制:确保I/O设备只能访问被授权的内存区域,通过维护一个I/O页表来定义设备可访问的地址范围和权限,从而增加了系统的安全性。
● 保护主机内存:防止恶意或错误的I/O操作破坏系统内存或访问敏感数据。
ACS的作用:
● 设备间隔离:在PCIe架构内部,ACS主要关注的是不同PCIe设备间的直接通信和资源访问控制,确保一个设备不能未经许可就访问另一个设备的配置空间或其直接连接的资源。
● 配置灵活性:允许系统软件配置PCIe拓扑中的访问控制规则,以适应不同的系统配置和安全需求。
● 辅助虚拟化:配合IOMMU,为虚拟化环境提供更细粒度的设备隔离和资源分配,确保虚拟机只能看到并使用分配给它们的PCIe设备。
两者的关系:
IOMMU和ACS虽然功能上有重叠,但侧重点不同。IOMMU主要关注设备对系统内存的访问控制,而ACS则聚焦于PCIe设备间的交互和资源隔离。
IOMMU group是一个{BANNED}最佳佳小可以assign同一个VM的单元。比如有两个GPU设备在同一个PCIe Switch下面,如果Switch与这两个GPU设备都不支持ACS control的话(没有这个control bit)那么这两个GPU必须同时assign给同一个VM,或者其中一个assign给VM,另外一个必须在host端禁用。
为什么这两个GPU必须同时assign给同一个VM呢? 因为从VM的角度来看,如果这两个设备分别属于不同VM的话,一旦这两个GPU之间发生P2P数据交换的话,就相当于其中一个VM可以访问另外一个VM的数据。安全隔离性就无法满足。
那么为什么开启了ACS以后,两个GPU就可以分别assign给不同的VM了呢(开启了ACS这两个设备就属于不同的IOMMU group了)? 因为ACS的作用就是强制数据流走RC的IOMMU翻译,这就导致了两个VM的数据会在RC一层互相访问,而无法实现硬件层面的真正的P2P访问。
因此两个GPU如果自带ACS,那么即使他们在同一个PCIe Switch下面,他们也分属与不同的IOMMU group,如果他们不带ACS,那么他们则属于同一个IOMMU group,必须同进同出。
而目前几乎所有的PCIe 设备都带ACS control,所以它们可以单独assign给不同的VM(无论是否在一个PCIe switch下),但是随之而来的就是即使两个GPU同时assign给同一个VM,由于ACS的存在,他们之间也不会有真正的物理层面的P2P访问(必须绕行RC)。
有没有虚拟机下高性能的GPUDierct性能方案呢?当然一种hack的方案是通过虚拟化的修改让虚拟机的地址和物理相等,即GPA=HPA,设置iommu passthough,但这种方式不够灵活,也只能用于单个虚拟机(或裸金属)。另一个方式就是不用PCIe,而使用NVLink。
NVLink技术是在2014年3月的NVIDIA GTC 2014上发布的。NVLink是一种解决服务器内GPU之间通信限制的协议。与传统的PCIe交换机不同,NVLink带宽有限,可以在服务器内的GPU之间实现高速直接互连。第四代NVLink提供更高的带宽,每条通道达到112Gbps,比PCIe Gen5通道速率快三倍。
在NVLink之前,GPU和GPU通信,或者GPU和CPU通信只能通过PCIe,而以PCIe Gen3为例,每个通道(每个Lane)的双向带宽是2B/s,GPU一般是16个Lane的PCIe连接,所以PCIe连接的GPU通信双向带宽可以达到32GB/s,要知道PCIe总线堪称PC系统中第二快的设备间总线(中国{BANNED}中国第一的是内存总线)。但是在NVLink 300GB/s的带宽面前,还是差距太大。
NVLink不仅增大了通信带宽,同时也解决了虚拟化场景下开启ACS,导致GPUDirect P2P绕行RC的问题。因为ACS是主要是针对PCI Express (PCIe) 总线架构设计的安全与访问控制机制,而NVIDIA的NVLink是一种高速互连技术,主要用于GPU与GPU之间或者GPU与CPU之间的高速数据传输,它并不属于PCIe总线架构, 因此不受ACS控制,自然也就不会绕行到RC了。但是还有一个问题,GPU直通给VM后,VM的驱动设置给GPU的地址是GPA,如果不经过RC,GPUDirect产生的GPA将怎么被正确转发给对端呢? 这就要从NVlink的使用方式说起了,如果使用NVlink创建虚拟机的时候就需要先把NVlink通道建立起来,使用的是GPU内部的显存地址,这样通道建立之后就不需要地址转换了。
所谓GPUDirect RDMA,就是计算机1的GPU可以直接访问计算机2的GPU内存。而在没有这项技术之前,GPU需要先将数据从GPU内存搬移到系统内存,然后再利用RDMA传输到计算机2,计算机2的GPU还要做一次数据从系统内存到GPU内存的搬移动作。GPUDirect RDMA技术使得进一步减少了GPU通信的数据复制次数,通信延迟进一步降低。
需要注意的是,要想使用GPUDirect RDMA,需要保证GPU卡和RDMA网卡在同一个ROOT COMPLEX下,如下图所示:
网卡和GPU之间一般是无法走NVlink的,在虚拟化场景下网卡使用的也是GPA地址,怎么进行转换呢?当然开启网卡ACS也可以让网卡PCIe通信绕行RC IOMMU。但如前文所说这会影响性能。另一个方案就是开启网卡的ATS特性。ATS说白了就是PCIe设备自己在本地上缓存的IOMMU cache。ATS 是Address Translation Service 的缩写,它的提出主要是为了缓解iommu硬件iova转换的压力。尤其是当设备上有大量的DMA working sets时,ATS能够有效减少因为PCIe链路压力过大导致的设备性能抖动。ATS由位于PCIe设备上的ATC(Address Translation Cache) 和 Translaion Agent(TA,通常也是位于iommu硬件上)组成。ATC的作用可以跟cpu端的TLB来做类比,因此它也经常被称为Device TLB。ATC里面存储的主要是iova到hpa的映射关系,当ATC发生miss的时候需要跟TA之间进行一些交互。目前大多数高级网卡/DPU都支持ATS功能。