linux内核的冒险md来源释义# 14raid5非条块读
转载请注明出处: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内核的冒险md来源释义# 14raid5非条块读的更多相关文章

  1. (实例)Linux 内核添加exfat驱动

    背景: 由于exfat是常用的文件系统格式,而Linux由于版权的问题,没有在官方中添加有关的驱动. 但是 微软也同意开源了,所以比较新的 Linux 会支持这一块. 为了支持exfat的驱动,我们需 ...

  2. Linux内核学习趣谈

    本文原创是freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/9304991 从大二开始学习Linux内核,到现在已经 ...

  3. Linux 内核剖解(转)

    Linux 内核剖析(转)  linux内核是一个庞大而复杂的操作系统的核心,不过尽管庞大,但是却采用子系统和分层的概念很好地进行了组织.在本文中,您将探索 Linux 内核的总体结构,并学习一些主要 ...

  4. linux内核(一)基础知识

    1,linux内核的基础知识 1.1 linux内核版本 从内核源码顶层目录Makefile中可以看到: VERSION和PATCHLEVEL组成主版本号,比如2.4.2.5.2.6等,稳定版本的德主 ...

  5. Linux 内核剖析

    https://www.ibm.com/developerworks/cn/linux/l-linux-kernel/ 由于本文的目标是对 Linux 内核进行介绍并探索其体系结构和主要组件,因此首先 ...

  6. linux内核奇遇记之md源代码解读之四

    linux内核奇遇记之md源代码解读之四 转载请注明出处:http://blog.csdn.net/liumangxiong 运行阵列意味着阵列经历从无到有,建立了作为一个raid应有的属性(如同步重 ...

  7. Linux内核配置选项

    http://blog.csdn.net/wdsfup/article/details/52302142 http://www.manew.com/blog-166674-12962.html Gen ...

  8. 陈莉君教授: 回望踏入Linux内核之旅

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 陈莉君 来源: 微信公众号linux阅码场(id: linuxdev) 初次踏入Linux 几多耕耘,几多收获 ...

  9. Linux 内核中的 Device Mapper 机制

    本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...

随机推荐

  1. 容易centos配置docker维修

    首先.由于docker实施需要linux某些组件支持本身和内核特性.所以一定要确保centos版本号大于6,和内核版本号大于2.6.32-431.可轻松升级centos6到最新的版本号. sudo y ...

  2. 查看SQLServer 代理作业的历史信息

    原文:查看SQLServer 代理作业的历史信息 不敢说众所周知,但是大部分人都应该知道SQLServer的代理作业情况都存储在SQLServer5大系统数据库(master/msdb/model/t ...

  3. POJ 1141 区间DP

    给一组小括号与中括号的序列,加入最少的字符,使该序列变为合法序列,输出该合法序列. dp[a][b]记录a-b区间内的最小值, mark[a][b]记录该区间的最小值怎样得到. #include &q ...

  4. Android采用canvas绘制各种图形

    canvas通俗的说就是一个帆布,我们可以用刷子paint,就此随机抽签显卡. 原理: 能够canvas视Surface替代或接口.图形绘制Surface向上.Canvas封装了全部的绘制调用. 通过 ...

  5. 泛泰A900 刷4.4中国民营TWRP2.7.1.1版本 支持自己主动识别移动版本号(世界上第一)

    因本人手上的A900S已砖, 所以临时弄不了ROM了. 先上传之前已经弄好的刷4.4专用的新版TWRP recovery 2.7.1.1  这个版本号是我自己定义的,为差别之前公布的2.7.0.0版( ...

  6. JavaEE(24) - JAAS开发安全的应用

    1. 安全域.角色和用户组 容器提供的两种安全性控制:声明式安全控制和编程式安全控制 安全域是指用户.用户组和ACL的逻辑集合.服务器支持的两种常用安全域:RDBMS安全域和文件系统安全域. 2. J ...

  7. ssh配置连接

    SSH构造: 1.改动vi /etc/ssh/sshd_config,依据模板将要改动的參数凝视去掉并改动參数值: Port 22 指定SSH连接的port号,安全方面不建议使用默认22port Pr ...

  8. Web 前端开发环境

    创建 Web 前端开发环境 Web 前端开发涉及多种工具,这里将常用工具的安装和配置进行说明,提供了详细的说明,为后继的开发创建一个坚实的基础. 本文介绍的工具有:NodeJS, NPM, Bower ...

  9. 创意HTML5文字特效 类似翻页的效果

    原文:创意HTML5文字特效 类似翻页的效果 之前在网上看到一款比较有新意的HTML5文字特效,文字效果是当鼠标滑过是出现翻开折叠的效果,类似书本翻页.于是我兴致勃勃的点开源码看了一下,发现其实实现也 ...

  10. JavaEE(15) - JPA实体继承

    1. 实体继承映射的三种策略 #1. 整个类层次对应一张表 #2. 连接子类 #3. 每个具体类对应一张表 2. 使用抽象实体 3. 使用非实体父类 4. 重定义子类实体的外键列 ---------- ...