在上一小節裡講到啟動同步線程:
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