AMD kernel slub 512内存泄漏分析

一:问题现象

内存不足导致OOM,kill掉qemu等进程,导致虚拟机异常。

初步排查发现slab内存占用量非常大,达到20G

二:初步定位

通过观察对应的slab信息,发现异常情况。

 使用的allocated和total差别太大了。正常情况slub是按需分配的,用多少申请多少。

正常的结构,基本在同一数量级。

 所以 kmalloc-512的SLUB结构有异常。

三:数据结构分析

struct kmem_cache ffff89804c807600

{

cpu_slab = 0x2dba0,

flags = 1073741824,

min_partial = 5,

size = 512,

object_size = 512,

random = 2979236655726901199,

remote_node_defrag_ratio = 1000,

random_seq = 0xffff89804c80c800,

useroffset = 0,

usersize = 512,

node = {0xffff89804c800ec0, 0xffff8a7c4d000ec0,

kmem_cache由两个kmem_cache_node组成,两个node里面包含分配的所有内存和对象数据。

struct kmem_cache_node0 {

[ffff89804c800ec0] spinlock_t list_lock;

[ffff89804c800ec8] unsigned long nr_partial; 总数:624398

[ffff89804c800ed0] struct list_head partial;

[ffff89804c800ee0] atomic_long_t nr_slabs;

[ffff89804c800ee8] atomic_long_t total_objects;

[ffff89804c800ef0] struct list_head full;

}

struct kmem_cache_node1 {

[ffff8a7c4d000ec0] spinlock_t list_lock;

[ffff8a7c4d000ec8] unsigned long nr_partial;总数:4798

[ffff8a7c4d000ed0] struct list_head partial;

[ffff8a7c4d000ee0] atomic_long_t nr_slabs;

[ffff8a7c4d000ee8] atomic_long_t total_objects;

[ffff8a7c4d000ef0] struct list_head full;

}

其中partial表示slab中部分使用,但未用完的内存,下一次slab申请将会使用这种内存。一个slab用完才会申请新的slab(其中会有算法维持平衡)。其中nr_partial值为624398,slabsize是32K,消耗总内存=(624398 + 4798)*32k =20134272k。 接近20G,其中node0在消耗绝大部分内存。

Node0的partial数为624398,数量异常,需要看看每个slab的使用情况。

比较神奇的事情,大部分的slab的使用都是1个object, 每个slab包含64个可用的object。

对inuse的数进行部分统计(全量太大,速度太慢),inuse=1的在18709里面占了17616,94%。

 所以大部分的slab都只用了一个object,意思是32K中只用了512b。

下午脚本对slub的扫描symbol:大部分值为0, 是因为slab的使用率很低,很多都没用。

目前的结论是:kmalloc-512的slab使用率非常低,就重新申请了新的slab。

和kmem_256d 对比,没有发现异常的项。

inuse=1占很大比重,还需继续定位。

四:slub分析

1:从slub NODE的角度分析:

选取一个只使用一个object的slub进行分析。

crash> kmem ffffea00130ef000

CACHE OBJSIZE ALLOCATED TOTAL SLABS SSIZE NAME

ffff89804c807600 512 980236 40472480 634465 32k kmalloc-512

SLAB MEMORY NODE TOTAL ALLOCATED FREE

ffffea00130ef000 ffff8884c3bc0000 0 64 1 63

通过slab的计算方式反推每一个object的对应关系(因为有加密的算法,手动计算耗时较多)。

反推出了第一个第二个object,以及最后一个object。发现并没有可用信息。看起来slub的数据完全正常,不清楚为什么使用时没有继续使用下一个object,而是申请新的slub。

ffff8884c3bc0000:ffff8884c3bc7800 
ffff8884c3bc0200:ffff8884c3bc7000 
ffff8884c3bc0400:ffff8884c3bc3e00 
ffff8884c3bc0600:ffff8884c3bc7200 第二个object 
...........ffff8884c3bc4c00:2914e337a2e44830 最后一个object ffff8884c3bc6a00:ffff8884c3bc7e00 
ffff8884c3bc6c00:0 第一个object 
.........

2:从slub_cache_cpu方面分析

对slub_cache_cpu的page进行排查,工作都是正常的。

3:SLUB进行分析

对后面的slub进行分析,发现SLUB的结构基本都一样,暂用的内存如下面所示,内容相似。

地址分析:

读内存进行分析,找到对应的结构体为 sched_entity

ffff888171b8fc00: d65b6f04d65ca030 0000000000000000 0.\..o[.........

ffff888171b8fc10: 0000000000100000 0000000000000000 ................

ffff888171b8fd50: 0000000000000000 ffff89804cf6a6c0 ...........L....

ffff888171b8fd60: ffff888171b8e800 0000000000000000 ...q............

ffff888171b8fd70: 0000000000000000 0000000000000000 ................

ffff888171b8fd80: 0006b35240e99c00 0000000000000000 ...@R...........

ffff888171b8fd90: 0000000000000000 0000026700000000 ............g...

另一个结构体为:cfs_rq

ffff888171b8e800: d64f6f04d65ce630 0000000000000000 0.\..oO.........

ffff888171b8e810: 0000000000000000 0000000000000000 ................

ffff888171b8e820: 0000000000000000 0000000000000000 ................

ffff888171b8e830: fffffffffff00000 0000000000000000 ................

ffff888171b8e930: ffff89804cf6a640 0000000000000000 @..L............

ffff888171b8e940: 0000000000000000 0000000000000000 ................

ffff888171b8e950: ffff897816a00500 0000000000000000 ....x...........

ffff888171b8e960: 0000000000000000 0000000000000000 ................

ffff888171b8e970: 0006b3523f10f337 0000000000000000 7..?R...........

ffff888171b8e980: 0000000000000000 ffff888171b8e988 ...........q....

ffff888171b8e990: ffff888171b8e988 0000000000000000 ...q............

所以近20G的slab内存被如上所示的内存占用。sched_entity的my_q指向了cfs_rq,所以它们在内存中生对出现的,不能被释放的原因是其中有slub为空又没释放的情况。

占用slub的内存如下:

全部为0,又没有使用。也没有释放

ffff8884c3bc6c00: 0000000000000000 0000000000000000 ................ 
ffff8884c3bc6c10: 0000000000000000 0000000000000000 ................ 
ffff8884c3bc6c20: 0000000000000000 0000000000000000 ................ 
ffff8884c3bc6c30: 0000000000000000 0000000000000000 ................ 
................. 
................. 
................. ffff8884c3bc6dc0: 0000000000000000 0000000000000000 ................ 
ffff8884c3bc6dd0: 0000000000000000 0000000000000000 ................ 
ffff8884c3bc6de0: 0000000000000000 0000000000000000 ................ 
ffff8884c3bc6df0: 0000000000000000 0000000000000000 SLAB MEMORY NODE TOTAL ALLOCATED FREE 
ffffea00130ef000 ffff8884c3bc0000 0 64 1  63 

对比正常的kmalloc-512,涉及sched_entity和cfs_rq的内容也是相似的。

目前的疑问是为什么会产生大量的sched_entity?

更有疑问的是空slub被谁申请没有释放,才pin住了内存。无法释放内存。

4:代码分析

Slub malloc-512中内存基本被sched_entity和cfs_rq占用,对应的代码如下:

两个结构体成对申请,其中CPU数量为256,一次调用使用内存为256*512*2=256KB,

申请时有置0的操作,所以内存没使用时全0,使用后如果没有被重新使用,会保留原有信息。

        全零的slub可能是这个alloc_fair_sched_group在处理异常分配时有泄露的可能,或者是其他模块的slub泄露导致内存被占用,无法释放。

还在继续查看。

int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent)

{

struct sched_entity *se;

struct cfs_rq *cfs_rq;

int i;

tg->cfs_rq = kcalloc(nr_cpu_ids, sizeof(cfs_rq), GFP_KERNEL);

if (!tg->cfs_rq)

goto err;

tg->se = kcalloc(nr_cpu_ids, sizeof(se), GFP_KERNEL);

if (!tg->se)

goto err;

tg->shares = NICE_0_LOAD;

init_cfs_bandwidth(tg_cfs_bandwidth(tg));

for_each_possible_cpu(i) {

cfs_rq = kzalloc_node(sizeof(struct cfs_rq),

GFP_KERNEL, cpu_to_node(i));

if (!cfs_rq)

goto err;

se = kzalloc_node(sizeof(struct sched_entity),

GFP_KERNEL, cpu_to_node(i));

if (!se)

goto err_free_rq;

init_cfs_rq(cfs_rq);

init_tg_cfs_entry(tg, cfs_rq, se, i, parent->se[i]);

init_entity_runnable_average(se);

}

return 1;

err_free_rq:

kfree(cfs_rq);

err:

return 0;

五:终章

经历的一周的挣扎还是找到了原因。

1:调用的kmalloc-512最多的是?

调用最多的进程是real_run。

2:对大量的调用栈进行分析

虽然real_run进程只有一个,但是调用slub的堆栈非常多。对大量栈进行分析,发现了比较异常的memcg_alloc_page_obj_cgroups,

因为这个函数会调用kmalloc-512非常奇怪。

对函数memcg_alloc_page_obj_cgroups的源码进行分析。

int memcg_alloc_page_obj_cgroups(struct page *page, struct kmem_cache *s,gfp_t gfp)
{
     unsigned int objects = objs_per_slab_page(s, page);
     void *vec;
    vec = kcalloc_node(objects, sizeof(struct obj_cgroup *), gfp,page_to_nid(page));
    //这里sizeof(struct obj_cgroup *)是指针大小 8byte,objects=64 =512byte
    //所以会指向kmalloc-512
    if (cmpxchg(&page->obj_cgroups, NULL,
        (struct obj_cgroup **) ((unsigned long)vec | 0x1UL)))
        kfree(vec);
    else
        kmemleak_not_leak(vec);

    return 0;
}
这里就矛盾点就很直观了,slub page中的obj_cgroup指向的内容也是在这一类的slub中。
灵机一动,之前的空的object,应该就是指向了obj_cgroup。所以去内存里查一下那些结构看看。

我们查看kmem_cache_node的最后一个slub:ffffea00f0cc5800 来查看。它的obj_cgroups是

0xffff88bc33167200就在它自己的object列表中。

由此可见:SLUB新分配时自己就占用了一个object,所以一直都无法释放。

由于intel的机型中kmalloc-512的slub是16KB,所以obj_cgroup的分配不会冲突。所以目前只会在AMD机器上发现。

3:解决办法

memcg_alloc_page_obj_cgroups中分配数组vec的slub(vec = kcalloc_node),不能是它自己。这个bug应该一直都有,只是极少触发,满足触发条件,同时大量申请slub才会比较明显的发现。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值