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

linux内核奇遇记之md源代码解读之八阵列同步二
转载请注明出处:http://blog.csdn.net/liumangxiong
在上一小节里讲到启动同步线程:
[cpp]  view plain  copy
  1. 7824                         mddev->sync_thread = md_register_thread(md_do_sync,  
  2. 7825                                                                 mddev,  
  3. 7826                                                                 "resync");  
md_register_thread函数如下:
   
   
  1. 6697 struct md_thread *md_register_thread(void (*run) (struct mddev *), struct mddev *mddev,  
  2. 6698                                  const char *name)  
  3. 6699 {  
  4. 6700         struct md_thread *thread;  
  5. 6701   
  6. 6702         thread = kzalloc(sizeof(struct md_thread), GFP_KERNEL);  
  7. 6703         if (!thread)  
  8. 6704                 return NULL;  
  9. 6705   
  10. 6706         init_waitqueue_head(&thread->wqueue);  
  11. 6707   
  12. 6708         thread->run = run;  
  13. 6709         thread->mddev = mddev;  
  14. 6710         thread->timeout = MAX_SCHEDULE_TIMEOUT;  
  15. 6711         thread->tsk = kthread_run(md_thread, thread,  
  16. 6712                                   "%s_%s",  
  17. 6713                                   mdname(thread->mddev),  
  18. 6714                                   name);  
  19. 6715         if (IS_ERR(thread->tsk)) {  
  20. 6716                 kfree(thread);  
  21. 6717                 return NULL;  
  22. 6718         }  
  23. 6719         return thread;  
  24. 6720 }  
我相信所有拿过程序员证书,北大青鸟证书的哥们看这些代码是轻而易举,然而我没上过这些培训学校,也没有拿过程序员证,实在是惭愧啊。这在很大程度上拖了广大技术人员的后腿,于是心里十分忐忑,特别是上海火灾是临时工所为,火车票系统出错是程序员无证上岗所为。想想在学校时老师教育我们:难道你们四年的学习都比不上一张证书,老师四年的培养都比不上一张程序员证吗?当时准备报名考试的我顿时就羞愧难当了。然而社会就是社会从来都没有哪次求职说要程序员证。但最怕的还是有关部门,哪天都有可能被抓去判个无证上岗。
这个函数有两个看点:
6706行,初始化等待队列,在此等待队列上休眠的线程正是md_thread,那又是谁来唤醒的呢?唤醒的函数都叫wakeup,那就find symbol看一下有没有叫md wakeup的函数,果真有md_wakeup_thread()函数。所以下次看到这个函数的时候就知道轮到线程处理啦。
6711行,创建一个线程,先关心一下线程的名字,是md名和作用名的结合。当这里执行完成之后,在用户态ps一下就能看到这个线程了。除了线程名字,我们还关心这个线程做什么?运行的是md_thread()函数,这个函数只是提供了一个线程运行模板,真正做的事情是函数传进来的run函数。回到7824行,我们知道同步真正做事情的是md_do_sync。
于是我们就跟进md_do_sync函数:
[cpp]  view plain  copy
  1. 7245 #define SYNC_MARKS      10  
  2. 7246 #define SYNC_MARK_STEP  (3*HZ)  
  3. 7247 void md_do_sync(struct mddev *mddev)  
  4. 7248 {  
  5. 7249         struct mddev *mddev2;  
  6. 7250         unsigned int currspeed = 0,  
  7. 7251                  window;  
  8. 7252         sector_t max_sectors,j, io_sectors;  
  9. 7253         unsigned long mark[SYNC_MARKS];  
  10. 7254         sector_t mark_cnt[SYNC_MARKS];  
  11. 7255         int last_mark,m;  
  12. 7256         struct list_head *tmp;  
  13. 7257         sector_t last_check;  
  14. 7258         int skipped = 0;  
  15. 7259         struct md_rdev *rdev;  
  16. 7260         char *desc;  
  17. 7261         struct blk_plug plug;  
  18. 7262   
  19. 7263         /* just incase thread restarts... */  
  20. 7264         if (test_bit(MD_RECOVERY_DONE, &mddev->recovery))  
  21. 7265                 return;  
  22. 7266         if (mddev->ro) /* never try to sync a read-only array */  
  23. 7267                 return;  
  24. 7268   
  25. 7269         if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {  
  26. 7270                 if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))  
  27. 7271                         desc = "data-check";  
  28. 7272                 else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))  
  29. 7273                         desc = "requested-resync";  
  30. 7274                 else  
  31. 7275                         desc = "resync";  
  32. 7276         } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))  
  33. 7277                 desc = "reshape";  
  34. 7278         else  
  35. 7279                 desc = "recovery";  

7264行,检查同步是否完成,再次友情提醒,这里的同步是指广义上的同步。
7266行,只读阵列就不要同步了。
7269行之后,设置线程打印信息。
7279-7345行,是用磁盘分区创建的阵列同步互斥用的。商业化的阵列没有必要用磁盘分区做阵列的,所以直接跳过。
[cpp]  view plain  copy
  1. 7346         j = 0;  
  2. 7347         if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {  
  3. 7348                 /* resync follows the size requested by the personality, 
  4. 7349                  * which defaults to physical size, but can be virtual size 
  5. 7350                  */  
  6. 7351                 max_sectors = mddev->resync_max_sectors;  
  7. 7352                 mddev->resync_mismatches = 0;  
  8. 7353                 /* we don't use the checkpoint if there's a bitmap */  
  9. 7354                 if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))  
  10. 7355                         j = mddev->resync_min;  
  11. 7356                 else if (!mddev->bitmap)  
  12. 7357                         j = mddev->recovery_cp;  
  13. 7358   
  14. 7359         } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))  
  15. 7360                 max_sectors = mddev->resync_max_sectors;  
  16. 7361         else {  
  17. 7362                 /* recovery follows the physical size of devices */  
  18. 7363                 max_sectors = mddev->dev_sectors;  
  19. 7364                 j = MaxSector;  
  20. 7365                 rcu_read_lock();  
  21. 7366                 rdev_for_each_rcu(rdev, mddev)  
  22. 7367                         if (rdev->raid_disk >= 0 &&  
  23. 7368                             !test_bit(Faulty, &rdev->flags) &&  
  24. 7369                             !test_bit(In_sync, &rdev->flags) &&  
  25. 7370                             rdev->recovery_offset < j)  
  26. 7371                                 j = rdev->recovery_offset;  
  27. 7372                 rcu_read_unlock();  
  28. 7373         }  
  29. 7374   
  30. 7375         printk(KERN_INFO "md: %s of RAID array %s\n", desc, mdname(mddev));  
  31. 7376         printk(KERN_INFO "md: minimum _guaranteed_  speed:"  
  32. 7377                 " %d KB/sec/disk.\n", speed_min(mddev));  
  33. 7378         printk(KERN_INFO "md: using maximum available idle IO bandwidth "  
  34. 7379                "(but not more than %d KB/sec) for %s.\n",  
  35. 7380                speed_max(mddev), desc);  
  36. 7381   
  37. 7382         is_mddev_idle(mddev, 1); /* this initializes IO event counters */  
7347行,是同步。
7348行,同步默认是physical size,也可以是virtual size。如果你第一次阅读就能明白其中的意思,那么恭喜你是一个内核天才。如果我这一次讲完你能看懂,那么恭喜你是一个内核人才。如果看不懂也没有多大关系,毕竟大多数人都只是想混混日子而已,只要有一颗向上努力的心,始终都有市场的。想当初我也是抱着赵炯博士的linux内核完全注释足足看了七遍,整本书都已经被我笔记得体无完肤了,但是仍然只是一知半解。所以看不懂没有关系,但是要把握两点:一是要把握方法,看懂原理挑重点看,二是多动手修改几行代码试试,并且持之以恒。
那什么是physical size,什么是virtual size?物理大小就是单个磁盘用于创建阵列空间的大小,虚拟大小就是阵列大小。怎么样,终于相信自己是内核天才了吧!那为什么同步要有这样的区别呢?这就要跟阵列的特性相关了,raid5阵列是属于前者,按磁盘从头到尾同步,raid10阵列是属于后者,是按照镜像对进行同步的。
7351行,所以对于不同阵列,max_sectors代表不同的含义。
接下来是reshape和重建,跳过。
7375行,打印阵列同步信息。
7376-7380行,打印同步速度信息。同步有速度控制是为了不影响正常数据流。
7382行,初始化rdev->last_events。函数is_mddev_idle用于控制同步速度,当一小段时间内IO太多时会休眠来降低同步速度。
[cpp]  view plain  copy
  1. 7384         io_sectors = 0;  
  2. 7385         for (m = 0; m < SYNC_MARKS; m++) {  
  3. 7386                 mark[m] = jiffies;  
  4. 7387                 mark_cnt[m] = io_sectors;  
  5. 7388         }  
  6. 7389         last_mark = 0;  
  7. 7390         mddev->resync_mark = mark[last_mark];  
  8. 7391         mddev->resync_mark_cnt = mark_cnt[last_mark];  
  9. 7392   
  10. 7393         /* 
  11. 7394          * Tune reconstruction: 
  12. 7395          */  
  13. 7396         window = 32*(PAGE_SIZE/512);  
  14. 7397         printk(KERN_INFO "md: using %dk window, over a total of %lluk.\n",  
  15. 7398                 window/2, (unsigned long long)max_sectors/2);  
  16. 7399   
  17. 7400         atomic_set(&mddev->recovery_active, 0);  
  18. 7401         last_check = 0;  
  19. 7402   
  20. 7403         if (j>2) {  
  21. 7404                 printk(KERN_INFO   
  22. 7405                        "md: resuming %s of %s from checkpoint.\n",  
  23. 7406                        desc, mdname(mddev));  
  24. 7407                 mddev->curr_resync = j;  
  25. 7408         }  
  26. 7409         mddev->curr_resync_completed = j;  

7385-7391行,同步点记录的初始化。这里设置了几个观察点,用几个观察点之间下发的数据流速度来控制同步线程。
7396行,设置窗口大小,数据流大小这个窗口大小才进入观察点。
7400行,下发但未返回请求的大小。
7403行,继续同步的。
7409行,设置同步完成点。
[cpp]  view plain  copy
  1. 7411         blk_start_plug(&plug);  
  2. 7412         while (j < max_sectors) {  
  3. 7413                 sector_t sectors;  
  4. 7414   
  5. 7415                 skipped = 0;  
  6. 7416   
  7. 7417                 if (!test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&  
  8. 7418                     ((mddev->curr_resync > mddev->curr_resync_completed &&  
  9. 7419                       (mddev->curr_resync - mddev->curr_resync_completed)  
  10. 7420                       > (max_sectors >> 4)) ||  
  11. 7421                      (j - mddev->curr_resync_completed)*2  
  12. 7422                      >= mddev->resync_max - mddev->curr_resync_completed  
  13. 7423                             )) {  
  14. 7424                         /* time to update curr_resync_completed */  
  15. 7425                         wait_event(mddev->recovery_wait,  
  16. 7426                                    atomic_read(&mddev->recovery_active) == 0);  
  17. 7427                         mddev->curr_resync_completed = j;  
  18. 7428                         set_bit(MD_CHANGE_CLEAN, &mddev->flags);  
  19. 7429                         sysfs_notify(&mddev->kobj, NULL, "sync_completed");  
  20. 7430                 }  
  21. 7431   
  22. 7432                 while (j >= mddev->resync_max && !kthread_should_stop()) {  
  23. 7433                         /* As this condition is controlled by user-space, 
  24. 7434                          * we can block indefinitely, so use '_interruptible' 
  25. 7435                          * to avoid triggering warnings. 
  26. 7436                          */  
  27. 7437                         flush_signals(current); /* just in case */  
  28. 7438                         wait_event_interruptible(mddev->recovery_wait,  
  29. 7439                                                  mddev->resync_max > j  
  30. 7440                                                  || kthread_should_stop());  
  31. 7441                 }  
  32. 7442   
  33. 7443                 if (kthread_should_stop())  
  34. 7444                         goto interrupted;  
  35. 7445   
  36. 7446                 sectors = mddev->pers->sync_request(mddev, j, &skipped,  
  37. 7447                                                   currspeed < speed_min(mddev));  
  38. 7448                 if (sectors == 0) {  
  39. 7449                         set_bit(MD_RECOVERY_INTR, &mddev->recovery);  
  40. 7450                         goto out;  
  41. 7451                 }  
  42. 7452   
  43. 7453                 if (!skipped) { /* actual IO requested */  
  44. 7454                         io_sectors += sectors;  
  45. 7455                         atomic_add(sectors, &mddev->recovery_active);  
  46. 7456                 }  
  47. 7457   
  48. 7458                 if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))  
  49. 7459                         break;  
  50. 7460   
  51. 7461                 j += sectors;  
  52. 7462                 if (j>1) mddev->curr_resync = j;  
  53. 7463                 mddev->curr_mark_cnt = io_sectors;  
  54. 7464                 if (last_check == 0)  
  55. 7465                         /* this is the earliest that rebuild will be 
  56. 7466                          * visible in /proc/mdstat 
  57. 7467                          */  
  58. 7468                         md_new_event(mddev);  
  59. 7469   
  60. 7470                 if (last_check + window > io_sectors || j == max_sectors)  
  61. 7471                         continue;  
  62. 7472   
  63. 7473                 last_check = io_sectors;  
  64. 7474         repeat:  
  65. 7475                 if (time_after_eq(jiffies, mark[last_mark] + SYNC_MARK_STEP )) {  
  66. 7476                         /* step marks */  
  67. 7477                         int next = (last_mark+1) % SYNC_MARKS;  
  68. 7478   
  69. 7479                         mddev->resync_mark = mark[next];  
  70. 7480                         mddev->resync_mark_cnt = mark_cnt[next];  
  71. 7481                         mark[next] = jiffies;  
  72. 7482                         mark_cnt[next] = io_sectors - atomic_read(&mddev->recovery_active);  
  73. 7483                         last_mark = next;  
  74. 7484                 }  
  75. 7485   
  76. 7486   
  77. 7487                 if (kthread_should_stop())  
  78. 7488                         goto interrupted;  
  79. 7489   
  80. 7490   
  81. 7491                 /* 
  82. 7492                  * this loop exits only if either when we are slower than 
  83. 7493                  * the 'hard' speed limit, or the system was IO-idle for 
  84. 7494                  * a jiffy. 
  85. 7495                  * the system might be non-idle CPU-wise, but we only care 
  86. 7496                  * about not overloading the IO subsystem. (things like an 
  87. 7497                  * e2fsck being done on the RAID array should execute fast) 
  88. 7498                  */  
  89. 7499                 cond_resched();  
  90. 7500   
  91. 7501                 currspeed = ((unsigned long)(io_sectors-mddev->resync_mark_cnt))/2  
  92. 7502                         /((jiffies-mddev->resync_mark)/HZ +1) +1;  
  93. 7503   
  94. 7504                 if (currspeed > speed_min(mddev)) {  
  95. 7505                         if ((currspeed > speed_max(mddev)) ||  
  96. 7506                                         !is_mddev_idle(mddev, 0)) {  
  97. 7507                                 msleep(500);  
  98. 7508                                 goto repeat;  
  99. 7509                         }  
  100. 7510                 }  
  101. 7511         }  
  102. 7512         printk(KERN_INFO "md: %s: %s done.\n",mdname(mddev), desc);  

这个循环真是长啊,为了保持完整性还是全部放在这样了。
7411行,这个函数背后还真有故事,不过是属于块层的。详细说明可参考我的另一篇博文:http://blog.csdn.net/liumangxiong/article/details/10279089
7412行,同步点小于最大同步值。
7417行,并非reshape
7418行,当前同步点大于当前同步完成位置
7419行,已下发未返回同步大小大于十六分之一总同步大小
7421行,已下发未返回同步大小*2大于等于同步检查点-当前同步完成点
这几行是什么意思呢?如果你坚持看完了前面两行并没有头晕的症状,那么恭喜你身体状况很好可以去玩跳伞等刺激活动。这几行的意思是说下发的同步请求太多了,超过这些阀值,需要停下来等待请求返回,并保存新的完成同步点。
7425行,等待同步请求返回。同步请求是按顺序下发的,但是底层的块设备不一定按原顺序完成,所以需要等待确认都返回。
7427行,保存新的同步完成点。
7428行,设置改变标志。
7429行,更新sysfs同步点。
7432行,由用户指定同步最大值,可以用于数据流很大时停止同步或其他类似用途。
7443行,设置了线程停止标志
7445行,调用pers的sync_request,每种阵列具体同步操作,这个在后面讲解
7448行,没有同步
7449行,设置同步中断标志
7453行,skipped表示bitmap认为是已同步条带,所以直接跳过。这里表示不能跳过
7454行,累计真实IO大小
7455行,累计下发同步IO大小
7461行,递增当前同步点
7462行,更新mddev当前同步点
7463行,统计用
7464行,更新/proc/stat显示
7470行,上次观察点以来下发同步IO不足窗口大小则继续下发同步请求
7475行,至少SYNC_MARK_STEP时间建立一个观察点
7501行,计算这次观察点的速度
7504行,如果小于最小速度则继续同步
7505行,如果大于最大速度或者非idle则短暂休眠再同步
7512行,看到done很开心,表示同步完成了
抛开同步具体的数据流不管,md_do_sync就只是一个简单的控制器用于控制同步的推进。
1)下发同步请求
2)记录同步观察点
3)同步速度太快则休眠
4)超过阀值,等待所有请求返回,更新同步完成点
5)转到步骤1)继续同步
继续往下看,同步完成之后还有一些事情要处理,说直接点就是要保存同步结果。
[cpp]  view plain  copy
  1. 7518         wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active));  
  2. 7519   
  3. 7520         /* tell personality that we are finished */  
  4. 7521         mddev->pers->sync_request(mddev, max_sectors, &skipped, 1);  
  5. 7522   
  6. 7523         if (!test_bit(MD_RECOVERY_CHECK, &mddev->recovery) &&  
  7. 7524             mddev->curr_resync > 2) {  
  8. 7525                 if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {  
  9. 7526                         if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {  
  10. 7527                                 if (mddev->curr_resync >= mddev->recovery_cp) {  
  11. 7528                                         printk(KERN_INFO  
  12. 7529                                                "md: checkpointing %s of %s.\n",  
  13. 7530                                                desc, mdname(mddev));  
  14. 7531                                         mddev->recovery_cp =  
  15. 7532                                                 mddev->curr_resync_completed;  
  16. 7533                                 }  
  17. 7534                         } else  
  18. 7535                                 mddev->recovery_cp = MaxSector;  
  19. 7536                 } else {  
  20. 7537                         if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery))  
  21. 7538                                 mddev->curr_resync = MaxSector;  
  22. 7539                         rcu_read_lock();  
  23. 7540                         rdev_for_each_rcu(rdev, mddev)  
  24. 7541                                 if (rdev->raid_disk >= 0 &&  
  25. 7542                                     mddev->delta_disks >= 0 &&  
  26. 7543                                     !test_bit(Faulty, &rdev->flags) &&  
  27. 7544                                     !test_bit(In_sync, &rdev->flags) &&  
  28. 7545                                     rdev->recovery_offset < mddev->curr_resync)  
  29. 7546                                         rdev->recovery_offset = mddev->curr_resync;  
  30. 7547                         rcu_read_unlock();  
  31. 7548                 }  
  32. 7549         }  
  33. 7550  skip:  
  34. 7551         set_bit(MD_CHANGE_DEVS, &mddev->flags);  
  35. 7552   
  36. 7553         if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {  
  37. 7554                 /* We completed so min/max setting can be forgotten if used. */  
  38. 7555                 if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))  
  39. 7556                         mddev->resync_min = 0;  
  40. 7557                 mddev->resync_max = MaxSector;  
  41. 7558         } else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))  
  42. 7559                 mddev->resync_min = mddev->curr_resync_completed;  
  43. 7560         mddev->curr_resync = 0;  
  44. 7561         wake_up(&resync_wait);  
  45. 7562         set_bit(MD_RECOVERY_DONE, &mddev->recovery);  
  46. 7563         md_wakeup_thread(mddev->thread);  
  47. 7564         return;  

7518行,等待所有同步请求返回
7521行,根据同步结果更新bitmap,回收资源
7525行,如果同步中断则设置recovery_cp为同步完成点,正常完成则设置为MaxSector。
7551行,设置改变状态
7553-7560行,恢复同步值
7561行,唤醒同磁盘分区同步等待线程
7562行,设置同步完成标志
7563行,唤醒主线程。
如果到这里就认为同步完成了,那就大错特错了。记得有一句话讲,每一个阶段的结束就是下一个阶段的起点。看到md_wake_up我们就想到事情又有了一个新起点。
在唤醒主线程之后,主线程会调用上一小节中的md_check_recovery来清理现场,最终调用到7774行的reap_sync_thread函数。对于同步来说,这个函数做了以下事情:
1)回收同步线程
2)更新超级块
3)更新mddev标志
其实阵列的同步很简单,下一小节讲raid5同步过程sync_request函数。
转载请注明出处:http://blog.csdn.net/liumangxiong
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值