欢迎光临
我们一直在努力

03 - AMDGPU驱动架构概览

难度: 🟡 进阶 预计学习时间: 1-1.5小时 前置知识: Linux驱动基础、前两章内容


📋 概述

在深入SVM实现细节之前,我们需要理解AMDGPU驱动的整体架构。AMDGPU是一个复杂的驱动系统,包含显卡驱动(AMDGPU)和计算驱动(KFD)两大部分。SVM功能主要在KFD中实现,但与AMDGPU的其他组件紧密协作。

本章将介绍驱动的层次结构、关键组件以及SVM在其中的位置。


3.1 AMDGPU驱动整体架构

AMD GPU驱动实际上包含两个主要部分:

┌─────────────────────────────────────────┐
│ 用户空间应用 │
│ [OpenGL] [Vulkan] [OpenCL] [HIP] │
└─────────────────────────────────────────┘
↓ ↓ ↓
┌────────┐ ┌─────────┐ ┌─────────┐
│Mesa/驱动│ │libdrm │ │ROCm RT │
└────────┘ └─────────┘ └─────────┘
↓ ↓
======================================
内核空间
======================================
↓ ↓
┌──────────┐ ┌──────────┐
│ AMDGPU │ │ KFD │
│ (DRM) │ ←──────→ │ (Compute)│
│ 图形驱动 │ │ 计算驱动 │
└──────────┘ └──────────┘
↓ ↓
┌────────────────────────────┐
│ GPU 硬件 │
│ [GFX] [Compute] [SDMA] │
└────────────────────────────┘

AMDGPU驱动
  • 位置: drivers/gpu/drm/amd/amdgpu/
  • 作用:
    • 图形渲染支持
    • 显示输出(DisplayPort, HDMI等)
    • GPU内存管理(TTM)
    • GPU电源管理
    • 基础硬件抽象
KFD驱动(Kernel Fusion Driver)
  • 位置: drivers/gpu/drm/amd/amdkfd/
  • 作用:
    • HSA支持(Heterogeneous System Architecture)
    • 计算队列管理
    • 进程管理
    • SVM实现 ← 我们关注的重点
    • 调试支持

目录结构

drivers/gpu/drm/amd/
├── amdgpu/ # AMDGPU核心驱动
│ ├── amdgpu_vm.c # 虚拟内存管理
│ ├── amdgpu_ttm.c # TTM内存管理
│ ├── amdgpu_device.c # 设备初始化
│ └── …
├── amdkfd/ # KFD计算驱动 ★ SVM在这里
│ ├── kfd_module.c # 模块初始化
│ ├── kfd_device.c # 设备管理
│ ├── kfd_process.c # 进程管理
│ ├── kfd_svm.c # SVM实现 (4272行)
│ ├── kfd_svm.h # SVM头文件
│ ├── kfd_migrate.c # 页面迁移
│ └── …
├── include/
│ └── kgd_kfd_interface.h # AMDGPU与KFD接口
└── …


3.2 KFD (Kernel Fusion Driver) 简介

KFD的历史

KFD是为HSA设计的:

  • 目标: 让CPU和GPU平等协作
  • 理念: “GPU is a first-class compute citizen”
  • 特性: 统一地址空间、用户态队列、抢占支持

KFD的核心组件

┌───────────────────────────────────────┐
│ KFD 驱动 │
├───────────────────────────────────────┤
│ ┌────────────┐ ┌────────────┐ │
│ │进程管理 │ │设备管理 │ │
│ │kfd_process │ │kfd_device │ │
│ └────────────┘ └────────────┘ │
│ ┌────────────┐ ┌─────────────┐ │
│ │队列管理 │ │中断处理 │ │
│ │kfd_queue │ │kfd_interrupt│ │
│ └────────────┘ └─────────────┘ │
│ ┌────────────┐ ┌────────────┐ │
│ │内存管理 │ │拓扑管理 │ │
│ │kfd_svm │ │kfd_topology│ │
│ └────────────┘ └────────────┘ │
│ ┌────────────┐ ┌────────────┐ │
│ │事件管理 │ │调试支持 │ │
│ │kfd_events │ │kfd_debug │ │
│ └────────────┘ └────────────┘ │
└───────────────────────────────────────┘

1. 进程管理(kfd_process.c)

struct kfd_process {
struct mm_struct *mm; // 进程的内存管理结构
struct mutex mutex;
uint32_t pasid; // Process Address Space ID

struct kfd_process_device *pdds[MAX_GPU_INSTANCE];

struct svm_range_list svms; // ★ SVM范围列表

// 其他成员…
};

作用:

  • 每个使用KFD的进程都有一个 kfd_process 结构
  • 管理进程在所有GPU上的资源
  • 持有SVM范围列表
2. 设备管理(kfd_device.c)

struct kfd_dev {
struct amdgpu_device *adev; // 指向AMDGPU设备
struct kfd_device_info device_info;

// 与AMDGPU交互的接口
const struct kfd2kgd_calls *kfd2kgd;

// 设备内存信息
struct dev_pagemap pgmap; // ★ 设备内存页面映射

// 其他成员…
};

初始化流程:

GPU硬件探测

amdgpu驱动加载

调用 kgd2kfd_probe() ← AMDGPU通知KFD

创建 kfd_dev 结构

kgd2kfd_device_init() ← 初始化KFD设备

注册到拓扑系统

设备就绪

3. 字符设备接口(kfd_chardev.c)

KFD通过字符设备暴露给用户空间:

/dev/kfd # 主要接口

IOCTL命令(部分):

#define AMDKFD_IOC_CREATE_QUEUE // 创建计算队列
#define AMDKFD_IOC_DESTROY_QUEUE // 销毁队列
#define AMDKFD_IOC_SET_MEMORY_POLICY // 设置内存策略
#define AMDKFD_IOC_SVM // ★ SVM操作
// … 更多

SVM IOCTL流程:

用户空间调用: ioctl(kfd_fd, AMDKFD_IOC_SVM, &args)

kfd_ioctl() 分发

kfd_ioctl_svm() ← kfd_chardev.c

svm_ioctl() ← kfd_svm.c

处理具体的SVM操作(添加范围、设置属性等)


3.3 TTM (Translation Table Maps)

什么是TTM

TTM是DRM子系统提供的GPU内存管理框架:

┌────────────────────────────────────┐
│ TTM 框架 │
├────────────────────────────────────┤
│ • 管理VRAM和GTT内存 │
│ • Buffer Object (BO) 管理 │
│ • 内存驱逐和交换 │
│ • 与系统内存交互 │
└────────────────────────────────────┘
↓ ↓
┌─────────┐ ┌─────────┐
│ VRAM │ │ GTT │
│(GPU显存) │ │(系统内存)│
└─────────┘ └─────────┘

TTM的内存域(Memory Domain)

// TTM定义的内存类型
#define TTM_PL_SYSTEM 0 // 系统RAM
#define TTM_PL_TT 1 // GTT (Graphics Translation Table)
#define TTM_PL_VRAM 2 // GPU显存
#define TTM_PL_PRIV 3 // 驱动私有

Buffer Object (BO)

struct amdgpu_bo {
struct ttm_buffer_object tbo; // TTM BO基类

// VRAM位置
u64 vram_offset;

// 偏好域
u32 preferred_domains;

// 允许的域
u32 allowed_domains;

// 其他成员…
};

BO的生命周期:

1. 创建BO
amdgpu_bo_create()

分配TTM资源

2. 放置BO(可能在VRAM或系统内存)
amdgpu_bo_pin() / ttm_bo_validate()

3. CPU访问
amdgpu_bo_kmap() ← 建立内核映射

4. GPU访问
通过GPU页表映射

5. 驱逐(内存不足时)
ttm_bo_evict()

移动到系统内存

6. 销毁
amdgpu_bo_unref()

TTM与SVM的关系

SVM也使用TTM管理VRAM:

// kfd_svm.c中
struct svm_range_bo {
struct amdgpu_bo *bo; // ← 使用TTM BO
// …
};

// 分配VRAM给SVM范围
int svm_range_vram_node_new(...) {
// 创建amdgpu_bo
ret = amdgpu_bo_create(...);
// …
}

关键区别:

  • 传统BO:整块分配,显式管理
  • SVM:按需分配页面,自动迁移

3.4 GART表的作用

什么是GART

GART (Graphics Address Remapping Table) 是GPU的地址重映射表,允许GPU访问系统内存。

没有GART:
GPU只能访问VRAM ✗

有GART:
GPU可以通过GART访问系统RAM ✓

GART的工作原理

GPU视角的地址空间:
┌──────────────────────┐
│ 0x0000_0000 │
│ … │ ← VRAM (直接映射)
│ VRAM End │
├──────────────────────┤
│ GART Start │
│ … │ ← GART窗口(映射系统RAM)
│ GART End │
└──────────────────────┘

GART映射表:
GPU地址 → DMA地址(系统内存物理地址)

示例:

// 系统内存页面的DMA地址: 0x8000_1000
// GART映射: GPU地址 0xF000_0000 → DMA 0x8000_1000

// GPU执行:
load R1, [0xF000_0000] // GPU地址

GART查表

通过PCIe访问 0x8000_1000 // 系统内存

GART在SVM中的使用

SVM使用GART进行页面迁移:

// kfd_migrate.c

// 使用GART映射系统内存页面
svm_migrate_gart_map(ring, npages, dma_addr, &gart_addr, flags);

// 使用SDMA在GART地址和VRAM间复制
amdgpu_copy_buffer(ring, gart_addr, vram_addr, size, ...);

为什么需要GART

  • SDMA只能访问GPU地址空间
  • 系统内存不在GPU地址空间中
  • GART将系统内存"映射"到GPU地址空间
  • SDMA现在可以访问系统内存了
  • 流程:

    系统内存页面

    建立GART映射 → GPU可见地址

    SDMA从GPU地址拷贝 → VRAM


    3.5 SVM在驱动中的位置

    SVM的模块划分

    kfd_svm.c # SVM核心逻辑
    ├─ 范围管理
    │ ├─ svm_range_add()
    │ ├─ svm_range_unlink()
    │ └─ svm_range_split()
    ├─ 页面映射
    │ ├─ svm_range_map_to_gpu()
    │ ├─ svm_range_unmap_from_gpu()
    │ └─ svm_range_validate_and_map()
    ├─ 缺页处理
    │ ├─ svm_range_restore_pages()
    │ └─ 页面恢复逻辑
    ├─ MMU Notifier
    │ └─ svm_range_cpu_invalidate_pagetables()
    ├─ IOCTL接口
    │ └─ svm_ioctl()
    └─ 其他辅助功能

    kfd_migrate.c # 页面迁移
    ├─ RAM → VRAM迁移
    ├─ VRAM → RAM迁移
    ├─ GART映射管理
    └─ SDMA复制操作

    kfd_svm.h # SVM数据结构定义

    SVM与其他组件的交互

    ┌──────────────────────────────┐
    │ 用户空间 │
    │ (ROCm Runtime) │
    └──────────────────────────────┘
    ↓ IOCTL
    ========================
    内核空间
    ========================

    ┌──────────────┐
    │ kfd_chardev │ ← IOCTL分发
    └──────────────┘

    ┌──────────────┐
    │ kfd_svm │ ← SVM核心
    └──────────────┘
    ↓ ↓ ↓
    ┌────┐ ┌────┐ ┌────┐
    │HMM │ │TTM │ │VM │ ← 内核子系统
    └────┘ └────┘ └────┘
    ↓ ↓ ↓
    ┌──────────────────┐
    │ kfd_migrate │ ← 页面迁移
    └──────────────────┘

    ┌──────────────────┐
    │ AMDGPU驱动 │
    │ (SDMA, VM, …) │
    └──────────────────┘

    ┌──────────────────┐
    │ GPU硬件 │
    └──────────────────┘

    关键接口

    1. KFD ↔ AMDGPU接口

    // include/kgd_kfd_interface.h

    struct kfd2kgd_calls {
    // GPU VM操作
    int (*map_memory_to_gpu)(...);
    int (*unmap_memory_from_gpu)(...);

    // SDMA操作
    int (*submit_ib)(...);

    // 其他…
    };

    2. SVM ↔ HMM接口

    // kfd_svm.c中使用HMM

    // 查询CPU页表
    ret = hmm_range_fault(&range);

    // 迁移页面
    ret = migrate_vma_setup(&migrate);
    // …
    ret = migrate_vma_finalize(&migrate);

    3. SVM ↔ 进程管理接口

    // kfd_process.c

    struct kfd_process {
    struct svm_range_list svms; // SVM范围列表
    // …
    };

    // 进程初始化时
    svm_range_list_init(p);

    // 进程退出时
    svm_range_list_fini(p);


    💡 重点提示

  • 双驱动架构:AMDGPU负责图形,KFD负责计算。SVM在KFD中实现。

  • KFD是核心:SVM的所有逻辑都在 drivers/gpu/drm/amd/amdkfd/ 目录下。

  • TTM管理VRAM:SVM使用TTM的BO机制管理GPU显存。

  • GART是桥梁:GART让GPU能够访问系统内存,是迁移的关键。

  • 接口抽象良好:KFD通过接口与AMDGPU交互,解耦合度高。


  • ⚠️ 常见误区

    ❌ 误区1:“SVM是AMDGPU驱动的功能”

    • ✅ 正确理解:SVM是KFD的功能,虽然依赖AMDGPU,但主要代码在KFD中。

    ❌ 误区2:“TTM和SVM是竞争关系”

    • ✅ 正确理解:SVM使用TTM管理VRAM,是合作关系。

    ❌ 误区3:“GART只用于传统内存访问”

    • ✅ 正确理解:SVM页面迁移也大量使用GART。

    ❌ 误区4:“KFD只支持AMD GPU”

    • ✅ 正确理解:KFD是AMD的实现,但HSA理念是通用的。

    📝 实践练习

  • 代码探索:

    cd drivers/gpu/drm/amd/amdkfd

    # 查看模块初始化
    grep -n "module_init\\|module_exit" kfd_module.c

    # 查看KFD与AMDGPU的接口
    grep -n "kgd2kfd" kfd_device.c | head -20

    # 统计SVM代码量
    wc -l kfd_svm.c kfd_migrate.c

  • 思考题:

    • 为什么需要独立的KFD驱动,而不是把所有功能放在AMDGPU中?
    • GART表的大小有限制吗?如果限制,如何影响SVM?
    • TTM的驱逐机制如何与SVM协作?
  • 系统观察:

    # 查看KFD设备(需要AMD GPU和ROCm)
    ls -l /dev/kfd

    # 查看GPU拓扑
    cat /sys/class/kfd/kfd/topology/nodes/*/name

    # 查看SVM支持(如果CONFIG_HSA_AMD_SVM=y)
    dmesg | grep -i svm


  • 📚 本章小结

    • 双驱动架构:AMDGPU(图形)+ KFD(计算)
    • KFD组件:进程管理、设备管理、队列管理、SVM等
    • TTM:GPU内存管理框架,SVM使用TTM管理VRAM
    • GART:地址重映射表,让GPU访问系统内存

    理解这些架构层次,有助于在后续章节深入SVM的具体实现。


    📖 扩展阅读

    • KFD设计文档:Documentation/gpu/amdgpu/driver-misc.rst
    • TTM详细分析
    • DRM子系统分析
    • HSA Foundation
    • AMDGPU的GART设计与实现分析

    ➡️ 下一步

    完成了基础篇的学习,我们已经建立了必要的知识背景。接下来进入数据结构篇,我们将详细剖析SVM的核心数据结构。


    🔗 导航

    • 上一章:02 – SVM相关的Linux内核基础
    • 下一章: 审核中…
    • 返回目录: AMD ROCm-SVM技术的实现与应用深度分析目录
    赞(0)
    未经允许不得转载:171主机测评 » 03 - AMDGPU驱动架构概览
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址