05 - AMDGPU中的VRAM管理器

难度 : 🟡 进阶级
预计学习时间 : 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
#   ...

💡 重点提示

  1. VRAM Manager是桥梁: 连接TTM框架和Buddy算法,负责协调和策略。

  2. Buddy是引擎: 实际的分配算法由Buddy完成,VRAM Manager只是调用者。

  3. 互斥锁是关键: 所有Buddy操作必须在锁保护下,确保线程安全。

  4. 统计信息重要: Visible VRAM使用量、分配追踪等对调试和优化很有帮助。

  5. sysfs vs debugfs: sysfs提供基本统计,debugfs提供详细调试信息。

  6. 资源追踪: 记录每个分配的PID和进程名,便于追踪内存泄漏。


⚠️ 常见陷阱

陷阱1: "直接调用drm_buddy API"

  • ✅ 正确: 应该通过TTM接口,让VRAM Manager协调分配。

陷阱2: "忘记更新vis_usage"

  • ✅ 正确: 分配/释放Visible区域的块时必须更新vis_usage统计。

陷阱3: "在锁外操作Buddy"

  • ✅ 正确: 所有drm_buddy_*调用都必须在mgr->lock保护下。

陷阱4: "混淆总VRAM和Visible VRAM"

  • ✅ 正确: 它们是不同的区域,统计和限制都不同。

📝 实践练习

  1. 查看实际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"
    
    # 计算使用率
  2. 分析分配追踪

    bash 复制代码
    # 查看哪些进程分配了VRAM
    cat /sys/kernel/debug/dri/0/amdgpu_vram_mm
    
    # 找出最大的分配
    # 分析是否有内存泄漏
  3. 模拟分配场景

    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分配算法]审核中...


相关推荐
DeeplyMind2 天前
AMDGPU SVM Range Restore 机制
amdgpu·drm_gpusvm
DeeplyMind4 天前
04 - 核心数据结构详解
drm·drm_buddy·vram
DeeplyMind6 天前
03 - DRM子系统与AMDGPU架构
drm·drm_buddy·vram分配
DeeplyMind2 个月前
11 - SVM的高级特性:多GPU支持
svm·amdgpu·rocm·kfd
DeeplyMind2 个月前
09 - SVM缺页处理机制
svm·amdgpu·rocm·kfd·rocr
DeeplyMind2 个月前
07 - SVM内存迁移机制
svm·amdgpu·rocm·kfd·rocr
DeeplyMind2 个月前
06 - SVM范围管理
svm·amdgpu·rocm·kfd
DeeplyMind2 个月前
05 - 进程与SVM的关系
svm·amdgpu·rocm·kfd
DeeplyMind2 个月前
03 - AMDGPU驱动架构概览
svm·amdgpu·rocm·kfd