Linux图形子系统之dma-buf

引言

dma-buf是linux内核提供的一种机制,用于不同模块实现内存共享。它提供生产者和消费者模式来实现不同模块对内存共享同时,不用关心各个模块的内部实现细节,从而解耦。在drm框架中也集成了dma-buf方式的内存管理。

1 导出buf

drm通过DRM_IOCTL_PRIME_HANDLE_TO_FD实现将一个gem对象句柄转为dma-buf的fd。其中会调用struct drm_driver的prime_handle_to_fd回调,drm_gem_prime_handle_to_fd函数是prime_handle_to_fd回调的默认实现函数:

int drm_gem_prime_handle_to_fd(struct drm_device *dev,
			       struct drm_file *file_priv, uint32_t handle,
			       uint32_t flags,
			       int *prime_fd)
{
    # 略略略... ...

	mutex_lock(&file_priv->prime.lock);
	obj = drm_gem_object_lookup(file_priv, handle);
	if (!obj)  {
		ret = -ENOENT;
		goto out_unlock;
	}

	dmabuf = drm_prime_lookup_buf_by_handle(&file_priv->prime, handle);
	if (dmabuf) {
		get_dma_buf(dmabuf);
		goto out_have_handle;
	}

	mutex_lock(&dev->object_name_lock);
	if (obj->import_attach) {
		dmabuf = obj->import_attach->dmabuf;
		get_dma_buf(dmabuf);
		goto out_have_obj;
	}

	if (obj->dma_buf) {
		get_dma_buf(obj->dma_buf);
		dmabuf = obj->dma_buf;
		goto out_have_obj;
	}

	dmabuf = export_and_register_object(dev, obj, flags);
	if (IS_ERR(dmabuf)) {
		ret = PTR_ERR(dmabuf);
		mutex_unlock(&dev->object_name_lock);
		goto out;
	}

out_have_obj:
	ret = drm_prime_add_buf_handle(&file_priv->prime,
				       dmabuf, handle);
	mutex_unlock(&dev->object_name_lock);
	if (ret)
		goto fail_put_dmabuf;

out_have_handle:
    # 略略略... ...
fail_put_dmabuf:
    # 略略略... ...
out:
    # 略略略... ...
out_unlock:
	mutex_unlock(&file_priv->prime.lock);
	return ret;
}

整个函数流程概况:

  • 首先,将handle转为gem对象;
  • 然后,在file_priv->prime内查询handle对应的dma-buf,若存在就挑战到out_have_handle;否则下一步;
  • 继续判断obj->import_attach是否非空,若非空(说明该obj是外部dma-buf导入所构建的对象)就将obj->import_attach->dmabuf作为dma-buf,然后就挑战到out_have_handle;否则下一步;
  • 继续判断obj->dma_buf(说明该obj或已导出,或已外部导入)是否非空,若非空就将obj->dmabuf作为dma-buf,然后就挑转到out_have_handle;否则下一步;
  • 经过以上,说明该对象是原始对象且没有导出过,则通过函数export_and_register_object构建一个dma-buf;
  • 最后,经过drm_prime_add_buf_handle函数将dma-buf添加到file_priv->prime。

export_and_register_object函数:

static struct dma_buf *export_and_register_object(struct drm_device *dev,
						  struct drm_gem_object *obj,
						  uint32_t flags)
{
    # 略略略... ...

	if (obj->funcs && obj->funcs->export)
		dmabuf = obj->funcs->export(obj, flags);
	else if (dev->driver->gem_prime_export)
		dmabuf = dev->driver->gem_prime_export(obj, flags);
	else
		dmabuf = drm_gem_prime_export(obj, flags);
	if (IS_ERR(dmabuf)) {
		return dmabuf;
	}

	obj->dma_buf = dmabuf;
    # 略略略... ...
}

注:若gem对象实现了drm_gem_object_funcs的export则调用其构建dma-buf;否则,若驱动实现了struct drm_driver的gem_prime_export回调,则调用其构建dma-buf;若两个回调都未实现,则通过drm_gem_prime_export函数构建dma-buf。最后将构建的dma-buf赋值给obj->dma_buf,然后返回该dma-buf。

1.1 drm_gem_prime_export

在drm_gem_prime_export中,将gem对象导出成dma-buf的导出信息如下:

struct dma_buf_export_info exp_info = {
	.exp_name = KBUILD_MODNAME, /* white lie for debug */
	.owner = dev->driver->fops->owner,
	.ops = &drm_gem_prime_dmabuf_ops,
	.size = obj->size,
	.flags = flags,
	.priv = obj,
	.resv = obj->resv,
};

drm_gem_prime_dmabuf_ops是dma相关操作的回调函数集合:

static const struct dma_buf_ops drm_gem_prime_dmabuf_ops =  {
	.cache_sgt_mapping = true,
	.attach = drm_gem_map_attach,
	.detach = drm_gem_map_detach,
	.map_dma_buf = drm_gem_map_dma_buf,
	.unmap_dma_buf = drm_gem_unmap_dma_buf,
	.release = drm_gem_dmabuf_release,
	.mmap = drm_gem_dmabuf_mmap,
	.vmap = drm_gem_dmabuf_vmap,
	.vunmap = drm_gem_dmabuf_vunmap,
};

相关回调函数概括:

  • drm_gem_map_attach:若gem对象实现了drm_gem_object_funcs的pin回调,则调用之;否则,若驱动实现了struct drm_driver的gem_prime_pin回调,则调用之;
  • drm_gem_map_detach:若gem对象实现了drm_gem_object_funcs的unpin回调,则调用之;否则,若驱动实现了struct drm_driver的的gem_prime_unpin回调,则调用之;
  • drm_gem_map_dma_buf:若gem对象实现了drm_gem_object_funcs的get_sg_table回调,则调用之;否则,若驱动实现了struct drm_driver的的gem_prime_get_sg_table回调,则调用之。最后,通过dma_map_sg_attrs将回调返回的CPU视角物理地址映射为设备视角的总线地址;
  • drm_gem_unmap_dma_buf:通过函数dma_unmap_sg_attrs将之前建立的映射解除映射;
  • drm_gem_dmabuf_mmap:通过struct drm_driver的gem_prime_mmap回调,将gem对象物理地址映射到用户空间的虚拟地址空间;
  • drm_gem_dmabuf_vmap:若gem对象实现了drm_gem_object_funcs的vmap回调,则调用之;否则,若驱动实现了struct drm_driver的的gem_prime_vmap回调,则调用之;最后,若都没实现,则返回异常地址;
  • drm_gem_dmabuf_vunmap:若gem对象实现了drm_gem_object_funcs的vunmap回调,则调用之;否则,若驱动实现了struct drm_driver的的gem_prime_vunmap回调,则调用之;

注:若gem对象未实现drm_gem_object_funcs的export回调、struct drm_driver也未实现gem_prime_export回调,则gem对象或struct drm_driver必须实现pin/gem_prime_pin、unpin/gem_prime_unpin、get_sg_table/gem_prime_get_sg_table、gem_prime_mmap、vmap/gem_prime_vmap、vunmap/gem_prime_vunmap回调(gem_prime_*为drm_driver函数集)。

1.2 自定义导出

通过实现drm_gem_object_funcs的export或struct drm_driver的gem_prime_export,可以自定义dma-buf的行为。一般是自定义struct dma_buf_ops集合

2 导入buf

drm通过DRM_IOCTL_PRIME_FD_TO_HANDLE实现将一个dma-buf的fd转为gem对象的handle。其中会调用struct drm_driver的prime_fd_to_handle回调,drm_gem_prime_fd_to_handle函数是prime_fd_to_handle回调的默认实现函数:

int drm_gem_prime_fd_to_handle(struct drm_device *dev,
			       struct drm_file *file_priv, int prime_fd,
			       uint32_t *handle)
{
    # 略略略... ...

	dma_buf = dma_buf_get(prime_fd);
	if (IS_ERR(dma_buf))
		return PTR_ERR(dma_buf);

	mutex_lock(&file_priv->prime.lock);

	ret = drm_prime_lookup_buf_handle(&file_priv->prime,
			dma_buf, handle);
	if (ret == 0)
		goto out_put;

	mutex_lock(&dev->object_name_lock);
	if (dev->driver->gem_prime_import)
		obj = dev->driver->gem_prime_import(dev, dma_buf);
	else
		obj = drm_gem_prime_import(dev, dma_buf);
	if (IS_ERR(obj)) {
		ret = PTR_ERR(obj);
		goto out_unlock;
	}

	if (obj->dma_buf) {
		WARN_ON(obj->dma_buf != dma_buf);
	} else {
		obj->dma_buf = dma_buf;
		get_dma_buf(dma_buf);
	}

	ret = drm_gem_handle_create_tail(file_priv, obj, handle);
	drm_gem_object_put_unlocked(obj);
	if (ret)
		goto out_put;

	ret = drm_prime_add_buf_handle(&file_priv->prime,
			dma_buf, *handle);
	mutex_unlock(&file_priv->prime.lock);
	if (ret)
		goto fail;

	dma_buf_put(dma_buf);

	return 0;

fail:
    # 略略略... ...

out_unlock:
    # 略略略... ...
out_put:
    # 略略略... ...
}

整个函数流程概况:

  • 首先,将fd转为dma-buf;
  • 然后,在file_priv->prime中查询dma-buf对应的handle,若存在就跳到out_put执行返回;否则,继续下一步;
  • 继续,若实现了struct drm_driver的gem_prime_import回调,则调用其构建gem对象;否则,调用drm_gem_prime_import构建gem对象;
  • 继续,将构建的dma-buf赋值给obj->dma_buf;
  • 继续,通过drm_gem_handle_create_tail将gem对象转为handle;
  • 最后,通过drm_prime_add_buf_handle建立dma-buf与handle的映射。

drm_gem_prime_import函数会直接调用drm_gem_prime_import_dev构建gem对象,drm_gem_prime_import_dev关键源码:

struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev,
					    struct dma_buf *dma_buf,
					    struct device *attach_dev)
{
    # 略略略... ...

	if (dma_buf->ops == &drm_gem_prime_dmabuf_ops) {
		obj = dma_buf->priv;
		if (obj->dev == dev) {
			drm_gem_object_get(obj);
			return obj;
		}
	}

    # 略略略... ...

	attach = dma_buf_attach(dma_buf, attach_dev);
	if (IS_ERR(attach))
		return ERR_CAST(attach);

	get_dma_buf(dma_buf);

	sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
	if (IS_ERR(sgt)) {
		ret = PTR_ERR(sgt);
		goto fail_detach;
	}

	obj = dev->driver->gem_prime_import_sg_table(dev, attach, sgt);
	if (IS_ERR(obj)) {
		ret = PTR_ERR(obj);
		goto fail_unmap;
	}

	obj->import_attach = attach;
	obj->resv = dma_buf->resv;

	return obj;

fail_unmap:
    # 略略略... ...
fail_detach:
    # 略略略... ...
}

整个函数流程概况:

  • 首先,若dma_buf->ops为drm_gem_prime_dmabuf_ops,说明是通过drm_gem_prime_export函数默认构建的。读出dma_buf->priv转为gem对象指针便可获取gem对象指针,同时若obj->dev等于当前dev,则可将obj返回;否则,若dma_buf->ops不是drm_gem_prime_dmabuf_ops或obj->dev不是当前dev,继续下一步;
  • 然后,通过函数dma_buf_attach构建struct dma_buf_attachment;
  • 继续,通过dma_buf_map_attachment函数调用dma-buf的map_dma_buf回调,通过该回调导出gem的设备内存信息;
  • 继续,将上一步返回的设备内存信息传入struct drm_driver的gem_prime_import_sg_table回调,用其构建gem对象;
  • 最后,将attach赋值给obj->import_attach、dma_buf->resv赋值给obj->resv;

2.1 外部导入

所谓外部导入是指dma-buf由其他模块导出,或无法通过dma-buf的field直接求出gem对象指针。此时,需要驱动实现struct drm_driver的gem_prime_import_sg_table回调,在该回调中实现根据struct sg_table的内存信息构建gem对象。

3 总结

最后,导出/导入dma-buf涉及到的回调和函数如下:
请添加图片描述

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值