linux内核的冒险md来源释义# 14raid5非条块读
2015-08-08 15:38
603 查看
linux内核的冒险md来源释义# 14raid5非条块读
转载请注明出处:http://blog.csdn.net/liumangxiong
假设是非条块内读。那么就至少涉及到两个条块的读,这就须要分别从这两个条块内读出数据。然后再凑成整个结果返回给上层。接下来我们将看到怎样将一个完整的bio读请求拆分成多个子请求下发到磁盘,从磁盘返回之后再又一次组合成请求结果返回给上层的。
首先计算请求起始位置,由于md下发到磁盘数据请求的最小单位为STRIPE_SECTORS,所以这里要将请求对齐。计算出请求起始位置为logical_sector ,结束位置为last_sector。4100行复用bi_phys_segments 用于计数下发条带数,这里防止意外释放先设置为1。
在这个循环中将请求拆分个多个条带,分别下发命令。
在处理条带的时候还须要做到相互排斥。不能有两个线程在同一时候操作同一个条带。
比方说同步线程在同步这个条带,raid5d在写这个条带,那么就会产生非预期的结果。
4103行。等待队列用于条带訪问相互排斥
4108行,增加等待队列
4135行。依据阵列逻辑扇区计算出磁盘物理偏移扇区,并计算相应的数据盘号和校验盘号
4142行,依据磁盘物理偏移扇区获取一个条带
在第一次看这段代码的时候。因为太匆忙全然没有找到重点在哪里。就像一个人在喧嚣的城市里长大,因为被城市的外表所迷惑全然不知道内心真正想追求的生活。当真正静下心来看的时候。最终发现最重要的一句在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:
回到handle_stripe函数中:
to_read触发了handle_stripe_fill,这个函数的作用就是设置须要读取的标志:
接着又来到了ops_run_io,将读请求下发到磁盘。读请求的回调函数为raid5_end_read_request:
这个函数做了两件事情。一是设置了R5_UPTODATE标志,还有一是调用了release_stripe又一次将条带送回了handle_stripe处理。
带着R5_UPTODATE标志进入了analyse_stripe函数:
在3255行设置了R5_Wantfill标志。在3246行设置了to_fill,再次回来handle_stripe:
条带状态设置了STRIPE_OP_BIOFILL,仅仅要设置了s.ops_request。就必须立即知道这个域相应的处理函数为raid_run_ops,实际操作在__raid_run_ops:
相应的处理函数是ops_run_biofill:
最终见到庐山真面目了,不禁感慨一下代码就是这样裹着一层又一层,就好像神奇的生日礼物一样要拆开一层又一层的包装,又像老胡同巷子走过一道又一道才干找到那个卖酒的店子。但无论怎么样,代码都对你毫无保留的。真诚的。
并且越是复杂的代码就越是风情万种、婀娜多姿,前提是你要懂得怎样走入她的内心里才干体会得到。等真正体会到的时候你就会拍案叫绝,从而获得征服的快感久久不能忘怀。在征服了这样一个又一个风情万种的代码之后。你的追求就不再局限于肉体之上,转而追求精神上的高度,像欧洲建筑师一样去设计大教堂,然后花个600多年把哥特式的科隆大教堂建好,这才叫艺术。
好吧,那个时候你我都已经不在了,但那种精神始终是你我要追求的境地。
823行,我们刚刚完毕了对磁盘的读取,这下将读取的数据从缓存区中复制到dev->page上,而此时dev->toread也转移到了dev->read。这里先构造了dma的描写叙述符,839和840将请求提交给DMA,在请求完毕之后会回调到839传入的參数ops_complete_biofill:
假设你已经练就了一目十行的火眼睛睛的话,你一定看到了806行的return_io,没错。这就是我之前提到的出口了:
最终看到bio_endio了吧,happy吧去庆祝喝一杯吧。
狂欢够了吗?接下来有两个思考题:
1)return_bi为什么不是一个bio。而有bi_next?
2)既然return_io结束了。808/809行为什么又要又一次增加到处理链表?
转载请注明出处:http://blog.csdn.net/liumangxiong
转载请注明出处: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++;
回到handle_stripe函数中:
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
相关文章推荐
- 新手的linux之旅 五、安装IE浏览器
- Linux操作系统-标准IO库(4)
- Linux操作系统-标准IO库(3)
- Linux操作系统-标准IO库(2)
- 关于linux下.tar.dz的包如何解压
- study linux mutithread
- ARM+linux环境下程序的开发和烧录
- linux下查看.so文件
- Linux下静态编译glib
- 多重引导系统启动流程
- linux修改/etc/hosts的hostName和修改后不生效的问题?
- 如何通过反向 SSH 隧道访问 NAT 后面的 Linux 服务器
- linux基础编程
- Centos6.4下安装protobuf及简单使用
- 64位linux报错Could not initialize class java.awt.image.BufferedImage
- svn高级使用->linux下修改冲突
- linux上传下载(scp篇)
- 简单的Linux 驱动模块编译,加载过程
- 如何编译一个linux下的驱动模块(1)
- linux中安装软件