AMD KFD驱动技术分析11:SVM驱动实现-1

接前文: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_ROGPU只读
GPU_EXECGPU可执行
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 *)。

    • startlast:要检查的虚拟地址区间(以页为单位)。

    • bo_sbo_l:如果区间已被映射,这两个指针返回已映射区间的起止页号。

  • 实现流程:

  1. 遍历进程下所有 GPU 设备(p->pdds)。

  2. 对每个设备,获取其 amdgpu_vm,并锁定其根 BO。

  3. 在该设备的 VA interval tree 中查找与 [start, last] 区间重叠的映射(amdgpu_bo_va_mapping)。

  4. 如果找到重叠映射,说明该区间已经被 KFD/AMDGPU 分配过,返回 -EADDRINUSE,并可通过 bo_sbo_l 得到实际映射区间。

  5. 如果所有设备都没有重叠映射,则返回 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

如有帮助,请三连:点赞、收藏、加关注。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeplyMind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值