引言
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涉及到的回调和函数如下: