3. drm_atomic_state 结构体详解:结构体成员、引用计数、生命周期管理

drm_atomic_state 是 DRM 原子提交框架的核心数据结构,它承载了一次原子操作中所有 CRTC、Plane、Connector 的完整状态快照。理解其内部成员、引用计数机制以及生命周期管理,是正确使用原子 API 和避免内存泄漏、UAF(Use-After-Free)等问题的前提。

3.1 核心结构体成员

drm_atomic_state 定义在 include/drm/drm_atomic.h 中,其关键成员可分为三类:状态对象指针数组、提交控制标志、以及内部管理字段。

成员名称 类型 说明
crtcs struct drm_crtc_state *[] 指向每个 CRTC 的新状态指针数组,索引为 CRTC 的 index。
planes struct drm_plane_state *[] 指向每个 Plane 的新状态指针数组,索引为 Plane 的 index。
connectors struct drm_connector_state *[] 指向每个 Connector 的新状态指针数组,索引为 Connector 的 index。
num_connector int 本次提交中实际涉及的 Connector 数量。
allow_modeset bool 是否允许本次提交触发 modeset(模式设置)。
legacy_cursor_update bool 是否为 legacy 光标更新(用于兼容旧接口)。
async_update bool 是否为异步更新(不等待 vblank)。
commit_work struct work_struct 用于异步提交的工作队列项。
ref struct kref 引用计数,用于管理对象的生命周期。
关键理解: crtcs、planes、connectors 三个数组是状态存储的核心。每个数组元素要么是 NULL(表示该对象不参与本次提交),要么指向一个通过 drm_atomic_get_crtc_state() 等函数获取的、引用计数已增加的新状态对象。

3.2 引用计数机制

drm_atomic_state 使用内核标准的 struct kref 进行引用计数管理。引用计数的变化遵循以下规则:

  • 初始计数为 1:当通过 drm_atomic_state_alloc() 分配时,引用计数被设置为 1。
  • 获取状态时增加:调用 drm_atomic_get_crtc_state()、drm_atomic_get_plane_state()、drm_atomic_get_connector_state() 时,内部会调用 drm_atomic_state_get() 增加引用计数。
  • 提交时转移所有权:当调用 drm_atomic_commit() 时,框架会接管 state 对象的所有权,并在提交完成后(同步或异步)调用 drm_atomic_state_put() 释放引用。
  • 错误路径需手动释放:如果在构建状态过程中发生错误(如内存不足、校验失败),驱动必须调用 drm_atomic_state_put() 来释放已分配的状态。
常见陷阱: 驱动在 atomic_check 回调中如果提前返回错误,必须确保之前通过 drm_atomic_get_*_state() 获取的状态对象引用已被正确释放。否则会导致引用泄漏,最终引发内核内存泄漏。

3.3 生命周期管理

一个典型的 drm_atomic_state 生命周期包含以下阶段:

  1. 分配阶段:驱动或用户态通过 IOCTL 触发,调用 drm_atomic_state_alloc() 创建空状态对象。
  2. 构建阶段:通过 drm_atomic_add_affected_*() 和 drm_atomic_get_*_state() 填充状态对象数组。
  3. 校验阶段:调用 drm_atomic_check_only() 或 drm_atomic_commit() 触发校验,驱动 atomic_check 回调被调用。
  4. 提交阶段:校验通过后,状态被提交到硬件。同步提交直接应用;异步提交通过 commit_work 延迟执行。
  5. 清理阶段:提交完成后,框架调用 drm_atomic_state_put() 减少引用计数。当计数归零时,drm_atomic_state_free() 被调用,释放所有子状态对象和自身内存。
生命周期要点: 在异步提交场景中,drm_atomic_state 的生命周期会跨越多个内核调度周期。驱动在 atomic_flush 或 atomic_commit_tail 回调中必须持有对 state 的引用,直到硬件完成状态切换。通常框架会在 drm_atomic_helper_commit_hw_done() 之后才释放引用。

3.4 状态对象关系图

下图展示了 drm_atomic_state 与 CRTC、Plane、Connector 状态对象之间的引用关系:

drm_atomic_state 与子状态对象关系图 drm_atomic_state ref (kref) | allow_modeset | async_update crtcs[0] struct drm_crtc_state * crtcs[1] struct drm_crtc_state * crtcs[2] struct drm_crtc_state * planes[0] struct drm_plane_state * planes[1] struct drm_plane_state * planes[2] struct drm_plane_state * connectors[0] struct drm_connector_state * connectors[1] struct drm_connector_state * connectors[2] struct drm_connector_state * 生命周期管理要点 每个子状态对象通过 drm_atomic_get_*_state() 获取,引用计数自动增加 提交完成后,drm_atomic_state_put() 递减引用,计数归零时自动释放所有子状态

3.5 引用计数操作 API

内核提供了以下 API 用于管理 drm_atomic_state 的引用计数:

API 函数 作用 调用时机
drm_atomic_state_alloc() 分配并初始化 state,ref 设为 1 IOCTL 入口或驱动内部创建提交时
drm_atomic_state_get() 增加引用计数(kref_get) 获取子状态、异步提交持有引用时
drm_atomic_state_put() 减少引用计数(kref_put),归零时释放 提交完成、错误路径、驱动不再需要时
drm_atomic_state_put_unlocked() 不带锁的版本,用于已持有锁的上下文 在 atomic_check 等已加锁回调中
最佳实践: 在驱动自定义的 atomic_commit_tail 实现中,如果需要将 state 传递给异步工作队列,务必在提交工作前调用 drm_atomic_state_get(),并在工作完成后调用 drm_atomic_state_put()。这样可以防止 state 在异步处理过程中被提前释放。

3.6 生命周期状态机

下图展示了 drm_atomic_state 从创建到销毁的完整状态转换:

drm_atomic_state 生命周期状态机 ALLOCATED BUILDING CHECKING COMMITTING COMPLETED FREED drm_atomic_add_affected drm_atomic_check_only 校验通过 硬件完成 drm_atomic_state_put() → ref==0 校验失败 → 手动 put

3.7 驱动中的典型使用模式

以下代码片段展示了驱动在 atomic_commit 回调中如何正确管理 state 的生命周期:

static int my_atomic_commit(struct drm_device *dev,
                            struct drm_atomic_state *state,
                            bool nonblock)
{
    int ret;

    // 1. 校验阶段:框架已调用 atomic_check
    ret = drm_atomic_helper_check(dev, state);
    if (ret) {
        // 校验失败,框架会自动释放 state
        return ret;
    }

    // 2. 非阻塞提交:增加引用,交给工作队列
    if (nonblock) {
        drm_atomic_state_get(state);
        if (!queue_work(system_unbound_wq, &state->commit_work)) {
            drm_atomic_state_put(state);
            return -EBUSY;
        }
        return 0;
    }

    // 3. 阻塞提交:直接执行
    drm_atomic_helper_commit_tail(state);
    return 0;
}
注意: 在非阻塞提交中,驱动必须确保 commit_work 回调函数在完成硬件操作后调用 drm_atomic_state_put()。如果工作队列被取消或延迟,可能导致 state 泄漏或 UAF。建议使用 drm_atomic_helper_commit() 辅助函数,它已经正确处理了这些细节。

3.8 总结

  • 结构体核心:drm_atomic_state 通过三个指针数组(crtcs、planes、connectors)管理所有子状态对象。
  • 引用计数:使用 kref 机制,通过 drm_atomic_state_get()/put() 管理生命周期,计数归零时自动释放。
  • 生命周期阶段:分配 → 构建 → 校验 → 提交 → 清理,每个阶段都有明确的 API 调用和引用计数变化规则。
  • 驱动责任:在错误路径和异步提交中必须正确管理引用,避免泄漏或 UAF。