接前文:AMD KFD驱动技术分析11:SVM原理与核心概念。
4. KFD SVM range的创建
创建流程概括如下,每个过程都会有专文进行分析,请按顺序阅读。
-
用户空间通过 ioctl(如
KFD_IOCTL_SVM_OP_SET_ATTR)请求分配/设置 SVM 区间。 -
驱动调用
svm_range_set_attr,检查属性,更新属性。 -
调用svm_range_is_valid检查区间有效性。
-
通过
svm_range_new创建,插入到svm_range_list的链表和区间树。 -
每个区间通过
mmu_interval_notifier注册到 Linux MMU,感知虚拟内存事件。
4.1 SVM 区间的创建
4.1.1 svm属性说明
svm的属性及其作用列表如下,包括:首选位置(CPU/GPU)、预取位置、访问权限(ACCESS/NO_ACCESS)、粒度、标志位(如 ALWAYS_MAPPED、COHERENT 等)。
-
用户空间通过 ioctl(如
KFD_IOCTL_SVM_OP_SET_ATTR)请求分配/设置 SVM 区间。 -
驱动调用
svm_range_set_attr,检查属性、查找/拆分/合并区间,更新属性。 -
新区间通过
svm_range_new创建,插入到svm_range_list的链表和区间树。 -
每个区间通过
mmu_interval_notifier注册到 Linux MMU,感知虚拟内存事件。
| 属性 | 作用说明 | 取值/含义 |
|---|---|---|
| PREFERRED_LOC | 指定SVM区间的首选驻留位置(CPU或某个GPU),影响迁移和映射策略 | 0=CPU,GPU ID,KFD_IOCTL_SVM_LOCATION_UNDEFINED |
| PREFETCH_LOC | 指定SVM区间的预取目标位置,用于预取迁移优化 | 0=CPU,GPU ID,KFD_IOCTL_SVM_LOCATION_UNDEFINED |
| ACCESS/ACCESS_IN_PLACE/ NO_ACCESS | .允许指定GPU对该区间有直接访问权限 .允许指定GPU对该区间有原地访问权限(不迁移,仅本地访问) .禁止指定GPU访问该区间 | GPU ID |
| SET_FLAGS | 设置SVM区间的标志位,如一致性、只读、执行权限等 | 标志位掩码,见下方说明 |
| CLR_FLAGS | 清除SVM区间的标志位 | 标志位掩码 |
| GRANULARITY | 设置SVM区间的迁移/映射粒度(以页为单位,影响性能与一致性) | 粒度值(如2MB、4KB等,最大0x3F) |
常见 Flags说明如下:
| 标志位(flags) | 作用说明 |
|---|---|
| HOST_ACCESS | 允许CPU访问该区间 |
| COHERENT | 区间为一致性内存,CPU/GPU可见一致 |
| EXT_COHERENT | 扩展一致性,跨NUMA或XGMI一致性 |
| GPU_RO | GPU只读 |
| GPU_EXEC | GPU可执行 |
| ALWAYS_MAPPED | 区间始终映射到GPU |
-
这些属性通过 SVM ioctl 接口设置或查询,驱动根据属性自动调整区间的迁移、映射、权限和一致性策略。典型用法如:指定某区间优先驻留在某GPU、限制某GPU访问、设置为只读或一致性内存等。
-
属性通过 ioctl 设置,驱动自动根据属性调整页表映射、迁移策略等。
-
支持多 GPU 的访问权限 bitmap,灵活控制各 GPU 对区间的访问。
4.1.2 区间检查
KFD驱动首先要检查区间的有效性。检查函数为:svm_range_is_valid,内部调用了svm_range_check_vm。
svm_range_check_vm 函数的作用是:检查指定虚拟地址区间 [start, last] 是否已经被 KFD 驱动通过 kfd_ioctl_alloc_memory_of_gpu 等接口分配并映射过(即是否已经被 TTM/AMDGPU 显存管理映射)。防止 SVM 区间与已分配的 GPU 显存区间重叠,避免内存管理混乱和数据一致性问题
-
参数说明:
-
p:当前 KFD 进程(struct kfd_process *)。 -
start、last:要检查的虚拟地址区间(以页为单位)。 -
bo_s、bo_l:如果区间已被映射,这两个指针返回已映射区间的起止页号。
-
-
实现流程:
-
遍历进程下所有 GPU 设备(
p->pdds)。 -
对每个设备,获取其
amdgpu_vm,并锁定其根 BO。 -
在该设备的 VA interval tree 中查找与
[start, last]区间重叠的映射(amdgpu_bo_va_mapping)。 -
如果找到重叠映射,说明该区间已经被 KFD/AMDGPU 分配过,返回
-EADDRINUSE,并可通过bo_s、bo_l得到实际映射区间。 -
如果所有设备都没有重叠映射,则返回 0,表示该区间未被映射,可以安全用于 SVM。
-
用途:在 SVM 区间创建、属性设置等操作前,作为安全检查。
static int
svm_range_is_valid(struct kfd_process *p, uint64_t start, uint64_t size)
{
const unsigned long device_vma = VM_IO | VM_PFNMAP | VM_MIXEDMAP;
struct vm_area_struct *vma;
unsigned long end;
unsigned long start_unchg = start;
start <<= PAGE_SHIFT;
end = start + (size << PAGE_SHIFT);
do {
vma = vma_lookup(p->mm, start);
if (!vma || (vma->vm_flags & device_vma))
return -EFAULT;
start = min(end, vma->vm_end);
} while (start < end);
return svm_range_check_vm(p, start_unchg, (end - 1) >> PAGE_SHIFT, NULL,
NULL);
}
static int
svm_range_check_vm(struct kfd_process *p, uint64_t start, uint64_t last,
uint64_t *bo_s, uint64_t *bo_l)
{
struct amdgpu_bo_va_mapping *mapping;
struct interval_tree_node *node;
uint32_t i;
int r;
for (i = 0; i < p->n_pdds; i++) {
struct amdgpu_vm *vm;
if (!p->pdds[i]->drm_priv)
continue;
vm = drm_priv_to_vm(p->pdds[i]->drm_priv);
r = amdgpu_bo_reserve(vm->root.bo, false);
if (r)
return r;
node = interval_tree_iter_first(&vm->va, start, last);
if (node) {
mapping = container_of((struct rb_node *)node,
struct amdgpu_bo_va_mapping, rb);
if (bo_s && bo_l) {
*bo_s = mapping->start;
*bo_l = mapping->last;
}
amdgpu_bo_unreserve(vm->root.bo);
return -EADDRINUSE;
}
amdgpu_bo_unreserve(vm->root.bo);
}
return 0;
}
在区间检查通过后,就是把该区间放入svm list,伪代码如下。这里有几个列表的功能需要解释一下:
int svm_range_add(...) {
初始化所有链表;
遍历 interval tree 查找所有与 [start, last] 重叠的区间;
for (每个重叠区间) {
if (属性相同且已映射) {
// 无需操作
} else if (区间部分重叠) {
克隆并拆分区间;
将原区间加入 remove_list;
将新段加入 insert_list, update_list, remap_list;
} else {
// 区间完全被包含,只需更新属性
将区间加入 update_list;
}
// 处理空白区间
if (有空白区间) {
创建新区间;
加入 new_list, update_list;
}
}
// 处理尾部剩余区间
if (start <= last)
创建新区间并加入 new_list, update_list;
// 错误回滚
if (出错) {
释放所有新建和克隆区间;
} else {
拼接 new_list 到 insert_list;
}
返回结果;
}
具体的实现在下文:AMD KFD驱动技术分析11:SVM驱动实现-2。
如有帮助,请三连:点赞、收藏、加关注。
1121

被折叠的 条评论
为什么被折叠?



