陣列同步在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