drm_pagemap: CPU 访问设备内存页面的迁回路径

1. 概述

当 CPU 访问一个已迁移到设备内存(如 GPU VRAM)的虚拟地址时,由于该页面在CPU 页表中是 DEVICE_PRIVATE swap entry(非 present),硬件触发 page fault,内核通过两级回调机制将数据从设备内存拷回系统 RAM:

  • 第一级dev_pagemap_ops.migrate_to_ram --- DRM pagemap 框架处理通用逻辑
  • 第二级drm_pagemap_devmem_ops.copy_to_ram --- GPU 驱动提供硬件特定的数据拷贝

这种设计将迁移流程的编排 (页面收集、RAM 分配、DMA mapping、页表更新)与数据搬运(硬件 DMA 引擎拷贝)解耦,使不同 GPU 驱动共享同一套迁移框架。

2. 完整调用链

复制代码
CPU 访问虚拟地址 (该地址对应 DEVICE_PRIVATE 页面)
  │
  ▼
① 硬件 page fault (PTE 中是 swap entry, 非 present)
  │
  ▼
② do_swap_page()                              [mm/memory.c]
  │  检测到 softleaf_is_device_private(entry)
  │  从 swap entry 取出 vmf->page
  │  获取 pgmap = page_pgmap(vmf->page)
  │
  ▼
③ pgmap->ops->migrate_to_ram(vmf)             [mm/memory.c]
  │
  │  pgmap->ops 由驱动初始化时设置:
  │    pgmap->ops = drm_pagemap_pagemap_ops_get()
  │  即 drm_pagemap_pagemap_ops = {
  │    .migrate_to_ram = drm_pagemap_migrate_to_ram,
  │    .folio_free     = drm_pagemap_folio_free,
  │  }
  │
  ▼
④ drm_pagemap_migrate_to_ram(vmf)             [drm_pagemap.c]
  │  从 vmf->page->zone_device_data 取 zdd
  │  zdd->devmem_allocation->size 决定迁移粒度
  │
  ▼
⑤ __drm_pagemap_migrate_to_ram(vma, page, fault_addr, size)
  │                                            [drm_pagemap.c]
  │
  │  a) timeslice 检查 --- 若页面刚迁移到设备不久,跳过迁回(防止抖动)
  │  b) migrate_vma_setup() --- 收集需要迁移的源页面(设备页面)
  │  c) 从 zdd 获取驱动 ops:
  │       ops = zdd->devmem_allocation->ops   (驱动注册的 devmem_ops)
  │       dev = zdd->devmem_allocation->dev
  │  d) drm_pagemap_migrate_populate_ram_pfn() --- 分配目标 RAM 页面
  │  e) drm_pagemap_migrate_map_pages() --- DMA map 目标 RAM 页面
  │  f) 构建 pages[] 数组(源设备页面)
  │
  ▼
⑥ ops->copy_to_ram(pages, pagemap_addr, npages, NULL)
  │                                            [drm_pagemap.c]
  │  调用驱动注册的 copy_to_ram 回调
  │  驱动通过硬件 DMA 引擎将数据从设备内存拷贝到 RAM
  │
  ▼
⑦ 驱动 copy_to_ram 实现                       [GPU 驱动]
  │  a) 从 pages[0] 获取驱动上下文(设备、地址基准等)
  │  b) 构建源地址(设备内存偏移)和目标地址(DMA 地址)数组
  │  c) 将连续页面合并为批次,提交硬件 DMA 拷贝
  │  d) 等待 DMA 完成
  │
  ▼
⑧ 返回 __drm_pagemap_migrate_to_ram():
     migrate_vma_pages()    --- 更新页表,swap entry → 新 RAM page
     migrate_vma_finalize() --- 清理旧设备页面引用
     返回 0(成功)→ CPU 重新执行访问指令

3.关键数据结构的关联

复制代码
ZONE_DEVICE page
  │
  ├── page_pgmap(page) ──→ dev_pagemap
  │                           └── ops = drm_pagemap_pagemap_ops  (第一级: 框架回调)
  │                                       ├── .migrate_to_ram
  │                                       └── .folio_free
  │
  └── page->zone_device_data ──→ drm_pagemap_zdd
                                    ├── dpagemap ──→ struct drm_pagemap
                                    │                  ├── ops (drm_pagemap_ops)
                                    │                  ├── drm (struct drm_device)
                                    │                  └── pagemap
                                    │
                                    └── devmem_allocation ──→ struct drm_pagemap_devmem
                                          ├── ops   (第二级: 驱动回调)
                                          │    ├── .copy_to_ram
                                          │    ├── .copy_to_devmem
                                          │    ├── .populate_devmem_pfn
                                          │    └── .devmem_release
                                          ├── dev
                                          ├── dpagemap
                                          ├── size  (迁移粒度)
                                          └── timeslice_expiration

4. 两级回调设计

4.1 第一级:dev_pagemap_ops(内核 mm → DRM 框架)

回调 实现 触发时机 职责
migrate_to_ram drm_pagemap_migrate_to_ram() CPU page fault 编排整个迁移流程
folio_free drm_pagemap_folio_free() 页面释放 释放 zdd 引用计数

这一级由 DRM pagemap 框架统一提供,所有使用该框架的 GPU 驱动共享同一实现。

驱动在初始化 dev_pagemap 时调用:

c 复制代码
pgmap->ops = drm_pagemap_pagemap_ops_get();

4.2 第二级:drm_pagemap_devmem_ops(DRM 框架 → GPU 驱动)

回调 触发时机 职责
copy_to_ram 设备页面迁回 RAM 硬件 DMA 拷贝:设备内存 → RAM
copy_to_devmem RAM 页面迁移到设备 硬件 DMA 拷贝:RAM → 设备内存
populate_devmem_pfn 迁移到设备前 将设备内存分配转为 PFN 数组
devmem_release 所有页面迁回后 释放设备内存分配

这一级由各 GPU 驱动各自实现,通过 drm_pagemap_devmem_init() 注册:

c 复制代码
drm_pagemap_devmem_init(&devmem, dev, mm, &driver_devmem_ops, dpagemap, size, fence);

5. 设计优势

复制代码
┌─────────────────────────────────────────────────────────────┐
│                     内核 mm 子系统                            │
│  do_swap_page() → pgmap->ops->migrate_to_ram()              │
└──────────────────────────┬──────────────────────────────────┘
                           │  第一级回调
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                   DRM pagemap 框架                           │
│  页面收集 → RAM 分配 → DMA mapping → 调用驱动 → 页表更新         │
│                                                             │
│  统一处理: migrate_vma, timeslice, zdd 生命周期管理            │
└──────────────────────────┬──────────────────────────────────┘
                           │  第二级回调
              ┌────────────┼────────────┐
              ▼            ▼            ▼
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │ 驱动 A    │ │ 驱动 B   │ │ 驱动 C    │
        │ (SDMA)   │ │ (CE)     │ │ (...)    │
        │copy_to_  │ │copy_to_  │ │copy_to_  │
        │  ram()   │ │  ram()   │ │  ram()   │
        └──────────┘ └──────────┘ └──────────┘
  • 关注点分离:框架管编排,驱动管拷贝
  • 代码复用:页面收集、RAM 分配、DMA mapping、页表更新等通用逻辑只写一次
  • 防抖动:timeslice 机制由框架统一实现,驱动无需关心
  • 生命周期:zdd 引用计数和 devmem_release 回调保证资源正确释放

6. 迁移流程中的地址空间

复制代码
设备内存偏移  [0, device_mem_size)       ← 设备内存管理器(buddy/slab 等)
    + hpa_base
HPA / PFN     [hpa_base, hpa_base+..)   ← ZONE_DEVICE struct page 管理
                                            (devm_memremap_pages 注册)

驱动在 copy_to_ram / copy_to_devmem 中需要完成:
  设备页面 PFN → HPA → 设备内存偏移 → 硬件可用的 DMA 源地址

7. copy_to_ram 全部调用站点

框架中有三处调用 ops->copy_to_ram(),对应不同的迁移场景:

调用站点 场景 触发条件
__drm_pagemap_migrate_to_ram() CPU page fault 迁回 CPU 访问 DEVICE_PRIVATE 页面
drm_pagemap_evict_to_ram() 主动驱逐 设备内存压力或进程退出
drm_pagemap_migrate_remote_to_local() P2P 迁移中间步骤 跨设备迁移,先从源设备迁回 RAM

所有路径共享同一个驱动回调,驱动实现只需关心"把这些设备页面的数据拷到这些 DMA 地址",不需要区分是哪种场景触发的迁移。

相关推荐
DeeplyMind2 天前
linux中的HMM vs drm_pagemap 对比分析
hmm·drm_gpusvm·drm_pagemap·dev_pagemap·hmm_range
DeeplyMind6 天前
AMDGPU 基于DRM SVM框架的新SVM功能实现 :attr_range 与 svm_range 的对应关系分析
drm_gpusvm·drm_pagemap