• dma-buf 由浅入深(一) —— 最简单的 dma-buf 驱动程序【转】


    转自:https://blog.csdn.net/hexiaolong2009/article/details/102596744

    dma-buf 由浅入深(一) —— 最简单的 dma-buf 驱动程序
    dma-buf 由浅入深(二) —— kmap / vmap
    dma-buf 由浅入深(三) —— map attachment
    dma-buf 由浅入深(四) —— mmap
    dma-buf 由浅入深(五) —— File
    dma-buf 由浅入深(六) —— begin / end cpu_access
    dma-buf 由浅入深(七) —— alloc page 版本
    dma-buf 由浅入深(八) —— ION 简化版

    前言
    最近因为工作内容发生了变化,导致《最简单的DRM驱动程序》系列文章暂停了更新,但我仍然会坚持把它写完。由于在后面讲解 DRM 驱动程序的过程中,会不可避免的介绍到 dma-buf,因此这里将该模块单独提取出来,形成一个系列文章,为后续讲解 DRM PRIME 打下基础。

    如果你和我一样,是一位从事Android多媒体底层开发的工程师,那么你对 dma-buf 这个词语一定不会陌生,因为不管是 Video、Camera 还是 Display、GPU,它们的buffer都来自于ION,而 ION 正是基于 dma-buf 实现的。

    假如你对 dma-buf 的理解并不深刻,又期望找个时间来彻底公关一下,那么很高兴,这几篇文章一定能让你对 dma-buf 有个更深入、更透彻的理解。

    历史
    dma-buf 最初的原型为 shrbuf,由 Marek Szyprowski (Samsung)于2011年8月2日首次提出,他实现了 “Buffer Sharing” 的概念验证(Proof-of-Concept),并在三星平台的 V4L2 驱动中实现了 camera 与 display 的 buffer 共享问题。该 patch 发表后,在内核社区引起了巨大反响,因为当时关于 buffer 共享问题很早就开始讨论了,但是由于内核没有现成的框架支持,导致各个厂商实现的驱动五花八门,此时急需要一个统一的框架来解决 buffer 共享问题。

    LWN: Buffer sharing proof-of-concept
    LWN: Sharing buffers between devices
    于是 Sumit Semwal (Linaro) 基于 Marek Szyprowski 的 patch 重构了一套新的框架,也就是我们今天看到的 dma-buf 核心代码,它经历了社区开发人员给出的重重考验,并最终于 2012 年 2 月 merge 到了 Linux-3.3 主线版本中,这也是 dma-buf 的第一个正式版本。此后 dma-buf 被广泛应用于内核多媒体驱动开发中,尤其在 V4L2、DRM 子系统中得到了充分应用。

    LWN: DMA buffer sharing in 3.3
    Patch: dma-buf: Documentation for buffer sharing framework
    Patch: dma-buf: Introduce dma buffer sharing mechanism
    第一个使用 dma-buf 的 DRM 分支: drm-prime-dmabuf
    概念
    dma-buf 的出现就是为了解决各个驱动之间 buffer 共享的问题,因此它本质上是 buffer 与 file 的结合,即 dma-buf 既是块物理 buffer,又是个 linux file。buffer 是内容,file 是媒介,只有通过 file 这个媒介才能实现同一 buffer 在不同驱动之间的流转。

    一个典型的 dma-buf 应用框图如下:


    通常,我们将分配 buffer 的模块称为 exporter,将使用该 buffer 的模块称为 importer 或 user。但在本系列文章中,importer 特指内核空间的使用者,user 特指用户空间的使用者。

    有的人习惯将 exporter 说成是生产者,importer 说成是消费者,我个人认为这样的说法并不严谨。举例来说,Android 系统中,graphic buffer 都是由 ION 来分配的,GPU 负责填充该 buffer,DPU 负责显示该 buffer。那么在这里,ION 则是 exporter,GPU 和 DPU 则都是 importer。但是从生产者/消费者模型来讲,GPU 则是生产者,DPU 是消费者,因此不能片面的认为 exporter 就是生产者。

    最简单的 dma-buf 驱动程序
    如下代码演示了如何编写一个最简单的 dma-buf 驱动程序,我将其称为 dummy 驱动,因为它什么事情也不做。注意:该代码已经是精简的不能再精简了,少一行代码都不行!

    exporter-dummy.c

    #include <linux/dma-buf.h>
    #include <linux/module.h>

    static struct sg_table *exporter_map_dma_buf(struct dma_buf_attachment *attachment,
    enum dma_data_direction dir)
    {
    return NULL;
    }

    static void exporter_unmap_dma_buf(struct dma_buf_attachment *attachment,
    struct sg_table *table,
    enum dma_data_direction dir)
    {
    }

    static void exporter_release(struct dma_buf *dmabuf)
    {
    }

    static void *exporter_kmap_atomic(struct dma_buf *dmabuf, unsigned long page_num)
    {
    return NULL;
    }

    static void *exporter_kmap(struct dma_buf *dmabuf, unsigned long page_num)
    {
    return NULL;
    }

    static int exporter_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
    {
    return -ENODEV;
    }

    static const struct dma_buf_ops exp_dmabuf_ops = {
    .map_dma_buf = exporter_map_dma_buf,
    .unmap_dma_buf = exporter_unmap_dma_buf,
    .release = exporter_release,
    .map_atomic = exporter_kmap_atomic,
    .map = exporter_kmap,
    .mmap = exporter_mmap,
    };

    static int __init exporter_init(void)
    {
    DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
    struct dma_buf *dmabuf;

    exp_info.ops = &exp_dmabuf_ops;
    exp_info.size = PAGE_SIZE;
    exp_info.flags = O_CLOEXEC;
    exp_info.priv = "null";

    dmabuf = dma_buf_export(&exp_info);

    return 0;
    }

    module_init(exporter_init);

    从上面的代码来看,要实现一个 dma-buf exporter驱动,需要执行3个步骤:

    dma_buf_ops
    DEFINE_DMA_BUF_EXPORT_INFO
    dma_buf_export()
    注意: 其中 dma_buf_ops 的回调接口中,如下接口又是必须要实现的,缺少任何一个都将导致 dma_buf_export() 函数调用失败!

    map_dma_buf
    unmap_dma_buf
    map
    map_atomic
    mmap
    release
    从 linux-4.19 开始,map_atomic 接口被废弃,map 和 mmap 接口不再被强制要求。

    dma-buf: make map_atomic and map function pointers optional
    dma-buf: remove kmap_atomic interface
    dma-buf: Remove requirement for ops->map() from dma_buf_export
    dma-buf: Make mmap callback actually optional
    开发环境
    内核源码 4.14.143
    示例源码 hexiaolong2008-GitHub/sample-code/dma-buf/01
    开发平台 Ubuntu14.04/16.04
    运行平台 my-qemu 仿真环境
    编译
    dma-buf 的核心代码由 CONFIG_DMA_SHARED_BUFFER 宏来控制是否参与编译,而该 config 并不是一个显式的菜单项,我们无法直接在 menuconfig 菜单中找到它,因此我这里就直接简单粗暴的修改 Kconfig 文件,设置 default y 来实现 dma-buf.c 的强制编译:

    linux-4.14.43/drivers/base/Kconfig:

    config DMA_SHARED_BUFFER
    bool
    default y
    1
    2
    3
    或者你也可以通过 menuconfig 菜单选择那些依赖 dma-buf 的设备驱动,如 DRM VGEM。

    然后编译 exporter_dummy.ko 文件,并打包到 my-qemu 环境中。

    运行
    在 my-qemu 仿真环境中执行如下命令:

    # insmod /lib/modules/4.14.143/kernel/drivers/dma-buf/exporter-dummy.ko
    # lsmod
    1
    2
    exporter_dummy 16384 1 - Live 0x7f000000
    1
    通过如下命令来查看 dma-buf 的相关信息:

    # cat /sys/kernel/debug/dma_buf/bufinfo
    1
    Dma-buf Objects:
    size flags mode count exp_name
    00004096 00000000 00000005 00000001 exporter_dummy
    Attached Devices:
    Total 0 devices attached

    Total 1 objects, 4096 bytes
    1
    2
    3
    4
    5
    6
    7
    运行截图:

    在实际运行的过程中,细心的小伙伴可能会发现,该 exporter_dummy.ko 只能被 insmod,无法被 rmmod。关于该问题的原因,我将在《dma-buf 由浅入深(五)—— File》中为大家解答。

    总结
    dma-buf 本质上是 buffer + file 的结合。
    编写 dma-buf 驱动的三个步骤:
    (1)dma_buf_ops
    (2)DEFINE_DMA_BUF_EXPORT_INFO
    (3)dma_buf_export()
    通过本篇我们学习了如何编写一个最简单的 dma-buf 驱动程序。但是该驱动什么事情也做不了,因为它的 dma_buf_ops 回调函数都是空的。从下一篇起,我们将一步步实现 dma_buf_ops 的回调函数,让大家一步步的掌握 dma-buf 的使用技巧。

    参考资料
    敬叶:Linux DMA-BUF


    下一篇:《dma-buf 由浅入深(二)—— kmap / vmap》
    文章汇总:《DRM(Direct Rendering Manager)学习简介》
    ————————————————
    版权声明:本文为CSDN博主「何小龙」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/hexiaolong2009/article/details/102596744

    【作者】张昺华
    【大饼教你学系列】https://edu.csdn.net/course/detail/10393
    【新浪微博】 张昺华--sky
    【twitter】 @sky2030_
    【微信公众号】 张昺华
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    ACdream 1114(莫比乌斯反演)
    ACdream 1148(莫比乌斯反演+分块)
    bzoj2301(莫比乌斯反演+分块)
    hdu1695(莫比乌斯反演)
    hdu4908(中位数)
    bzoj1497(最小割)
    hdu3605(最大流+状态压缩)
    【Leetcode】Add Two Numbers
    【Leetcode】Add Binary
    【Leetcode】Single Number II
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/15002334.html
Copyright © 2020-2023  润新知