ARM64平台5.15.149-android13内核下kmalloc大连续内存块失败问题求助
内核大块连续内存分配Panic问题排查分析
这个问题确实有点反直觉——明明DMA32 Zone显示有充足的64KB UNMOVABLE类型连续块,却还是触发了page allocation failure panic。我们从日志细节到内核机制一步步拆解原因:
先明确核心分配请求
首先看最关键的错误日志:
[29001.362425] kworker/u12:4: page allocation failure: order:4, mode:0x40dc0(GFP_KERNEL|__GFP_COMP|__GFP_ZERO), nodemask=(null),cpuset=/,mems_allowed=0
- 你需要的是order=4的连续内存:对应
2^4=16个4KB页,也就是64KB的连续物理内存 - 分配标志解析:
GFP_KERNEL:允许内核睡眠、回收页缓存来尝试分配,属于最宽松的分配模式之一__GFP_COMP:要求分配复合页(compound page)——这类页是把多个小页合并成大页,内核里通常用于驱动的大块内存申请__GFP_ZERO:分配完成后自动清零内存- 迁移类型:结合
__GFP_COMP和驱动内核对象的特性,这次分配的内存属于MIGRATE_UNMOVABLE(内核驱动的内存通常不能被移动,避免指针失效)
内存Zone状态的关键细节
我们分别看Normal和DMA32两个Zone的空闲页情况:
Normal Zone:确实无可用匹配块
[29001.382397] Normal: 49981*4kB (UMEH) 7697*8kB (UMEH) 3890*16kB (UMEH) 41*32kB (UMEH) 4*64kB (H) 2*128kB (H) 3*256kB (H) 0*512kB 0*1024kB 0*2048kB 0*4096kB = 326332kB
- 括号里的字母是迁移类型:
U=UNMOVABLE,M=MOVABLE,E=RECLAIMABLE,H=HIGHATOMIC - 你需要的64KB(order=4)块,Normal Zone里只有4个,且全是
H类型——这部分内存是预留给中断上下文等不能睡眠的紧急分配,GFP_KERNEL的普通UNMOVABLE请求完全碰不到,所以Normal Zone直接分配失败,符合你的判断。
DMA32 Zone:看似有可用块,为什么分配失败?
[29001.382385] DMA32: 3493*4kB (UMEC) 1622*8kB (UMEC) 663*16kB (UMEC) 338*32kB (UMEC) 383*64kB (UMEC) 245*128kB (UMEC) 91*256kB (UMC) 40*512kB (UMEC) 5*1024kB (UM) 2*2048kB (UM) 0*4096kB = 157236kB
- 这里明确显示有383个64KB的块,且迁移类型包含
U(UNMOVABLE),理论上应该能满足请求,但实际失败了,可能的原因有这些:
可能的根因分析
1. DMA32 Zone的分配优先级或限制
- Android定制内核可能修改了Zone的分配优先级:默认情况下
GFP_KERNEL会从Normal优先分配,失败后回退到DMA32,但某些定制场景下可能限制了DMA32的使用范围(比如只给特定硬件驱动),导致你的请求无法访问DMA32的UNMOVABLE块。 - 另外,检查内核配置的
CONFIG_DMA32相关选项,是否禁用了DMA32 Zone的高阶(order>=4)内存分配。
2. __GFP_COMP复合页的特殊限制
- 在ARM64平台的5.15-android13内核中,DMA32 Zone可能对复合页的支持有限:比如某些硬件不支持DMA32区域的大页,或者内核配置关闭了DMA32的复合页功能(比如
CONFIG_COMPOUND_PAGE的子选项限制)。 - 你可以尝试去掉
__GFP_COMP标志,只用GFP_KERNEL|__GFP_ZERO申请内存,如果能成功,就说明问题出在复合页的DMA32分配逻辑上。
3. 内存预留与回收机制的问题
- 看DMA32的
lowmem_reserve[]: 0 20049 20049:这个值是内核为高阶分配预留的内存量,order=4的分配需要检查预留是否足够,如果预留值不足,内核会拒绝分配。 - 另外,
GFP_KERNEL允许回收页缓存,但如果DMA32 Zone的可回收页(E类型)被锁定或者无法回收(比如被其他进程占用且不能释放),可能导致内核无法凑出连续块——不过这里你要的是U类型块,理论上不需要回收,所以这个可能性较低。
4. Android内核的定制特性干扰
- Android的
lowmemorykiller机制:如果分配发生时正好触发了LMK,内存回收的逻辑可能出现异常,导致分配失败。 - 内存控制组(memcg)限制:虽然日志显示
cpuset=/,但memcg可能限制了kworker进程的高阶内存分配额度,导致无法申请到64KB块。 - CMA区域:日志里有
free_cma:73684kB,但CMA是给MOVABLE类型内存用的,和你要的UNMOVABLE无关,所以可以排除。
5. 特定内核版本的bug
- 5.15.149是Android13的特定内核版本,可能存在已知的DMA32高阶分配bug。你可以查看Linux stable内核的5.15分支补丁列表,搜索是否有关于
page allocation failure order=4 DMA32的修复补丁,或者检查Android内核的定制补丁是否引入了问题。
建议的排查步骤
- 快速验证:修改分配标志:暂时去掉
__GFP_COMP,用GFP_KERNEL|__GFP_ZERO申请内存,如果能成功,就锁定问题在复合页的DMA32分配逻辑上。 - 检查内核配置:确认
CONFIG_DMA32、CONFIG_COMPOUND_PAGE、CONFIG_TRANSPARENT_HUGEPAGE等选项是否开启,且没有对DMA32 Zone做限制。 - 查看补丁历史:检查5.15.149内核的补丁,以及后续的5.15 stable补丁,是否有相关的分配失败修复。
- 启用内存调试:打开内核的
CONFIG_DEBUG_PAGEALLOC、CONFIG_DEBUG_KMEMLEAK等调试选项,获取更详细的分配失败时的内存状态,定位具体的分配失败点。 - 低负载测试:在系统空闲时触发驱动的内存分配,看是否能成功,排除内存碎片化的影响。
内容的提问来源于stack exchange,提问作者wonderzyp
相关产品推荐
相关产品推荐

