8

我们知道:http ://en.wikipedia.org/wiki/IOMMU#Advantages

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

在此处输入图像描述

但是当我们使用 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

从 PCI 设备的角度来看,GPUDirect 的 RDMA 目前依赖于所有物理地址相同。这使得它与 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 虚拟地址空间中的简单指针?

4

1 回答 1

14

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

“从 PCI 设备的角度来看,GPUDirect 的 RDMA 目前依赖于所有物理地址相同。”

-nVidia,rDMA 和 GPUDirect 的设计注意事项

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

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

CUDA 虚拟地址空间

这些内存区域进一步分为三种类型:CPU、GPU 和 FREE,它们都在此处记录。

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

但是,如果您在内核空间中,并且正在为您公司的卡编写驱动程序以将 rDMA 用于 GPUDirect,您将使用系统的 BIOS 和/或操作系统分配给 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、读/写到您的驱动程序等)。至少,您希望这三样东西最终出现在您的内核空间驱动程序中:
    • P2P 令牌返回的cuPointerGetAttribute(...)
    • 缓冲区的 UVA 虚拟地址
    • 缓冲区大小
  4. 现在通过调用 nVidia 的内核空间函数将这些虚拟地址转换为它们对应的物理地址,因为这些地址保存在 nVidia 的页表中,并且可以通过函数的 nVidia 导出来访问,例如:nvidia_p2p_get_pages(...)nvidia_p2p_put_pages(...)nvidia_p2p_free_page_table(...)
  5. 使用在上一步中获取的这些物理地址来初始化将操作这些缓冲区的 DMA 引擎。

可以在此处找到有关此过程的更深入解释。

于 2013-11-21T23:27:00.193 回答