嵌入式Linux/Android疑难杂症工作笔记
硬件平台
公司自研 ARM Cortex-A57 4核 SOC 产品板
软件环境
系统: Android-P
Linux内核版本: 4.9
运行公司内部的kernel_submit内核压力测试脚本,通过内存工具抓取物理内存使用率的情况
现象描述
系统内存使用率的情况如图,该图是连续24小时Linux 内核压力测试的物理内存使用率的情况,蓝色的点代表不运行压力测试的时候正常的内存使用率,红色的点代表运行内核压力测试时物理内存使用率上下震荡,缓慢上升,在16小时左右发生了low memory killer (oom killer) 导致系统很多后台服务被杀死,内存使用率断崖式下滑
Root Cause
Linux内核某个模块的驱动代码是这样写的,
int bug_driver(struct bug_dev *dev)
{
struct foo *bar = NULL;
bar = kzalloc(sizeof(struct foo)) ;
if (bar) {
kfree(dev->spec);
dev->spec = bar;
bar = NULL;
return 0;
}
return -1;
}
这段代码看起来没有导致内存泄漏,因为旧的dev->spec指针先kfree()之后,重新保存了kzalloc()分配的内存,下次运行到这里的时候,会先释放旧的,再获取新的内存,不会泄漏。
但是大公司,分工比较细,一个驱动模块可能很多个人进来修改过,另外一个工程师在这个函数上层的某个地方写了一段这样的代码:
struct bug_dev dev;
bug_driver(&dev);
....................一大段代码省略...................................
memset(&dev, 0x00, sizeof(struct bug_dev));
dev.spec = kmalloc(sizeof(struct bug_dev))
因为这段代码直接把通过bug_driver()函数获取的bug_dev->spec指针意外得memset()再重新分配了,因而下次再调用bug_driver(&dev) 函数时,在里面kfree(dev->spec)的指针并不是上一次的kzalloc()的bar指针,上一次kzalloc()的bar指针彻底变成孤魂野鬼未引用对象存在内核里面,久而久之内存使用率就上去了,造成了内存泄漏
该内存泄漏导致oom killer需要16小时才复现,每次泄漏2KB其实很少,而且该内核压力测试脚本本身运行时就有内存使用上下震荡波动,因而几分钟之内的实验是看不出有内存泄漏的
硬件平台
公司自研 ARM Cortex-A57 4核 SOC 产品板
软件环境
系统: Android-P
Linux内核版本: 4.9
运行公司内部的Linux内核压力测试脚本,通过内存工具抓取物理内存使用率的情况
现象描述
系统内存使用率的情况如图,物理内存使用率上下震荡,缓慢上升,该图是连续24小时Linux 内核压力测试的物理内存使用率的情况,通过缩小范围,修复内存泄露问题之后,发现压力测试反复调用Android Framework的stop / start 命令引发内存使用率升高,导致oom killer。从该图得到的另外的信息是,stop / start 压力测试停止之后(停止震荡上升的后面蓝色直线),内存使用率就维持一个很高的比例,不会降低
Root Cause
跑Android Framework stop /start 反复kill 和启动FW的核心进程,会导致物理内存的碎片化,而Android核心进程启动又需要大块的内存页,从 cat /proc/buddyinfo 可以看出刚开始跑测试,系统还要很多较大块的内存页,结果到后来,大块内存页越来越少,碎片页得不到释放合并,FW 的server 重新启动又不得不去分配超过需求的内存大页,引发内存使用率不断上升,最后oom killer
解决过程总结
硬件平台
公司自研 ARM Cortex-A57 4核 SOC 参考板
软件环境
系统: Android-N
Linux内核版本: 4.4
驱动: linux/drivers/hwtracing/coresight
现象描述
将Linux-4.4 upstream 官方内核ARM提交的的CPU coresight PTM驱动移植到板子上,替换公司私有的coresight PTM驱动,寄存器和pipeline的dts配置都正常,但是ARM官方的coresight PTM驱动不能正常工作,而公司私有驱动可以正常工作
Root Cause
跑Android 系统 ARM Cortex-A57 CPU有用于低功耗电源管理的dynamic power gating功能,该功能会以一定频率不停地运行。在系统运行dynamic power gating的时候,会把coresight PTM的寄存器全部清零,而如果没有保存之前寄存器的值,则cpu从dynamic power gating恢复之后,寄存器的值一直是0,之前配置的值没有恢复导致不能正常工作
解决过程总结
硬件平台
公司自研 ARM Cortex-A57 4核 SOC CostDown版本参考板
在之前的参考板基础上,内存从8G缩减到4G, 更换了PMIC, eMMC从32G缩减到16G
之前的板子是已经量产,完全能够正常工作,SOC芯片和CostDown的参考板完全一样
软件环境
系统: Android-N
Linux内核版本: 4.4
现象描述
Android系统启动Zygote SystemServer的时候,在尝试启动其它服务进程的时候,那些服务进程都因为SELinux security的原因,被kill掉,在尝试多次启动服务和被Kill之后,系统最后关机重启
Root Cause
PMIC 的SOC Thermal 加入了限流功能,一旦启动的时候开启限流,SOC的CPU就会降频,一旦CPU降频, 启动过程就会减慢,Security固件加载就会延后,一旦Security固件加载延后,就会在Zygote 启动各种系统服务的时候,引发SELinux的安全保护机制,导致各个系统服务因为Security原因被Kill掉,进而关机。可谓是一个小问题引发一连串问题
解决过程总结
硬件平台
公司自研 ARM Cortex-A57 4核 SOC 产品板
eMMC使用SanDisk新一代 eMMC 5.1存储器,有扩展指令支持device report 等温度信息、flash读写次数等芯片状态
软件环境
系统: Android-P
Linux内核版本: 4.9
现象描述
想在sysfs中增加一个文件,通过eMMC芯片的异步I/O读取eMMC芯片的温度信息,但是总是因为各种原因读取的时候直接卡死或者失败
Root Cause
eMMC设备驱动不同于led, 电源管理芯片等普通的字符设备驱动,可以直接异步I/O读取。因为在Linux内核里面存储相关的设备驱动上层有负责块Block设备队列管理的内核线程在实时占用设备并与文件系统数据同步,如果不加同步互斥的方法直接异步I/O读取eMMC的device report状态信息,那么将与其eMMC驱动上层的Block设备队列管理的内核线程发生干扰和冲突,进而会卡死或者引发意想不到的side effect。
另一方面,由于Android系统有动态电源,时钟等功耗管理,及时此时Block设备队列很空闲,没有做同步,但是由于电源管理的dynamic clock gating等特性,没有加锁互斥去情况下异步I/O读取的时候,eMMC可能会由于dynamic clock gating导致暂时休眠而读不到数据
解决过程总结
硬件平台
公司自研ARM Cortex-A9 / MIPS 32 SOC
NorFlash 启动bootloader,加载 Nandflash 中的Linux 内核与Rootfs
软件环境
现象描述
不同平台分别测试3块demo板,有一些板子挂载rootfs的时候出现kernel panic, 有一些没有,看上去和arm 还是 mips cpu无关, arm mips都有出现,是概率性问题,不是必现
Root Cause
烧写工具标记Nandflash坏块记录BBT表的功能出现bug, 导致烧写的时候某些坏块没有跳过,而rootfs如果正好烧写到某些坏块上,则出现kernel Panic
不要认为烧写工具是绝对可靠, 没有Bug的
解决过程总结
1.刚入职对BBT坏块表的概念不是很清楚,以为烧写工具是绝对没问题的,所以不敢怀疑。
现象描述
客户更换的某些型号的NandFlash 产品板有一定概率无法启动Uboot
Root Cause
这一代SOC芯片的bootrom 固件程序对某些NandFlash较长位数的ECC校验存在Bug
不要认为芯片本身是绝对可靠,没有Bug的
解决过程总结
硬件平台
公司自研 MIPS 32 SOC + 客户通过某安全认证的产品板
软件环境
嵌入式Linux系统, 客户基于公司Buildroot 构建rootfs移植了自己的系统,应用以及安全认证相关的第三方软件库,rootfs是ramdisk readonly的, 启动先挂载ramdisk rootfs再挂载客户自己的应用分区
现象描述
运行公司提供的SDK音乐播放测试用例,播放mp3文件出现exception异常,该异常来自于glibc,客户声称没有对sdk底层代码进行修改
Root Cause
客户为通过第三方安全认证的specification规定glibc库的要求,强行替换升级了板子里面rootfs的glibc库的版本重新编译。公司发布给客户的SDK里面有不开源的固件(比如MP3硬解码器插件)是基于旧的glibc编译的,客户替换升级glibc版本的时候,无法重新编译替换不开源的插件,从而导致兼容性问题
解决过程总结
硬件平台
公司自研 MIPS 32 SOC demo板, DRAM由256MB升级到512MB
软件环境
现象描述
PVR做整晚的录像和删除压力测试(录像文件超过某个大小,前面的帧会被删除), 用以测试新的512MB内存是否能够全部利用正常工作,结果压力测试导致系统死机
Root Cause
MIPS 32位架构的CPU的物理内存,普通内存是0~256MB, 高端内存默认从256MB往上。由于使用了512MB的DRAM, 重新调整了某些IP的memory CMA的地址空间,导致PVR录像模块使用了高端内存,而MIPS高端内存映射是不稳定不保证物理地址连续的,而录像一般需要连续的物理地址,从而导致系统问题死机
解决过程总结
硬件平台
全志R16 SOC Cortext-A7 四核处理器,公司产品板(某IPC+智能音箱产品)
软件环境
嵌入式Linux OpenWRT系统, 使用科大讯飞的SDK做离线的NLP语音识别,运行流程如下:
麦克风采集的语音信息 --> 讯飞远端服务器 --> 识别语音信息,转换成文字 --> 根据识别的文字生成对答的文字 --> 返回识别的文字与对答的文字的字符串数据(json格式)
现象描述
一晚上不停输入语音信息,做压力测试,会概率性出现段错误,找不到规律性复现段错误的方法
Root Cause
之前历史遗留代码代码中,有一段从返回关于“今天天气如何?/今天天气怎么样?”的对答语句中提取天气信息的代码,该代码在提取信息格式时用了一些hardcode编码,而服务器端返回相关对答信息的文字格式和组合会有一些变化,使用hardcode导致解析信息时指针越界段错误
解决过程总结
硬件平台
全志R16 SOC Cortext-A7 四核处理器,公司产品板(某IPC+智能音箱产品)
软件环境
嵌入式Linux OpenWRT系统
现象描述
晚上烧机压力测试,发现系统核心的业务进程被OOM Killer杀死,而根据抓取的Log显示系统应该还剩余20%左右的物理内存,应该不至于触发Linux内核的oom killer机制
Root Cause
全志提供的Linux内核可能和Android版的Linux内核一起维护或者混用,在OpenWRT系统的Linux内核配置中有一些Android Linux内核的配置,其中Android内核使用的Low Memory Killer模块被选中,而Low Memory Killer 对最少物理内存的容忍程度比普通Linux内核的oom killer低,在还剩20%左右物理内存的情况下触发killer, 杀死了进程
解决过程总结
硬件平台
全志R16 SOC Cortext-A7 四核处理器,公司产品板(某IPC+智能音箱产品)
软件环境
嵌入式Linux OpenWRT系统, 交叉工具链使用arm-linux-uclibc-gcc
现象描述
晚上烧机压力测试, 发现系统应用程序空间出现了较为严重的内存碎片,后面可能导致一些应用进程申请内存失败
Root Cause
系统使用了基于较低版本的uclibc的交叉工具链,该uclibc库的内存分配和管理的函数对对避免内存碎片这块做的很差,换成较高版本的glibc工具链,则不会复现这个问题
解决过程总结
硬件平台
Tiny4412 开发板, 三星Exynos-4412 Cortex-A9 四核处理器
使用台湾沛成科技iP297X 系列UVC转换芯片的USB摄像头
软件环境
嵌入式Linux Builroot制作的rootfs, Linux内核采用 Linux-3.5/Linux-4.4等较高版本内核
现象描述
根据原厂提供的移植手册,在Linux较高版的内核(3.5/4.4), 将官方补丁加入Linux内核开源的UVC驱动中,USB摄像头不能正常工作,而在Linux-2.6 版本的内核中,加入官方补丁之后,USB摄像头是可以正常工作的
Root Cause
该台湾小厂的UVC摄像头USB转换芯片存在Bug, 看起来没有拿到USB UVC官方组织的认证和ID号,在补丁中PID, VID都是手动加上去的(不是USB UVC Class官方自带兼容的)。
这个未认证的USB UVC芯片,它枚举出来的最高带宽endpoint是无法在iso模式下正常工作的,而Linux-2.6 内核的UVC驱动可以获取摄像头512Bytes的次高带宽Endpoint进行ISO模式的数据传输,而Linux 3.5/4.4等高版本的内核中采用的Endpoint选择方法都是一定要找到最高带宽Endpoint来在ISO模式下传输,最后选择了无法正常工作的最高带宽Endpoint
解决过程总结
硬件平台
公司自研 ARM Cortex-A57 4核 SOC 参考板
软件环境
系统: Android-N
Linux内核版本: 4.4
现象描述
Android系统为了支持低功耗动态调压调频,板子上有很多regulator动态调压芯片,对regulator调压做稳定性压力测试,在规定的调压范围内,自动脚本通过驱动随机发出regulator调压指令,调节相关IP的regulator电压,发行某个IP在该regulator动态调压压力测试中容易导致系统死机
Root Cause
Android系统板的SOC有ovp/uvp欠压保护功能,某些regulator芯片调压范围太广,当前电压和regulator设置的电压的delta值过大,而由于板子上电容,电感的影响,电压变化曲线太平缓,没有及时升压到指定的电压而导致系统过压欠压保护引发死机
解决过程总结
硬件平台
RK3399产品板, USB ADB口用的是USB type-C
软件环境
系统: Android-M
Linux内核版本: 4.4
现象描述
客户根据开发板稍微修改的产品板,板子的USB上电检测,烧写固件没有问题,刷入Android-M系统,开机能正常启动进入系统,但是adb 功能不能正常工作,
adb devices没有反应
Root Cause
硬件工程师对USB Type-C电路的原理和结构理解有误,不知道USB Type-C和以前USB2.0有区别,type-C除了本身的USB协议热插拔电路功能另外还有控制芯片与系统进行i2c通信,系统USB驱动通过i2c发命令给Type-C的控制芯片设置USB的工作模式(如充电, ADB等)。硬件工程师将该Type-C的控制芯片的i2c地址换,设备树没有配置,i2c时钟频率也没有想过配置,导致无法设置成ADB模式
解决过程总结