第二章:GEM 与 TTM 概述:2.1 DRM 设备模型速览
本节定位:为 BO 主线提供最小必要的设备模型上下文。只覆盖后续章节反复依赖的三个核心概念(
drm_device、drm_file、设备节点),不涉及 PCI probe 流程和 KMS 初始化。
DRM 设备模型本质上遵循 Linux 标准的字符设备文件操作模型:内核通过 struct file_operations 向用户态暴露设备节点(/dev/dri/renderD128),用户态通过 open() / ioctl() / mmap() / close() 等标准系统调用与驱动交互。DRM 子系统在此基础上做了两层封装——drm_device 封装了硬件实例,drm_file 封装了每次 open 的客户端状态——但底层的 VFS → char dev → file_operations 调用链路与任何 Linux 字符设备完全一致。关于 Linux 设备驱动模型的基础知识(字符设备注册、file_operations、VFS 层交互等),请参阅 LDD(Linux Device Drivers)专栏,本节不再赘述。
1. 全景:从用户态 open 到内核对象

一句话关系:drm_device 是"一块 GPU",drm_file 是"一个进程对这块 GPU 的一次打开"。后续章节中所有 “per-client” 资源(handle、syncobj、VM、entity)都挂在 drm_file 上。
2. drm_device — GPU 硬件的内核抽象
drm_device 在整个驱动生命周期中只有 一个实例(per PCI function),驱动初始化时由 devm_drm_dev_alloc() 分配。
2.1 与后续章节相关的关键字段
| 字段 | 类型 | 与 BO 主线的关联 |
|---|---|---|
driver |
const struct drm_driver * |
Ch3:gem_create_object 回调决定 GEM 对象如何分配 |
primary / render / accel |
struct drm_minor * |
决定用户通过哪个设备节点访问 |
anon_inode |
struct inode * |
Ch3:GEM mmap 使用的匿名 inode(drm_vma_offset_manager 基于此) |
filelist |
struct list_head |
所有已打开的 drm_file 列表 |
driver_features |
u32 |
功能标志位,决定是否启用 GEM/RENDER/SYNCOBJ/GPUVA 等 |
2.2 driver_features 与 BO 主线的关联
// include/drm/drm_drv.h
DRIVER_GEM = BIT(0), // 启用 GEM 子系统 → Ch3
DRIVER_RENDER = BIT(3), // 创建 render 节点(compute 必须)
DRIVER_SYNCOBJ = BIT(5), // 启用 syncobj → Ch6
DRIVER_GEM_GPUVA = BIT(8), // 启用 GPUVM → Ch8
DRIVER_COMPUTE_ACCEL = BIT(7), // 计算加速专用节点(与 RENDER 互斥)
2.3 驱动嵌入模式
现代驱动不直接分配 drm_device,而是将其嵌入更大的驱动私有结构:
// drivers/gpu/drm/amd/amdgpu/amdgpu.h
struct amdgpu_device {
struct drm_device ddev; // 嵌入 drm_device
// ... 数千个 amdgpu 私有字段
};
// 获取方式:
struct amdgpu_device *adev = drm_to_adev(ddev); // container_of
Xe 驱动同理:struct xe_device 嵌入 struct drm_device。
3. drm_file — 客户端上下文(最关键)
每次用户态调用 open("/dev/dri/renderD128") 都会创建一个 drm_file 实例。这是 BO 主线中出现频率最高的关联对象。
3.1 与 BO 主线直接相关的字段
| 字段 | 含义 | 后续章节使用场景 |
|---|---|---|
object_idr |
GEM handle → drm_gem_object * 的映射表 |
Ch3:drm_gem_handle_create() 插入、GEM_CLOSE 删除 |
syncobj_idr |
syncobj handle → drm_syncobj * 的映射表 |
Ch6:drm_syncobj_create() / SYNCOBJ_WAIT |
prime |
PRIME 导入缓存(fd → handle) | Ch5:避免同一个 dma-buf 重复导入 |
driver_priv |
驱动私有指针 | Ch7/Ch8:amdgpu 在此存放 amdgpu_fpriv(含 VM、ctx_mgr) |
pid / client_id |
进程标识 | 调试:clients debugfs、fdinfo |
minor |
打开的设备节点 | 区分 primary/render/accel 权限 |
authenticated |
是否已认证 | render node 自动认证,primary 需要 master 授权 |
3.2 amdgpu 的 drm_file 扩展
// drivers/gpu/drm/amd/amdgpu/amdgpu.h
struct amdgpu_fpriv {
struct amdgpu_vm vm; // per-client GPU 虚拟地址空间 → Ch8
struct amdgpu_bo_va *prt_va; // PRT(Partially Resident Textures)
struct mutex bo_list_lock;
struct idr bo_list_handles; // CS 提交时的 BO 列表
struct amdgpu_ctx_mgr ctx_mgr; // 上下文管理 → Ch7 scheduler entity
struct amdgpu_eviction_fence_mgr evf_mgr; // eviction fence
};
关键洞察:在 amdgpu 中,每次 open() 都会创建独立的 amdgpu_vm(GPU 页表空间),这意味着不同进程有隔离的 GPU 地址空间。这是理解 Ch8 GPUVM 的前提。
3.3 drm_file 的生命周期
open("/dev/dri/renderD128")
→ drm_open()
→ drm_file_alloc() // 分配 drm_file,初始化 idr 表
→ drm_driver.open() // 驱动回调:amdgpu_driver_open_kms()
→ 分配 amdgpu_fpriv
→ 创建 amdgpu_vm(GPU 页表)
→ 初始化 ctx_mgr
... 用户操作(create BO / submit job / ...)...
close(fd)
→ drm_release()
→ drm_file_free()
→ 遍历 object_idr,释放所有 GEM handle(减引用)
→ 遍历 syncobj_idr,释放所有 syncobj
→ drm_driver.postclose() // 驱动回调
→ 销毁 amdgpu_vm
→ 销毁 ctx_mgr(flush 所有 entity)
→ kfree(amdgpu_fpriv)
对后续章节的启示:
- Ch3 GEM:handle 是 per-file 的——进程 A 的 handle 42 和进程 B 的 handle 42 指向不同对象
- Ch5 共享:PRIME fd 是跨进程的,但导入后生成的 handle 仍是 per-file 的
- Ch7 Scheduler:entity 通常在
amdgpu_ctx(属于ctx_mgr)中创建,随 file close 一起销毁 - Ch8 GPUVM:
amdgpu_vm是 per-file 的,file close 时所有 GPU 映射被拆除
4. 设备节点:render node vs primary node vs accel node
/dev/dri/
├── card0 → drm_minor (DRM_MINOR_PRIMARY) → 图形显示 + 渲染
├── renderD128 → drm_minor (DRM_MINOR_RENDER) → 纯渲染/计算(无需 root/master)
└── accel/
└── accel0 → drm_minor (DRM_MINOR_ACCEL) → 纯计算加速
| 节点类型 | 权限要求 | 典型用户 | 与本专栏关系 |
|---|---|---|---|
card0 (primary) |
需要 DRM master 或 authenticated | X11/Wayland 合成器 | 本专栏不涉及 |
renderD128 (render) |
任何用户可 open | Mesa/Vulkan/ROCm | 本专栏核心路径 |
accel0 (accel) |
任何用户可 open | 专用 AI 加速器 | 与 render 互斥,原理相同 |
对 BO 主线的影响:本专栏所有 IOCTL 操作(GEM create、CS submit、VM_BIND 等)都走 render node 路径。render node 的用户无需 DRM_AUTH(自动 authenticated),也无法执行 modeset 操作——这确保了 compute 用户不会干扰显示。
5. IOCTL 分发机制(极简版)
后续每章都会涉及特定 IOCTL(GEM_CREATE、CS、VM_BIND 等),这里简述调用路径以建立直觉:
用户态: ioctl(fd, DRM_IOCTL_AMDGPU_GEM_CREATE, &args)
│
▼ VFS → drm_ioctl()
│
├─ 查表: drm_ioctls[] (DRM 核心) + amdgpu_ioctls[] (驱动)
├─ 权限检查: DRM_AUTH / DRM_MASTER / DRM_RENDER_ALLOW
├─ 参数拷贝: copy_from_user → 内核栈
└─ 调用回调: amdgpu_gem_create_ioctl(dev, data, file_priv)
↑ ↑
drm_device drm_file
关键点:几乎所有 IOCTL 回调都接收 (struct drm_device *dev, void *data, struct drm_file *file_priv) 三个参数。理解了 drm_device 和 drm_file,就能看懂每个 IOCTL 的入口签名。
6. 小结:后续章节的阅读前提
读完本节,你需要记住的三件事:
drm_device= 一块 GPU。驱动加载时创建,生命周期等同于硬件设备。drm_file= 一次 open。每个进程的每次 open 都是独立的客户端上下文,handle table、syncobj table、GPU VM 都是 per-file 隔离的。- render node 是本专栏的入口。所有 BO 操作通过
/dev/dri/renderD128的 IOCTL 进入内核,回调签名统一为(dev, data, file_priv)。
有了这三个锚点,后续章节中遇到 file_priv->object_idr(Ch3)、file_priv->syncobj_idr(Ch6)、fpriv->vm(Ch8)时,就不会感到突兀。
7. 本专栏不涉及的内容:PCI Probe 流程
drm_device 的创建发生在 PCI probe 阶段——内核发现 PCI 设备后调用驱动的 probe() 回调(如 amdgpu_pci_probe()),在其中完成硬件初始化、devm_drm_dev_alloc() 分配 drm_device、drm_dev_register() 注册设备节点等一系列操作。
本专栏聚焦于 drm_device 创建之后 的 BO 生命周期管理,不涉及以下内容:
- PCI 设备枚举与资源分配(BAR 映射、中断配置)
- 驱动的
probe()/remove()回调实现 devm_drm_dev_alloc()/drm_dev_register()/drm_dev_unregister()流程- 电源管理(suspend/resume)与热插拔
关于 PCI probe 的完整分析,请参阅 PCIe 专栏。本专栏的起点是:GPU 硬件已经就绪,drm_device 已经注册,用户态可以 open() 设备节点——从这里开始讲 BO 的故事。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)