资讯动态

第九章:GPUVM:drm_gpuvm_ops--驱动需要实现的回调,分组与职责

发布时间:2026/8/20 12:09:38 来源:尧图企业网站定制
前言drm_gpuvm是 DRM 里的「GPU 地址空间管理器」替驱动维护整段 GPU 虚拟地址空间。它遵循一条贯穿整个 DRM 子系统的设计原则框架只实现与硬件无关的通用逻辑凡是涉及具体硬件行为、驱动私有内存布局的动作都通过struct drm_gpuvm_ops的回调交给驱动完成。这个「机制与策略分离」的取向在前几章的设计里也反复出现。这篇把drm_gpuvm_ops的 9 个回调讲清楚这 9 个回调怎么分组、每组负责什么为什么框架不亲自做这些事而是交给驱动。按「服务谁」归类这 9 个回调恰好是三组本文按从对象生命周期到地址空间落地的顺序分组讲清楚。1. 9 个回调的全景structdrm_gpuvm_ops{/* 组一对象生命周期 驻留 */void(*vm_free)(structdrm_gpuvm*gpuvm);structdrm_gpuvm_bo*(*vm_bo_alloc)(void);void(*vm_bo_free)(structdrm_gpuvm_bo*vm_bo);int(*vm_bo_validate)(structdrm_gpuvm_bo*vm_bo,structdrm_exec*exec);/* 组二op 内存分配 */structdrm_gpuva_op*(*op_alloc)(void);void(*op_free)(structdrm_gpuva_op*op);/* 组三地址空间变更的落地 */int(*sm_step_map)(structdrm_gpuva_op*op,void*priv);int(*sm_step_remap)(structdrm_gpuva_op*op,void*priv);int(*sm_step_unmap)(structdrm_gpuva_op*op,void*priv);};分组回调服务对象一、对象生命周期 驻留vm_free、vm_bo_alloc/vm_bo_free、vm_bo_validateGPUVM / BO 对象本身二、op 内存分配op_alloc/op_free描述地址空间变更的drm_gpuva_op三、地址空间变更落地sm_step_map/sm_step_remap/sm_step_unmap每一步的页表插入 / 拆分 / 删除下面按这个顺序逐组说清职责。2. 组一对象生命周期 驻留管理这一组管的是 GPUVM 和 BO 这两类对象本身的「生老病死」与「显存换入换出」是整套回调里最基础、也最先被用到的。回调触发点用途可选性vm_freedrm_gpuvm最后一个引用被丢弃释放整个 VM强制vm_bo_alloc/vm_bo_free分配 / 释放drm_gpuvm_bovm_bo是「GEM 对象 ↔ VM」的连接件可选vm_bo_validatedrm_gpuvm_validate()提交前把被换出evicted的 BO 重新验证回显存用到才需几个要点vm_free是整个 ops 里唯一强制的回调。框架在引用归零时会drm_WARN_ON(!ops-vm_free)然后调ops-vm_free(gpuvm)。因为drm_gpuvm通常内嵌在驱动更大的结构体里框架只拿到内嵌字段的指针根本不知道该怎么释放外层对象。vm_bo_alloc/vm_bo_free定制vm_bo的内存布局。drm_gpuvm_bo是连接一个 GEM 对象与一个 VM 的中间结构很多驱动想把它内嵌进自己的结构体于是框架分配时先看驱动有没有实现if(opsops-vm_bo_alloc)vm_boops-vm_bo_alloc();elsevm_bokzalloc(sizeof(*vm_bo),GFP_KERNEL);不实现就走默认kzalloc实现了就用驱动定制版。vm_bo_validate是驻留路径。它通常内部调用驱动版的ttm_bo_validate()把被驱逐的 BO 搬回显存。这管的是「物理显存驻留」和「虚拟地址布局」是正交的两件事。3. 组二op 列表的内存分配配套op_alloc/op_free只做一件小事分配和释放drm_gpuva_op。drm_gpuva_op是框架描述「一次地址空间变更需要哪些步骤」的数据结构。和vm_bo同理很多驱动希望把drm_gpuva_op内嵌进自己的结构体里携带私有字段于是框架在需要分配 op 时先看驱动有没有实现op_alloc没有就用默认kzalloc。这两个回调也是可选的纯粹是内存布局定制不涉及任何页表语义。4. 组三地址空间变更的「落地三件套」这三个是把「地址空间该怎么变」真正写进驱动页表的回调。框架内部会遍历地址空间、算出一次请求需要做哪些插入、拆分、删除然后逐步回调给驱动落地回调语义动作sm_step_map插入一条新映射sm_step_remap把一条已有映射拆开原映射被新请求部分覆盖时sm_step_unmap删除一条已有映射一个容易忽略的点这三步不是「一个请求只触发一步」。一条新映射若覆盖了旧映射框架必须先 remap / unmap 掉旧的再 map 新的因此一次请求可能连续回调好几步。框架对这三者有强制要求入口会检查回调是否齐全if(unlikely(!(opsops-sm_step_mapops-sm_step_remapops-sm_step_unmap)))return-EINVAL;也就是说要用框架的地址空间变更引擎就必须提供这组落地回调——框架只出「该做哪些步骤」的决策驱动出「怎么写进硬件页表」的执行。5. 为什么框架不自己做而是交给驱动这是本文的核心问题。框架明明已经算出了「该做哪些步骤」为什么不干脆把页表也一起改了答案可以归纳成四条边界。5.1 硬件差异页表格式框架根本不认识不同 GPU 的页表结构、页大小、权限位编码、TLB 失效方式各不相同AMD 的 GPUVM、Intel、Nouveau、Panthor 各有各的 MMU。框架若要亲自写页表就得内置所有厂商的 MMU 知识——这既不现实也违背分层设计。所以框架只做厂商无关的部分地址区间的 split/merge 决策这部分逻辑对所有 GPU 都一样把厂商相关的落地动作sm_step_*留给驱动。这是最经典的「机制与策略分离」。5.2 内存布局对象由驱动拥有框架不知如何分配/释放drm_gpuvm、drm_gpuvm_bo、drm_gpuva_op在实际驱动里几乎总是内嵌在更大的驱动私有结构体中。框架手里只有一个内嵌字段的指针无法知道外层结构体的大小、也无法知道额外字段该怎么初始化和回收。这正是vm_free、vm_bo_alloc/free、op_alloc/free存在的原因谁分配、谁定义布局谁就得负责释放。框架只在「需要一个对象」和「对象该没了」的时机点回调具体怎么分配/释放由拥有者决定。5.3 时机与上下文只有驱动知道何时真正写硬件改页表往往不能立刻同步执行而要挂到 fence、排进 DMA/SDMA 队列、和调度器与 dma_resv 锁协同。什么时候刷 TLB、是否要等某个 fence signalled、批处理还是逐条提交——这些都是驱动的调度语义。框架把sm_step_*设计成「回调 驱动传入的priv指针」本质是把执行时机的控制权交还给驱动框架给你一步步的「要做什么」你自己决定「什么时候、以什么并发方式做」。5.4 驻留是驱动的独立子系统vm_bo_validate背后是 TTM/BO 的驱逐与回迁体系这套东西和地址空间管理是正交的两个子系统。框架没有理由、也没有能力去替驱动决定一个 BO 该不该被搬回显存、搬到哪个内存域。它只在drm_gpuvm_validate()需要时把「这个被驱逐的 BO 需要验证」这一信号回调出去。6. 小结drm_gpuvm_ops的 9 个回调按职责分三组组一对象生命周期 驻留vm_free强制、vm_bo_alloc/vm_bo_free、vm_bo_validate——管 GPUVM/BO 对象的分配释放与显存驻留组二op 内存分配配套op_alloc/op_free——可选定制drm_gpuva_op的内存布局组三地址空间变更落地sm_step_map/sm_step_remap/sm_step_unmap——框架强制要求把每一步写进硬件页表。框架不自己动手而是交给驱动根本原因是机制与策略分离框架掌握厂商无关的「算法」对象生命周期时机、地址区间怎么切驱动掌握厂商相关的「执行」对象内存布局、页表长什么样、何时写硬件、BO 怎么驻留。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价