RAID5 IO处理之写请求代码详解( 四 )

以上,读改写处理结束 。
3 重构写重构写整体逻辑与读改写相差不大,我们只将不相同的部分说明如下,整体逻辑大家可自行串联 。
第一轮读数据:
static void handle_stripe_dirtying(struct r5conf *conf,struct stripe_head *sh,struct stripe_head_state *s,int disks){ int rmw = 0, rcw = 0, i; sector_t recovery_cp = conf->mddev->recovery_cp; /** 以下几种情况不能进行读改写只能使用重构写* 1.RAID级别为6,因为读改写利用的是异或运算的特性,因此RAID6不适用* 2.IO起始范围超过了同步的进度 。因为读改写需要用旧的数据和旧的校验,*如果旧数据见不是一致的,那么再进行异或运算数据也是不一致的,*/ if (conf->max_degraded == 2 ||(recovery_cp < MaxSector && sh->sector >= recovery_cp)) {/* Calculate the real rcw later - for now make it* look like rcw is cheaper*/rcw = 1; rmw = 2;pr_debug("force RCW max_degraded=%u, recovery_cp=%llu sh->sector=%llu\n",conf->max_degraded, (unsigned long long)recovery_cp,(unsigned long long)sh->sector); } else for (i = disks; i--; ) {/* 如果dev有写请求或保存的是校验值那么该dev在读改写逻辑中是需要读的 */struct r5dev *dev = &sh->dev[i];if ((dev->towrite || i == sh->pd_idx) &&!test_bit(R5_LOCKED, &dev->flags) &&!(test_bit(R5_UPTODATE, &dev->flags) ||test_bit(R5_Wantcompute, &dev->flags))) {if (test_bit(R5_Insync, &dev->flags))rmw++;elsermw += 2*disks;/* cannot read it */}/* 如果dev非满写(为写满4K或无IO)并且不是校验那么该dev在重构写逻辑中是需要读的 */if (!test_bit(R5_OVERWRITE, &dev->flags) && i != sh->pd_idx &&!test_bit(R5_LOCKED, &dev->flags) &&!(test_bit(R5_UPTODATE, &dev->flags) ||test_bit(R5_Wantcompute, &dev->flags))) {if (test_bit(R5_Insync, &dev->flags)) rcw++;elsercw += 2*disks;} } /* 进行重构写需要读的次数少 */ if (rcw <= rmw && rcw > 0) {/* want reconstruct write, but need to get some data */int qread =0;rcw = 0;for (i = disks; i--; ) {struct r5dev *dev = &sh->dev[i];/** 满足以下条件下发读请求*1. 非满写*2. 不是校验*3. 尚未下发请求*4. 当前page中不是最新数据且不需要计算*/if (!test_bit(R5_OVERWRITE, &dev->flags) &&i != sh->pd_idx && i != sh->qd_idx &&!test_bit(R5_LOCKED, &dev->flags) &&!(test_bit(R5_UPTODATE, &dev->flags) ||test_bit(R5_Wantcompute, &dev->flags))) {rcw++;if (!test_bit(R5_Insync, &dev->flags))continue; /* it's a failed drive *//* 给条带/设备上锁表明正在进行IO */set_bit(R5_LOCKED, &dev->flags);/* 表明该条带/设备要调度读请求 */set_bit(R5_Wantread, &dev->flags);s->locked++;/* locked增加计数 */qread++;}} } /* locked不等于0本轮不调度schedule_reconstruction */ if ((s->req_compute || !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) &&(s->locked == 0 && (rcw == 0 || rmw == 0) &&!test_bit(STRIPE_BIT_DELAY, &sh->state)))schedule_reconstruction(sh, s, rcw == 0, 0);}在第二轮计算校验的条带处理中,因为直接使用数据段计算校验段 , 因此不需要做 ops_run_prexor,后续下发写请求和向上返回与读改写相同 。
满写是重构写的一种特殊情况 , 即所有数据段都是满写,此时不需要读旧数据,直接根据新数据计算校验然后一起写入 , 此时性能最好,我们在实际使用中,可以根据业务写IO的buffer大小调整数据盘个数和chunk大小 , 以达到满写的状态 。

推荐阅读