06 2012存档
某些开发测试团队会有这样的需求:多个开发或测试人员在一台物理机上搭环境、装rpm包、测试等,目录很可能互相干扰。避免干扰的最直接办法当然是装虚拟机,但是如果仅仅只是想把根目录隔离一下,而不需要隔离CPU和内存,那么虚拟机方案偏重了些。
所以,我们做了一个叫troot的小工具,每个人都可以自己创建一个虚根目录,然后进入这个虚根目录工作,工作起来感觉就像一个全新的跟别人互不干扰的新根目录一样。
不过这个troot的主要实现是靠overlayfs,也就是说,必须用淘宝linux-kernel,淘宝linux-kernel源代码的git库在 http://kernel.taobao.org/git/?p=taobao-kernel.git;a=summary
而troot本身只是个小shell脚本而已,代码在这里:
制作troot的rpm方法是:
svn co http://code.taobao.org/svn/troot/trunk troot
cd troot
sh rpm/troot-build.sh `pwd`
装上淘宝linux-kernel和troot以后就可以用了,用法:
troot list #显示本机已经创建的根目录,第二栏显示当前处于哪个根目录
troot create world1 #创建名为"world1“的根目录
troot enter world1 #进入名为"world1"的根目录,进入以后,就可以随意写文件了
#直接敲入exit就可以从根目录中退出
欢迎感兴趣的同学试用反馈
经过几个月的线上测试,ext4 bigalloc的测试报告终于出炉,我发复取了n多次的数据(这些去数据的过程最后都被我磨成bash脚本了),感谢刘亮同学和桑彦斌同学对CDN上线的支持,感谢柯旻同学和刘毅同学对hadoop线上的支持,还要感谢coly一遍又一遍的reivew报告和逼着我一遍又一遍的采集线上数据并作图(此公开版拿掉了图)。
测试报告(由于是从word文档改的,字体较小,可能需要全屏;部分内容由于公司政策不予显示,此保密政策是“量子时代”要求的,大家勿直接喷我):
Ext4 Bigalloc report public
View more documents from Robin Dong.
这一阵子在折腾hadoop集群上linux kernel的优化,开始没机器,只好悲惨的用虚拟机来装,但是kvm虚拟机又不给力,虚拟机之间死活连不上,最后终于等到柯旻同学婚假归来,支援了我几台线下测试机,刘毅同学帮我搭了环境,终于可以见到活生生的hadoop运行状态。
接下来是两周的运行观察和调试。
目前为止最大的进展是揪出一个2.6.32(upstream也有)的一个pagevec引发的性能退化,见patch。
然后,考虑到hadoop里,源数据一般只读一次,目标结果也只写一次,cache没什么用,所以我们很想试试新版带有cache抛弃功能(所谓的drop-behind)的hadoop,我看hadoop-0.23.1有此功能,于是想在测试集群里跑起来。
没想到hadoop-0.23.1比0.19变了那么多,配置文件的名字变了、配置项变了、连resource manager也变了,我在网上翻了不少文章,试了好多配置方法,hadoop的log报了不少WARN(无非都是某些配置项名字变了),好像这些WARN也不影响运行,但最后却只能在单机上跑任务....诸多毛病,我折腾了两个工作日外加一个周末,还是没能把最基本的terasort在多机上run起来....这五个白天看了不知道多少hadoop那无病呻吟却又提供不了多少帮助的log。
昨天(星期天)睡午觉,梦见早上起床要去上班,提起裤子准备穿,突然发现裤子上有妈妈绣的字:”WARN:此裤子颜色已经发旧,不要再穿“,我心想,不就是”WARN“嘛,又不是”ERROR“或者”FATAL“,于是我还是把裤子穿上了。等到醒来,不禁失笑。
今天早上,找到了吴威同学,还是专家有办法:别再搭郁闷的hadoop-0.23.1了,搭cdh3u4!吴威同学三下五除二把cdh搭好,我终于能跑上terasort测试了。
前年我去考过一次雅思,口语的考官是个面无表情的瘦高个儿,老外,非常非常的面无表情,我回答了一堆东西,他每次都只问一个字:“Why?“...“Why?”...“Why?“,我当时真想抓起桌上的电子钟砸他脑袋上去——你他妈哪来那么多的Why! 无奈考雅思就是求老外,我当然不敢动手。至今想起来,依然忿忿不平,唉,只要国内的食品略安全一点,我也用不着一把年纪了还背单词学英语(我读书时背得够累的了)、还得看这老外考官的脸色。
今天在组内群里发了娃的照片
涛哥: 你娃这么小就考雅思呵
我: 这你都看出来了
涛哥: 10天突破系列啊
我: 唉,10天是突破不了的,尤其是像我这样7年都在民企干,一点读、写、说、听英语的机
会都没有的人
文卿: 请参加ext4 weekly conference call
我: 雅思口语是不会考文件系统的
文卿: 但是会考你跟人扯淡
我: 雅思口语一般是问 “你最近最爽的一次散步是在哪里,跟谁散的,你们聊了什么,为什么
你觉得很爽” 一类的问题,然后接下来考官就一脸的木然,不停的问“why?..why?..why?”
文卿: 我最爽的一次是在google compus跟Ted散的,聊了ext4,因为我和他握了爪,所以爽 :)
我: 如果我是雅思考官,我就会继续问:“Why you feel happy when catching Ted's claw?"
文卿: because he is a well-known man in ext4 community.
我: why you want to catch a ext4 fellow's claw?
文卿: His claw can make me happy.
我: Why his claw could make you happy?
文卿: that was a long time ago. When I was a frehman in my university, I firstly hear his name and blabla ....
我: ....看来你能通过口语考试,我反正是不行了
为了监控上线的新内核,我们把google的netoops backport到了自己的内核,生产上如有kernel panic,会将panic的栈信息发送到日志服务器,方便调试和修复。
前天,洪川同学报告说以前线上的netoops都是把bond的slave网口作为发消息的dev,新上线2.6.32-220内核后,启动netoops失败,系统报:
”eth0 is a slave device, aborting."
找了一下从 2.6.32-131 到 2.6.32-220 的redhat的变动,发现了王聪同学的这个patch:
commit 0c1ad04aecb975f2a2014e1bc5a2fa23923ecbd9
Author: WANG Cong <amwang@redhat.com>
Date: Thu Jun 9 00:28:13 2011 -0700
netpoll: prevent netpoll setup on slave devices
In commit 8d8fc29d02a33e4bd5f4fa47823c1fd386346093
(netpoll: disable netpoll when enslave a device), we automatically
disable netpoll when the underlying device is being enslaved,
we also need to prevent people from setuping netpoll on
devices that are already enslaved.
Signed-off-by: WANG Cong <amwang@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 2d7d6d4..42ea4b0 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -792,6 +792,12 @@ int netpoll_setup(struct netpoll *np)
return -ENODEV;
}
+ if (ndev->master) {
+ printk(KERN_ERR "%s: %s is a slave device, aborting.\n",
+ np->name, np->dev_name);
+ return -EBUSY;
+ }
+
if (!netif_running(ndev)) {
unsigned long atmost, atleast;
从此,netpoll就无法使用slave设备了(netoops用的就是netpoll),不过我奇怪为什么以前可以现在又不行了,所以发邮件问了王聪同学为何现在不能使用slave设备,回答是:
“因为slave设备没有IP地址,http://wangcong.org/blog/archives/1657”
而且王同学在redhat搞netconsole也遇到了同样的问题,只能改用master网口。
我们的netoops也只能遵循同样的规则,统一改用 bond0做dev。
上个月,生产服务器上报来了内核bug:
------------[ cut here ]------------
kernel BUG at mm/mmap.c:2352!
invalid opcode: 0000 [#1] SMP
last sysfs file: /sys/devices/system/cpu/cpu23/cache/index2/shared_cpu_map
CPU 13
....
[<ffffffff8105fb55>] mmput+0x65/0x100
[<ffffffff81066605>] exit_mm+0x105/0x140
[<ffffffff810667ed>] do_exit+0x1ad/0x840
[<ffffffff81078680>] ? __sigqueue_free+0x40/0x50
[<ffffffff81066ec1>] do_group_exit+0x41/0xb0
[<ffffffff8107ccf8>] get_signal_to_deliver+0x1e8/0x430
[<ffffffff8100a554>] do_notify_resume+0xf4/0x8a0
[<ffffffff811f82f6>] ? security_task_kill+0x16/0x20
[<ffffffff8107a992>] ? recalc_sigpending+0x32/0x80
[<ffffffff8107ad15>] ? sigprocmask+0x75/0xf0
[<ffffffff8107ae11>] ? sys_rt_sigprocmask+0x81/0x100
[<ffffffff8107ad15>] ? sigprocmask+0x75/0xf0
[<ffffffff8100b301>] int_signal+0x12/0x17
原因就是 exit_mmap 函数最后一行的BUG_ON被触发了(我们用的是 2.6.32 内核)
void exit_mmap(struct mm_struct *mm)
{
....
BUG_ON(mm->nr_ptes > (FIRST_USER_ADDRESS+PMD_SIZE-1)>>PMD_SHIFT);
}
[ 这个 (FIRST_USER_ADDRESS+PMD_SIZE-1)>>PMD_SHIFT 其实就是0 ]
从代码直观能想到的就是进程在退出的时候pte没有释放对,或者nr_ptes计数漏了,导致最后一步nr_ptes没有变成0。
想归想,这个非常难重现。但是很快,好运来了,同事在开发cgroup的过程中无意中也触发了了这个BUG_ON,触发的方法是在 __mem_cgroup_uncharge_common 函数里加了一对 down_read/up_read (除了这一对,没有任何别的操作)。于是我开始想象:这一对 down_read/up_read 没有做任何与page或者pte相关的事情,却引起 nr_ptes计算出错,那八成是这一对锁的添加触发了原来的某个race condition,最终导致 nr_ptes 没有计算对。
于是,我沿着这条思路,顺着 exit_mmap 一路 trace_printk ,终于发现是在 page_remove_rmap() 前后nr_ptes开始不对,调用路线还挺深(花了不少力气才找到):
--> exit_mmap
--> unmap_vmas
--> unmap_page_range
--> zap_pud_range
--> zap_pmd_range
--> zap_pte_range
--> page_remove_rmap
为什么是 page_remove_rmap ? 又花了两天才发现,原来exit_mmap里用到了 tlb_gather_mmu/tlb_finish_mmu,就是把要清空页表项的page暂存在一个 struct mmu_gather 里,然后在tlb_finish_mmu时统一清空页表,以提高性能。2.6.32内核里的这个 struct mmu_gather 是per cpu变量(每个cpu一个),这就意味着在 tlb_gahter_mmu 和 tlb_finish_mmu 之间进程不能切换CPU,否则拿到的 struct mmu_gather就可能是另一个CPU上的变量,那就彻底不对了(upstream为了支持抢占,已经将mmu_gather改为 stack argument)。糟糕之处就在于,page_remove_rmap调用了mem_cgroup_uncharge_page进而调用了__mem_cgroup_uncharge_common,而我们在里面加了一个down_read,而down_read里有一句might_sleep(),结果,进程睡眠了,等他醒过来,可能已经身处另一个CPU,于是mmu_gather拿错了,于是nr_ptes不对了....(其实我挺好奇:为什么down_read里要加一句might_sleep?求高手解答)
花了三天辛辛苦苦的找,最后发现不是race condition,就是自己加的down_read造成的问题。
但是毕竟,生产报来的bug是没有这一更改的,那就是另有原因,还得查。
上周刘峥同学给了个链接 https://lkml.org/lkml/2012/2/15/322 ,看来redhat也遇到不少exit_mmap BUG_ON(),不过他们发现是Transparent Huge Page造成的,咱们报bug的生产服务器上并没有用到THP,还不是一个问题。不过,我总体感觉,进程退出的代码路径里,这个 BUG_ON(mm->nr_ptes > 0) 是最后一关,结果成了bug触发的汇集处,很多别处的甚至不是mm相关的代码错误都可能触发这个BUG_ON,比如这个 https://lkml.org/lkml/2012/5/25/553
后来,生产服务器从 2.6.32-131 升到 2.6.32-220 后,再没报过这个BUG_ON,至于终极的原因....可能是mm部分的bug fix,也可能是某个驱动的升级,这个,只有redhat知道了