2. DRM Prime 框架介绍

DRM Prime 是 DRM 子系统中用于实现 DMA-BUF 导出与导入的核心机制。它解决了传统图形栈中跨设备、跨驱动共享显存时存在的内存拷贝与同步开销问题,是现代嵌入式图形系统(如基于 DRM/KMS 的显示控制器与 GPU 协作)的基础设施。

2.1 DRM Prime 的由来

在早期的 Linux 图形栈中,不同设备(如 GPU、显示控制器、视频编解码器)之间的缓冲区共享通常依赖以下方式:

  • V4L2 导出/导入:仅适用于视频设备,与 DRM 驱动不通用。
  • ION 内存管理器:Android 专用,未合入主线内核。
  • 用户态拷贝:通过 CPU 在用户空间搬运数据,性能差、功耗高。

上述方案均存在 跨驱动框架不兼容 或 性能瓶颈 的问题。DRM Prime 应运而生,它基于内核通用 DMA-BUF 框架,为 DRM 驱动提供了一套标准化的缓冲区共享接口:

  • 导出 (Export):将 DRM GEM 对象转换为一个 dma_buf 文件描述符,供其他驱动或用户态使用。
  • 导入 (Import):从文件描述符获取 dma_buf,并将其绑定到本地 GEM 对象,实现零拷贝访问。
关键点:DRM Prime 不是一个新的内存分配器,而是 DMA-BUF 框架在 DRM 子系统中的具体实现。它复用了 GEM 对象的内存管理能力,并通过 dma_buf 接口实现跨设备共享。

2.2 DRM Prime 与 DMA-BUF 的关系

理解 DRM Prime 必须首先理解 DMA-BUF。DMA-BUF 是内核提供的一个通用框架,用于在设备驱动之间共享 DMA 缓冲区(物理连续或通过 IOMMU/SMMU 映射的缓冲区)。DRM Prime 是 DMA-BUF 在 DRM 子系统中的 封装与扩展。

维度 DMA-BUF(通用框架) DRM Prime(DRM 实现)
定位 内核级缓冲区共享基础设施 DRM 驱动对 DMA-BUF 的标准化接口
核心对象 struct dma_buf struct drm_gem_object + struct dma_buf
用户态接口 无直接用户态接口 DRM_IOCTL_PRIME_HANDLE_TO_FD
DRM_IOCTL_PRIME_FD_TO_HANDLE
主要使用者 V4L2、ALSA、RDMA、DRM 等 GPU、Display Controller、Codec
生命周期管理 引用计数 + 文件描述符 GEM 对象引用 + DMA-BUF 引用

下图展示了 DRM Prime 在 DMA-BUF 框架中的位置:

DRM Prime 与 DMA-BUF 关系架构 用户空间 (Userspace) 系统调用层:DRM_IOCTL_PRIME_HANDLE_TO_FD / FD_TO_HANDLE DRM Core (drm_prime.c) DMA-BUF Core (dma-buf.c) 调用 DRM 驱动 (gem_prime_export/import) 其他驱动 (V4L2, ALSA, ...) 硬件设备 (GPU / Display Controller / VPU / Camera) 说明:DRM Prime 是 DMA-BUF 在 DRM 子系统中的具体实现,通过标准 ioctl 连接用户空间与内核驱动

2.3 核心数据结构

DMA-BUF 框架的核心由三个数据结构组成:struct dma_buf、struct dma_buf_attachment 和 struct sg_table。DRM Prime 在此基础上增加了 GEM 对象的关联。

2.3.1 struct dma_buf

struct dma_buf 是 DMA-BUF 框架的 根对象,代表一个被共享的 DMA 缓冲区。它封装了缓冲区的元数据、访问操作和生命周期管理。

struct dma_buf {
    size_t size;                    // 缓冲区大小(字节)
    struct file *file;              // 关联的文件对象(用于引用计数和 fd 映射)
    struct list_head attachments;   // 所有 attachment 的链表头
    const struct dma_buf_ops *ops;  // 操作函数指针(map, unmap, release 等)
    struct mutex lock;              // 保护 attachments 链表的锁
    void *priv;                     // 私有数据(DRM 中指向 drm_gem_object)
    // ... 其他字段(如 resv 用于同步)
};
字段 说明 DRM Prime 中的角色
size 缓冲区大小,导出时由 GEM 对象决定 与 drm_gem_object->size 一致
file 文件对象,用户态通过 fd 引用 通过 dma_buf_fd() 创建
attachments 记录所有导入该缓冲区的设备 每个导入设备创建一个 attachment
ops 底层操作函数,由导出驱动实现 DRM 驱动通过 gem_prime_export 回调提供
priv 指向导出驱动的私有数据 指向 drm_gem_object

2.3.2 struct dma_buf_attachment

struct dma_buf_attachment 代表一个 导入设备 对 dma_buf 的绑定关系。每个导入设备在访问缓冲区之前,必须先创建一个 attachment,并通过它获取 scatterlist(用于 DMA 操作)。

struct dma_buf_attachment {
    struct dma_buf *dmabuf;         // 指向关联的 dma_buf
    struct device *dev;             // 导入设备的 struct device
    struct list_head node;          // 挂入 dma_buf->attachments 链表
    // ... 其他字段(如 dma_map_attrs, priv 等)
};
字段 说明 典型值/用途
dmabuf 所属的 dma_buf 对象 由 dma_buf_attach() 自动设置
dev 导入设备的 device 结构体 用于 IOMMU/SMMU 地址空间映射
node 链表节点,用于挂入 dmabuf->attachments 遍历所有导入者时使用
重要概念:一个 dma_buf 可以被多个设备同时导入,每个导入设备对应一个 dma_buf_attachment。通过 dma_buf_map_attachment() 获取的 sg_table 是设备相关的,不同设备可能得到不同的物理地址映射(取决于 IOMMU 配置)。

2.3.3 struct sg_table

struct sg_table 是 DMA-BUF 框架中用于描述 物理内存布局 的核心结构。它包含一个 scatterlist 数组,每个条目描述一段连续的物理内存区域。

struct sg_table {
    struct scatterlist *sgl;    // scatterlist 数组
    unsigned int nents;         // 实际使用的条目数
    unsigned int orig_nents;    // 原始条目数(可能包含合并前的条目)
};

在 DRM Prime 中,sg_table 的获取流程如下:

  1. 导出驱动调用 dma_buf->ops->map_dma_buf() 生成 sg_table。
  2. 导入驱动通过 dma_buf_map_attachment() 获取该 sg_table。
  3. 导入驱动使用 sg_dma_address() 和 sg_dma_len() 获取 DMA 地址和长度。

2.4 数据结构关系图

下图展示了 DRM Prime 中核心数据结构之间的关联:

DRM Prime 核心数据结构关系 用户空间 fd struct file struct dma_buf size, file, attachments, ops, priv priv drm_gem_object (导出驱动私有) attachments struct dma_buf_attachment dev: 导入设备1, dmabuf, priv struct dma_buf_attachment dev: 导入设备2, dmabuf, priv struct sg_table 关键路径:用户态 fd → struct file → struct dma_buf → struct dma_buf_attachment → struct sg_table

2.5 DRM Prime 操作流程概览

基于上述数据结构,DRM Prime 的导出与导入操作遵循以下步骤:

步骤 导出 (Export) 导入 (Import)
1 用户态调用 DRM_IOCTL_PRIME_HANDLE_TO_FD 用户态调用 DRM_IOCTL_PRIME_FD_TO_HANDLE
2 DRM Core 查找 GEM 对象 DRM Core 通过 fd 获取 dma_buf
3 调用 dma_buf_export() 创建 dma_buf 调用 dma_buf_get() 增加引用计数
4 通过 dma_buf_fd() 获取文件描述符 创建新的 GEM 对象并关联 dma_buf
5 返回 fd 给用户态 返回 GEM handle 给用户态
注意事项:导入操作不会复制缓冲区内容,而是通过 dma_buf_attachment 建立设备与缓冲区的映射关系。实际的 DMA 操作(如 GPU 渲染、显示扫描)由导入设备通过 sg_table 发起,整个过程无需 CPU 介入数据搬运。

2.6 小结

本章介绍了 DRM Prime 的由来、与 DMA-BUF 的关系以及三个核心数据结构:

  • struct dma_buf:代表共享缓冲区本身,是跨设备共享的根对象。
  • struct dma_buf_attachment:代表导入设备与缓冲区的绑定关系,每个导入设备一个。
  • struct sg_table:描述物理内存布局,供导入设备进行 DMA 操作。