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 下发读请求函数调用关系如下:
推荐阅读
- RAID5 IO处理之写请求代码详解
- RAID5 IO处理之重构代码详解
- RAID5 IO处理之replace代码详解
- Linux Block模块之deadline调度算法代码解析
- Linux Block模块之IO合并代码解析
- 【强烈推荐】用glob库的一行命令显著加速批量读取处理数据
- redis bitmap数据结构之java对等操作
- 一加9r参数_一加9r搭载什么处理器
- 苹果处理器a11a12区别_苹果处理器a11和a12区别大吗
- 奔腾处理器和i5哪个好-英特尔奔腾处理器和酷睿i5处理器哪个好