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

2.1.2 拷貝數據到BIO函數調用關系如下:
raid5_end_read_request() \_ handle_stripe()\_ analyse_stripe()\_ raid_run_ops()\_ ops_run_biofill()各函數執行的代碼邏輯如下:
static void raid5_end_read_request(struct bio * bi, int error){ /* 讀成功設置BIO_UPTODATE標記 */ int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags); if (uptodate) {/* 標記條帶/設備上的數據為最新的 */set_bit(R5_UPTODATE, &sh->dev[i].flags); } /* 自減成員磁盤的pending io計數 */ rdev_dec_pending(rdev, conf->mddev); /* 清除R5_LOCKED標記表明磁盤IO動作結束 */ clear_bit(R5_LOCKED, &sh->dev[i].flags); /* 將條帶推入狀態機處理 */ set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh);}static void handle_stripe(struct stripe_head *sh){ /* 解析條帶狀態 */ analyse_stripe(sh, &s); /* 設置標記將條帶中的數據填充到bio中 */ if (s.to_fill && !test_bit(STRIPE_BIOFILL_RUN, &sh->state)) {set_bit(STRIPE_OP_BIOFILL, &s.ops_request);set_bit(STRIPE_BIOFILL_RUN, &sh->state); } /* 因前邊設置了標記這里進入raid_run_ops函數 */ if (s.ops_request)raid_run_ops(sh, s.ops_request);}static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s){ rcu_read_lock(); for (i = disks; i--; ) {/* 設置R5_Wantfill標記表明需要將dev->page中的數據填充到bio->page中 */if (test_bit(R5_UPTODATE, &dev->flags) && dev->toread &&!test_bit(STRIPE_BIOFILL_RUN, &sh->state))set_bit(R5_Wantfill, &dev->flags);/* 統計數據與磁盤數據一致的dev */if (test_bit(R5_UPTODATE, &dev->flags))s->uptodate++;/* 統計需要進行填充數據的dev */if (test_bit(R5_Wantfill, &dev->flags))s->to_fill++; } rcu_read_unlock();}static void raid_run_ops(struct stripe_head *sh, unsigned long ops_request){ /* 在解析條帶時設置了該標記,進入if調用ops_run_biofill */ if (test_bit(STRIPE_OP_BIOFILL, &ops_request)) {ops_run_biofill(sh);overlap_clear++; }}static void ops_run_biofill(struct stripe_head *sh){ struct dma_async_tx_descriptor *tx = NULL; struct async_submit_ctl submit; int i; for (i = sh->disks; i--; ) {struct r5dev *dev = &sh->dev[i];/* 設置了R5_Wantfill標記的dev需要調度拷貝數據 */if (test_bit(R5_Wantfill, &dev->flags)) {struct bio *rbi;spin_lock_irq(&sh->stripe_lock);/* 將已完成讀處理的請求從dev的toread移動到read */dev->read = rbi = dev->toread;/* toread置空 */dev->toread = NULL;spin_unlock_irq(&sh->stripe_lock);while (rbi && rbi->bi_sector <dev->sector + STRIPE_SECTORS) {tx = async_copy_data(0, rbi, dev->page,dev->sector, tx);rbi = r5_next_bio(rbi, dev->sector);}} } atomic_inc(&sh->count); /* 通過異步引擎進行數據拷貝并設置回調函數為ops_complete_biofill */ init_async_submit(&submit, ASYNC_TX_ACK, tx, ops_complete_biofill, sh, NULL); async_trigger_callback(&submit);}2.1.3 向上返回函數調用關系如下:
ops_complete_biofill() \_ return_io()各函數執行的代碼邏輯如下:
static void ops_complete_biofill(void *stripe_head_ref){ struct stripe_head *sh = stripe_head_ref; struct bio *return_bi = NULL; int i; /* clear completed biofills */ for (i = sh->disks; i--; ) {struct r5dev *dev = &sh->dev[i];/* 判斷并清除R5_Wantfill標記 */if (test_and_clear_bit(R5_Wantfill, &dev->flags)) {struct bio *rbi, *rbi2;BUG_ON(!dev->read);/* 將完成拷貝的讀請求賦值到rbi,dev的read置空 */rbi = dev->read;dev->read = NULL;/* 遍歷條帶范圍內所有的bio */while (rbi && rbi->bi_sector <dev->sector + STRIPE_SECTORS) {/* 通過bi_next獲取條帶范圍內的下一個bio */rbi2 = r5_next_bio(rbi, dev->sector);/** 如果bi_phy_segments為0表明bio處理完畢,此時將bio掛載到* return_bi上 , 多個bio通過bi_next鏈接 。* 假設有2個bio執行完成,執行完這段代碼后結果為* return_bi->bio2->bio1->NULL*/if (!raid5_dec_bi_active_stripes(rbi)) {rbi->bi_next = return_bi;return_bi = rbi;}rbi = rbi2;}} } clear_bit(STRIPE_BIOFILL_RUN, &sh->state); return_io(return_bi); set_bit(STRIPE_HANDLE, &sh->state); /* 將條帶推入狀態機處理 */ release_stripe(sh);}static void return_io(struct bio *return_bi){ struct bio *bi = return_bi; /* 通過bi_next遍歷bio調用bio_endio結束io并通知上層 */ while (bi) {return_bi = bi->bi_next;bi->bi_next = NULL;bi->bi_size = 0;bio_endio(bi, 0);bi = return_bi; }}2.2 IO所在磁盤異常這里指下發IO之前磁盤異常 。當IO所在磁盤異常時無法從該磁盤上直接讀取數據 , 需要通過讀取同條帶的其他磁盤數據然后經過異或運算還原出當前要讀的磁盤的數據 。該流程會經過以下四輪的條帶處理,讀取成功后將數據返回給調用者 。
2.2.1 下發讀請求函數調用關系如下:

推薦閱讀