RAID5 IO處理之條帶讀代碼詳解( 四 )

2.2.3 拷貝數據到BIOstatic void ops_complete_compute(void *stripe_head_ref){ /* 調用mark_target_uptodate函數設置dev狀態為uptodate */ mark_target_uptodate(sh, sh->ops.target); mark_target_uptodate(sh, sh->ops.target2); /* 計算工作完成清除STRIPE_COMPUTE_RUN標記 */ clear_bit(STRIPE_COMPUTE_RUN, &sh->state); /* 將條帶推入狀態機處理 */ set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh);}之后進入到 BIOFILL 流程與前一小節的流程一致,這里不再贅述 。
2.2.4 向上返回該流程與讀成功流程一致,這里不再贅述 。
2.3 讀IO報錯這里所說的讀IO報錯指的是在下發IO之前RAID成員磁盤狀態正常,但在執行IO過程中底層報告錯誤,進而將該成員磁盤標記為故障磁盤 。條帶的第一輪下發讀請求的處理過程與讀成功章節相同,這里不再贅述 。在IO完成的回調函數 raid5_end_read_request() 中處理不同,流程如下 。
static void raid5_end_read_request(struct bio * bi, int error){ int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags); if (!uptodate) {/* 清除R5_UPTODATE標記表明dev IO異常 */clear_bit(R5_UPTODATE, &sh->dev[i].flags);/* 陣列第一次出現讀錯誤的情況下設置retry標記進入重試 */if (retry)/** 如果設置了R5_ReadNoMerge標記表明是正在重試的對齊讀,也說明異常的是IO本身* 所以此時設置R5_ReadError標記給dev并清除R5_ReadNoMerge標記*/if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags)) {set_bit(R5_ReadError, &sh->dev[i].flags);clear_bit(R5_ReadNoMerge, &sh->dev[i].flags);} else/** 如果未設置R5_ReadNoMerge標記 , 此次異常可能由于進行了IO合并其他IO異常導致返回錯誤,* 所以設置該標記再次下發重試,如果成功則按照“讀成功”流程繼續執行* 如果重試失敗則執行1817行的內容*/set_bit(R5_ReadNoMerge, &sh->dev[i].flags); }}static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s){ rcu_read_lock(); for (i = disks; i--; ) {/* 未設置R5_UPTODATE標記所以不設置R5_Wantfill填充數據 */if (test_bit(R5_UPTODATE, &dev->flags) && dev->toread &&!test_bit(STRIPE_BIOFILL_RUN, &sh->state))set_bit(R5_Wantfill, &dev->flags);/* 統計讀請求 */if (dev->toread)s->to_read++;/* 磁盤未設置為Faulty所以依然設置dev為R5_Insync狀態 */if (test_bit(In_sync, &rdev->flags))set_bit(R5_Insync, &dev->flags);/* 因為設置了R5_ReadError故清除dev的R5_Insync標記 */if (test_bit(R5_ReadError, &dev->flags))clear_bit(R5_Insync, &dev->flags);/* 記錄異常磁盤索引并統計異常dev */if (!test_bit(R5_Insync, &dev->flags)) {if (s->failed < 2)s->failed_num[s->failed] = i;s->failed++;if (rdev && !test_bit(Faulty, &rdev->flags))do_recovery = 1;} } rcu_read_unlock();}后續執行與 IO所在磁盤異常 流程相同的 讀其他成員磁盤、計算校驗、向上返回 流程 。
正常來講,執行到這里上層已經獲取到了想要的結果,我們可以結束流程了,但是成員磁盤還設置了R5_ReadError標記 , 我們是不是可以嘗試進行修復呢?因為現在生產的硬盤都有這樣的功能:在對硬盤的讀/寫過程中,如果發現一個壞扇區,則由內部管理程序自動分配一個備用扇區來替換該扇區 。這樣一來,少量的壞扇區有可能在使用過程中被自動替換掉了,我們還可以繼續使用 ?;谶@個想法 , 我們可以將計算好的數據再寫回成員磁盤 , 我們不管他是否寫到了一個替換過的扇區,只要能再成功讀回該數據就可以了 。
2.4 陣列超冗余陣列超冗余時IO直接返回錯誤 , 處理流程如下圖所示 。
函數調用關系如下:
handle_stripe() \_ analyse_stripe() \_ handle_failed_stripe()各函數執行的代碼邏輯如下:
static void handle_stripe(struct stripe_head *sh){ /* 解析條帶 */ analyse_stripe(sh, &s); /* 異常磁盤數超過RAID冗余 */ if (s.failed > conf->max_degraded) {sh->check_state = 0;sh->reconstruct_state = 0;/* s.to_read為真 */if (s.to_read+s.to_write+s.written)/* 調用handle_failed_stripe處理條帶中的請求 */handle_failed_stripe(conf, sh, &s, disks, &s.return_bi); } /* s.to_read為真進入到handle_stripe_fill中 , 但是條帶的toread設置為NULL* 因此在fetch_block中不會設置R5_Wantread標記*/ if (s.to_read || s.non_overwrite|| (conf->level == 6 && s.to_write && s.failed)|| (s.syncing && (s.uptodate + s.compute < disks))|| s.replacing|| s.expanding)handle_stripe_fill(sh, &s, disks); /* 無需要調度的請求 */ ops_run_io(sh, &s); /* 向上層返回 */ return_io(s.return_bi);}static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s){ rcu_read_lock(); for (i = disks; i--; ) {/* 統計讀請求 */if (dev->toread)s->to_read++;/* 磁盤異常rdev設置為NULL */if (rdev && test_bit(Faulty, &rdev->flags))rdev = NULL;/* 清除dev的R5_Insync標記 */clear_bit(R5_Insync, &dev->flags);if (!test_bit(R5_Insync, &dev->flags)) {if (s->failed < 2)s->failed_num[s->failed] = i;/* 統計異常計數 */s->failed++;if (rdev && !test_bit(Faulty, &rdev->flags))do_recovery = 1;} } rcu_read_unlock();}static voidhandle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,struct stripe_head_state *s, int disks,struct bio **return_bi){ /* 遍歷所有dev */ for (i = disks; i--; ) {spin_lock_irq(&sh->stripe_lock);/** R5_Wantfill標記表示讀成功需要將數據從dev拷貝到bio* R5_Insync表示dev對應磁盤是正常的,R5_ReadError表示讀錯誤* 因此這三個判斷的是dev中沒有也無法讀取正確的數據* 沒有正確的數據在超冗余的情況下直接返回所有的讀請求*/if (!test_bit(R5_Wantfill, &sh->dev[i].flags) &&(!test_bit(R5_Insync, &sh->dev[i].flags) ||test_bit(R5_ReadError, &sh->dev[i].flags))) {spin_lock_irq(&sh->stripe_lock);bi = sh->dev[i].toread;/* 將條帶的toread指針設置為NULL */sh->dev[i].toread = NULL;spin_unlock_irq(&sh->stripe_lock);if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))wake_up(&conf->wait_for_overlap);while (bi && bi->bi_sector <sh->dev[i].sector + STRIPE_SECTORS) {struct bio *nextbi =r5_next_bio(bi, sh->dev[i].sector);clear_bit(BIO_UPTODATE, &bi->bi_flags);if (!raid5_dec_bi_active_stripes(bi)) {bi->bi_next = *return_bi;/* 將條帶中的bio賦值給return_bi */*return_bi = bi;}bi = nextbi;}}clear_bit(R5_LOCKED, &sh->dev[i].flags); }}

推薦閱讀