linux内核奇遇记之md源代码解读之七阵列同步一
转载请注明出处:http://blog.csdn.net/liumangxiong
阵列同步在md_do_sync,那么入口在哪里呢?就是说阵列同步触发点在哪里呢?听说过md_check_recovery吧,但这还不是同步的入口点。那raid5d函数是入口点吧?如果要认真分析起来还不算是。
真正的同步入口点在do_md_run函数,就是在运行阵列run函数之后,有这么一行:
5171 md_wakeup_thread(mddev->thread);
是这一行把raid5d唤醒的,raid5d函数如下:
4823 static void raid5d(struct md_thread *thread)
4824 {
4825 struct mddev *mddev = thread->mddev;
4826 struct r5conf *conf = mddev->private;
4827 int handled;
4828 struct blk_plug plug;
4829
4830 pr_debug("+++ raid5d active\n");
4831
4832 md_check_recovery(mddev);
4832行,顾名思义就是检查同步,说明这里只是同步的检查点,不是真正处理同步的地方。
raid5d剩余部分是处理数据流的地方先不看,跟进md_check_recovery,先看注释:
7672 /*
7673 * This routine is regularly called by all per-raid-array threads to
7674 * deal with generic issues like resync and super-block update.
7675 * Raid personalities that don't have a thread (linear/raid0) do not
7676 * need this as they never do any recovery or update the superblock.
7677 *
7678 * It does not do any resync itself, but rather "forks" off other threads
7679 * to do that as needed.
7680 * When it is determined that resync is needed, we set MD_RECOVERY_RUNNING in
7681 * "->recovery" and create a thread at ->sync_thread.
7682 * When the thread finishes it sets MD_RECOVERY_DONE
7683 * and wakeups up this thread which will reap the thread and finish up.
7684 * This thread also removes any faulty devices (with nr_pending == 0).
7685 *
7686 * The overall approach is:
7687 * 1/ if the superblock needs updating, update it.
7688 * 2/ If a recovery thread is running, don't do anything else.
7689 * 3/ If recovery has finished, clean up, possibly marking spares active.
7690 * 4/ If there are any faulty devices, remove them.
7691 * 5/ If array is degraded, try to add spares devices
7692 * 6/ If array has spares or is not in-sync, start a resync thread.
7693 */
这个函数通常由阵列主线程调用,用于处理同步和超级块更新等事件。没有主线程的阵列不需要调用(如线性/raid0),因为这些阵列不需要重建或更新超级块。
这个函数并不做具体事宜,只是按需启动阵列同步线程。
当阵列需要同步时,设置MD_RECOVERY_RUNNING标志,并创建同步线程。
当同步线程结束时设置MD_RECOVERY_DONE标志,并唤醒主线程回收同步线程并结束同步。
这个线程还用于移除坏盘(当nr_pending==0时)。
这个函数处理过程如下:
1、需要时更新超级块
2、同步线程运行时就返回
3、同步线程结束时,回收资源,如果是重建完成则激活热备盘
4、移除坏盘
5、对降级阵列添加热备盘
6、有热备盘但阵列还不是同步状态,则启动同步线程
看完了以上的注释,我已经泪流满面了,因为写代码的哥们太敬业了,把所有精华都已经说出来了,害得像我这种写个博文已经没有什么可写的了。还好我写博文只是自娱自乐,如果是拿这个当饭碗还不早就喝西北风了。
说归说,还是得一行行阅读代码:
7694 void md_check_recovery(struct mddev *mddev)
7695 {
7696 if (mddev->suspended)
7697 return;
7698
7699 if (mddev->bitmap)
7700 bitmap_daemon_work(mddev);
7701
7702 if (signal_pending(current)) {
7703 if (mddev->pers->sync_request && !mddev->external) {
7704 printk(KERN_INFO "md: %s in immediate safe mode\n",
7705 mdname(mddev));
7706 mddev->safemode = 2;
7707 }
7708 flush_signals(current);
7709 }
7710
7711 if (mddev->ro && !test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
7712 return;
7713 if ( ! (
7714 (mddev->flags & ~ (1<<MD_CHANGE_PENDING)) ||
7715 test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
7716 test_bit(MD_RECOVERY_DONE, &mddev->recovery) ||
7717 (mddev->external == 0 && mddev->safemode == 1) ||
7718 (mddev->safemode == 2 && ! atomic_read(&mddev->writes_pending)
7719 && !mddev->in_sync && mddev->recovery_cp == MaxSector)
7720 ))
7721 return;
7696行,检查阵列是否挂起。阵列挂起是一个管理命令,挂起时可以将IO流hold住。
7699行,bitmap清理操作,等bitmap小节再讲。
7702行,接收到信号,进入safemode。
7711行,只读阵列并且未设置检查标志则返回。
7713行,只要一个条件满足,就继续检查,否则返回。
7714行,阵列状态发生改变,则继续检查。
7715行,设置了需要检查标志,则继续检查。
7716行,同步完成,则继续检查。
7717行,安全模式且非external,则继续检查。
7718行,safemode为2有两种情况,一是系统重启时,二是7768行接收到信号时,第一种情况时in_sync为1,第二种情况可以触发更新超级块,根据in_sync标志写回磁盘resync_offset等等。
7723 if (mddev_trylock(mddev)) {
7724 int spares = 0;
...
7746 if (!mddev->external) {
7747 int did_change = 0;
7748 spin_lock_irq(&mddev->write_lock);
7749 if (mddev->safemode &&
7750 !atomic_read(&mddev->writes_pending) &&
7751 !mddev->in_sync &&
7752 mddev->recovery_cp == MaxSector) {
7753 mddev->in_sync = 1;
7754 did_change = 1;
7755 set_bit(MD_CHANGE_CLEAN, &mddev->flags);
7756 }
7757 if (mddev->safemode == 1)
7758 mddev->safemode = 0;
7759 spin_unlock_irq(&mddev->write_lock);
7760 if (did_change)
7761 sysfs_notify_dirent_safe(mddev->sysfs_state);
7762 }
7763
7764 if (mddev->flags)
7765 md_update_sb(mddev, 0);
7766
7767 if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) &&
7768 !test_bit(MD_RECOVERY_DONE, &mddev->recovery)) {
7769 /* resync/recovery still happening */
7770 clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
7771 goto unlock;
7772 }
7773 if (mddev->sync_thread) {
7774 reap_sync_thread(mddev);
7775 goto unlock;
7776 }
7723行,对mddev加锁,这里用trylock是因为这里是raid5主线程调用,如果直接用lock会导致主线程休眠,IO流阻塞。
7746行,external表示超级块存储位置,为0表示存储在阵列所在磁盘上。默认值为0,也许好奇的你会问那可不可以既存储在外面又存储在阵列所在磁盘上呢?那只能怪你想像力太丰富了,不过我就是这么干的,原代码是不支持的需要自己修改代码支持这个特性。这个特性的重要性跟数据的重要性成正比。试想为什么市面上那么多数据恢复软件,为什么会丢数据?根本原因就是metadata丢了,metadata就是宝藏的藏宝图,再回头想想就很有必要啦。
7749行,这个判断就是刚刚7718行的判断,分支里代码也就验证了之前的说明,这个判断主要目的是用来更新超级块的。至于这里更新超级块的重要性理解了in_sync的功能就知道了。
7753行,设置in_sync标志。
7754行,设置阵列改变标识。
7755行,设置mddev改变标志。
7757行,设置safemode为0。尽管前面已经讲解了安全模式了,这里再从另外一个角度说一下,safemode标志就像软件看门狗,在阵列写数据时设置为0,然后需要在写完成时喂狗,如果不喂狗那么阵列为脏需要重新启动同步,喂狗程序就是safemode_timer定时器。
7760行,更新mddev的sysfs下状态。
7764行,更新阵列超级块。
7767行,同步线程正在工作,就没有必要再去凑热闹了。
7773行,同步已完成。
7774行,回收同步线程。
继续md_check_recovery函数:
7777 /* Set RUNNING before clearing NEEDED to avoid
7778 * any transients in the value of "sync_action".
7779 */
7780 set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
7781 /* Clear some bits that don't mean anything, but
7782 * might be left set
7783 */
7784 clear_bit(MD_RECOVERY_INTR, &mddev->recovery);
7785 clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
7786
7787 if (!test_and_clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
7788 test_bit(MD_RECOVERY_FROZEN, &mddev->recovery))
7789 goto unlock;
7790 /* no recovery is running.
7791 * remove any failed drives, then
7792 * add spares if possible.
7793 * Spare are also removed and re-added, to allow
7794 * the personality to fail the re-add.
7795 */
7796
7797 if (mddev->reshape_position != MaxSector) {
7798 if (mddev->pers->check_reshape == NULL ||
7799 mddev->pers->check_reshape(mddev) != 0)
7800 /* Cannot proceed */
7801 goto unlock;
7802 set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
7803 clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
7804 } else if ((spares = remove_and_add_spares(mddev))) {
7805 clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
7806 clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
7807 clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
7808 set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
7809 } else if (mddev->recovery_cp < MaxSector) {
7810 set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
7811 clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
7812 } else if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
7813 /* nothing to be done ... */
7814 goto unlock;
7780行,设置同步运行状态。同步状态有如下:
/* recovery/resync flags
* NEEDED: we might need to start a resync/recover
* RUNNING: a thread is running, or about to be started
* SYNC: actually doing a resync, not a recovery
* RECOVER: doing recovery, or need to try it.
* INTR: resync needs to be aborted for some reason
* DONE: thread is done and is waiting to be reaped
* REQUEST: user-space has requested a sync (used with SYNC)
* CHECK: user-space request for check-only, no repair
* RESHAPE: A reshape is happening
*
* If neither SYNC or RESHAPE are set, then it is a recovery.
*/
#define MD_RECOVERY_RUNNING 0
#define MD_RECOVERY_SYNC 1
#define MD_RECOVERY_RECOVER 2
#define MD_RECOVERY_INTR 3
#define MD_RECOVERY_DONE 4
#define MD_RECOVERY_NEEDED 5
#define MD_RECOVERY_REQUESTED 6
#define MD_RECOVERY_CHECK 7
#define MD_RECOVERY_RESHAPE 8
#define MD_RECOVERY_FROZEN 9
* NEEDED: 需要启动同步线程
* RUNNING: 准备启动或已经有同步线程有运行
* SYNC: 做同步操作
* RECOVER: 尝试或已经在重建操作
* INTR: 同步中断
* DONE: 同步完成
* REQUEST: 用户请求同步
* CHECK: 用户请求检查
* RESHAPE: reshape操作
这里有必要解释一下同步线程的意思,这里的同步是指广义的sync,包括狭义的同步和重建,因为同步和重建实际上做的是同一件事情就是把数据从一个地方拷贝到另一个地方。sync是包括syncing和recovery,所以不要一看到同步线程就以为是做同步操作。
正是这些标志指定同步线程下一步该怎么做,而我们一看到这些标志的时候心里必须要明白此时线程在做什么或者应该做什么。
7804行,这个if分支用于启动重建操作。
7809行,这个分支用于启动同步操作。
7812行,既不同步也不重建那就没有什么可以做的。
7816 if (mddev->pers->sync_request) {
7817 if (spares) {
...
7823 }
7824 mddev->sync_thread = md_register_thread(md_do_sync,
7825 mddev,
7826 "resync");
7827 if (!mddev->sync_thread) {
...
7837 } else
7838 md_wakeup_thread(mddev->sync_thread);
7839 sysfs_notify_dirent_safe(mddev->sysfs_action);
7840 md_new_event(mddev);
7841 }
...
7850 mddev_unlock(mddev);
7851 }
7816行,对于没有同步线程的阵列来说,就没什么事情了。
7824行,启动同步线程。
7838行,唤醒同步线程。
7839行,更新同步状态。
这时主线程调用md_check_recovery函数就已经结束了,启动的同步线程来做同步的具体事宜。那么为什么主线程自己不做同步而另起一个线程做同步呢?不妨想想现在事情都放在主线程里做有什么负面影响?当主线程在同步的时候是不是就不能很及时响应正常数据流了。而且同步和数据流本来就是两件差异很大的事情。
本小节只是介绍到同步入口,同步过程在下一小节开始阅读。