2.KMD-5.GEM-dumbbuffer-《计算机知识》

admin 2025-11-02 22:33:46 系统网络 来源:ZONE.CI 全球网 0 阅读模式
  • 相关参考
  • modetest的使用
  • gem的初始化
  • DRM中DUMB使用
    • mali-dp drm属性

    相关参考

    • 最简单的DRM应用程序 (single-buffer)
    • Linux Kernel 5.9

    modetest的使用

    在modetest使用dumb buffer的时候,操作方式如下:

    1. int main(int argc, char **argv)
    2. {
    3. open("/dev/dri/card0");
    4. drmModeGetResources(...);
    5. drmModeGetConnector(...);
    6. // 关于dumb buffer的使用, 参考modetest源码: bo_fb_create
    7. drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB);
    8. drmModeAddFB(...);
    9. drmIoctl(DRM_IOCTL_MODE_MAP_DUMB);
    10. mmap(...);
    11. // ...... 填充framebuffer
    12. drmModeSetCrtc(crtc_id, fb_id, connector_id, mode);
    13. ......
    14. }

    不考虑其他模块,就dumb而言,需要经过以下步骤image.jpeg

    • 打开当前DRM模块,获取到fd,对应底层会生成自己的drm_file;
    • 创建N块自己需要的显存,这个显存可以是GPU显存-独立显卡, 也可以是内存-集显或SOC设备;
    • 我们使用显存肯定要mmap到用户空间,来减少拷贝的耗时。但此时我们只有一个fd,所以需要底层fake mmap,将不同的空间,提供一个索引给用户;
    • 用户调用mmap,此时会去映射到自己 选择 的 offset 空间;
    • AddFramebuffer将用户分配的buffer描述成framebuffer,挂到drm_device中。

    **下边参考 mali-dp550 使用 内核是如何初始化gem的 // malidp-drv.c kernel_version: 5.9

    因为mali-dp550在ARM上是SoC,所以显存和内存公用,所以关于frmabuffer的分配都是基于CMA 来分配连续的大块内存,可供DMA使用** CMA,Contiguous Memory Allocator,是内存管理子系统中的一个模块,负责物理地址连续的内存分配**。一般系统会在启动过程中,从整个memory中配置一段连续内存用于CMA,然后内核其他的模块可以通过CMA的接口API进行连续内存的分配。CMA的核心并不是设计精巧的算法来管理地址连续的内存块,实际上它的底层还是依赖内核伙伴系统这样的内存管理机制,或者说CMA是处于需要连续内存块的其他内核模块(例如DMA mapping framework)和内存管理模块之间的一个中间层模块,主要功能包括: 1、解析DTS或者命令行中的参数,确定CMA内存的区域,这样的区域我们定义为CMA area。 2、提供cma_alloc和cma_release两个接口函数用于分配和释放CMA pages 3、记录和跟踪CMA area中各个pages的状态 4、调用伙伴系统接口,进行真正的内存分配。

    gem的初始化

    在drm初始化时,如果支持GEM属性的话,在初始化drm_device时会初始化一个 vma,这个是做什么用的??? // TBD

    1. drm_dev_init
    2. drm_gem_init(dev); // drm_driver->feature has DRIVER_GEM
    3. struct drm_vma_offset_manager {
    4. rwlock_t vm_lock;
    5. struct drm_mm vm_addr_space_mm;
    6. };
    7. // Initialize new offset-manager
    8. drm_vma_offset_manager_init(vma_offset_manager,
    9. DRM_FILE_PAGE_OFFSET_START,
    10. DRM_FILE_PAGE_OFFSET_SIZE);
    11. rwlock_init(&mgr->vm_lock);
    12. drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size);

    image.jpeg

    DRM中DUMB使用

    mali-dp drm属性

    1. #define DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(dumb_create_func) \
    2. .gem_create_object = drm_gem_cma_create_object_default_funcs, \
    3. .dumb_create = (dumb_create_func), \
    4. .prime_handle_to_fd = drm_gem_prime_handle_to_fd, \
    5. .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \
    6. .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, \
    7. .gem_prime_mmap = drm_gem_cma_prime_mmap
    8. static struct drm_driver malidp_driver = {
    9. .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
    10. DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(malidp_dumb_create),
    11. #ifdef CONFIG_DEBUG_FS
    12. .debugfs_init = malidp_debugfs_init,
    13. #endif
    14. .fops = &fops, // DEFINE_DRM_GEM_CMA_FOPS
    15. ......
    16. };

    DUMB创建计算图片的空间大小我们要计算一张图片所需要的空间大小,需要获取到以下参数:

    1. struct drm_mode_create_dumb {
    2. __u32 height; // 图像Height,我们这里将 YUV420相关格式的位深都写为8,但实际图像表示是超过8的,怎么办
    3. // 这里对这种情况, virtual_height = height * 3 / 2; 将图像的height 增加上UV相关占用字节数,即可。
    4. __u32 width;
    5. __u32 bpp; // 图像位深: bo_create 中计算了各种格式下的图像位深度。 YUV420将这里写为8,但height的增加解决了该问题
    6. __u32 flags;
    7. /* handle, pitch, size will be returned 内核返回以下参数 */
    8. __u32 handle; // dumb buffer的句柄
    9. __u32 pitch; // pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), alignment); 各硬件自定义对齐位数
    10. __u64 size; // 对齐后,图像占用空间的实际大小 size = pitch * height;
    11. };
    12. bo_create
    13. bo_create_dumb(fd, width, virtual_height, bpp);

    用户空间 调用 drmIoctl(DRMIOCTLMODE_CREATE_DUMB) 会分配一片显存,并用drm_gem_objct管理起来,同时初始化 mmap

    1. drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create); // 用户层
    2. DRM_IOCTL_MODE_CREATE_DUMB==>drm_mode_create_dumb_ioctl // 内核层
    3. drm_mode_create_dumb_ioctl
    4. => drm_mode_create_dumb
    5. => dev->driver->dumb_create(file_priv, dev, args); // mali-dp550使用: 5.9版本使用 malidp_dumb_create
    6. struct drm_driver {
    7. int (*dumb_create)(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args);
    8. };

    在mali-dp550中,申请framebuffer的方法

    1. malidp_dumb_create
    2. => alignment = malidp_hw_get_pitch_align(malidp->dev, 1); // !!! 重点:获取 malidp_device 中 .bus_align_bytes = 8, 硬件对齐位数,这里至少8对齐。
    3. => pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), alignment); // width*bpp 一个图像的总宽度占用字节数,然后根据硬件要求对齐
    4. => drm_gem_cma_dumb_create_internal(file_priv, drm, args); // 因为 MALI-DP550原生是为了SOC设计, 显存和内存公用,使用CMA分配
    5. => args->size = args->pitch * args->height; // 根据对齐后的数据,算出实际要占用的总空间数
    6. => drm_gem_cma_create_with_handle
    7. => drm_gem_cma_create(drm, size); // !!!重点
    8. => drm_gem_handle_create(file_priv, gem_obj, handle) // !!! 重点,这里给用户空间返回handle,是用户操作buffer的句柄
    9. => drm_gem_object_put(gem_obj);

    image.jpegdrm_gem_cma_create 此函数创建一个 CMA GEM 对象并分配一个连续的内存块作为后备存储。后备内存设置了 writecombine 属性。可以看到这个函数中,会有三操作:

    • struct drm_gem_object *gem_obj **gem buffer对象结构体**分配及初始化;
    • drm_gem_create_mmap_offset() offset的初始化;
    • dma_alloc_wc : 真实物理内存的分配-dma writecombine的内存属性; !!! 注意,gem_obj 和 gem_mmap 并不会分配真实的内存,只是建立一个管理机制。

    struct drm_gem_object *obj

    drm_gem_handle_create重点:用户空间在打开当前 /dev/dri/cardx后,可能会操作若干个dumb buffer,所以需要一个idr来定位当前申请的dumb buffer。 struct drm_file *file_priv 中 file_priv->object_idr 挂接了当前dumb buffer的所有 struct drm_gem_object 。

    dumb的mmap和linux的mmap在modetest里边,我们使用 dumb buffer,不仅仅是需要分配 bo_create_dumb,还需要mmap : bo_map

    1. drmIoctl(bo->fd, DRM_IOCTL_MODE_MAP_DUMB, &arg); .
    2. drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, bo->fd, arg.offset); // drm_mmap 就是内核的 mmap

    关于 **Linux的mmap,这个不需要强调,将设备物理地址(物理内存)映射到用户空间的虚拟地址(虚拟内存)上**。

    1. #include <sys/mman.h>
    2. void *mmap(void *addr, size_t length, int prot, int flags,
    3. int fd, off_t offset);
    4. int munmap(void *addr, size_t length);

    建立映射 void mmap(void addr, size_t length, int prot, int flags, int fd, off_t offset); addr: 指定映射的起始地址,通常设为NULL, 由系统指定。 len: 映射到内存的文件长度 prot: 映射区的保护方式, 可以是: PROT_EXEC: 映射区可被执行 PROT_READ: 映射区可被读取 PROT_WRITE: 映射区可被写入 flags: 映射区的特性,可以是: MAP_SHARED: 写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享 MAP_PRIVATE:对映射区的写入操作会产生一个映射区的复制(copy-on-write), 对此区域所做的修改不会写回原文件。 fd: 由open返回的文件描述符,代表要映射的文件 offset: 以文件开始处的偏移量/或其他对象,必须是分页大小的整数倍,通常为0,表示从文件开头映射

    解除映射 int munmap(void *start, size_t length)

    功能: 取消参数start所指向的映射内存,参数length表示欲取消的内存大小

    addr: mmap的返回值

    返回值: 解除成功返回0,否则返回-1,错误原因存于errno中。

    mmap调用 struct drm_driver 中的 mmap, 在mali-dp550中调用 drm_gem_cma_mmap

    1. DEFINE_DRM_GEM_CMA_FOPS(fops);
    2. static const struct file_operations fops = {\
    3. .owner = THIS_MODULE,\
    4. .open = drm_open,\
    5. .release = drm_release,\
    6. .unlocked_ioctl = drm_ioctl,\
    7. .compat_ioctl = drm_compat_ioctl,\
    8. .poll = drm_poll,\
    9. .read = drm_read,\
    10. .llseek = noop_llseek,\
    11. .mmap = drm_gem_cma_mmap,\
    12. DRM_GEM_CMA_UNMAPPED_AREA_FOPS \
    13. }

    dumb的mmapdumb_map_offset 的作用:

    在 drm 设备节点的地址空间中分配一个偏移量,以便能够内存映射一个 dumb缓冲区。 默认实现是 drm_gem_create_mmap_offset()。 基于 GEM 的驱动程序不能覆盖它。 由用户通过 ioctl 调用。 返回值 成功为零,失败为负 errno。 在 static struct drm_driver malidp_driver 中未实现 dumb_map_offset,默认使用 : drm_gem_dumb_map_offset()

    image.jpeg功能:给dumb建立fake mmap, 然后提供给用户一个offset,使得用户在mmap 当前fd时可选中对应buffer。drmModeAddFB2问题:我们显示图片的时候, 按照前边的步骤, 好像已经可以吧 所有buffer 映射到用户空间了, 为什么还需要 add framebuffer?这个都做了什么?AddFB和AddFB2有啥区别?

    1. int drmModeAddFB2(int fd, uint32_t width, uint32_t height,
    2. uint32_t pixel_format, const uint32_t bo_handles[4],
    3. const uint32_t pitches[4], const uint32_t offsets[4],
    4. uint32_t *buf_id, uint32_t flags);
    5. 参数:
    6. buf_id是一个传出参数,看来这个函数的目的是获取buf_id,应该就是framebuffer的obj-id
    7. fd, width,height,pixel_format 图像的基础信息
    8. bo_handles[4]:
    9. pitches[4]:
    10. offsets[4]: 在bo_create中,初始化这几个字段。根据format不同,将framebuffer 划分为1-3部分
    11. flags:
    12. #define DRM_MODE_FB_INTERLACED (1<<0) /* 隔行帧 */
    13. #define DRM_MODE_FB_MODIFIERS (1<<1) /* enables ->modifer[],如果使能 dev->mode_config.allow_fb_modifiers 必须为1 */

    image.jpeg可以看到addfb实现了以下行为:

    • framebuffer_check 对 buf数据格式的正确性进行校验。
    • 创建drm_framebuffer *fb,并根据地址填充
    • drm_framebuffer 内部包含 struct drm_mode_object,将这个挂接到mode_config的object_idr 查找表中,同时叶挂到 mode_config.fb_list 链表里

    总结: 这里就是将 dumb buffer 划分成framebuffer,并添加到 drm_device->mode_config 中,使得用户modeset 可检测出当前framebuffer

    01-shell脚本介绍-《shell脚本》 系统网络

    01-shell脚本介绍-《shell脚本》

    一、shell脚本是什么二、为什么要学shell,而不是其他计算机语言三、学习这门课程的优势四、学了能干什么五、学习什么内容六、学习的技巧七、成长路径八、学习环
    评论:0   参与:  13