linux内核奇遇记之md源代码解读之九阵列raid5同步函数sync_request

linux内核奇遇记之md源代码解读之九阵列raid5同步函数sync_request
转载请注明出处:http://blog.csdn.net/liumangxiong
我们再来回顾一下整个场景:
1)在运行阵列的时候调用md_wakeup_thread唤醒主线程
2)主线程调用md_check_recovery检查同步
3)md_check_recovery函数中检查需要同步调用md_register_thread创建同步线程
4)同步线程调用md_do_sync函数处理同步过程
5)md_do_sync做同步过程的管理,一步步推同步点,记录同步完成点,调用sync_request进行各种阵列级别的同步
6)sync_request做同步数据流的派发工作
对于raid5阵列来说,同步是按struct stripe_head为基本单位进行派发的。打个比方,我们现在要把一个土豆炸成薯片,这时首先要把土豆切成片,再把土豆片放到油锅里炸,炸开了再捞起来装盒。那么md_do_sync的作用就相当于把土豆切片,这个切片的大小也就是STRIPE_SECTORS大小了。sync_request接收到这个土豆片之后不能立即下锅,要用struct stripe_head把它包装一下,这就类似要在土豆片外面刷一层调料。然后再调用handle_stripe进行处理并最终下发到磁盘,这就类似于把土豆片放在锅里油炸加工的过程。最后调用bitmap_cond_end_sync保存同步完成记录,这就类似回收土豆片并盒装。
这里还有一个细节,就是为了周期性保存同步结果,每隔几秒钟都会等待所有同步请求返回再记录下来。这就类似于这个炸土豆的锅很小,一次只能放20片土豆,一开始我们不停的放薯片,等放满20片,我们就停下来直接到所有土豆都熟了然后一次性打捞上来,然后再放20片,重复上面的过程。
理解上以上的处理机制,再看代码就非常容易了。
4453 static inline sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipped, int go_faster)
4454 {
4455         struct r5conf *conf = mddev->private;
4456         struct stripe_head *sh;
4457         sector_t max_sector = mddev->dev_sectors;
4458         sector_t sync_blocks;
4459         int still_degraded = 0;
4460         int i;
4461
4462         if (sector_nr >= max_sector) {
4463                 /* just being told to finish up .. nothing much to do */
4464
4465                 if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) {
4466                         end_reshape(conf);
4467                         return 0;
4468                 }
4469
4470                 if (mddev->curr_resync < max_sector) /* aborted */
4471                         bitmap_end_sync(mddev->bitmap, mddev->curr_resync,
4472                                         &sync_blocks, 1);
4473                 else /* completed sync */
4474                         conf->fullsync = 0;
4475                 bitmap_close_sync(mddev->bitmap);
4476
4477                 return 0;
4478         }

这一部分是处理同步完成的,同步完成有两种情况,一种是正常完成的,另一种是被中断的。
4462行,同步完成。
4470行,同步中断,通知bitmap最后一次同步是abort
4474行,同步成功完成,更新fullsync为0,fullsync表示阵列要强制完全同步。
4475行,通知bitmap同步完成。
虽然这部分代码是放在函数比较靠前的位置,但是这部分代码是在md_do_sync退出同步循环之后的7521行的sync_request调用到的。接下来这部分才是md_do_sync循环中sync_request会执行到的部分:
4480         /* Allow raid5_quiesce to complete */
4481         wait_event(conf->wait_for_overlap, conf->quiesce != 2);
4482
4483         if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
4484                 return reshape_request(mddev, sector_nr, skipped);
4485
4486         /* No need to check resync_max as we never do more than one
4487          * stripe, and as resync_max will always be on a chunk boundary,
4488          * if the check in md_do_sync didn't fire, there is no chance
4489          * of overstepping resync_max here
4490          */
4491
4492         /* if there is too many failed drives and we are trying
4493          * to resync, then assert that we are finished, because there is
4494          * nothing we can do.
4495          */
4496         if (mddev->degraded >= conf->max_degraded &&
4497             test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
4498                 sector_t rv = mddev->dev_sectors - sector_nr;
4499                 *skipped = 1;
4500                 return rv;
4501         }
4502         if (!bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, 1) &&
4503             !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery) &&
4504             !conf->fullsync && sync_blocks >= STRIPE_SECTORS) {
4505                 /* we can skip this block, and probably more */
4506                 sync_blocks /= STRIPE_SECTORS;
4507                 *skipped = 1;
4508                 return sync_blocks * STRIPE_SECTORS; /* keep things rounded to whole stripes */
4509         }
4510
4511         bitmap_cond_end_sync(mddev->bitmap, sector_nr);
4512
4513         sh = get_active_stripe(conf, sector_nr, 0, 1, 0);
4514         if (sh == NULL) {
4515                 sh = get_active_stripe(conf, sector_nr, 0, 0, 0);
4516                 /* make sure we don't swamp the stripe cache if someone else
4517                  * is trying to get access
4518                  */
4519                 schedule_timeout_uninterruptible(1);
4520         }
4521         /* Need to check if array will still be degraded after recovery/resync
4522          * We don't need to check the 'failed' flag as when that gets set,
4523          * recovery aborts.
4524          */
4525         for (i = 0; i < conf->raid_disks; i++)
4526                 if (conf->disks[i].rdev == NULL)
4527                         still_degraded = 1;
4528
4529         bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, still_degraded);
4530
4531         set_bit(STRIPE_SYNC_REQUESTED, &sh->state);
4532
4533         handle_stripe(sh);
4534         release_stripe(sh);
4535
4536         return STRIPE_SECTORS;
4537 }

4481行,每一个wait_event都有一个同步的故事,wait_event就像是十字路口的红绿灯,没有红绿灯的话两边的车都以匀速前进很快就有悲剧发生。同样在linux内核中也有这样的问题,多个线程非原子地访问同一个资源时,都会发生不可预料的结果。这里的wait_event也是因为有了资源访问冲突,搜索wait_for_overlap发现有两种情况:一是正常读写请求,二是同步请求。即相当于两个写者,或者一个读者一个写者,所以就需要按次序去访问资源。
4492行,太多磁盘fail,同步就没必要进行下去了。
4496行,同步且太多fail盘,同步就是构建数据冗余,如果冗余盘都没了,就没必要玩下去了
4498-4500行,通知同步完成。
4502行,通知bitmap同步开始
4506行,很开心,bitmap说已经同步过了,那就跳过。
4511行,处理20土豆片炸好捞上来的情况
4513行,申请struct stripe_head
4525行,判断阵列是否降级,既然降级了为什么还要同步呢。前面讲过,同步就是构建数据冗余,对于Raid5来说只有一个数据冗余,所以降级了就不用同步了。但是对raid6来说有两份冗余数据,只有一个数据盘fail还可以进行同步,但是不更新bitmap。
4529行,通知bitmap开始同步
4531行,设置struct stripe_head同步标志,handle_stripe根据这个标志进行具体处理
4533行,开始处理具体的数据流,即炸土豆的过程
4536行,返回同步大小为STRIPE_SECTORS。
下一节开始介绍raid5数据流,轰轰烈烈的炸土豆秘方将为大家揭晓。
转载请注明出处:http://blog.csdn.net/liumangxiong
### 回答1: MDLinux内核中的一个模块,可实现磁盘阵列的软件级RAID,它和RAID0、RAID1、RAID4、RAID5、RAID6一样,就是一种磁盘阵列方案。 MD中最主要的部分是驱动程序,它运行在内核态中。它将多个磁盘设备组合在一起,成为一个逻辑设备,该逻辑设备对应着一个块设备文件。在这个逻辑设备上,可实现磁盘阵列的软件级RAID功能。 MD驱动程序的主要源代码是在/drivers/md目录下的md.c文件中,它包括了MD的全部源代码,还有一些其他相关文件,比如raid5.c等。 在这个文件中,最值得学习的是内核的模块化编程思想。模块化编程是一种将代码划分为模块的软件设计方法,通过将代码划分为不同的模块,实现代码的解耦、可重用、可维护性等目标。 在MD.c中我们还可以看到内核中的锁、内存管理等基本的内核技术的应用。通过对MD.c进行源代码解读,能够深入了解Linux内核的实现原理,特别是MDRAID功能的实现,对于我们进一步学习Linux内核的相关知识和对其进行应用开发具有很大的帮助。 总之,通过对MD.c源代码解读,我们可以学习到Linux内核模块化编程思想、内存管理、锁机制等基本内核技术,进一步掌握Linux内核的实现原理,从而在Linux应用开发中更加熟练娴熟。 ### 回答2: MD(Multiple Devices)是一种常用的软件RAID方案,可以在Linux内核中实现,同时也是Linux内核中最基本的RAID模式之一。MD在实现中使用了驱动程序和用户空间工具,其中驱动程序包含在内核中,因此我们需要对MD源代码进行解读MD源代码是由C语言编写的,主要包含在drivers/md/目录下。在这个目录下,可以看到一些重要的文件和子目录,例如md.c、md.h、raid1.c、raid5.c等。这些文件和子目录定义了MD的基本结构和函数,如磁盘阵列的基本信息结构、磁盘块的操作函数等。 MD的实现思路比较清晰,可以简单地理解为将多个物理磁盘组合在一起,形成一个虚拟的块设备。在这个虚拟的块设备上,可以进行读写等操作,而具体的数据操作则由MD提供的不同RAID模式实现。例如,MD支持的RAID1模式就是将数据同步写入两个物理磁盘,以实现磁盘容错。而MD支持的RAID5模式则是将数据分散写入多个物理磁盘,通过奇偶校验等方式实现磁盘容错。 在MD源代码解读过程中,需要重点关注这些RAID模式的实现方式和相关函数。同时,还需要了解整个MD的插入和移除机制、数据恢复机制等,以便更好地理解和修改MD源代码。 总之,对于想要深入了解Linux内核RAID相关实现的开发者来说,对MD源代码进行解读是一个非常有价值的学习和探索过程。 ### 回答3: mdlinux内核中的一个重要模块,支持多种存储设备,包括硬盘、闪存和网络存储等。如果想要深入了解linux内核的运行机制,就必须掌握md源代码。下面就对md源代码进行解读md源代码的核心是md.c文件。这个文件中定义了md模块的核心函数,包括md_init()、md_run()和md_stop()等。其中md_init()函数主要负责初始化md模块的各个子系统,包括raid核心、hotplugging、proc文件系统和sysfs文件系统等。md_run()函数则是md模块的主要循环,负责轮询设备状态并执行相应的IO操作。md_stop()函数则是md模块的关闭函数,用于释放模块占用的各种资源。 除了md.c文件外,md模块的代码还包括一些关键性质的文件,例如mddev.c、md.h和md_u.h等。其中,mddev.c文件定义了md设备的数据结构,包括磁盘阵列、线性设备和伪设备等。md.h和md_u.h文件则分别定义了用户空间和内核空间的md控制接口,包括创建和删除设备、添加和删除磁盘等。 在理解md源代码时需要注意的是,md模块涉及到多个子系统,包括块设备、文件系统和RAID等,因此需要对这些子系统的工作原理和相互关系有清晰的理解。同时,由于md模块的代码相当复杂,需要仔细地阅读和调试,才能完成内核的定制和优化工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值