2. DRM框架基础:核心数据结构与驱动模型
DRM(Direct Rendering Manager)是Linux内核中负责图形显示与GPU管理的核心子系统。理解DRM框架,首先需要掌握其两大基石:核心数据结构(描述设备与用户会话)和驱动模型(组件注册与生命周期管理)。本章将深入剖析drm_device与drm_file的结构设计,并梳理驱动注册的完整流程。
drm_file会话访问drm_device管理的显示与渲染资源。
2.1 drm_device:设备级核心结构
drm_device是DRM框架中代表一个显示控制器或GPU设备的顶层结构体。每个物理或虚拟的DRM设备在内核中对应一个drm_device实例,它聚合了所有子组件(CRTC、Encoder、Connector、Plane、FB等)的指针、锁、内存管理对象以及驱动私有数据。
| 字段 | 类型 | 说明 |
|---|---|---|
dev |
struct device * |
指向内核通用设备结构,用于电源管理、DMA API等 |
driver |
struct drm_driver * |
指向驱动实现的操作函数表(file_operations、ioctl等) |
mode_config |
struct drm_mode_config |
显示模式配置管理,包含CRTC/Encoder/Connector/Plane链表 |
primary |
struct drm_minor * |
主设备号对应的minor,用于用户态打开/dev/dri/card0 |
render |
struct drm_minor * |
渲染设备minor,用于GPU计算上下文(/dev/dri/renderD128) |
filelist_mutex |
struct mutex |
保护已打开文件句柄链表的互斥锁 |
filelist |
struct list_head |
当前打开此设备的所有drm_file实例链表 |
vblank |
struct drm_vblank_crtc * |
Vblank事件管理数组,每个CRTC一个 |
event_lock |
spinlock_t |
保护事件队列的自旋锁 |
drm_device采用“组合模式”,将显示管线中的各个硬件抽象对象(CRTC、Encoder等)作为子组件挂载到mode_config下。驱动初始化时只需注册这些子组件,DRM核心自动完成拓扑关系构建。
2.2 drm_file:用户会话上下文
每当用户态进程通过open("/dev/dri/card0")打开DRM设备时,内核会创建一个drm_file实例。它代表一个用户会话,隔离不同进程的GPU资源访问权限,并记录该会话的认证状态、已分配的对象句柄、事件订阅等信息。
| 字段 | 类型 | 说明 |
|---|---|---|
authenticated |
bool |
是否通过DRM认证(Master权限) |
master |
struct drm_master * |
指向Master权限对象,控制模式设置权限 |
pid |
struct pid * |
打开设备的进程PID |
object_idr |
struct idr |
句柄到GEM对象的映射表(用户态可见的整数句柄) |
table_lock |
struct mutex |
保护object_idr和事件队列的锁 |
event_list |
struct list_head |
待传递给用户态的事件链表(Vblank、Page Flip等) |
prime |
struct drm_prime_file_private |
dma-buf导入/导出句柄管理 |
drm_file才能执行模式设置(如DRM_IOCTL_MODE_SETCRTC)。普通渲染进程只能通过Render节点(/dev/dri/renderD128)提交GPU命令,无法修改显示配置。
2.3 驱动模型与组件注册流程
DRM驱动采用组件化注册模型。驱动初始化时,需要依次完成:设备探测 → 分配drm_device → 注册子组件(CRTC/Encoder/Connector/Plane) → 调用drm_dev_register()暴露给用户态。下图展示了完整的注册流程与数据结构关系:
2.4 组件注册关键API与顺序
驱动开发者在bind()回调中必须遵循严格的注册顺序,否则会导致drm_dev_register()失败或用户态无法正确枚举显示设备。以下是标准注册步骤:
- 分配
drm_device:调用drm_dev_alloc(),传入drm_driver操作表。 - 初始化模式配置:调用
drm_mode_config_init(),设置min_width、max_height等限制。 - 注册CRTC:
drm_crtc_init_with_planes(),绑定主平面和光标平面。 - 注册Encoder:
drm_encoder_init(),指定编码器类型(TMDS、LVDS、DP等)。 - 注册Connector:
drm_connector_init(),设置检测回调get_modes()。 - 注册Plane:
drm_universal_plane_init(),声明支持的像素格式和缩放能力。 - 建立连接关系:
drm_connector_attach_encoder(),将Connector绑定到Encoder。 - 注册设备:
drm_dev_register(),创建设备节点并暴露给用户态。
// 典型DRM驱动 bind() 回调示例(简化)
static int mygpu_bind(struct device *dev) {
struct drm_device *drm;
struct mygpu_private *priv;
int ret;
// 1. 分配 drm_device
drm = drm_dev_alloc(&mygpu_driver, dev);
if (IS_ERR(drm))
return PTR_ERR(drm);
priv = drm->dev_private;
drm_mode_config_init(drm);
drm->mode_config.min_width = 640;
drm->mode_config.max_width = 4096;
// 2. 注册CRTC
ret = mygpu_crtc_init(drm);
if (ret) goto err_free;
// 3. 注册Encoder
ret = mygpu_encoder_init(drm);
if (ret) goto err_free;
// 4. 注册Connector并绑定Encoder
ret = mygpu_connector_init(drm);
if (ret) goto err_free;
// 5. 注册Plane
ret = mygpu_plane_init(drm);
if (ret) goto err_free;
// 6. 最终注册设备
ret = drm_dev_register(drm, 0);
if (ret) goto err_free;
dev_set_drvdata(dev, drm);
return 0;
err_free:
drm_dev_put(drm);
return ret;
}
drmModeGetResources()时能更快获取完整拓扑。
2.5 用户态打开流程与drm_file创建
当用户态调用open("/dev/dri/card0")时,内核触发以下流程:
- VFS层根据设备号找到DRM主设备对应的
file_operations(即drm_stub_fops)。 drm_open()被调用,分配drm_file实例,初始化object_idr、event_list等字段。- 调用驱动提供的
driver->open()回调,允许驱动分配私有上下文(如GPU命令队列)。 - 将
drm_file加入drm_device->filelist链表。 - 返回文件描述符给用户态,后续ioctl通过
drm_ioctl()分发到具体处理函数。
// drm_open() 核心逻辑(位于 drm_fops.c)
static int drm_open(struct inode *inode, struct file *filp) {
struct drm_device *drm = drm_minor_acquire(iminor(inode));
struct drm_file *priv;
int ret;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
priv->pid = get_pid(task_pid(current));
idr_init(&priv->object_idr);
mutex_init(&priv->table_lock);
INIT_LIST_HEAD(&priv->event_list);
filp->private_data = priv;
priv->minor = drm->primary;
if (drm->driver->open) {
ret = drm->driver->open(drm, priv);
if (ret) goto err_free;
}
mutex_lock(&drm->filelist_mutex);
list_add(&priv->lhead, &drm->filelist);
mutex_unlock(&drm->filelist_mutex);
return 0;
err_free:
kfree(priv);
return ret;
}
cat /sys/kernel/debug/dri/0/clients可以查看当前打开DRM设备的所有drm_file会话,包括PID、认证状态和内存使用统计。
2.6 数据结构关系总结
下表总结了drm_device与drm_file在生命周期中的角色差异:
| 维度 | drm_device | drm_file |
|---|---|---|
| 生命周期 | 设备探测到移除(持久) | open()到close()(临时) |
| 数量 | 每个GPU/显示控制器一个 | 每个打开设备的进程一个 |
| 主要职责 | 管理硬件资源、模式配置、中断 | 隔离用户会话、句柄映射、事件传递 |
| 锁粒度 | 全局mode_config.mutex、event_lock |
每会话table_lock |
| 用户态可见 | 通过/dev/dri/card0设备节点 |
通过文件描述符(fd) |
理解这两个核心数据结构及其注册流程,是后续深入学习DRM模式设置(Chapter 3)、GEM内存管理(Chapter 4)和GPU调度(Chapter 5)的基础。驱动开发者必须确保组件注册顺序正确、锁使用得当,才能构建稳定高效的图形显示系统。