zoukankan      html  css  js  c++  java
  • 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
    ",
    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
    ", __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
    ", __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

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

  • 相关阅读:
    @SuppressWarnings("resource")
    连续根据两个字段排序
    java.sql.SQLException: ORA-00604: 递归 SQL 级别 1 出现错误
    java中数组的定义
    单表(多表需手动创建多个转换)插入,更新数据
    批量处理sql
    查询排序后前5名的信息
    面向对象详细
    Flask-SQLAlchemy
    Dbutils-数据库连接池
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4713262.html
Copyright © 2011-2022 走看看