AMD KFD技术分析:Eviction Fence

发布于:2025-08-07 ⋅ 阅读:(63) ⋅ 点赞:(0)

一、背景与设计目标

在 AMDGPU 驱动中,KFD(Kernel Fusion Driver)负责异构计算(如 ROCm/HSA)下的 GPU 资源管理。KFD 进程通过用户空间队列(user queues)提交任务,驱动需要高效管理这些进程的显存(VRAM)和系统内存(GTT)资源。

GPU 显存有限,多个进程/任务并发时,显存可能不足。此时,驱动需要“驱逐”(evict)部分 BO(Buffer Object,缓冲区对象),将其从 VRAM 移到系统内存或 swap 空间,为新任务腾出空间。但 KFD 的 BO 由用户空间队列驱动,不能像传统图形 BO 那样随时驱逐,必须保证所有相关队列 quiesce(静默、停止访问)后才能安全迁移。

Eviction Fence 机制就是为了解决这个同步问题:

  • 只有当进程的所有 user queue 都 quiesce 后,相关 BO 才能被安全eviciton;

  • 需要一种机制通知 TTM和 GPU 调度器,何时可以安全移动 BO。

二、核心数据结构

1. struct amdgpu_amdkfd_fence

struct amdgpu_amdkfd_fence {
    struct dma_fence base;           // 标准 fence 基类
    struct mm_struct *mm;            // 关联进程的内存描述符
    char timeline_name[TASK_COMM_LEN]; // 便于调试的进程名
    spinlock_t lock;                 // fence 锁
    struct svm_range_bo *svm_bo;     // SVM 相关 BO(可选)
};

继承自 dma_fence,可被 Linux 通用 fence 框架和 GPU 调度器识别。
关联进程的 mm_struct,用于判断 BO 是否属于当前进程。
svm_bo 用于 SVM(共享虚拟内存)场景,支持 VRAM overcommit。

2. dma_fence_ops

static const struct dma_fence_ops amdkfd_fence_ops = {
    .get_driver_name = amdkfd_fence_get_driver_name,
    .get_timeline_name = amdkfd_fence_get_timeline_name,
    .enable_signaling = amdkfd_fence_enable_signaling,
    .release = amdkfd_fence_release,
};

enable_signaling 是核心回调,用于触发Eviction流程。
release 负责 fence 生命周期管理。

三、eviction fence 的创建与生命周期

1. 创建

struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context,
                struct mm_struct *mm,
                struct svm_range_bo *svm_bo)
{
    struct amdgpu_amdkfd_fence *fence;

    fence = kzalloc(sizeof(*fence), GFP_KERNEL);
    if (fence == NULL)
        return NULL;

    mmgrab(mm); // 增加 mm_struct 引用计数
    fence->mm = mm;
    get_task_comm(fence->timeline_name, current);
    spin_lock_init(&fence->lock);
    fence->svm_bo = svm_bo;
    dma_fence_init(&fence->base, &amdkfd_fence_ops, &fence->lock,
           context, atomic_inc_return(&fence_seq));

    return fence;
}

每个进程(或 SVM BO)创建一个 eviction fence。
fence 生命周期与进程/BO 绑定,引用计数由 fence 框架管理。

2. 释放

static void amdkfd_fence_release(struct dma_fence *f)
{
    struct amdgpu_amdkfd_fence *fence = to_amdgpu_amdkfd_fence(f);

    if (WARN_ON(!fence))
        return;

    mmdrop(fence->mm); // 释放 mm_struct
    kfree_rcu(f, rcu); // 延迟释放 fence
}

fence 释放时,自动释放 mm_struct 引用,防止内存泄漏。

四、与 TTM/BO/调度器的交互

1. TTM 的 BO 驱逐流程

TTM是 Linux DRM 的通用内存管理框架,负责 BO 的分配、回收、迁移。

当进程 X 需要分配 VRAM,但空间不足时,TTM 会尝试从 LRU 列表中驱逐 BO。
TTM 首先调用 ttm_device_funcs->eviction_valuable() 判断 BO 是否可以被驱逐。

// 伪代码
if (ttm_device_funcs->eviction_valuable(bo)) {
    // 可以驱逐,继续后续流程
    ttm_bo_evict(bo);
}

对于 KFD 进程自己的 BO,eviction_valuable 返回 false,防止自我驱逐。
对于其他进程的 BO,返回 true,允许驱逐。

2. BO 驱逐的后续流程

TTM 调用 ttm_bo_evict,最终调用 amdgpu_bo_move,通过 amdgpu_copy_buffer 提交迁移任务到 GPU 调度器。
GPU 调度器(amd_sched_main)为迁移任务创建一个 fence,并注册回调(fence_add_callback),用于在 BO 可移动时通知后续流程。

3. fence 的 enable_signaling 回调

当 GPU 调度器检测到 BO 可以移动时,会调用 fence 的 enable_signaling 回调。

static bool amdkfd_fence_enable_signaling(struct dma_fence *f)
{
    struct amdgpu_amdkfd_fence *fence = to_amdgpu_amdkfd_fence(f);

    if (!fence)
        return false;

    if (dma_fence_is_signaled(f))
        return true;

    if (!fence->svm_bo) {
        if (!kgd2kfd_schedule_evict_and_restore_process(fence->mm, f))
            return true;
    } else {
        if (!svm_range_schedule_evict_svm_bo(fence))
            return true;
    }
    return false;
}

如果 fence 已经 signaled,直接返回。

否则,调度一个工作项(work item),通知 KFD 进程 quiesce 所有 user queue,并最终 signal fence。

五、KFD 进程的驱逐与恢复

1. 驱逐流程

kgd2kfd_schedule_evict_and_restore_process 会调度一个工作项(通常是 evict_process_worker),该 worker 会:

  • 通知用户空间队列 quiesce,确保没有新的 GPU 任务提交。
  • 等待所有相关任务完成。
  • signal fence,通知 TTM/调度器 BO 可以安全迁移。

只有当所有相关队列都静默后,fence 才会被 signal,TTM 才能继续后续的 BO 迁移。

2. 恢复流程

  • 驱逐完成后,KFD 会调度另一个延迟工作项(如 restore_process_worker),在合适时机尝试恢复进程的 GPU 资源和队列。
  • 这样可以在资源充足时自动恢复进程的 GPU 访问能力,提升用户体验。

六、SVM(共享虚拟内存)场景

SVM BO 支持 VRAM overcommit,允许多个进程共享同一块虚拟内存。

eviction fence 机制对 SVM BO 做了特殊处理:

  • amdkfd_fence_check_mm 中,如果是 SVM BO,总是返回 false,表示不阻止驱逐。
  • SVM BO 的驱逐和恢复由 svm_range_schedule_evict_svm_bo 负责,流程类似但更复杂。

七、典型调用路径梳理

  1. 进程 X 需要分配 VRAM,空间不足
  2. TTM 尝试驱逐 LRU BO
    1. 调用 eviction_valuable 判断 BO 是否可驱逐
    2. KFD 进程自己的 BO 返回 false,其他进程的 BO 返回 true
  3. TTM 调用 ttm_bo_evict,提交迁移任务到 GPU 调度器
  4. 调度器为迁移任务创建 fence,注册回调
  5. BO 可移动时,调度器调用 fence 的 enable_signaling
    1. 调用 kgd2kfd_schedule_evict_and_restore_process,调度 evict_process_worker
    2. worker 通知用户队列 quiesce,等待所有任务完成
    3. signal fence,TTM 继续迁移 BO
    4. 驱逐完成后,调度 restore_process_worker,尝试恢复进程资源

八、机制的优点

优点:

  • 安全性:保证 BO 迁移前,所有相关 user queue 都已静默,防止数据损坏和非法访问。
  • 高效性:通过异步工作队列和 fence 框架,实现高效的资源回收与恢复。
  • 可扩展性:支持多进程、多 GPU、SVM 等复杂场景。
  • 与 Linux 通用机制兼容:充分利用 dma_fence、TTM、GPU 调度器等内核通用机制。

九、代码片段与流程图示意

1. 伪代码流程

// 进程分配 VRAM,空间不足
if (ttm_device_funcs->eviction_valuable(bo)) {
    ttm_bo_evict(bo);
    // ...
    // GPU 调度器创建 fence
    fence_add_callback(fence, amdkfd_fence_enable_signaling);
}

// fence enable_signaling 回调
if (!dma_fence_is_signaled(f)) {
    kgd2kfd_schedule_evict_and_restore_process(fence->mm, f);
    // 调度 evict_process_worker
}

// evict_process_worker
quiesce_all_user_queues();
dma_fence_signal(fence); // 通知 TTM/调度器
// 调度 restore_process_worker

2. 流程图

[进程X分配VRAM]
      |
      v
[TTM尝试驱逐BO]
      |
      v
[eviction_valuable?]----否---->[跳过]
      |
     是
      v
[ttm_bo_evict -> amdgpu_bo_move -> GPU调度器]
      |
      v
[创建fence, 注册enable_signaling回调]
      |
      v
[enable_signaling被调用]
      |
      v
[调度evict_process_worker]
      |
      v
[quiesce所有user queue]
      |
      v
[signal fence]
      |
      v
[TTM继续迁移BO]
      |
      v
[调度restore_process_worker]
      |
      v
[恢复进程资源]

十、总结

eviction fence 机制是 AMDGPU/KFD 驱动为异构计算场景下的进程显存管理设计的关键同步机制。它通过 fence 框架与 TTM、GPU 调度器协作,确保只有在进程所有 user queue 静默后,相关 BO 才能被安全迁移。

该机制支持多进程、多 GPU、SVM 等复杂场景,兼顾安全性与高效性,是现代 GPU 驱动资源管理的核心组成部分。


网站公告

今日签到

点亮在社区的每一天
去签到