歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

linux內核md源代碼解讀 八 陣列同步二:同步過程

在上一小節裡講到啟動同步線程:

7824                         mddev->sync_thread = md_register_thread(md_do_sync,  
7825                                                                 mddev,  
7826                                                                 "resync");

md_register_thread函數如下:

6697 struct md_thread *md_register_thread(void (*run) (struct mddev *), struct mddev *mddev,  
6698                                  const char *name)  
6699 {  
6700         struct md_thread *thread;  
6701   
6702         thread = kzalloc(sizeof(struct md_thread), GFP_KERNEL);  
6703         if (!thread)  
6704                 return NULL;  
6705   
6706         init_waitqueue_head(&thread->wqueue);  
6707   
6708         thread->run = run;  
6709         thread->mddev = mddev;  
6710         thread->timeout = MAX_SCHEDULE_TIMEOUT;  
6711         thread->tsk = kthread_run(md_thread, thread,  
6712                                   "%s_%s",  
6713                                   mdname(thread->mddev),  
6714                                   name);  
6715         if (IS_ERR(thread->tsk)) {  
6716                 kfree(thread);  
6717                 return NULL;  
6718         }  
6719         return thread;  
6720 }

我相信所有拿過程序員證書,北大青鳥證書的哥們看這些代碼是輕而易舉,然而我沒上過這些培訓學校,也沒有拿過程序員證,實在是慚愧啊。這在很大程度上拖了廣大技術人員的後腿,於是心裡十分忐忑,特別是上海火災是臨時工所為,火車票系統出錯是程序員無證上崗所為。想想在學校時老師教育我們:難道你們四年的學習都比不上一張證書,老師四年的培養都比不上一張程序員證嗎?當時准備報名考試的我頓時就羞愧難當了。然而社會就是社會從來都沒有哪次求職說要程序員證。但最怕的還是有關部門,哪天都有可能被抓去判個無證上崗。

這個函數有兩個看點:

6706行,初始化等待隊列,在此等待隊列上休眠的線程正是md_thread,那又是誰來喚醒的呢?喚醒的函數都叫wakeup,那就find symbol看一下有沒有叫md wakeup的函數,果真有md_wakeup_thread()函數。所以下次看到這個函數的時候就知道輪到線程處理啦。

6711行,創建一個線程,先關心一下線程的名字,是md名和作用名的結合。當這裡執行完成之後,在用戶態ps一下就能看到這個線程了。除了線程名字,我們還關心這個線程做什麼?運行的是md_thread()函數,這個函數只是提供了一個線程運行模板,真正做的事情是函數傳進來的run函數。回到7824行,我們知道同步真正做事情的是md_do_sync。

於是我們就跟進md_do_sync函數:

7245 #define SYNC_MARKS      10  
7246 #define SYNC_MARK_STEP  (3*HZ)  
7247 void md_do_sync(struct mddev *mddev)  
7248 {  
7249         struct mddev *mddev2;  
7250         unsigned int currspeed = 0,  
7251                  window;  
7252         sector_t max_sectors,j, io_sectors;  
7253         unsigned long mark[SYNC_MARKS];  
7254         sector_t mark_cnt[SYNC_MARKS];  
7255         int last_mark,m;  
7256         struct list_head *tmp;  
7257         sector_t last_check;  
7258         int skipped = 0;  
7259         struct md_rdev *rdev;  
7260         char *desc;  
7261         struct blk_plug plug;  
7262   
7263         /* just incase thread restarts... */
7264         if (test_bit(MD_RECOVERY_DONE, &mddev->recovery))  
7265                 return;  
7266         if (mddev->ro) /* never try to sync a read-only array */
7267                 return;  
7268   
7269         if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {  
7270                 if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))  
7271                         desc = "data-check";  
7272                 else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))  
7273                         desc = "requested-resync";  
7274                 else
7275                         desc = "resync";  
7276         } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))  
7277                 desc = "reshape";  
7278         else
7279                 desc = "recovery";

7264行,檢查同步是否完成,再次友情提醒,這裡的同步是指廣義上的同步。

7266行,只讀陣列就不要同步了。

7269行之後,設置線程打印信息。

7279-7345行,是用磁盤分區創建的陣列同步互斥用的。商業化的陣列沒有必要用磁盤分區做陣列的,所以直接跳過。

7346         j = 0;  
7347         if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {  
7348                 /* resync follows the size requested by the personality, 
7349                  * which defaults to physical size, but can be virtual size 
7350                  */
7351                 max_sectors = mddev->resync_max_sectors;  
7352                 mddev->resync_mismatches = 0;  
7353                 /* we don't use the checkpoint if there's a bitmap */
7354                 if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))  
7355                         j = mddev->resync_min;  
7356                 else if (!mddev->bitmap)  
7357                         j = mddev->recovery_cp;  
7358   
7359         } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))  
7360                 max_sectors = mddev->resync_max_sectors;  
7361         else {  
7362                 /* recovery follows the physical size of devices */
7363                 max_sectors = mddev->dev_sectors;  
7364                 j = MaxSector;  
7365                 rcu_read_lock();  
7366                 rdev_for_each_rcu(rdev, mddev)  
7367                         if (rdev->raid_disk >= 0 &&  
7368                             !test_bit(Faulty, &rdev->flags) &&  
7369                             !test_bit(In_sync, &rdev->flags) &&  
7370                             rdev->recovery_offset < j)  
7371                                 j = rdev->recovery_offset;  
7372                 rcu_read_unlock();  
7373         }  
7374   
7375         printk(KERN_INFO "md: %s of RAID array %s\n", desc, mdname(mddev));  
7376         printk(KERN_INFO "md: minimum _guaranteed_  speed:"
7377                 " %d KB/sec/disk.\n", speed_min(mddev));  
7378         printk(KERN_INFO "md: using maximum available idle IO bandwidth "
7379                "(but not more than %d KB/sec) for %s.\n",  
7380                speed_max(mddev), desc);  
7381   
7382         is_mddev_idle(mddev, 1); /* this initializes IO event counters */

7347行,是同步。

7348行,同步默認是physical size,也可以是virtual size。如果你第一次閱讀就能明白其中的意思,那麼恭喜你是一個內核天才。如果我這一次講完你能看懂,那麼恭喜你是一個內核人才。如果看不懂也沒有多大關系,畢竟大多數人都只是想混混日子而已,只要有一顆向上努力的心,始終都有市場的。想當初我也是抱著趙炯博士的linux內核完全注釋足足看了七遍,整本書都已經被我筆記得體無完膚了,但是仍然只是一知半解。所以看不懂沒有關系,但是要把握兩點:一是要把握方法,看懂原理挑重點看,二是多動手修改幾行代碼試試,並且持之以恆。

那什麼是physical size,什麼是virtual size?物理大小就是單個磁盤用於創建陣列空間的大小,虛擬大小就是陣列大小。怎麼樣,終於相信自己是內核天才了吧!那為什麼同步要有這樣的區別呢?這就要跟陣列的特性相關了,raid5陣列是屬於前者,按磁盤從頭到尾同步,raid10陣列是屬於後者,是按照鏡像對進行同步的。
7351行,所以對於不同陣列,max_sectors代表不同的含義。

接下來是reshape和重建,跳過。

7375行,打印陣列同步信息。

7376-7380行,打印同步速度信息。同步有速度控制是為了不影響正常數據流。

7382行,初始化rdev->last_events。函數is_mddev_idle用於控制同步速度,當一小段時間內IO太多時會休眠來降低同步速度。

7384         io_sectors = 0;  
7385         for (m = 0; m < SYNC_MARKS; m++) {  
7386                 mark[m] = jiffies;  
7387                 mark_cnt[m] = io_sectors;  
7388         }  
7389         last_mark = 0;  
7390         mddev->resync_mark = mark[last_mark];  
7391         mddev->resync_mark_cnt = mark_cnt[last_mark];  
7392   
7393         /* 
7394          * Tune reconstruction: 
7395          */
7396         window = 32*(PAGE_SIZE/512);  
7397         printk(KERN_INFO "md: using %dk window, over a total of %lluk.\n",  
7398                 window/2, (unsigned long long)max_sectors/2);  
7399   
7400         atomic_set(&mddev->recovery_active, 0);  
7401         last_check = 0;  
7402   
7403         if (j>2) {  
7404                 printk(KERN_INFO   
7405                        "md: resuming %s of %s from checkpoint.\n",  
7406                        desc, mdname(mddev));  
7407                 mddev->curr_resync = j;  
7408         }  
7409         mddev->curr_resync_completed = j;

7385-7391行,同步點記錄的初始化。這裡設置了幾個觀察點,用幾個觀察點之間下發的數據流速度來控制同步線程。

7396行,設置窗口大小,數據流大小這個窗口大小才進入觀察點。

7400行,下發但未返回請求的大小。

7403行,繼續同步的。

7409行,設置同步完成點。

7411         blk_start_plug(&plug);  
7412         while (j < max_sectors) {  
7413                 sector_t sectors;  
7414   
7415                 skipped = 0;  
7416   
7417                 if (!test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&  
7418                     ((mddev->curr_resync > mddev->curr_resync_completed &&  
7419                       (mddev->curr_resync - mddev->curr_resync_completed)  
7420                       > (max_sectors >> 4)) ||  
7421                      (j - mddev->curr_resync_completed)*2  
7422                      >= mddev->resync_max - mddev->curr_resync_completed  
7423                             )) {  
7424                         /* time to update curr_resync_completed */
7425                         wait_event(mddev->recovery_wait,  
7426                                    atomic_read(&mddev->recovery_active) == 0);  
7427                         mddev->curr_resync_completed = j;  
7428                         set_bit(MD_CHANGE_CLEAN, &mddev->flags);  
7429                         sysfs_notify(&mddev->kobj, NULL, "sync_completed");  
7430                 }  
7431   
7432                 while (j >= mddev->resync_max && !kthread_should_stop()) {  
7433                         /* As this condition is controlled by user-space, 
7434                          * we can block indefinitely, so use '_interruptible' 
7435                          * to avoid triggering warnings. 
7436                          */
7437                         flush_signals(current); /* just in case */
7438                         wait_event_interruptible(mddev->recovery_wait,  
7439                                                  mddev->resync_max > j  
7440                                                  || kthread_should_stop());  
7441                 }  
7442   
7443                 if (kthread_should_stop())  
7444                         goto interrupted;  
7445   
7446                 sectors = mddev->pers->sync_request(mddev, j, &skipped,  
7447                                                   currspeed < speed_min(mddev));  
7448                 if (sectors == 0) {  
7449                         set_bit(MD_RECOVERY_INTR, &mddev->recovery);  
7450                         goto out;  
7451                 }  
7452   
7453                 if (!skipped) { /* actual IO requested */
7454                         io_sectors += sectors;  
7455                         atomic_add(sectors, &mddev->recovery_active);  
7456                 }  
7457   
7458                 if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))  
7459                         break;  
7460   
7461                 j += sectors;  
7462                 if (j>1) mddev->curr_resync = j;  
7463                 mddev->curr_mark_cnt = io_sectors;  
7464                 if (last_check == 0)  
7465                         /* this is the earliest that rebuild will be 
7466                          * visible in /proc/mdstat 
7467                          */
7468                         md_new_event(mddev);  
7469   
7470                 if (last_check + window > io_sectors || j == max_sectors)  
7471                         continue;  
7472   
7473                 last_check = io_sectors;  
7474         repeat:  
7475                 if (time_after_eq(jiffies, mark[last_mark] + SYNC_MARK_STEP )) {  
7476                         /* step marks */
7477                         int next = (last_mark+1) % SYNC_MARKS;  
7478   
7479                         mddev->resync_mark = mark[next];  
7480                         mddev->resync_mark_cnt = mark_cnt[next];  
7481                         mark[next] = jiffies;  
7482                         mark_cnt[next] = io_sectors - atomic_read(&mddev->recovery_active);  
7483                         last_mark = next;  
7484                 }  
7485   
7486   

7487                 if (kthread_should_stop())  
7488                         goto interrupted;  
7489   
7490   
7491                 /* 
7492                  * this loop exits only if either when we are slower than 
7493                  * the 'hard' speed limit, or the system was IO-idle for 
7494                  * a jiffy. 
7495                  * the system might be non-idle CPU-wise, but we only care 
7496                  * about not overloading the IO subsystem. (things like an 
7497                  * e2fsck being done on the RAID array should execute fast) 
7498                  */
7499                 cond_resched();  
7500   
7501                 currspeed = ((unsigned long)(io_sectors-mddev->resync_mark_cnt))/2  
7502                         /((jiffies-mddev->resync_mark)/HZ +1) +1;  
7503   
7504                 if (currspeed > speed_min(mddev)) {  
7505                         if ((currspeed > speed_max(mddev)) ||  
7506                                         !is_mddev_idle(mddev, 0)) {  
7507                                 msleep(500);  
7508                                 goto repeat;  
7509                         }  
7510                 }  
7511         }  
7512         printk(KERN_INFO "md: %s: %s done.\n",mdname(mddev), desc);

 

這個循環真是長啊,為了保持完整性還是全部放在這樣了。

7411行,這個函數背後還真有故事,不過是屬於塊層的。詳細說明可參考我的另一篇博文:http://blog.csdn.net/liumangxiong/article/details/10279089

7412行,同步點小於最大同步值。

7417行,並非reshape

7418行,當前同步點大於當前同步完成位置

7419行,已下發未返回同步大小大於十六分之一總同步大小

7421行,已下發未返回同步大小*2大於等於同步檢查點-當前同步完成點

這幾行是什麼意思呢?如果你堅持看完了前面兩行並沒有頭暈的症狀,那麼恭喜你身體狀況很好可以去玩跳傘等刺激活動。這幾行的意思是說下發的同步請求太多了,超過這些閥值,需要停下來等待請求返回,並保存新的完成同步點。

7425行,等待同步請求返回。同步請求是按順序下發的,但是底層的塊設備不一定按原順序完成,所以需要等待確認都返回。

7427行,保存新的同步完成點。

7428行,設置改變標志。

7429行,更新sysfs同步點。

7432行,由用戶指定同步最大值,可以用於數據流很大時停止同步或其他類似用途。

7443行,設置了線程停止標志

7445行,調用pers的sync_request,每種陣列具體同步操作,這個在後面講解

7448行,沒有同步

7449行,設置同步中斷標志

7453行,skipped表示bitmap認為是已同步條帶,所以直接跳過。這裡表示不能跳過

7454行,累計真實IO大小

7455行,累計下發同步IO大小

7461行,遞增當前同步點

7462行,更新mddev當前同步點

7463行,統計用

7464行,更新/proc/stat顯示

7470行,上次觀察點以來下發同步IO不足窗口大小則繼續下發同步請求

7475行,至少SYNC_MARK_STEP時間建立一個觀察點

7501行,計算這次觀察點的速度

7504行,如果小於最小速度則繼續同步

7505行,如果大於最大速度或者非idle則短暫休眠再同步

7512行,看到done很開心,表示同步完成了

拋開同步具體的數據流不管,md_do_sync就只是一個簡單的控制器用於控制同步的推進。

1)下發同步請求

2)記錄同步觀察點

3)同步速度太快則休眠

4)超過閥值,等待所有請求返回,更新同步完成點

5)轉到步驟1)繼續同步

繼續往下看,同步完成之後還有一些事情要處理,說直接點就是要保存同步結果。

7518         wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active));  
7519   
7520         /* tell personality that we are finished */
7521         mddev->pers->sync_request(mddev, max_sectors, &skipped, 1);  
7522   
7523         if (!test_bit(MD_RECOVERY_CHECK, &mddev->recovery) &&  
7524             mddev->curr_resync > 2) {  
7525                 if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {  
7526                         if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {  
7527                                 if (mddev->curr_resync >= mddev->recovery_cp) {  
7528                                         printk(KERN_INFO  
7529                                                "md: checkpointing %s of %s.\n",  
7530                                                desc, mdname(mddev));  
7531                                         mddev->recovery_cp =  
7532                                                 mddev->curr_resync_completed;  
7533                                 }  
7534                         } else
7535                                 mddev->recovery_cp = MaxSector;  
7536                 } else {  
7537                         if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery))  
7538                                 mddev->curr_resync = MaxSector;  
7539                         rcu_read_lock();  
7540                         rdev_for_each_rcu(rdev, mddev)  
7541                                 if (rdev->raid_disk >= 0 &&  
7542                                     mddev->delta_disks >= 0 &&  
7543                                     !test_bit(Faulty, &rdev->flags) &&  
7544                                     !test_bit(In_sync, &rdev->flags) &&  
7545                                     rdev->recovery_offset < mddev->curr_resync)  
7546                                         rdev->recovery_offset = mddev->curr_resync;  
7547                         rcu_read_unlock();  
7548                 }  
7549         }  
7550  skip:  
7551         set_bit(MD_CHANGE_DEVS, &mddev->flags);  
7552   
7553         if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {  
7554                 /* We completed so min/max setting can be forgotten if used. */
7555                 if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))  
7556                         mddev->resync_min = 0;  
7557                 mddev->resync_max = MaxSector;  
7558         } else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))  
7559                 mddev->resync_min = mddev->curr_resync_completed;  
7560         mddev->curr_resync = 0;  
7561         wake_up(&resync_wait);  
7562         set_bit(MD_RECOVERY_DONE, &mddev->recovery);  
7563         md_wakeup_thread(mddev->thread);  
7564         return;

7518行,等待所有同步請求返回7521行,根據同步結果更新bitmap,回收資源7525行,如果同步中斷則設置recovery_cp為同步完成點,正常完成則設置為MaxSector。7551行,設置改變狀態7553-7560行,恢復同步值7561行,喚醒同磁盤分區同步等待線程7562行,設置同步完成標志7563行,喚醒主線程。如果到這裡就認為同步完成了,那就大錯特錯了。記得有一句話講,每一個階段的結束就是下一個階段的起點。看到md_wake_up我們就想到事情又有了一個新起點。在喚醒主線程之後,主線程會調用上一小節中的md_check_recovery來清理現場,最終調用到7774行的reap_sync_thread函數。對於同步來說,這個函數做了以下事情:1)回收同步線程2)更新超級塊3)更新mddev標志其實陣列的同步很簡單,下一小節講raid5同步過程sync_request函數。

出處:http://blog.csdn.net/liumangxiong

Copyright © Linux教程網 All Rights Reserved