当前位置: 首页 > news >正文

AMD rocr-libhsakmt分析系列6-2:共享机制-import - 教程

前文:AMD rocr-libhsakmt分析系列6-1:共享机制-export分析了export hsa的bo,本文分析import graphics传过来的bo。

概述

函数名称: hsaKmtRegisterSharedHandle/hsakmt_fmm_register_graphics_handle
功能: 将图形栈(通过 DMA-BUF)导出的 GPU 内存注册到 ROCm/HSA 栈,实现图形与计算的内存互操作


1. 函数签名与参数

HSAKMT_STATUS hsakmt_fmm_register_graphics_handle(
HSAuint64 GraphicsResourceHandle,          // DMA-BUF 文件描述符
HsaGraphicsResourceInfo *GraphicsResourceInfo,  // 输出:资源信息
uint32_t *gpu_id_array,                    // GPU ID 数组(可选)
uint32_t gpu_id_array_size,                // GPU ID 数组大小
HSA_REGISTER_MEM_FLAGS RegisterFlags       // 注册标志
);

参数详解

参数类型方向说明
GraphicsResourceHandleHSAuint64IN图形栈导出的 DMA-BUF fd
GraphicsResourceInfoHsaGraphicsResourceInfo*OUT返回的资源信息(地址、大小、元数据)
gpu_id_arrayuint32_t*IN要映射的 GPU ID 列表(可为 NULL)
gpu_id_array_sizeuint32_tINGPU ID 数组大小
RegisterFlagsHSA_REGISTER_MEM_FLAGSIN注册标志(如是否需要虚拟地址)

2. 函数主流程分析

2.1 流程概览

┌──────────────────────────────────────────────────────────────────┐
│ 1. 参数验证                                                       │
│    - 检查 gpu_id_array 与 gpu_id_array_size 的一致性               │
└────────────────┬─────────────────────────────────────────────────┘│▼
┌──────────────────────────────────────────────────────────────────┐
│ 2. 第一个 IOCTL: AMDKFD_IOC_GET_DMABUF_INFO                       │
│    - 输入: DMA-BUF fd                                             │
│    - 输出: Buffer 信息(大小、GPU ID、标志、元数据)                  │
│    - 特性: 支持元数据动态调整(两次调用机制)                          │
└────────────────┬─────────────────────────────────────────────────┘│▼
┌──────────────────────────────────────────────────────────────────┐
│ 3. 选择内存 Aperture                                              │
│    - 无 VA 要求 → mem_handle_aperture                             │
│    - SVM 需求 → svm.dgpu_aperture                                 │
│    - 普通情况 → gpu_mem[id].gpuvm_aperture                        │
└────────────────┬─────────────────────────────────────────────────┘│▼
┌──────────────────────────────────────────────────────────────────┐
│ 4. 虚拟地址分配                                                    │
│    - aperture_allocate_area_aligned()                            │
│    - 对齐要求: IMAGE_ALIGN (256KB)                                │
│    - 互斥锁保护: aperture->fmm_mutex                               │
└────────────────┬─────────────────────────────────────────────────┘│▼
┌──────────────────────────────────────────────────────────────────┐
│ 5. 第二个 IOCTL: AMDKFD_IOC_IMPORT_DMABUF                         │
│    - 输入: DMA-BUF fd, VA 地址, GPU ID                            │
│    - 输出: KFD handle                                             │
│    - 功能: 将 DMA-BUF 导入到 KFD 并映射到指定 VA                     │
└────────────────┬─────────────────────────────────────────────────┘│▼
┌──────────────────────────────────────────────────────────────────┐
│ 6. 创建 VM 对象并注册                                              │
│    - aperture_allocate_object()                                  │
│    - 设置内存标志(强制 CoarseGrain)                               │
│    - 保存元数据和 GPU ID 数组                                      │
└────────────────┬─────────────────────────────────────────────────┘│▼
┌──────────────────────────────────────────────────────────────────┐
│ 7. 填充输出信息                                                    │
│    - MemoryAddress: 分配的虚拟地址                                 │
│    - SizeInBytes: Buffer 大小                                     │
│    - Metadata: 元数据指针                                          │
│    - NodeId: GPU 节点 ID                                          │
└──────────────────────────────────────────────────────────────────┘

3. 核心 IOCTL 分析

3.1 第一个 IOCTL: AMDKFD_IOC_GET_DMABUF_INFO

3.1.1 IOCTL 参数结构
struct kfd_ioctl_get_dmabuf_info_args {
__u64 size;           // [OUT] Buffer 大小(字节)
__u64 metadata_ptr;   // [IN/OUT] 元数据缓冲区指针
__u32 metadata_size;  // [IN/OUT] 元数据大小
//   IN: 用户分配的缓冲区大小
//   OUT: 实际元数据大小
__u32 gpu_id;         // [OUT] 创建此 buffer 的 GPU ID
__u32 flags;          // [OUT] 内存标志 (KFD_IOC_ALLOC_MEM_FLAGS_*)
__u32 dmabuf_fd;      // [IN] DMA-BUF 文件描述符
};
3.1.2 实现代码
// 准备参数
infoArgs.dmabuf_fd = GraphicsResourceHandle;
infoArgs.metadata_size = GRAPHICS_METADATA_DEFAULT_SIZE;  // 预设默认大小
metadata = calloc(infoArgs.metadata_size, 1);
infoArgs.metadata_ptr = (uint64_t)metadata;
r = hsakmt_ioctl(hsakmt_kfd_fd, AMDKFD_IOC_GET_DMABUF_INFO, (void *)&infoArgs);
3.1.3 IOCTL 功能详解

作用:

  1. 验证 DMA-BUF: 检查 fd 是否有效且为 AMD GPU buffer
  2. 获取 Buffer 属性:
    • size: Buffer 的实际大小
    • gpu_id: 创建该 buffer 的 GPU 标识
    • flags: 内存属性标志(可写、一致性等)
  3. 提取元数据: 读取图形栈设置的元数据
3.1.4 内核侧处理(概念)
KFD 内核驱动侧:
1. 通过 dmabuf_fd 找到对应的 dma_buf 对象
2. 从 dma_buf->priv 获取 amdgpu_bo (buffer object)
3. 读取 BO 属性:- bo->tbo.base.size → size- bo->preferred_domains → flags- bo->metadata → 拷贝到用户空间
4. 确定所属 GPU ID
5. 返回给用户空间

3.2 Aperture 选择逻辑

// 找到对应的 GPU 内存管理器
gpu_mem_id = gpu_mem_find_by_gpu_id(infoArgs.gpu_id);
// 根据需求选择 Aperture
if (!gpu_id_array && gpu_id_array_size == 0 &&
!RegisterFlags.ui32.requiresVAddr) {
// 场景 1: 不需要虚拟地址的导入
aperture = &mem_handle_aperture;
} else if (hsakmt_topology_is_svm_needed(
gpu_mem[gpu_mem_id].EngineId)) {
// 场景 2: 需要 SVM (Shared Virtual Memory)
aperture = svm.dgpu_aperture;
} else {
// 场景 3: 普通 GPU 虚拟内存
aperture = &gpu_mem[gpu_mem_id].gpuvm_aperture;
aperture_base = aperture->base;
}
Aperture 类型说明
Aperture 类型使用场景特点
mem_handle_aperture仅需要 handle,不需要 VA节省虚拟地址空间
svm.dgpu_apertureSVM 应用(CPU/GPU 共享地址)地址对所有设备可见
gpuvm_aperture普通 GPU 专用内存每个 GPU 独立地址空间

Aperture 选择影响:

  • 性能: SVM 可能有额外的一致性开销
  • 地址空间: 不同 aperture 管理不同的地址范围
  • 可访问性: 决定哪些设备可以访问该内存

3.3 虚拟地址分配

// 在选定的 aperture 中分配虚拟地址
mem = aperture_allocate_area_aligned(
aperture,        // 目标 aperture
NULL,            // 地址提示(NULL = 自动选择)
infoArgs.size,   // 大小
IMAGE_ALIGN      // 对齐 (256KB)
);
}

关键技术点:

  1. 线程安全: 使用互斥锁保护 aperture 操作
  2. 对齐要求: 256KB 对齐
    • 符合 GPU 页表要求
    • 优化图形资源访问性能
    • 可能与纹理 tiling 要求相关
  3. 自动分配: 传递 NULL 让系统自动选择最佳地址

内部机制 :

aperture_allocate_area_aligned() {
// 在 aperture 的空闲区域列表中查找
// 考虑对齐要求
// 返回分配的虚拟地址
}

3.4 第二个 IOCTL: AMDKFD_IOC_IMPORT_DMABUF

3.4.1 IOCTL 参数结构
struct kfd_ioctl_import_dmabuf_args {
__u64 va_addr;   // [IN] 目标虚拟地址
//      如果为 0,则不需要映射到 VA
__u64 handle;    // [OUT] KFD 内部 handle
__u32 gpu_id;    // [IN] 目标 GPU ID
__u32 dmabuf_fd; // [IN] DMA-BUF 文件描述符
};
3.4.2 实现代码
// 准备导入参数
if (aperture == &mem_handle_aperture)
importArgs.va_addr = 0;  // 不需要 VA 映射
else
importArgs.va_addr = VOID_PTRS_SUB(mem, aperture_base);  // 计算相对地址
importArgs.gpu_id = infoArgs.gpu_id;
importArgs.dmabuf_fd = GraphicsResourceHandle;
// 调用 IOCTL
r = hsakmt_ioctl(hsakmt_kfd_fd, AMDKFD_IOC_IMPORT_DMABUF, (void *)&importArgs);
if (r) {
pthread_mutex_unlock(&aperture->fmm_mutex);
goto error_release_aperture;
}
3.4.3 VA 地址计算
// VOID_PTRS_SUB 宏定义(概念)
#define VOID_PTRS_SUB(p1, p2) ((char*)(p1) - (char*)(p2))
// 示例:
// aperture_base = 0x8000000000
// mem = 0x8000100000
// va_addr = 0x8000100000 - 0x8000000000 = 0x100000 (相对偏移)

为什么使用相对地址:

  • GPU 页表中记录的是相对于 aperture base 的偏移
  • 内核驱动会将相对地址转换为实际物理地址映射
  • 支持 aperture 的动态重定位
3.4.4 IOCTL 功能详解

内核侧处理流程:

KFD 内核驱动侧:
┌─────────────────────────────────────────────────────────────┐
│ 1. DMA-BUF 查找                                              │
│    - dma_buf = dma_buf_get(dmabuf_fd)                       │
│    - amdgpu_bo = dma_buf->priv                              │
└────────────────┬────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ 2. BO 导入到 KFD                                             │
│    - 增加 BO 引用计数                                         │
│    - 创建 KFD 内部的 kfd_bo_va_list 结构                      │
│    - 分配 handle (唯一标识符)                                 │
└────────────────┬────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ 3. GPU 页表映射 (如果 va_addr != 0)                           │
│    - 计算物理地址: bo->tbo.resource->start                    │
│    - 更新 GPU 页表: VA → PA 映射                              │
│    - 设置页表项属性(缓存策略、权限等)                           │
└────────────────┬────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│ 4. 返回 handle                                               │
│    - 用户空间通过 handle 管理该映射                             │
│    - Unmap/Free 时需要提供此 handle                           │
└─────────────────────────────────────────────────────────────┘

关键操作:

  1. 引用计数管理: 防止 BO 在使用时被释放
  2. 页表更新: 建立 GPU 可访问的虚拟地址映射
  3. Handle 生成: 提供用户空间管理接口

3.5 VM 对象创建与注册

// 转换内存标志
mflags = fmm_translate_ioc_to_hsa_flags(infoArgs.flags);
mflags.ui32.CoarseGrain = 1;  // 强制设置为粗粒度内存
// 创建 VM 对象
obj = aperture_allocate_object(
aperture,             // 所属 aperture
mem,                  // 虚拟地址
importArgs.handle,    // KFD handle
infoArgs.size,        // 大小
mflags                // 内存标志
);

关键点 - 强制 CoarseGrain:

mflags.ui32.CoarseGrain = 1;  // ⚠️ 无条件设置

为什么强制 CoarseGrain:

  1. 图形内存特性: 图形栈分配的内存通常是粗粒度的
  2. 性能考虑: 粗粒度内存带宽更高,适合图形资源
  3. 一致性简化: 避免细粒度一致性维护开销

3.6 填充输出信息

GraphicsResourceInfo->MemoryAddress = mem;
GraphicsResourceInfo->SizeInBytes = infoArgs.size;
GraphicsResourceInfo->Metadata = (void *)(unsigned long)infoArgs.metadata_ptr;
GraphicsResourceInfo->MetadataSizeInBytes = infoArgs.metadata_size;
hsakmt_gpuid_to_nodeid(infoArgs.gpu_id, &GraphicsResourceInfo->NodeId);
return HSAKMT_STATUS_SUCCESS;

返回信息汇总:

字段来源说明
MemoryAddressmem分配的虚拟地址(ROCm 栈可见)
SizeInBytesinfoArgs.sizeBuffer 大小
MetadatainfoArgs.metadata_ptr元数据指针
MetadataSizeInBytesinfoArgs.metadata_size元数据大小
NodeIdgpu_id 转换HSA 节点 ID

4. 关键技术点总结

4.1 两个 IOCTL 的协同

AMDKFD_IOC_GET_DMABUF_INFO          AMDKFD_IOC_IMPORT_DMABUF│                                    │├─ 获取 buffer 属性                   ├─ 导入 buffer 到 KFD├─ 验证 DMA-BUF 有效性                ├─ 建立 VA → PA 映射├─ 读取元数据                         ├─ 更新 GPU 页表└─ 返回 GPU ID、大小、标志             └─ 返回 KFD handle│                                    │└────────── 信息传递 ──────────────┘(gpu_id, size, flags)

为什么需要两个 IOCTL:

  1. 职责分离: 查询验证 vs 执行操作
  2. 灵活性: 用户可以先查询再决定是否导入
  3. 性能优化: 批量查询多个 buffer 后一次性导入
  4. 错误处理: 查询失败不影响系统状态

4.2 DMA-BUF 的生命周期管理

图形栈                  用户空间 (Thunk)              KFD 内核│                          │                         ││ amdgpu_bo_export         │                         ││─────────────────────────>│                         ││ (返回 dmabuf_fd)         │                          ││                          │ GET_DMABUF_INFO         ││                          │────────────────────────>││                          │                         │ dma_buf_get(fd)│                          │                         │ 引用计数 +1│                          │<────────────────────────││                          │ (返回 buffer 信息)       ││                          │                         ││                          │ IMPORT_DMABUF           ││                          │────────────────────────>││                          │                         │ dma_buf_get(fd)│                          │                         │ 引用计数 +1│                          │<────────────────────────││                          │ (返回 handle)            ││                          │                         ││ close(dmabuf_fd)         │                         ││<─────────────────────────│                         ││                          │                         │ 引用计数 -1│                          │                         │ (仍然 > 0)│ amdgpu_bo_free           │                         ││<─────────────────────────│                         ││                          │                         │ 引用计数 -1│                          │                         │ (仍然 > 0)│                          │ FREE_MEMORY_OF_GPU      ││                          │────────────────────────>││                          │                         │ 引用计数 -1│                          │                         │ (降为 0,释放)

关键点:

  • DMA-BUF fd 可以在导入后立即关闭
  • KFD 持有独立的引用,保证 buffer 不会被过早释放
  • 只有所有引用都释放后,物理内存才真正回收

4.3 内存标志的重要性

// 从 KFD 标志转换
mflags = fmm_translate_ioc_to_hsa_flags(infoArgs.flags);
// ⚠️ 强制设置 CoarseGrain
mflags.ui32.CoarseGrain = 1;

内存标志影响的方面:

  1. 缓存策略: CoarseGrain vs FineGrain
  2. 一致性模型: 是否需要缓存刷新
  3. 访问权限: ReadOnly vs Writable
  4. 性能: 粗粒度内存带宽更高

5. 应用场景示例

场景 1: Vulkan 计算后处理

// Vulkan 渲染输出
VkDeviceMemory vkMem = RenderScene();
// 导出为 DMA-BUF
int fd = vkGetMemoryFdKHR(vkMem);
// 注册到 ROCm
HsaGraphicsResourceInfo info;
hsakmt_fmm_register_graphics_handle(
fd, &info, NULL, 0, {0}
);
// HIP kernel 进行后处理
PostProcessKernel<<<...>>>(info.MemoryAddress, ...);// 继续 Vulkan 渲染(无需拷贝)

场景 2: 视频解码 + AI 分析

// VA-API 硬件解码
VASurfaceID surface = DecodeH264Frame();
// 导出帧
VADRMPRIMESurfaceDescriptor desc;
vaExportSurfaceHandle(surface, &desc);
// 注册到 ROCm
HsaGraphicsResourceInfo info;
hsakmt_fmm_register_graphics_handle(
desc.objects[0].fd, &info, NULL, 0, {0}
);
// 直接在 GPU 上运行推理
RunObjectDetection(model, info.MemoryAddress);

6. 总结与改进建议

6.1 核心功能

hsakmt_fmm_register_graphics_handle 函数是 ROCm 图形互操作的关键入口点,通过两个 IOCTL 实现:

  1. GET_DMABUF_INFO: 查询 DMA-BUF buffer 的属性和元数据
  2. IMPORT_DMABUF: 将 buffer 导入到 KFD 并建立虚拟地址映射

6.2 改进建议

纯属在工作中遇到的问题,不一定合理。官方不支持,只能自己加了。

建议 1: 添加性能计数器

struct {
uint64_t total_imports;
uint64_t metadata_reallocs;
uint64_t avg_import_time_us;
} import_stats;

建议 2: 支持批量导入

HSAKMT_STATUS hsakmt_fmm_register_graphics_handles_batch(
HSAuint64 *handles,
uint32_t count,
HsaGraphicsResourceInfo *infos
);

建议 3: 异步导入支持

// 适用于大量小 buffer 的场景
HSAKMT_STATUS hsakmt_fmm_register_graphics_handle_async(
HSAuint64 handle,
completion_callback_t callback
);
http://www.jsqmd.com/news/262637/

相关文章:

  • Olib 2.4.4|免梯下载Zlibrary电子书 开源免费 需要自己的Zlib账号
  • 学霸同款2026 AI论文平台TOP9:继续教育写作全解析
  • 同昌新材料产品好用吗?在行业内地位怎样? - 工业品牌热点
  • 【SSM毕设全套源码+文档】基于SSM的疫情健康上报管理系统设计与实现(丰富项目+远程调试+讲解+定制)
  • 【SSM毕设全套源码+文档】基于SSM的优选农产品销售管理系统设计与实现(丰富项目+远程调试+讲解+定制)
  • 【SSM毕设源码分享】基于SSM+vue的学生干部管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)
  • 强烈安利10个论文写作工具,研究生一键生成论文工具推荐!
  • 存储--SMP(软件制作平台)语言基础知识之四十
  • 【SSM毕设源码分享】基于SSM+vue的学生选课管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)
  • 基于SpringBoot+Vue的精品在线试题库系统管理系统设计与实现【Java+MySQL+MyBatis完整源码】
  • CST Studio Suite Python自动化学习笔记
  • 详细介绍:【040-安全开发篇】JavaEE应用SpringBoot框架JWT身份鉴权打包部署JARWAR
  • 机械行业如何利用百度UE的JS截屏功能优化设计文档导入?
  • CSS盒子模型:网页布局的基石与艺术
  • 射频-驻波比的理解
  • Gemini CLI快速运维
  • Linux 基础开发工具详解(Yum, Vim, GCC, Make, GDB, Git) - 指南
  • 组合螺丝专业供应商靠谱吗,东层紧固件有哪些信任背书? - 工业品牌热点
  • 复合式分板机国内选哪个厂家的产品好? - 工业品牌热点
  • Godot Win32
  • 芯片制造企业网页如何集成百度开源上传组件实现文件夹上传?
  • 极端天气数据集 恶劣天气数据集 雾天道路行驶数据集 黑夜情境下的交通数据集 红绿灯检测数据集 疲劳驾驶检测数据集 交通路锥检测数据集 路面标识交通引导线 车道线检测数据集]
  • 14.2 去中心化协同与导航:基于LIVEPOINT框架的无死锁多机器人系统
  • 机械制造行业网页如何用html5实现文件夹上传?
  • 14.3 任务分配与协同操作:从市场拍卖到分布式优化的多机器人协作
  • 基于SpringBoot+Vue网络办公自动化系统的设计与实现
  • 15.1 机器人控制的伦理、安全与社会影响:技术成熟度伴随的责任审视
  • hadoop集群搭建 (超详细) 接入Impala、Hive,AI 大模型的数据底座 - 教程
  • 仓库管理软件哪个好用,有没有最简单的出入库系统
  • 15.2 技术融合与未来展望:AI、新材料与生物交叉驱动下的新形态与新能力