linux内核奇遇记之md源代码解读之七阵列同步一
转载请注明出处:http://blog.csdn.net/liumangxiong
阵列同步在md_do_sync,那么入口在哪里呢?就是说阵列同步触发点在哪里呢?听说过md_check_recovery吧,但这还不是同步的入口点。那raid5d函数是入口点吧?如果要认真分析起来还不算是。
真正的同步入口点在do_md_run函数,就是在运行阵列run函数之后,有这么一行:
[cpp] view plaincopy
  1. 5171         md_wakeup_thread(mddev->thread);
是这一行把raid5d唤醒的,raid5d函数如下:
[cpp] view plaincopy
  1. 4823 static void raid5d(struct md_thread *thread)
  2. 4824 {
  3. 4825         struct mddev *mddev = thread->mddev;
  4. 4826         struct r5conf *conf = mddev->private;
  5. 4827         int handled;
  6. 4828         struct blk_plug plug;
  7. 4829
  8. 4830         pr_debug("+++ raid5d active\n");
  9. 4831
  10. 4832         md_check_recovery(mddev);
4832行,顾名思义就是检查同步,说明这里只是同步的检查点,不是真正处理同步的地方。
raid5d剩余部分是处理数据流的地方先不看,跟进md_check_recovery,先看注释:
[cpp] view plaincopy
  1. 7672 /*
  2. 7673  * This routine is regularly called by all per-raid-array threads to
  3. 7674  * deal with generic issues like resync and super-block update.
  4. 7675  * Raid personalities that don't have a thread (linear/raid0) do not
  5. 7676  * need this as they never do any recovery or update the superblock.
  6. 7677  *
  7. 7678  * It does not do any resync itself, but rather "forks" off other threads
  8. 7679  * to do that as needed.
  9. 7680  * When it is determined that resync is needed, we set MD_RECOVERY_RUNNING in
  10. 7681  * "->recovery" and create a thread at ->sync_thread.
  11. 7682  * When the thread finishes it sets MD_RECOVERY_DONE
  12. 7683  * and wakeups up this thread which will reap the thread and finish up.
  13. 7684  * This thread also removes any faulty devices (with nr_pending == 0).
  14. 7685  *
  15. 7686  * The overall approach is:
  16. 7687  *  1/ if the superblock needs updating, update it.
  17. 7688  *  2/ If a recovery thread is running, don't do anything else.
  18. 7689  *  3/ If recovery has finished, clean up, possibly marking spares active.
  19. 7690  *  4/ If there are any faulty devices, remove them.
  20. 7691  *  5/ If array is degraded, try to add spares devices
  21. 7692  *  6/ If array has spares or is not in-sync, start a resync thread.
  22. 7693  */
这个函数通常由阵列主线程调用,用于处理同步和超级块更新等事件。没有主线程的阵列不需要调用(如线性/raid0),因为这些阵列不需要重建或更新超级块。
这个函数并不做具体事宜,只是按需启动阵列同步线程。
当阵列需要同步时,设置MD_RECOVERY_RUNNING标志,并创建同步线程。
当同步线程结束时设置MD_RECOVERY_DONE标志,并唤醒主线程回收同步线程并结束同步。
这个线程还用于移除坏盘(当nr_pending==0时)。
这个函数处理过程如下:
1、需要时更新超级块
2、同步线程运行时就返回
3、同步线程结束时,回收资源,如果是重建完成则激活热备盘
4、移除坏盘
5、对降级阵列添加热备盘
6、有热备盘但阵列还不是同步状态,则启动同步线程
看完了以上的注释,我已经泪流满面了,因为写代码的哥们太敬业了,把所有精华都已经说出来了,害得像我这种写个博文已经没有什么可写的了。还好我写博文只是自娱自乐,如果是拿这个当饭碗还不早就喝西北风了。
说归说,还是得一行行阅读代码:
[cpp] view plaincopy
  1. 7694 void md_check_recovery(struct mddev *mddev)
  2. 7695 {
  3. 7696         if (mddev->suspended)
  4. 7697                 return;
  5. 7698
  6. 7699         if (mddev->bitmap)
  7. 7700                 bitmap_daemon_work(mddev);
  8. 7701
  9. 7702         if (signal_pending(current)) {
  10. 7703                 if (mddev->pers->sync_request && !mddev->external) {
  11. 7704                         printk(KERN_INFO "md: %s in immediate safe mode\n",
  12. 7705                                mdname(mddev));
  13. 7706                         mddev->safemode = 2;
  14. 7707                 }
  15. 7708                 flush_signals(current);
  16. 7709         }
  17. 7710
  18. 7711         if (mddev->ro && !test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
  19. 7712                 return;
  20. 7713         if ( ! (
  21. 7714                 (mddev->flags & ~ (1<<MD_CHANGE_PENDING)) ||
  22. 7715                 test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
  23. 7716                 test_bit(MD_RECOVERY_DONE, &mddev->recovery) ||
  24. 7717                 (mddev->external == 0 && mddev->safemode == 1) ||
  25. 7718                 (mddev->safemode == 2 && ! atomic_read(&mddev->writes_pending)
  26. 7719                  && !mddev->in_sync && mddev->recovery_cp == MaxSector)
  27. 7720                 ))
  28. 7721                 return;
7696行,检查阵列是否挂起。阵列挂起是一个管理命令,挂起时可以将IO流hold住。
7699行,bitmap清理操作,等bitmap小节再讲。
7702行,接收到信号,进入safemode。
7711行,只读阵列并且未设置检查标志则返回。
7713行,只要一个条件满足,就继续检查,否则返回。
7714行,阵列状态发生改变,则继续检查。
7715行,设置了需要检查标志,则继续检查。
7716行,同步完成,则继续检查。
7717行,安全模式且非external,则继续检查。
7718行,safemode为2有两种情况,一是系统重启时,二是7768行接收到信号时,第一种情况时in_sync为1,第二种情况可以触发更新超级块,根据in_sync标志写回磁盘resync_offset等等。
[cpp] view plaincopy
  1. 7723         if (mddev_trylock(mddev)) {
  2. 7724                 int spares = 0;
  3. ...
  4. 7746                 if (!mddev->external) {
  5. 7747                         int did_change = 0;
  6. 7748                         spin_lock_irq(&mddev->write_lock);
  7. 7749                         if (mddev->safemode &&
  8. 7750                             !atomic_read(&mddev->writes_pending) &&
  9. 7751                             !mddev->in_sync &&
  10. 7752                             mddev->recovery_cp == MaxSector) {
  11. 7753                                 mddev->in_sync = 1;
  12. 7754                                 did_change = 1;
  13. 7755                                 set_bit(MD_CHANGE_CLEAN, &mddev->flags);
  14. 7756                         }
  15. 7757                         if (mddev->safemode == 1)
  16. 7758                                 mddev->safemode = 0;
  17. 7759                         spin_unlock_irq(&mddev->write_lock);
  18. 7760                         if (did_change)
  19. 7761                                 sysfs_notify_dirent_safe(mddev->sysfs_state);
  20. 7762                 }
  21. 7763
  22. 7764                 if (mddev->flags)
  23. 7765                         md_update_sb(mddev, 0);
  24. 7766
  25. 7767                 if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) &&
  26. 7768                     !test_bit(MD_RECOVERY_DONE, &mddev->recovery)) {
  27. 7769                         /* resync/recovery still happening */
  28. 7770                         clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
  29. 7771                         goto unlock;
  30. 7772                 }
  31. 7773                 if (mddev->sync_thread) {
  32. 7774                         reap_sync_thread(mddev);
  33. 7775                         goto unlock;
  34. 7776                 }
7723行,对mddev加锁,这里用trylock是因为这里是raid5主线程调用,如果直接用lock会导致主线程休眠,IO流阻塞。
7746行,external表示超级块存储位置,为0表示存储在阵列所在磁盘上。默认值为0,也许好奇的你会问那可不可以既存储在外面又存储在阵列所在磁盘上呢?那只能怪你想像力太丰富了,不过我就是这么干的,原代码是不支持的需要自己修改代码支持这个特性。这个特性的重要性跟数据的重要性成正比。试想为什么市面上那么多数据恢复软件,为什么会丢数据?根本原因就是metadata丢了,metadata就是宝藏的藏宝图,再回头想想就很有必要啦。
7749行,这个判断就是刚刚7718行的判断,分支里代码也就验证了之前的说明,这个判断主要目的是用来更新超级块的。至于这里更新超级块的重要性理解了in_sync的功能就知道了。
7753行,设置in_sync标志。
7754行,设置阵列改变标识。
7755行,设置mddev改变标志。
7757行,设置safemode为0。尽管前面已经讲解了安全模式了,这里再从另外一个角度说一下,safemode标志就像软件看门狗,在阵列写数据时设置为0,然后需要在写完成时喂狗,如果不喂狗那么阵列为脏需要重新启动同步,喂狗程序就是safemode_timer定时器。
7760行,更新mddev的sysfs下状态。
7764行,更新阵列超级块。
7767行,同步线程正在工作,就没有必要再去凑热闹了。
7773行,同步已完成。
7774行,回收同步线程。
继续md_check_recovery函数:
[cpp] view plaincopy
  1. 7777                 /* Set RUNNING before clearing NEEDED to avoid
  2. 7778                  * any transients in the value of "sync_action".
  3. 7779                  */
  4. 7780                 set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
  5. 7781                 /* Clear some bits that don't mean anything, but
  6. 7782                  * might be left set
  7. 7783                  */
  8. 7784                 clear_bit(MD_RECOVERY_INTR, &mddev->recovery);
  9. 7785                 clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
  10. 7786
  11. 7787                 if (!test_and_clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
  12. 7788                     test_bit(MD_RECOVERY_FROZEN, &mddev->recovery))
  13. 7789                         goto unlock;
  14. 7790                 /* no recovery is running.
  15. 7791                  * remove any failed drives, then
  16. 7792                  * add spares if possible.
  17. 7793                  * Spare are also removed and re-added, to allow
  18. 7794                  * the personality to fail the re-add.
  19. 7795                  */
  20. 7796
  21. 7797                 if (mddev->reshape_position != MaxSector) {
  22. 7798                         if (mddev->pers->check_reshape == NULL ||
  23. 7799                             mddev->pers->check_reshape(mddev) != 0)
  24. 7800                                 /* Cannot proceed */
  25. 7801                                 goto unlock;
  26. 7802                         set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
  27. 7803                         clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
  28. 7804                 } else if ((spares = remove_and_add_spares(mddev))) {
  29. 7805                         clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
  30. 7806                         clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
  31. 7807                         clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
  32. 7808                         set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
  33. 7809                 } else if (mddev->recovery_cp < MaxSector) {
  34. 7810                         set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
  35. 7811                         clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
  36. 7812                 } else if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery))
  37. 7813                         /* nothing to be done ... */
  38. 7814                         goto unlock;
7780行,设置同步运行状态。同步状态有如下:
[cpp] view plaincopy
  1. /* recovery/resync flags
  2. * NEEDED:   we might need to start a resync/recover
  3. * RUNNING:  a thread is running, or about to be started
  4. * SYNC:     actually doing a resync, not a recovery
  5. * RECOVER:  doing recovery, or need to try it.
  6. * INTR:     resync needs to be aborted for some reason
  7. * DONE:     thread is done and is waiting to be reaped
  8. * REQUEST:  user-space has requested a sync (used with SYNC)
  9. * CHECK:    user-space request for check-only, no repair
  10. * RESHAPE:  A reshape is happening
  11. *
  12. * If neither SYNC or RESHAPE are set, then it is a recovery.
  13. */
  14. #define     MD_RECOVERY_RUNNING     0
  15. #define     MD_RECOVERY_SYNC     1
  16. #define     MD_RECOVERY_RECOVER     2
  17. #define     MD_RECOVERY_INTR     3
  18. #define     MD_RECOVERY_DONE     4
  19. #define     MD_RECOVERY_NEEDED     5
  20. #define     MD_RECOVERY_REQUESTED     6
  21. #define     MD_RECOVERY_CHECK     7
  22. #define     MD_RECOVERY_RESHAPE     8
  23. #define     MD_RECOVERY_FROZEN     9
* NEEDED:   需要启动同步线程
     * RUNNING:  准备启动或已经有同步线程有运行
     * SYNC:     做同步操作
     * RECOVER:  尝试或已经在重建操作
     * INTR:     同步中断
     * DONE:     同步完成
     * REQUEST:  用户请求同步
     * CHECK:    用户请求检查
     * RESHAPE:  reshape操作
这里有必要解释一下同步线程的意思,这里的同步是指广义的sync,包括狭义的同步和重建,因为同步和重建实际上做的是同一件事情就是把数据从一个地方拷贝到另一个地方。sync是包括syncing和recovery,所以不要一看到同步线程就以为是做同步操作。
正是这些标志指定同步线程下一步该怎么做,而我们一看到这些标志的时候心里必须要明白此时线程在做什么或者应该做什么。
7804行,这个if分支用于启动重建操作。
7809行,这个分支用于启动同步操作。
7812行,既不同步也不重建那就没有什么可以做的。
[cpp] view plaincopy
  1. 7816                 if (mddev->pers->sync_request) {
  2. 7817                         if (spares) {
  3. ...
  4. 7823                         }
  5. 7824                         mddev->sync_thread = md_register_thread(md_do_sync,
  6. 7825                                                                 mddev,
  7. 7826                                                                 "resync");
  8. 7827                         if (!mddev->sync_thread) {
  9. ...
  10. 7837                         } else
  11. 7838                                 md_wakeup_thread(mddev->sync_thread);
  12. 7839                         sysfs_notify_dirent_safe(mddev->sysfs_action);
  13. 7840                         md_new_event(mddev);
  14. 7841                 }
  15. ...
  16. 7850                 mddev_unlock(mddev);
  17. 7851         }
7816行,对于没有同步线程的阵列来说,就没什么事情了。
7824行,启动同步线程。
7838行,唤醒同步线程。
7839行,更新同步状态。
这时主线程调用md_check_recovery函数就已经结束了,启动的同步线程来做同步的具体事宜。那么为什么主线程自己不做同步而另起一个线程做同步呢?不妨想想现在事情都放在主线程里做有什么负面影响?当主线程在同步的时候是不是就不能很及时响应正常数据流了。而且同步和数据流本来就是两件差异很大的事情。
本小节只是介绍到同步入口,同步过程在下一小节开始阅读。
转载请注明出处:http://blog.csdn.net/liumangxiong

linux内核奇遇记之md源代码解读之七阵列同步一相关推荐

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

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

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

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

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

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

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

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

  5. xilinx linux内核,Xilinx-Zynq Linux内核源码编译过程

    本文内容依据http://www.wiki.xilinx.com网址编写,编译所用操作系统为ubuntu 14 1.交叉编译环境的安装配置 2.uboot的编译 1)下载uboot源代码 下载uboo ...

  6. Linux内核调试方法【转】

    转自:http://www.cnblogs.com/shineshqw/articles/2359114.html kdb:只能在汇编代码级进行调试: 优点是不需要两台机器进行调试. gdb:在调试模 ...

  7. Linux内核移植之一:内核源码结构与Makefile分析

    内容来自 韦东山<嵌入式Linux应用开发完全手册> 一.内核介绍 1.版本及其特点 Linux内核的版本号可以从源代码的顶层目录下的Makefile中看到,比如下面几行它们构成了Linu ...

  8. linux 内核移植和根文件系统的制作【转载】

    原文地址:http://www.cnblogs.com/hnrainll/archive/2011/06/09/2076214.html 1.1 Linux内核基础知识 在动手进行Linux内核移植之 ...

  9. linux内核源码lxr,配置glimpse与LXR读取linux内核源码

    配置glimpse与LXR读取linux内核源码 1.安装 源代码编译glimpse sudo apt-get install flex sudo apt-get install lxr 2.设置Ap ...

  10. zynq linux内核出错,Xilinx Zynq Linux内核源码编译过程

    1.交叉编译环境的安装配置 1) +Xilinx+Tools 2.uboot的编译 1)下载uboot源代码 下载uboot源代码,务必要下载tar.gz格式的文件,地址: https://githu ...

最新文章

  1. 2022-2028年中国汽车制动器行业投资分析及前景预测报告
  2. SQL Server2000 未公开的存储过程
  3. Java黑皮书课后题第2章:2.5(金融应用:计算小费)编写一个程序,读入一笔费用与小费利率,计算小费和总钱数
  4. 【SPSS】第十周-面板数据的线性回归
  5. Notable magic numbers
  6. nodejs基础整理
  7. oracle 基本语法,正则表达式应用
  8. 【BZOJ 1036】树的统计【树链剖分模板】
  9. 钣金编程软件Radan无人值守,自动排版功能
  10. python爬虫如何更换ip_Python爬虫被封IP,怎么换ip?
  11. 几种凹凸贴图(Bump Mapping)的学习记录
  12. 台式电脑锁定计算机,电脑锁住了怎么解锁
  13. java人民币金额大写_[求助]用java实现整数转换为人民币金额大写的功能
  14. Jib使用小结(Maven插件版)
  15. Python之Turtle库绘制简单图形
  16. 对26个英文字母进行huffman编码
  17. 不怕千招会,就怕一招精,学编程不要盲目跟风
  18. App中的微信支付和支付宝支付如何实现
  19. Kali下载Python软件包管理工具
  20. create与oncreate的区别

热门文章

  1. HDU 3639 Hawk-and-Chicken
  2. photon四种同步方式_Map 四种同步方式的性能比较
  3. 线程不安全 静态变量_【高并发】面试官问我:为啥局部变量是线程安全的?...
  4. SQL Server Join方式
  5. Python 学习笔记 - Redis
  6. 一个按照行来截取显示文章摘要的函数
  7. Confluence3.4的安装和配置
  8. git push 忽略.idea文件夹下的文件
  9. zookeeper集群节点为什么是奇数个
  10. 本地编译tomcat的docker镜像