MTK Linux DRM分析(十八)- KMS drm_gem.c drm_prime.c

内容分享8小时前发布
0 0 0

一、drm_gem分析

drm_gem.c 是 Linux DRM (Direct Rendering Manager) 框架的核心文件之一,主要负责 GEM (Graphics Execution Manager) 模块的实现。GEM 是 DRM 用于管理图形缓冲区对象的标准接口,提供内存分配、映射、共享、同步和释放等功能。它支持设备驱动(如 Intel、AMD、MediaTek)实现自定义缓冲区管理,同时保持与用户空间(如 OpenGL、Wayland)的兼容性。该文件处理 GEM 对象的生命周期、PRIME (DMA-BUF 共享)、mmap、fence 同步和锁机制,确保高效的图形内存操作。代码基于 shmem (shared memory) 后备存储,并支持私有对象(无 shmem)。它不直接处理硬件细节,而是提供抽象接口供驱动扩展(如通过 drm_gem_object_funcs)。

代码被截断,但从提供的片段看,它包括 GEM 初始化、对象管理、PRIME 支持、mmap、vmap、锁和 fence 操作。以下按逻辑模块分解代码,解释其实现和作用。

1. 初始化和释放函数(drm_gem_init、drm_gem_init_release、drm_gem_object_init、drm_gem_private_object_init)

实现

drm_gem_init:初始化 drm_device 的 GEM 字段,包括互斥锁 (object_name_lock)、IDR (object_name_idr) 和 VMA 偏移管理器 (vma_offset_manager)。使用 drmm_kzalloc 分配内存,并添加释放动作 (drmm_add_action)。drm_gem_init_release:释放 VMA 管理器 (drm_vma_offset_manager_destroy)。drm_gem_object_init:初始化 GEM 对象,使用 shmem_file_setup 创建 shmem 文件作为后备存储,设置 filp。drm_gem_private_object_init:初始化私有 GEM 对象(无 shmem),设置大小、引用计数 (kref_init)、dma_resv 和 VMA 节点。
作用
设置 DRM 设备的 GEM 子系统,支持对象 ID 管理和 VMA 偏移(用于 mmap)。为 GEM 对象分配后备内存(shmem 或私有),初始化引用计数和锁,确保对象安全创建/释放。支持标准 GEM 对象(shmem 后备,用于 swap)和私有对象(驱动自定义后备,如 pinned VRAM)。

2. PRIME 支持(drm_gem_remove_prime_handles、drm_gem_prime_export、drm_gem_prime_import 等)

实现

drm_gem_remove_prime_handles:移除 PRIME 句柄 (dma_buf_put)。drm_gem_prime_export:导出 GEM 为 dma_buf,使用 dma_buf_export 和驱动的 export 回调。drm_gem_prime_import:导入 dma_buf 为 GEM,使用 dma_buf_get 和驱动的 import 回调。drm_gem_prime_fd_to_handle:从 fd 导入 dma_buf,并创建 GEM 句柄。drm_gem_prime_handle_to_fd:从 GEM 句柄导出 dma_buf,并返回 fd。drm_gem_dmabuf_export:包装 dma_buf 导出信息,包括 mmap、vmap 等回调。
作用
支持 PRIME 机制(DMA-BUF),允许 GEM 对象跨驱动/进程共享(如 GPU 到 display 或 V4L2)。处理 dma_buf 的导入/导出、fd/句柄转换,确保缓冲区可共享且同步(通过 dma_resv)。集成驱动自定义回调(如 gem_prime_export、gem_prime_import),支持特定硬件优化。

3. 对象管理函数(drm_gem_object_release、drm_gem_object_free、drm_gem_object_put、drm_gem_object_lookup 等)

实现

drm_gem_object_release:释放 dma_resv、VMA 节点和驱动自定义资源 (free 回调)。drm_gem_object_free:调用 drm_gem_object_release,释放对象内存。drm_gem_object_put:减少引用计数 (kref_put),若为 0 调用 drm_gem_object_free。drm_gem_object_lookup:从句柄查找 GEM 对象,增加引用计数。drm_gem_handle_create:创建对象 ID,添加引用。drm_gem_handle_delete:移除 ID,释放引用。
作用
管理 GEM 对象的引用计数和生命周期,确保线程安全释放。支持用户空间通过句柄访问对象(如 ioctl),并处理对象查找/删除。防止内存泄漏,通过 kref 机制延迟释放。

4. mmap 支持(drm_gem_mmap_single、drm_gem_mmap、drm_gem_create_mmap_offset 等)

实现

drm_gem_mmap_single:处理单次 mmap,使用驱动的 mmap 回调。drm_gem_mmap:通用 mmap,计算偏移,调用驱动的 mmap。drm_gem_create_mmap_offset:创建 VMA 偏移,使用 drm_vma_offset_add。drm_gem_dumb_map_offset:为 dumb buffer 创建偏移。
作用
支持用户空间 mmap GEM 对象,将缓冲区映射到用户地址空间。处理 VMA 偏移管理,确保 mmap 安全(VM_IO | VM_DONTEXPAND)。支持 dumb buffer(简单缓冲区,用于软件渲染)。

5. vmap/unmap 和 pin/unpin(drm_gem_vmap、drm_gem_vunmap、drm_gem_pin、drm_gem_unpin)

实现

drm_gem_vmap:调用驱动的 vmap 回调,映射到内核 VA。drm_gem_vunmap:调用驱动的 vunmap 回调,解映射。drm_gem_pin:调用驱动的 pin 回调,固定缓冲区。drm_gem_unpin:调用驱动的 unpin 回调,解固定。
作用
支持内核访问 GEM 缓冲区(vmap 用于 CPU 读写)。pin/unpin 用于固定内存(如 VRAM),防止页面交换。依赖驱动回调,允许硬件特定实现(如 IOMMU 映射)。

6. 锁和预留函数(drm_gem_lock_reservations、drm_gem_unlock_reservations)

实现

drm_gem_lock_reservations:使用 ww_acquire_ctx 加锁多个 GEM 的 dma_resv,支持重试(-EDEADLK 处理)。drm_gem_unlock_reservations:解锁 dma_resv,结束 ww_ctx。
作用
支持原子操作中的缓冲区锁,确保多对象同步(用于渲染作业)。使用 dma_resv 机制处理共享/独占锁,防止死锁。

7. fence 操作(drm_gem_fence_array_add、drm_gem_fence_array_add_implicit)

实现

drm_gem_fence_array_add:添加 fence 到 xarray,去重(基于上下文),使用 xa_alloc。drm_gem_fence_array_add_implicit:从 GEM 的 dma_resv 获取隐式 fence(共享或独占),添加到数组。
作用
支持渲染作业的 fence 依赖管理,确保缓冲区数据就绪。去重优化,减少 fence 数组大小(按上下文)。用于调度作业(如 GPU 命令提交)。

8. 打印和调试函数(drm_gem_print_info)

实现

drm_gem_print_info:打印对象信息(name、refcount、start、size、imported),调用驱动的 print_info 回调。
作用
提供调试输出,支持 sysfs 或 DRM 调试接口查看 GEM 状态。

总体作用

核心作用:提供 GEM API,抽象图形内存管理,支持用户空间(如 GL/Vulkan)和驱动交互。确保缓冲区分配、共享(PRIME)、同步(fence、dma_resv)和映射(mmap/vmap)高效、安全。与框架集成
依赖 dma-buf、shmem_fs 和 ww_mutex,用于跨驱动共享和锁。驱动可通过 drm_gem_object_funcs 扩展(如自定义 vmap/pin)。支持内核模块导出(EXPORT_SYMBOL),供第三方驱动使用。
优化点
使用 kref 引用计数和 dma_resv 锁,确保线程安全。PRIME 支持 dma-buf,兼容 Android 和 Wayland。条件处理(如 -EDEADLK 重试)防止死锁。
依赖:DRM 核心头文件(如 drm_device.h、drm_vma_manager.h)、内核 API(如 dma-buf.h、kref.h)。


基于文本的函数关系调用图

以下是基于文本的函数调用关系图,使用树状结构表示(缩进表示调用层次)。图聚焦于主要函数及其调用链,省略次要日志/错误检查。箭头 -> 表示调用关系。



drm_gem_init (初始化 GEM 子系统,入口)
  -> mutex_init (内核:初始化锁)
  -> idr_init_base (内核:初始化 IDR)
  -> drmm_kzalloc (DRM:分配 VMA 管理器)
  -> drm_vma_offset_manager_init (DRM:初始化 VMA)
  -> drmm_add_action (DRM:添加释放动作)
     -> drm_gem_init_release (释放 VMA)
        -> drm_vma_offset_manager_destroy (DRM:销毁 VMA)
 
drm_gem_object_init (初始化 shmem GEM 对象)
  -> drm_gem_private_object_init (初始化私有字段)
     -> kref_init (内核:初始化引用计数)
     -> dma_resv_init (内核:初始化 dma_resv)
     -> drm_vma_node_reset (DRM:重置 VMA 节点)
  -> shmem_file_setup (内核 shmem:创建后备文件)
 
drm_gem_prime_export (导出 PRIME dma_buf)
  -> drm_gem_object_get (增加引用)
  -> dma_buf_export (内核 DMA-BUF:导出 dma_buf)
     -> drm_gem_dmabuf_export (包装导出信息)
        -> drm_gem_prime_mmap (mmap 回调)
        -> drm_gem_prime_vmap (vmap 回调)
        -> drm_gem_prime_vunmap (vunmap 回调)
 
drm_gem_prime_import (导入 PRIME dma_buf)
  -> dma_buf_get (内核 DMA-BUF:获取 dma_buf)
  -> drm_gem_prime_import_dev (导入到设备)
     -> drm_prime_fd_to_handle_ioctl (fd 到句柄)
 
drm_gem_object_release (释放 GEM 对象)
  -> dma_resv_fini (内核:释放 dma_resv)
  -> drm_vma_node_cleanup (DRM:清理 VMA 节点)
  -> fput (内核:释放 filp)
  -> drm_gem_object_free (释放内存)
     -> kfree (内核:释放对象)
 
drm_gem_object_put (减少引用)
  -> kref_put (内核:减少 kref,若 0 调用 drm_gem_object_free)
     -> drm_gem_object_free (释放对象)
        -> drm_gem_object_release (释放资源)
 
drm_gem_handle_create (创建句柄)
  -> drm_gem_object_get (增加引用)
  -> drm_gem_create_object_id (创建 ID)
     -> idr_alloc (内核:分配 IDR)
 
drm_gem_handle_delete (删除句柄)
  -> drm_gem_object_lookup (查找对象)
     -> idr_find (内核:查找 IDR)
  -> drm_gem_remove_prime_handles (移除 PRIME)
     -> dma_buf_put (内核 DMA-BUF:释放 dma_buf)
  -> drm_gem_object_put (释放引用)
 
drm_gem_mmap (mmap GEM 对象)
  -> drm_vma_offset_lookup_locked (DRM:查找 VMA 偏移)
  -> vma_set_file (内核 VM:设置 vma filp)
  -> drm_gem_mmap_single (单次 mmap)
     -> drm_gem_object_funcs->mmap (驱动回调:自定义 mmap)
 
drm_gem_vmap (vmap GEM 对象)
  -> drm_gem_object_funcs->vmap (驱动回调:映射到内核 VA)
 
drm_gem_vunmap (vunmap GEM 对象)
  -> drm_gem_object_funcs->vunmap (驱动回调:解映射)
 
drm_gem_pin (pin GEM 对象)
  -> drm_gem_object_funcs->pin (驱动回调:固定内存)
 
drm_gem_unpin (unpin GEM 对象)
  -> drm_gem_object_funcs->unpin (驱动回调:解固定)
 
drm_gem_lock_reservations (锁多个 dma_resv)
  -> ww_acquire_init (内核 WW:初始化 ww_ctx)
  -> dma_resv_lock_interruptible (内核 DMA-BUF:加锁,支持重试)
     -> dma_resv_lock_slow_interruptible (慢路径锁)
  -> ww_acquire_done (内核 WW:完成获取)
 
drm_gem_unlock_reservations (解锁 dma_resv)
  -> dma_resv_unlock (内核 DMA-BUF:解锁)
  -> ww_acquire_fini (内核 WW:结束 ww_ctx)
 
drm_gem_fence_array_add (添加 fence 到数组)
  -> xa_for_each (内核 XA:遍历数组,去重)
     -> dma_fence_is_later (内核 DMA-FENCE:比较 fence)
  -> xa_alloc (内核 XA:分配并存储 fence)
 
drm_gem_fence_array_add_implicit (添加隐式 fence)
  -> dma_resv_get_fences (内核 DMA-BUF:从 resv 获取 fence 数组)
  -> drm_gem_fence_array_add (添加到数组)
     -> dma_fence_put (内核 DMA-FENCE:释放多余 fence)
 
drm_gem_print_info (打印 GEM 信息)
  -> drm_printf_indent (DRM:打印 name、refcount、start、size、imported)
  -> drm_gem_object_funcs->print_info (驱动回调:自定义打印)

说明

图中顶层函数(如 drm_gem_init)是入口,通常在驱动 probe 时调用。回调函数(如 drm_gem_object_funcs 中的方法)由驱动实现,并在 GEM 操作中被调用。箭头表示直接调用;间接依赖(如错误重试循环)未展开。内核函数(如 kref_init)是底层依赖,DRM 函数(如 drm_vma_offset_manager_init)是框架内部协作。

二、drm_prime分析

drm_prime.c 是 Linux DRM (Direct Rendering Manager) 框架的核心文件之一,负责实现 PRIME 机制,用于跨进程或跨设备共享缓冲区对象。PRIME 基于 DMA-BUF(Linux 内核的缓冲区共享框架),允许 DRM 驱动(如 GPU 或显示驱动)通过文件描述符(fd)共享 GEM (Graphics Execution Manager) 对象。它提供更高的安全性(fd 需通过 UNIX 域套接字显式传递)和去重机制(避免重复导入),支持现代图形堆栈(如 Wayland、Android)。该模块为驱动提供了标准接口(如 drm_gem_prime_handle_to_fd、drm_gem_prime_fd_to_handle),并支持 GEM 对象的导入/导出、sg_table 转换和资源清理。

它包括 PRIME 数据结构、句柄管理、导入/导出操作和 sg_table 转换函数。以下按逻辑模块分解代码,解释其实现和作用。


1. 数据结构和初始化(drm_prime_member、drm_prime_add_buf_handle)

实现

struct drm_prime_member:表示 PRIME 句柄映射,包含 dma_buf(DMA-BUF 对象)、handle(GEM 句柄)、红黑树节点(dmabuf_rb 和 handle_rb)用于缓存。drm_prime_add_buf_handle:将 dma_buf 和 handle 插入 drm_prime_file_private 的红黑树(dmabufs 和 handles),用于去重和快速查找。使用 rb_link_node 和 rb_insert_color 维护红黑树。
作用
管理每个 DRM 文件(drm_file)的 PRIME 句柄缓存,确保同一 dma_buf 在进程中只有一个 GEM 句柄。红黑树提供高效的查找和插入(O(log n)),支持去重检测(如自导入)。

2. PRIME 句柄管理(drm_prime_lookup_buf_handle、drm_prime_add_buf_handle 等)

实现

drm_prime_lookup_buf_handle:从 prime_fpriv->dmabufs 查找 dma_buf 对应的 handle。drm_prime_add_buf_handle:添加 dma_buf 和 handle 到红黑树,增加 dma_buf 引用计数 (get_dma_buf)。(截断部分可能包含 drm_prime_remove_buf_handle):从红黑树移除映射,释放 dma_buf 引用。
作用
支持用户空间通过 fd 导入/导出 GEM 对象,确保句柄唯一性。防止重复导入(自导入检测),避免资源浪费和潜在错误(如重复提交缓冲区)。

3. PRIME 导出(drm_gem_prime_handle_to_fd、drm_gem_dmabuf_export)

实现

drm_gem_prime_handle_to_fd:从 GEM 句柄查找对象,调用 drm_gem_prime_export 导出为 dma_buf,返回 fd。支持 DRM_CLOEXEC 和 DRM_RDWR 标志。drm_gem_dmabuf_export:配置 dma_buf_ops(包括 map/unmap、mmap、release 等),调用 dma_buf_export 创建 dma_buf,存储 GEM 对象到 priv。
作用
将 GEM 对象导出为 dma_buf,生成 fd,供其他进程/驱动使用(如 V4L2 或显示驱动)。配置 dma_buf_ops 确保兼容性,支持 mmap、vmap 和同步。

4. PRIME 导入(drm_gem_prime_fd_to_handle、drm_gem_prime_import、drm_gem_prime_import_dev)

实现

drm_gem_prime_fd_to_handle:从 fd 获取 dma_buf,调用 drm_gem_prime_import,创建 GEM 句柄并添加到红黑树。drm_gem_prime_import:调用 drm_gem_prime_import_dev 使用 dev->dev 作为 attach 设备。drm_gem_prime_import_dev:
如果 dma_buf->ops 是 drm_gem_prime_dmabuf_ops 且对象属于同一设备,直接增加 GEM 引用(自导入优化)。否则,调用 dma_buf_attach 和 dma_buf_map_attachment 获取 sg_table,通过驱动的 gem_prime_import_sg_table 创建 GEM 对象。设置 obj->import_attach 和 obj->resv,确保同步。

作用
从 fd 导入 dma_buf 为 GEM 对象,支持跨设备共享。自导入优化减少冗余对象创建,保持引用计数一致。驱动通过 gem_prime_import_sg_table 自定义导入逻辑(如分配 VRAM 或映射 IOMMU)。

5. sg_table 转换(drm_prime_sg_to_page_array、drm_prime_sg_to_dma_addr_array)

实现

drm_prime_sg_to_page_array:遍历 sg_table,将页面存储到 pages 数组(已标记为 deprecated)。drm_prime_sg_to_dma_addr_array:遍历 sg_table,将 DMA 地址存储到 addrs 数组。
作用
将 sg_table 转换为页面或 DMA 地址数组,供驱动使用(如硬件 DMA 配置)。drm_prime_sg_to_page_array 不推荐使用(因页面访问可能不安全),推荐直接使用 sg_table 或 dma_addr_array。

6. 资源清理(drm_prime_gem_destroy)

实现

drm_prime_gem_destroy:解映射 sg_table (dma_buf_unmap_attachment),断开 dma_buf 连接 (dma_buf_detach),释放 dma_buf 引用 (dma_buf_put)。
作用
清理导入的 GEM 对象,释放关联的 dma_buf 和 sg_table,防止内存泄漏。驱动需在 drm_gem_object_funcs.free 中调用。

总体作用

核心作用:实现 PRIME 机制,支持 GEM 对象的跨进程/设备共享,通过 DMA-BUF 提供安全、高效的缓冲区共享接口。用于现代图形堆栈(如 Wayland、Android)中 GPU 和显示驱动的协作。与框架集成
依赖 dma-buf.h 提供缓冲区共享和同步(dma_resv)。与 drm_gem.c 协作,调用 GEM 函数(如 drm_gem_object_get、drm_gem_object_put)。驱动通过 drm_driver.gem_prime_import_sg_table 和 drm_gem_object_funcs.export 扩展硬件特定逻辑。
优化点
红黑树缓存 (drm_prime_file_private) 确保句柄唯一性,优化查找。自导入检测避免重复创建对象。支持 DRM_CLOEXEC 和 DRM_RDWR,符合用户空间需求。
依赖:DRM 核心(drm_gem.h、drm_file.h)、内核 DMA-BUF(dma-buf.h)、红黑树(rbtree.h)。


基于文本的函数关系调用图

以下是基于文本的函数调用关系图,使用树状结构表示(缩写表示调用层次)。图聚焦于主要函数及其调用链,省略次要日志/错误检查。箭头 -> 表示调用关系。



drm_gem_prime_handle_to_fd (导出 GEM 为 fd,入口)
  -> drm_gem_object_lookup (DRM:查找 GEM 对象)
  -> drm_prime_lookup_buf_handle (查找缓存)
     -> rb_entry (内核 RB:遍历 dmabufs 红黑树)
  -> drm_gem_prime_export (导出 dma_buf)
     -> drm_gem_dmabuf_export (配置 dma_buf_ops)
        -> dma_buf_export (内核 DMA-BUF:创建 dma_buf)
  -> drm_prime_add_buf_handle (添加句柄到缓存)
     -> get_dma_buf (内核 DMA-BUF:增加引用)
     -> rb_link_node (内核 RB:插入红黑树)
     -> rb_insert_color (内核 RB:平衡红黑树)
  -> drm_gem_object_put (DRM:释放 GEM 引用)
 
drm_gem_prime_fd_to_handle (从 fd 导入 GEM,入口)
  -> dma_buf_get (内核 DMA-BUF:从 fd 获取 dma_buf)
  -> drm_prime_lookup_buf_handle (查找缓存)
     -> rb_entry (内核 RB:遍历 dmabufs 红黑树)
  -> drm_gem_prime_import (导入 GEM)
     -> drm_gem_prime_import_dev (核心导入逻辑)
        -> dma_buf_attach (内核 DMA-BUF:附加设备)
        -> dma_buf_map_attachment (内核 DMA-BUF:映射 sg_table)
        -> drm_driver.gem_prime_import_sg_table (驱动回调:创建 GEM)
        -> drm_gem_object_get (DRM:自导入时增加引用)
  -> drm_gem_handle_create (DRM:创建句柄)
     -> drm_gem_create_object_id (DRM:分配 IDR)
  -> drm_prime_add_buf_handle (添加句柄到缓存)
  -> dma_buf_put (内核 DMA-BUF:释放 dma_buf 引用)
 
drm_gem_prime_import (导入 dma_buf 为 GEM)
  -> drm_gem_prime_import_dev (核心导入)
     -> dma_buf_attach (内核 DMA-BUF:附加设备)
     -> dma_buf_map_attachment (内核 DMA-BUF:映射 sg_table)
     -> drm_driver.gem_prime_import_sg_table (驱动回调:创建 GEM)
     -> drm_gem_object_get (DRM:自导入时增加引用)
 
drm_prime_gem_destroy (清理导入的 GEM)
  -> dma_buf_unmap_attachment (内核 DMA-BUF:解映射 sg_table)
  -> dma_buf_detach (内核 DMA-BUF:断开连接)
  -> dma_buf_put (内核 DMA-BUF:释放 dma_buf)
 
drm_prime_sg_to_page_array (转换 sg_table 为页面数组,deprecated)
  -> for_each_sgtable_page (内核 SG:遍历页面)
     -> sg_page_iter_page (内核 SG:获取页面)
 
drm_prime_sg_to_dma_addr_array (转换 sg_table 为 DMA 地址数组)
  -> for_each_sgtable_dma_page (内核 SG:遍历 DMA 页面)
     -> sg_page_iter_dma_address (内核 SG:获取 DMA 地址)

说明

图中顶层函数(如 drm_gem_prime_handle_to_fd)是用户空间 ioctl 入口(如 DRM_IOCTL_PRIME_HANDLE_TO_FD)。驱动回调(如 gem_prime_import_sg_table)由驱动实现(如 mtk_gem_prime_import_sg_table)。箭头表示直接调用;间接依赖(如错误处理)未展开。内核函数(如 dma_buf_attach)是底层依赖,DRM 函数(如 drm_gem_object_get)是框架协作。


补充说明

与 mtk_drm_gem.c 的关系:drm_prime.c 提供通用 PRIME 实现,mtk_drm_gem.c 通过 mtk_gem_prime_get_sg_table 和 mtk_gem_prime_import_sg_table 实现 MediaTek 特定的导入/导出逻辑。与 mtk_drm_fb.c 的关系:帧缓冲区 (mtk_drm_fb) 使用 GEM 对象,依赖 PRIME 共享缓冲区(如 mtk_fb_get_gem_obj)。潜在问题:红黑树缓存需妥善清理(drm_prime_remove_buf_handle),否则可能导致内存泄漏;drm_prime_sg_to_page_array 已废弃,驱动应优先使用 sg_table 或 DMA 地址。调试支持:通过 DRM 核心日志(DRM_ERROR)和红黑树遍历便于调试。

© 版权声明

相关文章

暂无评论

none
暂无评论...