章节概述
本章深入探讨AMDGPU驱动中VRAM分配的完整流程,从用户态应用程序到底层Buddy分配器的调用链。通过理解实际的驱动集成,可以看到前面章节学习的算法如何在真实环境中应用。
难度级别 : 🟡🔴 中高级
预计阅读时间 : 50分钟
前置知识: 核心算法篇、TTM框架基础
📋 本章学习目标
完成本章学习后,你将能够:
- ✅ 理解VRAM分配请求的来源和触发条件
- ✅ 掌握AMDGPU VRAM Manager的初始化流程
- ✅ 追踪从TTM到Buddy的完整调用链
- ✅ 分析Buffer Object创建时的内存分配
- ✅ 理解分配失败的处理机制
11.1 VRAM分配请求的来源
11.1.1 三大来源
在AMDGPU驱动中,VRAM分配请求主要来自三个方面:
c
/**
* 来源1: Buffer Object (BO) 创建
* - 用户态应用通过DRM ioctl创建GEM对象
* - Vulkan/OpenGL分配显存纹理和缓冲区
* - 最常见的分配路径
*/
int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp)
{
union drm_amdgpu_gem_create *args = data;
// 解析用户参数
// 调用amdgpu_gem_object_create()
}
/**
* 来源2: 内核内部分配
* - 驱动初始化时的固件缓冲区
* - Ring buffer、IB pool等
* - Page table和Page directory
*/
int amdgpu_bo_create_kernel(struct amdgpu_device *adev,
unsigned long size,
int align,
u32 domain,
struct amdgpu_bo **bo_ptr,
u64 *gpu_addr,
void **cpu_addr)
{
// 内核使用的BO,通常需要CPU映射
}
/**
* 来源3: Display相关分配
* - Framebuffer
* - Cursor buffer
* - Display plane
*/
// 通过DRM框架调用,最终到amdgpu_display_gem_fb_create()
11.1.2 分配标志
不同的应用场景需要不同的分配特性:
c
/**
* AMDGPU GEM创建标志
*/
#define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED (1 << 0) // CPU需要访问
#define AMDGPU_GEM_CREATE_NO_CPU_ACCESS (1 << 1) // CPU不访问
#define AMDGPU_GEM_CREATE_CPU_GTT_USWC (1 << 2) // GTT使用USWC
#define AMDGPU_GEM_CREATE_VRAM_CLEARED (1 << 3) // VRAM需要清零
#define AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS (1 << 9) // 连续VRAM
#define AMDGPU_GEM_CREATE_VM_ALWAYS_VALID (1 << 10) // VM始终有效
#define AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE (1 << 13) // 释放时擦除
/**
* TTM placement标志
*/
#define TTM_PL_FLAG_CONTIGUOUS (1 << 0) // 需要连续内存
#define TTM_PL_FLAG_TOPDOWN (1 << 1) // 从高地址分配
11.1.3 实际应用场景
c
/**
* 场景1: Vulkan纹理分配
* - 大小: 4K纹理可能需要32MB
* - 对齐: 通常4KB或更大
* - 特性: 不需要CPU访问,非连续OK
*/
flags = AMDGPU_GEM_CREATE_NO_CPU_ACCESS;
domain = AMDGPU_GEM_DOMAIN_VRAM;
/**
* 场景2: Display Framebuffer
* - 大小: 4K@60Hz需要约8MB
* - 对齐: 64KB或更大
* - 特性: 必须在Visible VRAM,可能需要连续
*/
flags = AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED |
AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS;
domain = AMDGPU_GEM_DOMAIN_VRAM;
/**
* 场景3: Shader缓冲区
* - 大小: 通常较小,KB到MB级别
* - 对齐: 256字节对齐
* - 特性: 频繁更新,可能CPU访问
*/
flags = AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED;
domain = AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT;
11.2 AMDGPU VRAM Manager初始化
11.2.1 初始化时机
VRAM Manager在驱动初始化的早期阶段创建:
c
/**
* AMDGPU驱动初始化流程
*/
amdgpu_driver_load_kms()
→ amdgpu_device_init()
→ amdgpu_ttm_init()
→ amdgpu_vram_mgr_init() // ← VRAM Manager初始化
/**
* 调用栈示例
*/
[1] amdgpu_driver_load_kms (amdgpu_kms.c)
[2] amdgpu_device_init (amdgpu_device.c)
[3] amdgpu_ttm_init (amdgpu_ttm.c)
[4] amdgpu_vram_mgr_init (amdgpu_vram_mgr.c)
11.2.2 初始化函数详解
c
/**
* amdgpu_vram_mgr_init - 初始化VRAM管理器
* @adev: amdgpu设备指针
*/
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. 注册DRM cgroup(资源控制)
man->cg = drmm_cgroup_register_region(adev_to_drm(adev),
"vram",
adev->gmc.real_vram_size);
// 2. 初始化TTM资源管理器
ttm_resource_manager_init(man, &adev->mman.bdev,
adev->gmc.real_vram_size);
// 3. 初始化VRAM Manager特有的数据结构
mutex_init(&mgr->lock);
INIT_LIST_HEAD(&mgr->reservations_pending);
INIT_LIST_HEAD(&mgr->reserved_pages);
INIT_LIST_HEAD(&mgr->allocated_vres_list);
mgr->default_page_size = PAGE_SIZE;
// 4. 设置操作函数表
man->func = &amdgpu_vram_mgr_func;
// 5. 初始化DRM Buddy分配器
err = drm_buddy_init(&mgr->mm, man->size, PAGE_SIZE);
// 6. 注册到TTM
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager);
ttm_resource_manager_set_used(man, true);
return 0;
}
11.2.3 关键数据结构
c
/**
* struct amdgpu_vram_mgr - AMDGPU VRAM管理器
*/
struct amdgpu_vram_mgr {
struct ttm_resource_manager manager; // TTM资源管理器基类
struct drm_buddy mm; // Buddy分配器实例
struct mutex lock; // 保护分配器的互斥锁
// 预留内存管理
struct list_head reservations_pending; // 待处理的预留
struct list_head reserved_pages; // 已预留的页面
// 分配跟踪
struct list_head allocated_vres_list; // 所有已分配资源列表
// 统计信息
atomic64_t vis_usage; // Visible VRAM使用量
u64 default_page_size; // 默认页面大小
};
/**
* struct amdgpu_vram_mgr_resource - 分配的VRAM资源
*/
struct amdgpu_vram_mgr_resource {
struct ttm_resource base; // TTM资源基类
struct list_head blocks; // Buddy分配的块链表
struct list_head vres_node; // 在allocated_vres_list中的节点
unsigned long flags; // 分配标志
// 任务跟踪(调试用)
struct {
pid_t pid;
char comm[TASK_COMM_LEN];
} task;
};
11.2.4 初始化后的状态
初始化完成后的结构:
amdgpu_device
└── mman (内存管理器)
└── vram_mgr (VRAM管理器)
├── manager (TTM资源管理器)
│ ├── size: 8GB (总VRAM大小)
│ ├── usage: 0 (初始未使用)
│ └── func: &amdgpu_vram_mgr_func
├── mm (DRM Buddy)
│ ├── size: 8GB
│ ├── chunk_size: 4KB (PAGE_SIZE)
│ ├── max_order: 21 (8GB/4KB = 2^21)
│ ├── avail: 8GB (全部可用)
│ └── free_list[0..21]: 初始化的空闲列表
├── lock: mutex (未锁定)
├── reservations_pending: 空列表
├── reserved_pages: 空列表
├── allocated_vres_list: 空列表
├── vis_usage: 0
└── default_page_size: 4KB
此时Buddy分配器已准备好处理分配请求
11.3 从TTM到Buddy的调用链
11.3.1 完整调用链
c
/**
* 用户态GEM对象创建的完整流程
*/
// 1. 用户态ioctl入口
DRM_IOCTL_AMDGPU_GEM_CREATE
↓
amdgpu_gem_create_ioctl() // amdgpu_gem.c
↓
amdgpu_gem_object_create()
↓
// 2. BO创建
amdgpu_bo_create() // amdgpu_object.c
↓
amdgpu_bo_create_restricted()
↓
amdgpu_bo_do_create()
↓
// 3. TTM层
ttm_bo_init_reserved() // ttm_bo.c (TTM核心)
↓
ttm_bo_validate()
↓
ttm_bo_mem_space()
↓
ttm_resource_manager_first_pass()
↓
// 4. 驱动特定的资源管理器
man->func->alloc()
↓
amdgpu_vram_mgr_new() // amdgpu_vram_mgr.c
↓
// 5. Buddy分配器
drm_buddy_alloc_blocks() // drm_buddy.c
↓
alloc_from_freelist()
↓
split_block() / mark_allocated()
11.3.2 关键接口函数
c
/**
* amdgpu_gem_object_create - 创建GEM对象
*/
int amdgpu_gem_object_create(struct amdgpu_device *adev,
unsigned long size,
int alignment,
u32 initial_domain,
u64 flags,
struct amdgpu_bo **bo_ptr)
{
struct amdgpu_bo_param bp;
// 准备BO参数
memset(&bp, 0, sizeof(bp));
bp.size = size;
bp.byte_align = alignment;
bp.domain = initial_domain;
bp.flags = flags;
bp.type = ttm_bo_type_device;
bp.resv = NULL;
// 创建BO
return amdgpu_bo_create(adev, &bp, bo_ptr);
}
/**
* amdgpu_bo_create - 创建Buffer Object
*/
int amdgpu_bo_create(struct amdgpu_device *adev,
struct amdgpu_bo_param *bp,
struct amdgpu_bo **bo_ptr)
{
// 选择内存域
if (bp->domain & AMDGPU_GEM_DOMAIN_VRAM)
bp->domain = AMDGPU_GEM_DOMAIN_VRAM;
else if (bp->domain & AMDGPU_GEM_DOMAIN_GTT)
bp->domain = AMDGPU_GEM_DOMAIN_GTT;
// 调用实际创建函数
return amdgpu_bo_create_restricted(adev, bp->size,
bp->byte_align,
bp->domain,
bp->flags,
...);
}
11.4 BO创建时的内存分配
11.4.1 amdgpu_vram_mgr_new函数详解
这是连接TTM和Buddy分配器的关键函数:
c
/**
* amdgpu_vram_mgr_new - 分配VRAM资源
* @man: TTM资源管理器
* @tbo: TTM buffer object
* @place: placement描述(内存域、标志等)
* @res: 输出分配的资源
*/
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_device *adev = to_amdgpu_device(mgr);
struct amdgpu_bo *bo = ttm_to_amdgpu_bo(tbo);
struct amdgpu_vram_mgr_resource *vres;
struct drm_buddy *mm = &mgr->mm;
u64 size, remaining_size, lpfn, fpfn;
u64 vis_usage = 0, max_bytes, min_block_size;
unsigned long pages_per_block;
// 1. 解析范围限制(fpfn/lpfn)
lpfn = (u64)place->lpfn << PAGE_SHIFT;
if (!lpfn || lpfn > man->size)
lpfn = man->size;
fpfn = (u64)place->fpfn << PAGE_SHIFT;
// 2. 检查可用空间
max_bytes = adev->gmc.mc_vram_size;
if (tbo->type != ttm_bo_type_kernel)
max_bytes -= AMDGPU_VM_RESERVED_VRAM;
if (ttm_resource_manager_usage(man) > max_bytes)
return -ENOSPC;
// 3. 计算对齐要求
if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS) {
pages_per_block = ~0ul; // 连续分配
} else {
pages_per_block = HPAGE_PMD_NR; // 2MB大页
pages_per_block = max_t(u32, pages_per_block,
tbo->page_alignment);
}
// 4. 分配vres结构
vres = kzalloc(sizeof(*vres), GFP_KERNEL);
ttm_resource_init(tbo, place, &vres->base);
INIT_LIST_HEAD(&vres->blocks);
// 5. 设置Buddy分配标志
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;
if (fpfn || lpfn != mgr->mm.size)
vres->flags |= DRM_BUDDY_RANGE_ALLOCATION;
// 6. 分配循环(可能需要多次分配)
remaining_size = (u64)vres->base.size;
mutex_lock(&mgr->lock);
while (remaining_size) {
// 计算min_block_size(对齐)
if (tbo->page_alignment)
min_block_size = (u64)tbo->page_alignment << PAGE_SHIFT;
else
min_block_size = mgr->default_page_size;
size = remaining_size;
// 调整为2MB对齐(如果可能)
if ((size >= (u64)pages_per_block << PAGE_SHIFT) &&
!(size & (((u64)pages_per_block << PAGE_SHIFT) - 1)))
min_block_size = (u64)pages_per_block << PAGE_SHIFT;
// 调用Buddy分配器
r = drm_buddy_alloc_blocks(mm, fpfn, lpfn,
size, min_block_size,
&vres->blocks,
vres->flags);
if (unlikely(r))
goto error_free_blocks;
if (size > remaining_size)
remaining_size = 0;
else
remaining_size -= size;
}
// 7. 记录分配任务信息
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);
// 8. 计算起始地址和可见VRAM使用
vres->base.start = 0;
list_for_each_entry(block, &vres->blocks, link) {
unsigned long start;
start = amdgpu_vram_mgr_block_start(block) +
amdgpu_vram_mgr_block_size(block);
start >>= PAGE_SHIFT;
vres->base.start = max(vres->base.start, start);
vis_usage += amdgpu_vram_mgr_vis_size(adev, block);
}
// 9. 检查是否连续
if (amdgpu_is_vram_mgr_blocks_contiguous(&vres->blocks))
vres->base.placement |= TTM_PL_FLAG_CONTIGUOUS;
// 10. 更新统计
atomic64_add(vis_usage, &mgr->vis_usage);
*res = &vres->base;
return 0;
error_free_blocks:
drm_buddy_free_list(mm, &vres->blocks, 0);
mutex_unlock(&mgr->lock);
ttm_resource_fini(man, &vres->base);
kfree(vres);
return r;
}
11.4.2 分配流程可视化
输入参数:
- size: 16MB
- alignment: 2MB
- domain: VRAM
- flags: TOPDOWN
步骤1: 解析参数
fpfn = 0, lpfn = 8GB (全范围)
pages_per_block = 512 (2MB / 4KB)
步骤2: 设置Buddy标志
vres->flags = DRM_BUDDY_TOPDOWN_ALLOCATION
步骤3: 分配循环
remaining_size = 16MB
循环1:
size = 16MB
min_block_size = 2MB (对齐)
调用 drm_buddy_alloc_blocks(mm, 0, 8GB, 16MB, 2MB, ...)
Buddy分配器可能返回:
- [8MB @ offset 1GB]
- [8MB @ offset 1GB+8MB]
或者:
- [16MB @ offset 2GB] (如果有连续的)
remaining_size = 0 (分配完成)
步骤4: 构建vres
vres->blocks: [Block1] → [Block2] → NULL
vres->base.start: 计算最高地址
vres->task.pid: 当前进程PID
vres->task.comm: 进程名称
步骤5: 返回
*res = &vres->base
TTM现在可以使用这些块
11.4.3 多块分配示例
c
/**
* 场景: 分配3MB,2MB对齐
* VRAM当前没有3MB的连续块
*/
// 输入
size = 3MB = 3145728 bytes
min_block_size = 2MB = 2097152 bytes
flags = 0 (非连续OK)
// Buddy分配器执行
remaining_size = 3MB
// 第一次循环
size = 3MB
计算order: 需要能容纳3MB且对齐2MB
→ 分配2MB块
remaining_size = 3MB - 2MB = 1MB
// 第二次循环
size = 1MB
min_block_size仍然是2MB(对齐要求)
→ 分配2MB块(但只使用1MB)
remaining_size = 0
// 结果
vres->blocks:
[Block1: 2MB @ offset X]
[Block2: 2MB @ offset Y] (只使用1MB)
总分配: 4MB (有1MB内部碎片)
实际使用: 3MB
11.5 分配失败处理
11.5.1 失败的原因
c
/**
* VRAM分配可能失败的原因
*/
// 原因1: 空间不足
if (ttm_resource_manager_usage(man) > max_bytes)
return -ENOSPC;
// 原因2: 无法找到符合条件的块
r = drm_buddy_alloc_blocks(...);
if (r == -ENOSPC) {
// 没有足够大的空闲块
// 或者范围限制内没有空间
}
// 原因3: 连续分配失败
if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS) {
// 需要连续块,但VRAM碎片化
return -ENOSPC;
}
// 原因4: Visible VRAM耗尽
if (need_visible_vram && vis_usage_too_high)
return -ENOMEM;
11.5.2 Fallback机制
c
/**
* amdgpu_vram_mgr_new中的fallback逻辑
*/
// 尝试1: 连续分配
if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS) {
pages_per_block = ~0ul; // 尝试单个大块
vres->flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION;
}
r = drm_buddy_alloc_blocks(...);
// Fallback: 如果连续分配失败,尝试非连续
if (unlikely(r == -ENOSPC) &&
pages_per_block == ~0ul &&
!(place->flags & TTM_PL_FLAG_CONTIGUOUS)) {
// 移除连续分配标志
vres->flags &= ~DRM_BUDDY_CONTIGUOUS_ALLOCATION;
// 使用2MB块
pages_per_block = max_t(u32, 2UL << (20UL - PAGE_SHIFT),
tbo->page_alignment);
continue; // 重试
}
11.5.3 TTM层的Fallback
c
/**
* TTM提供更高层的fallback机制
*/
// placement描述多个可选内存域
struct ttm_placement placement = {
.num_placement = 2,
.placement = places,
};
places[0].mem_type = TTM_PL_VRAM; // 优先VRAM
places[1].mem_type = TTM_PL_TT; // Fallback到GTT
// TTM会依次尝试
ttm_bo_mem_space() {
for (i = 0; i < placement->num_placement; i++) {
r = man->func->alloc(...); // 尝试VRAM
if (!r)
return 0;
}
// VRAM失败,尝试GTT
}
11.5.4 错误处理和清理
c
/**
* amdgpu_vram_mgr_new的错误路径
*/
error_free_blocks:
// 释放已分配的块
drm_buddy_free_list(mm, &vres->blocks, 0);
mutex_unlock(&mgr->lock);
error_fini:
// 清理TTM资源
ttm_resource_fini(man, &vres->base);
// 释放vres结构
kfree(vres);
return r;
/**
* 调用者的处理
*/
int amdgpu_bo_create(...) {
r = ttm_bo_init_reserved(...);
if (r) {
// 分配失败
if (r == -ENOSPC) {
dev_err(adev->dev, "VRAM allocation failed: out of space\n");
} else if (r == -ENOMEM) {
dev_err(adev->dev, "VRAM allocation failed: no memory\n");
}
// 尝试其他内存域或返回错误
return r;
}
}
11.5.5 实际应用中的处理
c
/**
* 用户态应用的错误处理
*/
// Vulkan示例
VkResult vkAllocateMemory(...) {
int ret = amdgpu_bo_create(...);
if (ret == -ENOSPC) {
// VRAM不足
// 1. 尝试回收不用的资源
// 2. 尝试使用GTT内存
// 3. 降低纹理质量
// 4. 返回VK_ERROR_OUT_OF_DEVICE_MEMORY
}
}
/**
* 驱动内部的处理
*/
int amdgpu_init_firmware_buffer(...) {
// 内核分配必须成功
ret = amdgpu_bo_create_kernel(...);
if (ret) {
dev_err(adev->dev, "Failed to allocate firmware buffer\n");
return ret; // 驱动初始化失败
}
}
11.6 释放流程
11.6.1 完整释放链
c
/**
* VRAM释放的调用链
*/
// 1. 用户态关闭GEM对象
close(gem_fd)
↓
drm_gem_object_release()
↓
// 2. BO引用计数归零
amdgpu_bo_unref()
↓
ttm_bo_put()
↓
ttm_bo_release()
↓
ttm_resource_free()
↓
// 3. 资源管理器释放
man->func->free()
↓
amdgpu_vram_mgr_del() // amdgpu_vram_mgr.c
↓
// 4. Buddy释放
drm_buddy_free_list() // drm_buddy.c
↓
drm_buddy_free_block()
↓
__drm_buddy_free()
11.6.2 amdgpu_vram_mgr_del函数
c
/**
* amdgpu_vram_mgr_del - 释放VRAM资源
* @man: TTM资源管理器
* @res: 要释放的资源
*/
static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man,
struct ttm_resource *res)
{
struct amdgpu_vram_mgr_resource *vres =
to_amdgpu_vram_mgr_resource(res);
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_device *adev = to_amdgpu_device(mgr);
struct drm_buddy *mm = &mgr->mm;
struct drm_buddy_block *block;
uint64_t vis_usage = 0;
mutex_lock(&mgr->lock);
// 1. 从分配列表中移除
list_del(&vres->vres_node);
memset(&vres->task, 0, sizeof(vres->task));
// 2. 计算释放的visible VRAM
list_for_each_entry(block, &vres->blocks, link)
vis_usage += amdgpu_vram_mgr_vis_size(adev, block);
// 3. 释放块到Buddy
if (vres->flags & DRM_BUDDY_CLEAR_ALLOCATION)
drm_buddy_free_list(mm, &vres->blocks, DRM_BUDDY_CLEARED);
else
drm_buddy_free_list(mm, &vres->blocks, 0);
mutex_unlock(&mgr->lock);
// 4. 更新统计
atomic64_sub(vis_usage, &mgr->vis_usage);
// 5. 清理TTM资源
ttm_resource_fini(man, res);
// 6. 释放vres结构
kfree(vres);
}
11.7 实战示例
11.7.1 追踪一次实际分配
bash
# 启用调试
echo 0x1ff > /sys/module/drm/parameters/debug
echo 1 > /sys/module/amdgpu/parameters/debug_mask
# 运行应用
vulkaninfo
# 查看分配信息
cat /sys/kernel/debug/dri/0/amdgpu_vram_mm
# 示例输出
VRAM mm
man size:8388608 pages, page size:4096 bytes
allocated size:2097152 pages
free size:6291456 pages
Order 0 (4KB): 1024 blocks
Order 1 (8KB): 512 blocks
Order 2 (16KB): 256 blocks
...
Order 11 (8MB): 8 blocks
Order 12 (16MB): 4 blocks
11.7.2 Debug打印分析
c
/**
* 添加调试打印
*/
static int amdgpu_vram_mgr_new(...) {
...
pr_info("VRAM alloc: pid=%d comm=%s size=%lluKB align=%lluKB flags=0x%lx\n",
current->pid, current->comm,
size >> 10, min_block_size >> 10, vres->flags);
r = drm_buddy_alloc_blocks(...);
if (!r) {
pr_info("VRAM alloc: success, blocks=%d\n",
count_blocks(&vres->blocks));
} else {
pr_err("VRAM alloc: failed, error=%d\n", r);
}
...
}
/**
* 示例输出
*/
[ 10.123] VRAM alloc: pid=1234 comm=vulkan size=16384KB align=2048KB flags=0x2
[ 10.125] VRAM alloc: success, blocks=2
[ 15.456] VRAM alloc: pid=1234 comm=vulkan size=8192KB align=256KB flags=0x0
[ 15.458] VRAM alloc: success, blocks=1
🎯 最佳实践
✅ 应该做的
-
合理设置分配标志
c// ✓ Display buffer需要CPU访问 flags = AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED; // ✓ Texture不需要CPU访问 flags = AMDGPU_GEM_CREATE_NO_CPU_ACCESS; -
使用TOPDOWN分配
c// ✓ 普通分配从高地址开始 places[0].flags = TTM_PL_FLAG_TOPDOWN; // 保留低地址(Visible VRAM)给Display使用 -
正确处理分配失败
c// ✓ 提供fallback路径 ret = amdgpu_bo_create(adev, &bp, &bo); if (ret == -ENOSPC) { // 尝试GTT内存 bp.domain = AMDGPU_GEM_DOMAIN_GTT; ret = amdgpu_bo_create(adev, &bp, &bo); }
❌ 不应该做的
-
不要过度使用连续分配
c// ❌ 不必要的连续分配 flags = AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS; // 增加碎片,降低成功率 // ✓ 只在必要时使用 if (hardware_requires_contiguous) flags = AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS; -
不要忽略分配失败
c// ❌ 忽略错误 amdgpu_bo_create(adev, &bp, &bo); // 继续使用bo → 可能崩溃 // ✓ 检查返回值 if (amdgpu_bo_create(adev, &bp, &bo)) { handle_error(); return -ENOMEM; }
💡 实践练习
练习1: 追踪分配流程
编写一个内核模块,追踪VRAM分配:
c
/**
* 追踪VRAM分配的探针
*/
static int trace_vram_alloc(struct amdgpu_device *adev,
u64 size, u64 alignment)
{
// TODO: 实现此函数
// 提示:
// 1. 记录分配前的空闲空间
// 2. 调用amdgpu_bo_create
// 3. 记录分配后的状态
// 4. 打印统计信息
}
练习2: 模拟分配失败
模拟VRAM不足的情况:
c
/**
* 测试分配失败处理
*/
void test_allocation_failure(struct amdgpu_device *adev)
{
// TODO: 实现此函数
// 1. 分配大量小块占用VRAM
// 2. 尝试分配大块(应该失败)
// 3. 释放一些小块
// 4. 重试大块分配
}
📚 本章总结
核心要点
-
分配来源:
- 用户态GEM对象创建
- 内核内部分配
- Display相关分配
-
调用链:
- ioctl → GEM → BO → TTM → VRAM Mgr → Buddy
- 每层有不同的职责和抽象
-
关键函数:
amdgpu_vram_mgr_init()- 初始化amdgpu_vram_mgr_new()- 分配amdgpu_vram_mgr_del()- 释放
-
错误处理:
- Fallback到GTT
- 连续→非连续降级
- 资源清理
关键数据结构
c
struct amdgpu_vram_mgr {
struct drm_buddy mm; // Buddy分配器
struct mutex lock; // 互斥锁
struct list_head allocated_vres_list; // 分配列表
atomic64_t vis_usage; // Visible VRAM使用
};
struct amdgpu_vram_mgr_resource {
struct list_head blocks; // Buddy块链表
unsigned long flags; // 分配标志
};
🔗 导航
- 上一章 : 10-空闲列表管理 - 空闲列表管理(第三部分)
- 下一章 : 12-连续与非连续内存分配 - 连续性处理
- 返回目录 : 00-DRM-Buddy-Allocator学习文档目录