目录

Linux-dma-buf-框架原理实现与应用详解

Linux dma-buf 框架原理、实现与应用详解

1. 背景与意义

1.1 异构系统与缓冲区共享的挑战

在现代 SoC、嵌入式、图形和多媒体系统中,CPU、GPU、VPU、ISP、DMA 控制器等多个硬件单元需要高效地共享和传递大块数据(如图像帧、视频流、AI 张量等)。如果每个设备都维护独立的缓冲区,数据在设备间传递时就需要频繁拷贝,极大浪费带宽和内存,降低系统性能。

1.2 dma-buf 的目标

dma-buf 框架是 Linux 内核为解决“跨设备零拷贝缓冲区共享”而设计的通用机制。其核心目标包括:

  • 跨驱动、跨设备共享物理缓冲区,避免冗余拷贝。
  • 标准化缓冲区导出、导入、映射、同步等操作,简化驱动开发。
  • 支持多种内存类型(系统内存、显存、专用区域等),适应复杂硬件架构。
  • 支持同步机制(fence),保证多设备并发访问的数据一致性。

2. dma-buf 的核心原理

2.1 角色划分

  • Exporter(导出者):负责分配和管理物理缓冲区的驱动(如 GPU、VPU、分配器等)。
  • Importer(导入者):需要访问该缓冲区的其他驱动(如显示控制器、ISP、DMA 控制器等)。
  • dma-buf 核心框架:为 exporter 和 importer 提供标准化的缓冲区共享、映射、同步等接口。

2.2 共享机制

  • Exporter 通过 dma_buf_export() 导出一个物理缓冲区,获得一个 struct dma_buf 对象,并为其分配一个匿名文件描述符(fd)。
  • Importer 通过 fd 调用 dma_buf_get() 或 dma_buf_attach(),获得对该缓冲区的访问权。
  • Importer 可通过 dma_buf_map_attachment() 将缓冲区映射到自己的设备地址空间,实现零拷贝访问。
  • 多个 importer 可同时 attach 同一个 dma-buf,实现多设备并发访问。

dma-buf机制建立在 anon_inode之上,是 anon_inode技术一个重要应用示例。

2.3 同步机制

  • dma-buf 支持 fence(同步栅栏)机制,保证多设备并发访问时的数据一致性。
  • Exporter/Importer 可通过 dma_resv 对象管理读写 fence,协调访问时序。

3. 关键数据结构与实现

3.1 struct dma_buf_ops

dma_buf_ops 定义了 dma-buf 的所有操作接口,由 exporter 实现。

struct dma_buf_ops {
    int (*attach)(struct dma_buf *, struct dma_buf_attachment *);
    void (*detach)(struct dma_buf *, struct dma_buf_attachment *);
    int (*pin)(struct dma_buf_attachment *attach);
    void (*unpin)(struct dma_buf_attachment *attach);
    struct sg_table *(*map_dma_buf)(struct dma_buf_attachment *, enum dma_data_direction);
    void (*unmap_dma_buf)(struct dma_buf_attachment *, struct sg_table *, enum dma_data_direction);
    void (*release)(struct dma_buf *);
    int (*begin_cpu_access)(struct dma_buf *, enum dma_data_direction);
    int (*end_cpu_access)(struct dma_buf *, enum dma_data_direction);
    int (*mmap)(struct dma_buf *, struct vm_area_struct *vma);
    int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map);
    void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
};
  • attach/detach:设备 attach/detach 缓冲区时的回调。
  • pin/unpin:锁定/解锁缓冲区,防止被移动。
  • map/unmap_dma_buf:将缓冲区映射到设备地址空间。
  • release:最后一个引用释放时的清理回调。
  • begin/end_cpu_access:CPU 访问前后的同步操作(如 cache flush)。
  • mmap/vmap/vunmap:内核/用户空间映射支持。

3.2 struct dma_buf

dma_buf 是 dma-buf 的核心对象,描述一个可共享的物理缓冲区。

struct dma_buf {
    size_t size;
    struct file *file;
    struct list_head attachments;
    const struct dma_buf_ops *ops;
    unsigned vmapping_counter;
    struct iosys_map vmap_ptr;
    const char *exp_name;
    const char *name;
    spinlock_t name_lock;
    struct module *owner;
    struct list_head list_node;
    void *priv;
    struct dma_resv *resv;
    wait_queue_head_t poll;
    struct dma_buf_poll_cb_t cb_in, cb_out;
    // ... 其他成员
};
  • size:缓冲区大小。
  • file:匿名文件,用于 fd 传递和引用计数。
  • attachments:所有 attach 到该缓冲区的设备链表。
  • ops:操作函数集。
  • resv:同步对象,管理 fence。
  • poll/cb_in/cb_out:支持用户空间 poll/epoll 事件通知。

3.3 struct dma_buf_attachment

描述一个设备与 dma-buf 的关联关系。

struct dma_buf_attachment {
    struct dma_buf *dmabuf;
    struct device *dev;
    struct list_head node;
    bool peer2peer;
    const struct dma_buf_attach_ops *importer_ops;
    void *importer_priv;
    void *priv;
};
  • dmabuf:关联的 dma-buf。
  • dev:关联的设备。
  • priv:exporter/importer 的私有数据。

3.4 struct dma_buf_export_info

导出 dma-buf 时的参数描述。

struct dma_buf_export_info {
    const char *exp_name;
    struct module *owner;
    const struct dma_buf_ops *ops;
    size_t size;
    int flags;
    struct dma_resv *resv;
    void *priv;
};

4. 主要 API 及用法

4.1 Exporter 侧

4.1.1 导出缓冲区
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &my_dma_buf_ops;
exp_info.size = buffer_size;
exp_info.priv = my_priv_data;
struct dma_buf *dmabuf = dma_buf_export(&exp_info);
4.1.2 获取 fd
int fd = dma_buf_fd(dmabuf, O_CLOEXEC);
4.1.3 释放缓冲区
dma_buf_put(dmabuf);

4.2 Importer 侧

4.2.1 通过 fd 获取 dma-buf
struct dma_buf *dmabuf = dma_buf_get(fd);
4.2.2 设备 attach
struct dma_buf_attachment *attach = dma_buf_attach(dmabuf, dev);
4.2.3 映射到设备地址空间
struct sg_table *sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
4.2.4 解除映射与 detach
dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
dma_buf_detach(dmabuf, attach);
4.2.5 释放 dma-buf
dma_buf_put(dmabuf);

4.3 CPU 访问同步

dma_buf_begin_cpu_access(dmabuf, DMA_FROM_DEVICE);
// CPU 访问缓冲区
dma_buf_end_cpu_access(dmabuf, DMA_FROM_DEVICE);

4.4 用户空间 mmap

mmap(fd, ...); // 通过 fd 直接 mmap 到用户空间

5. dma-buf 的同步机制

5.1 dma_resv 与 fence

  • dma-buf 通过 struct dma_resv 管理同步 fence,协调多设备并发访问。
  • Exporter/Importer 在读写缓冲区前后添加/等待 fence,保证数据一致性。
  • 支持隐式同步(如 GPU/显示管线)和显式同步(如用户空间 fence)。

5.2 begin_cpu_access/end_cpu_access

  • 在 CPU 访问前后,需调用 begin_cpu_access/end_cpu_access,由 exporter 实现 cache flush、内存屏障等操作,保证数据一致性。

6. dma-buf 的典型应用场景

6.1 GPU-显示管线零拷贝

  • GPU 渲染输出帧缓冲区,通过 dma-buf fd 传递给显示控制器(DRM/KMS),实现零拷贝显示。

6.2 摄像头-ISP-GPU 协作

  • 摄像头驱动分配帧缓冲区,ISP 处理后通过 dma-buf fd 传递给 GPU 进行后处理或 AI 推理。

6.3 多媒体编解码

  • VPU 解码器输出缓冲区,通过 dma-buf fd 传递给 GPU/显示/AI 单元,实现高效视频播放和处理。

6.4 用户空间跨进程共享

  • 用户空间应用通过 dma-buf fd 在不同进程间共享大块数据(如图像、视频帧、AI 张量等)。

7. dma-buf 的内核实现细节

7.1 文件描述符与引用计数

  • 每个 dma-buf 对象都对应一个匿名文件(anon_inode),通过 fd 传递和引用计数。anon_inode技术参见博文: 。
  • 内核通过 get_dma_buf/dma_buf_put 管理生命周期。

7.2 设备 attach/detach

  • 每个 importer attach 时,exporter 可检查设备兼容性、DMA 约束等,拒绝不支持的设备。
  • detach 时清理相关资源。

7.3 映射与同步

  • map/unmap_dma_buf 负责将缓冲区映射到 importer 设备地址空间,支持多种内存类型和 DMA 方向。
  • begin/end_cpu_access 负责 cache flush、内存屏障等同步操作。

7.4 poll/epoll 支持

  • dma-buf 支持 poll/epoll 机制,用户空间可通过 poll/epoll 监控缓冲区 fence 状态,实现异步事件通知。

8. 总结

  • dma-buf 是 Linux 内核支持异构系统、跨设备零拷贝缓冲区共享的核心机制。
  • 通过标准化的导出、导入、映射、同步等接口,极大简化了驱动开发和系统集成。
  • 支持多种内存类型、同步机制和异步事件通知,适应复杂硬件和高性能场景。
  • 正确实现和使用 dma-buf 能显著提升系统性能、带宽利用率和开发效率。