AMD KFD BO设计分析系列10-1:bo内存分配位置flags详解

本文是: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_VRAMAMDGPU_GEM_DOMAIN_GTTAMDGPU_GEM_DOMAIN_CPUAMDGPU_GEM_DOMAIN_CPUAMDGPU_GEM_DOMAIN_CPU
BO类型ttm_bo_type_devicettm_bo_type_devicettm_bo_type_devicettm_bo_type_sgttm_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 + UncachedCached(默认)
WC(可选)
CachedUncachedUncached
DMA支持✓ 高性能DMA✓ Scatter-Gather DMA✓ 需pin页面✗ 不适用✗ 不适用
内存复用GPU独占CPU/GPU共享零拷贝共享队列专用只读/调试用
页表管理GPU页表GPU页表GPU页表+MMU Notifier无需页表无需页表
驱逐机制可驱逐到GTT可驱逐到SystemMMU 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/1GB4KB/2MB4KB(用户页)不适用不适用
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占用计算公式
VRAMacc_sizeacc_sizesizesystem: acc, ttm: acc, vram: size
GTTacc_size + sizeacc_size + size0system: acc+size, ttm: acc+size
USERPTRacc_size + sizeacc_size0system: acc+size, ttm: acc
DOORBELLacc_sizeacc_size0system: acc, ttm: acc(SG类型)
MMIO_REMAPacc_sizeacc_size0system: 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 调试与诊断

各内存类型的常见问题及解决方案:

内存类型常见问题诊断方法解决方案
VRAMOOM错误检查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资源管理。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeplyMind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值