linux内核的冒险md来源释义# 14raid5非条块读

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

版权声明:本文博客原创文章。博客,未经同意,不得转载。

转载于:https://www.cnblogs.com/mengfanrong/p/4713262.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/290697.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为record类型自定义Equals方法

前言record类型&#xff0c;这是一种新引用类型&#xff0c;而不是类或结构。record与类不同&#xff0c;区别在于record类型使用基于值的相等性。例如&#xff1a;public record DemoRecord(int id);public class DemoClass {public DemoClass(int id){this.id id;}public in…

解决IDEA修改已有项目为maven项目时目录结构被改变的问题

Idea可以在项目根目录上右键选择“添加框架支持”&#xff0c;选择maven&#xff0c;为项目添加Maven支持。 但这样会导致原有项目的目录结构被破坏。 更好的方法是在根目录添加pom.xml文件 在<build>标签内添加 <sourceDirectory>标签&#xff0c;并填入源码根目…

C语言之获取32字节随机数的字符串

1、问题 获取32字节随机数的字符串 2、代码实现 #include <stdio.h> #include <time.h> #include <stdlib.h>#define SIZE 32void get_rand(char *p, int length) { char value[10] "0123456789";srand(time(NULL));for (int i 0; i < leng…

排序——选择排序

选择排序 作者&#xff1a;上品物语 知识点&#xff1a; 原理示意图算法特点复杂度1.1 原理 首先&#xff0c;找到数组中最小的那个元素&#xff0c;其次&#xff0c;将它和数组的第一个元素交换位置&#xff08;如果第一个元素就是最小元素&#xff0c;那么它就和自己交换&…

《Java线程与并发编程实践》—— 2.3 谨防活跃性问题

本节书摘来异步社区《Java线程与并发编程实践》一书中的第2章&#xff0c;第2.3节&#xff0c;作者&#xff1a; 【美】Jeff Friesen&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看。 2.3 谨防活跃性问题 活跃性这个词代表着某件正确的事情最终会发生。活跃…

.Net/C#分库分表高性能O(1)瀑布流分页

框架介绍依照惯例首先介绍本期主角:ShardingCore 一款ef-core下高性能、轻量级针对分表分库读写分离的解决方案&#xff0c;具有零依赖、零学习成本、零业务代码入侵dotnet下唯一一款全自动分表,多字段分表框架,拥有高性能,零依赖、零学习成本、零业务代码入侵,并且支持读写分离…

centos静默安装oracle关于报错UnsatisfiedLinkError exception loading native library:njni10

静默安装oracle时&#xff0c;日志文件中打印出如下语句 提示Oracle NetConfiguration Assistant failed&#xff0c;原因是找不到libaio.so.1 这是缺少依赖 执行指令 yum -y install libaio* libaio-devel* 删除home文件夹&#xff0c;再次执行 ./runInstaller -silent…

Ant—使用Ant构建一个简单的Java工程(两)

博客《Ant—使用Ant构建一个简单的Java项目&#xff08;一&#xff09;》演示了使用Ant工具构建简单的Java项目&#xff0c;接着这个样例来进一步学习Ant&#xff1a;上面样例须要运行多条ant命令才干运行Test类中的main函数&#xff0c;能不能简化须要运行命令呢&#xff1f;答…

C++之函数指针实现函数回调

1、问题 C++很多地方需要函数指针回调,但是我理解还是不够深刻,今天再写了测试例子,方便后面更深入理解和记忆。 2、代码实现 A.h 文件实现 // // A.h // TestC++ // // Created by 1111 on 17/8/17. // Copyright © 2017年 sangfor. All rights reserved. //#…

JMS的样例

1、JMS是一个由AS提供的Message服务。它能接受消息产生者(Message Provider)所发出的消息&#xff0c;并把消息转发给消息消费者(Message Consumer)。2、JMS提供2种类型的消息服务&#xff1a;(1)Queue&#xff0c;即点对点&#xff0c;每一个消息仅仅转发给一个消息消费者使用…

《面向对象的思考过程(原书第4版)》一1.11 组合

本节书摘来自华章出版社《面向对象的思考过程&#xff08;原书第4版&#xff09;》一书中的第1章&#xff0c;第1.11节&#xff0c;&#xff3b;美&#xff3d; 马特魏斯费尔德&#xff08;Matt Weisfeld&#xff09; 著黄博文 译更多章节内容可以访问云栖社区“华章计算机”…

Hadoop 新增删除节点

1 新增Data节点 1.1 修改/etc/hosts&#xff0c;增加datanode的ip 1.2 在新增加的节点启动服务 hadoop-daemon.sh start datanode yarn-daemon.sh start nodemanager 1.3 均衡block start-balancer.sh 1&#xff09;如果不balance&#xff0c;那么cluster会把新的数据都存放在…

Java Thread Status(转)

public static enum Thread.State extends Enum<Thread.State>线程状态。线程可以处于下列状态之一&#xff1a; 1.NEW 至今尚未启动的线程的状态。 2.RUNNABLE 可运行线程的线程状态。 处于可运行状态的某一线程正在 Java 虚拟机中运行&#xff0c;但它可能正在…

秀!微软《550页图解.Net+WPF完整版》.pdf 附下载!

赶紧看看微软公司基于最新的.netwpf编写文档。对于零基础可以作为.net的快速入门教材&#xff0c;对于高级程序员而言&#xff0c;这也是你的充电之路&#xff01;NO.1资料介绍该手册,全面的介绍.net的新特性&#xff0c;看完这个资料&#xff0c;你能够对.net/c#WPF的新功能&a…

Android之编译jni出错解决办法

1、问题 1&#xff09;、我编写的类一开始都能编译&#xff0c;后来编译不过 2&#xff09;、undefined reference to Qt::value 2、解决办法 1 )、我在写Android.mk的时候这样注释了下面的代码 LOCAL_SRC_FILES : \A.cpp \B.cpp \# C.cpp \D.cpp \这样导致找不到D…

《移动App测试的22条军规》—第1章1.2节移动App的生命周期

本节书摘来自异步社区《移动App测试的22条军规》一书中的第1章&#xff0c;第1.2节移动App的生命周期&#xff0c;作者黄勇&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.2 移动App的生命周期移动App测试的22条军规&#xff08;1&#xff09;对于还处于…

C语言之常见错误解决办法

1、问题 1)、忘记写const char* p string.c_str()char* p string.data(); 自己傻逼了&#xff0c;编译不过&#xff0c;应该这样写&#xff0c;不要忘记加上const const char* p string.c_str();const char* p string.data(); 2&#xff09;、const char*p 转 char* p co…

电子商务应用课程知识整理 第一章-电子商务概述与类型

一、电子商务定义 电子商务&#xff08;Electronic Commerce&#xff0c;简称EC&#xff09;&#xff0c;是指在全球各地广泛的商业贸易活动中&#xff0c;在因特网开放的网络环境下&#xff0c;基于客户端&#xff08;浏览器、移动端&#xff09;/服务器&#xff0c;买卖双方…

nodejs中文件,目录的操作(1)

首先&#xff0c;我们对fs文件系统分为两类操作&#xff0c;第一类是xxx方法&#xff0c;第二类是xxxSync方法。所有的fs操作几乎都是这两类&#xff0c;第一类是异步回调&#xff0c;第二类是同步等待。 A.对于文件的读写操作 a.完整性读写 1.fs.readFile(filename,[options],…

如何制作自己的静态库

如何制作自己的静态库 将一些不想暴露给使用方的实现代码打包成.a库&#xff08;比如&#xff1a;百度地图sdk,写sdk的时候&#xff0c;需要使用到&#xff09; 需要创建一个静态库的工程&#xff0c;来实现代码逻辑&#xff0c;并完成对代码的打包&#xff08;.a库&#xff09…