DongHao: 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文档改的,字体较小,可能需要全屏;部分内容由于公司政策不予显示,此保密政策是“量子时代”要求的,大家勿直接喷我):


hadoop与午睡

| | Comments (2) | TrackBacks (0)
这一阵子在折腾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知道了