AMD KFD的BO设计分析系列6-1: VRAM BO的显存分配分析
以 AMD 的 VRAM 管理器(amdgpu_vram_mgr)为例,分析 TTM 框架下 ttm_resource
物理地址分配的代码实现,重点关注 VRAM 分配流程和物理地址的确定。
1. TTM资源分配的入口
在 TTM 框架中,ttm_resource
代表 BO实际分配到的物理内存资源。对于 VRAM 类型的 BO,分配流程由 VRAM 管理器实现,核心入口是:
static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,struct ttm_buffer_object *tbo,const struct ttm_place *place,struct ttm_resource **res);
该函数负责为 BO 分配 VRAM,并初始化 ttm_resource
结构体。AMD扩展的相关结构体如下,一个manager,一个resource。
struct amdgpu_vram_mgr {struct ttm_resource_manager manager; // TTM资源管理器,用于统一管理显存资源struct drm_buddy mm; // buddy分配器,负责VRAM物理地址空间的分配struct mutex lock; // 互斥锁,保护缓冲区对象的并发访问struct list_head reservations_pending; // 等待处理的预留请求链表struct list_head reserved_pages; // 已预留页的链表atomic64_t vis_usage; // 记录CPU可见VRAM的使用量u64 default_page_size; // 默认分配页大小
};struct amdgpu_vram_mgr_resource {struct ttm_resource base; // TTM资源结构体,描述分配到的物理资源struct list_head blocks; // 分配到的buddy块链表,每块代表一段物理地址区间unsigned long flags; // 资源属性标志(如已清零、分配策略等)
};
2. 分配流程详解
2.1 参数准备与资源结构体分配
-
获取 VRAM 管理器和设备指针。
-
分配
amdgpu_vram_mgr_resource
(继承自ttm_resource
),用于描述分配到的物理资源。 -
初始化资源结构体,设置分配大小、flags等。
2.2 分配物理地址区间
-
通过
drm_buddy_alloc_blocks
在 VRAM 地址空间分配物理块(block),每个 block 代表一段物理地址区间。 -
支持多种分配策略(如连续分配、TOPDOWN、DCC对齐等),根据 BO 的 flags 和 place 参数决定分配方式。
-
分配结果以 block 链表形式存储在
vres->blocks
。
2.3 物理地址的确定
-
每个 block 的起始物理地址通过
amdgpu_vram_mgr_block_start(block)
获取,大小通过amdgpu_vram_mgr_block_size(block)
获取。 -
分配完成后,遍历所有 block,确定资源的物理地址区间。
-
ttm_resource
的start
字段通常记录分配的首个页帧号(PFN),而实际物理地址由 block 的 start 字段决定。
2.4 资源结构体与物理地址绑定
-
ttm_resource
结构体通过amdgpu_vram_mgr_resource
关联到分配的 block 链表。 -
BO 通过
bo->tbo.resource
指向该资源结构体,从而间接获得物理地址信息。
可以看出,vram的分配最终是使用了drm框架的分配函数drm_buddy_alloc_blocks,这是一个在前面文章中没有涉及的概念。下面重点讲解。
3. drm_buddy物理显存分配器
drm_buddy是drm框架提供的物理显存分配器。drm_buddy_alloc_blocks
是drm 框架下用于分配显存等大块物理内存的核心分配器接口,采用 buddy 算法实现高效的内存管理。它广泛应用于 amdgpu_vram_mgr 等显存管理器,为 BO(Buffer Object)分配物理地址区间。下面详细分析其实现原理和流程:
int drm_buddy_alloc_blocks(struct drm_buddy *mm,u64 start, u64 end, u64 size,u64 min_block_size,struct list_head *blocks,unsigned long flags)
-
mm:buddy 管理器实例,管理整个物理地址空间。
-
start
/end
:允许分配的物理地址范围(通常由 fpfn/lpfn 约束)。 -
size
:需要分配的总字节数。 -
min_block_size
:分配块的最小对齐(通常为页或更大)。 -
blocks
:输出链表,存储分配到的 block。 -
flags
:分配策略(如连续分配、TOPDOWN、RANGE等)。
3.1 参数校验
函数首先对参数进行严格校验,确保分配范围、对齐、大小等合法:
-
size
和min_block_size
必须大于等于 chunk_size(最小分配单位)。 -
min_block_size
必须是 2 的幂。 -
start
、end
、size
必须按 chunk_size 对齐。 -
end
不能超过管理器的总空间。 -
检查分配区间不会溢出。
3.2 分配策略选择
根据参数和 flags,选择不同的分配策略:
-
严格区间分配(RANGE)
如果start + size == end
,表示分配必须完全落在指定区间,调用__drm_buddy_alloc_range
,遍历树结构分配。 -
连续分配(CONTIGUOUS)
如果要求分配连续物理块,则将 size 向上取整为 2 的幂,并将 min_block_size 设为 size。 -
普通分配
如果没有特殊要求,则按最小块对齐分配。
3.3 分配主流程
-
计算分配 order:计算分配块的 order(即块大小的 log2),用于 buddy 算法分配合适大小的块。
-
循环分配:按需分配多个 block,直到满足 size。每次分配调用
__drm_buddy_alloc_blocks
,根据策略选择区间分配或从 freelist 分配。如果实际分配的块大于请求 size(如连续分配时),会调用drm_buddy_block_trim
修剪多余部分,释放未使用的空间。 -
分配失败处理: 如果分配失败,尝试合并空闲块(force merge),或采用“try harder”策略分配更大的块。如果仍然失败,则释放已分配的块并返回错误。
-
分配成功处理:标记分配的 block 为已分配状态并更新管理器的可用空间。所有分配到的 block 以链表形式输出到
blocks
,每个 block 记录物理地址(offset)、大小(block_size)等信息。上层管理器(amdgpu_vram_mgr)会遍历这些 block,填充到 BO 的物理地址映射中。
3.4 算法核心特点
-
Buddy 算法:通过二叉树分割和合并空闲块,实现高效的内存分配和回收,减少碎片。
-
区间约束:支持严格的物理地址范围限制,满足显存分区、可见性等需求。
-
多策略支持:支持连续分配、TOPDOWN分配、普通分配等多种策略,灵活适应不同场景。
-
高效修剪:分配后可修剪多余空间,提升空间利用率。
4. 总结
-
TTM 的物理地址分配由资源管理器(如 amdgpu_vram_mgr)实现,核心是通过 buddy allocator 在 VRAM 地址空间分配物理块。
-
每个
ttm_resource
结构体通过 block 链表记录分配到的物理地址区间,BO 通过 resource 指针间接访问物理地址。
如有帮助,请三连:点赞、收藏、加关注。