首页 > 代码库 > linux内核奇遇记之md源代码解读之十四raid5非条块内读
linux内核奇遇记之md源代码解读之十四raid5非条块内读
linux内核奇遇记之md源代码解读之十四raid5非条块内读
转载请注明出处:http://blog.csdn.net/liumangxiong
如果是非条块内读,那么就至少涉及到两个条块的读,这就需要分别从这两个条块内读出数据,然后再凑成整个结果返回给上层。接下来我们将看到如何将一个完整的bio读请求拆分成多个子请求下发到磁盘,从磁盘返回之后再重新组合成请求结果返回给上层的。
4097 logical_sector = bi->bi_sector & ~((sector_t)STRIPE_SECTORS-1); 4098 last_sector = bi->bi_sector + (bi->bi_size>>9); 4099 bi->bi_next = NULL; 4100 bi->bi_phys_segments = 1; /* over-loaded to count active stripes */
首先计算请求起始位置,因为md下发到磁盘数据请求的最小单位为STRIPE_SECTORS,所以这里要将请求对齐。计算出请求起始位置为logical_sector ,结束位置为last_sector。4100行复用bi_phys_segments 用于计数下发条带数,这里防止意外释放先设置为1。
4102 for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) { 4103 DEFINE_WAIT(w); 4104 int previous; 4105 4106 retry: 4107 previous = 0; 4108 prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE); ... 4134 4135 new_sector = raid5_compute_sector(conf, logical_sector, 4136 previous, 4137 &dd_idx, NULL); 4138 pr_debug("raid456: make_request, sector %llu logical %llu\n", 4139 (unsigned long long)new_sector, 4140 (unsigned long long)logical_sector); 4141 4142 sh = get_active_stripe(conf, new_sector, previous, 4143 (bi->bi_rw&RWA_MASK), 0);
在这个循环中将请求拆分个多个条带,分别下发命令。在处理条带的时候还需要做到互斥,不能有两个线程在同时操作同一个条带。比如说同步线程在同步这个条带,raid5d在写这个条带,那么就会产生非预期的结果。
4103行,等待队列用于条带访问互斥
4108行,加入等待队列
4135行,根据阵列逻辑扇区计算出磁盘物理偏移扇区,并计算对应的数据盘号和校验盘号
4142行,根据磁盘物理偏移扇区获取一个条带
4144 if (sh) { .... 4186 if (test_bit(STRIPE_EXPANDING, &sh->state) || 4187 !add_stripe_bio(sh, bi, dd_idx, rw)) { 4188 /* Stripe is busy expanding or 4189 * add failed due to overlap. Flush everything 4190 * and wait a while 4191 */ 4192 md_wakeup_thread(mddev->thread); 4193 release_stripe(sh); 4194 schedule(); 4195 goto retry; 4196 } 4197 finish_wait(&conf->wait_for_overlap, &w); 4198 set_bit(STRIPE_HANDLE, &sh->state); 4199 clear_bit(STRIPE_DELAYED, &sh->state); 4200 if ((bi->bi_rw & REQ_SYNC) && 4201 !test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) 4202 atomic_inc(&conf->preread_active_stripes); 4203 release_stripe_plug(mddev, sh); 4204 } else { 4205 /* cannot get stripe for read-ahead, just give-up */ 4206 clear_bit(BIO_UPTODATE, &bi->bi_flags); 4207 finish_wait(&conf->wait_for_overlap, &w); 4208 break; 4209 } 4210 }
在第一次看这段代码的时候,由于太匆忙完全没有找到重点在哪里。就像一个人在喧嚣的城市里长大,由于被城市的外表所迷惑完全不知道内心真正想追求的生活。当真正静下心来看的时候,终于发现最重要的一句在4187行,即add_stripe_bio函数,从此开始stripe不再孤单,因为有了bio的附体,它已经准备好要加入了条带处理流程,一场轰轰烈烈的条带人生路由此展开。
在4198行和4203行release_stripe_plug之后一个新的条带正式加入了处理队列(conf->handle_list)。
人的上半生在不断地找入口,下半生在不断地找出口。在这里,读stripe找到了入口,那么出口在哪里呢?读过LDD的同学一定知道答案,对于不使用默认请求队列的块设备驱动来说,对应的make_request函数为入口,出口就是bio_endio。接下来我们就一步步迈向这个出口。
release_stripe_plug之后首先进入的是handle_stripe,handle_stripe调用analyse_stripe,在这个函数中设置了to_read:
3245 if (test_bit(R5_Wantfill, &dev->flags)) 3246 s->to_fill++; 3247 else if (dev->toread) 3248 s->to_read++;
3472 if (s.to_read || s.non_overwrite 3473 || (conf->level == 6 && s.to_write && s.failed) 3474 || (s.syncing && (s.uptodate + s.compute < disks)) 3475 || s.replacing 3476 || s.expanding) 3477 handle_stripe_fill(sh, &s, disks);
to_read触发了handle_stripe_fill,这个函数的作用就是设置需要读取的标志:
2696 set_bit(R5_LOCKED, &dev->flags); 2697 set_bit(R5_Wantread, &dev->flags); 2698 s->locked++;
接着又来到了ops_run_io,将读请求下发到磁盘。读请求的回调函数为raid5_end_read_request:
1745 if (uptodate) { 1746 set_bit(R5_UPTODATE, &sh->dev[i].flags); ... 1824 rdev_dec_pending(rdev, conf->mddev); 1825 clear_bit(R5_LOCKED, &sh->dev[i].flags); 1826 set_bit(STRIPE_HANDLE, &sh->state); 1827 release_stripe(sh);
这个函数做了两件事情,一是设置了R5_UPTODATE标志,另一是调用了release_stripe重新将条带送回了handle_stripe处理。
带着R5_UPTODATE标志进入了analyse_stripe函数:
3231 if (test_bit(R5_UPTODATE, &dev->flags) && dev->toread && 3232 !test_bit(STRIPE_BIOFILL_RUN, &sh->state)) 3233 set_bit(R5_Wantfill, &dev->flags); 3234 3235 /* now count some things */ 3236 if (test_bit(R5_LOCKED, &dev->flags)) 3237 s->locked++; 3238 if (test_bit(R5_UPTODATE, &dev->flags)) 3239 s->uptodate++; 3240 if (test_bit(R5_Wantcompute, &dev->flags)) { 3241 s->compute++; 3242 BUG_ON(s->compute > 2); 3243 } 3244 3245 if (test_bit(R5_Wantfill, &dev->flags)) 3246 s->to_fill++;
在3255行设置了R5_Wantfill标志,在3246行设置了to_fill,再次回来handle_stripe:
3426 if (s.to_fill && !test_bit(STRIPE_BIOFILL_RUN, &sh->state)) { 3427 set_bit(STRIPE_OP_BIOFILL, &s.ops_request); 3428 set_bit(STRIPE_BIOFILL_RUN, &sh->state); 3429 }
条带状态设置了STRIPE_OP_BIOFILL,只要设置了s.ops_request,就必须马上知道这个域对应的处理函数为raid_run_ops,实际操作在__raid_run_ops:
1378 if (test_bit(STRIPE_OP_BIOFILL, &ops_request)) { 1379 ops_run_biofill(sh); 1380 overlap_clear++; 1381 }
对应的处理函数是ops_run_biofill:
812static void ops_run_biofill(struct stripe_head *sh) 813{ 814 struct dma_async_tx_descriptor *tx = NULL; 815 struct async_submit_ctl submit; 816 int i; 817 818 pr_debug("%s: stripe %llu\n", __func__, 819 (unsigned long long)sh->sector); 820 821 for (i = sh->disks; i--; ) { 822 struct r5dev *dev = &sh->dev[i]; 823 if (test_bit(R5_Wantfill, &dev->flags)) { 824 struct bio *rbi; 825 spin_lock_irq(&sh->stripe_lock); 826 dev->read = rbi = dev->toread; 827 dev->toread = NULL; 828 spin_unlock_irq(&sh->stripe_lock); 829 while (rbi && rbi->bi_sector < 830 dev->sector + STRIPE_SECTORS) { 831 tx = async_copy_data(0, rbi, dev->page, 832 dev->sector, tx); 833 rbi = r5_next_bio(rbi, dev->sector); 834 } 835 } 836 } 837 838 atomic_inc(&sh->count); 839 init_async_submit(&submit, ASYNC_TX_ACK, tx, ops_complete_biofill, sh, NULL); 840 async_trigger_callback(&submit); 841}
终于见到庐山真面目了,不禁感慨一下代码就是这样裹着一层又一层,就好像神秘的生日礼物一样要拆开一层又一层的包装,又像老胡同巷子走过一道又一道才能找到那个卖酒的店子。但不管怎么样,代码都对你毫无保留的,真诚的。而且越是复杂的代码就越是风情万种、婀娜多姿,前提是你要懂得如何走入她的内心里才能体会得到,等真正体会到的时候你就会拍案叫绝,从而获得征服的快感久久不能忘怀。在征服了这样一个又一个风情万种的代码之后,你的追求就不再局限于肉体之上,转而追求精神上的高度,像欧洲建筑师一样去设计大教堂,然后花个600多年把哥特式的科隆大教堂建好,这才叫艺术。好吧,那个时候你我都已经不在了,但那种精神始终是你我要追求的境界。
823行,我们刚刚完成了对磁盘的读取,这下将读取的数据从缓存区中拷贝到dev->page上,而此时dev->toread也转移到了dev->read。这里先构造了dma的描述符,839和840将请求提交给DMA,在请求完成之后会回调到839传入的参数ops_complete_biofill:
769static void ops_complete_biofill(void *stripe_head_ref) 770{ 771 struct stripe_head *sh = stripe_head_ref; 772 struct bio *return_bi = NULL; 773 int i; 774 775 pr_debug("%s: stripe %llu\n", __func__, 776 (unsigned long long)sh->sector); 777 778 /* clear completed biofills */ 779 for (i = sh->disks; i--; ) { 780 struct r5dev *dev = &sh->dev[i]; 781 782 /* acknowledge completion of a biofill operation */ 783 /* and check if we need to reply to a read request, 784 * new R5_Wantfill requests are held off until 785 * !STRIPE_BIOFILL_RUN 786 */ 787 if (test_and_clear_bit(R5_Wantfill, &dev->flags)) { 788 struct bio *rbi, *rbi2; 789 790 BUG_ON(!dev->read); 791 rbi = dev->read; 792 dev->read = NULL; 793 while (rbi && rbi->bi_sector < 794 dev->sector + STRIPE_SECTORS) { 795 rbi2 = r5_next_bio(rbi, dev->sector); 796 if (!raid5_dec_bi_active_stripes(rbi)) { 797 rbi->bi_next = return_bi; 798 return_bi = rbi; 799 } 800 rbi = rbi2; 801 } 802 } 803 } 804 clear_bit(STRIPE_BIOFILL_RUN, &sh->state); 805 806 return_io(return_bi); 807 808 set_bit(STRIPE_HANDLE, &sh->state); 809 release_stripe(sh); 810}
如果你已经练就了一目十行的火眼睛睛的话,你一定看到了806行的return_io,没错,这就是我之前提到的出口了:
177static void return_io(struct bio *return_bi) 178{ 179 struct bio *bi = return_bi; 180 while (bi) { 181 182 return_bi = bi->bi_next; 183 bi->bi_next = NULL; 184 bi->bi_size = 0; 185 bio_endio(bi, 0); 186 bi = return_bi; 187 } 188}
终于看到bio_endio了吧,happy吧去庆祝喝一杯吧。
狂欢够了吗?接下来有两个思考题:
1)return_bi为什么不是一个bio,而有bi_next?
2)既然return_io结束了,808/809行为什么又要重新加入到处理链表?
转载请注明出处:http://blog.csdn.net/liumangxiong
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。