Linux dma-buf核心函数实现分析

1. 前言

Linux dma-buf 框架原理、实现与应用 一文中,介绍了dma-buf的框架和应用场景,这里再啰嗦一句:

DMA-BUF 允许一个驱动(Exporter,导出者)将自己管理的物理内存缓冲区以文件描述符(fd)的形式导出,其他驱动(Importer,导入者)通过 fd 获取该缓冲区的访问权,实现跨设备的高效数据共享。其核心思想是:

  • 以文件描述符为统一接口,屏蔽底层物理内存的分配与管理细节。

  • 通过引用计数、同步机制(如 dma_fence)、附件(attachment)等,保证多方安全、同步地访问同一块物理内存。

  • 支持 mmap、vmap、CPU/GPU 访问、同步等多种操作。

2. 导出与导入的完整流程示例

2.1 导出(Exporter)流程

  1. 驱动分配物理缓冲区

    例如 GPU 驱动分配一块显存或系统内存。最常见的就是分配一个drm_gem_object。

  2. 填充 dma_buf_export_info

    • 设置 priv 指向私有缓冲区对象。

    • 设置 ops 为导出者实现的操作函数表。

    • 设置 sizeownerexp_name 等。

  3. 调用 dma_buf_export

    根据dma_buf_export_info,返回 struct dma_buf *dmabuf

  4. 调用 dma_buf_fd

    获得 fd,返回给用户空间。

  5. 用户空间传递 fd 给其他驱动/进程

2.2 导入(Importer)流程

  1. 用户空间将 fd 传递给导入驱动(如通过 IOCTL)

  2. 导入驱动调用 dma_buf_get

    通过dma_buf_get(fd) 获取 struct dma_buf *,增加引用计数。

  3. 调用 dma_buf_attach

    在 dma_buf 上创建 attachment,加入 attachments 链表。

  4. 调用 dma_buf_map_attachment

    获取物理页表(sg_table),进行 DMA 映射。

  5. 数据传输/处理

  6. 完成后 detach、put

    调用 dma_buf_detach、dma_buf_put,释放引用。

2.3 代码示例(伪代码)

导出端:

struct my_buffer *buf = my_buffer_alloc(...);

DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.priv = buf;
exp_info.ops = &my_dma_buf_ops;
exp_info.size = buf->size;
exp_info.owner = THIS_MODULE;
exp_info.exp_name = "my_exporter";

struct dma_buf *dmabuf = dma_buf_export(&exp_info);

int fd = dma_buf_fd(dmabuf, O_CLOEXEC);
return fd; // 返回给用户空间

导入端:

int fd = ...; // 用户空间传入

struct dma_buf *dmabuf = dma_buf_get(fd);
struct dma_buf_attachment *attach = dma_buf_attach(dmabuf, my_device);
struct sg_table *sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
// 进行 DMA 操作
dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);

dma_buf_detach(dmabuf, attach);
dma_buf_put(dmabuf);

3. 导出端核心函数分析

核心函数入口dma_buf_export。关键调用路径和函数如下:

dma_buf_export
    dma_buf_getfile
        alloc_anon_inode
        alloc_file_pseudo
struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
{
	struct dma_buf *dmabuf;
	struct dma_resv *resv = exp_info->resv;
	struct file *file;
	size_t alloc_size = sizeof(struct dma_buf);
	int ret;

    ...
    //1. 生成file
	file = dma_buf_getfile(exp_info->size, exp_info->flags);
    //2. 创建dmabuf
	dmabuf = kzalloc(alloc_size, GFP_KERNEL);

    //3. 把传入的参数设置给dmabuf
	dmabuf->priv = exp_info->priv;
	dmabuf->ops = exp_info->ops;
	dmabuf->size = exp_info->size;
	dmabuf->exp_name = exp_info->exp_name;
	dmabuf->owner = exp_info->owner;
	spin_lock_init(&dmabuf->name_lock);
	init_waitqueue_head(&dmabuf->poll);
	dmabuf->cb_in.poll = dmabuf->cb_out.poll = &dmabuf->poll;
	dmabuf->cb_in.active = dmabuf->cb_out.active = 0;
	INIT_LIST_HEAD(&dmabuf->attachments);

    //数据同步访问用,可忽略
	if (!resv) {
		dmabuf->resv = (struct dma_resv *)&dmabuf[1];
		dma_resv_init(dmabuf->resv);
	} else 
		dmabuf->resv = resv;

	ret = dma_buf_stats_setup(dmabuf, file);
    
    //4. 设置双向关联
	file->private_data = dmabuf;
	dmabuf->file = file;

	__dma_buf_list_add(dmabuf);

	return dmabuf;
}

有个dma_buf(这时已经是一个buf[存储在dmabuf的priv里]和file结合体了),就可以用dma_buf_fd来关联fd了。

int dma_buf_fd(struct dma_buf *dmabuf, int flags)
{
	int fd;

	if (!dmabuf || !dmabuf->file)
		return -EINVAL;

	fd = get_unused_fd_flags(flags);
	fd_install(fd, dmabuf->file);

	return fd;
}

4. 导入端核心函数分析

importer侧做的关键动作就是拿到fd后找到对应的dmabuf。这个动作的实现是dma_buf_get。

struct dma_buf *dma_buf_get(int fd)
{
	struct file *file;

	file = fget(fd);

	if (!file)
		return ERR_PTR(-EBADF);

	if (!is_dma_buf_file(file)) {
		fput(file);
		return ERR_PTR(-EINVAL);
	}
    //exporter时已经把dmabuf放到了file->private_data
	return file->private_data;
}

拿到dmabuf就可以继续访问dmabuf里的私有数据priv,可能就是一个drm_gem_object了。

5.设计哲学与安全性分析

5.1 统一接口与抽象

  • 以 fd 为统一抽象,简化用户空间与内核、驱动间的交互。
  • 通过 file_operations 实现多态,支持多种导出者/导入者。

5.2 引用计数与生命周期管理

  • 通过 file 的引用计数,自动管理 dma_buf 生命周期。
  • 导入者/导出者无需关心底层释放时机,防止悬挂指针。

5.3 同步与一致性

  • 通过 dma_resv、dma_fence等机制,保证多方访问的一致性与同步。
  • 支持隐式同步(poll、fence)与显式同步(ioctl、sync_file)。

5.4 安全性与隔离

  • 只暴露 fd,用户空间无法直接访问物理地址。
  • 通过权限、引用计数、锁等机制,防止资源泄漏与竞争。

DMA-BUF 框架通过 dma_buf_exportdma_buf_fddma_buf_getfile 等核心函数,实现了跨设备、跨驱动、跨进程的高效缓冲区共享。其设计充分利用了 Linux 文件系统、引用计数、同步等基础设施,既保证了高性能,又兼顾了安全性和易用性。

Everything is a file in Linux。好好理解这句话,建议读者去按下面的顺序去学习:

1. 理解linux里的file结构体以及与其相关的结构体;

2. 理解anon_inode,参见相关博文;

3. 理解本文dma_buf;

接下来理解DRM框架里的prime机制,该机制就是在dma-buf的机制上实现的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

DeeplyMind

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

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

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

打赏作者

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

抵扣说明:

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

余额充值