这些文章已经写了好几年了,可能已经过时了。在MSN space和QQzone几经辗转之后,我想也许这些技术文章还是放在搞技术的博客中更能帮助人。于是做了一个艰难的决定,把这些文章一篇篇搬过来!绝对是原创的。
说到调度,一般都是说进程或者线程调度,这里也不例外,因为resync和recovery都是由一个后台的MD工作线程在执行的,我们且称之为syncd。不再象2.4那样在RAID启动时为其注册一个syncd,如果没有工作就一直休眠,有resync的需要时就起来工作,2.6内核是在有resync需要时才注册syncd,在resync完毕就将其注销;2.6也不象2.4那样将resync线程与recovery线程分开,而是在需要时注册一个线程需要做resync就做resync,需要recovery就做recovery。我们暂且将如何注册MD工作线程放到一边,集中精力看看何时启动syncd,它又是何时结束的。
这一切的玄机都在一个函数里——md_check_recovery。这个函数是挺唬人的,至少我在读它的时候懵住了好一会儿,因为在这个函数中处理syncd结束的代码在启动的代码之前,而且里面有些条件并不好理解。在代码中搜一下这个函数,我们就会看到,它是在RAID-1456的守护线程中被调用,所以我们可以得出结论:要想判断是否需要启动或结束,我们要做的就是唤醒守护线程。事实也的确如此,我们可以再搜一下MD_RECOVERY_NEEDED这个宏,就不难发现在set_bit(MD_RECOVERY_NEEDED, &mddev->recovery)的后面总能找到md_wakeup_thread(mddev->thread)的,mddev->thread就是守护线程的指针。说道这里,不得不讲一讲mddev的recovery字段,它是表示resync/recovery的状态的。其中的0~5位这里先拿来说道说道,顾名思义,它们的作用都很直观。RUNNING表示syncd在运行;DONE表示syncd完成;NEEDED表示可能需要resync或recovery;SYNC如果设置表示在做resync,否则是recovery;ERR表示resync/recovery过程中发生错误;INTR表示resync/recovery过程中收到中断信号。
还是先来总结何时需要启动syncd,还是老办法,在md.c中搜一下设置MD_RECOVERY_NEEDED的地方:
1.在action_store函数中,这是为用户提供的发起resync的操作。
2.在MD开始运行时,自然需要检查
3.restart_array中自然也要检查
4.add_new_disk或者hot_add_disk的时候很有可能就是要发起recovery
5.md_error被调用的时候,我们很可能需要recovery
另外还有几处设置了MD_RECOVERY_NEEDED位,但是它们的作用并不是很直观,有些我也还没有特别明白,所以略去不提。
那么下面,我们就集中火力来讨论syncd的生与死,生死都在md_check_recovery中进行。当然,我们看md_check_recovery的代码,会发现他并不仅仅是处理syncd相关的事,还要处理super-block更新的事情,前者自然是我们目前关注的重点。
首先先要检查是否有syncd在运行而且没有结束工作,如果是,那我们就不要多此一举再去启动syncd了。如果没有syncd在跑,那又有两件事要先做:移除一些设备或加入一些设备。这两件事实际上就是在处理md_error或add_disk引发的recovery需求。这里移除设备就是移除MD中Faulty的设备或者没有同步的设备。所谓没有同步(In_sync)就是还没有能正常工作的设备,这种状态好像在前面RAID-5中有所提及。这里我们要注意的是,不仅仅是Faulty的设备要被移除,还有在做recovery的spare设备,我虽然不是很明白为什么要将spare设备先移除然后又立即加回,但是这样做代码的确是比较简洁而直观的。至于这里的移除实际上只是从RAID中将设备隔离出来,实际上并没有从MD中移除,只是通过调用personality中的hot_remove_disk函数完成的,这类设备在/proc/mdstat中可以通过看它后面有没有(F)或者(S)来区别。那么加入设备就是一处设备的逆过程。当然,只有在MD是degraded的时候才要加入spare设备。也许我还需要提一提什么是spare设备,实际上我们可以为RAID准备一些hot spare设备随时在RAID中有设备故障时挺身而出替代故障的设备。所以说这里的spare设备实际上已经在MD中,只是没有参与RAID工作而已。同样,通过调用personality中的hot_add_disk函数完成添加。
接下来就是要判断事要做resync还是做recovery,其差别就是是否在前面加入了spare设备进来。如果有spare加入,那就是recovery,否则就是resync。当然,在启动resync之前我们还要看recovery_cp是否已经到MaxSector了。这里就要先说说什么是recovery_cp,全称是recovery check point,其作用就是在上次resync被打断以后(MD被stop而不是由于md_error打断)记下被打断的位置,下次启动时就可以从这个打断的位置继续做resync而无需再从头来过。而当resync完成时,recovery_cp就会被置为MaxSector,也就是如果recovery_cp的值为MaxSector,表明resync已完成。所以检查recovery_cp的目的就是检查resync是否完成。从这里其实我们也应该看出来了,recovery是不记断点的,所以它总是从头开始。如果既不是recovery,又不用做resync,那就是说没有syncd的必要了这个函数就直接退出了。如果要recovery或者resync,就进入启动syncd的过程。
启动syncd的过程很简单,就是调用md_register_thread,然后将其wake up就行。