关于CMA内存利用率问题

作者 by adtxl / 2022-07-28 / 暂无评论 / 309 个足迹

kernel version: Android common kernel 4.19.176

1. 问题的引入

在monkey测试app切换的过程中,会出现重启的情况,原因为Internal error: TLB conflict abort: 96000070 [#1] PREEMPT SMP.

以及wifi service出现的分配atomic内存失败问题,page allocation failure: order:0, mode:0x400000(GFP_NOWAIT), nodemask=(null)

基本排除内存泄漏的原因,怀疑该重启的原因和内存不足和内存碎片化有关。通过减少一些CMA内存和系统中的no-map内存,page allocation failure的问题明显改善,测试过程中也未出现TLB导致的问题。由此确定解决此问题可能需要增大系统中的内存或提高系统中的内存利用率。

下面是我在测试过程中抓取的一个meminfo和pagetypeinfo信息,从中可以看出,MemFree为240920KB,其中CmaFree为206272KB,也就是说,此时系统中的剩余的大部分空闲内存都是CMA内存。

console:/ # cat /proc/meminfo
MemTotal:        1306504 kB
MemFree:          240920 kB
MemAvailable:     593240 kB
Buffers:           62476 kB
Cached:           325232 kB
SwapCached:         4396 kB
Active:           318544 kB
Inactive:         380500 kB
Active(anon):     154436 kB
Inactive(anon):   163480 kB
Active(file):     164108 kB
Inactive(file):   217020 kB
Unevictable:        3840 kB
Mlocked:            3840 kB
SwapTotal:        393212 kB
SwapFree:         316412 kB
Dirty:                 4 kB
Writeback:             0 kB
AnonPages:        311356 kB
Mapped:           211900 kB
Shmem:              4400 kB
KReclaimable:      60036 kB
Slab:             145844 kB
SReclaimable:      60036 kB
SUnreclaim:        85808 kB
KernelStack:       22384 kB
PageTables:        32592 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     1046464 kB
Committed_AS:   20596196 kB
VmallocTotal:   263061440 kB
VmallocUsed:       39044 kB
VmallocChunk:          0 kB
Percpu:             3552 kB
HardwareCorrupted:     0 kB
CmaTotal:         380928 kB
CmaFree:          206272 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:               0 kB


console:/ # cat /proc/pagetypeinfo
Page block order: 9
Pages per block:  512

Free pages count per migrate type at order       0      1      2      3      4      5      6      7      8      9     10
Node    0, zone    DMA32, type    Unmovable      1    238     83    169      4      5      1      1      0      0      0
Node    0, zone    DMA32, type      Movable   2530    947    103     54      1      2      1      1      1      0      0
Node    0, zone    DMA32, type  Reclaimable     55     25      6      5      0      0      0      0      0      0      0
Node    0, zone    DMA32, type          CMA      1    260    596    211     70     17      1      0      0      0     44
Node    0, zone    DMA32, type   HighAtomic      2      1      0      0      0      0      0      0      0      0      0
Node    0, zone    DMA32, type      Isolate      0      0      0      0      0      0      0      0      0      0      0

Number of blocks type     Unmovable      Movable  Reclaimable          CMA   HighAtomic      Isolate
Node 0, zone    DMA32          122          340           26          186            1            0

2. 关于一些patch的研究

2.1 cma: redirect page allocation to CMA

关于如何提高CMA利用率,从网上找了一些博客,其中内存之旅——如何提升CMA利用率?提到一笔patch,即https://lkml.org/lkml/2020/11/2/646。

该笔patch的详细介绍可以参考上面的博客介绍,核心就是yin引入 GFP_CMA 标记。触发 page fault 时,对用户态的匿名页内存申请,将 Movable 申请中的用户态匿名页申请重定向到 CMA 区域分配,因为这类内存比内核态申请的 Movable 迁移成功的概率更大。

需要注意的是,该patch并未合入到kernel 4.19分支,参考https://elixir.bootlin.com/linux/v4.19.176/source/mm/page_alloc.c
但是,该分支在Android common kernel 4.19分支上已经合入,也就是说我们现在使用的版本上,已经是在使用此patch的。

image.png

2.2 允许在zram中使用cma内存

由于我们的内存比较小,所以使用了zram。近期zram做过一次更改,考虑到zram并不是越大越好,使用zram也是存在一些问题的。

参考zRAM内存压缩技术原理与应用,使用zram是会导致系统中内存碎片增加的。

zRam大小是可灵活配置的, 那是不是配置越大越好呢? 如果不是配置多大是最合适的呢?
zRam大小的配置比较灵活, 如果zRam配置过大, 后台缓存了应用过多, 这也是有可能会影响前台应用使用的流畅度。另外, zRam配置越大, 也需要关注系统的内存碎片化情。因此zRam并不是配置越大越好,具体的大小需要根据内存总大小及系统负载情况考虑及实测而定。

使用zRam,可能会存在低内存场景由于频繁的内存压缩导致kswapd进程占CPU高, 怎样改善?
zRam本质就是以时间换空间, 在低内存的情况下, 肯定会比较频繁地回收内存, 这时kswapd进程是比较活跃的, 再加上通过压缩内存, 会更加消耗CPU资源。 改善这种情况方法也比较多, 比如, 可以使用更优的压缩算法, 区别使用场景, 后台不影响用户使用的场景异步进行深度内存压缩, 与用户体验相关的场景同步适当减少内存压缩, 通过增加文件页的回收比例加快内存回收等等。

增大了zRam配置,对系统内存碎片是否有影响?
使用zRam是有可能导致系统内存碎片变得更严重的, 特别是zsmalloc分配不支持可移动内存类型的时候。新版的内核zsmalloc已经支持可移动类型分配的, 但由于增大了zRam,结合android手机的使用特点, 仍然会有可能导致系统内存碎片较严重的情况,因些内存碎片问题也是需要重点关注的。解决系统内存碎片的方法也比较多, 可以结合具体的原因及场景进行优化。

怀疑该问题可能和近期增大zram有关,但实际测试,将zram恢复到修改前,长时间测试还是会出现TLB导致的重启问题。

修改前
/dev/block/zram0 none swap defaults zramsize=402653184
修改后:
/dev/block/zram0 none swap defaults zramsize=507510784

zram默认是不会使用cma的内存的,考虑让zram使用cma内存,参考patchzram: allow zram to allocate CMA pages

实测发现在我们的平台上这样做会让系统挂的更快,测试不到十分钟就会重启,重启原因也是TLB导致的,所以这个patch在这里应该是用不上的。

[  601.687785] .(0)[630:kswapd0]Unhandled fault at 0xffffffc04b0d2000
[  601.694055] .(0)[630:kswapd0]Mem abort info:
[  601.698383] .(0)[630:kswapd0]  ESR = 0x96000070
[  601.702969] .(0)[630:kswapd0]  Exception class = DABT (current EL), IL = 32 bits
[  601.710424] .(0)[630:kswapd0]  SET = 0, FnV = 0
[  601.714994] .(0)[630:kswapd0]  EA = 0, S1PTW = 0
[  601.719677] .(0)[630:kswapd0]Data abort info:
[  601.724060] .(0)[630:kswapd0]  ISV = 0, ISS = 0x00000070
[  601.729390] .(0)[630:kswapd0]  CM = 0, WnR = 1
[  601.733866] .(0)[630:kswapd0]swapper pgtable: 4k pages, 39-bit VAs, pgdp = 000000004f8ee196
[  601.742265] .(0)[630:kswapd0][ffffffc04b0d2000] pgd=00000000fff99803, pud=00000000fff99803, pmd=00000000fff4c803, pte=00780000cb0d2f13
[  601.754435] -(0)[630:kswapd0]Internal error: TLB conflict abort: 96000070 [#1] PREEMPT SMP
[  601.762723] -(0)[630:kswapd0]Modules linked in: wlan_7961_usb(O) mtk7921_btusb(O) snd_usb_audio snd_usbmidi_lib snd_rawmidi snd_hwdep sni_ave combined_init dsp_uniphier_ld20(O) dsp_uniphier(O) hantrodec(O) avtv_com hx170dec(O) mss acodec_uniphier(O) uniphier_spdif_tx(O) uniphier_thru_io(O) tsd avout uniphier_thru_in(O) uniphier_thru_out(O) tvadjust uniphier_evea(O) tvp snd_soc_uniphier_aio2013(O) power pincon snd_soc_bd28623(O) uniphier_ld20_mn884434_helene hdmirx uniphier_dvb ttuner sc1501a helene stream tsport demux audio video dwc3_uniphier dwc3 eeprom mali_kbase vbi vacancy vpedrv_ld20(PO) tuner_pwr vocdrv_ld20(O) svl psi msc led_drv iccard counter hsc_udl(O) vio_rm(O) cec exivdrv_ld20(PO) amixer cpcl_ld20(PO) aesdescramble fbmem stream_com fa_uniphier(O) descramble set ion_uniphier(O) piedrv_sc devglue(O) pow(O)
[  601.835394]  map_reg(O) [last unloaded: wlan_7961_usb]
[  601.840551] -(0)[630:kswapd0]Process kswapd0 (pid: 630, stack limit = 0x00000000269b2ad5)
[  601.848755] -(0)[630:kswapd0]CPU: 0 PID: 630 Comm: kswapd0 Tainted: P           O      4.19.176 #53
[  601.857820] -(0)[630:kswapd0]Hardware name: UniPhier LD20 Global Board v4 (REF_LD20_GP_V4) (DT)
[  601.866543] -(0)[630:kswapd0]pstate: 80400005 (Nzcv daif +PAN -UAO)
[  601.872838] -(0)[630:kswapd0]pc : obj_malloc.isra.8+0xb8/0x170
[  601.878683] -(0)[630:kswapd0]lr : obj_malloc.isra.8+0x88/0x170
[  601.884530] -(0)[630:kswapd0]sp : ffffff800b1236b0
[  601.889324] -(0)[630:kswapd0]x29: ffffff800b1236b0 x28: 0000000000001680 
[  601.896132] -(0)[630:kswapd0]x27: ffffffc06e5bfc00 x26: 0000000000000000 
[  601.902939] -(0)[630:kswapd0]x25: 0000000000000008 x24: ffffffc014da5bf1 
[  601.909745] -(0)[630:kswapd0]x23: ffffffc017640f60 x22: ffffffc06e5bfc80 
[  601.916551] -(0)[630:kswapd0]x21: 0000000000000000 x20: 0000000000000000 
[  601.923358] -(0)[630:kswapd0]x19: ffffffbf012c3480 x18: ffffffc072265ff6 
[  601.930165] -(0)[630:kswapd0]x17: 0000000000000000 x16: 0000000000000000 
[  601.936971] -(0)[630:kswapd0]x15: 00000000000000b0 x14: ffffffc072265f00 
[  601.943778] -(0)[630:kswapd0]x13: 0000000000000020 x12: 000000001824429d 
[  601.950584] -(0)[630:kswapd0]x11: ffffffc072265fec x10: 000000000000001c 
[  601.957391] -(0)[630:kswapd0]x9 : ffffffc01da7c69b x8 : ffffffc01da7c69b 
[  601.964197] -(0)[630:kswapd0]x7 : 000000000000001d x6 : 0000000000000003 
[  601.971003] -(0)[630:kswapd0]x5 : 00000000000006d0 x4 : 0000000000000000 
[  601.977810] -(0)[630:kswapd0]x3 : ffffffc014da5bf0 x2 : 0000000000002000 
[  601.984616] -(0)[630:kswapd0]x1 : 0000000000000000 x0 : ffffffc04b0d2000 
[  601.991424] -(0)[630:kswapd0]Call trace:
[  601.995351] -(0)[630:kswapd0] obj_malloc.isra.8+0xb8/0x170
[  602.000853] -(0)[630:kswapd0] zs_malloc+0x224/0x478
[  602.005745] -(0)[630:kswapd0] zram_bvec_rw.isra.22+0x354/0x740
[  602.011591] -(0)[630:kswapd0] zram_rw_page+0x8c/0x120
[  602.016651] -(0)[630:kswapd0] bdev_write_page+0xac/0xf8
[  602.021891] -(0)[630:kswapd0] __swap_writepage+0x198/0x3e0
[  602.027390] -(0)[630:kswapd0] swap_writepage+0x3c/0x60
[  602.032546] -(0)[630:kswapd0] shrink_page_list+0x954/0xf08
[  602.038045] -(0)[630:kswapd0] shrink_inactive_list+0x24c/0x630
[  602.043894] -(0)[630:kswapd0] shrink_node_memcg+0x36c/0x680
[  602.049482] -(0)[630:kswapd0] shrink_node+0xb8/0x430
[  602.054458] -(0)[630:kswapd0] balance_pgdat+0x1dc/0x420
[  602.059690] -(0)[630:kswapd0] kswapd+0x14c/0x4d8
[  602.064322] -(0)[630:kswapd0] kthread+0x120/0x160
[  602.069040] -(0)[630:kswapd0] ret_from_fork+0x10/0x1c
[  602.074109] -(0)[630:kswapd0]Code: d341fc42 b9000ae2 f9400262 37500402 (f8216818) 
[  602.081698] -(0)[630:kswapd0]---[ end trace d42a6dba99ad7dc0 ]---
[  602.087804] -(0)[630:kswapd0]Kernel panic - not syncing: Fatal exception
[  602.094528] -(0)[630:kswapd0]SMP: stopping secondary CPUs
[  602.099942] -(0)[630:kswapd0]Kernel Offset: 0x80000 from 0xffffff8008000000
[  602.106920] -(0)[630:kswapd0]CPU features: 0x00000000,2180600c
[  602.112767] -(0)[630:kswapd0]Memory Limit: none
[  602.117317] -(0)[630:kswapd0]Rebooting in 5 seconds..

2.3 mm,page_alloc,cma: conditionally prefer cma pageblocks for movable allocations

在看cma: redirect page allocation to CMA这个patch时,注意到这个修改的kernel版本和当前使用的4.19.176有点不一样,多了下面这个patch,mm,page_alloc,cma: conditionally prefer cma pageblocks for movable allocations

该patch的主要修改是,当 #free_cma > 1/2#total_free 时(CMA 区域空闲量占总内存空闲量的一半以上),Movable 可以申请到 CMA 区域分配。

image8d731a2a0e7f9839.png

imagecebe12952bbae687.png

现在做个简单总结,

  1. 在linux kernel 4.19.176上,当moveable类型内存分配失败时,将使用cma内存
  2. 在Android common kernel 4.19.176上,加入了patch:cma: redirect page allocation to CMA。允许分配匿名内存时直接使用CMA内存,提升CMA内存使用率。但该patch一直未合入到linux kernel分支。
  3. 可能在Linux kernel 5.8上,Linux kernel加入了patch:mm,page_alloc,cma: conditionally prefer cma pageblocks for movable allocations,当CMA的空闲内存占到总空闲内存的一半以上,申请movable内存将开始使用CMA内存

如上所述,考虑到我们的MemFree为240920KB时,CmaFree为206272KB,已经远超总空闲内存的一半了,使用率偏低,考虑将这个patch加入。

实测发现,在加入这个patch后(同时减少zram),系统中的cma内存是有明显的提高,

如下所示,在测试过程中,和之前的CmaFree相比,使用率得到了提高。
而且测试过程中,wifi service的page allocation failure问题在测试过程中只出现了两次,和之前相比明显减少。但是,在monkey测试了4个多小时后还是挂掉了,原因还是TLB导致的。

CmaTotal:         380928 kB
CmaFree:          139540 kB
CmaTotal:         380928 kB
CmaFree:          135712 kB
CmaTotal:         380928 kB
CmaFree:          137408 kB
CmaTotal:         380928 kB
CmaFree:          137016 kB
CmaTotal:         380928 kB
CmaFree:          137688 kB
CmaTotal:         380928 kB
CmaFree:          139380 kB
CmaTotal:         380928 kB

从挂掉前打印的pagetype info信息看,Movable内存快速减少,

Free pages count per migrate type at order       0      1      2      3      4      5      6      7      8      9     10 
Node    0, zone    DMA32, type    Unmovable   2874   1348   1446    565      1     37      1      1      0      0      0 
Node    0, zone    DMA32, type      Movable   5881    563     22      1      0      0      0      0      0      0      0 
Node    0, zone    DMA32, type  Reclaimable    115     46     58     16      0      0      0      0      0      0      0 
Node    0, zone    DMA32, type          CMA   1630    464    158     59     22     10      9      5      3      1     32 
Node    0, zone    DMA32, type   HighAtomic      0      0      0      0      0      0      0      0      0      0      0 
Node    0, zone    DMA32, type      Isolate      0      0      0      0      0      0      0      0      0      0      0 

Number of blocks type     Unmovable      Movable  Reclaimable          CMA   HighAtomic      Isolate 
Node 0, zone    DMA32          248          221           19          186            1            0 
Page block order: 9
Pages per block:  512

Free pages count per migrate type at order       0      1      2      3      4      5      6      7      8      9     10 
Node    0, zone    DMA32, type    Unmovable   2516   1438   1462    564    285     82      2      1      0      0      0 
Node    0, zone    DMA32, type      Movable   6264    632     25      1      0      0      0      0      0      0      0 
Node    0, zone    DMA32, type  Reclaimable    115     46     58     17      1      0      0      0      0      0      0 
Node    0, zone    DMA32, type          CMA   1020    540    185     68     23     10      9      5      3      1     32 
Node    0, zone    DMA32, type   HighAtomic      0      0      0      0      0      0      0      0      0      0      0 
Node    0, zone    DMA32, type      Isolate      0      0      0      0      0      0      0      0      0      0      0 

Number of blocks type     Unmovable      Movable  Reclaimable          CMA   HighAtomic      Isolate 
Node 0, zone    DMA32          248          221           19          186            1            0 
Page block order: 9
Pages per block:  512

Free pages count per migrate type at order       0      1      2      3      4      5      6      7      8      9     10 
Node    0, zone    DMA32, type    Unmovable   2715   1859   1508    594     42     90     14      1      4      0      0 
Node    0, zone    DMA32, type      Movable    686     27      2      0      0      0      0      0      0      0      0 
Node    0, zone    DMA32, type  Reclaimable     50     46     58     17      1      0      0      0      0      0      0 
Node    0, zone    DMA32, type          CMA    974     63      1      4     12     15     11      9      9      4     39 
Node    0, zone    DMA32, type   HighAtomic      0      0      0      0      0      0      0      0      0      0      0 
Node    0, zone    DMA32, type      Isolate      0      0      0      0      0      0      0      0      0      0      0 

Number of blocks type     Unmovable      Movable  Reclaimable          CMA   HighAtomic      Isolate 
Node 0, zone    DMA32          248          221           19          186            1            0 

该问题仍需继续研究,需要再测试下减少CMA和no-map内存是否真的必不复现此问题。

独特见解