首页 > 其他分享 >实战中学习:CMA和DMA_CMA导致安装大游戏失败的问题

实战中学习:CMA和DMA_CMA导致安装大游戏失败的问题

时间:2024-09-14 15:24:48浏览次数:3  
标签:实战 DMA tgz cache CMA 内存 CONFIG


 在实际遇到的问题中,遇到Android虚机中安装超过1.8G以上的大游戏APK文件时,就会出现安装失败的现象,通过分析,发现在/data/目录下进行大文件拷贝时,就会出现数据不一致,发现时内核打开了CMA和DMA_CMA,解决方法目前主要回退,将CMA和DMA_CMA的配置关闭

现象: 

拷贝5次: 数据不一致

/data/app # cp -raf testdata.tgz t1.tgz         
/data/app # cp -raf testdata.tgz t2.tgz         
/data/app # cp -raf testdata.tgz t3.tgz         
/data/app # cp -raf testdata.tgz t4.tgz         
/data/app # cp -raf testdata.tgz t5.tgz         
/data/app # md5sum *.tgz                                                                                                                                      
56d399c2c7ec1790c6bbd0fc81a08a97  t1.tgz
4d998543350652d4507c5d8383534399  t2.tgz
765c8ed479e98530a914e2459ffa9044  t3.tgz
dd508d8f080df7c8d9898bc5788e86df  t4.tgz
4a26d0bd23f9ecd52d11ccc5fd6cde8b  t5.tgz
c5c9032ba047d8c4c8e0dd1492fe1a35  testdata.tgz
/data/app # sync
/data/app # md5sum *.tgz                                                                                                         
3778398d88bc9ce492de7aa8ca91617c  t1.tgz
5056cd7cb67968777ea0be98427e85d4  t2.tgz
50fd5d9db85acc6d46250db3e971838c  t3.tgz
afaf4797277936d3c88416c07fc75034  t4.tgz
c3d1b91b8529783e38a9faa91d71df8b  t5.tgz
b49da3e10c92d5f189829d9ac0845ad1  testdata.tgz

/data/app # ls -lh
total 6.6G
-rw-rw-rw- 1 root   root   2.1G 2024-09-10 18:53 t1.tgz
-rw-rw-rw- 1 root   root   2.1G 2024-09-10 18:53 t2.tgz
-rw-rw-rw- 1 root   root   2.1G 2024-09-10 18:53 t3.tgz
-rw-rw-rw- 1 root   root   2.1G 2024-09-10 18:53 t4.tgz
-rw-rw-rw- 1 root   root   2.1G 2024-09-10 18:53 t5.tgz
-rw-rw-rw- 1 root   root   2.1G 2024-09-10 18:50 testdata.tgz
 

内核中的配置:

@@ -376,6 +376,7 @@ CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
 CONFIG_SPARSEMEM_VMEMMAP=y
 CONFIG_HAVE_MEMBLOCK=y
 CONFIG_NO_BOOTMEM=y
+CONFIG_MEMORY_ISOLATION=y
 # CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
 CONFIG_SPLIT_PTLOCK_CPUS=4
 CONFIG_MEMORY_BALLOON=y
@@ -389,7 +390,10 @@ CONFIG_KSM=y
 CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
 # CONFIG_TRANSPARENT_HUGEPAGE is not set
 # CONFIG_CLEANCACHE is not set
-# CONFIG_CMA is not set
+CONFIG_CMA=y
+# CONFIG_CMA_DEBUG is not set
+# CONFIG_CMA_DEBUGFS is not set
+CONFIG_CMA_AREAS=7
 # CONFIG_ZPOOL is not set
 # CONFIG_ZBUD is not set
 # CONFIG_ZSMALLOC is not set
@@ -980,6 +984,17 @@ CONFIG_REGMAP=y
 CONFIG_REGMAP_MMIO=y
 CONFIG_DMA_SHARED_BUFFER=y
 # CONFIG_FENCE_TRACE is not set
+CONFIG_DMA_CMA=y
+
+#
+# Default contiguous memory area size:
+#
+CONFIG_CMA_SIZE_MBYTES=16
+CONFIG_CMA_SIZE_SEL_MBYTES=y
+# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set
+# CONFIG_CMA_SIZE_SEL_MIN is not set
+# CONFIG_CMA_SIZE_SEL_MAX is not set
+CONFIG_CMA_ALIGNMENT=8
 

CMA和DMA_CMA相关的概念之前没怎么接触过,特此做个笔记: 

一、什么是CMA

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、调用伙伴系统接口,进行真正的内存分配。

Linux内核中已经提供了各种内存分配的接口,为何还有建立CMA这种连续内存分配的机制呢?

我们先来看看内核哪些模块有物理地址连续的需求。huge page模块需要物理地址连续是显而易见的。大家都熟悉的处理器(不要太古老),例如ARM64,其内存管理单元都可以支持多个页面大小(4k、64K、2M或者更大的page size),但在大多数CPU架构上,Linux内核总是倾向使用最小的page size,即4K page size。Page size大于4K的page统称为“huge page”。对于一个2M的huge page,MMU会把一个连续的2M的虚拟地址mapping到连续的、2M的物理地址上去,当然,这2M size的物理地址段必须是由512个地址连续的4k page frame组成。

当然,更多的连续内存的分配需求来自形形色色的驱动。例如现在大家的手机都有视频功能,camer功能,这类驱动都需要非常大块的内存,而且有DMA用来进行外设和大块内存之间的数据交换。对于嵌入式设备,一般不会有IOMMU,而且DMA也不具备scatter-getter功能,这时候,驱动分配的大块内存(DMA buffer)必须是物理地址连续的。

顺便说一句,huge page的连续内存需求和驱动DMA buffer还是有不同的,例如在对齐要求上,一个2M的huge page,其底层的2M 的物理页面的首地址需要对齐在2M上,一般而言,DMA buffer不会有这么高的对齐要求。因此,我们这里讲的CMA主要是为设备驱动准备的,huge page相关的内容不在本文中描述。

我们来一个实际的例子吧:我的手机,像素是1300W的,一个像素需要3B,那么拍摄一幅图片需要的内存大概是1300W x 3B = 26MB。通过内存管理系统分配26M的内存,压力可是不小。当然,在系统启动之处,伙伴系统中的大块内存比较大,也许分配26M不算什么,但是随着系统的运行,内存不断的分配、释放,大块内存不断的裂解,再裂解,这时候,内存碎片化导致分配地址连续的大块内存变得不是那么的容易了,怎么办?作为驱动工程师,我们有两个选择:其一是在启动时分配用于视频采集的DMA buffer,另外一个方案是当实际使用camer设备的时候分配DMA buffer。前者的选择是可靠的,但它有一个缺点,即当照相机不使用时(大多数时间内camera其实都是空闲的),预留的那些DMA BUFFER的内存实际上是浪费了(特别在内存配置不大的系统上更是如此)。后一种选择不会浪费内存,但是不可靠,随着内存碎片化,大的、连续的内存分配变得越来越困难,一旦内存分配失败,camera功能就会缺失,估计用户不会答应。

这就是驱动工程师面临的困境,为了解决这个问题,各个驱动各出奇招,但是都不能非常完美的解决问题。最终来自Michal Nazarewicz的CMA补丁将可以把各个驱动工程师的烦恼“一洗了之”。对于CMA 内存,当前驱动没有分配使用的时候,这些memory可以内核的被其他的模块使用(当然有一定的要求),而当驱动分配CMA内存后,那些被其他模块使用的内存需要吐出来,形成物理地址连续的大块内存,给具体的驱动来使用。

三、CMA模块的蓝图是怎样的?

实战中学习:CMA和DMA_CMA导致安装大游戏失败的问题_android

虽然用于解决驱动的内存分配问题,但是驱动并不会直接调用CMA模块的接口,而是通过DMA mapping framework来间接使用CMA的服务。一开始,CMA area的概念是全局的,通过内核配置参数和命令行参数,内核可以定位到Global CMA area在内存中的起始地址和大小(注:这里的Global的意思是针对所有的driver而言的)。并在初始化的时候,调用dma_contiguous_reserve函数,将指定的memory region保留给Global CMA area使用。人性是贪婪的,驱动亦然,很快,有些驱动想吃独食,不愿意和其他驱动共享CMA,因此出现两种CMA area:Global CMA area给大家共享,而per device CMA可以给指定的一个或者几个驱动使用。这时候,命令行参数不是那么合适了,因此引入了device tree中的reserved memory node的概念。当然,为了兼容,内核仍然支持CMA的command line参数。

DMA其他概念: 一致性缓存

CPU写内存的时候有两种方式:

  1. write through: CPU直接写内存,不经过cache。
  2. write back: CPU只写到cache中。cache的硬件使用LRU算法将cache里面的内容替换到内存。通常是这种方式。

DMA可以完成从内存到外设直接进行数据搬移。但DMA不能访问CPU的cache,CPU在读内存的时候,如果cache命中则只是在cache去读,而不是从内存读,写内存的时候,也可能实际上没有写到内存,而只是直接写到了cache。

实战中学习:CMA和DMA_CMA导致安装大游戏失败的问题_内存分配_02

这样一来,如果DMA从将数据从外设写到内存,CPU中cache中的数据(如果有的话)就是旧数据了,这时CPU在读内存的时候命中cache了,就是读到了旧数据;CPU写数据到内存时,如果只是先写到了cache,则内存里的数据就是旧数据了。这两种情况(两个方向)都存在cache一致性问题。例如,网卡发包的时候,CPU将数据写到cache,而网卡的DMA从内存里去读数据,就发送了错误的数据。

实战中学习:CMA和DMA_CMA导致安装大游戏失败的问题_Linux内核_03

2 如何解决一致性问题
主要靠两类APIs:

2.1 一致性DMA缓存(Coherent DMA buffers)

DMA需要的内存由内核去申请,内核可能需要对这段内存重新做一遍映射,特点是映射的时候标记这些页是不带cache的,这个特性也是存放在页表里面的。
上面说“可能”需要重新做映射,如果内核在highmem映射区申请内存并将这个地址通过vmap映射到vmalloc区域,则需要修改相应页表项并将页面设置为非cache的,而如果内核从lowmem申请内存,我们知道这部分是已经线性映射好了,因此不需要修改页表,只需修改相应页表项为非cache即可。

相关的接口就是dma_alloc_coherent()和dma_free_coherent()。dma_alloc_coherent()会传一个device结构体指明给哪个设备申请一致性DMA内存,它会产生两个地址,一个是给CPU看的,一个是给DMA看的。CPU需要通过返回的虚拟地址来访问这段内存,才是非cache的。至于dma_alloc_coherent()的内部实现可以不关注,它是和体系结构如何实现非cache(如mips的kseg1)相关,也可能与硬件特性(如是否支持CMA)相关。

还有一个接口dma_cache_sync(),可以手动去做cache同步,上面说dma_alloc_coherent()分配的是uncached内存,但有时给DMA用的内存是其他模块已经分配好的,例如协议栈发包时,最终要把skb的地址和长度交给DMA,除了将skb地址转换为物理地址外,还要将CPU cache写回(因为cache里可能是新的,内存里是旧的)。

2.2 流式DMA映射(DMA Streaming Mapping),
相关接口为 dma_map_sg(), dma_unmap_sg(),dma_map_single(),dma_unmap_single()。
一致性缓存的方式是内核专门申请好一块内存给DMA用。而有时驱动并没这样做,而是让DMA引擎直接在上层传下来的内存里做事情。例如从协议栈里发下来的一个包,想通过网卡发送出去。
但是协议栈并不知道这个包要往哪里走,因此分配内存的时候并没有特殊对待,这个包所在的内存通常都是可以cache的。
这时,内存在给DMA使用之前,就要调用一次dma_map_sg()或dma_map_single(),取决于你的DMA引擎是否支持聚集散列(DMA scatter-gather),支持就用dma_map_sg(),不支持就用dma_map_single()。DMA用完之后要调用对应的unmap接口。

由于协议栈下来的包的数据有可能还在cache里面,调用dma_map_single()后,CPU就会做一次cache的flush,将cache的数据刷到内存,这样DMA去读内存就读到新的数据了。

注意,在map的时候要指定一个参数,来指明数据的方向是从外设到内存还是从内存到外设:
从内存到外设:CPU会做cache的flush操作,将cache中新的数据刷到内存。
从外设到内存:CPU将cache置无效,这样CPU读的时候不命中,就会从内存去读新的数据。

还要注意,这几个接口都是一次性的,每次操作数据都要调用一次map和unmap。并且在map期间,CPU不能去操作这段内存,因此如果CPU去写,就又不一致了。
同样的,dma_map_sg()和dma_map_single()的后端实现也都是和硬件特性相关。

参考:

Linux内存管理 —— DMA和一致性缓存(dma_alloc_coherent(),dma_map_sg())_还没写完内存就读-博客

 https://zhuanlan.zhihu.com/p/542008458

http://www.wowotech.net/memory_management/cma.html

https://zhuanlan.zhihu.com/p/575194785

标签:实战,DMA,tgz,cache,CMA,内存,CONFIG
From: https://blog.51cto.com/u_15243273/12017116

相关文章

  • 实战分享:我是如何挖到C站漏洞的?
    文章目录前言一、过程二、总结《Windows信息安全和网络攻防》——清华大学出版社前言CxxN是国内很出名的博客平台,用户量非常大,注册用户据说有1个亿?(官方写的)本次我发现的漏洞详情是可以通过用户的id直接获取用户完整的手机号,姓名,性别,出生年月日、毕业院校、就职公......
  • MySQL 误操作 误修改 binlog 还原恢复实战 超详细
    硬盘有价,数据无价,数据库执行,谨慎操作!binlog日志还原不适用于直接删表删库的误操作!目录实战恢复1、导出相关时间binlog数据2、找到对应语句以及pos区间3、导出改动区间的sql4、将binlog导出的sql转换为逆向修复sql        1、从上面binlog日志可以看出  开头......
  • ChatGPT实战100例 - (21) 搞定汉字新解,o1-mini 在李继刚老师这扳回一局
    文章目录搞定汉字新解,o1-mini在李继刚老师这扳回一局翻车开车飙车出图福利在这福利+1搞定汉字新解,o1-mini在李继刚老师这扳回一局昨天朋友圈刷爆了李继刚老师的汉字新解,废话不说,上prompt;;作者:李继刚;;版本:0.1;;模型:ClaudeSonnet;;......
  • 项目实战 (11)---搜索进度
    目录背景相关技术需要解决的问题查询进度实时展示描述代码python后端htmlJS运行效果查询逻辑结合描述代码运行效果总结与问题背景通过前面1-10,视频搜索系统的前后端及视频录入功能已经可以正常使用。但是我们清楚随着视频量的增加及客户搜索并发数的增加,后......
  • Qt 实战(10)模型视图 | 10.3、模型数据索引
    文章目录一、模型数据索引1、什么是模型数据索引?2、模型3、获取`QModelndex`3.1、获取列表数据项索引3.2、获取表格数据项索引3.3、获取树数据项索引4、模型数据更新前言:在Qt框架中,模型/视图(Model/View)架构是一种强大的机制,它允许开发者将数据的存储(Model)与数据......
  • 企业私有云容器化架构运维实战
    一、虚拟化技术概述了解什么是虚拟化虚拟化(Virtualization)技术最早出现在20世纪60年代的IBM大型机系统,在70年代的System370系列中逐渐流行起来,这些机器通过一种叫虚拟机监控器(VirtualMachineMonitor,VMM)的程序在物理硬件之上生成许多可以运行独立操作系统软件的......
  • python-游戏自动化(三)(实战-豆腐女孩)
    前提准备特别注意:        本节教程所演示的模拟器分辨率设置为720x1080(手机版),电脑分辨率设置大720x1080并且没有设置放大。        今天的课程开始之前我们来回顾一下昨天所学的知识内容,因为今天要学的内容和昨天内容有着紧密的联系。昨天的课程主要讲解了......
  • 8、【实战中提升自己】华为 华三中小型企业网络架构搭建 【无线架构之三层漫游】
     1 拓扑与说明         某公司的网络架构,这样的架构在目前的网络中是在常见的,假设您接收一个这样的网络,应该如何部署,该实战系列,就是一步一步讲解,如何规划、设计、部署这样一个环境,这里会针对不同的情况给出不同的讲解,比如拓扑中有2个ISP,假设客户需求是,想实现主备的......
  • YoloV8实战:使用Ultralytics实现Sam2的推理
    SegmentAnythingModel2(SAM2)作为Meta公司发布的SegmentAnythingModel(SAM)的升级版本,在图像和视频分割领域展现出了显著的优点和特性。论文连接:https://arxiv.org/pdf/2408.00714Demo:https://sam2.metademolab.comCode:https://github.com/facebookresearch/segme......
  • 自然语言处理系列六十八》搜索引擎项目实战》搜索引擎系统架构设计
    注:此文章内容均节选自充电了么创始人,CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】文章目录自然语言处理系列六十八搜索引擎项目实战》搜索引擎系统架构设计搜索引擎项目代码实战总结自然语言处理系......