本文是:AMD KFD BO设计分析系列10:bo内存分配位置flags概述的细化篇。
1. 概述
在 KFD 的内存分配接口( kfd_ioctl_alloc_memory_of_gpu_args)中,flags 字段可用于指定分配内存的类型和属性。不同的 flag 决定了底层分配的物理区域、访问方式和用途。
分配位置flags决定了缓冲对象在物理内存中的布局策略,直接影响内存访问效率和硬件特性发挥。深入理解这些标志位是进行存储优化分析的前提条件。
本文聚焦于分配位置相关flags的解析,为后续基于物理存储位置的amdgpu_bo分析奠定基础。掌握本文核心概念有助于建立全局认知框架。透彻理解本文内容后,能够快速把握后续物理存储位置分析的要点,形成完整的知识体系架构。所以,不要跳过此文。
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/mmap)的系统内存传递给 GPU 计算。
- 支持 SVM(Shared Virtual Memory)和 userptr-alloc 的统一内存模型。
实现细节:
- 用户空间传入已有内存的虚拟地址,驱动通过 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 区域,实现用户态直接通知GPU硬件的低延迟队列通知。
- 典型用法:
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;
关于GTT的drm框架下的实现ttm的分析,详见:DRM-05-1:TTM ttm_tt技术分析系列。
4.3 零拷贝数据传输
用户空间分配大数组,直接注册为 GPU buffer:
flags = KFD_IOC_ALLOC_MEM_FLAGS_USERPTR;
va_addr = (uintptr_t)user_allocated_buffer;
同样的,uerptr bo的实现是AMD设计的特有实现,其底层也依赖于ttm。详见::DRM-05-1:TTM ttm_tt技术分析系列。
4.4 队列通知机制
创建 GPU 队列时分配 doorbell:
flags = KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL;
doorbell的具体实现详见:AMD rocr-libhsakmt分析系列7-1: doorbell机制概览。
4.5 调试与硬件控制
分配 MMIO 区域,用户空间直接访问 GPU 寄存器:
flags = KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP;
5. 总结与对比
KFD的内存分配flags机制为用户空间和内核空间之间搭建了高效的桥梁,通过精心设计的内存类型标志,灵活支持异构计算、深度学习、图形渲染等多样化应用场景。理解并正确选择这些flags是实现高效GPU资源管理的关键。
5.1 五种内存位置flags全面对比
| 对比维度 | VRAM (1 << 0) | GTT (1 << 1) | USERPTR (1 << 2) | DOORBELL (1 << 3) | MMIO_REMAP (1 << 4) |
|---|---|---|---|---|---|
| 物理位置 | GPU板载显存 | 系统内存(IOMMU映射) | 用户空间系统内存 | 专用硬件区域 | GPU MMIO寄存器区域 |
| 分配域 | AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT | AMDGPU_GEM_DOMAIN_CPU | AMDGPU_GEM_DOMAIN_CPU | AMDGPU_GEM_DOMAIN_CPU |
| BO类型 | ttm_bo_type_device | ttm_bo_type_device | ttm_bo_type_device | ttm_bo_type_sg | ttm_bo_type_sg |
| CPU直接访问 | 需PCIe BAR映射 (可见VRAM有限) | ✓ 直接访问 | ✓ 直接访问 (已存在用户内存) | ✓ mmap访问 | ✓ mmap访问 |
| GPU访问方式 | 直接高速访问 | 通过GART映射访问 | 通过IOMMU映射访问 | 硬件自动读取 | 硬件寄存器访问 |
| 访问带宽 | 最高 (HBM: ~1TB/s) | 中等 (PCIe: ~32GB/s) | 中等 (PCIe: ~32GB/s) | 低 (寄存器级) | 低 (寄存器级) |
| 访问延迟 | 最低 (~100ns) | 中等 (~1μs) | 中等 (~1μs) | 极低 (纳秒级) | 极低 (纳秒级) |
| 容量限制 | GPU显存大小 (4GB-128GB) | 系统RAM 3/8 (共享限制) | 系统RAM 15/16 (共享限制) | KB级 (每队列数字节) | KB级 (寄存器空间) |
| 缓存策略 | WC + Uncached | Cached(默认) WC(可选) | Cached | Uncached | Uncached |
| DMA支持 | ✓ 高性能DMA | ✓ Scatter-Gather DMA | ✓ 需pin页面 | ✗ 不适用 | ✗ 不适用 |
| 内存复用 | GPU独占 | CPU/GPU共享 | 零拷贝共享 | 队列专用 | 只读/调试用 |
| 页表管理 | GPU页表 | GPU页表 | GPU页表+MMU Notifier | 无需页表 | 无需页表 |
| 驱逐机制 | 可驱逐到GTT | 可驱逐到System | MMU Notifier自动处理 | 不可驱逐(固定) | 不可驱逐(MMIO) |
| 分配开销 | 低 | 低 | 中等 (需注册MMU) | 极低 | 极低 |
| 内存统计 | vram_used vram_pin_size | system_mem_used ttm_mem_used gart_pin_size | system_mem_used (不计size,仅acc_size) | 不计入统计 | 不计入统计 |
| 同步需求 | DMA Fence同步 | DMA Fence同步 | DMA Fence + MMU Notifier | 硬件自动同步 | 软件同步 |
| 典型用途 | • 模型参数 • 纹理数据 • 帧缓冲 • 高性能计算 | • 命令缓冲 • 数据交换 • 主机端缓存 • CPU/GPU协作 | • 零拷贝传输 • SVM统一内存 • 已分配应用内存 • HSA计算 | • 队列通知 • 任务提交 • 异步调度 • 低延迟信号 | • 调试分析 • 性能监控 • 硬件控制 • 寄存器访问 |
| 特殊标志 | VRAM_WIPE_ON_RELEASE CPU_ACCESS_REQUIRED NO_CPU_ACCESS VRAM_CONTIGUOUS | CPU_GTT_USWC (写合并) | 需设置va_addr (用户地址) | 队列创建时分配 | 需特殊权限 (安全考虑) |
| mmap偏移 | 返回mmap_offset (GPU地址偏移) | 返回mmap_offset | 使用用户va_addr | 返回doorbell_offset | 返回MMIO_offset |
| 适用场景 | GPU密集计算 | CPU/GPU频繁交互 | 数据已在用户空间 | 高性能队列管理 | 底层硬件调试 |
| 性能特点 | 最高GPU性能 | 平衡性能 | 零拷贝优化 | 超低延迟通知 | 直接硬件控制 |
| 安全性 | 进程隔离 | 进程隔离 | 用户空间隔离 MMU保护 | 队列隔离 | 受限访问 (调试权限) |
| 页面粒度 | 4KB/2MB/1GB | 4KB/2MB | 4KB(用户页) | 不适用 | 不适用 |
| TLB失效 | GPU TLB刷新 | GPU TLB刷新 | GPU TLB + CPU TLB | 不适用 | 不适用 |
5.2 内存限制配额对比
// 内存限制配额策略
kfd_mem_limit.max_system_mem_limit = mem - (mem >> 4); // 15/16 系统RAM
kfd_mem_limit.max_ttm_mem_limit = (mem >> 1) - (mem >> 3); // 3/8 系统RAM
| 内存类型 | 系统内存占用 | TTM内存占用 | VRAM占用 | 计算公式 |
|---|---|---|---|---|
| VRAM | acc_size | acc_size | size | system: acc, ttm: acc, vram: size |
| GTT | acc_size + size | acc_size + size | 0 | system: acc+size, ttm: acc+size |
| USERPTR | acc_size + size | acc_size | 0 | system: acc+size, ttm: acc |
| DOORBELL | acc_size | acc_size | 0 | system: acc, ttm: acc(SG类型) |
| MMIO_REMAP | acc_size | acc_size | 0 | system: acc, ttm: acc(SG类型) |
5.3 性能优化选择指南
5.3.1 选择决策树
需要分配内存?
│
├─ GPU密集计算,高带宽需求? ────────────────→ VRAM
│ └─ flags = KFD_IOC_ALLOC_MEM_FLAGS_VRAM
│
├─ CPU/GPU频繁交互,数据交换? ─────────────→ GTT
│ └─ flags = KFD_IOC_ALLOC_MEM_FLAGS_GTT
│
├─ 已有用户空间内存,零拷贝? ──────────────→ USERPTR
│ └─ flags = KFD_IOC_ALLOC_MEM_FLAGS_USERPTR
│ └─ va_addr = user_buffer
│
├─ GPU队列通知机制? ──────────────────────→ DOORBELL
│ └─ flags = KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL
│
└─ 调试或硬件寄存器访问? ─────────────────→ MMIO_REMAP
└─ flags = KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP
5.3.2 组合使用策略
实际应用中,常常需要组合使用多种flags:
// 示例1: 深度学习训练
// 模型参数在VRAM(高性能),输入数据用GTT(CPU预处理)
vram_flags = KFD_IOC_ALLOC_MEM_FLAGS_VRAM | KFD_IOC_ALLOC_MEM_FLAGS_WRITABLE;
gtt_flags = KFD_IOC_ALLOC_MEM_FLAGS_GTT | KFD_IOC_ALLOC_MEM_FLAGS_WRITABLE;
// 示例2: 图像处理
// 纹理数据VRAM(GPU渲染),结果回读GTT(CPU后处理)
texture_flags = KFD_IOC_ALLOC_MEM_FLAGS_VRAM;
readback_flags = KFD_IOC_ALLOC_MEM_FLAGS_GTT | KFD_IOC_ALLOC_MEM_FLAGS_WRITABLE;
// 示例3: HPC计算
// 大数组用USERPTR(避免拷贝),中间结果VRAM(高速计算)
input_flags = KFD_IOC_ALLOC_MEM_FLAGS_USERPTR;
compute_flags = KFD_IOC_ALLOC_MEM_FLAGS_VRAM | KFD_IOC_ALLOC_MEM_FLAGS_WRITABLE;
5.4 内存迁移路径
驱逐压力下的迁移路径:
VRAM ─驱逐→ GTT ─驱逐→ System RAM
↑ ↓
└──────validate────────┘
(性能需求时恢复到VRAM)
USERPTR: 特殊处理
用户页面失效 → MMU Notifier → 释放GPU映射 → 延迟恢复(1ms) → 重新获取页面
5.5 最佳实践建议
5.5.1 VRAM使用建议
- ✓ 适用:GPU密集计算、高频访问数据、纹理和帧缓冲
- ✗ 避免:CPU频繁读写、临时缓冲区、大块低频数据
- 📌 技巧:使用
CPU_ACCESS_REQUIRED标志控制可见性,合理利用visible/invisible VRAM
5.5.2 GTT使用建议
- ✓ 适用:命令缓冲、CPU/GPU共享数据、数据上传/下载
- ✗ 避免:纯GPU计算(浪费PCIe带宽)、实时性能关键路径
- 📌 技巧:使用write-combine(USWC)优化写入性能,批量传输减少PCIe往返
5.5.3 USERPTR使用建议
- ✓ 适用:零拷贝场景、已分配用户内存、SVM统一内存模型
- ✗ 避免:频繁分配/释放、页面频繁换出的内存区域
- 📌 技巧:Pin住关键页面,注意MMU Notifier开销,配合HMM使用
5.5.4 DOORBELL使用建议
- ✓ 适用:队列通知、任务提交、低延迟信号
- ✗ 避免:大数据传输、通用内存分配
- 📌 技巧:每个队列独立doorbell,避免伪共享(false sharing)
5.5.5 MMIO_REMAP使用建议
- ✓ 适用:调试分析、性能计数器、硬件诊断
- ✗ 避免:生产环境普通应用、安全敏感场景
- 📌 技巧:仅用于开发调试,注意访问权限限制
5.6 扩展与前沿技术
5.6.1 结合先进特性
- SVM(共享虚拟内存):结合USERPTR实现CPU/GPU统一地址空间
- DMA-BUF共享:跨设备、跨进程高效数据共享(详见系列6)
- Doorbell优化:实现低延迟、高吞吐的队列管理
- HMM(异构内存管理):自动页面迁移和统一内存
5.7 调试与诊断
各内存类型的常见问题及解决方案:
| 内存类型 | 常见问题 | 诊断方法 | 解决方案 |
|---|---|---|---|
| VRAM | OOM错误 | 检查vram_used统计 | 减小分配,使用GTT备份 |
| GTT | 性能下降 | PCIe带宽分析 | 批量传输,减少往返次数 |
| USERPTR | 页面失效频繁 | MMU Notifier计数 | Pin页面,避免swap |
| DOORBELL | 队列阻塞 | Doorbell寄存器状态 | 检查队列深度,优化提交频率 |
| MMIO_REMAP | 访问失败 | 权限检查 | 确认调试权限,检查地址范围 |
总结
KFD的五种内存分配flags(VRAM、GTT、USERPTR、DOORBELL、MMIO_REMAP)构成了完整的异构内存管理体系,每种类型都有其独特的应用场景和性能特点:
- VRAM是GPU高性能计算的核心,提供最高带宽和最低延迟
- GTT是CPU/GPU协作的桥梁,平衡性能和灵活性
- USERPTR实现零拷贝优化,支持SVM统一内存模型
- DOORBELL提供超低延迟队列通知机制
- MMIO_REMAP支持底层硬件调试和性能监控
驱动开发者和高性能计算应用开发者应根据实际场景选择合适的flags组合,合理利用各类内存的优势,实现高效、安全的GPU资源管理。
673

被折叠的 条评论
为什么被折叠?



