AMD KFD的BO设计分析系列6-1: VRAM BO的显存分配分析

前文:AMD KFD的BO设计分析系列6: BO的物理地址部分-PM概述了BO物理空间的核心结构体间的关系,根据分配位置的不同,使用不同的管理器来实现物理空间的分配。本文介绍的AMD 的 VRAM 管理器(amdgpu_vram_mgr)负责为BO分配具体的设备内存(VRAM),重点分析TTM 框架下 ttm_resource 物理地址分配的代码实现,重点关注 VRAM 分配流程和物理地址的确定。

1. TTM资源分配的入口

在 TTM 框架中,ttm_resource 代表 BO实际分配到的物理内存资源。对于 VRAM 类型的 BO,分配流程由 VRAM 管理器实现,核心入口是:

cpp 复制代码
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。

cpp 复制代码
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_resourcestart 字段通常记录分配的首个页帧号(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)分配物理地址区间。下面详细分析其实现原理和流程:

cpp 复制代码
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 参数校验

函数首先对参数进行严格校验,确保分配范围、对齐、大小等合法:

  • sizemin_block_size 必须大于等于 chunk_size(最小分配单位)。

  • min_block_size 必须是 2 的幂。

  • startendsize 必须按 chunk_size 对齐。

  • end 不能超过管理器的总空间。

  • 检查分配区间不会溢出。

3.2 分配策略选择

根据参数和 flags,选择不同的分配策略:

  1. 严格区间分配(RANGE)

    如果 start + size == end,表示分配必须完全落在指定区间,调用 __drm_buddy_alloc_range,遍历树结构分配。

  2. 连续分配(CONTIGUOUS)

    如果要求分配连续物理块,则将 size 向上取整为 2 的幂,并将 min_block_size 设为 size。

  3. 普通分配

    如果没有特殊要求,则按最小块对齐分配。

3.3 分配主流程

  1. **计算分配 order:**计算分配块的 order(即块大小的 log2),用于 buddy 算法分配合适大小的块。

  2. 循环分配: 按需分配多个 block,直到满足 size。每次分配调用 __drm_buddy_alloc_blocks,根据策略选择区间分配或从 freelist 分配。如果实际分配的块大于请求 size(如连续分配时),会调用 drm_buddy_block_trim 修剪多余部分,释放未使用的空间。

  3. **分配失败处理:**如果分配失败,尝试合并空闲块(force merge),或采用"try harder"策略分配更大的块。如果仍然失败,则释放已分配的块并返回错误。

  4. 分配成功处理: 标记分配的 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 指针间接访问物理地址。


如有帮助,请三连:点赞、收藏、加关注。

相关推荐
HLC++10 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室12 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy14 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕15 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫16 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生16 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1118 小时前
linux基础IO
linux·运维·服务器
BelongPanda19 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯20 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_7779983420 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器