第五章:BO的共享:5.2.2 dma-buf 机制

1. 共享的一般性问题

dma-buf 框架是 Linux 内核为解决“跨设备零拷贝缓冲区共享”而设计的通用机制。这里强调一下该机制是内核级通用缓冲区共享框架(底层基础设施),从其实现文件的目录上drivers/dma-buf也可以看出这一点。dma-buf提供统一的共享接口,各种外设的驱动、各子系统都可以使用。

既然是共享,就涉及共享功能的通用问题:

序号问题答案
1共享的内容是什么?buffer
2共享方是谁?exporter
3使用方是谁?importer
4如何共享?dma-buf
5怎么控制同步访问?dma-fence

2. dma-buf 的核心原理

2.1 角色划分

该节回答了共享方和使用方的问题。

  • Exporter(导出者):负责分配和管理物理缓冲区的驱动(如 GPU、VPU、分配器等)。

  • Importer(导入者):需要访问该缓冲区的其他驱动(如显示控制器、ISP、DMA 控制器等)。

2.2 共享机制

该节回答了如何共享。 

dma-buf机制建立在 anon_inode机制之上,是 anon_inode技术的一个重要应用我们来回顾下anon_inode导出内核对象的步骤,并说明该步骤对应的dma-buf中的结构体或对象。

1. 自定义内核对象:这里是 struct dma_buf,描述一个可共享的物理缓冲区;

2. 定义 file_operations : 对应的是dma_buf_fops;

static const struct file_operations dma_buf_fops = {
	.release	= dma_buf_file_release,
	.mmap		= dma_buf_mmap_internal,
	.llseek		= dma_buf_llseek,
	.poll		= dma_buf_poll,
	.unlocked_ioctl	= dma_buf_ioctl,
	.compat_ioctl	= compat_ptr_ioctl,
	.show_fdinfo	= dma_buf_show_fdinfo,
};

3. 获取fd:核心逻辑是如下:

//全局变量
static struct vfsmount *dma_buf_mnt;
//1. 获取匿名的inode
struct inode *inode = alloc_anon_inode(dma_buf_mnt->mnt_sb);
//2. 打开inode,得到file
struct file *file = alloc_file_pseudo(
                        inode, dma_buf_mnt, "dmabuf",
				        flags, &dma_buf_fops);
//3. 创建dma_buf,设置dma_buf的内部成员
struct dma_buf *dmabuf = kzalloc(alloc_size, GFP_KERNEL);
...
//4. dma_buf与file做双向关联
file->private_data = dmabuf;
dmabuf->file = file;
//5. 获取未用的fd,然后关联到file
fd = get_unused_fd_flags(flags);
fd_install(fd, file);

整个链路从用户态可见的fd开始:

  • 用户空间通过 fd 调用特定ioctl来获取buffer;

  • fd 在内核中对应 struct file;

  • file->private_data 指向 struct dma_buf;

  • dma_buf 结构体内部再通过 priv  指针指向实际的 buffer object(显存对象、内存块等)。

用户空间 fd
   │
   ▼
struct file
   │
   ▼
struct dma_buf (file->private_data 指向)
   │
   ▼
buffer object (bo) (dma_buf->priv指向)

获取到的fd就可以交给用户态了,用户态程序通过fd传递机制共享给其他进程,就实现了共享。先用这个anon_inode机制去理解dma-buf机制,是比较好的一个学习路线,抓住了核心,不会被庞杂的代码消灭了学习的热情。

2.3 同步机制

该节回答了如何同步访问。第六章我们会专门讲解同步机制。

  • dma-buf 支持 fence(同步栅栏)机制,保证多设备并发访问时的数据一致性。

  • Exporter/Importer 可通过 dma_resv 对象管理读写 fence,协调访问时序。

3. bo的共享流程

dma-buf 框架为 exporter 和 importer 提供标准化的缓冲区共享、映射、同步等接口。下面的两个流程中涉及函数和结构体都是实际的实现。我并不打算详细讲这些函数和结构体,一个是这些命名都见名知意;二个是在理解上面anon_inode机制后,这些可看可不看。

3.1  Exporter(导出者)流程

  • 场景:某个驱动(exporter)创建了底层 buffer,想把它导出为 dma-buf 供其他驱动/用户空间使用。

Exporter driver has BO (private)
    │
    ├─ prepare export info: struct dma_buf_export_info {
    │        .priv = pointer to BO,
    │        .ops = &dma_buf_ops (callbacks),
    │        .resv = optional reservation object or NULL
    │    }
    │
    └─ dmabuf = dma_buf_export(&exp_info)
            ├-- create anon file (alloc_file_pseudo)
            ├-- allocate struct dma_buf
            ├-- if !resv: allocate internal dma_resv and dma_resv_init(dmabuf->resv)
            └-- file->private_data = dmabuf; dmabuf->file = file
    ▼
User space gets fd: fd = dma_buf_fd(dmabuf)

3.2 Importer(导入者)流程

importer流程要复杂些,但不要紧,可以先记住主流程,等理解了5.2.3 dma-buf的实现后再来看细节。

  • 场景:用户空间拿到一个 dma-buf 的 fd,Importer(设备驱动)想要把它附着到自己的设备并为 DMA 做准备。

User space fd
    │
    ▼
kernel: dma_buf_get(fd)         // fget -> file -> file->private_data -> struct dma_buf *
    │
    ▼
struct dma_buf *dmabuf = file->private_data

(Attach phase)  [Importer MUST NOT hold dmabuf->resv here]
    │
    ├─> dma_buf_attach(dmabuf, dev)   // wrapper -> dma_buf_dynamic_attach()
    │       ├-- may call dmabuf->ops->attach(dmabuf, attach)  (EXPORTER callback)
    │       └-- list_add(&attach->node, &dmabuf->attachments)   // protected by dma_resv_lock around list op
    │
    ▼
(Prepare mapping / pin) [Importer MUST hold dmabuf->resv for map/pin]
    │
    ├─ hold dma-resv: dma_resv_lock(dmabuf->resv, NULL)
    │
    ├─ optional: dma_buf_pin(attach) // calls exporter ops->pin() while resv locked
    │
    ├─ sg_table = dma_buf_map_attachment(attach, direction)
    │       └-- __map_dma_buf -> dmabuf->ops->map_dma_buf(attach, direction)
    │           (exporter callback invoked WITH dmabuf->resv locked)
    │
    └─ release dma-resv: dma_resv_unlock(dmabuf->resv)
    ▼
(Use DMA) --> device performs DMA using returned sg_table
    │
(Teardown mapping) [Importer MUST hold dmabuf->resv]
    │
    ├─ hold dma-resv
    ├─ dma_buf_unmap_attachment(attach, sg_table, direction)
    │       └-- exporter ops->unmap_dma_buf (called WITH resv locked)
    ├─ if pinned: dma_buf_unpin(attach) // exporter ops->unpin() WITH resv locked
    └─ release dma-resv
    ▼
(Detach)  [Importer MUST NOT hold dmabuf->resv]
    │
    └─ dma_buf_detach(dmabuf, attach) // calls exporter ops->detach() (invoked with unlocked resv)
    │
    ▼
dma_buf_put(dmabuf)  // fput(file)

//中文版
导入者驱动
    │
    ├─ 接收文件描述符(来自用户空间或其他驱动)
    │
    ├─ 调用 dma_buf_get(fd)
    │  └─ 获取 struct dma_buf 指针
    │
    ├─ 调用 dma_buf_attach()
    │  └─ 创建 dma_buf_attachment
    │     └─ 通知导出者有新设备附加(调用 ops->attach)
    │
    ├─ 调用 dma_buf_map_attachment()
    │  └─ 获取 sg_table(scatter-gather 表)
    │     └─ 导出者返回适合此设备的物理地址映射
    │
    └─ 设备使用 sg_table 进行 DMA 访问

4. 关键特性与应用

4.1 基于文件描述符的共享

  • 安全性:fd 不能被猜测,必须显式传递(通过 UNIX socket 的 SCM_RIGHTS)

  • 引用计数:内核自动管理 fd 的生命周期

  • 跨进程:fd 可以传递给其他进程,实现安全的跨进程共享

4.2 Scatter-Gather 支持

  • 非连续内存:支持物理上不连续的内存(通过 sg_table

  • IOMMU 支持:可利用 IOMMU 将分散的物理页映射为连续的设备地址

  • 灵活性:不同设备可能得到不同的映射

4.3 同步机制

  • dma-fence:异步操作完成的信号机制

  • 隐式同步:使用dma_resv自动处理设备间的依赖

正是因为是底层机制,dma-buf 被广泛应用于多个子系统:

  • DRM:GPU 缓冲区共享

  • V4L2(Video for Linux 2):视频帧缓冲共享

  • 媒体子系统:ISP、编解码器等设备间的缓冲区共享

  • DMA 引擎:通用 DMA 控制器的缓冲区共享

  • Android ION/DMA Heaps:用户空间缓冲区分配器

下一节看下dma_buf的关键结构体和函数接口的使用。

在bash中执行`cd smartmon-vhe-installer-5.2.2`提示`No such file or directory`,可按以下方法解决: ### 检查目录名拼写 要保证目录名的拼写准确无误,包括大小写。因为在Linux系统里,文件名和目录名是区分大小写的。可使用`ls`命令列出当前目录下的所有文件和目录,查看是否存在`smartmon-vhe-installer-5.2.2`目录: ```bash ls ``` ### 确认当前工作目录 要确保当前工作目录是包含`smartmon-vhe-installer-5.2.2`目录的父目录。可以使用`pwd`命令查看当前工作目录: ```bash pwd ``` 若当前目录并非预期的目录,可使用`cd`命令切换到正确的目录: ```bash cd /path/to/parent/directory ``` 这里的`/path/to/parent/directory`是`smartmon-vhe-installer-5.2.2`目录的父目录路径。 ### 检查目录是否存在 若`ls`命令未显示`smartmon-vhe-installer-5.2.2`目录,可能该目录确实不存在。可以通过搜索整个文件系统来确认: ```bash find / -type d -name "smartmon-vhe-installer-5.2.2" 2>/dev/null ``` 这个命令会在整个文件系统中搜索名为`smartmon-vhe-installer-5.2.2`的目录,并将错误信息重定向到`/dev/null`以避免显示不必要的错误。 ### 检查文件系统挂载情况 若该目录位于外部存储设备(如USB驱动器、NFS共享等)上,要确保设备已正确挂载。可以使用`df -h`命令查看已挂载的文件系统: ```bash df -h ``` 若设备未挂载,可使用`mount`命令进行挂载。 ### 检查权限问题 确保当前用户有访问`smartmon-vhe-installer-5.2.2`目录的权限。可以使用`ls -l`命令查看目录的权限: ```bash ls -l /path/to/smartmon-vhe-installer-5.2.2 ``` 若权限不足,可联系系统管理员或使用`sudo`命令获取更高的权限。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeplyMind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值