AMD KFD的BO设计分析系列5-4:VM-amdgpu_vm_bo_map的完成详解
1. 设计背景与作用
在 AMDGPU 驱动的 GPU 虚拟内存(VM)管理体系中,amdgpu_vm_bo_map 是将一个显存对象(BO, Buffer Object)映射到某个虚拟地址空间(VM)的核心接口。它负责将 BO 的物理内存区域映射到指定的 GPU 虚拟地址,并设置访问属性(flags),从而实现用户进程或内核任务对显存的安全、灵活访问。
该接口是 GPU 显存虚拟化、资源隔离、页表管理的基础,广泛应用于图形渲染、异构计算、KFD 进程管理等场景。
2. 核心数据结构
2.1 amdgpu_vm
代表一个 GPU 虚拟地址空间,通常对应一个用户进程或 KFD 计算上下文。
管理所有映射的 BO 及其虚拟地址关系。
2.2 amdgpu_bo_va
代表某个 BO 在某个 VM 中的映射关系。
包含 BO 指针、VM 指针、映射状态链表等。
2.3 amdgpu_bo_va_mapping
代表一次具体的虚拟地址映射(即 BO 的一段物理内存映射到 VM 的某个虚拟地址区间)。
包含起始地址、结束地址、偏移、flags 等信息。
该结构体相当于CPU进程虚拟地址空间中的VMA。该类型对象会把加入的GPUVM中,代表这段GPU虚拟地址已经被分配。
3. amdgpu_vm_bo_map 实现流程
int amdgpu_vm_bo_map(
struct amdgpu_device *adev, // 指向 AMDGPU 设备对象,代表当前 GPU
struct amdgpu_bo_va *bo_va, // BO 在 VM 中的映射关系对象,包含 BO 和 VM 指针
uint64_t saddr, // GPU 虚拟地址空间中的起始地址(映射目标地址)
uint64_t offset, // BO 内部的偏移(从 BO 的哪个位置开始映射)
uint64_t size, // 映射的字节数(BO 映射到 VM 的长度)
uint64_t flags // 页属性标志(如读/写/有效/PRT等)
)
3.1 参数校验
校验参数合法性,包括地址对齐、offset、size 是否越界、虚拟地址空间是否足够等。
通过 amdgpu_vm_verify_parameters 实现,防止非法映射导致内存安全问题。
3.2 地址转换与冲突检测
将字节地址转换为页号(GPU 页大小)。
计算映射区间的起始页号和结束页号。
检查目标虚拟地址区间是否已被其他 BO 映射(通过 interval tree),防止地址冲突。
saddr /= AMDGPU_GPU_PAGE_SIZE;
eaddr = saddr + (size - 1) / AMDGPU_GPU_PAGE_SIZE;
tmp = amdgpu_vm_it_iter_first(&vm->va, saddr, eaddr);
if (tmp) {
// 地址冲突,返回错误
}
3.3 映射结构分配与初始化
分配一个新的 amdgpu_bo_va_mapping 结构体,填充起始页号、结束页号、offset、flags 等信息。
mapping = kmalloc(sizeof(*mapping), GFP_KERNEL);
mapping->start = saddr;
mapping->last = eaddr;
mapping->offset = offset;
mapping->flags = flags;
3.4 插入映射关系、状态迁移与同步
这是最关键的一步,通过这步各个结构体之间就建立了关联。这一步由amdgpu_vm_bo_insert_map实现。它是 AMDGPU 虚拟内存管理中用于插入新的 BO 映射关系的核心函数,主要功能是将一个显存对象(BO)的虚拟地址映射(mapping)插入到 VM(虚拟地址空间)和 BO 的管理结构中,并维护相关的状态和特性。
核心流程如下:
映射关系挂接
将新的mapping结构体与bo_va(BO 在 VM 中的映射对象)关联,并加入到bo_va->invalids链表,表示该映射尚未被页表更新。插入 interval tree
把mapping插入到 VM 的 interval tree(vm->va),便于后续查找、冲突检测和批量管理所有虚拟地址映射。PRT(Partial Resident Texture)处理
如果映射带有 PRT 标志,则增加 PRT 用户计数,驱动后续会根据用户数启用或禁用 PRT硬件特性。BO 状态迁移
如果该 BO 在 VM 中始终有效且未迁移,则将其状态迁移到moved链表,等待后续页表更新。
amdgpu_vm_bo_insert_map(adev, bo_va, mapping);
static void amdgpu_vm_bo_insert_map(struct amdgpu_device *adev,
struct amdgpu_bo_va *bo_va,
struct amdgpu_bo_va_mapping *mapping)
{
struct amdgpu_vm *vm = bo_va->base.vm;
struct amdgpu_bo *bo = bo_va->base.bo;
mapping->bo_va = bo_va;
list_add(&mapping->list, &bo_va->invalids);
amdgpu_vm_it_insert(mapping, &vm->va);
if (mapping->flags & AMDGPU_PTE_PRT_FLAG(adev))
amdgpu_vm_prt_get(adev);
if (amdgpu_vm_is_bo_always_valid(vm, bo) && !bo_va->base.moved)
amdgpu_vm_bo_moved(&bo_va->base);
trace_amdgpu_vm_bo_map(bo_va, mapping);
}
4. 与虚拟内存系统的协作机制
4.1 页表更新
新的 mapping 插入后,实际的页表项(PTE)尚未更新,需后续调用
amdgpu_vm_bo_update或amdgpu_vm_update_range完成页表写入。页表更新涉及同步机制(fence)、TLB 刷新等,确保 GPU 能正确访问新映射的显存。
4.2 状态链表管理
映射的生命周期由多个状态链表管理(invalids、valids、moved、idle、evicted等),
amdgpu_vm_bo_map负责将新映射挂载到 invalids 链表,后续状态迁移由页表更新和驱逐机制驱动。
4.3 资源隔离与安全
通过 interval tree 和参数校验,确保不同 BO 之间的虚拟地址空间不冲突,实现多进程资源隔离和安全访问。
5. 总结
amdgpu_vm_bo_map 是 AMDGPU 驱动虚拟内存管理的核心接口之一,负责将显存对象安全、高效地映射到 GPU 虚拟地址空间。其设计充分考虑了多进程隔离、资源安全、页表管理、状态迁移和硬件特性,是现代 GPU 驱动架构的基础。
对于从专栏首篇一路跟进的读者而言,想必已深度理解 GPU 物理地址与虚拟地址的对应关系。稍加梳理便不难发现:二者的映射原理,与 CPU 端虚拟地址到物理地址的转换逻辑,在核心设计思路上高度契合。玩不出什么花来。
如有帮助,请三连:点赞、收藏、加关注。
浙公网安备 33010602011771号