linux内核奇遇记之md源代码解读之七阵列同步一

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函数就已经结束了,启动的同步线程来做同步的具体事宜。那么为什么主线程自己不做同步而另起一个线程做同步呢?不妨想想现在事情都放在主线程里做有什么负面影响?当主线程在同步的时候是不是就不能很及时响应正常数据流了。而且同步和数据流本来就是两件差异很大的事情。
本小节只是介绍到同步入口,同步过程在下一小节开始阅读。
转载请注明出处:http://blog.csdn.net/liumangxiong
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值