免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 10995 | 回复: 9

[网络子系统] 一个Linux Kernel Memory Leak的定位过程 [复制链接]

论坛徽章:
3
射手座
日期:2014-08-18 12:15:53戌狗
日期:2014-08-22 09:53:36寅虎
日期:2014-08-22 14:15:29
发表于 2014-04-29 18:13 |显示全部楼层
本帖最后由 gaojl0728 于 2014-04-29 21:06 编辑

一个Linux Kernel Memory Leak的定位过程。

最近客户那里报了个的OOM问题, 一开始以为是HTTP并发连接数太多,导致TCP在协议栈这一层分配的内存太多,
在这种情况下基本没有很好地解决方案,无非就是调整下TCP的一些参数如tcp_window_scaling, tcp_rmem, tcp_wmem等让单TCP连接占用的低端内存少一些, 这样服务器可以承受更多连接。

后来远程登录到客户的机器研究了下,发现OOM的时候连接数并不多,才300多TCP连接, 所以排除了并发连接数太多的问题,在分析OOM log的时候发现有时OOM发生kernel干掉的进程才释放了200多K的物理内存,
说明当时系统的内存已经极其紧缺,连200K物理内存都不放过,但是干掉进程后系统并没有释放多少内存,紧接着又发生了多次OOM, 于是怀疑耗尽内存的源头不在应用层,而在内核。

用slabtop发现了OOM的根源, 我们的服务器物理内存一共才9G, 内核的ip_dst_cache占去了7G多, 加上其他一些关键业务进程占用的内存, 导致系统内存耗尽。
而且ip_dst_cache的内存一直在增长。
查了下内核源码, ip_dst_cache的内存主要分配给了内核路由表rt_table, 这个问题应该跟内核协议栈相关。

后来公司的TS提供了一些关键的线索, 他们做了一些测试发现在干掉某个进程的时候, ip_dst_cache停止增长,这个进程主要业务就是每隔一秒钟向网络里的其他服务器发送4个多播UDP包用于同步系统状态信息。
现在范围已经很小了, 肯定跟内核的多播IP报文的处理有关系。

于是痛苦的读kernel代码开始了, 分析了从ip_rcv_finish内核收到IP报文开始, 在IP协议栈报文向上传递到ip_route_input_noref->ip_route_input_mc(就是在这里内核从ip_dst_cache分配了rt_table)->ip_local_deliver, 然后一直到UDP多播报文的处理udp_rcv->__udp4_lib_rcv->__udp4_lib_mcast_deliver, 一切流程貌似正常,所有IP/UDP报文分配的rt_table在应用层通过recv系统调用跑到udp_recvmsg的时候会正常释放。


到这里一切都卡住了没有进展,后来分析了一下从客户那里抓的pcap,  发现了一点线索, 就是在这4个多播报文当中,有一个多播报文因为太大(大于1500), 被分成了两个分片。
难道是因为IP分片导致了内存泄露?

读了下内核IP分片重组的那块代码,终于找到了问题所在:)
流程是这样的,ip_local_deliver收到新的IP报文的时候会检查是不是分片, 如果是分片,会调用ip_defrag-->ip_frag_queue->ip_frag_reasm重组分片,
问题就是出在重组分片的时候,上代码:
static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
                         struct net_device *dev)
{
        for (fp = head->next; fp {

                if (skb_try_coalesce(head, fp, &headstolen, &delta)) {
                        kfree_skb_partial(fp, headstolen);
                } else {
                        if (!skb_shinfo(head)->frag_list)
                                skb_shinfo(head)->frag_list = fp;
                        head->data_len += fp->len;
                        head->len += fp->len;
                        head->truesize += fp->truesize;
                }
                fp = next;
        }
}

在分片重组的时候, 内核合并所有分片到一个skb_buff, 这样之前分片的skb就没用了,内核会调用kfree_skb_partial释放分片,
内存泄露就发生在kfree_skb_partial
这是出问题的版本,内核只是放了skb本身, 并没有有释放skb引用的dst_entry/rt_table
void kfree_skb_partial(struct sk_buff *skb, bool head_stolen)
{
        if (head_stolen)
                kmem_cache_free(skbuff_head_cache, skb);
        else
                __kfree_skb(skb);
}


对比了下3.12.6的内核代码,这个问题已经修正了, 就是这行skb_release_head_state(skb):
void kfree_skb_partial(struct sk_buff *skb, bool head_stolen)
{
        if (head_stolen) {
                skb_release_head_state(skb);
                kmem_cache_free(skbuff_head_cache, skb);
        } else {
                __kfree_skb(skb);
        }
}

改完之后重新上线测试, 一切OK, ip_dst_cache一直维持在200K以内不怎么增长。
终于可以松一口气了

需要说明的是我们服务器的内核版本是3.6.3

评分

参与人数 2可用积分 +6 收起 理由
瀚海书香 + 4 赞一个!
embeddedlwp + 2 赞一个!

查看全部评分

论坛徽章:
0
发表于 2014-04-29 18:20 |显示全部楼层
本帖最后由 l4rmbr 于 2014-04-29 18:24 编辑

回复 1# gaojl0728

赞!!!

楼主能不能反馈给上游一下,顺便可以提出你的解决方法。
看新版本的内核是否已经解决这个问题了。没有的话,这对社区也是一个有用的报告。

对了,不知你题中的生产内核是否有自已用的patch或发行版的patch呢?如果有,最好能确保不是外加的patch引起的。
   

论坛徽章:
3
射手座
日期:2014-08-18 12:15:53戌狗
日期:2014-08-22 09:53:36寅虎
日期:2014-08-22 14:15:29
发表于 2014-04-29 21:00 |显示全部楼层
回复 2# l4rmbr

这个问题已经在最新的3.12.6修正了, 就是加上skb_release_head_state(skb)这行释放rt_table.
void kfree_skb_partial(struct sk_buff *skb, bool head_stolen)
{
        if (head_stolen) {
                skb_release_head_state(skb);
                kmem_cache_free(skbuff_head_cache, skb);
        } else {
                __kfree_skb(skb);
        }
}


我们公司由于种种原因选择了3.6.3版本的内核。其实我查了下kernel.org并不维护这个版本,所以用这个版本是有风险的。


这个bug比较隐蔽是因为只有多播IP报文才会触发这个bug, 对于其他的IP报文,如UDP或者TCP报文等等, 内核会首先通过fib_lookup查找是否有缓存的rt_table, 如果找不到才会从ip_dst_cache新分配rt_table并且放到fib缓存中, 所以即使报文分了片也没关系,只有第一个报文会分配rt_table, 后续的分片都会通过fib_lookup查找到缓存的rt_table。

内核处理多播IP报文十分奇怪, 只要来了包,不管是不是分片的,都会马上从ip_dst_cache分配新的rt_table, 对于分了片的报文就会有我前面说的内存泄露。
那帮内核开发者的想法不是我等所能理解的。

论坛徽章:
0
发表于 2014-04-30 10:12 |显示全部楼层
好文,支持

论坛徽章:
33
荣誉会员
日期:2011-11-23 16:44:17天秤座
日期:2014-08-26 16:18:20天秤座
日期:2014-08-29 10:12:18丑牛
日期:2014-08-29 16:06:45丑牛
日期:2014-09-03 10:28:58射手座
日期:2014-09-03 16:01:17寅虎
日期:2014-09-11 14:24:21天蝎座
日期:2014-09-17 08:33:55IT运维版块每日发帖之星
日期:2016-04-17 06:23:27操作系统版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-24 06:20:0015-16赛季CBA联赛之天津
日期:2016-05-06 12:46:59
发表于 2014-08-15 18:47 |显示全部楼层
multicast 一般不是限制在 512 以内么. 怎么会有 1500 这么大的报文?

论坛徽章:
0
发表于 2014-08-26 07:50 来自手机 |显示全部楼层
楼主厉害!学习了!

论坛徽章:
15
射手座
日期:2014-02-26 13:45:082015年迎新春徽章
日期:2015-03-04 09:54:452015年辞旧岁徽章
日期:2015-03-03 16:54:15羊年新春福章
日期:2015-02-26 08:47:552015年亚洲杯之卡塔尔
日期:2015-02-03 08:33:45射手座
日期:2014-12-31 08:36:51水瓶座
日期:2014-06-04 08:33:52天蝎座
日期:2014-05-14 14:30:41天秤座
日期:2014-04-21 08:37:08处女座
日期:2014-04-18 16:57:05戌狗
日期:2014-04-04 12:21:33技术图书徽章
日期:2014-03-25 09:00:29
发表于 2014-08-26 09:55 |显示全部楼层
支持,这种问题通常很难定位的。。。。

论坛徽章:
0
发表于 2014-09-07 14:14 |显示全部楼层
收藏了,给公司工程师看看

招聘 : c/c++研发
论坛徽章:
0
发表于 2014-09-17 16:22 |显示全部楼层
感谢分享
gaojl0728 发表于 2014-04-29 18:13
一个Linux Kernel Memory Leak的定位过程。

最近客户那里报了个的OOM问题, 一开始以为是HTTP并发连接数 ...

论坛徽章:
0
发表于 2014-09-23 16:59 |显示全部楼层
这个不错,学习了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP