AMD 正在使用 drm svm框架重构SVM的实现,看来drm svm框架要进入大范围应用了。下面是在kernel社区上由AMD的开发人员提交的POC 验证版本的patches的技术方案实现。这里快速总结了实现,以飨读者。
因是POC版本,设计可能会变动,读者们慎重使用。本文仅用来跟踪前沿驱动技术的迭代发展现状。
1. 总体架构概览
AMDGPU SVM 的 VRAM migration 建立在 Linux DRM 子系统的 drm_pagemap 框架之上。整体分为三层:
┌─────────────────────────────────────────────────────────┐
│ AMDGPU SVM Map Path (Caller) │
│ amdgpu_svm_range_migrate.c │
│ ┌───────────────────────────────────────────────────┐ │
│ │ migrate_range() → migrate_to_vram() │ │
│ │ 3-layer migration decision: │ │
| | Capability→Command→Policy │ │
│ └───────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ DRM Pagemap Framework (Generic Layer) │
│ 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 migrate) │ │
│ │ drm_pagemap_evict_to_ram() (explicit eviction) │ │
│ │ │ │
│ │ drm_gpusvm_range_get_pages() + device_map() │ │
│ └───────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ AMDGPU Migration Backend (HW Driver Layer) │
│ amdgpu_migrate.c │
│ ┌───────────────────────────────────────────────────┐ │
│ │ ZONE_DEVICE register + address space management │ │
│ │ │ │
│ │ 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 array │ │
│ │ .copy_to_devmem → SDMA RAM→VRAM │ │
│ │ .copy_to_ram → SDMA VRAM→RAM │ │
│ │ .devmem_release → release BO │ │
│ │ │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
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_mgr |
| HPA/PFN | [hpa_base, hpa_base+size) |
ZONE_DEVICE struct page 索引 | devm_memremap_pages() |
| PTE address | [vram_base_offset, ...] |
GPU 页表项中的物理地址 | amdgpu_vm_update_range() |
地址转换公式:
HPA=page_to_pfn(page)≪PAGE_SHIFT\text{HPA} = \text{page\_to\_pfn}(\text{page}) \ll \text{PAGE\_SHIFT}HPA=page_to_pfn(page)≪PAGE_SHIFT
VRAM_offset=HPA−hpa_base\text{VRAM\_offset} = \text{HPA} - \text{hpa\_base}VRAM_offset=HPA−hpa_base
PTE_addr=VRAM_offset+vram_base_offset\text{PTE\_addr} = \text{VRAM\_offset} + \text{vram\_base\_offset}PTE_addr=VRAM_offset+vram_base_offset
3. 关键数据结构
3.1 struct amdgpu_pagemap --- 设备级 migration 基础设施
c
struct amdgpu_pagemap {
struct drm_pagemap dpagemap; // DRM pagemap wrapper (device_map/populate_mm)
struct amdgpu_device *adev; // 反向指针,加速查找
resource_size_t hpa_base; // ZONE_DEVICE 区域的 HPA 起始地址
bool initialized; // 初始化完成标记
struct dev_pagemap pgmap; // ZONE_DEVICE 注册结构(必须为最后成员)
};
存放位置:adev->kfd.apagemap,全局唯一(per-GPU)。
3.2 struct amdgpu_svm_bo --- 每次迁移的临时 BO wrapper
c
struct amdgpu_svm_bo {
struct amdgpu_bo *bo; // VRAM backing BO
struct drm_pagemap_devmem devmem; // 框架层的设备内存分配描述
};
生命周期 :在 populate_mm() 中创建,在 devmem_release() 中释放。由框架内部的 drm_pagemap_zdd 引用计数管理------当所有 device-private 页都迁移回系统内存后,zdd refcount 降为零,触发 devmem_release()。
3.3 struct drm_pagemap_zdd --- 每个 device page 的元数据
c
struct drm_pagemap_zdd {
struct kref refcount;
struct drm_pagemap *dpagemap; // 所属 drm_pagemap
struct drm_pagemap_devmem *devmem_allocation; // 指向 svm_bo->devmem
};
存放在 page->zone_device_data 中,是 ZONE_DEVICE page 到驱动层的桥梁。
3.4 enum amdgpu_svm_migrate_mode --- 迁移意图
c
enum amdgpu_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 = true
pgmap.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.2 populate_devmem_pfn --- BO → PFN 转换
将 VRAM BO 的 buddy block 地址转换为 ZONE_DEVICE PFN:
c
list_for_each_entry(block, &ares->blocks, link) {
u64 block_offset = amdgpu_vram_mgr_block_start(block); // VRAM 内偏移
u64 block_pfn = PHYS_PFN(block_offset + svm_dm->hpa_base); // 转为 ZONE_DEVICE PFN
u64 block_pages = amdgpu_vram_mgr_block_size(block) >> PAGE_SHIFT;
for (i = 0; i < block_pages && j < npages; i++, j++)
pfn[j] = block_pfn + i;
}
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页
c
// copy_to_devmem 中的批处理逻辑:
for (i = 0, j = 0; i < npages; i++) {
sys[j] = pagemap_addr[i].addr; // 系统 DMA 地址
vram[j] = page_pfn_to_vram_offset(pages[i]); // VRAM 偏移
// 检测 VRAM 地址连续性,不连续时 flush 当前批次
if (j > 0 && vram[j] != vram[j-1] + PAGE_SIZE)
goto flush;
j++;
continue;
flush:
amdgpu_svm_copy_memory_gart(adev, sys, vram, j, FROM_RAM_TO_VRAM, &fence);
j = 0;
// 重新处理当前不连续的页
}
5.4 -EBUSY 重试机制
当目标区间已有其他 BO 分配的 device-private 页时,drm_pagemap_migrate_to_devmem() 返回 -EBUSY(碎片化场景)。AMDGPU 通过 evict + retry 处理:
c
do {
ret = drm_pagemap_populate_mm(...);
if (ret == -EBUSY && retries) {
// 先将旧 device pages evict 回 RAM
drm_gpusvm_range_evict(&svm->gpusvm, range);
}
} while (ret && retries--);
drm_gpusvm_range_evict() 使用 hmm_range_fault(dev_private_owner=NULL) 强制将 device-private 页 fault 回系统内存。
6. VRAM → RAM 回迁路径
有两种回迁触发方式:
6.1 CPU Page Fault 回迁(被动)
当 CPU 访问已迁移到 VRAM 的 device-private 页时,触发 page fault:
CPU 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 刚将数据迁移到 VRAM,CPU 立刻 fault 可能导致"乒乓迁移"。timeslice 机制让数据在 VRAM 停留足够长时间。
6.2 显式回迁(主动)
当用户通过 ioctl 设置 prefetch_loc=SYSMEM 或者 AMDGPU 内部需要释放 VRAM 时:
amdgpu_svm_range_migrate_range(mode=TO_SYSMEM)
│ // range_in_vram(range) == true
▼
drm_gpusvm_range_evict(&svm->gpusvm, range)
│
├─ hmm_range_fault(dev_private_owner=NULL)
│ └─ 对每个 device-private 页触发 migrate_to_ram
│ └─ drm_pagemap_migrate_to_ram() [同 6.1]
│
└─ 等待 hmm_range_fault 完成:所有页都回到 RAM
6.3 drm_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():
c
// drm_gpusvm.c --- get_pages 中:
if (is_device_private_page(page) || is_device_coherent_page(page)) {
dpagemap = drm_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 {
addr = dma_map_page(gpusvm->drm->dev, page, 0, ...);
svm_pages->dma_addr[j] = drm_pagemap_addr_encode(addr, DRM_INTERCONNECT_SYSTEM, ...);
}
amdgpu_svm_device_map() 的转换逻辑:
c
static struct drm_pagemap_addr
amdgpu_svm_device_map(dpagemap, dev, page, order, dir)
{
if (dpagemap->drm->dev == dev) {
// 同设备: 直接地址转换
u64 hpa = page_to_pfn(page) << PAGE_SHIFT;
u64 vram_offset = hpa - svm_dm->hpa_base;
addr = vram_offset + adev->vm_manager.vram_base_offset;
} else {
// P2P: 暂未支持
addr = DMA_MAPPING_ERROR;
}
return drm_pagemap_addr_encode(addr, AMDGPU_INTERCONNECT_VRAM, order, dir);
}
然后在 GPU PTE 更新时根据协议类型区分:
c
// amdgpu_svm_range_update_gpu_range() 中:
if (entry->proto == AMDGPU_INTERCONNECT_VRAM)
seg_pte_flags = pte_flags & ~(AMDGPU_PTE_SYSTEM | AMDGPU_PTE_SNOOPED);
else // DRM_INTERCONNECT_SYSTEM
seg_pte_flags = pte_flags; // 保留 SYSTEM + SNOOPED
8. Notifier 递归保护 (in_populate)
RAM→VRAM 迁移过程中,drm_pagemap_populate_mm() 内部的 migrate_vma_setup() 会产生 MMU_NOTIFY_MIGRATE 事件,因为页面正在从 CPU 页表中移除。如果不加保护,这个 notifier 会触发 SVM 的 invalidate 回调,尝试清除 GPU 映射------但此时迁移尚未完成,会导致逻辑混乱。
保护方案:
c
// amdgpu_svm_range_migrate_to_vram():
atomic_set(&svm->in_populate, 1);
ret = drm_pagemap_populate_mm(...);
atomic_set(&svm->in_populate, 0);
// amdgpu_svm_range_invalidate():
if (mmu_range->event == MMU_NOTIFY_MIGRATE &&
mmu_range->owner == AMDGPU_SVM_PGMAP_OWNER(svm->adev) &&
atomic_read(&svm->in_populate))
return; // 跳过自身触发的迁移事件
双重过滤条件:
owner匹配:确认是同一设备触发的迁移in_populate标记:确认当前正在执行 populate
9. 迁移后的 Map Path 衔接
迁移完成后,控制流回到 amdgpu_svm_range_map() 的 per-range 循环中:
c
// 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
│ │ │ │
│ prefetch=VRAM │ │ │
│─────────────────────▶│ │ │
│ │ │ │
│ migrate_range(TO_VRAM) │ │
│ │ │ │
│ migrate_to_vram() │ │
│ │ in_populate=1 │ │
│ │─────────────────────▶│ │
│ │ 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_populate=0 │ │
│ │ │ │
│ 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 可见的中间地址 |
| 连续性检测 + 批量 SDMA | copy_to_devmem/ram 中检测 VRAM 地址连续性,合并连续页为单次 SDMA 拷贝,减少 job 提交开销 |
| BO-per-migration | 每次迁移分配独立 BO,避免复杂的 VRAM 空间共享管理。BO 通过 zdd refcount 自动释放 |
| ZONE_DEVICE 统一管理 | 利用 Linux MM 的 device-private page 机制,迁移后的 VRAM 页在 CPU 侧有对应 struct page,可被 MM 子系统统一管理 |
in_populate 防递归 |
单个 atomic 标记 + owner 双重过滤,精准跳过自身触发的 MMU notifier |
-EBUSY evict+retry |
优雅处理碎片化:目标区间有旧 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_OWNER |
amdgpu_migrate.c |
硬件驱动层 | drm_pagemap_ops + drm_pagemap_devmem_ops 实现, SDMA copy, ZONE_DEVICE 注册 |
amdgpu_amdkfd.h |
数据结构 | struct amdgpu_pagemap 定义 |
drm_pagemap.c |
DRM 框架 | migrate_to_devmem(), evict_to_ram(), migrate_to_ram(), zdd 管理 |
drm_gpusvm.c |
DRM 框架 | get_pages() 中的 device_map() 调用, range 管理, evict() |