AMDGPU SVM VRAM Migration 实现详解

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  │              │
└────────────────┘        └─────────────┘        └──────────────┘

流程

  1. amdgpu_svm_gart_map() --- 将系统 DMA 地址写入 GART PTEs,获得 GART 地址
  2. amdgpu_copy_buffer() --- 提交 SDMA job: GART addr → VRAM MC addr(或反向)
  3. 分批处理:每批最多 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;   // 跳过自身触发的迁移事件

双重过滤条件:

  1. owner 匹配:确认是同一设备触发的迁移
  2. 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()
相关推荐
DeeplyMind2 天前
05 - AMDGPU中的VRAM管理器
drm·amdgpu·drm_buddy·ttm
DeeplyMind4 天前
AMDGPU SVM Range Restore 机制
amdgpu·drm_gpusvm
DeeplyMind15 天前
drm_pagemap: CPU 访问设备内存页面的迁回路径
drm_pagemap·dev_pagemap
DeeplyMind17 天前
linux中的HMM vs drm_pagemap 对比分析
hmm·drm_gpusvm·drm_pagemap·dev_pagemap·hmm_range
DeeplyMind21 天前
AMDGPU 基于DRM SVM框架的新SVM功能实现 :attr_range 与 svm_range 的对应关系分析
drm_gpusvm·drm_pagemap
DeeplyMind2 个月前
11 - SVM的高级特性:多GPU支持
svm·amdgpu·rocm·kfd
DeeplyMind2 个月前
09 - SVM缺页处理机制
svm·amdgpu·rocm·kfd·rocr
DeeplyMind2 个月前
07 - SVM内存迁移机制
svm·amdgpu·rocm·kfd·rocr
DeeplyMind2 个月前
06 - SVM范围管理
svm·amdgpu·rocm·kfd