1. 背景与概念
KFD(Kernel Fusion Driver)是 AMD ROCm 平台下用于异构计算(HSA/HPC/GPU)资源管理的内核驱动。KFD 通过 ioctl 接口为用户空间应用(如 OpenCL、HIP、Pytorch、TensorFlow 等)提供 GPU 资源分配、队列管理、事件通知、内存分配等功能。
在 KFD 的内存分配接口(如 kfd_ioctl_alloc_memory_of_gpu_args
)中,flags
字段用于指定分配内存的类型和属性。不同的 flag 决定了底层分配的物理内存区域、访问方式和用途。
本文针对分配位置的相关flags进行分析。
2. 各内存类型 flag 的含义
2.1 KFD_IOC_ALLOC_MEM_FLAGS_VRAM (1 << 0)
含义:分配 GPU 的专用显存(VRAM,Video RAM)。
应用场景:
-
适用于需要高带宽、低延迟的 GPU 计算任务,如深度学习模型参数、图像处理等。
-
分配的内存物理上位于 GPU 的本地显存,CPU 通常不可直接访问(除非支持 PCIe BAR 映射或 HSA SVM)。
实现细节:
-
内核驱动调用 amdgpu/ttm 等底层接口分配 VRAM。
-
用户空间通过 mmap 或 DMA-BUF 导出访问分配的 VRAM。
-
典型用法:
flags = KFD_IOC_ALLOC_MEM_FLAGS_VRAM
,分配后返回 handle 和 mmap offset。
2.2 KFD_IOC_ALLOC_MEM_FLAGS_GTT (1 << 1)
含义:分配 GTT(Graphics Translation Table)内存,也称为可分页显存或系统内存。
应用场景:
-
适用于需要 CPU/GPU 共享访问的缓冲区,如数据交换、映射文件、主机侧缓存等。
-
GTT 内存物理上位于系统 RAM,通过 IOMMU 映射到 GPU 地址空间。
实现细节:
-
内核驱动分配系统内存,并通过 GTT 映射到 GPU。
-
支持 CPU 直接访问,适合 host-GPU 协作场景。
-
典型用法:
flags = KFD_IOC_ALLOC_MEM_FLAGS_GTT
。
2.3 KFD_IOC_ALLOC_MEM_FLAGS_USERPTR (1 << 2)
含义:分配用户指针(userptr)类型内存,即直接使用用户空间已有的内存作为 GPU buffer。
应用场景:
-
适用于零拷贝场景,如直接将应用分配的 malloc/buffer 传递给 GPU 计算。
-
支持 SVM(Shared Virtual Memory)和 HSA 的统一内存模型。
实现细节:
-
用户空间传入已有内存的虚拟地址,驱动通过 MMU notifier 追踪页表变化。
-
GPU 通过 IOMMU 访问用户空间内存,支持页迁移和异常处理。
-
典型用法:
flags = KFD_IOC_ALLOC_MEM_FLAGS_USERPTR
,并设置va_addr
为用户空间地址。
2.4 KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL (1 << 3)
含义:分配 Doorbell 区域,用于 GPU 队列的信号通知。
应用场景:
-
Doorbell 是 GPU 队列机制中的关键内存区域,用户空间通过写入 doorbell 通知 GPU 有新任务。
-
适用于高性能队列提交、异步任务调度等场景。
实现细节:
-
驱动分配专用的 doorbell 内存,并返回物理/虚拟地址给用户空间。
-
用户空间通过 mmap doorbell 区域,实现低延迟队列通知。
-
典型用法:
flags = KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL
,用于队列创建时分配 doorbell。
2.5 KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP (1 << 4)
含义:分配 MMIO(Memory-Mapped I/O)重映射区域,允许用户空间访问 GPU 的 MMIO 寄存器。
应用场景:
-
适用于调试、性能分析、特殊硬件控制等场景。
-
用户空间可通过 mmap 访问 GPU 的部分寄存器,实现直接硬件控制。
实现细节:
-
驱动分配 MMIO 区域,并设置合适的访问权限。
-
典型用法:
flags = KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP
,用于特殊用途的内存分配。
3. 应用实现流程
3.1 用户空间分配内存
用户空间通过 ioctl 调用分配内存:
struct kfd_ioctl_alloc_memory_of_gpu_args args = {
.va_addr = ..., // 虚拟地址(userptr类型时有效)
.size = ..., // 分配大小
.gpu_id = ..., // 目标 GPU
.flags = KFD_IOC_ALLOC_MEM_FLAGS_VRAM | KFD_IOC_ALLOC_MEM_FLAGS_WRITABLE,
};
ioctl(kfd_fd, AMDKFD_IOC_ALLOC_MEMORY_OF_GPU, &args);
3.2 内核驱动处理分配请求
驱动根据 flags 选择分配方式:
-
VRAM:调用 GPU 驱动分配显存,建立 GPU 地址映射。
-
GTT:分配系统内存,通过 GTT 映射到 GPU。
-
USERPTR:注册用户空间内存,建立 IOMMU 映射,追踪页表变化。
-
DOORBELL:分配 doorbell 区域,返回给队列管理模块。
-
MMIO_REMAP:分配 MMIO 区域,设置访问权限。
驱动完成分配后,返回 handle 和 mmap offset 给用户空间。
3.3 用户空间访问分配的内存
用户空间通过 mmap 或 DMA-BUF 导出访问分配的内存:
void *ptr = mmap(NULL, args.size, PROT_READ | PROT_WRITE,
MAP_SHARED, kfd_fd, args.mmap_offset);
对于 doorbell 或 MMIO 区域,用户空间可直接写入/读取,实现队列通知或寄存器访问。
4. 典型应用场景举例
4.1 GPU 计算任务分配显存
深度学习框架分配 VRAM 作为模型参数存储:
flags = KFD_IOC_ALLOC_MEM_FLAGS_VRAM | KFD_IOC_ALLOC_MEM_FLAGS_WRITABLE;
4.2 主机与 GPU 共享缓冲区
数据预处理后直接传递给 GPU:
flags = KFD_IOC_ALLOC_MEM_FLAGS_GTT | KFD_IOC_ALLOC_MEM_FLAGS_WRITABLE;
4.3 零拷贝数据传输
用户空间分配大数组,直接注册为 GPU buffer:
flags = KFD_IOC_ALLOC_MEM_FLAGS_USERPTR;
va_addr = (uintptr_t)user_allocated_buffer;
4.4 队列通知机制
创建 GPU 队列时分配 doorbell:
flags = KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL;
4.5调试与硬件控制
分配 MMIO 区域,用户空间直接访问 GPU 寄存器:
flags = KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP;
5. 总结与扩展
KFD 的内存分配 flags 设计为用户空间和内核空间之间的桥梁,灵活支持多种内存类型和访问模式,满足异构计算、深度学习、图形渲染等多样化需求。驱动开发者和高性能计算应用开发者应根据实际场景选择合适的 flags,合理利用 VRAM、GTT、USERPTR、DOORBELL 和 MMIO_REMAP 等类型,实现高效、安全的 GPU 资源管理。
扩展建议:
-
结合 SVM(共享虚拟内存)和 DMA-BUF,实现跨设备、跨进程的高效数据共享。
-
利用 doorbell 和 MMIO 区域,实现低延迟、高性能的队列和硬件控制。
-
针对安全和性能需求,合理设置访问属性(如 WRITABLE、EXECUTABLE、COHERENT 等)。
如有帮助,请三连:点赞、收藏、加关注。