rhel6 的软RAID问题

先是彭敏同学报线上服务器出现了kernel panic,我上去一看panic的地方在
md_seq_show()--> mddev_unlock()
里的 md_wakeup_thread(mddev->thread),说是mddev->thread为NULL。用的是基于2.6.32-220的ali_kernel。查了一下,这个问题倒是在2.6.32-279已经fix了,upstream的补丁是"md: Avoid waking up a thread after it has been freed"

于是我让彭敏同学升级内核到2.6.32-279,但是更悲剧的是,279更不靠谱,创建raid10后刚开始mkfs.ext4就panic了....panic的地方更诡异,在drivers/scsi/scsi_lib.c的一个BUG_ON里:
panic

        /*
         * Filesystem requests must transfer data.
         */
        BUG_ON(!req->nr_phys_segments);

        cmd = scsi_get_cmd_from_req(sdev, req);
        if (unlikely(!cmd))
                return BLKPREP_DEFER;

        memset(cmd->cmnd, 0, BLK_MAX_CDB);
        return scsi_init_io(cmd, GFP_ATOMIC);
}

只好顺着代码一点点调试,才发现是raid10在处理io的函数make_request()里错把upstream的补丁直接backport过来,upstream里已经没有BIO_FLUSH和BIO_FUA只有REQ_FLUSH和REQ_FUA了,但是backport的人显然不知道,就直接用REQ_FLUSH来代替BIO_FLUSH,但在2.6.32内核里,这压根是两个不同的值。于是,make_request()在clone request时把request的FLUSH标志给漏掉了,到了scsi层:

static int sd_prep_fn(struct request_queue *q, struct request *rq)
{
        ....

        /*
         * Discard request come in as REQ_TYPE_FS but we turn them into
         * block PC requests to make life easier.
         */
        if (rq->cmd_flags & REQ_DISCARD) {
                ret = sd_setup_discard_cmnd(sdp, rq);
                goto out;
        } else if (rq->cmd_flags & REQ_WRITE_SAME) {
                ret = sd_setup_write_same_cmnd(sdp, rq);
                goto out;
        } else if (rq->cmd_flags & REQ_FLUSH) {
                ret = scsi_setup_flush_cmnd(sdp, rq);
                goto out;
        } else if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
                ret = scsi_setup_blk_pc_cmnd(sdp, rq);
                goto out;
        } else if (rq->cmd_type != REQ_TYPE_FS) {
                ret = BLKPREP_KILL;
                goto out;
        }
        ret = scsi_setup_fs_cmnd(sdp, rq);

对REQ_FLUSH的判断失效,进不了scsi_setup_flush_cmd而直接走了scsi_setup_fs_cmnd,结果悲剧了。看来升279不是办法,还是把"md: Avoid waking up a thread after it has been freed"的补丁打到220上吧。
感谢彭敏同学对软RAID功能的支持。

把这个279的bug告诉了涛哥
涛哥:upstream把BIO_FLUSH和REQ_FLUSH合并为一个不是Tejun Heo搞的鬼吗?他不是去红帽了吗?他搞了这摊事怎么不backport到rhel6的279上去?
我:....大概他去红帽就只做upstream不用干backport的苦活吧

系统装在软raid上

前一阵遇到一个系统装到软raid上后设备名对不上的问题。正常用kickstart安装rhel或centos时,可以创建软raid设备,例如/dev/md1,然后把系统装到md1里,装好重启后可以看到根目录就在/dev/md1设备上。但是,同事用无盘环境装的,重启后设备名字变成了md127,系统是自动把软raid设备找到了,但是给它的名字不是安装时的名字。试着在/etc/mdadm.conf里把/dev/md1加上,重启还是无效。
后来才想明白,mdadm.conf配置文件自己就放在软raid上,在识别软raid以前,上哪儿去找这个文件啊。不是修改系统的,而是修改dracut里的mdadm.conf,这个修改也不麻烦,改好/etc/mdadm.conf后重新mkinitrd一下,新的mdadm.conf就带到新initrd里去了。这次重启,设备名正确了,是/dev/md1。

那md127是咋来的?
再翻了代码,原来,dracut里的sbin/mdadm_auto脚本里有一句:

/sbin/mdadm -As --auto=yes --run 2>&1 | vinfo

如果找不到etc/mdadm.conf,就会用mdadm工具来自动寻找并组装旧有的软raid设备,“-A”表示Assemble,看看mdadm的源码:

(Assemble.c)
create_mddev()
    ----> find_free_devnm()

char *find_free_devnm(int use_partitions)                         
{                       
        static char devnm[32]; 
        int devnum;                                                
        for (devnum = 127; devnum != 128;                
             devnum = devnum ? devnum-1 : (1<<20)-1) {
             ......

就是从127往下递减来作为设备名称的。

感谢君瑞同学提的问题,我也学到了很多。