nVidia RDMA GPUDirect是否总是只操作物理地址(在CPU的物理地址空间中)?

8

正如我们所知道的:http://en.wikipedia.org/wiki/IOMMU#Advantages

IOMMU可以支持外围设备内存分页。使用PCI-SIG PCIe地址转换服务(ATS)页面请求接口(PRI)扩展的外围设备可以检测并发出内存管理器服务的信号。

enter image description here

但是当我们使用CUDA >= 5.0的nVidia GPU时,我们可以使用RDMA GPUDirect,并且了解到:

http://docs.nvidia.com/cuda/gpudirect-rdma/index.html#how-gpudirect-rdma-works

传统上,像BAR窗口这样的资源会使用CPU的MMU作为内存映射I/O(MMIO)地址映射到用户或内核地址空间中。然而,由于当前操作系统没有足够的机制来在驱动程序之间交换MMIO区域,因此NVIDIA内核驱动程序导出函数以执行必要的地址转换和映射。

http://docs.nvidia.com/cuda/gpudirect-rdma/index.html#supported-systems

GPUDirect的RDMA当前依赖于所有物理地址都是从PCI设备的角度相同的。这使其与IOMMU不兼容,因此必须禁用IOMMU才能使GPUDirect的RDMA工作。

如果我们将CPU-RAM分配和映射到UVA上,如下所示:

#include <iostream>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

int main() {
    // Can Host map memory
    cudaSetDeviceFlags(cudaDeviceMapHost);  

    // Allocate memory
    unsigned char *host_src_ptr = NULL;
    cudaHostAlloc(&host_src_ptr, 1024*1024, cudaHostAllocMapped);
    std::cout << "host_src_ptr = " << (size_t)host_src_ptr << std::endl;

    // Get UVA-pointer
    unsigned int *uva_src_ptr = NULL;
    cudaHostGetDevicePointer(&uva_src_ptr, host_src_ptr, 0);
    std::cout << "uva_src_ptr  = " << (size_t)uva_src_ptr << std::endl;

    int b;  std::cin >> b;
    return 0;
}

我们在Windows7x64中获得了相等的指针,这意味着cudaHostGetDevicePointer()无效:

host_src_ptr = 68719476736

uva_src_ptr = 68719476736

“充分的机制用于驱动程序之间交换MMIO区域”是什么意思?这里指的是什么机制?为什么我不能使用IOMMU通过虚拟地址访问通过PCIe到BAR的物理区域 - 另一个内存映射设备?
这是否意味着RDMA GPUDirect始终仅操作物理地址(在CPU的物理地址空间中),但我们发送给内核函数uva_src_ptr,它等于host_src_ptr - CPU的虚拟地址空间中的简单指针?
1个回答

14
IOMMU非常有用,因为它提供了一组映射寄存器。它可以安排任何物理内存出现在设备可访问的地址范围内,并且也可以使物理分散的缓冲区对设备看起来是连续的。但这对于第三方PCI/PCI-Express卡或试图访问nVidia GPU的原始物理偏移量的远程机器来说并不好,因为这可能导致未实际访问所需的内存区域,或者被IOMMU单元以每个卡的基础上抑制/限制此类访问。因此必须将其禁用。
“GPUDirect的RDMA目前依赖于所有物理地址从PCI设备的角度看都相同。” -nVidia, rDMA和GPUDirect的设计考虑因素 当驱动程序尝试利用CPU的MMU并映射内存映射I/O(MMIO)区域以在内核空间中使用时,它们通常会将从内存映射返回的地址保留给自己。由于每个驱动程序都在自己的上下文或命名空间中运行,因此在nVidia的驱动程序和其他希望支持rDMA + GPUDirect的第三方供应商的驱动程序之间交换这些映射将非常困难,并且可能会导致特定于供应商的解决方案(如果第三方产品之间的驱动程序差异很大,甚至可能是特定于产品)。此外,今天的操作系统目前没有任何好的解决方案来在驱动程序之间交换MMIO映射,因此nVidia导出了几个函数,允许第三方驱动程序轻松地从内核空间本身访问此信息。
nVidia强制使用“物理寻址”通过rDMA访问每个卡,以进行GPUDirect。这极大地简化了将数据从一台计算机移动到远程系统的PCI-Express总线的过程,通过使用该机器的物理寻址方案而不必担心与虚拟寻址相关的问题(例如将虚拟地址解析为物理地址)。每张卡都有一个物理地址,它驻留在此偏移处可以被访问;只需添加一小部分逻辑即可尝试执行rDMA操作的第三方驱动程序。此外,这些32位或64位基地址寄存器是标准PCI配置空间的一部分,因此可以通过简单地从其BAR中读取而不必获取nVidia驱动程序在连接到卡时获得的映射地址来轻松获得卡的物理地址。 nVidia的通用虚拟寻址(UVA)处理上述物理地址映射到似乎连续的内存区域中的用户空间应用程序。

CUDA Virtual Address Space

这些内存区域进一步分为三种类型:CPU、GPU和FREE,所有这些都在此处有记录。
回到您的用例:由于您位于用户空间,因此无法直接访问系统的物理地址空间,并且您使用的地址可能是nVidia的UVA提供给您的虚拟地址。假设没有进行先前的分配,则您的内存分配应位于偏移量+0x00000000处,这将导致您看到与GPU本身相同的偏移量。如果您要分配第二个缓冲区,我想您会在第一个缓冲区结束后立即看到该缓冲区开始(在您的情况下,基于1 MB分配,它将从GPU的基本虚拟地址偏移+0x00100000开始)。

如果您处于内核空间,并正在为公司的卡编写驱动程序以利用rDMA进行GPUDirect,那么您将使用系统的BIOS和/或OS分配给GPU的32位或64位物理地址,直接将数据rDMA到GPU本身。

此外,值得注意的是,并非所有DMA引擎都支持虚拟地址进行传输--事实上,大多数需要物理地址,因为从DMA引擎处理虚拟寻址可能变得复杂(第7页),因此许多DMA引擎缺乏对此的支持。

然而,回答您帖子标题中的问题:nVidia目前仅支持在内核空间中使用物理寻址进行rDMA + GPUDirect。对于用户空间应用程序,您将始终使用nVidia的UVA提供给您的GPU的虚拟地址,该地址位于CPU的虚拟地址空间中。


关于您的应用程序,以下是您可以执行rDMA操作的简化过程:

1. 您的用户空间应用程序创建缓冲区,这些缓冲区在 nVidia 提供的统一虚拟寻址空间(虚拟地址)范围内。 2. 调用 cuPointerGetAttribute(...) 获取 P2P 令牌;这些令牌与 CUDA 上下文内存相关。 3. 将所有这些信息以某种方式发送到内核空间(例如 IOCTL、对驱动程序的读/写等)。至少,您需要将以下三个内容传递给内核空间驱动程序: - cuPointerGetAttribute(...) 返回的 P2P 令牌 - 缓冲区的 UVA 虚拟地址 - 缓冲区的大小 4. 现在通过调用 nVidia 的内核空间函数将这些虚拟地址转换为相应的物理地址,因为这些地址保存在 nVidia 的页表中,并且可以使用 nVidia 导出的函数进行访问,例如:nvidia_p2p_get_pages(...)、nvidia_p2p_put_pages(...) 和 nvidia_p2p_free_page_table(...)。 5. 使用在上一步骤中获取的这些物理地址初始化 DMA 引擎,该引擎将操作这些缓冲区。

这个过程的更详细解释可以在这里找到。


1
非常感谢!1. 即在内核空间中,我们必须始终使用物理寻址并禁用IOMMU,在用户空间中,我们必须始终使用启用了IOMMU的虚拟寻址,但是我们在引导时禁用了IOMMU,那么虚拟寻址(UVA)如何工作? 2. “因为每个驱动程序都在其自己的上下文或命名空间中运行”-但据我所知,所有内核空间的驱动程序都在单个地址空间(上下文)中运行,那么你所说的“自己的上下文”是什么意思? - Alex
1
“这对第三方PCI/PCI-Express卡或尝试访问nVidia GPU原始物理偏移的远程机器来说并不好” - 您的意思是第三方卡可能不使用IOMMU,但第三方卡在物理地址内操作,如果GPU使用IOMMU或不使用IOMMU会有什么问题,如果GPU在任何情况下都使用虚拟地址,那么会有什么问题?相比之下:当CPU使用虚拟地址时,我们不会看到使用传统MMU的任何障碍,即使我们使用RDMA(CPU-CPU)。 - Alex
1
1:在许多系统(如x86)上,实际上并没有为所有设备使用的IOMMU。大多数情况下,设备本身将具有功能块,用于进行页表查找并基本上作为IOMMU的功能。此外,您可以在内核空间中使用虚拟和物理寻址。这与问题2有关,因为现在每个驱动程序都负责在该设备的IOMMU中设置这些映射 - 第三方驱动程序无法访问此内容。而“上下文”仅指功能驱动程序本身引用的私有资源。 - datboi
1
通过拥有一个通用的IOMMU,这将打破所有物理地址从PCI设备角度看都是相同的假设;因此需要禁用它。我相信如果存在IOMMU并且仅被禁用或者根本不解析地址,那么这种情况将会起作用,但这通常不是情况。:( - datboi
1
非常感谢!即针对RDMA(通过Infiniband),我必须禁用CPU-IOMMU,并在Infiniband和GPU之间使用物理寻址,因为Infiniband-IOMMU和GPU-IOMMU使用不同的虚拟地址?我在GPU-RAM上创建了固定内存区域,并使用内核空间中的nvidia_p2p_get_pages()获取page_table/SGL(scatter-gather-list)/SGE(scatter/gather entries),然后将它们提供给Infiniband ibv_post_send(),并将数据发送到远程CPU-RAM? - Alex
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接