news 2026/5/8 19:45:50

第六章:异步访问的同步:6.3.1 dma_resv_usage 层级机制详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第六章:异步访问的同步:6.3.1 dma_resv_usage 层级机制详解

1. 概述

dma_resv(DMA reservation object)是 Linux 内核中管理 GPU buffer 同步的核心机制。每个dma_resv对象维护一组dma_fence,用于追踪对该 buffer 的各种操作。

enum dma_resv_usage定义了 fence 的用途级别,控制"谁能看到这个 fence"。这套层级机制是理解 TTM 内存管理、GPU 命令提交、隐式同步的关键。


2. 四个 Usage 级别

enumdma_resv_usage{DMA_RESV_USAGE_KERNEL,// 0 — 内核内存管理DMA_RESV_USAGE_WRITE,// 1 — 隐式写同步DMA_RESV_USAGE_READ,// 2 — 隐式读同步DMA_RESV_USAGE_BOOKKEEP,// 3 — 无隐式同步(仅记账)};

2.1 KERNEL — 内核内存管理操作

级别值: 0(最低/最严格) 语义: 内核内部的内存管理 DMA 操作 场景: BO 搬迁(move)、内存清零(clear)、页表更新

谁应该等待 KERNEL fence?
所有人。任何访问该 buffer 的代码都必须先等待 KERNEL fence 完成。
唯一的例外是 buffer 已被 pin 住(位置锁定)的情况。

amdgpu 实例

// TTM 搬迁时添加 KERNEL fencedma_resv_add_fence(bo->base.resv,fence,DMA_RESV_USAGE_KERNEL);// VM 页表更新等待 KERNEL fencedma_resv_wait_timeout(bo->tbo.base.resv,DMA_RESV_USAGE_KERNEL,...);

2.2 WRITE — 隐式写同步

级别值: 1 语义: 用户空间命令提交的写操作 场景: GPU 渲染写入 render target、compute shader 写入 buffer

amdgpu 实例(命令提交amdgpu_cs.c):

// gang leader 的 fence 作为 WRITE 添加dma_resv_add_fence(gobj->resv,p->fence,DMA_RESV_USAGE_WRITE);

2.3 READ — 隐式读同步

级别值: 2 语义: 用户空间命令提交的读操作 场景: GPU 采样纹理、读取 uniform buffer

amdgpu 实例(命令提交amdgpu_cs.c):

// gang 非 leader 的 fence 作为 READ 添加dma_resv_add_fence(gobj->resv,&p->jobs[i]->base.s_fence->finished,DMA_RESV_USAGE_READ);

2.4 BOOKKEEP — 无隐式同步

级别值: 3(最高/最宽松) 语义: 不参与隐式同步,仅内部记账 场景: 抢占 fence、页表更新、TLB flush、eviction fence

BOOKKEEP fence不会被隐式同步查询(READ、WRITE 级别)看到。
只有显式使用DMA_RESV_USAGE_BOOKKEEP级别查询时才会被返回。

KFD eviction fence 实例amdgpu_amdkfd_gpuvm.c):

dma_resv_add_fence(vm->root.bo->tbo.base.resv,&vm->process_info->eviction_fence->base,DMA_RESV_USAGE_BOOKKEEP);

3. 层级包含规则

3.1 核心规则

查询某个级别的 fence 时,该级别及所有更低级别的 fence 都会被返回。

这是由数值大小决定的:

KERNEL(0)<WRITE(1)<READ(2)<BOOKKEEP(3)\text{KERNEL}(0) < \text{WRITE}(1) < \text{READ}(2) < \text{BOOKKEEP}(3)KERNEL(0)<WRITE(1)<READ(2)<BOOKKEEP(3)

查询 KERNEL → 返回 KERNEL 查询 WRITE → 返回 KERNEL + WRITE 查询 READ → 返回 KERNEL + WRITE + READ 查询 BOOKKEEP → 返回 KERNEL + WRITE + READ + BOOKKEEP(全部)

3.2 实现原理

fence 列表中每个条目存储的是(fence_ptr | usage),usage 编码在指针的低 2 位。

迭代器的过滤逻辑(dma-resv.c):

staticvoiddma_resv_iter_walk_unlocked(structdma_resv_iter*cursor){do{dma_resv_list_entry(cursor->fences,cursor->index++,cursor->obj,&cursor->fence,&cursor->fence_usage);// ...if(!dma_fence_is_signaled(cursor->fence)&&cursor->usage>=cursor->fence_usage)// ← 关键过滤条件break;}while(true);}

过滤条件cursor->usage >= cursor->fence_usage的含义:

  • 查询级别(cursor->usage)大于等于fence 的实际级别(fence_usage)时,该 fence 被返回
  • 例:查询 READ(2),fence 级别为 WRITE(1) → 2 ≥ 1 → ✅ 返回
  • 例:查询 WRITE(1),fence 级别为 READ(2) → 1 ≥ 2 → ❌ 跳过

3.3 可见性矩阵

查询级别 ↓ / Fence 级别 →KERNELWRITEREADBOOKKEEP
KERNEL
WRITE
READ
BOOKKEEP

4. Fence 升降级规则

4.1 可以升级(提升可见性)

// 原来是 BOOKKEEP,再次添加为 READ → 升级为 READdma_resv_add_fence(resv,fence,DMA_RESV_USAGE_BOOKKEEP);// fence 级别 = 3dma_resv_add_fence(resv,fence,DMA_RESV_USAGE_READ);// fence 级别 → 2

升级意味着:数值变小 → 被更多查询可见 → 参与更多同步。

4.2 不能降级(降低可见性)

// 原来是 WRITE,再次添加为 READ → 不变,仍为 WRITEdma_resv_add_fence(resv,fence,DMA_RESV_USAGE_WRITE);// fence 级别 = 1dma_resv_add_fence(resv,fence,DMA_RESV_USAGE_READ);// fence 级别仍然 = 1

实现(dma_resv_add_fence):

for(i=0;i<count;++i){dma_resv_list_entry(fobj,i,obj,&old,&old_usage);if((old->context==fence->context&&old_usage>=usage&&dma_fence_is_later_or_same(fence,old))||dma_fence_is_signaled(old)){dma_resv_list_set(fobj,i,fence,usage);// 替换return;}}

替换条件中old_usage >= usage确保:只有当旧 fence 的 usage 值 ≥ 新 usage 值时
(即旧 fence 可见性 ≤ 新 fence 可见性),才执行替换。


5. 主要 API

5.1 添加 Fence

// 预留 slot(不可失败的后续 add 需要预先分配)intdma_resv_reserve_fences(structdma_resv*obj,unsignedintnum_fences);// 添加 fence(必须先 reserve,且持有 resv lock)voiddma_resv_add_fence(structdma_resv*obj,structdma_fence*fence,enumdma_resv_usageusage);

5.2 等待 Fence

// 等待指定级别及以下的所有 fencelongdma_resv_wait_timeout(structdma_resv*obj,enumdma_resv_usageusage,bool intr,unsignedlongtimeout);// 返回值: >0 成功, 0 超时, <0 错误

5.3 测试 Fence 状态

// 检查指定级别及以下的所有 fence 是否都已 signaledbooldma_resv_test_signaled(structdma_resv*obj,enumdma_resv_usageusage);

5.4 遍历 Fence

// 需要持锁遍历structdma_resv_itercursor;structdma_fence*fence;dma_resv_for_each_fence(&cursor,obj,DMA_RESV_USAGE_READ,fence){// fence 的 usage <= READ 的都会被遍历到}// 无锁遍历(RCU 保护,可能 restart)dma_resv_for_each_fence_unlocked(&cursor,fence){if(dma_resv_iter_is_restarted(&cursor))// 处理 restart}

5.5 获取所有 Fence

// 获取指定级别及以下的所有 fence(返回数组,调用者负责释放)intdma_resv_get_fences(structdma_resv*obj,enumdma_resv_usageusage,unsignedint*num_fences,structdma_fence***fences);

5.6 辅助函数

// 隐式同步用:写操作需要等读+写,读操作只需等写staticinlineenumdma_resv_usagedma_resv_usage_rw(bool write){returnwrite?DMA_RESV_USAGE_READ:DMA_RESV_USAGE_WRITE;}

这个看似"反直觉"的映射逻辑:

  • 新写操作(write=true)→ 返回READ(2) → 等待 KERNEL + WRITE + READ → 等所有读写完成
  • 新读操作(write=false)→ 返回WRITE(1) → 等待 KERNEL + WRITE → 只等写完成

6. TTM 如何使用 Usage 层级

6.1 TTM 搬迁时添加 KERNEL fence

// ttm_bo.c — ttm_bo_handle_move_memdma_resv_add_fence(bo->base.resv,fence,DMA_RESV_USAGE_KERNEL);

搬迁产生的 DMA 操作是内核内部行为,必须被所有后续操作等待。

6.2 TTM eviction 等待所有 fence

// ttm_bo.c — ttm_bo_wait_ctxintttm_bo_wait_ctx(structttm_buffer_object*bo,structttm_operation_ctx*ctx){ret=dma_resv_wait_timeout(bo->base.resv,DMA_RESV_USAGE_BOOKKEEP,ctx->interruptible,15*HZ);}

TTM 在驱逐 BO 前用BOOKKEEP级别等待 →等待所有 fence(包括 BOOKKEEP)
这确保了:

  • 所有 GPU 命令已完成(WRITE/READ fence)
  • 所有内核搬迁已完成(KERNEL fence)
  • 所有内部记账 fence 已完成(BOOKKEEP fence)

6.3 DISCARDABLE BO 的驱逐流程

ttm_bo_evict(bo) // num_placement = 0 (DISCARDABLE) → ttm_bo_wait_ctx(bo) // 等 BOOKKEEP 级(全部 fence) → dma_resv_wait_timeout(..., DMA_RESV_USAGE_BOOKKEEP, ...) → 遍历所有 fence,等待每一个 → ttm_bo_pipeline_gutting(bo) // 丢弃 BO 内容

7. 实际应用场景

7.1 GPU 命令提交(隐式同步)

// amdgpu_cs.c — 提交完成后在每个引用的 BO 上添加 fence// 所有 gang member 的 fence → READ(其他人能并行读)dma_resv_add_fence(gobj->resv,&p->jobs[i]->base.s_fence->finished,DMA_RESV_USAGE_READ);// gang leader 的 fence → WRITE(后续读写都要等它)dma_resv_add_fence(gobj->resv,p->fence,DMA_RESV_USAGE_WRITE);

后续对同一 BO 的操作会按需等待:

  • 另一个写操作提交时 → 查询 READ → 等待所有读+写 fence
  • 另一个读操作提交时 → 查询 WRITE → 只等写 fence

7.2 DMA-BUF 导出/隐式同步

// dma-buf.c — 用户空间 DMA_BUF_IOCTL_SYNCusage=(arg.flags&DMA_BUF_SYNC_WRITE)?DMA_RESV_USAGE_WRITE:DMA_RESV_USAGE_READ;dma_resv_wait_timeout(dmabuf->resv,dma_resv_usage_rw(write),...);

跨设备共享 buffer 时,隐式同步依赖 WRITE/READ fence。
BOOKKEEP fence 不参与 → 不会阻塞跨设备共享。

7.3 SVM Eviction Fence

dma_resv_reserve_fences(bo->tbo.base.resv,1);dma_resv_add_fence(bo->tbo.base.resv,&evict_fence->base,DMA_RESV_USAGE_BOOKKEEP);

选择 BOOKKEEP 的原因

  1. 语义正确:eviction fence 是内部管理用途,不代表 GPU 读写操作
  2. 不干扰隐式同步:READ/WRITE 级别的查询看不到这个 fence
  3. TTM 驱逐时可见ttm_bo_wait_ctx使用 BOOKKEEP 查询 → 能等到我们的 fence

7.4 Xe 驱动中的 Page Table 更新

// xe_vm.c — 页表更新 fence 使用 BOOKKEEPdma_resv_add_fence(xe_vm_resv(vm),&fence->base,DMA_RESV_USAGE_BOOKKEEP);

页表更新是内部操作,不参与 buffer 隐式同步。


8. 如何选择正确的 Usage 级别

我的 fence 代表什么操作? │ ├─ 内核内存管理 DMA(搬迁/清零/拷贝) │ → DMA_RESV_USAGE_KERNEL │ ├─ GPU 命令提交的写操作 │ → DMA_RESV_USAGE_WRITE │ ├─ GPU 命令提交的读操作 │ → DMA_RESV_USAGE_READ │ └─ 内部管理/不参与隐式同步 (抢占、页表更新、eviction fence、TLB flush) → DMA_RESV_USAGE_BOOKKEEP

等待时应该用什么级别?

我需要等什么? │ ├─ 只等内核搬迁完成 │ → dma_resv_wait_timeout(resv, DMA_RESV_USAGE_KERNEL, ...) │ ├─ 等之前的写操作完成(我要读) │ → dma_resv_wait_timeout(resv, DMA_RESV_USAGE_WRITE, ...) │ ├─ 等之前的所有读写完成(我要写) │ → dma_resv_wait_timeout(resv, DMA_RESV_USAGE_READ, ...) │ └─ 等所有操作完成(驱逐/释放 BO) → dma_resv_wait_timeout(resv, DMA_RESV_USAGE_BOOKKEEP, ...)

9. amdgpu_bo_fence 封装说明

amdgpu_bo_fence是 amdgpu 对dma_resv_add_fence的封装:

voidamdgpu_bo_fence(structamdgpu_bo*bo,structdma_fence*fence,bool shared){structdma_resv*resv=bo->tbo.base.resv;intr;r=dma_resv_reserve_fences(resv,1);if(r){dma_fence_wait(fence,false);// OOM 时退化为同步等待return;}dma_resv_add_fence(resv,fence,shared?DMA_RESV_USAGE_READ:DMA_RESV_USAGE_WRITE);}
  • shared=trueDMA_RESV_USAGE_READ
  • shared=falseDMA_RESV_USAGE_WRITE

注意amdgpu_bo_fence只支持 READ 和 WRITE 两个级别。
如果需要 KERNEL 或 BOOKKEEP,必须直接调用dma_resv_add_fence


10. 总结

级别数值语义典型场景被谁等待
KERNEL0内核内存管理BO move/clear所有操作
WRITE1隐式写同步GPU 写命令后续读写
READ2隐式读同步GPU 读命令后续写操作
BOOKKEEP3无隐式同步eviction fence, PT update仅 BOOKKEEP 查询

核心设计思想

  • 数值越小,可见范围越广,同步约束越强
  • 数值越大,可见范围越窄,越不影响其他操作
  • TTM eviction 和 BO 释放用 BOOKKEEP 查询 → 保证等待一切
  • 隐式同步(dma-buf 共享)只看 KERNEL/WRITE/READ → BOOKKEEP 完全透明
  • 选择 BOOKKEEP 给内部管理 fence 是最安全的做法:不干扰外部同步,只在需要时(驱逐/释放)被等待
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/12 9:48:17

SeqGPT-560m一键部署教程:基于Docker的快速环境搭建

SeqGPT-560m一键部署教程&#xff1a;基于Docker的快速环境搭建 1. 引言 想不想在10分钟内拥有一个强大的文本理解AI助手&#xff1f;SeqGPT-560m就是这样一个神奇的工具——它不需要复杂的训练过程&#xff0c;就能帮你完成文本分类、实体识别、阅读理解等各种自然语言理解任…

作者头像 李华
网站建设 2026/4/17 3:58:31

Ostrakon-VL 终端 Anaconda 虚拟环境管理:多项目 Python 依赖隔离指南

Ostrakon-VL 终端 Anaconda 虚拟环境管理&#xff1a;多项目 Python 依赖隔离指南 1. 为什么需要虚拟环境管理 在开发Ostrakon-VL这类计算机视觉项目时&#xff0c;我们经常会遇到这样的困扰&#xff1a;项目A需要PyTorch 1.8&#xff0c;而项目B需要PyTorch 2.0&#xff0c;…

作者头像 李华
网站建设 2026/4/12 20:52:01

用 AI Coding 工具生成 万字奇幻世界设定的实践记录狗

一、Actor 模型&#xff1a;不是并发技巧&#xff0c;而是领域单元 Actor 模型的本质是&#xff1a; Actor 是独立运行的实体 Actor 之间只通过消息交互 Actor 内部状态不可被外部直接访问 Actor 自行决定如何处理收到的消息 Actor 模型真正解决的是&#xff1a; 如何在不共享状…

作者头像 李华
网站建设 2026/4/12 15:52:09

AI超清画质增强镜像优化指南:常见问题排查与性能提升建议

AI超清画质增强镜像优化指南&#xff1a;常见问题排查与性能提升建议 1. 镜像核心能力与适用场景 AI超清画质增强镜像基于OpenCV EDSR模型构建&#xff0c;能够将低分辨率图像智能放大3倍&#xff0c;同时修复细节和去除噪点。这项技术在多个领域都有广泛应用价值&#xff1a…

作者头像 李华
网站建设 2026/4/12 10:19:17

Anthropic-Mythos-AI安全模型

Anthropic 发布 Claude Mythos&#xff1a;当 AI 开始主动"挖虫"&#xff0c;网络安全格局悄然改变 关键词&#xff1a; AI安全、漏洞挖掘、Project Glasswing、Claude Mythos事情是怎么发生的 2026年4月7日&#xff0c;Anthropic发布了Claude Mythos Preview模型。这…

作者头像 李华
网站建设 2026/4/26 18:29:53

OpenClaw对接千问3.5-9B实战:低成本自动化办公方案

OpenClaw对接千问3.5-9B实战&#xff1a;低成本自动化办公方案 1. 为什么选择这个组合&#xff1f; 去年夏天&#xff0c;我发现自己每天要花2小时处理邮件和整理文件——客户咨询、会议纪要、周报草稿全混在一起。尝试过各种自动化工具后&#xff0c;要么功能太单一&#xf…

作者头像 李华