
drm_gem_object回答对象是什么而drm_gem_object_funcs对象里的funcs指针回答对这个对象做各种操作时具体该怎么做。本文分析这张回调表为什么这样设计以及每个回调由谁、在什么时机、持什么锁调用。本节算是对gem_object动态视角的一个从回调函数角度的补充。1. 设计初衷为什么是一张函数指针表GEM 的核心哲学是只提供驱动间共享的公共代码不试图解决所有问题。这条哲学落到代码上就必须解决一个矛盾DRM core 想用统一的流程处理所有 GEM 对象handle 创建、mmap、dma-buf 导出、销毁……但每个驱动的后端存储千差万别shmem 页、VRAM、TTM 管理的 BO、CMA 连续内存……具体每一步该怎么做只有驱动自己知道。drm_gem_object_funcs就是这个矛盾的解法——把流程骨架留在 core把每一步的具体实现通过函数指针下放给驱动。它体现了几个明确的设计意图设计意图含义机制与策略分离core 定义何时该做某件事机制驱动通过回调决定具体怎么做策略面向对象的多态每个对象携带自己的funcs同一段 core 代码对不同驱动的对象表现出不同行为——等价于 C 的虚函数表vtable可选即降级绝大多数回调是 optional不实现某回调就等于声明本对象不支持该能力core 会走默认路径或返回不支持每对象粒度funcs挂在对象而非驱动上因此同一驱动可为不同类型的对象如 shmem BO vs 私有 BO挂不同的回调表稳定的 ABI 边界core 与驱动只通过这张表交互新增回调不破坏既有驱动老驱动该字段为 NULL 即可历史注记早期这些操作分散在drm_driver的全局操作集里如gem_free_object等是每驱动一份。后来收敛为每对象的drm_gem_object_funcs粒度更细、也更符合对象自带行为的直觉。2. 回调全景观测 / 调试print_infostatusrss内存管理evictCPU 映射mmapvm_opsdma-buf 共享 / PRIMEexportpin / unpinget_sg_tablevmap / vunmap生命周期free必需openclosedrm_gem_object.funcs下表汇总谁调用、何时调用、锁上下文obj-resv指 GEM 的 dma_resv 预留锁回调必需主要调用者core 函数触发时机锁上下文free✅drm_gem_object_free()refcount归零、对象最终销毁无已无人引用open○drm_gem_handle_create_tail()每次为对象新建一个 handle含 dma-buf 导入生成 handle无特殊要求close○drm_gem_object_release_handle()每次释放一个 handleGEM_CLOSE、进程退出、handle_delete无特殊要求export○drm_gem_prime_export()PRIME_HANDLE_TO_FD把对象导出为 dma-buf无默认实现已足够少有覆盖pin○drm_gem_map_attach()dma-buf被导入方 attach时钉住后端存储内部持obj-resvunpin○drm_gem_map_detach()dma-buf detach 时解钉内部持obj-resvget_sg_table○†drm_gem_map_dma_buf()导入方dma_buf_map_attachment()需要 sg 表时由 dma-buf 框架保证vmap○drm_gem_dmabuf_vmap()/drm_gem_vmap()需要把 BO 映射到内核虚拟地址如导入方 vmap调用时已持obj-resvvunmap○drm_gem_dmabuf_vunmap()释放上面的内核映射调用时已持obj-resvmmap○drm_gem_mmap_obj()用户态 mmap 该对象含 PRIME mmap无特殊要求evict○drm_gem_object_evict()内存紧张、需把对象后端换出调用时已持obj-resvprint_info○drm_gem_print_info()debugfs 打印对象信息无status○drm_show_memory_stats()fdinfo 统计内存resident/purgeable 等table_lockrss○drm_show_memory_stats()fdinfo 统计物理常驻大小table_lockvm_ops○‡缺页时由 mm 子系统回调用户态访问 mmap 区域触发缺页mm/vma 锁†get_sg_table在用 core 默认的drm_gem_map_dma_buf作为 dma-buf 导出实现时是必需的驱动若自己实现map_dma_buf则不需要。‡vm_ops与mmap二选一若实现了mmap回调则由它自行设置vma-vm_ops此时表中的vm_ops字段不被使用。3. 分组详解3.1 生命周期free / open / close这三者对应对象从被引用到被释放的关键节点。理解它们必须先分清 [gem设计目标]讲的双计数refcount内核总引用与handle_count用户态 handle 数。free唯一必需回调refcount归零时由drm_gem_object_free()调用是对象的析构器。驱动在这里释放后端存储页/VRAM/TTM BO、调用drm_gem_object_release()清理 core 部分然后kfree自己的对象。因为此刻已无人引用无需加锁。open每新建一个 handle就调一次drm_gem_handle_create_tail。注意它绑定的是handle不是对象——同一对象被同一/不同进程多次 open会多次触发。典型用途amdgpu 在此为进程drm_file× BO建立 per-VM 的记账如把 BO 关联到该文件的 VM。close每释放一个 handle调一次GEM_CLOSE、drm_release进程退出遍历、drm_gem_handle_delete。与open对称用于拆掉 open 时建立的 per-file 关系。驱动回调DRM core用户态驱动回调DRM core用户态... 使用对象 ...alt[refcount 归零]创建 / 导入 → 得到 handlefuncs-open(obj, file) (每个 handle 一次)GEM_CLOSE / 进程退出funcs-close(obj, file) (每个 handle 一次)handle_count--, refcount--funcs-free(obj) (仅一次析构)常见误区把free当成handle 关闭就触发。实际上只要对象还被 mmap、dma-buf 导出或驱动内部引用close之后对象不会free——真正触发free的是refcount归零。3.2 dma-buf 共享export / pin / unpin / get_sg_table / vmap / vunmap这一组服务于PRIME/dma-buf 跨进程、跨设备共享它们的调用方往往是导入端或 dma-buf 框架而非本驱动自己。export把对象包成dma_buf。多数驱动不覆盖它直接用 core 的drm_gem_prime_export()内部会装配一套标准dma_buf_ops其回调再转回下面这些函数。仅当驱动需要自定义 dma-buf 行为时才实现。pin/unpin当导入方 attach到这个 dma-buf 时drm_gem_map_attachcore 会先dma_resv_lock再调pin把后端存储钉在一个可被外部 DMA 访问的位置例如从可迁移的 VRAM 钉到稳定处防止共享期间被迁移/换出。detach 时对称调unpin。这正是共享会抑制迁移的根源。get_sg_table导入方真正映射时drm_gem_map_dma_bufcore 调它拿到描述后端物理页的 scatter-gather 表再dma_map_sgtable给导入设备。注意core 的释放路径用dma_unmap_sg sg_free_table因此该回调不能用于指向驱动私有内存区间的 sg 表。vmap/vunmap把 BO 映射到内核地址空间返回iosys_map。用于导入方或内核内部需要 CPU 直接读写 BO 的场景。关键约束core 调用时已持obj-resv锁回调内不得再去拿这把锁。导出端驱动回调DRM core (导出端)dma-buf 框架导入方 (另一驱动/进程)导出端驱动回调DRM core (导出端)dma-buf 框架导入方 (另一驱动/进程)opt[需要内核 CPU 访问]dma_buf_attach()drm_gem_map_attach()dma_resv_lock(obj-resv)funcs-pin(obj) (钉住抑制迁移)dma_buf_map_attachment()drm_gem_map_dma_buf()funcs-get_sg_table(obj)dma_map_sgtable() → 返回 sgtdma_buf_vmap() → drm_gem_dmabuf_vmap()funcs-vmap(obj, map) (已持 resv)3.3 CPU 映射mmap / vm_opsmmap用户态对该对象mmap()时drm_gem_mmap_obj()会调它由驱动完成 VMA 的建立页缓存属性、vma-vm_ops等。若实现了它就由它负责设置vm_ops。vm_ops不实现mmap回调时的默认路径所用的 VMA 操作集其中的fault处理缺页——用户首次访问某页时按需把后端页填入。二者互斥有mmap就不用表里的vm_ops。这组回调让给对象分配 mmap 伪偏移与真正建立 CPU 映射衔接起来。3.4 内存管理evictevict内存紧张时drm_gem_object_evict()调它把对象后端换出如从 VRAM 移到系统内存或释放可重建的页。调用时已持obj-resv。它体现了一个边界GEM core不自己做迁移策略只在合适时机通知驱动去 evict具体搬移仍由驱动通常借 TTM完成——与第 6 节迁移属 TTM一致。与 gpuva 的联动一个对象被 evict 后指向它的所有 GPU VA 映射都要标记失效。驱动通常在 evict 路径里遍历gpuva.list逐条drm_gpuva_invalidate()。3.5 观测与调试print_info / status / rss这三者不影响功能只服务于可观测性print_infodebugfs 打印对象的驱动私有信息用drm_printf_indent()输出。status返回对象状态如 resident/purgeable决定它被计入 fdinfo 的哪类统计在table_lock下调用允许与状态变更竞争“无害”。rss返回对象在物理内存中的常驻大小Resident Set Size。status与rss都由drm_show_memory_stats()调用支撑/proc/pid/fdinfo的 GPU 内存统计gputop 等工具依赖它。4. 锁上下文速记最易出错处实现回调时最常见的 bug 是锁误用。归纳如下回调进入时是否已持obj-resv实现注意vmap/vunmap是内部不要再dma_resv_lockevict是同上且此路径可能在内存回收上下文避免大块分配pin/unpin是由drm_gem_map_*加持—free否已无引用可自由清理open/close/mmap/print_info否按需自行加锁5. 与 amdgpu 的对照amdgpu 通过amdgpu_bo内嵌ttm_buffer_object→ 内嵌drm_gem_object实现这张表把 GEM 的对象语义与 TTM 的迁移能力缝合free→ 释放amdgpu_bo、退还 TTM 资源open/close→ 维护drm_file × BO在该进程 VM 中的关系pin/unpin/get_sg_table→ 桥接到 TTM 的 pin 与 sg 表evict→ 交给 TTM 的迁移逻辑mmap/vm_ops→ TTM 的缺页与页属性处理。可见这张表正是 core 与驱动 TTM之间的契约边界core 决定时机驱动借 TTM落实动作。6. 小结drm_gem_object_funcs是 GEM机制与策略分离哲学的直接产物——一张每对象的虚函数表让统一的 core 流程对接千差万别的驱动后端。唯一必需的是free其余皆 optional不实现即代表不支持该能力。记住三条主线生命周期free/open/close注意双计数、dma-buf 共享export/pin/unpin/get_sg_table/vmap/vunmap注意 pin 抑制迁移、vmap 已持 resv、CPU 映射mmap/vm_ops 互斥。锁是最大的坑vmap/vunmap/evict/pin/unpin进入时 core已持obj-resv回调内不得重复加锁。这张表定义了 core 与驱动及 TTM之间清晰的契约core 管何时驱动管如何。