linux内核奇遇记之md源代码解读之十raid5数据流之同步数据流程
 转载请注明出处:http://blog.csdn.net/liumangxiong
上一节讲到在raid5的同步函数sync_request中炸土豆片是通过handle_stripe来进行的。从最初的创建阵列,到申请各种资源,建立每个阵列的personality,所有的一切都是为了迎接数据流而作的准备。就像我们寒窗苦读就是为了上大学一样。数据流的过程就像大学校园一样丰富多彩并且富有挑战性,但只要跨过了这道坎,内核代码将不再神秘,剩下的问题只是时间而已。
首先看handle_stripe究竟把我们的土豆片带往何处:
[cpp] view plaincopy
  1. 3379 static void handle_stripe(struct stripe_head *sh)
  2. 3380 {
  3. 3381         struct stripe_head_state s;
  4. 3382         struct r5conf *conf = sh->raid_conf;
  5. 3383         int i;
  6. 3384         int prexor;
  7. 3385         int disks = sh->disks;
  8. 3386         struct r5dev *pdev, *qdev;
  9. 3387
  10. 3388         clear_bit(STRIPE_HANDLE, &sh->state);
  11. 3389         if (test_and_set_bit_lock(STRIPE_ACTIVE, &sh->state)) {
  12. 3390                 /* already being handled, ensure it gets handled
  13. 3391                  * again when current action finishes */
  14. 3392                 set_bit(STRIPE_HANDLE, &sh->state);
  15. 3393                 return;
  16. 3394         }
  17. 3395
  18. 3396         if (test_and_clear_bit(STRIPE_SYNC_REQUESTED, &sh->state)) {
  19. 3397                 set_bit(STRIPE_SYNCING, &sh->state);
  20. 3398                 clear_bit(STRIPE_INSYNC, &sh->state);
  21. 3399         }
  22. 3400         clear_bit(STRIPE_DELAYED, &sh->state);
  23. 3401
  24. 3402         pr_debug("handling stripe %llu, state=%#lx cnt=%d, "
  25. 3403                 "pd_idx=%d, qd_idx=%d\n, check:%d, reconstruct:%d\n",
  26. 3404                (unsigned long long)sh->sector, sh->state,
  27. 3405                atomic_read(&sh->count), sh->pd_idx, sh->qd_idx,
  28. 3406                sh->check_state, sh->reconstruct_state);
  29. 3407
  30. 3408         analyse_stripe(sh, &s);
这个函数代码比较长先贴第一部分,分析条带。分析的作用就是根据条带的状态做一些预处理,根据这些状态再来判断下一步应该做什么具体操作。比如说同步,那么首先会读数据盘,等读回来之后,再校验,然后再写校验值。但是这些步骤又不是一次性在handle_stripe里就完成的,因为跟磁盘IO都是异步的,所以必要要等上一次磁盘请求回调之后再次调用handle_stripe,通常每个数据流都会多次进入handle_stripe,而每一次进入经过的代码流程是不大一样的。
struct stripe_head有很多状态,这些状态决定条带应该怎么处理,所以必须非常小心处理这些标志,这些标志很多,现在先简单地过一下。
[cpp] view plaincopy
  1. enum {
  2. STRIPE_ACTIVE,   // 正在处理
  3. STRIPE_HANDLE,  // 需要处理
  4. STRIPE_SYNC_REQUESTED,  // 同步请求
  5. STRIPE_SYNCING,  // 正在处理同步
  6. STRIPE_INSYNC,  // 条带已同步
  7. STRIPE_PREREAD_ACTIVE,  // 预读
  8. STRIPE_DELAYED,  // 延迟处理
  9. STRIPE_DEGRADED,  // 降级
  10. STRIPE_BIT_DELAY,  // 等待bitmap处理
  11. STRIPE_EXPANDING,  //
  12. STRIPE_EXPAND_SOURCE,  //
  13. STRIPE_EXPAND_READY,  //
  14. STRIPE_IO_STARTED,     /* do not count towards 'bypass_count' */   // IO已下发
  15. STRIPE_FULL_WRITE,     /* all blocks are set to be overwritten */  // 满写
  16. STRIPE_BIOFILL_RUN,  // bio填充,就是将page页拷贝到bio
  17. STRIPE_COMPUTE_RUN,  // 运行计算
  18. STRIPE_OPS_REQ_PENDING,  // handle_stripe排队用
  19. STRIPE_ON_UNPLUG_LIST,  // 批量release_stripe时标识是否加入unplug链表
  20. };
3388行,清除需要处理标志。
3389行,设置正在处理标志。
3392行,如果已经在处理则设置下次处理标志并返回。
3396行,如果是同步请求。
3397行,设置正在处理同步标志。
3398行,清除已同步标志。
3400行,清除延迟处理标志。
3408行,分析stripe,这个函数很长分几段来说明:
[cpp] view plaincopy
  1. 3198 static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
  2. 3199 {
  3. 3200         struct r5conf *conf = sh->raid_conf;
  4. 3201         int disks = sh->disks;
  5. 3202         struct r5dev *dev;
  6. 3203         int i;
  7. 3204         int do_recovery = 0;
  8. 3205
  9. 3206         memset(s, 0, sizeof(*s));
  10. 3207
  11. 3208         s->expanding = test_bit(STRIPE_EXPAND_SOURCE, &sh->state);
  12. 3209         s->expanded = test_bit(STRIPE_EXPAND_READY, &sh->state);
  13. 3210         s->failed_num[0] = -1;
  14. 3211         s->failed_num[1] = -1;
  15. 3212
  16. 3213         /* Now to look around and see what can be done */
  17. 3214         rcu_read_lock();
数据初始化和加锁,接着看:
[cpp] view plaincopy
  1. 3215         for (i=disks; i--; ) {
  2. 3216                 struct md_rdev *rdev;
  3. 3217                 sector_t first_bad;
  4. 3218                 int bad_sectors;
  5. 3219                 int is_bad = 0;
  6. 3220
  7. 3221                 dev = &sh->dev[i];
  8. 3222
  9. 3223                 pr_debug("check %d: state 0x%lx read %p write %p written %p\n",
  10. 3224                          i, dev->flags,
  11. 3225                          dev->toread, dev->towrite, dev->written);
接着是一个大循环,循环次数是数据盘的个数,循环的对象的3221行的dev,dev的类型是struct r5dev,那我们先来看一看这个结构,这个结构是嵌套在struct stripe_head里面的:
[cpp] view plaincopy
  1. struct r5dev {
  2. /* rreq and rvec are used for the replacement device when
  3. * writing data to both devices.
  4. */
  5. struct bio     req, rreq;
  6. struct bio_vec     vec, rvec;
  7. struct page     *page;
  8. struct bio     *toread, *read, *towrite, *written;
  9. sector_t     sector;               /* sector of this page */
  10. unsigned long     flags;
  11. } dev[1]; /* allocated with extra space depending of RAID geometry */
首先看注释,rreq 和rvec由replacement设备在写数据时使用。r就是replacement的简写,replacement是什么意思呢?就是原数据盘的替代,replacement是最近几个版本里才引入的特性,在实际产品中这个特性很重要,具体实现后面会讲到。page是缓存页,通常用于运行计算,接着几个bio是读写bio头指针。sector是条带对应的物理扇区位置。flags是struct r5dev的标志。
[cpp] view plaincopy
  1. 3226                 /* maybe we can reply to a read
  2. 3227                  *
  3. 3228                  * new wantfill requests are only permitted while
  4. 3229                  * ops_complete_biofill is guaranteed to be inactive
  5. 3230                  */
  6. 3231                 if (test_bit(R5_UPTODATE, &dev->flags) && dev->toread &&
  7. 3232                     !test_bit(STRIPE_BIOFILL_RUN, &sh->state))
  8. 3233                         set_bit(R5_Wantfill, &dev->flags);
  9. 3234
  10. 3235                 /* now count some things */
  11. 3236                 if (test_bit(R5_LOCKED, &dev->flags))
  12. 3237                         s->locked++;
  13. 3238                 if (test_bit(R5_UPTODATE, &dev->flags))
  14. 3239                         s->uptodate++;
  15. 3240                 if (test_bit(R5_Wantcompute, &dev->flags)) {
  16. 3241                         s->compute++;
  17. 3242                         BUG_ON(s->compute > 2);
  18. 3243                 }
  19. 3244
  20. 3245                 if (test_bit(R5_Wantfill, &dev->flags))
  21. 3246                         s->to_fill++;
  22. 3247                 else if (dev->toread)
  23. 3248                         s->to_read++;
  24. 3249                 if (dev->towrite) {
  25. 3250                         s->to_write++;
  26. 3251                         if (!test_bit(R5_OVERWRITE, &dev->flags))
  27. 3252                                 s->non_overwrite++;
  28. 3253                 }
  29. 3254                 if (dev->written)
  30. 3255                         s->written++;
3231行,什么样的r5dev要设置R5_Wantfill标志呢?已更新、有读请求、不在拷贝过程。这又是什么意思呢?就是说需要的数据已经为最新了,这时只要把数据从page拷贝到bio就可以了。
3236行,统计加锁磁盘数
3238行,统计已最新磁盘数
3240行,统计需要计算的磁盘数
3245行,统计需要拷贝操作磁盘数
3247行,统计需要读的磁盘数
3249行,统计需要写的磁盘数
3251行,统计满写的磁盘数
3254行,统计已下发写的磁盘数
[cpp] view plaincopy
  1. 3256                 /* Prefer to use the replacement for reads, but only
  2. 3257                  * if it is recovered enough and has no bad blocks.
  3. 3258                  */
  4. 3259                 rdev = rcu_dereference(conf->disks[i].replacement);
  5. 3260                 if (rdev && !test_bit(Faulty, &rdev->flags) &&
  6. 3261                     rdev->recovery_offset >= sh->sector + STRIPE_SECTORS &&
  7. 3262                     !is_badblock(rdev, sh->sector, STRIPE_SECTORS,
  8. 3263                                  &first_bad, &bad_sectors))
  9. 3264                         set_bit(R5_ReadRepl, &dev->flags);
  10. 3265                 else {
  11. 3266                         if (rdev)
  12. 3267                                 set_bit(R5_NeedReplace, &dev->flags);
  13. 3268                         rdev = rcu_dereference(conf->disks[i].rdev);
  14. 3269                         clear_bit(R5_ReadRepl, &dev->flags);
  15. 3270                 }
  16. 3271                 if (rdev && test_bit(Faulty, &rdev->flags))
  17. 3272                         rdev = NULL;
  18. 3273                 if (rdev) {
  19. 3274                         is_bad = is_badblock(rdev, sh->sector, STRIPE_SECTORS,
  20. 3275                                              &first_bad, &bad_sectors);
  21. 3276                         if (s->blocked_rdev == NULL
  22. 3277                             && (test_bit(Blocked, &rdev->flags)
  23. 3278                                 || is_bad < 0)) {
  24. 3279                                 if (is_bad < 0)
  25. 3280                                         set_bit(BlockedBadBlocks,
  26. 3281                                                 &rdev->flags);
  27. 3282                                 s->blocked_rdev = rdev;
  28. 3283                                 atomic_inc(&rdev->nr_pending);
  29. 3284                         }
  30. 3285                 }
3256行,优先读重建过并没有坏扇区的replacement盘
3264行,读replacement盘
3267行,写replacement盘
3271行,坏盘
3273行,检查坏扇区
3286行,初始化dev状态
3300行,没有坏扇区,设置同步标志
3312行,写错误处理
3325行,数据盘修复处理
3336行,replacement盘修复处理
3352行,记录不同步盘
3360行,判断同步还是重构replacement盘
到此analyse_stripe就结束了,那么对于同步来说,这个函数做了哪些事情呢?就只是设置了s.syncing=1而已,所以不要看这个函数那么长,每一次进来做的事情却很少。
继续返回到handle_stripe函数中,中间不执行的代码先跳过,然后就会执行到这里:
[cpp] view plaincopy
  1. 3468         /* Now we might consider reading some blocks, either to check/generate
  2. 3469          * parity, or to satisfy requests
  3. 3470          * or to load a block that is being partially written.
  4. 3471          */
  5. 3472         if (s.to_read || s.non_overwrite
  6. 3473             || (conf->level == 6 && s.to_write && s.failed)
  7. 3474             || (s.syncing && (s.uptodate + s.compute < disks))
  8. 3475             || s.replacing
  9. 3476             || s.expanding)
  10. 3477                 handle_stripe_fill(sh, &s, disks);
3468行,这里是准备读磁盘,在生成校验、读写请求时都有可能读磁盘
3474行,在analyse_stripe中设置了syncing标志,所以这里满足这个条件,进入handle_stripe_fill函数。
[cpp] view plaincopy
  1. 2707 /**
  2. 2708  * handle_stripe_fill - read or compute data to satisfy pending requests.
  3. 2709  */
  4. 2710 static void handle_stripe_fill(struct stripe_head *sh,
  5. 2711                                struct stripe_head_state *s,
  6. 2712                                int disks)
  7. 2713 {
  8. 2714         int i;
  9. 2715
  10. 2716         /* look for blocks to read/compute, skip this if a compute
  11. 2717          * is already in flight, or if the stripe contents are in the
  12. 2718          * midst of changing due to a write
  13. 2719          */
  14. 2720         if (!test_bit(STRIPE_COMPUTE_RUN, &sh->state) && !sh->check_state &&
  15. 2721             !sh->reconstruct_state)
  16. 2722                 for (i = disks; i--; )
  17. 2723                         if (fetch_block(sh, s, i, disks))
  18. 2724                                 break;
  19. 2725         set_bit(STRIPE_HANDLE, &sh->state);
  20. 2726 }
2720行,如果已经在计算、校验或重建状态,则不需要再读磁盘
2722行,循环每一个r5dev看是否需要读磁盘
跟进fetch_block函数:
2618 /* fetch_block - checks the given member device to see if its data needs
2619  * to be read or computed to satisfy a request.
2620  *
2621  * Returns 1 when no more member devices need to be checked, otherwise returns
2622  * 0 to tell the loop in handle_stripe_fill to continue
2623  */
查看指定设置是否有必要读入数据,返回1表示剩余设置不需要检查了,返回0表示需要继续检查剩余的设置。
[cpp] view plaincopy
  1. 2624 static int fetch_block(struct stripe_head *sh, struct stripe_head_state *s,
  2. 2625                        int disk_idx, int disks)
  3. 2626 {
  4. 2627         struct r5dev *dev = &sh->dev[disk_idx];
  5. 2628         struct r5dev *fdev[2] = { &sh->dev[s->failed_num[0]],
  6. 2629                                   &sh->dev[s->failed_num[1]] };
  7. 2630
  8. 2631         /* is the data in this block needed, and can we get it? */
  9. 2632         if (!test_bit(R5_LOCKED, &dev->flags) &&
  10. 2633             !test_bit(R5_UPTODATE, &dev->flags) &&
  11. 2634             (dev->toread ||
  12. 2635              (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) ||
  13. 2636              s->syncing || s->expanding ||
  14. 2637              (s->replacing && want_replace(sh, disk_idx)) ||
  15. 2638              (s->failed >= 1 && fdev[0]->toread) ||
  16. 2639              (s->failed >= 2 && fdev[1]->toread) ||
  17. 2640              (sh->raid_conf->level <= 5 && s->failed && fdev[0]->towrite &&
  18. 2641               !test_bit(R5_OVERWRITE, &fdev[0]->flags)) ||
  19. 2642              (sh->raid_conf->level == 6 && s->failed && s->to_write))) {
  20. 2643                 /* we would like to get this block, possibly by computing it,
  21. 2644                  * otherwise read it if the backing disk is insync
  22. 2645                  */
  23. 2646                 BUG_ON(test_bit(R5_Wantcompute, &dev->flags));
  24. 2647                 BUG_ON(test_bit(R5_Wantread, &dev->flags));
  25. 2648                 if ((s->uptodate == disks - 1) &&
  26. 2649                     (s->failed && (disk_idx == s->failed_num[0] ||
  27. 2650                                    disk_idx == s->failed_num[1]))) {
  28. 2651                         /* have disk failed, and we're requested to fetch it;
  29. 2652                          * do compute it
  30. 2653                          */
  31. 2654                         pr_debug("Computing stripe %llu block %d\n",
  32. 2655                                (unsigned long long)sh->sector, disk_idx);
  33. 2656                         set_bit(STRIPE_COMPUTE_RUN, &sh->state);
  34. 2657                         set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request);
  35. 2658                         set_bit(R5_Wantcompute, &dev->flags);
  36. 2659                         sh->ops.target = disk_idx;
  37. 2660                         sh->ops.target2 = -1; /* no 2nd target */
  38. 2661                         s->req_compute = 1;
  39. 2662                         /* Careful: from this point on 'uptodate' is in the eye
  40. 2663                          * of raid_run_ops which services 'compute' operations
  41. 2664                          * before writes. R5_Wantcompute flags a block that will
  42. 2665                          * be R5_UPTODATE by the time it is needed for a
  43. 2666                          * subsequent operation.
  44. 2667                          */
  45. 2668                         s->uptodate++;
  46. 2669                         return 1;
  47. 2670                 } else if (s->uptodate == disks-2 && s->failed >= 2) {
  48. 2671                         /* Computing 2-failure is *very* expensive; only
  49. 2672                          * do it if failed >= 2
  50. 2673                          */
  51. 2674                         int other;
  52. 2675                         for (other = disks; other--; ) {
  53. 2676                                 if (other == disk_idx)
  54. 2677                                         continue;
  55. 2678                                 if (!test_bit(R5_UPTODATE,
  56. 2679                                       &sh->dev[other].flags))
  57. 2680                                         break;
  58. 2681                         }
  59. 2682                         BUG_ON(other < 0);
  60. 2683                         pr_debug("Computing stripe %llu blocks %d,%d\n",
  61. 2684                                (unsigned long long)sh->sector,
  62. 2685                                disk_idx, other);
  63. 2686                         set_bit(STRIPE_COMPUTE_RUN, &sh->state);
  64. 2687                         set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request);
  65. 2688                         set_bit(R5_Wantcompute, &sh->dev[disk_idx].flags);
  66. 2689                         set_bit(R5_Wantcompute, &sh->dev[other].flags);
  67. 2690                         sh->ops.target = disk_idx;
  68. 2691                         sh->ops.target2 = other;
  69. 2692                         s->uptodate += 2;
  70. 2693                         s->req_compute = 1;
  71. 2694                         return 1;
  72. 2695                 } else if (test_bit(R5_Insync, &dev->flags)) {
  73. 2696                         set_bit(R5_LOCKED, &dev->flags);
  74. 2697                         set_bit(R5_Wantread, &dev->flags);
  75. 2698                         s->locked++;
  76. 2699                         pr_debug("Reading block %d (sync=%d)\n",
  77. 2700                                 disk_idx, s->syncing);
  78. 2701                 }
  79. 2702         }
  80. 2703
  81. 2704         return 0;
  82. 2705 }
从这个函数进入,我们拥有的仅仅是s.syncing这张牌,那么这张牌在这里能不能发挥作用呢?
2632行,判断是否需要读设置
2636行,很明显地,这个判断为真,因为s.syncing==1,其他判断暂且不看
2648行,当前设置都未读入,所以s->uptodate==0
2670行,同上也不成立 
2695行,真正执行到的是这个分支
2696行,设置设备加锁标志
2697行,设置设备准备读标志
2698行,递增本条带加锁设备数
handle_stripe函数执行完成,条带的每个struct r5dev都被设置了R5_Wantread标志。在接下来handle_stripe就会调用ops_run_io函数去读:
[cpp] view plaincopy
  1. 3673         ops_run_io(sh, &s);

我们再跟进这个函数,为了突出重点,这里只列出跟同步相关的代码:

[cpp] view plaincopy
  1. 537 static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
  2. 538 {
  3. 539         struct r5conf *conf = sh->raid_conf;
  4. 540         int i, disks = sh->disks;
  5. 541
  6. 542         might_sleep();
  7. 543
  8. 544         for (i = disks; i--; ) {
  9. 545                 int rw;
  10. 546                 int replace_only = 0;
  11. 547                 struct bio *bi, *rbi;
  12. 548                 struct md_rdev *rdev, *rrdev = NULL;
  13. ...
  14. 554                 } else if (test_and_clear_bit(R5_Wantread, &sh->dev[i].flags))
  15. 555                         rw = READ;
  16. ...
  17. 560                 } else
  18. 561                         continue;
  19. 564
  20. 565                 bi = &sh->dev[i].req;
  21. 566                 rbi = &sh->dev[i].rreq; /* For writing to replacement */
  22. 567
  23. 568                 bi->bi_rw = rw;
  24. 569                 rbi->bi_rw = rw;
  25. 570                 if (rw & WRITE) {
  26. 573                 } else
  27. 574                         bi->bi_end_io = raid5_end_read_request;
  28. 575
  29. 576                 rcu_read_lock();
  30. 577                 rrdev = rcu_dereference(conf->disks[i].replacement);
  31. 578                 smp_mb(); /* Ensure that if rrdev is NULL, rdev won't be */
  32. 579                 rdev = rcu_dereference(conf->disks[i].rdev);
  33. 580                 if (!rdev) {
  34. 581                         rdev = rrdev;
  35. 582                         rrdev = NULL;
  36. 583                 }
  37. ...
  38. 598                 if (rdev)
  39. 599                         atomic_inc(&rdev->nr_pending);
  40. ...
  41. 604                 rcu_read_unlock();
  42. ...
  43. 643                 if (rdev) {
  44. 644                         if (s->syncing || s->expanding || s->expanded
  45. 645                             || s->replacing)
  46. 646                                 md_sync_acct(rdev->bdev, STRIPE_SECTORS);
  47. 647
  48. 648                         set_bit(STRIPE_IO_STARTED, &sh->state);
  49. 649
  50. 650                         bi->bi_bdev = rdev->bdev;
  51. 651                         pr_debug("%s: for %llu schedule op %ld on disc %d\n",
  52. 652                                 __func__, (unsigned long long)sh->sector,
  53. 653                                 bi->bi_rw, i);
  54. 654                         atomic_inc(&sh->count);
  55. 655                         if (use_new_offset(conf, sh))
  56. 656                                 bi->bi_sector = (sh->sector
  57. 657                                                  + rdev->new_data_offset);
  58. 658                         else
  59. 659                                 bi->bi_sector = (sh->sector
  60. 660                                                  + rdev->data_offset);
  61. 661                         if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags))
  62. 662                                 bi->bi_rw |= REQ_FLUSH;
  63. 663
  64. 664                         bi->bi_flags = 1 << BIO_UPTODATE;
  65. 665                         bi->bi_idx = 0;
  66. 666                         bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
  67. 667                         bi->bi_io_vec[0].bv_offset = 0;
  68. 668                         bi->bi_size = STRIPE_SIZE;
  69. 669                         bi->bi_next = NULL;
  70. 670                         if (rrdev)
  71. 671                                 set_bit(R5_DOUBLE_LOCKED, &sh->dev[i].flags);
  72. 672                         generic_make_request(bi);
  73. 673                 }
  74. ...
  75. 709         }
  76. 710 }
542行,函数可能休眠
544行,遍历每一个r5dev
554行,设置读标志
568行,设置bio为读
574行,设置bio回调函数为raid5_end_read_request,这里将是下发读请求之后代码继续执行的入口点。
598行,增加设备nr_pending
646行,统计信息
648行,设置IO下发标志
650行,设置bio设备为对应的磁盘设备
654行,增加stripe_head引用计数
655-660行,设置新的扇区数,需要加上磁盘上的数据偏移
661行,如果为NoMerge读,则设置bio REQ_FLUSH标志
664行,接着设置bio其他域
672行,下发bio到磁盘
在磁盘执行完读请求的时候,raid5_end_read_request被调用:
[cpp] view plaincopy
  1. 1710 static void raid5_end_read_request(struct bio * bi, int error)
  2. 1711 {
  3. ...
  4. 1824         rdev_dec_pending(rdev, conf->mddev);
  5. 1825         clear_bit(R5_LOCKED, &sh->dev[i].flags);
  6. 1826         set_bit(STRIPE_HANDLE, &sh->state);
  7. 1827         release_stripe(sh);
  8. 1828 }
在这个函数中,清除了R5_LOCKED标志,并重新将stripe_head加入处理。经过raid5d中转,重新调用到handle_stripe函数,这一次调用时在analyse_stripe函数中递增s->uptodate,所有数据盘都递增1,所以s->uptodate等于数据盘。接着handle_tripe函数到达:
[cpp] view plaincopy
  1. 3528     if (sh->check_state ||
  2. 3529         (s.syncing && s.locked == 0 &&
  3. 3530          !test_bit(STRIPE_COMPUTE_RUN, &sh->state) &&
  4. 3531          !test_bit(STRIPE_INSYNC, &sh->state))) {
  5. 3532          if (conf->level == 6)
  6. 3533               handle_parity_checks6(conf, sh, &s, disks);
  7. 3534          else
  8. 3535               handle_parity_checks5(conf, sh, &s, disks);
  9. 3536     }
进入3535行进行校验,进入handle_parity_check5函数:
[cpp] view plaincopy
  1. 2881     switch (sh->check_state) {
  2. 2882     case check_state_idle:
  3. 2883          /* start a new check operation if there are no failures */
  4. 2884          if (s->failed == 0) {
  5. 2885               BUG_ON(s->uptodate != disks);
  6. 2886               sh->check_state = check_state_run;
  7. 2887               set_bit(STRIPE_OP_CHECK, &s->ops_request);
  8. 2888               clear_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags);
  9. 2889               s->uptodate--;
  10. 2890               break;
  11. 2891          }
2881行,check_state为0,进入2882行分支
2886行,设置check_state_run状态
2887行,设置STRIPE_OP_CHECK操作
2889行,递减s->uptodate
由于这里设置了STRIPE_OP_CHECK操作,所以在handle_stripe会调用到raid_run_ops,进而会调用到:
[cpp] view plaincopy
  1. 1412     if (test_bit(STRIPE_OP_CHECK, &ops_request)) {
  2. 1413          if (sh->check_state == check_state_run)
  3. 1414               ops_run_check_p(sh, percpu);

ops_run_check_p校验条带是否同步,对应的回调函数为:

[cpp] view plaincopy
  1. 1301static void ops_complete_check(void *stripe_head_ref)
  2. 1302{
  3. 1303     struct stripe_head *sh = stripe_head_ref;
  4. 1304
  5. 1305     pr_debug("%s: stripe %llu\n", __func__,
  6. 1306          (unsigned long long)sh->sector);
  7. 1307
  8. 1308     sh->check_state = check_state_check_result;
  9. 1309     set_bit(STRIPE_HANDLE, &sh->state);
  10. 1310     release_stripe(sh);
  11. 1311}
第1308行将状态设置为check_state_check_result,条带继续又重新加入到handle_list。handle_stripe再一次调用到handle_parity_check5函数,但这一次check_state==check_state_check_result:
[cpp] view plaincopy
  1. 2916     case check_state_check_result:
  2. 2917          sh->check_state = check_state_idle;
  3. 2918
  4. 2919          /* if a failure occurred during the check operation, leave
  5. 2920          * STRIPE_INSYNC not set and let the stripe be handled again
  6. 2921          */
  7. 2922          if (s->failed)
  8. 2923               break;
  9. 2924
  10. 2925          /* handle a successful check operation, if parity is correct
  11. 2926          * we are done.  Otherwise update the mismatch count and repair
  12. 2927          * parity if !MD_RECOVERY_CHECK
  13. 2928          */
  14. 2929          if ((sh->ops.zero_sum_result & SUM_CHECK_P_RESULT) == 0)
  15. 2930               /* parity is correct (on disc,
  16. 2931               * not in buffer any more)
  17. 2932               */
  18. 2933               set_bit(STRIPE_INSYNC, &sh->state);
  19. 2934          else {
  20. 2935               conf->mddev->resync_mismatches += STRIPE_SECTORS;
  21. 2936               if (test_bit(MD_RECOVERY_CHECK, &conf->mddev->recovery))
  22. 2937                    /* don't try to repair!! */
  23. 2938                    set_bit(STRIPE_INSYNC, &sh->state);
  24. 2939               else {
  25. 2940                    sh->check_state = check_state_compute_run;
  26. 2941                    set_bit(STRIPE_COMPUTE_RUN, &sh->state);
  27. 2942                    set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request);
  28. 2943                    set_bit(R5_Wantcompute,
  29. 2944                         &sh->dev[sh->pd_idx].flags);
  30. 2945                    sh->ops.target = sh->pd_idx;
  31. 2946                    sh->ops.target2 = -1;
  32. 2947                    s->uptodate++;
  33. 2948               }
  34. 2949          }
  35. 2950          break;
2929行,如果校验的结果是同步的
2933行,直接设置条带为同步的,不需要进行其他任何操作了
2934行,如果条带不同步
2940行,设置check_state为check_state_compute_run
2942行,ops_request 为STRIPE_OP_COMPUTE_BLK,即准备计算校验
2943行,计算目标为条带校验盘
2947行,由于之前计算校验时uptodate递减,这里恢复
如果条带已经同步了,那么带着STRIPE_INSYNC标志我们来到了handle_stripe:
[cpp] view plaincopy
  1. 3550         if ((s.syncing || s.replacing) && s.locked == 0 &&
  2. 3551             test_bit(STRIPE_INSYNC, &sh->state)) {
  3. 3552                 md_done_sync(conf->mddev, STRIPE_SECTORS, 1);
  4. 3553                 clear_bit(STRIPE_SYNCING, &sh->state);
  5. 3554         }

如果条带未同步,那带着STRIPE_OP_COMPUTE_BLK标志来到了raid_run_ops函数,该函数调用__raid_run_ops:

[cpp] view plaincopy
  1. 1383     if (test_bit(STRIPE_OP_COMPUTE_BLK, &ops_request)) {
  2. 1384          if (level < 6)
  3. 1385               tx = ops_run_compute5(sh, percpu);

最终调用ops_run_compute5函数计算出条带中校验盘的值,该函数回调函数ops_complete_compute:

[cpp] view plaincopy
  1. 856static void ops_complete_compute(void *stripe_head_ref)
  2. 857{
  3. 858     struct stripe_head *sh = stripe_head_ref;
  4. 859
  5. 860     pr_debug("%s: stripe %llu\n", __func__,
  6. 861          (unsigned long long)sh->sector);
  7. 862
  8. 863     /* mark the computed target(s) as uptodate */
  9. 864     mark_target_uptodate(sh, sh->ops.target);
  10. 865     mark_target_uptodate(sh, sh->ops.target2);
  11. 866
  12. 867     clear_bit(STRIPE_COMPUTE_RUN, &sh->state);
  13. 868     if (sh->check_state == check_state_compute_run)
  14. 869          sh->check_state = check_state_compute_result;
  15. 870     set_bit(STRIPE_HANDLE, &sh->state);
  16. 871     release_stripe(sh);
  17. 872}
864行,设置校验盘dev为R5_UPTODATE
869行,由于handle_parity_check5中设置为check_state_compute_run,这里继续设置为check_state_compute_result
870行,设置处理标志,在871之后再一次进入handle_stripe
当再一次进入handle_stripe函数,又再一次来到handle_parity_check5函数,由于这次是check_state_compute_result标志:
[cpp] view plaincopy
  1. 2894     case check_state_compute_result:
  2. 2895          sh->check_state = check_state_idle;
  3. 2896          if (!dev)
  4. 2897               dev = &sh->dev[sh->pd_idx];
  5. 2898
  6. 2899          /* check that a write has not made the stripe insync */
  7. 2900          if (test_bit(STRIPE_INSYNC, &sh->state))
  8. 2901               break;
  9. 2902
  10. 2903          /* either failed parity check, or recovery is happening */
  11. 2904          BUG_ON(!test_bit(R5_UPTODATE, &dev->flags));
  12. 2905          BUG_ON(s->uptodate != disks);
  13. 2906
  14. 2907          set_bit(R5_LOCKED, &dev->flags);
  15. 2908          s->locked++;
  16. 2909          set_bit(R5_Wantwrite, &dev->flags);
  17. 2910
  18. 2911          clear_bit(STRIPE_DEGRADED, &sh->state);
  19. 2912          set_bit(STRIPE_INSYNC, &sh->state);
  20. 2913          break;
我们可以一眼看2912行设置了STRIPE_INSYNC标志,那么也意味着条带同步的结束。但是也别高兴得太早,回头看却有2908行s->locked++,同步结束的判断条件之一就是s->locked==0,所以在同步结束之前我们还有一件事情要做,2909行设置了R5_Wantwrite标志就是告诉我们需要调用一次ops_run_io将刚才计算的校验值写入条带的校验盘中,再写成功再返回时就会满足同步结束的条件了。就这样,一次简单的同步过程就完成了。

 转载请注明出处:http://blog.csdn.net/liumangxiong

linux内核奇遇记之md源代码解读之十raid5数据流之同步数据流程相关推荐

  1. linux内核奇遇记之md源代码解读之十二raid读写

    linux内核奇遇记之md源代码解读之十二raid读写 转载请注明出处:http://blog.csdn.net/liumangxiong 我们都知道,对一个linux块设备来说,都有一个对应的请求队 ...

  2. linux内核奇遇记之md源代码解读之八阵列同步二

    linux内核奇遇记之md源代码解读之八阵列同步二 转载请注明出处:http://blog.csdn.net/liumangxiong 在上一小节里讲到启动同步线程: 7824 mddev->s ...

  3. 复制linux内核,linux内核写时复制机制源代码解读

    作者简介 写时复制技术(一下简称COW)是linux内核比较重要的一种机制,我们都知道:父进程fork子进程的时候,子进程会和父进程会以只读的方式共享所有私有的可写页,当有一方将要写的时候会发生COW ...

  4. 《Linux内核设计与实现》读书笔记(十九)- 可移植性

    linux内核的移植性非常好, 目前的内核也支持非常多的体系结构(有20多个). 但是刚开始时, linux也只支持 intel i386 架构, 从 v1.2版开始支持 Digital Alpha, ...

  5. 高通linux内核目录,高通 android 源代码以及目标系统目录结构

    下面为高通android源代码结构 build/ – Build 环境建立和makefiles生成4 bionic/ – Android C 库 dalvik/ – Android Java 虚拟机 ...

  6. linux内核页高速缓存,《Linux内核设计与实现》读书笔记(十六)- 页高速缓存和页回写(示例代码)...

    主要内容: 缓存简介 页高速缓存 页回写 1. 缓存简介 在编程中,缓存是很常见也很有效的一种提高程序性能的机制. linux内核也不例外,为了提高I/O性能,也引入了缓存机制,即将一部分磁盘上的数据 ...

  7. 《Linux内核设计与实现》读书笔记(十)- 内核同步方法【转】

    转自:http://www.cnblogs.com/wang_yb/archive/2013/05/01/3052865.html 内核中提供了多种方法来防止竞争条件,理解了这些方法的使用场景有助于我 ...

  8. 《Linux内核设计与实现》读书笔记(十四)- 块I/O层

    最近太忙,居然过了2个月才更新第十四章.... 主要内容: 块设备简介 内核访问块设备的方法 内核I/O调度程序 1. 块设备简介 I/O设备主要有2类: 字符设备:只能顺序读写设备中的内容,比如 串 ...

  9. 《Linux内核设计与实现》读书笔记(十八)- 内核调试

    内核调试的难点在于它不能像用户态程序调试那样打断点,随时暂停查看各个变量的状态. 也不能像用户态程序那样崩溃后迅速的重启,恢复初始状态. 用户态程序和内核交互,用户态程序的各种状态,错误等可以由内核来 ...

  10. 《Linux内核设计与实现》读书笔记(十六)- 页高速缓存和页回写

    主要内容: 缓存简介 页高速缓存 页回写 1. 缓存简介 在编程中,缓存是很常见也很有效的一种提高程序性能的机制. linux内核也不例外,为了提高I/O性能,也引入了缓存机制,即将一部分磁盘上的数据 ...

最新文章

  1. 使用C++ stringstream来进行数据类型转换
  2. python函数可选参数传递_Python中函数的参数传递
  3. 实战SSM_O2O商铺_23【商铺列表】Controller层开发
  4. calibre中的hcell_关于calibre的Hcell你知道多少?
  5. Windows API一日一练(2)使用应用程序句柄
  6. Javascript 动态修改select方法大全【转】
  7. 我终于知道post和get的区别
  8. 5月份Github上最热门的数据科学和机器学习项目
  9. 四大招让无处不在的工作空间成为可能?揭秘Ivanti 的战略布局
  10. vue-router路由安装与使用
  11. 的正确使用_如何正确使用隔离霜
  12. 简单的网页编辑器js代码
  13. 微信网页授获取code
  14. 企业要如何利用360评估法做好人才盘点?
  15. 区分计算机网络和互联网的概念,网络的概念,网络与互联网的区别
  16. Candence学习篇(5)使用Padstack Editor制作贴片焊盘和通孔焊盘
  17. 大数据面试常见问题(三)——Hadoop部分
  18. 区块链再度走入沉寂期,下一个撬动行业的支点会在哪里?
  19. Android Studio Dolphin 稳定版正式发布
  20. mac下如何配搭建配置自己的svn

热门文章

  1. 转:开源项目学习方法ABC
  2. Smith Builder ERP代码生成器(开源,提供源码下载)
  3. 大型网站架构演变和知识体系【转载】
  4. [书目20080630]人一生要养成的50个习惯
  5. 【内购篇】5 分钟教你成为会赚钱的独立开发者
  6. FireEye实验室在一次水坑式攻击中发现IE 0DAY
  7. css实现在一行显示多余部分显示省略号
  8. 判断登陆权限的操作,登录后调到之前所操作的地址。
  9. SQL 删除数据-select在当前表字段作为条件
  10. 移动搜索入口争夺提速