Linux内核5.15下AMD显卡休眠唤醒:BO驱逐流程与显存管理3个关键阶段

引言

在Linux系统中,显卡的休眠唤醒机制一直是驱动开发者关注的焦点。特别是对于AMD显卡而言,其显存管理机制在休眠唤醒过程中扮演着至关重要的角色。本文将深入剖析Linux内核5.15版本下AMD显卡的休眠唤醒流程,重点聚焦Buffer Object(BO)的驱逐机制以及显存管理的三个关键阶段。

对于内核开发者和驱动工程师来说,理解这一流程不仅有助于解决实际开发中遇到的休眠唤醒问题,更能为性能优化提供理论依据。我们将从技术细节入手,通过图解和代码分析,揭示BO在VRAM、GTT和系统内存之间的迁移路径,以及不同阶段驱逐操作的具体目的。

1. BO驱逐流程全景解析

1.1 BO在显存管理中的核心作用

Buffer Object(BO)是AMD显卡驱动中管理显存的基本单元,它封装了显存分配、映射和同步等核心功能。在休眠唤醒过程中,BO的状态迁移直接影响整个流程的成功率。

BO的主要属性包括:

  • 内存域(domain):标识当前所处的内存区域(VRAM/GTT/系统内存)
  • 驱逐优先级(eviction priority)
  • 内存类型(可见显存/不可见显存)
  • 引用计数

1.2 休眠过程中的两次驱逐调用

在休眠流程中, radeon_bo_evict_vram 会被调用两次,每次都有不同的目的:

static int radeon_pmops_suspend(struct device *dev)
{
    // 第一次驱逐:确保数据写回
    radeon_bo_evict_vram(rdev);
    radeon_fence_driver_force_completion(rdev);
    
    // 第二次驱逐:确保数据保存
    radeon_suspend(rdev);
    radeon_bo_evict_vram(rdev);
}

第一次驱逐的关键任务:

  1. 将VRAM中的活跃数据迁移到安全位置
  2. 等待GPU完成所有未决操作
  3. 保存BIOS scratch寄存器等关键状态

第二次驱逐的核心目标:

  1. 释放所有剩余的VRAM资源
  2. 确保数据已持久化到系统内存或休眠镜像
  3. 为设备完全断电做准备

1.3 BO驱逐的TTM实现机制

BO驱逐最终通过TTM(Translation Table Maps)内存管理器实现,其核心流程如下:

int ttm_bo_evict_mm(struct ttm_bo_device *bdev, struct ttm_mem_type_manager *man)
{
    list_for_each_entry(bo, &man->lru[i], lru) {
        if (!bdev->driver->eviction_valuable(bo, place))
            continue;
        
        ttm_bo_evict(bo, interruptible, no_wait_gpu);
    }
}

驱逐决策矩阵:

BO位置 目标位置 条件判断
可见VRAM 不可见VRAM 不可见VRAM有足够空间
可见VRAM GTT 不可见VRAM空间不足
不可见VRAM GTT GTT有可用空间
GTT 系统内存 GTT空间不足

2. 显存管理的三个关键阶段

2.1 预驱逐阶段(Pre-Eviction)

在正式驱逐前,驱动需要完成一系列准备工作:

  1. 显示输出处理 :

    • 关闭所有显示输出(DRM_MODE_DPMS_OFF)
    • Unpin桌面和鼠标相关的BO(保留fb BO)
  2. 硬件状态保存 :

    radeon_save_bios_scratch_regs(rdev);
    evergreen_suspend(rdev);  // ASIC特定挂起例程
    
  3. 中断禁用 :

    • 关闭HPD(热插拔检测)中断
    • 禁用DMA引擎
    • 关闭写回缓存

2.2 主驱逐阶段(Core Eviction)

这是显存管理的核心阶段,涉及复杂的迁移决策:

关键迁移路径:

  1. VRAM → 不可见显存 (首选路径)

    • 需要检查不可见显存容量
    • 通过DMA引擎完成数据传输
  2. VRAM → GTT (次选路径)

    • 当不可见显存不足时采用
    • 需要考虑GTT容量限制
  3. GTT → 系统内存 (最后选择)

    • 当GTT空间不足时触发
    • 可能影响休眠镜像大小

性能关键参数:

  • radeon.gtt_size :调整GTT空间大小
  • radeon.vram_limit :限制VRAM使用量
  • ttm.page_pool_size :控制TTM页池大小

2.3 后驱逐阶段(Post-Eviction)

驱逐完成后需要确保系统状态一致:

  1. PCI设备状态管理 :

    pci_disable_device(pdev);  // 禁用PCI设备
    
  2. 内存一致性检查 :

    • 验证所有关键BO已正确迁移
    • 检查内存引用计数归零
  3. 错误恢复准备 :

    • 设置恢复标记
    • 保存必要的硬件状态

3. 容量关系与休眠成功率

3.1 关键容量参数

参数 典型值 影响范围
VRAM容量 2GB-16GB 决定可保留的显存数据量
GTT大小 通常为VRAM的1/2 影响中间缓冲能力
系统内存 8GB-64GB 决定最终休眠镜像大小

3.2 容量不足的解决方案

当遇到休眠失败时,可考虑以下调整:

内核参数调整:

# 增大GTT空间(需重启生效)
echo "options radeon gtt_size=2048" > /etc/modprobe.d/radeon.conf

# 限制VRAM使用
echo "options radeon vram_limit=1536" >> /etc/modprobe.d/radeon.conf

用户空间预防措施:

// 应用层检测示例
long vram_free = radeon_gem_info(RADEON_GEM_INFO_VRAM_FREE);
if (vram_free < MIN_REQUIRED) {
    trigger_early_eviction();
}

3.3 测试案例分析

基于提供的测试代码,我们观察到:

  1. VRAM耗尽行为 :

    • 当VRAM使用达到75%时开始驱逐
    • 每次驱逐批处理约500MB数据
  2. GTT溢出处理 :

    [drm:radeon_bo_evict_GTT] GTT空间不足,开始向系统内存迁移
    
  3. 休眠失败临界点 :

    • 系统内存需求 = VRAM使用量 + GTT使用量
    • 当超过80%系统内存时休眠可能失败

4. 唤醒流程与BO恢复

4.1 唤醒阶段的核心任务

  1. 硬件初始化 :

    evergreen_resume(rdev);  // ASIC特定恢复例程
    radeon_restore_bios_scratch_regs(rdev);
    
  2. GART表恢复 :

    • 第一个被pin回VRAM的BO
    • 需要特殊处理DMA映射
  3. 显示系统重启 :

    • 重新初始化显示管道
    • 恢复显示模式设置

4.2 按需恢复机制

BO的恢复采用懒加载策略:

int radeon_bo_list_validate(struct radeon_device *rdev,
                           struct list_head *head)
{
    list_for_each_entry(entry, head, tv.head) {
        ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
    }
}

恢复触发条件:

  1. 应用通过ioctl提交命令
  2. BO验证发现位置不匹配
  3. 显存访问缺页异常

4.3 性能优化建议

  1. BO分配策略 :

    // 优先分配不可见显存
    args.initial_domain = RADEON_GEM_DOMAIN_VRAM | RADEON_GEM_DOMAIN_GTT;
    
  2. 唤醒加速技巧 :

    • 预加载关键BO(如桌面环境)
    • 启用异步恢复机制
    • 调整TTM缓存策略
  3. 调试信息收集 :

    # 查看BO状态
    cat /sys/kernel/debug/dri/0/radeon_bo
    
    # 监控迁移事件
    echo 1 > /sys/module/drm/parameters/trace
    
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐