4-6 指针信息反查:HsaPointerInfo

发布时间:2026/8/30 6:07:51
4-6 指针信息反查:HsaPointerInfo 1. 概述与定位第 4 章的前序各节沿内存对象的正向生命周期展开从hsaKmtAllocMemory/hsaKmtRegisterMemory完成分配与注册经hsaKmtMapMemoryToGPU建立 GPU 侧映射到hsaKmtUnmapMemoryToGPU、hsaKmtDeregisterMemory、hsaKmtFreeMemory逐级回收。这条主线描述的是由参数生成对象的过程。在 HSA 异构计算系统中正是由于同一段虚拟地址可能来自设备分配、用户指针注册、图形资源注册、IPC 共享或纯地址预留等多种途径因而承载着各不相同的元信息。本篇讨论其逆向能力给定一个已经存在的虚拟地址如何反向查找出它所对应内存对象的完整元数据。这一能力由两个 APIs 提供hsaKmtQueryPointerInfo根据地址查询类型、归属节点、大小以及注册与映射拓扑等信息hsaKmtSetMemoryUserData后者允许上层把一份自定义句柄挂接到该内存对象上供后续查询时取回。二者共同构成 libhsakmt 面向上层运行时的地址到元数据反查通道。ROCr 运行时实现hsa_amd_pointer_info等对外查询语义时正是以此为底层支撑将一个裸指针解析为运行时可理解的内存描述。2. 数据结构HsaPointerInfo与HSA_POINTER_TYPE查询结果统一承载于HsaPointerInfo结构定义位于hsakmttypes.h。其字段可分为四组。类型与归属Type标识内存对象的种类取值来自HSA_POINTER_TYPE枚举Node给出内存所在的节点编号MemFlags回填该对象分配时所用的HsaMemFlags。地址与尺寸CPUAddress为 CPU 侧访问起始地址GPUAddress为 GPU 侧访问起始地址SizeInBytes为对象大小。三者的取值随对象类型而有所差异详见第 5 节。注册与映射拓扑NRegisteredNodes与RegisteredNodes描述内存被注册到的节点集合NMappedNodes与MappedNodes描述内存实际映射到的节点集合。数组内容由库内部维护。用户数据UserData保存上层通过hsaKmtSetMemoryUserData关联的自定义指针。HSA_POINTER_TYPE定义了六种内存对象类别取值含义HSA_POINTER_UNKNOWN未能识别的地址查询失败时的缺省值HSA_POINTER_ALLOCATED经hsaKmtAllocMemory分配的设备内存scratch 除外HSA_POINTER_REGISTERED_USER注册进来的用户态指针userptrHSA_POINTER_REGISTERED_GRAPHICS注册的图形资源缓冲HSA_POINTER_REGISTERED_SHARED注册的共享缓冲IPC 导入HSA_POINTER_RESERVED_ADDR仅保留虚拟地址、尚未落实后端内存的预留 VA需要强调的是RegisteredNodes与MappedNodes指向的是库内部为该内存对象持有的数组其有效期受注册状态与映射状态的变更约束一旦发生解注册、注册到新节点、解映射、映射到新节点或内存释放相应数组即可能被释放或重建。调用方应在查询后即时使用这些数据不应长期缓存指针。3. 接口原型与调用契约两个接口的原型定义于公共头文件hsakmt.hHSAKMT_STATUS HSAKMTAPIhsaKmtQueryPointerInfo(constvoid*Pointer,// INHsaPointerInfo*PointerInfo// OUT);HSAKMT_STATUS HSAKMTAPIhsaKmtSetMemoryUserData(constvoid*Pointer,// INvoid*UserData// IN);hsaKmtQueryPointerInfo接收待查询地址与输出结构指针成功时以完整元数据回填PointerInfo。hsaKmtSetMemoryUserData接收地址与一份任意用户指针将其绑定到对应内存对象。在多上下文subcontext体系下二者均为对应*Ctx版本的薄包装。hsaKmtQueryPointerInfo与hsaKmtSetMemoryUserData直接以主上下文hsakmt_primary_kfd_ctx为参数转发至hsaKmtQueryPointerInfoCtx与hsaKmtSetMemoryUserDataCtx。这一设计与第 4 章其余内存接口一致单上下文形态是多上下文形态在主上下文上的特例二者共享同一套内部实现。4. 调用链路从公共接口到内部实现两条链路结构对称查询路径hsaKmtQueryPointerInfo→hsaKmtQueryPointerInfoCtx→hsakmt_fmm_get_mem_info。*Ctx层负责CHECK_KFD_OPEN状态检查与输出参数非空校验随后进入 fmmFine-grained Memory Manager层完成实际查询。写入路径hsaKmtSetMemoryUserData→hsaKmtSetMemoryUserDataCtx→hsakmt_fmm_set_mem_user_data。*Ctx层同样先执行状态检查再转入 fmm 层完成用户数据挂接。两条路径的核心逻辑都落在 fmm 层且都以同一套地址到 VM 对象的查找机制为基础。5. 查询实现hsakmt_fmm_get_mem_info查询实现的第一步是地址定位由vm_find_object完成。该函数以UINT64_MAX作为 size 参数表示允许地址落在对象区间内部的任意位置而非要求精确匹配起始地址。定位过程依序在多个 aperture 中检索先在各 GPU 的 GPUVM aperture 中按地址区间匹配其次是 mem_handle aperture再次是 SVM 的主 aperture 与备用 aperture若均不命中且地址处于 SVM 范围之外则回退按 userptr 语义在 SVM aperture 中查找在 APU非独立显卡场景下还会追加在 CPUVM aperture 中检索。查找成功时函数持有该 aperture 的fmm_mutex返回因此后续字段填充均在锁保护下进行。定位到 VM 对象后实现按固定优先级判定Type首先判断是否为导入的 KFD BOREGISTERED_SHARED其次判断是否携带图形元数据REGISTERED_GRAPHICS再判断是否为 userptrREGISTERED_USER随后判断是否为无后端句柄的纯地址预留RESERVED_ADDR以上均不成立时归为ALLOCATED。这一优先级顺序确保具备多重属性的对象被归入语义最具体的类别。类型确定后实现回填Node、GPUAddress、SizeInBytes等基础字段并处理注册与映射节点数组。节点数组采用惰性构建仅当对应节点数非零且缓存数组尚未建立时才分配数组并通过 gpuid 到 nodeid 的转换逐项填充构建完成的数组由 VM 对象持有并缓存其释放时机与注册、映射状态的变更绑定。若数组分配失败实现会先释放 aperture 锁再返回内存不足错误避免锁泄漏。不同类型对地址字段的处理存在差异对REGISTERED_USERCPUAddress取自 userptr、SizeInBytes取自 userptr 的实际长度并将 GPU 地址按用户指针的页内偏移做相应校正对ALLOCATEDCPUAddress直接取对象起始地址。UserData与MemFlags则统一从 VM 对象取回。完成全部填充后释放 aperture 锁并返回成功。错误路径同样明确当vm_find_object未能定位到对象时实现将Type置为HSA_POINTER_UNKNOWN并返回错误状态。调用方据此即可区分地址无效与地址有效但类型特殊两类情形。上述查询流程可归纳如下否是, 持有 aperture-fmm_mutex是否是否是否是否是否REGISTERED_USERALLOCATED其他hsakmt_fmm_get_mem_info(address, info)memset 清零 infovm_find_object(address, UINT64_MAX)依次检索 GPUVM / mem_handle / SVM / userptr / CPUVM命中 VM 对象?info.Type HSA_POINTER_UNKNOWN返回 HSAKMT_STATUS_ERROR按优先级判定 Typeis_imported_kfd_bo?REGISTERED_SHAREDmetadata?REGISTERED_GRAPHICSuserptr?REGISTERED_USERhandles[0] 0?RESERVED_ADDRALLOCATED回填 Node / GPUAddress / SizeInBytes / MemFlags / UserData惰性构建 RegisteredNodes / MappedNodesgpuid → nodeid 转换数组 malloc 失败?解锁 fmm_mutex返回 HSAKMT_STATUS_NO_MEMORYType?CPUAddress userptrSizeInBytes userptr_sizeGPUAddress 页内偏移CPUAddress 对象起始地址保持已填字段解锁 fmm_mutex, 返回 SUCCESS6. 写入实现hsakmt_fmm_set_mem_user_data用户数据写入复用同一套定位机制。hsakmt_fmm_set_mem_user_data以待关联地址调用vm_find_object取得 VM 对象将传入指针写入对象的user_data字段随后释放 aperture 锁返回。若地址无法定位则直接返回错误。写入侧的user_data与查询侧info-UserData vm_obj-user_data构成一读一写的闭环上层先通过hsaKmtSetMemoryUserData把自身的分配句柄绑定到内存对象之后在任意持有该地址的位置调用hsaKmtQueryPointerInfo即可从UserData取回该句柄完成从地址到上层对象的反向解析。ROCr 运行时正是借此把 thunk 层内存对象与运行时内部的分配记录关联起来。7. 消费者视角ROCr 运行时的Runtime::PtrInfo是该接口的主要消费者。它在优先尝试自身的VMemoryPtrInfo之后对 SVM 分配与RESERVED_ADDR等特殊场景进一步调用hsaKmtQueryPointerInfo补全信息。运行时对返回状态采取宽容策略即便查询失败也仅将指针类型标记为未知而不中断整体流程从而保证hsa_amd_pointer_info对任意输入地址都能给出确定结果。在分配路径上运行时则通过hsaKmtSetMemoryUserData将 userptr 等上层信息绑定到内存对象为后续查询预置数据。在测试层面kfdtest 的KFDTestUtil.cpp直接调用hsaKmtQueryPointerInfo校验分配结果的类型与拓扑属性是理解该接口预期行为的直接参考。8. 对 SVM 地址的适用性SVM 地址在 libhsakmt 中对应两种截然不同的来源二者能否被反查到的结论相反使用时务必区分。第一种是经hsaKmtAllocMemory在 dGPU 上分配、落入 fmm 的 SVM aperturedgpu_aperture/dgpu_alt_aperture的缓冲可以查到。这类对象在分配时即建立vm_object_t并挂入 aperture 红黑树vm_find_object能够命中随后按第 5 节的判定返回HSA_POINTER_ALLOCATED若仅保留了虚拟地址而尚未落实后端句柄则返回HSA_POINTER_RESERVED_ADDR。Node、GPUAddress、SizeInBytes等字段均完整可用。第二种是 KFD 统一 SVM 所管理的任意进程 CPU 虚拟地址——即通过hsaKmtSVMSetAttr/AMDKFD_IOC_SVM赋予 GPU 可访问属性、但由内核svm_range跟踪的内存例如 malloc、mmap 得到的地址查不到。这类地址从未经过 fmm 的分配路径进程内不存在对应的vm_object_tvm_find_object必然落空接口返回HSA_POINTER_UNKNOWN与HSAKMT_STATUS_ERROR。这一差异并非实现缺陷而是设计使然源码中亦有旁证。svm.c中的hsaKmtSVMSetAttr在需要确定目标设备句柄时会尝试性地调用hsaKmtQueryPointerInfoCtx但它将查询失败或Node INVALID_NODEID视为正常分支随即回退到默认设备句柄hsakmt_fmm_get_default_amdgpu_device_handle。可见库自身并不假定 SVM 地址一定能被反查到。同理ROCr 的Runtime::PtrInfo对 SVM 场景以自身的VMemoryPtrInfo为主仅将本接口作为补充信息来源并容忍UNKNOWN结果。因此在工程实践中应牢记hsaKmtQueryPointerInfo反查的对象是 Thunk 用户态维护的vm_object_t元数据而非内核svm_range意义上的统一地址空间。对后者应通过 SVM 属性查询路径hsaKmtSVMGetAttr而非本接口获取信息。9. 并发、有效期与使用注意使用该组接口时需注意以下约束加锁语义查询与写入在定位成功后均持有 aperture 的fmm_mutex并在函数返回前释放。字段填充全程处于锁保护之下调用方无需自行加锁但也不应假设返回后的数组内容仍受锁保护。数组有效期RegisteredNodes与MappedNodes指向库内部缓存其生命周期与注册、映射状态及内存存续绑定。调用方应在查询后立即取用避免跨越可能触发状态变更的操作长期持有。未知类型的含义Type HSA_POINTER_UNKNOWN表示地址未落入任何受管 aperture或对象已被释放。这是接口对无效地址的正常反馈而非异常。上下文边界查询与写入均针对特定上下文的地址空间。在多上下文体系下应确保查询所用上下文与地址所属上下文一致否则将返回未知类型。需要特别说明的是与第 4 章其余接口如hsaKmtAllocMemory经AMDKFD_IOC_ALLOC_MEMORY_OF_GPU下发到 KFD不同hsaKmtQueryPointerInfo与hsaKmtSetMemoryUserData的实现全程不涉及任何 ioctl属于纯用户态操作。二者读写的HsaPointerInfo与user_data均来自 Thunk 在此前分配、注册、映射阶段已建立并缓存于vm_object_t中的元数据vm_find_object也仅在进程内的 aperture 红黑树中检索。因此该接口没有对应的内核侧实现其能够返回的全部信息本质上是 KFD 在更早的分配与映射 ioctl 中提供、并由 libhsakmt 在用户态维护的账本快照。10. 小结hsaKmtQueryPointerInfo与hsaKmtSetMemoryUserData为 libhsakmt 提供了地址到元数据的反查能力前者依托vm_find_object的多 aperture 定位与类型判定将裸指针还原为包含类型、节点、尺寸与拓扑的完整描述后者则以user_data字段建立上层句柄与内存对象的关联使反查得以延伸至运行时自身的数据结构。二者共同闭合了第 4 章内存生命周期中正向创建与逆向解析的两个方向构成 ROCr 运行时实现指针信息查询语义的底层基础。至此第 4 章对内存操作全生命周期的剖析告一段落。