nVidia RDMA GPUDirect是否始终仅运行物理地址(在CPU的物理地址空间中)?

时间:2013-11-07 16:50:40

标签: cuda gpgpu pci-e memory-mapping gpudirect

我们知道: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不兼容,因此必须为RDMA禁用它们   GPUDirect工作。

如果我们将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;
}

我们在Windwos7x64中得到相同的指针,这意味着cudaHostGetDevicePointer()什么都不做:

  

host_src_ptr = 68719476736

     

uva_src_ptr = 68719476736

这意味着“在驱动程序之间交换MMIO区域的充分机制”是什么意思,这意味着什么机制,以及为什么我不能通过使用虚拟地址通过PCIe访问BAR的物理区域来使用IOMMU - 另一个内存映射设备通过PCIe?

这是否意味着RDMA GPUDirect始终只运行物理地址(在CPU的物理地址空间中),但是为什么我们发送到等于uva_src_ptr的内核函数host_src_ptr - CPU的虚拟地址空间中的简单指针?

1 个答案:

答案 0 :(得分:10)

IOMMU非常有用,因为它提供了一组映射寄存器。它可以安排任何物理内存出现在设备可访问的地址范围内,并且它也可以使物理上分散的缓冲区看起来与设备相邻。这对于试图访问nVidia GPU的原始物理偏移的第三方PCI / PCI-Express卡或远程机器来说并不好,因为这可能导致实际无法访问预期的内存区域或禁止/限制IOMMU单位对每张卡的访问。必须禁用此功能,因为

  

&#34; GPUDirect的RDMA目前依赖所有物理地址   同样来自PCI设备&#39;观点。&#34;

     

-nVidia,Design Considerations for rDMA and GPUDirect

当驱动程序尝试利用CPU的MMU并映射内存映射I / O(MMIO)区域以在内核空间中使用时,它们通常会将返回的地址从内存映射保留到自身。因为每个驱动程序在其自己的上下文或命名空间内操作,所以在nVidia的驱动程序和其他希望支持rDMA + GPUDirect的第三方供应商的驱动程序之间交换这些映射将非常困难,将导致特定于供应商的解决方案(如果驱动程序与第三方的产品之间的差异很大,甚至可能是产品特定的)。此外,今天的操作系统目前还没有任何好的解决方案来交换驱动程序之间的MMIO映射,因此nVidia导出了几个允许第三方驱动程序从内核空间本身轻松访问这些信息的功能。 / p>

nVidia强制使用&#34;物理寻址&#34;通过rDMA访问GPUDirect的每张卡。这大大简化了通过使用该机器的物理寻址方案将数据从一台计算机移动到远程系统的PCI-Express总线的过程,而不必担心与虚拟寻址相关的问题(例如,将虚拟地址解析为物理的)。每张卡都有一个物理地址,可以在此偏移处访问;只有少量逻辑必须添加到试图执行rDMA操作的第三方驱动程序。此外,这些32位或64位基本地址寄存器是标准PCI配置空间的一部分,因此只需读取它的BAR即可轻松获得卡的物理地址,而不必获得nVidia的驱动程序在附加到卡上时获得的映射地址。 nVidia的通用虚拟寻址(UVA)负责将上述物理地址映射到用户空间应用程序的看似连续的内存区域,如下所示:

CUDA Virtual Address Space

这些内存区域又分为三种类型:CPU,GPU和FREE,它们都记录在案here

回到您的使用案例:由于您处于用户空间,因此您无法直接访问系统的物理地址空间和地址您正在使用的可能是nVidia的UVA提供给您的虚拟地址。假设之前没有进行任何分配,您的内存分配应该位于偏移量+ 0x00000000,这将导致您看到GPU本身的相同偏移量。如果你要分配第二个缓冲区,我想你会看到这个缓冲区在第一个缓冲区结束后立即开始(在距离GPU的基础虚拟地址的偏移+ 0x00100000处)您的1 MB分配情况)。

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

此外,值得注意的是,并非所有DMA引擎实际上都支持传输的虚拟地址 - 实际上,大多数需要物理地址,例如处理来自DMA引擎can get complex的虚拟寻址(第7页),因此许多DMA引擎都缺乏对此的支持。

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


关于您的应用程序,这里是您可以为rDMA操作执行的过程的简化细分:

  1. 您的用户空间应用程序会创建缓冲区,这些缓冲区位于nVidia提供的统一虚拟寻址空间范围内(虚拟地址)。
  2. 拨打cuPointerGetAttribute(...)以获取P2P令牌;这些令牌与CUDA上下文中的内存有关。
  3. 以某种方式将所有这些信息发送到内核空间(例如,IOCTL,读取/写入您的驱动程序等)。至少,您希望这三件事最终在您的内核空间驱动程序中:
    • cuPointerGetAttribute(...)
    • 返回的P2P令牌
    • 缓冲区的UVA虚拟地址
    • 缓冲区的大小
  4. 现在通过调用nVidia的内核空间函数将这些虚拟地址转换为相应的物理地址,因为这些地址保存在nVidia的页面表中,可以通过函数nVidia&#访问39;已导出,例如:nvidia_p2p_get_pages(...)nvidia_p2p_put_pages(...)nvidia_p2p_free_page_table(...)
  5. 使用上一步中获取的这些物理地址来初始化将要操作这些缓冲区的DMA引擎。
  6. 可以找到有关此过程的更深入的解释here