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的博客。

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

相关推荐
IT成长日记2 小时前
【LVS入门宝典】LVS-TUN模式原理与配置:跨越网络界限的负载均衡解决方案
linux·网络·负载均衡·lvs·tun
IT成长日记2 小时前
【LVS入门宝典】LVS-TUN模式配置实战以及配置关键点:Real Server的路由表调整、ipip模块加载
linux·运维·服务器·lvs·tun
“αβ”2 小时前
网络编程套接字(三)---简单的TCP网络程序
linux·服务器·网络·c++·网络协议·tcp/ip·套接字
x-cmd3 小时前
[x-cmd] x-cmd 对 Xonsh 的支持
linux·运维·服务器·终端·命令行
打不了嗝 ᥬ᭄3 小时前
【Linux】网络基础
linux·运维·网络
风车带走过往4 小时前
部署开源PPTagent 生成工具
linux·开源
范桂飓4 小时前
在 Windows GPU 机器上运行 Linux CUDA
linux·运维·windows
小跌—4 小时前
Linux:线程池
linux
深思慎考5 小时前
LinuxC++项目开发日志——基于正倒排索引的boost搜索引擎(4——通过jsoncpp库建立搜索模块)
linux·c++·搜索引擎