2.KMD-6.GEM显存使用-2-《计算机知识》

admin 2025-11-02 22:33:56 系统网络 来源:ZONE.CI 全球网 0 阅读模式
  • Linux中显存管理使用方法(使用)
  • DRM Memory Management(原理)
    • TTM-The Translation Table Manager
    • GEM-the Graphics Execution Manage
      • GEM Initialization
      • GEM Objects Creation
      • GEM对象的生命周期
      • GEM Objects Mapping
      • GEM对象命名和句柄-handle
    • GEM代码分析
      • drm_driver中的gem相关接口
      • gem初始化drm_gem_init
      • GPU的内存管理和使用-实战
      • DUMB内存
        • handle管理
        • map管理
      • PRIME内存

    相关参考:

    • 内核文档
    • DRM(Direct Rendering Manager)学习简介

    Linux中显存管理使用方法(使用)

    DRM Memory Management(原理)

    现代Linux系统需要⼤量的图形内存来动态存储帧缓冲区、纹理、顶点和其他与图形相关的数据,因此有效管理图形内存对于图形堆栈⾄关重要,并且在DRM基础架构中发挥着核⼼作⽤。

    DRM核⼼包括两个内存管理器,即 转换表映射(TTM)和图形执⾏管理器(GEM)。

    TTM是第⼀个开发的DRM内存管理器,并试图成为⼀种“千篇⼀律”的解决⽅案。它提供了⼀个单⼀的⽤⼾空间API,可以满⾜所有硬件的需求,同时⽀持统⼀内存体系结构(UMA)设备和具有专⽤视频RAM的设备(即⼤多数离散视频卡)。这导致了⼀个庞⼤,复杂的代码⽚段,结果证明这些代码难以⽤于驱动程序开发。

    GEM最初是由英特尔赞助的项⽬,以应对TTM的复杂性。它的设计理念是完全不同的:GEM 没有为每个与图形内存相关的问题提供解决⽅案,⽽是确定了驱动程序之间的通⽤代码,并创建了⼀个共享它的⽀持库。

    与TTM相⽐,GEM的初始化和执⾏要求更简单,但它不具有视频RAM管理功能,因此仅限于NUMA设备。


    TTM-The Translation Table Manager

    已经不使用喽,放弃搬砖


    GEM-the Graphics Execution Manage

    • 内核文档 or 中文版
    • DRM(Direct Rendering Manager)学习简介

    GEM向用户空间公开了一组标准的与内存相关的操作,向驱动程序公开了一组帮助函数,并让驱动程序使用自己的私有API实现特定于硬件的操作

    GEM 用户空间API:提供了一些列用户空间API的规则(虽然有些过时,但很好地概述了GEM API原则)

    image.png

    内核文档中描述:从根本上讲,GEM涉及以下几种操作

    • 内存分配和释放
    • 命令执行
    • 执行命令时的光圈管理(???)

    缓冲区对象分配相对简单,主要由Linux的shmem层提供,后者提供了用于备份每个对象的内存。

    GEM由三部分组成:

    • DUMB:只支持连续物理内存的buffer类型,基于kernel中通用CMA API实现,多用于小分辨率简单场景
    • PRIME:连续、非连续物理内存都支持,基于DMA-BUF机制,可以实现buffer共享,多用 于大内存复杂场景
    • fence:buffer同步机制,基于内核dma_fence机制实现,用于防止显示内容出现异步问题

    DUMP和PRIME的来源可以参考:关于 DRM 中 DUMB 和 PRIME 名字的由来

    image.png

    GEM Initialization

    首先 struct drm_driver中driver_features必须包含DRIVER_GEM属性

    DRM core会在load之前初始化GEM core。

    1. drm_dev_alloc
    2. drm_dev_init
    3. if(DRIVER_GEM)drm_gem_init(dev); // 有GEM属性的要初始化gem,还记得这个吧

    然后会创建 DRM Memory Manager objec 提供用于对象分配的address space pool。

    在KMS配置中,如果硬件需要,驱动程序需要在核心GEM初始化之后分配和初始化命令环缓冲区。UMA设备通常有所谓的stolen 内存区域,它为初始framebuffer和设备所需的较大的连续内存区域提供空间。这个空间通常不是由GEM管理的,必须单独初始化到它自己的DRM MM对象中

    GEM Objects Creation

    struct drm_gem_object 表示一个GEM objects instance。

    初始化方式一:驱动调用 drm_gem_object_init() 进行初始化。驱动负责调用shmem_read_mapping_page_gfp()做实际物理页面的申请,初始化GEM对象时驱动可以决定申请页面,或者延迟到需要内存时再申请需要内存时是指:用户态访问内存发生缺页中断,或是驱动需要启动DMA用到这段内存。

    初始化方式二:匿名页面内存并不是一定需要的,嵌入式设备一般需要物理连续系统内存,驱动可以创建GEM对象儿没有shmfs,并调用 drm_gem_private_object_init() 初始化此私有GEM对象,不过这样的话,私有GEM对象的存储管理就需要驱动自行完成。

    再次强调:drm_gem_object_init 函数不进行分配内存,只是创建shmfs,需要驱动自己分配内存

    1. struct drm_gem_priv_object{ // 私有GEM结构体
    2. .....
    3. stuct drm_gem_object gem_obj;
    4. ..... // 自己的GEM拓展 信息
    5. };
    6. drm_gem_object_init
    7. filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); // 申请一个共享内存
    8. obj->filp = filp;
    9. or
    10. drm_gem_private_object_init() // 嵌入式设备分配连续内存

    可以看到,GEM分配一个匿名的共享内存,然后struct drm_gem_object->filp指向这里,当图形硬件直接使用系统内存时,这块内存用作对象的主存储,或者作为后备存储。

    GEM对象的生命周期

    drm_gem_object_get() 加数-需要加锁drm_gem_object_put() 减数-需要加锁drm_gem_object_put_unlocked()也可以不持锁操作,当引用计数为0,则销毁

    GEM Objects Mapping

    drm_gem_create_mmap_offset用户为了使用GEM对象内存,所以必须mmap映射到用户空间

    mmap系统调用不能直接映射GEM对象,因为GEM对象没有自己的文件描述符, 所以:

    • 用户自己实现do_mmap函数(不推荐)
    • DRM文件描述符使用mmap直接映射GEM对象(推荐)

    这里就用drm提供的mmap映射,GEM对象没有文件描述符,但是通过mmap(void*addr,size_t length,int port,int flags,int fd, off_t offset)中的offset参数

    为了能识别这个offset,驱动必须先对这个GEM对象执行了drm_gem_create_mmap_offset(),这样得到的offset值需要先通过驱动指定的方式传给用户态程序,然后就可以通过mmap传下来 。

    GEM对象命名和句柄-handle

    drm_gem_handle_create()创建GEM对象handle-32 bit int类型,获取一个局部唯一handle,通过drm_gem_handle_delete()来删除,drm_gem_object_lookup()可以由handle找出对应的GEM对象。handle仅是一个对GEM对象的引用,在handle销毁时减去这一引用

    1. amdgpu_gem_create_ioctl
    2. int r = drm_gem_handle_create(filp, gobj, &handle);
    3. /* drop reference from allocate - handle holds it now */
    4. drm_gem_object_put_unlocked(gobj);

    GEM代码分析

    drm_driver中的gem相关接口

    1. struct drm_driver {
    2. // 与GEM相关
    3. u32 driver_features; // 需要使能DRIVER_GEM
    4. // 以下函数已被drm_gem_object_funcs 替代,但大部分还是这么用的,所以列出来
    5. #if 0.
    6. // drm_gem_objects的析构函数,已不使用
    7. gem_free_object / gem_free_object_unlocked
    8. gem_open_object / gem_close_object
    9. gem_print_info
    10. #endif
    11. gem_create_object // *用于分配GEM对象结构,供CMA和SHMEM GEM帮助程序使用。
    12. // prime buffer操作函数
    13. prime_handle_to_fd / prime_fd_to_handle // prime基于DMA-BUF,fd和handle转换 ***
    14. gem_prime_export / gem_prime_import // DMA-BUF的export和import
    15. gem_prime_pin / gem_prime_unpin // TBD ???
    16. gem_prime_get_sg_table / gem_prime_import_sg_table
    17. gem_prime_vmap / gem_prime_vunmap / gem_prime_mmap
    18. // dumb buffer使用
    19. dumb_create / dumb_map_offset / dumb_destroy
    20. };
    21. // 5.7版本建议将部分操作接口分离出来:
    22. struct drm_gem_object {
    23. ......
    24. /* Optional GEM object functions. If this is set, it will be used instead of the
    25. * corresponding &drm_driver GEM callbacks. */
    26. const struct drm_gem_object_funcs *funcs; // 新版本推荐使用该方法,替代drm_driver
    27. }
    28. struct drm_gem_object_funcs {
    29. void (*free)(struct drm_gem_object *obj);
    30. int (*open)(struct drm_gem_object *obj, struct drm_file *file);
    31. void (*close)(struct drm_gem_object *obj, struct drm_file *file);
    32. void (*print_info)(struct drm_printer *p, unsigned int indent,
    33. const struct drm_gem_object *obj);
    34. struct dma_buf *(*export)(struct drm_gem_object *obj, int flags);
    35. int (*pin)(struct drm_gem_object *obj);
    36. void (*unpin)(struct drm_gem_object *obj);
    37. struct sg_table *(*get_sg_table)(struct drm_gem_object *obj);
    38. void *(*vmap)(struct drm_gem_object *obj);
    39. void (*vunmap)(struct drm_gem_object *obj, void *vaddr);
    40. int (*mmap)(struct drm_gem_object *obj, struct vm_area_struct *vma);
    41. const struct vm_operations_struct *vm_ops; // 这是可选的,但对于mmap支持是必需的。
    42. }
    43. /*** 这部分推荐参考: struct drm_gem_object_funcs drm_cma_gem_default_funcs ***/

    gem初始化drm_gem_init

    1. struct drm_vma_offset_manager {
    2. rwlock_t vm_lock;
    3. struct drm_mm vm_addr_space_mm;
    4. };
    5. drm_dev_init->
    6. drm_gem_init
    7. struct drm_vma_offset_manager *vma_offset_manager = kzalloc;
    8. dev->vma_offset_manager = vma_offset_manager
    9. // 管理到用户空间的线性映射 TBD ???
    10. drm_vma_offset_manager_init(vma_offset_manager, DRM_FILE_PAGE_OFFSET_START, DRM_FILE_PAGE_OFFSET_SIZE);
    11. // #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1) // 1M
    12. // #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 256) // 255M
    13. // start is 0x100000, offset is 0xfffff00
    14. rwlock_init(&mgr->vm_lock);
    15. drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size); // initialize a drm-mm allocator

    GPU的内存管理和使用-实战

    看了上边的,还是一脸懵逼:

    • 显存怎么用?
    • 显存的MMU?
    • 显存怎么透过PCI?
    • PC和显卡是怎么通过GEM进行数据交互的 ?
    • 自己的代码怎么使用显存?
    • 传说中的dma-buf?

    还是不咋会用GPU显存吧

    DUMB内存

    DRM GEM 驱动程序开发

    我们先看下所有文件的drm_file是怎么来的?

    1. static struct drm_driver mygem_driver = {
    2. .driver_features = DRIVER_GEM,
    3. .fops = &mygem_fops,
    4. }
    5. static const struct file_operations mygem_fops = {
    6. .owner = THIS_MODULE,
    7. .open = drm_open,
    8. .release = drm_release,
    9. .unlocked_ioctl = drm_ioctl,
    10. ...
    11. }
    12. .open = drm_open
    13. drm_open_helper(filp, minor);
    14. priv = drm_file_alloc(minor); // 就这里申请的
    15. if (dev->driver->open) {
    16. ret = dev->driver->open(dev, file); // 这里如果自定了open函数,在这里调用(不推荐)
    17. filp->private_data = priv;
    18. unlocked_ioctl = drm_ioctl,
    19. drm_ioctl
    20. is_driver_ioctl = nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END; // 允许用户自定义命令(这里用不到)
    21. ioctl = &drm_ioctls[nr];
    22. func = ioctl->func;
    23. drm_ioctl_kernel(filp, func, kdata, ioctl->flags);
    24. struct drm_file *file_priv = file->private_data; // 就是open时申请的文件
    25. retcode = func(dev, kdata, file_priv);

    使用了DUMP buffer进行操作

    1. 1.创建dumb buffer
    2. drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req);
    3. 调用内核中
    4. drm_ioctl->drm_ioctls
    5. static const struct drm_ioctl_desc drm_ioctls[]
    6. drm_mode_create_dumb_ioctl 函数
    7. drm_mode_create_dumb(dev, data, file_priv);
    8. dev->driver->dumb_create(file_priv, dev, args);
    9. 最终调用自己driver的dumb_create函数
    10. 参考代码中dumb_create = drm_gem_cma_dumb_create 直接使用 CMA helper 函数实现
    11. drm_gem_cma_dumb_create
    12. drm_gem_cma_create_with_handle
    13. drm_gem_cma_create
    14. __drm_gem_cma_create(drm, size);
    15. drm_gem_object_init(drm, gem_obj, size); // init object对象 ***
    16. drm_gem_create_mmap_offset(gem_obj); // mmap映射 ***
    17. dma_alloc_wc(drm->dev, size, &cma_obj->paddr // 这才是实际分配内存的地方
    18. // 也可以在mmap中缺页中断分配
    19. drm_gem_handle_create // 创建handle,这里会drm_gem_object_get 一次
    20. drm_gem_object_put_unlocked(gem_obj); // 释放drm_gem_handle_create的句柄
    21. ##### 申请并初始化gem_obj
    22. drm_gem_object_init(drm, gem_obj, size)
    23. drm_gem_private_object_init // initialize an allocated private GEM object
    24. filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); // ???
    25. obj->filp = filp;
    26. #### mmap映射使用
    27. drm_gem_create_mmap_offset(gem_obj);
    28. #### desoty使用
    29. struct drm_driver中dumb_destroy默认使用drm_gem_dumb_destroy()
    30. drm_mode_destroy_dumb_ioctl
    31. drm_mode_destroy_dumb
    32. if (dev->driver->dumb_destroy)
    33. return dev->driver->dumb_destroy(file_priv, dev, handle);
    34. else
    35. return drm_gem_dumb_destroy(file_priv, dev, handle);

    handle管理

    1. ##### handle部分使用
    2. drm_gem_handle_create + drm_gem_object_put_unlocked
    3. #if 0
    4. 先看下handle,倒叙追下
    5. drm_mode_create_dumb中第二个参数:
    6. struct drm_mode_create_dumb *args,
    7. 使用drm_ioctl_kernel 函数 void *kdata参数
    8. drm_ioctl 中 kdata = stack_kdata;
    9. 注:最后会把这部分导入用户空间:copy_to_user((void __user *)arg, kdata, out_size)
    10. #endif
    11. ***再次整理下drm_gem_handle_create代码***:*(重点:handle获取方式)
    12. { // 用户态代码
    13. struct drm_mode_create_dumb create_req = {};
    14. create_req.bpp = 32;
    15. create_req.width = 240;
    16. create_req.height = 320;
    17. drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req);
    18. }
    19. { // 内核态代码
    20. drm_ioctl
    21. kdata = stack_kdata; // or kdata = kmalloc(ksize, GFP_KERNEL);
    22. copy_from_user(kdata, (void __user *)arg, in_size) // 获取用户态参数
    23. drm_ioctl_kernel(filp, func, kdata, ioctl->flags)
    24. drm_mode_create_dumb(dev, data, file_priv);
    25. .....
    26. drm_gem_handle_create(); //获取到用户句柄******
    27. copy_to_user((void __user *)arg, kdata, out_size) // 将handle返回到用户态

    handle的创建

    map管理2.map%20dumb%20bufferdrmIoctl(fd,%20DRM_IOCTL_MODE_MAP_DUMB,%20&map_req)%20调用内核中drm_mode_mmap_dumb_ioctl%20%20%20%20这里没实现dev->driver->dumb_map_offset,所以调用:%20%20%20%20drm_gem_dumb_map_offset%20%20%20%20根据handle%20来获取offset3.用户空间map后操作mmap(0,%20create_req.size,%20PROT_WRITE,%20MAP_SHARED,%20fd,%20map_req.offset)mmap(0,%20create_req.size,%20PROT_READ,%20MAP_SHARED,%20fd,%20map_req.offset);...使用直接调用内核%20%20%20%20drm_gem_mmap4.释放drmIoctl(fd,%20DRM_IOCTL_MODE_DESTROY_DUMB,%20&destroy_req);drm_mode_destroy_dumb_ioctl%20%20%20%20drm_mode_destroy_dumb%20%20%20%20%20%20%20%20%20%20if%20(dev->driver->dumb_destroy)%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20dev->driver->dumb_destroy(file_priv,%20dev,%20handle);%20%20%20%20%20%20%20%20%20%20%20%20else%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20drm_gem_dumb_destroy(file_priv,%20dev,%20handle);%20%20%20%20%20%20%20%20//%20struct%20drm_driver中dumb_destroy默认使用drm_gem_dumb_destroy(),或者自定义释放函数

    PRIME内存

    从最简单的%20dma-buf%20驱动程序%20可以知道dma_buffer的基本过程:

    dma-buf%20的出现就是为了解决各个驱动之间%20buffer%20共享的问题,因此它本质上是%20buffer%20与%20file%20的结合,即%20dma-buf%20既是块物理%20buffer,又是个%20linux%20file。buffer%20是内容,file%20是媒介,只有通过%20file%20这个媒介才能实现同一%20buffer%20在不同驱动之间的流转查看Linux 下DMA-BUF使用情况:

    1. sudo cat /sys/kernel/debug/dma_buf/bufinfo
    2. Dma-buf Objects:
    3. size flags mode count exp_name ino
    4. 10485760 00000000 00080005 00000003 i915 01030576
    5. Exclusive fence: i915 signaled signalled
    6. Shared fence: i915 signaled signalled
    7. Attached Devices:
    8. Total 0 devices attached
    9. .......

    DMA-BUF最小化实现

    1. dma_buf_ops # dma-buf回调接口
    2. DEFINE_DMA_BUF_EXPORT_INFO # 定义struct dma_buf_export_info 对象
    3. dma_buf_export() # 初始化struct dma_buf_export_info

    GEM中的DMA-BUF

    1. struct drm_device {
    2. ......
    3. /* prime: */
    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_export // export GEM -> dmabuf 自定义dma_buf的export和import接口
    7. .gem_prime_import // import dmabuf -> GEM
    8. };
    9. // drm_ioctl.c中
    10. DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_RENDER_ALLOW),
    11. DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_RENDER_ALLOW),
    12. drm_prime_handle_to_fd_ioctl
    13. struct drm_driver->prime_handle_to_fd(); // 调用自己实现的prime_handle_to_fd接口
    14. drm_prime_fd_to_handle_ioctl
    15. obj = drm_gem_object_lookup(file_priv, handle); // 调用自己实现的prime_fd_to_handle接口
    16. drm_gem_prime_handle_to_fd
    17. // 先判断当前drm_file是否已经注册过dma-buf
    18. dmabuf = drm_prime_lookup_buf_by_handle(&file_priv->prime, handle);
    19. if (obj->import_attach) ;
    20. if (obj->dma_buf);
    21. // 没注册过,需要注册
    22. dmabuf = export_and_register_object(dev, obj, flags);
    23. dmabuf = dev->driver->gem_prime_export(dev, obj, flags);
    24. // 这个接口,就是dma_buf得export接口实现,可以参考 amdgpu_gem_prime_export
    25. obj->dma_buf = dmabuf;
    26. get_dma_buf(obj->dma_buf);
    27. // 添加到 import list,和handle绑定
    28. ret = drm_prime_add_buf_handle(&file_priv->prime,
    29. dmabuf, handle);
    30. // dma buf转fd描述符
    31. dma_buf_fd(dmabuf, flags);
    32. // 释放
    33. dma_buf_put(dmabuf); // 在export_and_register_object中get过
    34. // import
    35. drm_gem_prime_fd_to_handle
    36. obj = dev->driver->gem_prime_import(dev, dma_buf); // armada_gem_prime_import
    01-shell脚本介绍-《shell脚本》 系统网络

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

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