难度 : 🟡 进阶
预计学习时间 : 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)
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)
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通过字符设备暴露给用户空间:
bash
/dev/kfd # 主要接口
IOCTL命令(部分):
c
#define AMDKFD_IOC_CREATE_QUEUE // 创建计算队列
#define AMDKFD_IOC_DESTROY_QUEUE // 销毁队列
#define AMDKFD_IOC_SET_MEMORY_POLICY // 设置内存策略
#define AMDKFD_IOC_SVM // ★ SVM操作
// ... 更多
SVM IOCTL流程:
c
用户空间调用: 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)
c
// 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)
c
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:
c
// 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地址(系统内存物理地址)
示例:
c
// 系统内存页面的DMA地址: 0x8000_1000
// GART映射: GPU地址 0xF000_0000 → DMA 0x8000_1000
// GPU执行:
load R1, [0xF000_0000] // GPU地址
↓
GART查表
↓
通过PCIe访问 0x8000_1000 // 系统内存
GART在SVM中的使用
SVM使用GART进行页面迁移:
c
// 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接口
c
// 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接口
c
// kfd_svm.c中使用HMM
// 查询CPU页表
ret = hmm_range_fault(&range);
// 迁移页面
ret = migrate_vma_setup(&migrate);
// ...
ret = migrate_vma_finalize(&migrate);
3. SVM ↔ 进程管理接口
c
// 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理念是通用的。
📝 实践练习
-
代码探索:
bashcd 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协作?
-
系统观察:
bash# 查看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内核基础
- 下一章: 04 - SVM核心数据结构详解
- 返回目录: AMD ROCm-SVM技术的实现与应用深度分析目录