MTK Linux DRM分析(二十)- KMS drm_mm.c drm_vma_manager.c

一、代码分析

1. 代码分析

drm_mm.c 的分析

概述:这是一个通用的简单内存范围分配器(range allocator),专为 DRM(Direct Rendering Manager)子系统设计,用于管理 GPU 或图形驱动中的内存区域分配。它不进行实际内存分配,而是跟踪和管理范围(ranges),支持预分配块的保留、颜色调整(用于缓存域等限制)、对齐限制和范围限制。内部使用间隔树(interval tree)和红黑树(rb-tree)来优化查找和插入操作。算法简单,适合 GPU 的特殊需求,但不追求极致性能(如碎片化优化)。它支持自底向上(bottom-up)和自顶向下(top-down)的分配策略,以及扫描模式用于驱逐(evict)对象以腾出空间。关键数据结构
drm_mm:分配器主结构,包含间隔树、孔洞栈(hole stack)和红黑树,用于跟踪节点和空闲孔洞。drm_mm_node:表示分配的节点,包含起始地址、大小、颜色等。
特性
支持预保留节点(reserve node),用于接管固件配置。支持扫描模式(scan mode),用于 LRU(Least Recently Used)驱逐,以腾出连续空间。调试功能:内存泄漏跟踪和打印分配状态。非线程安全:驱动程序需自行加锁。
潜在问题:碎片化可能导致性能问题,但适合 GPU 的线性分配需求。

drm_vma_manager.c 的分析

概述:这是一个 VMA(Virtual Memory Area)偏移管理器,用于将驱动程序特定的内存区域映射到用户空间的线性地址空间。它提供偏移量(offsets),允许用户空间通过 mmap 系统调用访问 DRM 设备内存。它确保区域不重叠、大小合适,并避免与内核 mm-core 冲突。内部使用 drm_mm 作为后端进行实际范围分配,但添加了访问控制(allow/revoke)和快速查找(rb-tree)。它不用于 VMEM(视频内存)对象放置,仅用于用户空间线性 VM 映射。关键数据结构
drm_vma_offset_manager:管理器结构,包含 drm_mm 作为地址空间后端,以及读写锁。drm_vma_offset_node:节点结构,嵌入 drm_mm_node,并添加访问控制的 rb-tree(用于文件标签)。drm_vma_offset_file:表示允许访问的文件标签(tag),用于权限管理。
特性
支持页面对齐的偏移分配。访问控制:通过 allow/revoke 管理文件上下文的访问权限,防止未授权 mmap。查找优化:使用 rb-tree 加速偏移查找。与 mm-core 兼容:确保 VM 是线性的,避免多管理器冲突。
潜在问题:节点分配/销毁由调用者负责;必须严格匹配 allow 和 revoke 以避免内存泄漏。

两个代码之间的联系

依赖关系:drm_vma_manager.c 强烈依赖 drm_mm.c,将其作为后端使用。具体来说:

drm_vma_offset_manager 内部的 vm_addr_space_mm 是 drm_mm 的实例,用于实际管理偏移范围。drm_vma_offset_manager_init 调用 drm_mm_init 初始化后端。drm_vma_offset_manager_destroy 调用 drm_mm_takedown 清理后端。drm_vma_offset_add 调用 drm_mm_insert_node 插入节点。drm_vma_offset_remove 调用 drm_mm_remove_node 移除节点。drm_vma_offset_lookup_locked 间接使用 drm_mm 的间隔树(通过手动遍历 rb-tree 来模拟查找)。
功能互补:drm_mm.c 提供低级范围分配(纯内存管理),而 drm_vma_manager.c 在其基础上添加用户空间映射支持、访问控制和快速查找。drm_mm.c 是通用工具,可独立使用;drm_vma_manager.c 是其上层封装,专为 VMA 偏移设计。使用场景:在 DRM 驱动中,drm_mm.c 用于内部内存管理(如缓冲区分配),drm_vma_manager.c 用于暴露给用户空间的映射(如帧缓冲区或共享内存)。如果驱动需要用户空间访问,必须结合两者使用。联系点总结:drm_vma_manager.c 是 drm_mm.c 的应用层扩展,提供额外的用户空间功能,而不重复实现核心分配逻辑。这避免了代码冗余,并保持 DRM 子系统的模块化。

2. 函数调用关系图(基于文本)

以下是基于两个代码内容的函数调用关系图,以树状结构表示。图中重点突出主要函数及其内部调用(包括辅助函数和宏)。由于代码使用宏(如 INTERVAL_TREE_DEFINE)和内联函数,我将它们简化表示。调用图按代码文件分开,最后在联系中整合。

drm_mm.c 的函数调用关系图


drm_mm_init
├── add_hole (初始化 head_node 的孔洞)
└── INIT_LIST_HEAD / RB_ROOT_CACHED (初始化链表和树)
 
drm_mm_reserve_node
├── find_hole_addr (查找孔洞)
├── add_hole (如果有剩余空间,添加新孔洞)
├── rm_hole (移除旧孔洞)
├── drm_mm_interval_tree_add_node (插入间隔树)
├── list_add (添加到节点链表)
└── save_stack (调试栈跟踪)
 
drm_mm_insert_node_in_range
├── first_hole (根据模式查找第一个孔洞)
├── next_hole (迭代下一个孔洞)
├── drm_mm_interval_tree_add_node (插入间隔树)
├── add_hole (添加剩余孔洞)
├── rm_hole (移除旧孔洞)
├── list_add (添加到节点链表)
└── save_stack (调试栈跟踪)
 
drm_mm_remove_node
├── drm_mm_interval_tree_remove (从间隔树移除)
├── list_del (从节点链表移除)
├── rm_hole (移除相关孔洞)
└── add_hole (合并新孔洞)
 
drm_mm_replace_node
├── rb_replace_node_cached (替换间隔树节点)
├── list_replace (替换节点链表)
├── rb_replace_node_cached (替换孔洞大小树)
└── rb_replace_node (替换孔洞地址树)
 
drm_mm_scan_init_with_range
└── (仅设置 scan 结构,无外部调用)
 
drm_mm_scan_add_block
├── list_prev_entry (获取前一个节点)
├── __list_del_entry (临时移除节点以扩大孔洞)
├── mm->color_adjust (可选颜色调整)
└── (更新 hit_start/hit_end)
 
drm_mm_scan_remove_block
├── list_prev_entry (获取前一个节点)
├── list_add (恢复节点到链表)
└── (检查是否重叠 hit 范围)
 
drm_mm_scan_color_evict
├── list_for_each_entry (查找匹配孔洞)
└── mm->color_adjust (调整颜色以查找重叠节点)
 
drm_mm_takedown
└── show_leaks (调试泄漏检查)
 
drm_mm_print
├── drm_mm_dump_hole (打印孔洞)
└── drm_mm_for_each_node (迭代节点打印)

辅助函数/宏:包括 add_hole、rm_hole、best_hole、find_hole_addr、next_hole 等,用于内部树/链表操作。宏如 INTERVAL_TREE_DEFINE 定义间隔树操作;DECLARE_NEXT_HOLE_ADDR 定义 next_hole_high_addr/next_hole_low_addr。

drm_vma_manager.c 的函数调用关系图


drm_vma_offset_manager_init
├── rwlock_init (初始化锁)
└── drm_mm_init (调用 drm_mm.c 初始化后端)
 
drm_vma_offset_manager_destroy
└── drm_mm_takedown (调用 drm_mm.c 清理后端)
 
drm_vma_offset_lookup_locked
├── (手动遍历 mgr->vm_addr_space_mm 的 rb_root)
└── (返回匹配节点,无外部调用)
 
drm_vma_offset_add
├── write_lock (加写锁)
├── drm_mm_insert_node (调用 drm_mm.c 插入节点)
└── write_unlock (解锁)
 
drm_vma_offset_remove
├── write_lock (加写锁)
├── drm_mm_remove_node (调用 drm_mm.c 移除节点)
└── write_unlock (解锁)
 
drm_vma_node_allow
├── write_lock (加写锁)
├── (遍历 rb_tree 查找或插入 drm_vma_offset_file)
├── rb_insert_color (插入 rb-tree)
└── write_unlock (解锁)
 
drm_vma_node_revoke
├── write_lock (加写锁)
├── (遍历 rb_tree 查找并递减计数)
├── rb_erase (如果计数为0,移除节点)
└── write_unlock (解锁)
 
drm_vma_node_is_allowed
├── read_lock (加读锁)
├── (遍历 rb_tree 查找标签)
└── read_unlock (解锁)

辅助函数/宏:包括 rb-tree 操作(如 rb_link_node、rb_insert_color、rb_erase),用于访问控制链表。锁(rwlock)用于并发保护。

整体函数调用关系图(整合联系)


drm_vma_offset_manager_init
└── drm_mm_init (从 drm_vma_manager.c 调用 drm_mm.c)
 
drm_vma_offset_manager_destroy
└── drm_mm_takedown (从 drm_vma_manager.c 调用 drm_mm.c)
 
drm_vma_offset_add
└── drm_mm_insert_node (从 drm_vma_manager.c 调用 drm_mm.c 的插入变体)
 
drm_vma_offset_remove
└── drm_mm_remove_node (从 drm_vma_manager.c 调用 drm_mm.c)
 
drm_vma_offset_lookup_locked
└── (间接使用 drm_mm.c 的间隔树,通过 rb-tree 遍历查找节点)

说明:在整合图中,突出 drm_vma_manager.c 如何调用 drm_mm.c 的核心函数,实现偏移管理。其他函数(如访问控制)独立于 drm_mm.c,但节点嵌入 drm_mm_node,确保一致性。

二、drm_mm.c drm_vma_manager.c drm_prime.c drm_gem.c drm_frambuffer.c drm_vm.c关系和区别

这些文件都是 Linux DRM 内存管理子系统里的一部分,它们主要围绕 显存/系统内存分配、用户态映射、跨进程/跨设备共享 这几个核心点展开。下面我帮你梳理一下它们的 关系和区别


1.
drm_mm.c

核心功能:一个通用的内存分配器(范围分配器)。

机制:管理一段线性地址空间(通常是显存 VRAM 或 I/O 映射区),支持分配/释放子区间。

应用场景

GPU VRAM 分配

GTT(Graphics Translation Table)地址分配

不关心内存的具体类型,只是管理一块“区间”。

👉 它是 DRM 内存管理的最底层工具,很多驱动(包括 GEM/TTM)都会用。


2.
drm_vma_manager.c

核心功能:管理 用户态 mmap 的虚拟地址区间

作用:当用户通过
mmap()
映射 GEM buffer 到进程空间时,用
drm_vma_offset_manager
来管理这些 VMA 区域。

区别于
drm_mm.c


drm_mm
管理 显存/IO 区域


drm_vma_manager
管理 用户进程虚拟地址区间


3.
drm_prime.c

核心功能:处理 跨设备/跨进程 buffer 共享,基于 DMA-BUF 框架。

功能点

将 GEM 对象导出为 dma-buf (
drm_gem_prime_export
)

将外部 dma-buf 导入为 GEM (
drm_gem_prime_import
)

场景

Camera ISP 抓取图像 buffer → GPU 使用 → Display 显示

Android 的 Gralloc/GraphicBufferManager 中大量使用


4.
drm_gem.c

核心功能:提供 GEM (Graphics Execution Manager) 框架的通用支持。

内容

GEM 对象创建、销毁、引用计数

内核对象和用户句柄之间的映射


mmap

ioctl
框架的对接

地位:几乎所有现代 DRM 驱动都基于 GEM。

联系

GEM 内部可能用
drm_mm
来分配显存

GEM 的 mmap 依赖
drm_vma_manager

GEM 的共享依赖
drm_prime


5.
drm_framebuffer.c

核心功能:管理 Framebuffer(帧缓冲)对象

作用

将 GEM buffer 绑定成一个 framebuffer

提供 fb create/destroy API

用于 KMS (Kernel Mode Setting) → CRCT/LCD Controller 显示

关系

framebuffer 本质上就是一个 GEM buffer 的封装,附带分辨率、像素格式等元数据。

所以 framebuffer = (GEM buffer + 显示参数)。


6.
drm_vm.c

核心功能:提供 DRM 的 vm_ops(虚拟内存操作)实现

用途:当用户 mmap GEM buffer 时,处理
fault()
回调,将物理页映射到用户态。

关系


drm_vm.c
+
drm_vma_manager.c
一起实现 GEM → 用户态的 mmap。

GEM 驱动一般会提供
drm_gem_vm_ops
,由
drm_vm.c
提供。



         用户空间
        +-----------------+
        | mmap/ioctl/FD   |
        +--------+--------+
                 |
         drm_vma_manager.c   (管理用户态VMA区间)
                 |
         drm_vm.c           (处理缺页, 页表映射)
                 |
         drm_gem.c          (GEM对象:创建/销毁/引用)
           |       
           |        drm_prime.c   (跨设备共享, dma-buf)
           |
         drm_mm.c           (显存/地址区间分配)
           |
         驱动平台内存 (VRAM/GTT/系统内存)
 
   drm_framebuffer.c   (把GEM封装成Framebuffer供显示)

🔑 总结

drm_mm.c → 最底层的线性区间分配器(显存/GTT)

drm_vma_manager.c + drm_vm.c → 负责 mmap 和用户态虚拟地址管理

drm_gem.c → 提供 GEM 对象管理的统一框架

drm_prime.c → 处理 GEM buffer 跨设备共享(dma-buf)

drm_framebuffer.c → 把 GEM buffer 变成 framebuffer,最终输出到屏幕

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容