资讯详情

资讯详情

4.3.1 对象与创建:只有文件,没有页

4.3 论证了采用 shmem 文件作为后备的理由内容存放于文件中helper 仅按需获取页使用完毕后归还页。本节先给出 helper 的核心数据结构struct drm_gem_shmem_object的全貌再讨论drm_gem_shmem_create()创建对象时完成的工作。结论只有一句创建结束时对象拥有一个 shmem 文件但没有任何物理页。1、结构全貌该结构定义于include/drm/drm_gem_shmem_helper.hstructdrm_gem_shmem_object{structdrm_gem_objectbase;/* 内嵌的 GEM 对象 */structpage**pages;/* 从 shmem 文件获取的页 */refcount_tpages_use_count;/* pages[] 的持有者计数 */refcount_tpages_pin_count;/* pin 计数 */intmadv;/* 0 在用0 可丢弃0 已 purge */structlist_headmadv_list;/* 加入驱动的可回收链表 */structsg_table*sgt;/* 已 DMA 映射的 sg 表 */void*vaddr;/* 内核连续映射地址 */refcount_tvmap_use_count;/* vaddr 的持有者计数 */bool pages_mark_dirty_on_put:1;bool pages_mark_accessed_on_put:1;bool map_wc:1;/* 以 write-combined 方式映射 */};除base外其余字段均描述同一事项所持有的页当前处于何种状态包括是否已获取、是否被 pin、被映射为哪些形态以及能否被回收。2、base内嵌的 GEM 对象base是内嵌的结构体而非指针因此drm_gem_shmem_object的头部即为一个完整的drm_gem_object。GEM core 仅可见basehelper 获得drm_gem_object *后通过container_of取回外层结构#defineto_drm_gem_shmem_obj(obj)\container_of(obj,structdrm_gem_shmem_object,base)驱动通常再将drm_gem_shmem_object内嵌到自身的结构中形成两层内嵌见 4.3.7。3、创建入口分配与初始化分离对象刚创建时上述字段几乎均为空pages为 NULL各计数为 0。公开入口drm_gem_shmem_create()直接转调内部函数structdrm_gem_shmem_object*drm_gem_shmem_create(structdrm_device*dev,size_tsize){return__drm_gem_shmem_create(dev,size,false);}__drm_gem_shmem_create()分两步完成先分配外层对象再进行初始化。staticstructdrm_gem_shmem_object*__drm_gem_shmem_create(structdrm_device*dev,size_tsize,bool private){...sizePAGE_ALIGN(size);if(dev-driver-gem_create_object){objdev-driver-gem_create_object(dev,size);...shmemto_drm_gem_shmem_obj(obj);}else{shmemkzalloc_obj(*shmem);...objshmem-base;}ret__drm_gem_shmem_init(dev,shmem,size,private);...returnshmem;}分配可由驱动接管。驱动实现gem_create_object时helper 使用其返回的对象。驱动由此可分配内嵌了drm_gem_shmem_object的更大结构例如 virtio-gpu 的virtio_gpu_object4.3.7。初始化可单独调用。drm_gem_shmem_init()直接调用__drm_gem_shmem_init()供自行分配对象的调用者使用例如 Rust 的 GEM 绑定。private参数。普通创建传falsedma-buf 导入时传true见第 6 节。4、__drm_gem_shmem_init()的四步staticint__drm_gem_shmem_init(structdrm_device*dev,structdrm_gem_shmem_object*shmem,size_tsize,bool private){structdrm_gem_object*objshmem-base;intret0;if(!obj-funcs)obj-funcsdrm_gem_shmem_funcs;/* ① 回调表 */if(private){drm_gem_private_object_init(dev,obj,size);shmem-map_wcfalse;}else{retdrm_gem_object_init(dev,obj,size);/* ② shmem 文件 */}...retdrm_gem_create_mmap_offset(obj);/* ③ 伪偏移 */...INIT_LIST_HEAD(shmem-madv_list);if(!private)mapping_set_gfp_mask(obj-filp-f_mapping,/* ④ GFP 掩码 */GFP_HIGHUSER|__GFP_RETRY_MAYFAIL|__GFP_NOWARN);return0;...}① 回调表drm_gem_shmem_funcs是 helper 预置的一套drm_gem_object_funcsfree、pin、unpin、get_sg_table、vmap、vunmap、mmap、vm_ops都指向 helper 的实现。该赋值的条件为if (!obj-funcs)若驱动已在gem_create_object中设置了自身的回调表helper 不会将其覆盖4.3.7。② 建立 shmem 文件drm_gem_object_init()位于 GEM coredrm_gem.c不属于 helperintdrm_gem_object_init(structdrm_device*dev,structdrm_gem_object*obj,size_tsize){structvfsmount*huge_mnt;structfile*filp;constvma_flags_tflagsmk_vma_flags(VMA_NORESERVE_BIT);drm_gem_private_object_init(dev,obj,size);huge_mntdrm_gem_get_huge_mnt(dev);if(huge_mnt)filpshmem_file_setup_with_mnt(huge_mnt,drm mm object,size,flags);elsefilpshmem_file_setup(drm mm object,size,flags);...obj-filpfilp;return0;}shmem_file_setup()在内核内部的 tmpfs 上创建一个没有路径的文件并设置到obj-filp。文件大小即对象大小此后不再改变文件在drm_gem_object_release()中随对象一起释放与创建它的进程无关。此即 4.3 第 3.1 节所述的「必选层满足」。此时尚未分配任何物理页。shmem_file_setup()仅建立了一个可按偏移取页的容器address_space已就位page cache 仍为空。页要等到 4.3.2 的get_pages才获取。若驱动事先调用drm_gem_huge_mnt_create()为设备挂载了启用 THP 的 tmpfs文件即创建于该挂载点上此后获取的页可能为 PMD 大小的 foliommap 缺页时可按大页插入4.3.4。③ 伪偏移drm_gem_create_mmap_offset()在设备的drm_vma_offset_manager中为对象分配一段伪偏移用户态以该偏移调用mmap。这是第三章 3.3 节所述的 GEM 通用机制shmem helper 直接复用。伪偏移在 shmem helper 中有两处用途mmap 时据此找到对象4.3.4purge 时被回收4.3.6。④ GFP 掩码设备层的第一处修补mapping_set_gfp_mask(obj-filp-f_mapping,GFP_HIGHUSER|__GFP_RETRY_MAYFAIL|__GFP_NOWARN);该步骤修改的是address_space的gfp_mask即「此后从该文件分配页时使用的标志」。源码注释说明了其理由Our buffers are kept pinned, so allocating them from the MOVABLE zone is a really bad idea, and conflicts with CMA.GFP_HIGHUSER不含__GFP_MOVABLE。shmem 文件的页默认从可迁移区域分配而这些页将被 helper 长期持有并供设备访问若位于可迁移区域会妨碍内存规整并与 CMA 冲突__GFP_RETRY_MAYFAIL尽力重试但允许最终失败避免大缓冲分配触发 OOM killer__GFP_NOWARN分配失败时不打印警告由上层处理错误。5、private导入对象不创建文件private为 true 时调用drm_gem_private_object_init()不创建 shmem 文件也不设置 GFP 掩码。该路径用于 dma-buf 导入页由导出方提供本地无需后备4.3.5。此时map_wc被置为 false源码注释给出的理由是 dma-buf 映射总是使用 write-combine由导出方决定。6、销毁与创建对称销毁路径drm_gem_shmem_release()对本地对象的处理恰好按相反顺序撤销创建与使用过程中建立的各项资源voiddrm_gem_shmem_release(structdrm_gem_shmem_object*shmem){structdrm_gem_object*objshmem-base;if(drm_gem_is_imported(obj)){drm_prime_gem_destroy(obj,shmem-sgt);}else{dma_resv_lock(shmem-base.resv,NULL);...if(shmem-sgt){/* 设备映射 */dma_unmap_sgtable(obj-dev-dev,shmem-sgt,DMA_BIDIRECTIONAL,0);sg_free_table(shmem-sgt);kfree(shmem-sgt);}if(shmem-pages)drm_gem_shmem_put_pages_locked(shmem);/* 所持有的页 */...dma_resv_unlock(shmem-base.resv);}drm_gem_object_release(obj);/* shmem 文件与伪偏移 */}helper 默认回调表中的free为drm_gem_shmem_object_free()该函数调用drm_gem_shmem_free()后者即drm_gem_shmem_release()加上对象本身内存的释放。7、本节小结drm_gem_shmem_object头部内嵌drm_gem_object对象身份、resv、伪偏移、后备文件均由base提供其余字段均围绕「所持有的页处于何种状态」组织。创建分为分配与初始化两步分配可由驱动通过gem_create_object接管。初始化四步设置回调表、建立 shmem 文件、分配伪偏移、设定 GFP 掩码。导入对象走private路径不建文件。创建结束时只有文件没有页。下一节4.3.2页何时、如何从 shmem 文件中取出又如何归还以及这些操作共同依赖的那把锁。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →