AMD 正在使用 drm svm框架重构SVM的实现看来drm svm框架要进入大范围应用了。下面是在kernel社区上由AMD的开发人员提交的POC 验证版本的patches的技术方案实现。这里快速总结了实现以飨读者。因是POC版本设计可能会变动读者们慎重使用。本文仅用来跟踪前沿驱动技术的迭代发展现状。1. 总体架构概览AMDGPU SVM 的 VRAM migration 建立在 Linux DRM 子系统的drm_pagemap框架之上。整体分为三层┌─────────────────────────────────────────────────────────┐ │ AMDGPU SVM Map Path (调用者) │ │ amdgpu_svm_range_migrate.c │ │ ┌───────────────────────────────────────────────────┐ │ │ │ migrate_range() → migrate_to_vram() │ │ │ │ 三层迁移决策: Capability → Command → Policy │ │ │ └───────────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────┤ │ DRM Pagemap Framework (通用层) │ │ drm_pagemap.c / drm_gpusvm.c │ │ ┌───────────────────────────────────────────────────┐ │ │ │ drm_pagemap_populate_mm() │ │ │ │ → drm_pagemap_migrate_to_devmem() │ │ │ │ migrate_vma_setup → populate_pfn → copy → │ │ │ │ migrate_vma_pages → migrate_vma_finalize │ │ │ │ │ │ │ │ drm_pagemap_migrate_to_ram() (CPU fault回迁) │ │ │ │ drm_pagemap_evict_to_ram() (显式回迁) │ │ │ │ │ │ │ │ drm_gpusvm_range_get_pages() device_map() │ │ │ └───────────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────┤ │ AMDGPU Migration Backend (硬件驱动层) │ │ amdgpu_migrate.c │ │ ┌───────────────────────────────────────────────────┐ │ │ │ drm_pagemap_ops: │ │ │ │ .device_map amdgpu_svm_device_map │ │ │ │ .populate_mm amdgpu_svm_populate_mm │ │ │ │ │ │ │ │ drm_pagemap_devmem_ops: │ │ │ │ .populate_devmem_pfn → BO buddy → PFN 数组 │ │ │ │ .copy_to_devmem → SDMA RAM→VRAM │ │ │ │ .copy_to_ram → SDMA VRAM→RAM │ │ │ │ .devmem_release → 释放 BO │ │ │ │ │ │ │ │ ZONE_DEVICE 注册 地址空间管理 │ │ │ └───────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘2. 三个地址空间整个 migration 涉及三个地址空间的转换理解它们是理解代码的前提VRAM Buddy Allocator [0, real_vram_size) │ │ hpa_base (pgmap.range.start) ▼ HPA / PFN 空间 [hpa_base, hpa_base real_vram_size) ← ZONE_DEVICE struct page 管理 → │ │ vm_manager.vram_base_offset (MMHUB FB_OFFSET) ▼ GPU PTE 地址空间 [vram_base_offset, ...] ← GPU 页表使用的地址 →地址空间范围用途管理者VRAM offset[0, real_vram_size)Buddy 分配器内部偏移amdgpu_vram_mgrHPA/PFN[hpa_base, hpa_basesize)ZONE_DEVICE struct page 索引devm_memremap_pages()PTE address[vram_base_offset, ...]GPU 页表项中的物理地址amdgpu_vm_update_range()地址转换公式:HPApage_to_pfn(page)≪PAGE_SHIFT\text{HPA} \text{page\_to\_pfn}(\text{page}) \ll \text{PAGE\_SHIFT}HPApage_to_pfn(page)≪PAGE_SHIFTVRAM_offsetHPA−hpa_base\text{VRAM\_offset} \text{HPA} - \text{hpa\_base}VRAM_offsetHPA−hpa_basePTE_addrVRAM_offsetvram_base_offset\text{PTE\_addr} \text{VRAM\_offset} \text{vram\_base\_offset}PTE_addrVRAM_offsetvram_base_offset3. 关键数据结构3.1struct amdgpu_pagemap— 设备级 migration 基础设施structamdgpu_pagemap{structdrm_pagemapdpagemap;// DRM pagemap wrapper (device_map/populate_mm)structamdgpu_device*adev;// 反向指针加速查找resource_size_thpa_base;// ZONE_DEVICE 区域的 HPA 起始地址bool initialized;// 初始化完成标记structdev_pagemappgmap;// ZONE_DEVICE 注册结构必须为最后成员};存放位置adev-kfd.apagemap全局唯一per-GPU。3.2struct amdgpu_svm_bo— 每次迁移的临时 BO wrapperstructamdgpu_svm_bo{structamdgpu_bo*bo;// VRAM backing BOstructdrm_pagemap_devmemdevmem;// 框架层的设备内存分配描述};生命周期在populate_mm()中创建在devmem_release()中释放。由框架内部的drm_pagemap_zdd引用计数管理——当所有 device-private 页都迁移回系统内存后zdd refcount 降为零触发devmem_release()。3.3struct drm_pagemap_zdd— 每个 device page 的元数据structdrm_pagemap_zdd{structkrefrefcount;structdrm_pagemap*dpagemap;// 所属 drm_pagemapstructdrm_pagemap_devmem*devmem_allocation;// 指向 svm_bo-devmem};存放在page-zone_device_data中是 ZONE_DEVICE page 到驱动层的桥梁。3.4enum amdgpu_svm_migrate_mode— 迁移意图enumamdgpu_svm_migrate_mode{AMDGPU_SVM_MIGRATE_PREFERRED,// 遵循 preferred_loc 策略AMDGPU_SVM_MIGRATE_TO_VRAM,// 强制迁移到 VRAM (prefetch)AMDGPU_SVM_MIGRATE_TO_SYSMEM,// 回迁到系统内存AMDGPU_SVM_MIGRATE_NONE,// 禁止迁移 (restore)};4. 初始化流程 (amdgpu_svm_migration_init)amdgpu_svm_migration_init(adev) │ ├─ 分配 ZONE_DEVICE 区域 │ ├─ XGMI (connected_to_cpu): 使用 GPU aperture → MEMORY_DEVICE_COHERENT │ └─ Discrete GPU: devm_request_free_mem_region() → MEMORY_DEVICE_PRIVATE │ ├─ pgmap 配置 │ pgmap.ops drm_pagemap_pagemap_ops_get() │ pgmap.owner AMDGPU_SVM_PGMAP_OWNER(adev) │ ├─ XGMI hive: hive 指针 (同 hive 内所有 GPU 共享) │ └─ Standalone: adev-kfd.apagemap (per-device 唯一) │ ├─ devm_memremap_pages(adev-dev, pgmap) │ └─ 注册 ZONE_DEVICE struct page 到 Linux MM │ ├─ drm_pagemap_init(svm_dm-dpagemap, pgmap, drm, amdgpu_svm_drm_pagemap_ops) │ └─ svm_dm-hpa_base pgmap-range.start svm_dm-initialized truepgmap.owner 的作用:migrate_vma_setup()使用 owner 区分自己的 device page 和别人的 device page。owner 相同的页被视为已在目标位置会被跳过。XGMI hive 中所有 GPU 共享一个 owner使 intra-hive 页被视为 local。5. RAM → VRAM 迁移路径完整流程这是最核心的迁移路径由 map path 中的amdgpu_svm_range_migrate_to_vram()发起。5.1 调用链amdgpu_svm_range_map() │ // per-range loop ▼ amdgpu_svm_range_migrate_range(svm, range, attrs, migrate_mode) │ // 三层决策: Capability → Command → Policy ▼ amdgpu_svm_range_migrate_to_vram(svm, range) │ ├─ atomic_set(svm-in_populate, 1) ← 防止 notifier 递归 │ ├─ drm_pagemap_populate_mm(dpagemap, start, end, mm, 0) │ │ │ ├─ mmget_not_zero() mmap_read_lock() │ │ │ └─ dpagemap-ops-populate_mm() │ amdgpu_svm_populate_mm() │ │ │ ├─ amdgpu_svm_bo_alloc(adev, dpagemap, mm, size) │ │ ├─ kzalloc(sizeof(amdgpu_svm_bo)) │ │ ├─ amdgpu_bo_create(VRAM, CONTIGUOUS, NO_CPU_ACCESS) │ │ └─ drm_pagemap_devmem_init(svm_bo-devmem, ...) │ │ │ └─ drm_pagemap_migrate_to_devmem(svm_bo-devmem, mm, start, end) │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ DRM Framework: migrate_to_devmem │ │ │ │ │ │ │ │ 1. migrate_vma_setup() │ │ │ │ └─ Linux MM 锁定源页准备迁移 │ │ │ │ │ │ │ │ 2. populate_devmem_pfn() │ │ │ │ amdgpu_svm_populate_devmem_pfn() │ │ │ │ └─ BO buddy blocks → VRAM PFN array │ │ │ │ │ │ │ │ 3. drm_pagemap_get_devmem_page() │ │ │ │ └─ 设置 zdd → page-zone_device_data │ │ │ │ │ │ │ │ 4. DMA map source pages (系统内存) │ │ │ │ └─ dma_map_page() 或 device_map() │ │ │ │ │ │ │ │ 5. copy_to_devmem() │ │ │ │ amdgpu_svm_copy_to_devmem() │ │ │ │ └─ GART window SDMA: RAM → VRAM │ │ │ │ │ │ │ │ 6. DMA unmap source pages │ │ │ │ │ │ │ │ 7. migrate_vma_pages() │ │ │ │ └─ Linux MM 交换 PTE: system → device │ │ │ │ │ │ │ │ 8. migrate_vma_finalize() │ │ │ │ └─ 释放旧系统页 │ │ │ └─────────────────────────────────────────────┘ │ ├─ atomic_set(svm-in_populate, 0) │ ├─ drm_gpusvm_range_unmap_pages() ← 使缓存的 DMA 地址失效 └─ WRITE_ONCE(range-gpu_mapped, false) ← 强制后续 get_pages 重新获取5.2populate_devmem_pfn— BO → PFN 转换将 VRAM BO 的 buddy block 地址转换为 ZONE_DEVICE PFNlist_for_each_entry(block,ares-blocks,link){u64 block_offsetamdgpu_vram_mgr_block_start(block);// VRAM 内偏移u64 block_pfnPHYS_PFN(block_offsetsvm_dm-hpa_base);// 转为 ZONE_DEVICE PFNu64 block_pagesamdgpu_vram_mgr_block_size(block)PAGE_SHIFT;for(i0;iblock_pagesjnpages;i,j)pfn[j]block_pfni;}5.3 SDMA Copy — GART Window 机制系统内存不能被 SDMA 直接访问discrete GPU需通过 GART window 中转┌────────────────┐ ┌─────────────┐ ┌──────────────┐ │ System RAM │ │ GART Window │ │ VRAM │ │ (DMA mapped) │───────▶│ (PTEs) │───────▶│ (MC addr) │ │ │ DMA │ 映射到 RAM │ SDMA │ 目标位置 │ │ src pages │ addr │ 的 GPU 地址 │ copy │ │ └────────────────┘ └─────────────┘ └──────────────┘流程amdgpu_svm_gart_map()— 将系统 DMA 地址写入 GART PTEs获得 GART 地址amdgpu_copy_buffer()— 提交 SDMA job: GART addr → VRAM MC addr或反向分批处理每批最多AMDGPU_GTT_MAX_TRANSFER_SIZE页// copy_to_devmem 中的批处理逻辑:for(i0,j0;inpages;i){sys[j]pagemap_addr[i].addr;// 系统 DMA 地址vram[j]page_pfn_to_vram_offset(pages[i]);// VRAM 偏移// 检测 VRAM 地址连续性不连续时 flush 当前批次if(j0vram[j]!vram[j-1]PAGE_SIZE)gotoflush;j;continue;flush:amdgpu_svm_copy_memory_gart(adev,sys,vram,j,FROM_RAM_TO_VRAM,fence);j0;// 重新处理当前不连续的页}5.4-EBUSY重试机制当目标区间已有其他 BO 分配的 device-private 页时drm_pagemap_migrate_to_devmem()返回-EBUSY碎片化场景。AMDGPU 通过 evict retry 处理do{retdrm_pagemap_populate_mm(...);if(ret-EBUSYretries){// 先将旧 device pages evict 回 RAMdrm_gpusvm_range_evict(svm-gpusvm,range);}}while(retretries--);drm_gpusvm_range_evict()使用hmm_range_fault(dev_private_ownerNULL)强制将 device-private 页 fault 回系统内存。6. VRAM → RAM 回迁路径有两种回迁触发方式6.1 CPU Page Fault 回迁被动当 CPU 访问已迁移到 VRAM 的 device-private 页时触发 page faultCPU access → page fault → do_swap_page() │ ▼ dev_pagemap_ops.migrate_to_ram() drm_pagemap_migrate_to_ram(vmf) │ ├─ 检查 timeslice: 如果未过期返回 0阻止回迁保护 GPU 使用 │ └─ __drm_pagemap_migrate_to_ram() ├─ migrate_vma_setup(SELECT_DEVICE_PRIVATE | SELECT_DEVICE_COHERENT) ├─ drm_pagemap_migrate_populate_ram_pfn() — 分配目标 RAM 页 ├─ drm_pagemap_migrate_map_pages() — DMA map 目标页 ├─ ops-copy_to_ram() — SDMA: VRAM → RAM │ amdgpu_svm_copy_to_ram() ├─ migrate_vma_pages() — 交换 PTE └─ migrate_vma_finalize() — 清理Timeslice 保护:zdd-devmem_allocation-timeslice_expiration阻止过早回迁。如果 GPU 刚将数据迁移到 VRAMCPU 立刻 fault 可能导致乒乓迁移。timeslice 机制让数据在 VRAM 停留足够长时间。6.2 显式回迁主动当用户通过 ioctl 设置prefetch_locSYSMEM或者 AMDGPU 内部需要释放 VRAM 时amdgpu_svm_range_migrate_range(modeTO_SYSMEM) │ // range_in_vram(range) true ▼ drm_gpusvm_range_evict(svm-gpusvm, range) │ ├─ hmm_range_fault(dev_private_ownerNULL) │ └─ 对每个 device-private 页触发 migrate_to_ram │ └─ drm_pagemap_migrate_to_ram() [同 6.1] │ └─ 等待 hmm_range_fault 完成所有页都回到 RAM6.3drm_pagemap_evict_to_ram()— 无 mmap lock 回迁用于不持有 mmap lock 的场景如设备 unbind使用migrate_device_*API 代替migrate_vma_*drm_pagemap_evict_to_ram(devmem_allocation) ├─ populate_devmem_pfn() — 获取 BO 的 PFN 列表 ├─ migrate_device_pfns() — 锁定 device 页 ├─ 分配 RAM 目标页 ├─ copy_to_ram() — SDMA: VRAM → RAM ├─ migrate_device_pages() — 执行迁移 └─ migrate_device_finalize() — 完成7.device_map()— 迁移后的地址转换迁移完成后页面变为 ZONE_DEVICE (device-private)。后续drm_gpusvm_range_get_pages()需要获取 GPU 可见的地址。对于 device-private 页走device_map()而非dma_map_page()// drm_gpusvm.c — get_pages 中:if(is_device_private_page(page)||is_device_coherent_page(page)){dpagemapdrm_pagemap_page_to_dpagemap(page);svm_pages-dma_addr[j]dpagemap-ops-device_map(dpagemap,gpusvm-drm-dev,page,order,dma_dir);// → 返回 { .addr PTE_addr, .proto AMDGPU_INTERCONNECT_VRAM }}else{addrdma_map_page(gpusvm-drm-dev,page,0,...);svm_pages-dma_addr[j]drm_pagemap_addr_encode(addr,DRM_INTERCONNECT_SYSTEM,...);}amdgpu_svm_device_map()的转换逻辑staticstructdrm_pagemap_addramdgpu_svm_device_map(dpagemap,dev,page,order,dir){if(dpagemap-drm-devdev){// 同设备: 直接地址转换u64 hpapage_to_pfn(page)PAGE_SHIFT;u64 vram_offsethpa-svm_dm-hpa_base;addrvram_offsetadev-vm_manager.vram_base_offset;}else{// P2P: 暂未支持addrDMA_MAPPING_ERROR;}returndrm_pagemap_addr_encode(addr,AMDGPU_INTERCONNECT_VRAM,order,dir);}然后在 GPU PTE 更新时根据协议类型区分// amdgpu_svm_range_update_gpu_range() 中:if(entry-protoAMDGPU_INTERCONNECT_VRAM)seg_pte_flagspte_flags~(AMDGPU_PTE_SYSTEM|AMDGPU_PTE_SNOOPED);else// DRM_INTERCONNECT_SYSTEMseg_pte_flagspte_flags;// 保留 SYSTEM SNOOPED8. Notifier 递归保护 (in_populate)RAM→VRAM 迁移过程中drm_pagemap_populate_mm()内部的migrate_vma_setup()会产生MMU_NOTIFY_MIGRATE事件因为页面正在从 CPU 页表中移除。如果不加保护这个 notifier 会触发 SVM 的 invalidate 回调尝试清除 GPU 映射——但此时迁移尚未完成会导致逻辑混乱。保护方案:// amdgpu_svm_range_migrate_to_vram():atomic_set(svm-in_populate,1);retdrm_pagemap_populate_mm(...);atomic_set(svm-in_populate,0);// amdgpu_svm_range_invalidate():if(mmu_range-eventMMU_NOTIFY_MIGRATEmmu_range-ownerAMDGPU_SVM_PGMAP_OWNER(svm-adev)atomic_read(svm-in_populate))return;// 跳过自身触发的迁移事件双重过滤条件owner匹配确认是同一设备触发的迁移in_populate标记确认当前正在执行 populate9. 迁移后的 Map Path 衔接迁移完成后控制流回到amdgpu_svm_range_map()的 per-range 循环中// Step 2 完成: 页面已在 VRAM 中// Step 3: 获取 DMA 地址drm_gpusvm_range_get_pages(svm-gpusvm,range,map_ctx);// → hmm_range_fault() 发现 device-private 页// → device_map() 返回 VRAM MC 地址 AMDGPU_INTERCONNECT_VRAM// Step 4: 写 GPU 页表amdgpu_svm_range_update_gpu_range(svm,range,pte_flags,...);// → 对 VRAM 页清除 SYSTEM/SNOOPED// → amdgpu_vm_update_range() 写入 GPU PTE注意:migrate_to_vram()结束时调用了drm_gpusvm_range_unmap_pages()gpu_mapped false确保后续get_pages()会重新 fault 获取新的 VRAM DMA 地址而不是使用过期的 RAM DMA 地址。10. 生命周期与引用计数┌─────────────────┐ │ amdgpu_svm_bo │ │ .bo(amdgpu_bo)│ │ .devmem │◄─────────────────┐ └────────┬────────┘ │ │ │ populate_mm() 创建 │ │ devmem_allocation ▼ │ ┌─────────────────┐ ┌────────┴───────┐ │ drm_pagemap_zdd │────────▶│ 每个迁移到 │ │ .dpagemap │ │ VRAM 的 │ │ .devmem_alloc │─────────│ ZONE_DEVICE │ │ .refcount │ │ page │ └────────┬────────┘ └────────────────┘ │ │ │ page-zone_device_data │ zdd (get) │ refcount 降为 0 时: │ drm_pagemap_zdd_destroy() │ devmem_release() amdgpu_svm_devmem_release() │ ├─ amdgpu_bo_unref(svm_bo-bo) └─ kfree(svm_bo)关键: 只要有一个 device-private 页还在zdd refcount 就不为零BO 就不会释放。当所有页都回迁到 RAM或进程退出zdd destroy 触发 BO 释放。11. 完整数据流时序图User/GPU SVM Map Path DRM Framework AMDGPU Backend │ │ │ │ │ prefetchVRAM │ │ │ │─────────────────────▶│ │ │ │ │ │ │ │ migrate_range(TO_VRAM) │ │ │ │ │ │ │ migrate_to_vram() │ │ │ │ in_populate1 │ │ │ │─────────────────────▶│ │ │ │ populate_mm() │ │ │ │ │─────────────────────▶│ │ │ │ populate_mm() │ │ │ │◀─────────────────────│ │ │ │ svm_bo (VRAM BO) │ │ │ │ │ │ │ migrate_to_devmem() │ │ │ │ │ │ │ migrate_vma_setup() │ │ │ │ │ │ │ │─────────────────────▶│ │ │ │ populate_devmem_pfn │ │ │ │◀─────────────────────│ │ │ │ PFN array │ │ │ │ │ │ │ DMA map src (RAM pages) │ │ │ │ │ │ │ │─────────────────────▶│ │ │ │ copy_to_devmem() │ │ │ │ GART map │ │ │ │ SDMA copy │ │ │ │ fence wait │ │ │ │◀─────────────────────│ │ │ │ │ │ │ migrate_vma_pages() │ │ │ migrate_vma_finalize() │ │ │ │ │ │ │◀─────────────────────│ │ │ │ in_populate0 │ │ │ │ │ │ │ unmap_pages() (invalidate old DMA) │ │ gpu_mapped false │ │ │ │ │ │ get_pages() │ │ │ │ hmm_range_fault │ │ │ │ → device_private │ │ │ │ │─────────────────────▶│ │ │ │ device_map() │ │ │ │ page → PTE addr │ │ │ │◀─────────────────────│ │ │ │ AMDGPU_INTERCONNECT │ │ │ │ _VRAM │ │ │ │ │ │ update_gpu_range() │ │ │ (VRAM PTE: no SYSTEM/SNOOPED) │ │ │ │ │ │ │◀─────────────────────│ │ │ │ GPU accesses VRAM directly │ │12. 设计亮点总结设计决策技术理由GART window 做 SDMA 中转Discrete GPU 的 SDMA 不能直接访问系统内存GART 提供 GPU 可见的中间地址连续性检测 批量 SDMAcopy_to_devmem/ram中检测 VRAM 地址连续性合并连续页为单次 SDMA 拷贝减少 job 提交开销BO-per-migration每次迁移分配独立 BO避免复杂的 VRAM 空间共享管理。BO 通过 zdd refcount 自动释放ZONE_DEVICE 统一管理利用 Linux MM 的device-privatepage 机制迁移后的 VRAM 页在 CPU 侧有对应 struct page可被 MM 子系统统一管理in_populate防递归单个 atomic 标记 owner 双重过滤精准跳过自身触发的 MMU notifier-EBUSYevictretry优雅处理碎片化目标区间有旧 device page 时先 evict 再重试最多 3 次Timeslice 防乒乓CPU fault 回迁前检查 timeslice防止 GPU 刚迁移完就被 CPU 抢回AMDGPU_INTERCONNECT_VRAM协议在drm_pagemap_addr中区分 SYSTEM 和 VRAM让 PTE 更新逻辑自动选择正确的 flag 组合三层迁移决策Capability → Command → Policy 分层逻辑清晰各层独立可扩展13. 文件职责对照文件层级职责amdgpu_svm_range_migrate.h接口enum amdgpu_svm_migrate_mode定义amdgpu_svm_range_migrate.c决策层三层迁移决策 migrate_to_vram()执行amdgpu_migrate.h接口AMDGPU_INTERCONNECT_VRAM,AMDGPU_SVM_PGMAP_OWNERamdgpu_migrate.c硬件驱动层drm_pagemap_opsdrm_pagemap_devmem_ops实现, SDMA copy, ZONE_DEVICE 注册amdgpu_amdkfd.h数据结构struct amdgpu_pagemap定义drm_pagemap.cDRM 框架migrate_to_devmem(),evict_to_ram(),migrate_to_ram(), zdd 管理drm_gpusvm.cDRM 框架get_pages()中的device_map()调用, range 管理,evict()