AMD KFD的BO设计分析系列3-4:Linux DRM GEM mmap 与 drm_vma_offset_node 机制详解

1. 背景与核心概念

在 Linux 图形驱动开发中,DRM(Direct Rendering Manager)子系统负责管理 GPU 资源、内存分配和用户空间与内核空间的交互。GEM(Graphics Execution Manager)是 DRM 的一套内存管理框架,主要用于分配、管理和映射显存对象(GEM objects)。

用户空间应用(如 X server、Wayland compositor 或 OpenGL/Vulkan 驱动)通常需要将 GPU buffer 映射到自己的地址空间,以便直接访问显存。这一过程通过 mmap 系统调用实现,而 DRM/GEM 通过一套虚拟 offset 机制(fake offset)来安全地管理和查找这些对象。drm_gem_mmap 就是核心的 mmap 入口函数。

要理解本文,首先要理解mmap的功能和原理,这个文章一抓一大把,本专栏里也水了一篇:linux mmap 的匿名映射和设备映射-CSDN博客。这里给出一个解释:mmap实现了物理内存上的文件内容映射到用户态空间(VMA),用户态可直接访问的效果;本质是以page为单位将VMA与物理page进行关联的机制。那问题就来了,根据该函数的传入参数:

  1. 要映射的文件:要映射的是设备文件,也就是drm_device打开后对应到drm_file;

  2. 映射的文件长度:文件长度通常是要访问的GEM对象的大小;

  3. 映射的文件偏移:这里就有疑问了,就不是一个真正的文件,哪有偏移? 这就是上面fake offset的作用了,这也是本篇文章要讲清楚的一个技术点。

2. drm_gem_mmap 实现流程详解

drm_gem_mmap 的主要职责是:根据用户空间传入的 mmap offset,查找对应的 GEM 对象,并完成内存映射。代码如下:

cpp 复制代码
int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
	struct drm_file *priv = filp->private_data;
	struct drm_device *dev = priv->minor->dev;
	struct drm_gem_object *obj = NULL;
	struct drm_vma_offset_node *node;
	int ret;

	if (drm_dev_is_unplugged(dev))
		return -ENODEV;

    // 1. 通过 offset 查找 GEM 对象
	drm_vma_offset_lock_lookup(dev->vma_offset_manager);
	node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager,
						  vma->vm_pgoff,
						  vma_pages(vma));

	drm_vma_offset_unlock_lookup(dev->vma_offset_manager);

    // 2. 权限检查
	if (!drm_vma_node_is_allowed(node, priv)) {
		drm_gem_object_put(obj);
		return -EACCES;
	}

    // 3. 完成内存映射
	ret = drm_gem_mmap_obj(obj, 
                   drm_vma_node_size(node) << PAGE_SHIFT,
			       vma);

	drm_gem_object_put(obj);

	return ret;
}

代码实现的流程概述如下:

  1. 用户空间调用 mmap(fd, offset, size, ...),offset 为 DRM/GEM 分配的 fake offset。

  2. 内核进入 DRM 设备的 drm_gem_mmap 实现。

  3. 通过 vma_offset_manager 查找 offset 对应的 vma_node(上面代码中步骤1)

  4. 通过 vma_node 反查 GEM 对象,检查引用计数和权限(上面代码中步骤2)

  5. 调用 drm_gem_mmap_obj,设置 VMA 的回调和页表,完成映射(上面代码中步骤3)

下面具体描述。

2.1 通过 offset 查找 GEM 对象

  • drm_vma_offset_manager:DRM 设备的 VMA offset 管理器,负责管理所有 GEM 对象的虚拟 offset 区间。

  • drm_vma_offset_exact_lookup_locked :根据 mmap offset(vma->vm_pgoff)和映射页数,查找对应的 drm_vma_offset_nodevma->vm_pgoff是如下函数返回的,可以看到就是drm_mm_node的start的偏移,因为start是以page为单位,所以左移了。

cpp 复制代码
static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
{
	return ((__u64)node->vm_node.start) << PAGE_SHIFT;
}
  • drm_vma_offset_node :每个 GEM 对象都包含一个 drm_vma_offset_nodel类型的vma_node成员,记录其虚拟 offset 区间(start, size),用于 mmap 查找。

2.2 权限检查

  • 检查当前文件(进程)是否有权限访问该对象(防止跨进程非法映射)。函数实现如下,就是在node->vm_files中查找。这些drm_file是在gem创建或者import过程中通过drm_vma_node_allow加入的node->vm_files中的。
cpp 复制代码
bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node,
			     struct drm_file *tag)
{
	struct drm_vma_offset_file *entry;
	struct rb_node *iter;

	read_lock(&node->vm_lock);

	iter = node->vm_files.rb_node;
	while (likely(iter)) {
		entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb);
		if (tag == entry->vm_tag)
			break;
		else if (tag > entry->vm_tag)
			iter = iter->rb_right;
		else
			iter = iter->rb_left;
	}

	read_unlock(&node->vm_lock);

	return iter;
}

2.3 完成映射

  • 调用 drm_gem_mmap_obj,设置 VMA 的回调、权限和页表等。

3. drm_vma_offset_node 的作用与机制

3.1 结构定义与核心字段

drm_vma_offset_node 是 DRM/GEM 内存管理的核心结构,定义如下(见 drm_vma_manager.h):

cpp 复制代码
struct drm_vma_offset_node {
	rwlock_t vm_lock;
	struct drm_mm_node vm_node; //实际物理地址和大小
	struct rb_root vm_files; //存放可访问该node的drm_file
	void *driver_private;
};

3.2 作用与设计动机

  • 虚拟 offset 映射:用户空间 mmap 时,传入的 offset 并不是物理地址,而是 DRM/GEM 分配的"fake offset"。这样可以避免用户空间直接访问物理显存,提升安全性和灵活性。

  • 对象查找:通过 offset 能快速定位到对应的 GEM 对象,无需遍历所有对象。

  • 权限隔离:每个对象的 offset 区间唯一,防止跨进程、跨对象的非法访问。

  • 高效管理:红黑树结构支持高效的插入、查找和删除操作,适合大量 buffer 管理。

3.3 生命周期与管理流程

  • 分配 :GEM 对象创建时,调用 drm_gem_create_mmap_offset,分配一个唯一的 offset 区间,并将 vma_node 插入到 vma_offset_manager 的红黑树中。

  • 查找 :mmap 时,通过 offset 查找 vma_node,进而定位 GEM 对象。

  • 权限控制 :每个 vma_node 记录允许访问的文件(进程),通过 drm_vma_node_is_allowed 检查。

  • 释放 :对象销毁时,调用 drm_gem_free_mmap_offset,从红黑树中移除 vma_node,释放 offset 区间。

4. 总结

drm_gem_mmapdrm_vma_offset_node 是 Linux DRM/GEM 子系统内存管理的基石。通过虚拟 offset 映射机制,内核实现了安全、高效、灵活的显存对象管理。驱动开发者在实现自定义 GEM 对象、支持 mmap 映射时,需充分理解 offset 管理机制、对象生命周期和权限控制,确保系统安全与性能。

**一句话总结:fake offset就是drm_mm_node的start值,mmap实现的是用户态VMA与GEM对象进行了绑定;**真正实现虚拟地址到物理页的页表填写可能并不在该函数中,可能是page_fault中进行的。这个可以看下何小龙的mmap的博客。

如有帮助,请三连:点赞、收藏、加关注。

相关推荐
倔强的石头1064 分钟前
【Linux指南】动静态库系列(二):从源码复用到目标文件复用:为什么需要把 .o 打包成库
linux·运维·服务器
HLC++12 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室13 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy16 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕17 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫18 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生18 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1119 小时前
linux基础IO
linux·运维·服务器
BelongPanda21 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯21 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算