难度 : 🟡 进阶级
预计学习时间 : 60分钟
前置知识 : 04-drm_buddy核心数据结构详解
📋 概述
AMDGPU VRAM Manager是Buddy分配器和TTM框架之间的桥梁:
- 🔗 集成层: 将Buddy嵌入到TTM资源管理框架
- 📊 统计层: 追踪VRAM使用情况和分配统计
- 🎯 策略层: 处理对齐、范围、连续性等分配策略
- 🔐 并发控制: 使用互斥锁保护Buddy操作
本章深入分析VRAM Manager的实现,理解它如何协调各个组件。
5.1 struct amdgpu_vram_mgr 结构
完整定义
c
// drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
struct amdgpu_vram_mgr {
struct ttm_resource_manager manager; // 继承TTM资源管理器
struct drm_buddy mm; // ← Buddy分配器实例
struct mutex lock; // 保护并发访问
u64 default_page_size; // 默认页大小
atomic64_t vis_usage; // Visible VRAM使用量
struct list_head reservations_pending; // 待处理的预留
struct list_head reserved_pages; // 已预留的页面
struct list_head allocated_vres_list; // 已分配资源列表
};
// VRAM资源结构
struct amdgpu_vram_mgr_resource {
struct ttm_resource base; // TTM资源基类
struct list_head blocks; // Buddy分配的块链表
unsigned long flags; // 分配标志
struct {
pid_t pid; // 分配进程PID
char comm[TASK_COMM_LEN]; // 进程名
} task; // 用于调试追踪
struct list_head vres_node; // 链入allocated_vres_list
};
字段详解
1. manager - TTM资源管理器接口
c
struct ttm_resource_manager manager;
// 初始化时设置回调函数
mgr->manager.func = &amdgpu_vram_mgr_func;
// TTM通过这个接口调用VRAM Manager
static const struct ttm_resource_manager_func amdgpu_vram_mgr_func = {
.alloc = amdgpu_vram_mgr_new, // 分配接口
.free = amdgpu_vram_mgr_del, // 释放接口
.debug = amdgpu_vram_mgr_debug, // 调试接口
};
2. mm - Buddy分配器实例
c
struct drm_buddy mm;
// 这是核心!VRAM Manager使用mm管理所有VRAM
// 所有分配最终调用: drm_buddy_alloc_blocks(&mgr->mm, ...)
// 所有释放最终调用: drm_buddy_free_list(&mgr->mm, ...)
3. lock - 并发保护
c
struct mutex lock;
// 为什么需要锁?
// - Buddy数据结构不是线程安全的
// - 多个CPU核心可能同时分配/释放VRAM
// - 需要保证操作的原子性
// 使用模式
mutex_lock(&mgr->lock);
drm_buddy_alloc_blocks(&mgr->mm, ...); // 操作Buddy
mutex_unlock(&mgr->lock);
4. vis_usage - Visible VRAM统计
c
atomic64_t vis_usage;
// 追踪Visible VRAM的使用量
// Visible VRAM是CPU可直接访问的区域,通常很小
// 更新使用量
if (block_in_visible_range(block)) {
atomic64_add(size, &mgr->vis_usage);
}
// 查询使用量
u64 amdgpu_vram_mgr_vis_usage(struct amdgpu_vram_mgr *mgr)
{
return atomic64_read(&mgr->vis_usage);
}
5.2 与DRM Buddy的集成
初始化流程
c
// drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
{
struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
struct ttm_resource_manager *man = &mgr->manager;
// 1. 初始化互斥锁
mutex_init(&mgr->lock);
// 2. 初始化链表
INIT_LIST_HEAD(&mgr->reservations_pending);
INIT_LIST_HEAD(&mgr->reserved_pages);
INIT_LIST_HEAD(&mgr->allocated_vres_list);
// 3. 获取VRAM大小
u64 vram_size = adev->gmc.real_vram_size;
u64 chunk_size = PAGE_SIZE; // 4KB
// 4. 初始化Buddy分配器 ← 关键步骤
drm_buddy_init(&mgr->mm, vram_size, chunk_size);
// 5. 设置默认页大小
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
mgr->default_page_size = HPAGE_PMD_SIZE; // 2MB
#else
mgr->default_page_size = 2UL << 20; // 2MB
#endif
// 6. 初始化TTM资源管理器
ttm_resource_manager_init(man, &adev->mman.bdev, vram_size);
man->func = &amdgpu_vram_mgr_func;
// 7. 注册到TTM
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, man);
ttm_resource_manager_set_used(man, true);
return 0;
}
初始化后的状态:
amdgpu_device
└── mman (memory manager)
└── vram_mgr
├── manager (TTM接口)
│ └── func = amdgpu_vram_mgr_func
│
├── mm (Buddy实例)
│ ├── free_list[0..max_order]
│ ├── roots[]
│ ├── size = 8GB
│ └── chunk_size = 4KB
│
├── lock (互斥锁)
└── allocated_vres_list (空)
清理流程
c
void amdgpu_vram_mgr_fini(struct amdgpu_device *adev)
{
struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
struct ttm_resource_manager *man = &mgr->manager;
// 1. 从TTM注销
ttm_resource_manager_set_used(man, false);
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, NULL);
// 2. 清理Buddy分配器
drm_buddy_fini(&mgr->mm);
// 3. 清理TTM资源管理器
ttm_resource_manager_cleanup(man);
// 4. 销毁互斥锁
mutex_destroy(&mgr->lock);
}
5.3 分配统计和跟踪
资源分配
c
// TTM调用的分配接口
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)
{
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_bo *bo = ttm_to_amdgpu_bo(tbo);
struct amdgpu_vram_mgr_resource *vres;
u64 size = tbo->base.size;
u64 min_block_size;
unsigned long pages_per_block;
// 1. 创建资源对象
vres = kzalloc(sizeof(*vres), GFP_KERNEL);
// 2. 初始化资源
ttm_resource_init(tbo, place, &vres->base);
INIT_LIST_HEAD(&vres->blocks);
// 3. 设置分配标志
vres->flags = 0;
if (place->flags & TTM_PL_FLAG_TOPDOWN)
vres->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION;
if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS)
vres->flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION;
if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CLEARED)
vres->flags |= DRM_BUDDY_CLEAR_ALLOCATION;
// 4. 计算页大小和对齐
if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS) {
pages_per_block = ~0ul; // 无限大,强制连续
} else {
pages_per_block = HPAGE_PMD_NR; // 2MB huge page
pages_per_block = max_t(u32, pages_per_block, tbo->page_alignment);
}
// 5. 执行Buddy分配
mutex_lock(&mgr->lock);
u64 fpfn = place->fpfn << PAGE_SHIFT; // 起始地址
u64 lpfn = place->lpfn << PAGE_SHIFT; // 结束地址
if (tbo->page_alignment)
min_block_size = tbo->page_alignment << PAGE_SHIFT;
else
min_block_size = mgr->default_page_size;
// 调用Buddy分配
drm_buddy_alloc_blocks(&mgr->mm,
fpfn,
lpfn ? lpfn : mgr->mm.size,
size,
min_block_size,
&vres->blocks,
vres->flags);
// 6. 记录分配信息(用于调试)
vres->task.pid = task_pid_nr(current);
get_task_comm(vres->task.comm, current);
list_add_tail(&vres->vres_node, &mgr->allocated_vres_list);
mutex_unlock(&mgr->lock);
// 7. 计算起始PFN
vres->base.start = calculate_start_pfn(vres);
// 8. 更新Visible VRAM统计
update_vis_usage(mgr, vres, true);
*res = &vres->base;
return 0;
}
资源释放
c
static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man,
struct ttm_resource *res)
{
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_vram_mgr_resource *vres;
vres = to_amdgpu_vram_mgr_resource(res);
// 1. 更新Visible VRAM统计
update_vis_usage(mgr, vres, false);
mutex_lock(&mgr->lock);
// 2. 从分配列表移除
list_del(&vres->vres_node);
// 3. 释放所有Buddy块
drm_buddy_free_list(&mgr->mm, &vres->blocks, 0);
mutex_unlock(&mgr->lock);
// 4. 释放资源对象
kfree(vres);
}
Visible VRAM统计更新
c
// 辅助函数:更新Visible VRAM使用量
static void update_vis_usage(struct amdgpu_vram_mgr *mgr,
struct amdgpu_vram_mgr_resource *vres,
bool allocate)
{
struct amdgpu_device *adev = to_amdgpu_device(mgr);
struct drm_buddy_block *block;
u64 vis_size = adev->gmc.visible_vram_size;
u64 usage = 0;
// 遍历所有块,统计Visible区域的使用量
list_for_each_entry(block, &vres->blocks, link) {
u64 start = drm_buddy_block_offset(block);
u64 size = drm_buddy_block_size(&mgr->mm, block);
// 检查块是否在Visible区域
if (start < vis_size) {
// 计算在Visible区域内的部分
u64 end = start + size;
if (end > vis_size)
end = vis_size;
usage += end - start;
}
}
// 更新统计
if (allocate)
atomic64_add(usage, &mgr->vis_usage);
else
atomic64_sub(usage, &mgr->vis_usage);
}
5.4 VRAM使用率监控
Sysfs接口
AMDGPU通过sysfs暴露VRAM使用信息:
c
// Sysfs属性定义
// 总VRAM大小
static ssize_t amdgpu_mem_info_vram_total_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct amdgpu_device *adev = dev_get_drvdata(dev);
return sysfs_emit(buf, "%llu\n", adev->gmc.real_vram_size);
}
// 已使用VRAM
static ssize_t amdgpu_mem_info_vram_used_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct amdgpu_device *adev = dev_get_drvdata(dev);
struct ttm_resource_manager *man = &adev->mman.vram_mgr.manager;
return sysfs_emit(buf, "%llu\n", ttm_resource_manager_usage(man));
}
// Visible VRAM总大小
static ssize_t amdgpu_mem_info_vis_vram_total_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct amdgpu_device *adev = dev_get_drvdata(dev);
return sysfs_emit(buf, "%llu\n", adev->gmc.visible_vram_size);
}
// 已使用Visible VRAM
static ssize_t amdgpu_mem_info_vis_vram_used_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct amdgpu_device *adev = dev_get_drvdata(dev);
return sysfs_emit(buf, "%llu\n",
amdgpu_vram_mgr_vis_usage(&adev->mman.vram_mgr));
}
// 注册sysfs属性
static DEVICE_ATTR(mem_info_vram_total, S_IRUGO,
amdgpu_mem_info_vram_total_show, NULL);
static DEVICE_ATTR(mem_info_vram_used, S_IRUGO,
amdgpu_mem_info_vram_used_show, NULL);
static DEVICE_ATTR(mem_info_vis_vram_total, S_IRUGO,
amdgpu_mem_info_vis_vram_total_show, NULL);
static DEVICE_ATTR(mem_info_vis_vram_used, S_IRUGO,
amdgpu_mem_info_vis_vram_used_show, NULL);
使用示例
bash
# 查看VRAM信息
cd /sys/class/drm/card0/device
# 总VRAM容量
cat mem_info_vram_total
# 输出: 8589934592 (8GB)
# 已使用VRAM
cat mem_info_vram_used
# 输出: 2147483648 (2GB)
# Visible VRAM容量
cat mem_info_vis_vram_total
# 输出: 268435456 (256MB)
# 已使用Visible VRAM
cat mem_info_vis_vram_used
# 输出: 134217728 (128MB)
Debugfs接口
更详细的调试信息通过debugfs提供:
c
// drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
static void amdgpu_vram_mgr_debug(struct ttm_resource_manager *man,
struct drm_printer *p)
{
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_device *adev = to_amdgpu_device(mgr);
struct drm_buddy *mm = &mgr->mm;
struct amdgpu_vram_mgr_resource *vres;
mutex_lock(&mgr->lock);
// 打印整体统计
drm_printf(p, "man size:%llu MiB\n", man->size >> 20);
drm_printf(p, "man usage:%llu MiB\n",
ttm_resource_manager_usage(man) >> 20);
drm_printf(p, "man available:%llu MiB\n",
(man->size - ttm_resource_manager_usage(man)) >> 20);
// 打印Buddy状态
drm_buddy_print(mm, p);
// 打印每个分配的详细信息
drm_printf(p, "\nAllocated resources:\n");
list_for_each_entry(vres, &mgr->allocated_vres_list, vres_node) {
struct drm_buddy_block *block;
drm_printf(p, " PID:%d COMM:%s SIZE:%lluMB\n",
vres->task.pid,
vres->task.comm,
vres->base.size >> 20);
// 打印块信息
list_for_each_entry(block, &vres->blocks, link) {
drm_printf(p, " Block: offset=0x%llx size=%lluKB order=%u\n",
drm_buddy_block_offset(block),
drm_buddy_block_size(mm, block) >> 10,
drm_buddy_block_order(block));
}
}
mutex_unlock(&mgr->lock);
}
Debugfs使用示例
bash
# 查看详细的VRAM管理器状态
cat /sys/kernel/debug/dri/0/amdgpu_vram_mm
# 输出示例:
# man size:8192 MiB
# man usage:2048 MiB
# man available:6144 MiB
#
# buddy_mm size:8589934592
# buddy_mm available:6442450944
#
# Allocated resources:
# PID:1234 COMM:glxgears SIZE:64MB
# Block: offset=0x10000000 size=65536KB order=14
# PID:1235 COMM:vkcube SIZE:128MB
# Block: offset=0x20000000 size=131072KB order=15
# ...
💡 重点提示
-
VRAM Manager是桥梁: 连接TTM框架和Buddy算法,负责协调和策略。
-
Buddy是引擎: 实际的分配算法由Buddy完成,VRAM Manager只是调用者。
-
互斥锁是关键: 所有Buddy操作必须在锁保护下,确保线程安全。
-
统计信息重要: Visible VRAM使用量、分配追踪等对调试和优化很有帮助。
-
sysfs vs debugfs: sysfs提供基本统计,debugfs提供详细调试信息。
-
资源追踪: 记录每个分配的PID和进程名,便于追踪内存泄漏。
⚠️ 常见陷阱
❌ 陷阱1: "直接调用drm_buddy API"
- ✅ 正确: 应该通过TTM接口,让VRAM Manager协调分配。
❌ 陷阱2: "忘记更新vis_usage"
- ✅ 正确: 分配/释放Visible区域的块时必须更新vis_usage统计。
❌ 陷阱3: "在锁外操作Buddy"
- ✅ 正确: 所有drm_buddy_*调用都必须在mgr->lock保护下。
❌ 陷阱4: "混淆总VRAM和Visible VRAM"
- ✅ 正确: 它们是不同的区域,统计和限制都不同。
📝 实践练习
-
查看实际VRAM使用:
bash# 在你的系统上查看VRAM信息 cd /sys/class/drm/card0/device echo "Total VRAM: $(cat mem_info_vram_total) bytes" echo "Used VRAM: $(cat mem_info_vram_used) bytes" echo "Visible VRAM: $(cat mem_info_vis_vram_total) bytes" echo "Used Visible: $(cat mem_info_vis_vram_used) bytes" # 计算使用率 -
分析分配追踪:
bash# 查看哪些进程分配了VRAM cat /sys/kernel/debug/dri/0/amdgpu_vram_mm # 找出最大的分配 # 分析是否有内存泄漏 -
模拟分配场景:
c// 设计一个分配场景 // 场景: 游戏启动 // 1. 分配 512MB 纹理 (可以不连续) // 2. 分配 8MB framebuffer (必须连续, Visible区域) // 3. 分配 256MB compute buffer (可以不连续) // 计算: // - 每个分配调用drm_buddy_alloc_blocks几次? // - flags参数应该是什么? // - min_block_size应该设置为多少? // - 是否需要指定fpfn/lpfn?
📚 本章小结
- amdgpu_vram_mgr: TTM和Buddy之间的集成层
- 核心职责: 策略控制、并发保护、统计追踪
- Buddy集成: 通过mgr->mm调用Buddy API
- 监控接口: sysfs提供基本信息,debugfs提供详细调试
- 资源追踪: 记录每个分配的来源,便于调试
VRAM Manager展示了如何将通用的Buddy算法集成到实际的驱动框架中,是理解驱动开发的好例子。
➡️ 下一步
理解了VRAM Manager的集成方式后,下一章将深入Buddy的核心算法------分配算法的实现细节。
👉 [06-Buddy分配算法]审核中...