md源代码解析-part1

最近花了一段时间认真地学习了一下md代码,并且在原代码的基础上开发了一系列的新功能,这些新功能让md更完善、更适合于企业大容量存储,通过增加阵列缓存和bitmap优化大大提升了存储速度,提高了数据的可靠性,在任何掉电的情况下保证数据一致性,超级块异常情况下完全不影响阵列使用,完全控制了踢盘问题,简化了用户操作。简单地概括一下,就是让存储不再有门槛。说了这么多,其实想表达的意思就是md的学习之路并非十分顺利,特此写此博文与所有兄弟姐妹们共享一下我的学习经验,如果您看完之后能有所收获,那就不枉费我下功夫写这些技术文章,同时您的感想和回复也将是我能够继续写下去的最大动力。

md代码在内核树中十多年经久不衰,跟作者neil brown,一位久经考验的开源战士,是分不开的。neil brown的博客地址是http://blog.neil.brown.name/,这个网址非常重要,因为我发现自己遇到疑难问题的时候80%问题都能在这里找到答案,所以有空的时间从到头到尾扫一遍,可以解决许多“为什么要这样做”的问题。

最初我看内核代码都是从module_init开始看的,可是自从学习了kconfig之后,我就改变了一下习惯,从kconfig和Makefile开始看代码了,如果谁有更好的办法请分享一下谢谢。

下面就来看一下drivers/md/Kconfig

[cpp] view plain copy

  1. #
  2. # Block device driver configuration
  3. #
  4. menuconfig MD
  5. bool "Multiple devices driver support (RAID and LVM)"
  6. depends on BLOCK
  7. help
  8. Support multiple physical spindles through a single logical device.
  9. Required for RAID and logical volume management.
  10. if MD
  11. config BLK_DEV_MD
  12. tristate "RAID support"
  13. ---help---
  14. #...省略若干
  15. endif # MD

menuconfig这个单词已经很熟悉了,因为自从开始学习编译内核的时候就有这样一个命令make menuconfig,当我们在内核源代码目录下敲下这个命令时,就会出现一个文本配置界面,在文本配置界面中可以选择需要编译到内核的模块,那有了这里的menuconfig MD,文本配置界面中才会有MD的一项,当选中MD之后,文本配置界面才会出现config BLK_DEV_MD和之后的选项,这些选项一般有两种状态,一个是tristate,表示内建、模块、移除三种状态,另一个是bool,表示选中或不选中。depends on表示正向依赖,如果选上了这个模块,那么正向依赖的模块也会自动选上,正向依赖模块递归所依赖的模块也会选上。一般把这些驱动模块选择为按模块加载,可以方便修改调试。

知道了Kconfig的基本配置,就可以按需定制内核,把不需要的统统去掉,也明白了为什么有时候系统的lib/module/下面为什么没有对应的模块了。

而对于阅读源代码来说,知道了哪些源代码编译进了内核,哪些源代码编译进了模块,哪些源代码没有编译,这样就可以按需阅读源代码了。另外在源代码中有一些编译选项类似:

#ifdef CONFIG_*
     //code
#endif

那么*号就是对应这里Kconfig中config后面的选项,如果这里选项选上,那么编译选项就为真。这些编译选项还用于对应的Makefile文件中,如md对应的Makefile文件有:

[cpp] view plain copy

  1. obj-$(CONFIG_MD_RAID0)          += raid0.o
  2. obj-$(CONFIG_MD_RAID1)          += raid1.o
  3. obj-$(CONFIG_MD_RAID10)          += raid10.o
  4. obj-$(CONFIG_MD_RAID456)     += raid456.o
  5. obj-$(CONFIG_MD_MULTIPATH)     += multipath.o
  6. obj-$(CONFIG_MD_FAULTY)          += faulty.o
  7. obj-$(CONFIG_BLK_DEV_MD)     += md-mod.o

如果在Kconfig中选中了config BLK_DEV_MD,那么Makefile就要编译生成md-mod.ko模块,那这个模块由哪几个文件生成的呢?看Makefile中定义:

md-mod-y     += md.o bitmap.o
raid456-y     += raid5.o

就是说在Kconfig中选中了config BLK_DEV_MD,md.c, bitmap.c就会被编译。同理,config MD_RAID456被选中,raid5.c就会被编译。这里,我们也可以知道,哪一个模块对应着哪几个源文件。例如要加载md-mod.ko,那么就需要md.c, bitmap.c。修改raid5代码时,只需要重新编译raid5.c一个文件就可以了。

md源代码解析-part2

在编译完成linux内核源代码的时候,drivers/md目录下会生成多个ko文件,那么这些内核模块哪一个先加载,哪一个后加载的呢?例如md-mod.ko, raid5.ko, raid10.ko,这些模块是一起加载的呢,还是有先后顺序呢?如果熟悉linux内核编程的话,知道有一个request_module函数,这个函数用于请求加载一个模块,但这个函数并不能说明一个模块对另一个模块的依赖关系。准确的信息还是来自于Kconfig,这里只抽取Kconfig中相关的部分:
config BLK_DEV_MD
     tristate "RAID support"
config MD_RAID10
     tristate "RAID-10 (mirrored striping) mode"
     depends on BLK_DEV_MD
config MD_RAID456
     tristate "RAID-4/RAID-5/RAID-6 mode"
     depends on BLK_DEV_MD

从这里我们可以看出,raid5.ko, raid10.ko都是依赖于md-mod.ko的(参见如上标红部分的关联),这就决定了我们的阅读方向是从md-mod.ko中开始的。

那么md-mod.ko中又是从哪个文件开始的呢?这就要找module_init函数,这个函数在md.c中定义的,那么就从这里入手。
8416 static int __init md_init(void)
8417 {
8418         int ret = -ENOMEM;
8419 
8420         md_wq = alloc_workqueue("md", WQ_MEM_RECLAIM, 0);
8421         if (!md_wq)
8422                 goto err_wq;
8423 
8424         md_misc_wq = alloc_workqueue("md_misc", 0, 0);
8425         if (!md_misc_wq)
8426                 goto err_misc_wq;
8427 
8428         if ((ret = register_blkdev(MD_MAJOR, "md")) < 0)
8429                 goto err_md;
8430 
8431         if ((ret = register_blkdev(0, "mdp")) < 0)
8432                 goto err_mdp;
8433         mdp_major = ret;
8434 
8435         blk_register_region(MKDEV(MD_MAJOR, 0), 1UL<<MINORBITS, THIS_MODULE,
8436                             md_probe, NULL, NULL);
8437         blk_register_region(MKDEV(mdp_major, 0), 1UL<<MINORBITS, THIS_MODULE,
8438                             md_probe, NULL, NULL);
8439 
8440         register_reboot_notifier(&md_notifier);
8441         raid_table_header = register_sysctl_table(raid_root_table);
8442 
8443         md_geninit();
8444         return 0;
8445 
8446 err_mdp:
8447         unregister_blkdev(MD_MAJOR, "md");
8448 err_md:
8449         destroy_workqueue(md_misc_wq);
8450 err_misc_wq:
8451         destroy_workqueue(md_wq);
8452 err_wq:
8453         return ret;
8454 }

模块的初始化过程看起来异常地简单,这就像有些人表面看起来十分普通,内心里却无比地强大,所以不要只看外表,还要听其言观其行。内在的美丽比外表的荣华更具吸引力和持久性。

8420和8424行,分别创建了工作队列,md_wq是用于flush命令的,另一个md_misc_wq,misc是miscellaneous的简写,是杂项的意思,用于处理一些零零碎碎的事情。

8428和8431行,创建了两个块设备,刚开始我只注意md的设备,压根没在意mdp,搜索变量mdp_major,在函数autorun_devices中使用了这个变量:
5474                 if (part) {
5475                         dev = MKDEV(mdp_major,
5476                                     rdev0->preferred_minor << MdpMinorShift);
5477                         unit = MINOR(dev) >> MdpMinorShift;
5478                 } else {
5479                         dev = MKDEV(MD_MAJOR, rdev0->preferred_minor);
5480                         unit = MINOR(dev);
5481                 }

5474行,变量part是函数传入参数,表示磁盘第几个分区,那么就知道mdp_major中字母p是表示part的意思,而mdp_major就表示用磁盘分区创建的阵列。

8435和8437行,创建了两个region,这两个函数的作用是在用户态创建了一个/dev/md*设备时,内核态就会对应调用md_probe创建一个mddev结构体,之后在用户态对/dev/md*的操作到内核态就相应地对mddev的操作了。

8440行,注册关机回调函数,主要作用是停止阵列线程,刷数据操作。

8441行,注册sysctl函数,用于控制阵列最小和最大的sync速度。

8443行,注册proc函数,于是有了目录/proc/mdstat,该目录的显示由函数md_seq_show控制。
md初始化代码就这样轻松地完成了,可是回到我们的初衷,仍然对md设备一无所知。那么md设备是如何创建的呢?创建的过程又是怎么的呢?一个阵列拥有哪些资源?下一节我们直入核心,开始阅读阵列创建的过程。

md源代码解析-part3

按照常理出牌,我们到ioctl中找阵列创建命令,md对应的ioctl函数是md_ioctl,当找对应的cmd命令字时,却完全没有类似CREATE_ARRAY的命令,那么就说明md设备并不是通过ioctl函数来创建的。其实如果我们仔细阅读一下md_ioctl函数的原型就会发现其实创建md设备根本就不在这个地方,函数原型如下:
6303 static int md_ioctl(struct block_device *bdev, fmode_t mode,
6304                         unsigned int cmd, unsigned long arg)

6303行,第一个参数是struct block_device*,就是说对一个块设备下发的命令,可是我们在创建md设备之前就没有对应的md块设备。到此,线索就断了,创建设备的入口到底是在哪里呢?

此路不通,我们换一条路走。创建md设备总是要创建struct mddev结构吧,那就找哪里申请了struct mddev内存结构不就可以了吗?这个方法是可行的,可是struct mddev结构体是用kmalloc申请的,这是怎么知道的呢?因为在函数md_init中根本就没有申请struct mddev内存池的代码,只好用kmalloc申请了。我们在md.c文件中搜索kmalloc再根据结果一条条找就能找出struct mddev创建的位置。但这里我们使用一个更简便的方法,那就是申请到了struct mddev结构总要进行初始化的吧,初始化函数是mddev_init,搜索这个函数,md.c文件中只有函数mddev_find()一处调用到,很显然已经找到了struct mddev结构的创建入口了,那就接着往上层调用去找创建md设备的入口函数吧。

我们可以找到这样的调用关系,箭头表示调用关系:

mddev_find()  <--- md_alloc()  <--- md_probe()

md_probe()函数就是在模块初始化函数md_init()中调用的blk_register_region()函数中的传入参数,熟悉blk层的同学都知道,只要在用户态创建了一个md设备,就会相应调用到内核probe()函数,而这里传入的probe()函数正是md_probe()。所以创建struct mddev结构体是由用户态触发的,而不是由内核态直接进行的。如果到了今天这个时代,还把这种琐碎的事情放在内核态去做,你都不好意思说你是做linux开发的。做linux开发就是要引导时尚,崇尚简单才是美。linux内核只提供机制,不提供具体实现策略。跟机制不相关的控制命令就需要从内核搬到用户态,一方面简化了内核,突出重点,方便了内核维护,另一方面在用户态维护策略让应用程序更加灵活并且方便了调试。

这样我们就从内核态杀到了用户态,用户态程序就是大名鼎鼎的mdadm,网上随便一搜就是一大堆人云亦云的文章,但最好的文章不是在网上,而是用命令man mdadm。用命令mdadm create来创建一个阵列,这里不去阅读mdadm的代码,因为这是用户态程序不是我们阅读的重点,其次这些代码也很简单基本上学过初中英语的同学都能看得懂。我们需要知道的是mdadm create命令最终会调用mknod()函数来创建一个/dev/md*设备,这样内核也就相应有了struct mddev结构体,这时这个结构体还是一个空结构体,空的意思就是说这个阵列没有设置属性,没有对应的物理磁盘,没有运行阵列。

到这个时候既然已经有了md设备,那就轮到md_ioctl上场的时候了,这个函数对应的ioctl命令字在文件include\linux\raid\md_u.h:
 36 /* ioctls */
 37 
 38 /* status */
 39 #define RAID_VERSION            _IOR (MD_MAJOR, 0x10, mdu_version_t)
 40 #define GET_ARRAY_INFO          _IOR (MD_MAJOR, 0x11, mdu_array_info_t)
 41 #define GET_DISK_INFO           _IOR (MD_MAJOR, 0x12, mdu_disk_info_t)
 42 #define PRINT_RAID_DEBUG        _IO (MD_MAJOR, 0x13)
 43 #define RAID_AUTORUN            _IO (MD_MAJOR, 0x14)
 44 #define GET_BITMAP_FILE         _IOR (MD_MAJOR, 0x15, mdu_bitmap_file_t)
 45 
 46 /* configuration */
 47 #define CLEAR_ARRAY             _IO (MD_MAJOR, 0x20)
 48 #define ADD_NEW_DISK            _IOW (MD_MAJOR, 0x21, mdu_disk_info_t)
 49 #define HOT_REMOVE_DISK         _IO (MD_MAJOR, 0x22)
 50 #define SET_ARRAY_INFO          _IOW (MD_MAJOR, 0x23, mdu_array_info_t)
 51 #define SET_DISK_INFO           _IO (MD_MAJOR, 0x24)
 52 #define WRITE_RAID_INFO         _IO (MD_MAJOR, 0x25)
 53 #define UNPROTECT_ARRAY         _IO (MD_MAJOR, 0x26)
 54 #define PROTECT_ARRAY           _IO (MD_MAJOR, 0x27)
 55 #define HOT_ADD_DISK            _IO (MD_MAJOR, 0x28)
 56 #define SET_DISK_FAULTY         _IO (MD_MAJOR, 0x29)
 57 #define HOT_GENERATE_ERROR      _IO (MD_MAJOR, 0x2a)
 58 #define SET_BITMAP_FILE         _IOW (MD_MAJOR, 0x2b, int)
 59 
 60 /* usage */
 61 #define RUN_ARRAY               _IOW (MD_MAJOR, 0x30, mdu_param_t)
 62 /*  0x31 was START_ARRAY  */
 63 #define STOP_ARRAY              _IO (MD_MAJOR, 0x32)
 64 #define STOP_ARRAY_RO           _IO (MD_MAJOR, 0x33)
 65 #define RESTART_ARRAY_RW        _IO (MD_MAJOR, 0x34)

这个文件为什么不放在md目录而放在include目录下?是因为文件里的内容是用户态跟内核态共用的,如果是内核态单独用的就没有必要放在这里了。
对于阵列的创建流程,最关心的命令字有:
SET_ARRAY_INFO  设置阵列信息
ADD_NEW_DISK     添加磁盘到阵列
RUN_ARRAY           运行阵列
首先看设置阵列信息,这个函数是这三个函数中最简单的一个:
6000 /*
6001  * set_array_info is used two different ways
6002  * The original usage is when creating a new array.
6003  * In this usage, raid_disks is > 0 and it together with
6004  *  level, size, not_persistent,layout,chunksize determine the
6005  *  shape of the array.
6006  *  This will always create an array with a type-0.90.0 superblock.
6007  * The newer usage is when assembling an array.
6008  *  In this case raid_disks will be 0, and the major_version field is
6009  *  use to determine which style super-blocks are to be found on the devices.
6010  *  The minor and patch _version numbers are also kept incase the
6011  *  super_block handler wishes to interpret them.
6012  */
6013 static int set_array_info(struct mddev * mddev, mdu_array_info_t *info)
6014 {
6015 
6016         if (info->raid_disks == 0) {
6017                 /* just setting version number for superblock loading */
6018                 if (info->major_version < 0 ||
6019                     info->major_version >= ARRAY_SIZE(super_types) ||
6020                     super_types[info->major_version].name == NULL) {
6021                         /* maybe try to auto-load a module? */
6022                         printk(KERN_INFO 
6023                                 "md: superblock version %d not known\n",
6024                                 info->major_version);
6025                         return -EINVAL;
6026                 }
6027                 mddev->major_version = info->major_version;
6028                 mddev->minor_version = info->minor_version;
6029                 mddev->patch_version = info->patch_version;
6030                 mddev->persistent = !info->not_persistent;
6031                 /* ensure mddev_put doesn't delete this now that there
6032                  * is some minimal configuration.
6033                  */
6034                 mddev->ctime         = get_seconds();
6035                 return 0;
6036         }
6037         mddev->major_version = MD_MAJOR_VERSION;
6038         mddev->minor_version = MD_MINOR_VERSION;
6039         mddev->patch_version = MD_PATCHLEVEL_VERSION;
6040         mddev->ctime         = get_seconds();
6041 
6042         mddev->level         = info->level;
6043         mddev->clevel[0]     = 0;
6044         mddev->dev_sectors   = 2 * (sector_t)info->size;
6045         mddev->raid_disks    = info->raid_disks;
6046         /* don't set md_minor, it is determined by which /dev/md* was
6047          * openned
6048          */
6049         if (info->state & (1<<MD_SB_CLEAN))
6050                 mddev->recovery_cp = MaxSector;
6051         else
6052                 mddev->recovery_cp = 0;
6053         mddev->persistent    = ! info->not_persistent;
6054         mddev->external      = 0;
6055 
6056         mddev->layout        = info->layout;
6057         mddev->chunk_sectors = info->chunk_size >> 9;
6058 
6059         mddev->max_disks     = MD_SB_DISKS;
6060 
6061         if (mddev->persistent)
6062                 mddev->flags         = 0;
6063         set_bit(MD_CHANGE_DEVS, &mddev->flags);
6064 
6065         mddev->bitmap_info.default_offset = MD_SB_BYTES >> 9;
6066         mddev->bitmap_info.default_space = 64*2 - (MD_SB_BYTES >> 9);
6067         mddev->bitmap_info.offset = 0;
6068 
6069         mddev->reshape_position = MaxSector;
6070 
6071         /*
6072          * Generate a 128 bit UUID
6073          */
6074         get_random_bytes(mddev->uuid, 16);
6075 
6076         mddev->new_level = mddev->level;
6077         mddev->new_chunk_sectors = mddev->chunk_sectors;
6078         mddev->new_layout = mddev->layout;
6079         mddev->delta_disks = 0;
6080         mddev->reshape_backwards = 0;
6081 
6082         return 0;
6083 }

首先看注释,这个函数有两种用途,一是用于创建阵列,当创建阵列时,raid_disk>0,另一种用途是assemble阵列,这时raid_disk==0。

那这里的raid_disk到底是多少呢?注释里又有这样的一句话,如果raid_disk>0,那么直接创建0.90阵列超级块,很显然,我们要创建的阵列超级块是1.2的,所以6037-6080只是用于兼容老版本的阵列的,需要阅读的代码只有6016行if语句中的那几行代码。

6027-6029行,设置阵列超级块版本号。

6030行,设置persistent属性,就是说超级块是保存在磁盘上还是只放在内存中啊,这里我们都是保存在磁盘上,以后看到这个属性就永远为true。

6034行,设置阵列创建时间。

那么是在什么时候才开始设置阵列属性呢?比如说阵列级别?别急,好戏还在后头。

接着看ADD_NEW_DISK对应的处理函数:
5672 static int add_new_disk(struct mddev * mddev, mdu_disk_info_t *info)
5673 {
5674         char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
5675         struct md_rdev *rdev;
5676         dev_t dev = MKDEV(info->major,info->minor);
5677
5678         if (info->major != MAJOR(dev) || info->minor != MINOR(dev))
5679                 return -EOVERFLOW;
5680
5681         if (!mddev->raid_disks) {
5682                 int err;
5683                 /* expecting a device which has a superblock */
5684                 rdev = md_import_device(dev, mddev->major_version, mddev->minor_version);
5685                 if (IS_ERR(rdev)) {
5686                         printk(KERN_WARNING
5687                                 "md: md_import_device returned %ld\n",
5688                                 PTR_ERR(rdev));
5689                         return PTR_ERR(rdev);
5690                 }
5691                 if (!list_empty(&mddev->disks)) {
5692                         struct md_rdev *rdev0
5693                                 = list_entry(mddev->disks.next,
5694                                              struct md_rdev, same_set);
5695                         err = super_types[mddev->major_version]
5696                                 .load_super(rdev, rdev0, mddev->minor_version);
5697                         if (err < 0) {
5698                                 printk(KERN_WARNING
5699                                         "md: %s has different UUID to %s\n",
5700                                         bdevname(rdev->bdev,b),
5701                                         bdevname(rdev0->bdev,b2));
5702                                 export_rdev(rdev);
5703                                 return -EINVAL;
5704                         }
5705                 }
5706                 err = bind_rdev_to_array(rdev, mddev);
5707                 if (err)
5708                         export_rdev(rdev);
5709                 return err;
5710         }

这个函数只截取了一部分,因为这一次添加磁盘流程只会走到这一部分代码,首先注意到函数的参数:第一个参数是struct mddev结构体,这个结构体域比较多,我们会在后面用到具体域时再讲,第二个参数是表示一个要加入阵列的磁盘,这里用到了该结构体的两个域,major和minor,表示磁盘主设备号和次设备号。

5676行,根据主设备号和次设备号算出dev_t。

5678行,这里为什么还要再检查一下呢?返回的错误码叫溢出,意思是说很久很久以前linux中设备还不是很多的时候dev_t只要用16位来表示就可以了,然而随着linux服务器单一种类外设数量越来越多,dev_t扩展到32位,所以这里检查保证输入major,minor的正确。

5681行,这里还未添加磁盘,所以进入这个if分支。

5684行,创建磁盘struct md_rdev结构,继续跟入到函数中:
3236 /*
3237  * Import a device. If 'super_format' >= 0, then sanity check the superblock
3238  *
3239  * mark the device faulty if:
3240  *
3241  *   - the device is nonexistent (zero size)
3242  *   - the device has no valid superblock
3243  *
3244  * a faulty rdev _never_ has rdev->sb set.
3245  */
3246 static struct md_rdev *md_import_device(dev_t newdev, int super_format, int super_minor)
3247 {
3248         char b[BDEVNAME_SIZE];
3249         int err;
3250         struct md_rdev *rdev;
3251         sector_t size;
3252 
3253         rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
3254         if (!rdev) {
3255                 printk(KERN_ERR "md: could not alloc mem for new device!\n");
3256                 return ERR_PTR(-ENOMEM);
3257         }
3258 
3259         err = md_rdev_init(rdev);
3260         if (err)
3261                 goto abort_free;
3262         err = alloc_disk_sb(rdev);
3263         if (err)
3264                 goto abort_free;
3265 
3266         err = lock_rdev(rdev, newdev, super_format == -2);
3267         if (err)
3268                 goto abort_free;
3269 
3270         kobject_init(&rdev->kobj, &rdev_ktype);
3271 
3272         size = i_size_read(rdev->bdev->bd_inode) >> BLOCK_SIZE_BITS;
3273         if (!size) {
3274                 printk(KERN_WARNING 
3275                         "md: %s has zero or unknown size, marking faulty!\n",
3276                         bdevname(rdev->bdev,b));
3277                 err = -EINVAL;
3278                 goto abort_free;
3279         }
3280 
3281         if (super_format >= 0) {
3282                 err = super_types[super_format].
3283                         load_super(rdev, NULL, super_minor);
3284                 if (err == -EINVAL) {
3285                         printk(KERN_WARNING
3286                                 "md: %s does not have a valid v%d.%d "
3287                                "superblock, not importing!\n",
3288                                 bdevname(rdev->bdev,b),
3289                                super_format, super_minor);
3290                         goto abort_free;
3291                 }
3292                 if (err < 0) {
3293                         printk(KERN_WARNING 
3294                                 "md: could not read %s's sb, not importing!\n",
3295                                 bdevname(rdev->bdev,b));
3296                         goto abort_free;
3297                 }
3298         }
3299         if (super_format == -1)
3300                 /* hot-add for 0.90, or non-persistent: so no badblocks */
3301                 rdev->badblocks.shift = -1;
3302 
3303         return rdev;
3304 
3305 abort_free:
3306         if (rdev->bdev)
3307                 unlock_rdev(rdev);
3308         md_rdev_clear(rdev);
3309         kfree(rdev);
3310         return ERR_PTR(err);
3311 }

3252行,创建一个struct md_rdev结构体。

3259行,初始化struct md_rdev结构体。

3262行,申请一个page页,用于存放磁盘超级块信息。

3266行,对磁盘加锁,防止被其他程序操作如mount, 分区等。

3270行,初始化struct md_rdev磁盘kobject结构。

3272行,读磁盘大小,判断是否合法。

3281行,阵列超级块是1.2版本的,进入if分支。

3282行,读入阵列超级块信息,具体调用的函数是:

1450 static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_version)

这个函数很简单,根据超级块版本从磁盘上读入阵列超级块信息并保存到md_rdev->sb_page中,查做一些基本的校验和检,并将超级块信息保存到struct md_rdev结构中。到这里就返回到add_new_disk函数,

5684行返回的rdev就含有从磁盘上加载的超级块信息。

5691行,由于阵列中还没有磁盘,所以list_empty(&mddev->disks)成立,不会进入if分支。

5706行,建立阵列struct mddev和磁盘struct md_rdev结构之间的联系,进函数:

2077 static int bind_rdev_to_array(struct md_rdev * rdev, struct mddev * mddev)
2078 {
2079         char b[BDEVNAME_SIZE];
2080         struct kobject *ko;
2081         char *s;
2082         int err;
2083 
2084         if (rdev->mddev) {
2085                 MD_BUG();
2086                 return -EINVAL;
2087         }
2088 
2089         /* prevent duplicates */
2090         if (find_rdev(mddev, rdev->bdev->bd_dev))
2091                 return -EEXIST;
2092 
2093         /* make sure rdev->sectors exceeds mddev->dev_sectors */
2094         if (rdev->sectors && (mddev->dev_sectors == 0 ||
2095                         rdev->sectors < mddev->dev_sectors)) {
2096                 if (mddev->pers) {
2097                         /* Cannot change size, so fail
2098                          * If mddev->level <= 0, then we don't care
2099                          * about aligning sizes (e.g. linear)
2100                          */
2101                         if (mddev->level > 0)
2102                                 return -ENOSPC;
2103                 } else
2104                         mddev->dev_sectors = rdev->sectors;
2105         }
2106 
2107         /* Verify rdev->desc_nr is unique.
2108          * If it is -1, assign a free number, else
2109          * check number is not in use
2110          */
2111         if (rdev->desc_nr < 0) {
2112                 int choice = 0;
2113                 if (mddev->pers) choice = mddev->raid_disks;
2114                 while (find_rdev_nr(mddev, choice))
2115                         choice++;
2116                 rdev->desc_nr = choice;
2117         } else {
2118                 if (find_rdev_nr(mddev, rdev->desc_nr))
2119                         return -EBUSY;
2120         }
2121         if (mddev->max_disks && rdev->desc_nr >= mddev->max_disks) {
2122                 printk(KERN_WARNING "md: %s: array is limited to %d devices\n",
2123                        mdname(mddev), mddev->max_disks);
2124                 return -EBUSY;
2125         }
2126         bdevname(rdev->bdev,b);
2127         while ( (s=strchr(b, '/')) != NULL)
2128                 *s = '!';
2129 
2130         rdev->mddev = mddev;
2131         printk(KERN_INFO "md: bind<%s>\n", b);
2132 
2133         if ((err = kobject_add(&rdev->kobj, &mddev->kobj, "dev-%s", b)))
2134                 goto fail;
2135 
2136         ko = &part_to_dev(rdev->bdev->bd_part)->kobj;
2137         if (sysfs_create_link(&rdev->kobj, ko, "block"))
2138                 /* failure here is OK */;
2139         rdev->sysfs_state = sysfs_get_dirent_safe(rdev->kobj.sd, "state");
2140 
2141         list_add_rcu(&rdev->same_set, &mddev->disks);
2142         bd_link_disk_holder(rdev->bdev, mddev->gendisk);
2143 
2144         /* May as well allow recovery to be retried once */
2145         mddev->recovery_disabled++;
2146 
2147         return 0;
2148 
2149  fail:
2150         printk(KERN_WARNING "md: failed to register dev-%s for %s\n",
2151                b, mdname(mddev));
2152         return err;
2153 }

2090行,检查是否磁盘已经加入阵列了,加过就不必重复添加。

2094-2105行,比较磁盘大小,记录最小的磁盘空间。

2111行,desc_nr分配,这个号只描述加入阵列的早晚。

2130行,建立struct md_rdev到mddev的关联。

2133-2139行,建立sysfs相关状态和链接。

2141行,建立mddev到struct md_rdev的关联。

add_new_disk就这么快结束了,简单地说就是创建struct md_rdev结构并与struct mddev结构之间创建联系。第三个命令字RUN_ARRAY的处理过程具有重要的意义,并且其过程不是三言两语能够说完的,我们把该命令字处理流程放到下一个小节单独来讲。

md源代码解析-part4

运行阵列意味着阵列经历从无到有,建立了作为一个raid应有的属性(如同步重建),并为随后的读写做好的铺垫。那么运行阵列的时候到底做了哪些事情,让原来的磁盘像变形金刚一样组成一个新的巨无霸。现在就来看阵列运行处理流程:
5158 static int do_md_run(struct mddev *mddev)
5159 {
5160         int err;
5161 
5162         err = md_run(mddev);
5163         if (err)
5164                 goto out;
5165         err = bitmap_load(mddev);
5166         if (err) {
5167                 bitmap_destroy(mddev);
5168                 goto out;
5169         }
5170 
5171         md_wakeup_thread(mddev->thread);
5172         md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */5173 
5174         set_capacity(mddev->gendisk, mddev->array_sectors);
5175         revalidate_disk(mddev->gendisk);
5176         mddev->changed = 1;
5177         kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
5178 out:
5179         return err;
5180 }

如果说运行阵列的过程是一本书,那么这个函数就是这本书的目录,每一个目录中都隐含着一个深刻的故事。

5162行,md_run运行阵列,这个函数比较长,我们按一段一段来分析:
4956 int md_run(struct mddev *mddev)
4957 {
4958         int err;
4959         struct md_rdev *rdev;
4960         struct md_personality *pers;
4961 
4962         if (list_empty(&mddev->disks))
4963                 /* cannot run an array with no devices.. */
4964                 return -EINVAL;
4965 
4966         if (mddev->pers)
4967                 return -EBUSY;
4968         /* Cannot run until previous stop completes properly */
4969         if (mddev->sysfs_active)
4970                 return -EBUSY;
4971 
4972         /*
4973          * Analyze all RAID superblock(s)
4974          */
4975         if (!mddev->raid_disks) {
4976                 if (!mddev->persistent)
4977                         return -EINVAL;
4978                 analyze_sbs(mddev);
4979         }

4962-4969行检查,阵列还没运行,所以直接到4978行。

4978行,analyze_sbs,分析超级块,依次分析每一个磁盘的超级块,不符合阵列需求的磁盘将会被踢出阵列。
3310 static void analyze_sbs(struct mddev * mddev)
3311 {
3312         int i;
3313         struct md_rdev *rdev, *freshest, *tmp;
3314         char b[BDEVNAME_SIZE];
3315 
3316         freshest = NULL;
3317         rdev_for_each_safe(rdev, tmp, mddev)
3318                 switch (super_types[mddev->major_version].
3319                         load_super(rdev, freshest, mddev->minor_version)) {
3320                 case 1:
3321                         freshest = rdev;
3322                         break;
3323                 case 0:
3324                         break;
3325                 default:
3326                         printk( KERN_ERR \
3327                                 "md: fatal superblock inconsistency in %s"
3328                                 " -- removing from array\n", 
3329                                 bdevname(rdev->bdev,b));
3330                         kick_rdev_from_array(rdev);
3331                 }
3332 
3333 
3334         super_types[mddev->major_version].
3335                 validate_super(mddev, freshest);
3336 
3337         i = 0;
3338         rdev_for_each_safe(rdev, tmp, mddev) {
3339                 if (mddev->max_disks &&
3340                     (rdev->desc_nr >= mddev->max_disks ||
3341                      i > mddev->max_disks)) {
3342                         printk(KERN_WARNING
3343                                "md: %s: %s: only %d devices permitted\n",
3344                                mdname(mddev), bdevname(rdev->bdev, b),
3345                                mddev->max_disks);
3346                         kick_rdev_from_array(rdev);
3347                         continue;
3348                 }
3349                 if (rdev != freshest)
3350                         if (super_types[mddev->major_version].
3351                             validate_super(mddev, rdev)) {
3352                                 printk(KERN_WARNING "md: kicking non-fresh %s"
3353                                         " from array!\n",
3354                                         bdevname(rdev->bdev,b));
3355                                 kick_rdev_from_array(rdev);
3356                                 continue;
3357                         }
3358                 if (mddev->level == LEVEL_MULTIPATH) {
3359                         rdev->desc_nr = i++;
3360                         rdev->raid_disk = rdev->desc_nr;
3361                         set_bit(In_sync, &rdev->flags);
3362                 } else if (rdev->raid_disk >= (mddev->raid_disks - min(0, mddev->delta_disks))) {
3363                         rdev->raid_disk = -1;
3364                         clear_bit(In_sync, &rdev->flags);
3365                 }
3366         }
3367 }

3316-3331行,依次对阵列中每一个磁盘加载超级块,如果是最新的超级块则保存对应的struct md_rdev在freshest指针中,如果是不符合条件的超级块,将会踢出阵列。3319行,我们用1.2版本的超级块,那么对应这里load_super为super_1_load函数,这个级函数就是把超块信息从磁盘读出来,然后保存在md_rdev->sb_page中。然而这个函数还额外做了一件事情,就是比较哪个磁盘的超级块最新,看函数原型:
1433 static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_version)

第一个参数就是要加载超级块的磁盘,第二个参数是目前为止最新的超级块,第一次比较时为空。当返回值为1时表示rdev为最新,当返回为0时表示rdfdev仍然为最新超级块,小于0表示非法超级块。

3330行,将非法超级块的磁盘踢出阵列。

3334行,对应的validate_super函数为super_1_validate,这个函数根据最新超级块信息初始化了阵列struct mddev信息,这略了不相关的if分支:里代码省
1600 static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
1601 {
1602         struct mdp_superblock_1 *sb = page_address(rdev->sb_page);
1603         __u64 ev1 = le64_to_cpu(sb->events);
1604
1605         rdev->raid_disk = -1;
1606         clear_bit(Faulty, &rdev->flags);
1607         clear_bit(In_sync, &rdev->flags);
1608         clear_bit(WriteMostly, &rdev->flags);
1609
1610         if (mddev->raid_disks == 0) {
1611                 mddev->major_version = 1;
1612                 mddev->patch_version = 0;
1613                 mddev->external = 0;
1614                 mddev->chunk_sectors = le32_to_cpu(sb->chunksize);
1615                 mddev->ctime = le64_to_cpu(sb->ctime) & ((1ULL << 32)-1);
1616                 mddev->utime = le64_to_cpu(sb->utime) & ((1ULL << 32)-1);
1617                 mddev->level = le32_to_cpu(sb->level);
1618                 mddev->clevel[0] = 0;
1619                 mddev->layout = le32_to_cpu(sb->layout);
1620                 mddev->raid_disks = le32_to_cpu(sb->raid_disks);
1621                 mddev->dev_sectors = le64_to_cpu(sb->size);
1622                 mddev->events = ev1;
1623                 mddev->bitmap_info.offset = 0;
1624                 mddev->bitmap_info.space = 0;
1625                 /* Default location for bitmap is 1K after superblock
1626                  * using 3K - total of 4K
1627                  */
1628                 mddev->bitmap_info.default_offset = 1024 >> 9;
1629                 mddev->bitmap_info.default_space = (4096-1024) >> 9;
1630                 mddev->reshape_backwards = 0;
1631
1632                 mddev->recovery_cp = le64_to_cpu(sb->resync_offset);
1633                 memcpy(mddev->uuid, sb->set_uuid, 16);
1634
1635                 mddev->max_disks =  (4096-256)/2;
1636
1637                 if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET) &&
1638                     mddev->bitmap_info.file == NULL) {
1639                         mddev->bitmap_info.offset =
1640                                 (__s32)le32_to_cpu(sb->bitmap_offset);
1641                         /* Metadata doesn't record how much space is available.
1642                          * For 1.0, we assume we can use up to the superblock
1643                          * if before, else to 4K beyond superblock.
1644                          * For others, assume no change is possible.
1645                          */
1646                         if (mddev->minor_version > 0)
1647                                 mddev->bitmap_info.space = 0;
1648                         else if (mddev->bitmap_info.offset > 0)
1649                                 mddev->bitmap_info.space =
1650                                         8 - mddev->bitmap_info.offset;
1651                         else
1652                                 mddev->bitmap_info.space =
1653                                         -mddev->bitmap_info.offset;
1654                 }
1655
1656                 if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RESHAPE_ACTIVE)) {
1657                         mddev->reshape_position = le64_to_cpu(sb->reshape_position);
1658                         mddev->delta_disks = le32_to_cpu(sb->delta_disks);
1659                         mddev->new_level = le32_to_cpu(sb->new_level);
1660                         mddev->new_layout = le32_to_cpu(sb->new_layout);
1661                         mddev->new_chunk_sectors = le32_to_cpu(sb->new_chunk);
1662                         if (mddev->delta_disks < 0 ||
1663                             (mddev->delta_disks == 0 &&
1664                              (le32_to_cpu(sb->feature_map)
1665                               & MD_FEATURE_RESHAPE_BACKWARDS)))
1666                                 mddev->reshape_backwards = 1;
1667                 } else {
1668                         mddev->reshape_position = MaxSector;
1669                         mddev->delta_disks = 0;
1670                         mddev->new_level = mddev->level;
1671                         mddev->new_layout = mddev->layout;
1672                         mddev->new_chunk_sectors = mddev->chunk_sectors;
1673                 }
1674
1675         } 
...
1695         if (mddev->level != LEVEL_MULTIPATH) {
1696                 int role;
1697                 if (rdev->desc_nr < 0 ||
1698                     rdev->desc_nr >= le32_to_cpu(sb->max_dev)) {
1699                         role = 0xffff;
1700                         rdev->desc_nr = -1;
1701                 } else
1702                         role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]);
1703                 switch(role) {
1704                 case 0xffff: /* spare */
1705                         break;
1706                 case 0xfffe: /* faulty */
1707                         set_bit(Faulty, &rdev->flags);
1708                         break;
1709                 default:
1710                         if ((le32_to_cpu(sb->feature_map) &
1711                              MD_FEATURE_RECOVERY_OFFSET))
1712                                 rdev->recovery_offset = le64_to_cpu(sb->recovery_offset);
1713                         else
1714                                 set_bit(In_sync, &rdev->flags);
1715                         rdev->raid_disk = role;
1716                         break;
1717                 }
1718                 if (sb->devflags & WriteMostly1)
1719                         set_bit(WriteMostly, &rdev->flags);
1720                 if (le32_to_cpu(sb->feature_map) & MD_FEATURE_REPLACEMENT)
1721                         set_bit(Replacement, &rdev->flags);
1722         } else /* MULTIPATH are always insync */
1723                 set_bit(In_sync, &rdev->flags);
1724
1725         return 0;
1726 }

1602行,获取磁盘对应的超级块信息。

1610行,if分支成立,进入初始化struct mddev结构体,就是将阵列磁盘中最新超级块信息赋给struct mddev。

1695行,设置rdev->raid_disk和rdev->recovery_offset信息,注意这里的role有几个特殊值,0xffff表示热备盘,0xfffe表示faulty盘。recovery_offset顾名思义就是已重建偏移,In_sync表示磁盘在同步状态,WriteMostly表示优先读只用于raid1阵列。

又回到analyze_sbs函数中,

3338行,这个循环遍历阵列所有磁盘,依次validate每一个磁盘。validate的作用就是给每一个磁盘定一个身份,到底是数据盘啊还是热备盘,当然还有些磁盘超级块信息检查不合格,要淘汰出阵列的。

3350行,再一次进入validate_super函数,不过上一次主要作用是初始化struct mddev信息,这一次主要鉴定磁盘身份信息。
1600 static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
1601 {
1602         struct mdp_superblock_1 *sb = page_address(rdev->sb_page);
1603         __u64 ev1 = le64_to_cpu(sb->events);
1604
1605         rdev->raid_disk = -1;
1606         clear_bit(Faulty, &rdev->flags);
1607         clear_bit(In_sync, &rdev->flags);
1608         clear_bit(WriteMostly, &rdev->flags);
1609
1610         if (mddev->raid_disks == 0) {
...
1675         } else if (mddev->pers == NULL) {
1676                 /* Insist of good event counter while assembling, except for
1677                  * spares (which don't need an event count) */
1678                 ++ev1;
1679                 if (rdev->desc_nr >= 0 &&
1680                     rdev->desc_nr < le32_to_cpu(sb->max_dev) &&
1681                     le16_to_cpu(sb->dev_roles[rdev->desc_nr]) < 0xfffe)
1682                         if (ev1 < mddev->events)
1683                                 return -EINVAL;
1684         }
1695         if (mddev->level != LEVEL_MULTIPATH) {
1696                 int role;
1697                 if (rdev->desc_nr < 0 ||
1698                     rdev->desc_nr >= le32_to_cpu(sb->max_dev)) {
1699                         role = 0xffff;
1700                         rdev->desc_nr = -1;
1701                 } else
1702                         role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]);
1703                 switch(role) {
1704                 case 0xffff: /* spare */
1705                         break;
1706                 case 0xfffe: /* faulty */
1707                         set_bit(Faulty, &rdev->flags);
1708                         break;
1709                 default:
1710                         if ((le32_to_cpu(sb->feature_map) &
1711                              MD_FEATURE_RECOVERY_OFFSET))
1712                                 rdev->recovery_offset = le64_to_cpu(sb->recovery_offset);
1713                         else
1714                                 set_bit(In_sync, &rdev->flags);
1715                         rdev->raid_disk = role;
1716                         break;
1717                 }
1718                 if (sb->devflags & WriteMostly1)
1719                         set_bit(WriteMostly, &rdev->flags);
1720                 if (le32_to_cpu(sb->feature_map) & MD_FEATURE_REPLACEMENT)
1721                         set_bit(Replacement, &rdev->flags);
1722         } else /* MULTIPATH are always insync */
1723                 set_bit(In_sync, &rdev->flags);
1724
1725         return 0;
1726 }

1610行,经过上一次struct mddev的初始化,这时raid_disk已经不为0了。

1675行,阵列还未运行起来,if成立进入分支。

1679行,先判断rdev->desc_nr是否合法,再判断是否为数据盘。

1682行,如果为数据盘,则判断时间戳是否为最新,不是最新的超级块,数据也不是最新的,就不能继续留在阵列中了。

1695行,设置rdev->raid_disk和rdev->recovery_offset信息。

analyze_sbs函数已经完成,返回到md_run函数中继续往下看:
4981         if (mddev->level != LEVEL_NONE)
4982                 request_module("md-level-%d", mddev->level);
4983         else if (mddev->clevel[0])
4984                 request_module("md-%s", mddev->clevel);
4985
4986         /*
4987          * Drop all container device buffers, from now on
4988          * the only valid external interface is through the md
4989          * device.
4990          */
4991         rdev_for_each(rdev, mddev) {
4992                 if (test_bit(Faulty, &rdev->flags))
4993                         continue;
4994                 sync_blockdev(rdev->bdev);
4995                 invalidate_bdev(rdev->bdev);
4996
4997                 /* perform some consistency tests on the device.
4998                  * We don't want the data to overlap the metadata,
4999                  * Internal Bitmap issues have been handled elsewhere.
5000                  */
5001                 if (rdev->meta_bdev) {
5002                         /* Nothing to check */;
5003                 } else if (rdev->data_offset < rdev->sb_start) {
5004                         if (mddev->dev_sectors &&
5005                             rdev->data_offset + mddev->dev_sectors
5006                             > rdev->sb_start) {
5007                                 printk("md: %s: data overlaps metadata\n",
5008                                        mdname(mddev));
5009                                 return -EINVAL;
5010                         }
5011                 } else {
5012                         if (rdev->sb_start + rdev->sb_size/512
5013                             > rdev->data_offset) {
5014                                 printk("md: %s: metadata overlaps data\n",
5015                                        mdname(mddev));
5016                                 return -EINVAL;
5017                         }
5018                 }
5019                 sysfs_notify_dirent_safe(rdev->sysfs_state);
5020         }
4981-4984行,用于请求内核模块加载,因为linux内核模块可以按需加载,只有在需要该模块的时候再加载这样比较节约资源。
4991行,首先看注释,丢掉原磁盘设置的缓存,从现在开始这些磁盘只能由md访问了。就好像一个人要去当兵了,进入部队之后原来的身份证作废,新发了一个军人证,并且这个人以后只归部队管了,地方政府法庭不能管。
4992行,判断为faulty盘,坏盘就不用多费心思了。
4994行,刷磁盘buffer。
4995行,注销掉原来的身份证。
4997行,看注释,基本检查,看磁盘上数据部分与超级块是否overlap。rdev->data_offset表示磁盘上数据区开始偏移,rdev->sb_start表示超级块开始偏移,mddev->dev_sectors表示磁盘用于阵列的空间,rdev->sb_size表示超级块大小。
5019行,更新sysfs文件中磁盘state状态。
5022         if (mddev->bio_set == NULL)
5023                 mddev->bio_set = bioset_create(BIO_POOL_SIZE, 0);
5024
5025         spin_lock(&pers_lock);
5026         pers = find_pers(mddev->level, mddev->clevel);
5027         if (!pers || !try_module_get(pers->owner)) {
5028                 spin_unlock(&pers_lock);
5029                 if (mddev->level != LEVEL_NONE)
5030                         printk(KERN_WARNING "md: personality for level %d is not loaded!\n",
5031                                mddev->level);
5032                 else
5033                         printk(KERN_WARNING "md: personality for level %s is not loaded!\n",
5034                                mddev->clevel);
5035                 return -EINVAL;
5036         }
5037         mddev->pers = pers;
5038         spin_unlock(&pers_lock);
5039         if (mddev->level != pers->level) {
5040                 mddev->level = pers->level;
5041                 mddev->new_level = pers->level;
5042         }
5043         strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel));
5044
5045         if (mddev->reshape_position != MaxSector &&
5046             pers->start_reshape == NULL) {
5047                 /* This personality cannot handle reshaping... */
5048                 mddev->pers = NULL;
5049                 module_put(pers->owner);
5050                 return -EINVAL;
5051         }
5052
5053         if (pers->sync_request) {
5054                 /* Warn if this is a potentially silly
5055                  * configuration.
5056                  */
5057                 char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
5058                 struct md_rdev *rdev2;
5059                 int warned = 0;
5060
5061                 rdev_for_each(rdev, mddev)
5062                         rdev_for_each(rdev2, mddev) {
5063                                 if (rdev < rdev2 &&
5064                                     rdev->bdev->bd_contains ==
5065                                     rdev2->bdev->bd_contains) {
5066                                         printk(KERN_WARNING
5067                                                "%s: WARNING: %s appears to be"
5068                                                " on the same physical disk as"
5069                                                " %s.\n",
5070                                                mdname(mddev),
5071                                                bdevname(rdev->bdev,b),
5072                                                bdevname(rdev2->bdev,b2));
5073                                         warned = 1;
5074                                 }
5075                         }
5076
5077                 if (warned)
5078                         printk(KERN_WARNING
5079                                "True protection against single-disk"
5080                                " failure might be compromised.\n");
5081         }
5082
5083         mddev->recovery = 0;
5084         /* may be over-ridden by personality */
5085         mddev->resync_max_sectors = mddev->dev_sectors;
5086
5087         mddev->ok_start_degraded = start_dirty_degraded;
5088
5089         if (start_readonly && mddev->ro == 0)
5090                 mddev->ro = 2; /* read-only, but switch on first write */

5022行,创建bio内存池,用于读写时克隆保存原始bio。
5026行,查找对应阵列级别的struct md_personality是否存在,经过我们在4982行的request_module之后,新加载的模块会调用register_md_personality函数注册struct md_personality结构体,所以这里可以找到需要的pers。
5037行,将找到的pers赋值给mddev->pers。
5053行,这个if分支用于检查阵列中是否有两个struct md_rdev位于同一物理磁盘上。因为创建阵列可以用分区来创建,所以这里需要检查一下。如果两个struct md_rdev位于同一物理磁盘上,导致阵列性能很差。既然要玩raid就没有必要那么小气嘛,直接用整个磁盘,没有必要用磁盘分区。
5083行,初始化阵列sync标记。
5085行,初始化阵列最大同步偏移。
5087行,是否自动运行降级的脏阵列。可别小看了简简单单的一行代码,却代表了一个raid5阵列很复杂的问题。当一个raid5/6为脏并且降级时,就可能有数据错误的风险。为脏就是校验盘数据未经过同步,再加上降级就表示这一条带数据无法通过重建来恢复。所以md就不直接去运行阵列,而是由系统管理员手动运行。然而如果根文件系统是建立在raid上的时候,就会导致系统无法启动,所以就提供一个内核模块参数start_dirty_degraded来控制强制运行这样的阵列。
但实际上情况并没有看起来那么严重,例如在一个raid5阵列上建立一个ext4文件系统,为脏部分代表阵列还没有同步,而没有同步的条带是没有文件存储在条带上的(如果存储代表已经写过,写过的条带是同步的),所以这个时候强制运行降级的脏阵列是没有问题的。
5089行,在很多用户的环境里,经常会遇到一个问题,就是系统重启之后查看cat /proc/mdstat目录下阵列resync=pending状态,解决这个问题有两个方法,一是使用命令mdadm --read-write /dev/md*,另一个是设置模块参数/sys/module/md_mod/parameters/start_ro为0。那么为什么要设置这样一个状态呢?代码作者neil brown,是为了解决在Debian系统启动时要做一个重要的事情,所以让阵列进入这个临时状态。还好只要有读写阵列就会自动解除这个临时状态,对于正常使用没有影响。
5092         err = mddev->pers->run(mddev);
5093         if (err)
5094                 printk(KERN_ERR "md: pers->run() failed ...\n");
5095         else if (mddev->pers->size(mddev, 0, 0) < mddev->array_sectors) {
5096                 WARN_ONCE(!mddev->external_size, "%s: default size too small,"
5097                           " but 'external_size' not in effect?\n", __func__);
5098                 printk(KERN_ERR
5099                        "md: invalid array_size %llu > default size %llu\n",
5100                        (unsigned long long)mddev->array_sectors / 2,
5101                        (unsigned long long)mddev->pers->size(mddev, 0, 0) / 2);
5102                 err = -EINVAL;
5103                 mddev->pers->stop(mddev);
5104         }
5105         if (err == 0 && mddev->pers->sync_request &&
5106             (mddev->bitmap_info.file || mddev->bitmap_info.offset)) {
5107                 err = bitmap_create(mddev);
5108                 if (err) {
5109                         printk(KERN_ERR "%s: failed to create bitmap (%d)\n",
5110                                mdname(mddev), err);
5111                         mddev->pers->stop(mddev);
5112                 }
5113         }
5114         if (err) {
5115                 module_put(mddev->pers->owner);
5116                 mddev->pers = NULL;
5117                 bitmap_destroy(mddev);
5118                 return err;
5119         }
5092行,毫无疑问一看函数名就知道这一行是重中之重。这里选择raid1的run作示例,因为raid1是比较简单的,raid5和raid10在后面小节单独讲解。在讲run之前先简要说明一下mddev->pers->run是怎么调用到各个模块的run函数的?
首先每个模块初始化的时候都会调用到register_md_persionality函数,向md模块注册各自的struct md_personality结构,
7158 int register_md_personality(struct md_personality *p)
7159 {
7160         spin_lock(&pers_lock);
7161         list_add_tail(&p->list, &pers_list);
7162         printk(KERN_INFO "md: %s personality registered for level %d\n", p->name, p->level);
7163         spin_unlock(&pers_lock);
7164         return 0;
7165 }
在md_run函数中根据mddev->level初始化mddev->pers,如果level为1,这里pers就指向raid1的struct md_personality raid1_personality,那么这里调用的run函数也就是raid1中的run函数。接着看raid1中的run函数:
2769 static int run(struct mddev *mddev)
2770 {
2771         struct r1conf *conf;
2772         int i;
2773         struct md_rdev *rdev;
2774         int ret;
2775         bool discard_supported = false;
2776
2777         if (mddev->level != 1) {
2778                 printk(KERN_ERR "md/raid1:%s: raid level not set to mirroring (%d)\n",
2779                        mdname(mddev), mddev->level);
2780                 return -EIO;
2781         }
2782         if (mddev->reshape_position != MaxSector) {
2783                 printk(KERN_ERR "md/raid1:%s: reshape_position set but not supported\n",
2784                        mdname(mddev));
2785                 return -EIO;
2786         }
2787         /*
2788          * copy the already verified devices into our private RAID1
2789          * bookkeeping area. [whatever we allocate in run(),
2790          * should be freed in stop()]
2791          */
2792         if (mddev->private == NULL)
2793                 conf = setup_conf(mddev);
2794         else
2795                 conf = mddev->private;
2796
2797         if (IS_ERR(conf))
2798                 return PTR_ERR(conf);
2799
2800         if (mddev->queue)
2801                 blk_queue_max_write_same_sectors(mddev->queue, 0);
2802
2803         rdev_for_each(rdev, mddev) {
2804                 if (!mddev->gendisk)
2805                         continue;
2806                 disk_stack_limits(mddev->gendisk, rdev->bdev,
2807                                   rdev->data_offset << 9);
2808                 if (blk_queue_discard(bdev_get_queue(rdev->bdev)))
2809                         discard_supported = true;
2810         }
2811
2812         mddev->degraded = 0;
2813         for (i=0; i < conf->raid_disks; i++)
2814                 if (conf->mirrors[i].rdev == NULL ||
2815                     !test_bit(In_sync, &conf->mirrors[i].rdev->flags) ||
2816                     test_bit(Faulty, &conf->mirrors[i].rdev->flags))
2817                         mddev->degraded++;
2818
2819         if (conf->raid_disks - mddev->degraded == 1)
2820                 mddev->recovery_cp = MaxSector;
2821
2822         if (mddev->recovery_cp != MaxSector)
2823                 printk(KERN_NOTICE "md/raid1:%s: not clean"
2824                        " -- starting background reconstruction\n",
2825                        mdname(mddev));
2826         printk(KERN_INFO
2827                 "md/raid1:%s: active with %d out of %d mirrors\n",
2828                 mdname(mddev), mddev->raid_disks - mddev->degraded,
2829                 mddev->raid_disks);
2830
2831         /*
2832          * Ok, everything is just fine now
2833          */
2834         mddev->thread = conf->thread;
2835         conf->thread = NULL;
2836         mddev->private = conf;
2837
2838         md_set_array_sectors(mddev, raid1_size(mddev, 0, 0));
2839
2840         if (mddev->queue) {
2841                 mddev->queue->backing_dev_info.congested_fn = raid1_congested;
2842                 mddev->queue->backing_dev_info.congested_data = mddev;
2843                 blk_queue_merge_bvec(mddev->queue, raid1_mergeable_bvec);
2844
2845                 if (discard_supported)
2846                         queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
2847                                                 mddev->queue);
2848                 else
2849                         queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD,
2850                                                   mddev->queue);
2851         }
2852
2853         ret =  md_integrity_register(mddev);
2854         if (ret)
2855                 stop(mddev);
2856         return ret;
2857 }

2777-2786行,基本检查。
2792行,域private未赋值,进入if分支。
2793行,配置raid1环境。俗话说,国有国法,家有家规。如果说struct mddev是国法,那么setup_conf要建立的struct r1conf就是家规了,同样对于raid5和raid10都有自己有家规struct r5conf和struct r10conf。struct mddev存放是所有阵列共同的属性,而各自struct r*conf存放是私有的属性,而这些私有属性就是为了管理好各自管辖的磁盘。进入setup_conf函数:
2648 static struct r1conf *setup_conf(struct mddev *mddev)
2649 {
2650         struct r1conf *conf;
2651         int i;
2652         struct raid1_info *disk;
2653         struct md_rdev *rdev;
2654         int err = -ENOMEM;
2655
2656         conf = kzalloc(sizeof(struct r1conf), GFP_KERNEL);
2657         if (!conf)
2658                 goto abort;
2659
2660         conf->mirrors = kzalloc(sizeof(struct raid1_info)
2661                                 * mddev->raid_disks * 2,
2662                                  GFP_KERNEL);
2663         if (!conf->mirrors)
2664                 goto abort;
2665
2666         conf->tmppage = alloc_page(GFP_KERNEL);
2667         if (!conf->tmppage)
2668                 goto abort;
2669
2670         conf->poolinfo = kzalloc(sizeof(*conf->poolinfo), GFP_KERNEL);
2671         if (!conf->poolinfo)
2672                 goto abort;
2673         conf->poolinfo->raid_disks = mddev->raid_disks * 2;
2674         conf->r1bio_pool = mempool_create(NR_RAID1_BIOS, r1bio_pool_alloc,
2675                                           r1bio_pool_free,
2676                                           conf->poolinfo);
2677         if (!conf->r1bio_pool)
2678                 goto abort;
2679
2680         conf->poolinfo->mddev = mddev;
2681
2682         err = -EINVAL;
2683         spin_lock_init(&conf->device_lock);
2684         rdev_for_each(rdev, mddev) {
2685                 struct request_queue *q;
2686                 int disk_idx = rdev->raid_disk;
2687                 if (disk_idx >= mddev->raid_disks
2688                     || disk_idx < 0)
2689                         continue;
2690                 if (test_bit(Replacement, &rdev->flags))
2691                         disk = conf->mirrors + mddev->raid_disks + disk_idx;
2692                 else
2693                         disk = conf->mirrors + disk_idx;
2694
2695                 if (disk->rdev)
2696                         goto abort;
2697                 disk->rdev = rdev;
2698                 q = bdev_get_queue(rdev->bdev);
2699                 if (q->merge_bvec_fn)
2700                         mddev->merge_check_needed = 1;
2701
2702                 disk->head_position = 0;
2703                 disk->seq_start = MaxSector;
2704         }
2705         conf->raid_disks = mddev->raid_disks;
2706         conf->mddev = mddev;
2707         INIT_LIST_HEAD(&conf->retry_list);
2708
2709         spin_lock_init(&conf->resync_lock);
2710         init_waitqueue_head(&conf->wait_barrier);
2711
2712         bio_list_init(&conf->pending_bio_list);
2713         conf->pending_count = 0;
2714         conf->recovery_disabled = mddev->recovery_disabled - 1;
2715
2716         err = -EIO;
2717         for (i = 0; i < conf->raid_disks * 2; i++) {
2718
2719                 disk = conf->mirrors + i;
2720
2721                 if (i < conf->raid_disks &&
2722                     disk[conf->raid_disks].rdev) {
2723                         /* This slot has a replacement. */
2724                         if (!disk->rdev) {
2725                                 /* No original, just make the replacement
2726                                  * a recovering spare
2727                                  */
2728                                 disk->rdev =
2729                                         disk[conf->raid_disks].rdev;
2730                                 disk[conf->raid_disks].rdev = NULL;
2731                         } else if (!test_bit(In_sync, &disk->rdev->flags))
2732                                 /* Original is not in_sync - bad */
2733                                 goto abort;
2734                 }
2735
2736                 if (!disk->rdev ||
2737                     !test_bit(In_sync, &disk->rdev->flags)) {
2738                         disk->head_position = 0;
2739                         if (disk->rdev &&
2740                             (disk->rdev->saved_raid_disk < 0))
2741                                 conf->fullsync = 1;
2742                 }
2743         }
2744
2745         err = -ENOMEM;
2746         conf->thread = md_register_thread(raid1d, mddev, "raid1");
2747         if (!conf->thread) {
2748                 printk(KERN_ERR
2749                        "md/raid1:%s: couldn't allocate thread\n",
2750                        mdname(mddev));
2751                 goto abort;
2752         }
2753
2754         return conf;

2656-2680行,申请与读写相关的资源,后面讲读写的时候再深入。
2684行,对每个阵列中数据盘,在struct r1conf中建立关联,读写时用到。
2697行,建立struct r1conf到struct md_rdev关联。
2717行,磁盘replacement机制,这是阵列的高级特性,这里先不关注。
2746行,注册阵列处理线程。每个运行阵列都有这样的一个主线程,主要负责检查同步重建(只检查由另一线程负责具体处理),数据流处理。
小结一下,setup_conf函数主要作用是初始化struct r1conf,建立阵列数据流处理的上下文环境。
继续回到raid1的run函数中。
2803行,对阵列中每一个磁盘设置struct queue_limit,每个块设备都有一个struct queue_limit,表示块设备队列物理特性。这里主要作用是让磁盘请求队列根据阵列请求队列调整请求块大小和对齐。
2812-2817行,计算阵列降级磁盘数。
2834行,设置mddev->thread。
2836行,设置mddev->private为struct r1conf。
2838行,设置阵列大小。
2840-2851行,设置拥塞处理函数和请求合并函数。
2853行,块设备integrity,有兴趣可查看内核文档的integrity说明。
run函数就结束了,小结一下,run函数的主要作用是建立阵列读写的上下文环境,包括struct r1conf,阵列主线程等等。
继续回到md_run函数中。
5107行,创建阵列bitmap,具体过程在bitmap章节里再详细阅读。
接下来就是一些sysfs的显示和链接,最有欣赏价值的是mddev->safemode,什么是安全模式呢?没有写(包括同步和重建写)的时候就是安全模式,反之正在写的时候就不安全。因为对于有数据冗余的阵列来说,每一份数据都至少要写入两个物理磁盘中,在写的过程中程序异常或者系统掉电异常都会导致数据不一致,为了保证数据一致性,必须要在系统重启之后做全盘同步。然而全盘同步需要花费很长时间,bitmap的出现在一定程度上解决了这个问题,但却对阵列性能产生一定的消极作用。
经过了这么长的跋山涉水,终于又回到do_md_run的温暖怀抱了。这个函数不长,我们不厌其烦地再贴一次代码:
5158 static int do_md_run(struct mddev *mddev)
5159 {
5160         int err;
5161 
5162         err = md_run(mddev);
5163         if (err)
5164                 goto out;
5165         err = bitmap_load(mddev);
5166         if (err) {
5167                 bitmap_destroy(mddev);
5168                 goto out;
5169         }
5170 
5171         md_wakeup_thread(mddev->thread);
5172         md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */
5173 
5174         set_capacity(mddev->gendisk, mddev->array_sectors);
5175         revalidate_disk(mddev->gendisk);
5176         mddev->changed = 1;
5177         kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
5178 out:
5179         return err;
5180 }
5165行,加载bitmap,同样留到bitmap章节再详解。
5171行,唤醒阵列主线程。
5172行,唤醒阵列同步线程。
5174行,设置虚拟gendisk磁盘大小。
5175行,运行磁盘,让磁盘为系统可见。
5176行,设置md改变标志。
5177行,上报磁盘信息到udev。
do_md_run完成,RUN_ARRAY命令也就执行完成了。
小结一下,do_md_run函数的作用就是向上虚拟一个块设备,向下包装磁盘,建立读写请求的通道,将对md设备的请求能够转发到磁盘上去。

linux下raid(md)驱动源码解析相关推荐

  1. Linux下USB suspend/resume源码分析【转】

    转自:http://blog.csdn.net/aaronychen/article/details/3928479 Linux下USB suspend/resume源码分析 Author:aaron ...

  2. 【NanoPi T2】 7.uboot gmac网卡驱动(3) - 驱动源码解析

    1.mac控制器,phy芯片,rgmii协议 2.寄存器介绍 3.驱动源码解析 4.nanopi t2 移植rtl8211e网卡驱动(首发) 驱动注册入口 驱动的注册分两个部分,一个部分是静态编译的时 ...

  3. 《OV4689摄像头模组驱动源码解析》

    <OV4689摄像头模组驱动源码解析> OV4689是一款广泛应用于工业.安防等领域的高清图像传感器.在这篇文章中,我们将介绍如何在单片机上使用OV4689模组,并提供相应的驱动源码. O ...

  4. mysql驱动源码解析_mysql驱动源码分析

    注: 本系列文章使用JDK1.5 数据库驱动版本 mysql-connector-java-5.1.8b JAVA连接数据库是其众多功能中的一部分,主要有两种方式连接DataBase: 一种是采用JD ...

  5. rtl8139网卡驱动源码解析

    学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难免让人有心惊胆颤,捉襟见肘的感觉.读Linux网卡驱动也是一样.那长长的源码夹杂着那些我们陌生的变量和符号,望而生畏便 ...

  6. Linux下如何编译Android源码~~~

    使用的是putty.exe软件 本身机器windows环境连接服务器的linux环境编译android putty.exe界面在上面有提到了 输入你的账号密码后会进入 这样的效果就算是成功了可以ls就 ...

  7. Linux系统e1000e网络驱动源码(最深入)分析过程

    简介 本文讲解得e1000e网卡驱动主要用于intel网卡,以驱动的设计流程,分析整个驱动的接收和发送包过程. 首先介绍4个e1000e基础知识: 1)PCIE的配置空间初始化:PCIE卡都遵循一个标 ...

  8. linux下Ubuntu交叉编译boost源码编译

    源码下载,选择需要的版本下载压缩包然后解压. https://boostorg.jfrog.io/ui/native/main/release/ 1.解压后执行脚本 ./bootstrap.sh 2. ...

  9. 【SystemTap】 Linux下安装使用SystemTap源码安装SystemTap

    文章 http://blog.csdn.net/zklth/archive/2010/09/28/5912785.aspx 介绍的是使用CentOS默认的SystemTap,这里介绍使用SystemT ...

最新文章

  1. Spring-boot国际化
  2. 【H.264/AVC视频编解码技术具体解释】十三、熵编码算法(4):H.264使用CAVLC解析宏块的残差数据...
  3. Java之控制反转和依赖注入
  4. Android之Menu动态改变文字
  5. 十条实用的jQuery代码片段
  6. nat+端口转发,使得宿主机secureCRT可以访问vbox里linux虚拟机
  7. Android之SurfaceView简介(一)
  8. 字段的某记录相同,但是时间不同,找到MySQL里面最新的数据
  9. [从零开始]HelloWorld——第一个应用程序
  10. MySQL is running but PID file could not be found(在macOS系统下解决方法)
  11. 添加native和java系统服务
  12. lmi克罗内克积 matlab_行正交矩阵的分层克罗内克积扩大法
  13. 网络七层模型及各层协议
  14. 计算机科学的特刊,科学网—SCI收录期刊《IEEE计算机图形与应用杂志》特刊预报 - 万跃华的博文...
  15. python文本文件操作诗句给上一句输出下一句_使用RNN生成文本实战:莎士比亚风格诗句...
  16. Linksys玩多了,来看看真正的Cisco~技术帖
  17. 周师计算机专业学校分数线,周口师范学院是几本?录取分数线是多少
  18. Python解决线性规划问题
  19. 19号笔刷 ps_【精品】ps19号笔刷如何设置?19号笔刷的设置技巧!
  20. CMakeCMakeList.txt

热门文章

  1. 将excel内列表每行数据分别填充到另一个word模板里-批量生成同一模板下的不同文档,word邮件功能
  2. 根轨迹图、Bode图、Nyquist图的Matlab仿真
  3. 汉诺塔(内部+伪图形)
  4. R3.6.3下载 Rstudio下载及安装,网盘链接永久有效
  5. Caching best practices max-age
  6. Excel 2010中上下标的输入方法
  7. Android OkHttp是如何上位的?
  8. Bootstrap(ui框架)
  9. Saber 能量激光描边光效插件
  10. JSD-2204-API-线程并发-集合-Day08