AMDGPU 基于DRM SVM框架的新SVM功能实现 :属性子系统结构体关系解析
AMD 正在使用 drm svm框架重构SVM的实现,看来drm svm框架要进入大范围应用了。下面是在kernel社区上由AMD的开发人员提交的POC 验证版本的patches的技术方案实现。这里快速总结了实现,以飨读者。
因是POC版本,设计可能会变动,读者们慎重使用。本文仅用来跟踪前沿驱动技术的迭代发展现状。
1. 整体架构
用户空间 (UAPI) 内核空间 (attr 管理) 内核空间 (SVM 核心) ┌───────────────────┐ ┌──────────────────────┐ ┌──────────────────┐ │drm_amdgpu_gem_svm │ ioctl │ amdgpu_svm_attr_tree │ │ amdgpu_svm │ │ start_addr │──SET/GET─────────▶│ lock (mutex) │◄────────────── │ attr_tree │ │ size │ │ tree (rb_root) │ │ gpusvm │ │ nattr │ │ range_list │ │ adev, vm ... │ │ attrs_ptr ───────┤ └─────┬────────────────┘ └──────────────────┘ │ │ │ 包含 N 个 │ │ ┌────────────────┤ ▼ │ 包含 N 个 │ │drm_amdgpu_svm_ │ ┌──────────────────────┐ ┌──────┴───────────┐ │ │ attribute │ 转换为 │amdgpu_svm_attr_range │ │amdgpu_svm_range │ │ │ type (u32) │────────────────▶ │ it_node (区间树) │ 查询 attrs │ base (gpusvm) │ │ │ value (u32) │ │ list (链表) │◄────────────── │ pte_flags │ │ └────────────────┘ │ attrs ──────────────┤ │ attr_flags │ │ │ preferred_loc │ │ gpu_mapped ... │ │ │ prefetch_loc │ └──────────────────┘ │ │ flags │ │ │ granularity │ │ │ access (enum) │ │ └──────────────────────┘2. 各结构体解析
2.1amdgpu_svm_attrs— 属性值集合(Value Object)
structamdgpu_svm_attrs{int32_tpreferred_loc;// 首选内存位置: GPU ID / SYSMEM(0) / UNDEFINED(0xffffffff)int32_tprefetch_loc;// 预取位置uint32_tflags;// 位标志组合 (COHERENT, GPU_RO, GPU_EXEC 等)uint32_tgranularity;// 映射粒度enumamdgpu_svm_attr_accessaccess;// NONE / ENABLE / IN_PLACE};纯数据容器,没有指针,可以直接拷贝。它把用户空间的多个离散drm_amdgpu_svm_attribute(type/value 对)聚合成一个完整的属性快照。
2.2amdgpu_svm_attr_range— 区间-属性映射节点
structamdgpu_svm_attr_range{structinterval_tree_nodeit_node;// ← 嵌入区间树,key=[start, last] 页号structlist_headlist;// ← 链入 attr_tree->range_list 有序链表structamdgpu_svm_attrsattrs;// ← 嵌入属性值};双索引结构:同一个节点同时存在于两个数据结构中:
| 索引方式 | 数据结构 | 用途 |
|---|---|---|
it_node | 区间红黑树(rb_root_cached) | O(log n) 按地址范围查询 |
list | 有序链表(range_list) | O(n) 顺序遍历、合并相邻同属性区间 |
这是内核中常见的"同一对象多索引"模式(类似 VMA 同时在mm->mm_rb和mm->mmap中)。
2.3amdgpu_svm_attr_tree— 属性管理器(容器)
structamdgpu_svm_attr_tree{structmutexlock;// 保护下面两个数据结构structrb_root_cachedtree;// ← attr_range 的区间红黑树根structlist_headrange_list;// ← attr_range 的有序链表头structamdgpu_svm*svm;// ← 回指所属 SVM 实例};每个 SVM 实例一个attr_tree,管理该进程(fd)所有地址范围的属性。
2.4amdgpu_svm_attr_change_trigger— 变更分类枚举
enumamdgpu_svm_attr_change_trigger{ACCESS_CHANGE=(1U<<0),// access 级别变了 → 可能需要 unmap/remapPTE_FLAG_CHANGE=(1U<<1),// GPU PTE flags 变了 → 需要重建 PTEMAPPING_FLAG_CHANGE=(1U<<2),// 映射策略变了 → 可能影响缓存一致性LOCATION_CHANGE=(1U<<3),// 首选位置变了 → 可能触发迁移GRANULARITY_CHANGE=(1U<<4),// 粒度变了 → 需要拆分/合并 rangeATTR_ONLY=(1U<<5),// 只更新属性,不影响 GPU 映射};位掩码设计,允许一次set_attr操作同时触发多种变更。
3. 属性标志分类
amdgpu_svm_attrs.flags中的标志位按作用范围分为两组:
PTE 级标志(影响 GPU 页表条目)
#defineAMDGPU_SVM_PTE_FLAG_MASK\(AMDGPU_SVM_FLAG_COHERENT|AMDGPU_SVM_FLAG_EXT_COHERENT|\AMDGPU_SVM_FLAG_GPU_RO|AMDGPU_SVM_FLAG_GPU_EXEC)| 标志 | 值 | 含义 |
|---|---|---|
COHERENT | 0x02 | GPU-CPU 缓存一致性 |
EXT_COHERENT | 0x80 | 跨节点扩展一致性 |
GPU_RO | 0x08 | GPU 只读访问 |
GPU_EXEC | 0x10 | GPU 可执行 |
映射级标志(影响映射策略)
#defineAMDGPU_SVM_MAPPING_FLAG_MASK\(AMDGPU_SVM_FLAG_HOST_ACCESS|AMDGPU_SVM_FLAG_HIVE_LOCAL|\AMDGPU_SVM_FLAG_GPU_READ_MOSTLY|AMDGPU_SVM_FLAG_GPU_ALWAYS_MAPPED)| 标志 | 值 | 含义 |
|---|---|---|
HOST_ACCESS | 0x01 | 允许 CPU 访问设备内存 |
HIVE_LOCAL | 0x04 | 限制在 XGMI hive 内 |
GPU_READ_MOSTLY | 0x20 | 读多写少优化 |
GPU_ALWAYS_MAPPED | 0x40 | 始终保持 GPU 映射 |
4. 数据流
用户调用 ioctl(AMDGPU_SVM_OP_SET_ATTR) │ ▼ drm_amdgpu_svm_attribute[] (type/value 对数组) │ ▼ amdgpu_svm_attr_set() │ ├─▶ 解析为 amdgpu_svm_attrs (聚合所有 type/value) │ ├─▶ 在 attr_tree 中插入/拆分/合并 amdgpu_svm_attr_range │ (区间树 + 链表同步维护) │ ├─▶ 计算 trigger 位掩码 (哪些属性变了) │ └─▶ 通知 svm_range 层:amdgpu_svm_range_apply_attr_change() │ ▼ 根据 trigger 决定:重建 PTE / 迁移 VRAM / 销毁重建 range5. 关键设计:属性层与映射层解耦
attr_range和svm_range是独立的区间覆盖,粒度不同:
地址空间: 0 4K 8K 12K 16K attr_range: |←── preferred=GPU, flags=COHERENT ──────────→| (一个大区间) svm_range: |← range1 →|← range2 →| |← range3 →| (按 fault 按需创建) (已映射) (已映射) (无fault) (已映射)attr_range:由用户set_attr显式创建,覆盖用户声明的整个地址范围svm_range:由 GPU page fault 按需创建,只覆盖实际被 GPU 访问的页- 当
svm_range需要知道某个页的属性时,调用amdgpu_svm_attr_lookup_page_locked()查询attr_tree
这种解耦的好处:
| 场景 | 优势 |
|---|---|
| 用户声明 1GB 范围的属性 | attr_tree只存一个节点(O(1) 空间) |
| 只有 4KB 被 GPU 访问 | svm_range只创建一个 4KB 范围 |
| 用户修改属性 | 只更新attr_range,通知svm_range按需调整 |
| GPU fault 新地址 | svm_range查询attr_tree获取属性 |
6. 内存中的对象层次
amdgpu_device (GPU 设备) └── amdgpu_vm (每个进程一个) └── amdgpu_svm (每个 fd/vm 一个) ├── drm_gpusvm gpusvm │ └── drm_gpusvm_notifier[] │ └── drm_gpusvm_range[] ← amdgpu_svm_range.base │ └── amdgpu_svm_attr_tree *attr_tree ├── rb_root_cached tree ─────────┐ └── list_head range_list ─────────┤ ▼ amdgpu_svm_attr_range ├── it_node (in tree) ├── list (in range_list) └── attrs (属性值)7. UAPI 到内核属性的转换
用户空间通过drm_amdgpu_svm_attribute数组传递离散的 type/value 对:
// 用户空间structdrm_amdgpu_svm_attributeattrs[]={{.type=AMDGPU_SVM_ATTR_PREFERRED_LOC,.value=gpu_id},{.type=AMDGPU_SVM_ATTR_SET_FLAGS,.value=AMDGPU_SVM_FLAG_COHERENT},{.type=AMDGPU_SVM_ATTR_ACCESS,.value=gpu_id},};内核在amdgpu_svm_attr_set()中将其聚合为一个amdgpu_svm_attrs:
| UAPI type | attrs 字段 |
|---|---|
ATTR_PREFERRED_LOC | preferred_loc = value |
ATTR_PREFETCH_LOC | prefetch_loc = value |
ATTR_ACCESS | access = ENABLE |
ATTR_ACCESS_IN_PLACE | access = IN_PLACE |
ATTR_NO_ACCESS | access = NONE |
ATTR_SET_FLAGS | flags |= value |
ATTR_CLR_FLAGS | flags &= ~value |
ATTR_GRANULARITY | granularity = value |
