linux内核奇遇记之md源代码解读之六

linux内核奇遇记之md源代码解读之六
转载请注明出处:http://blog.csdn.net/liumangxiong
raid10的run函数与raid5的run函数最大区别在于setup_conf,那就直接深入核心:
[cpp]  view plain  copy
  1. 3540 static struct r10conf *setup_conf(struct mddev *mddev)  
  2. 3541 {  
  3. 3542         struct r10conf *conf = NULL;  
  4. 3543         int err = -EINVAL;  
  5. 3544         struct geom geo;  
  6. 3545         int copies;  
  7. 3546   
  8. 3547         copies = setup_geo(&geo, mddev, geo_new);  
  9. 3548   
  10. 3549         if (copies == -2) {  
  11. 3550                 printk(KERN_ERR "md/raid10:%s: chunk size must be "  
  12. 3551                        "at least PAGE_SIZE(%ld) and be a power of 2.\n",  
  13. 3552                        mdname(mddev), PAGE_SIZE);  
  14. 3553                 goto out;  
  15. 3554         }  
  16. 3555   
  17. 3556         if (copies < 2 || copies > mddev->raid_disks) {  
  18. 3557                 printk(KERN_ERR "md/raid10:%s: unsupported raid10 layout: 0x%8x\n",  
  19. 3558                        mdname(mddev), mddev->new_layout);  
  20. 3559                 goto out;  
  21. 3560         }  
  22. 3561   
  23. 3562         err = -ENOMEM;  
  24. 3563         conf = kzalloc(sizeof(struct r10conf), GFP_KERNEL);  
  25. 3564         if (!conf)  
  26. 3565                 goto out;  
  27. 3566   
  28. 3567         /* FIXME calc properly */  
  29. 3568         conf->mirrors = kzalloc(sizeof(struct raid10_info)*(mddev->raid_disks +  
  30. 3569                                                             max(0,-mddev->delta_disks)),  
  31. 3570                                 GFP_KERNEL);  
  32. 3571         if (!conf->mirrors)  
  33. 3572                 goto out;  
  34. 3573   
  35. 3574         conf->tmppage = alloc_page(GFP_KERNEL);  
  36. 3575         if (!conf->tmppage)  
  37. 3576                 goto out;  
  38. 3577   
  39. 3578         conf->geo = geo;  
  40. 3579         conf->copies = copies;  
  41. 3580         conf->r10bio_pool = mempool_create(NR_RAID10_BIOS, r10bio_pool_alloc,  
  42. 3581                                            r10bio_pool_free, conf);  
  43. 3582         if (!conf->r10bio_pool)  
  44. 3583                 goto out;  
  45. 3584   
  46. 3585         calc_sectors(conf, mddev->dev_sectors);  
  47. 3586         if (mddev->reshape_position == MaxSector) {  
  48. 3587                 conf->prev = conf->geo;  
  49. 3588                 conf->reshape_progress = MaxSector;  
  50. 3589         } else {  
  51. 3590                 if (setup_geo(&conf->prev, mddev, geo_old) != conf->copies) {  
  52. 3591                         err = -EINVAL;  
  53. 3592                         goto out;  
  54. 3593                 }  
  55. 3594                 conf->reshape_progress = mddev->reshape_position;  
  56. 3595                 if (conf->prev.far_offset)  
  57. 3596                         conf->prev.stride = 1 << conf->prev.chunk_shift;  
  58. 3597                 else  
  59. 3598                         /* far_copies must be 1 */  
  60. 3599                         conf->prev.stride = conf->dev_sectors;  
  61. 3600         }  
  62. 3601         spin_lock_init(&conf->device_lock);  
  63. 3602         INIT_LIST_HEAD(&conf->retry_list);  
  64. 3603   
  65. 3604         spin_lock_init(&conf->resync_lock);  
  66. 3605         init_waitqueue_head(&conf->wait_barrier);  
  67. 3606   
  68. 3607         conf->thread = md_register_thread(raid10d, mddev, "raid10");  
  69. 3608         if (!conf->thread)  
  70. 3609                 goto out;  
  71. 3610   
  72. 3611         conf->mddev = mddev;  
  73. 3612         return conf;  

3547行,设置raid10布局,这个函数代码很简单,但意义很重要,特别是在处理读写流程里要对这个布局十分清楚。看setup_geo函数:
[cpp]  view plain  copy
  1. 3498 enum geo_type {geo_new, geo_old, geo_start};  
  2. 3499 static int setup_geo(struct geom *geo, struct mddev *mddev, enum geo_type new)  
  3. 3500 {  
  4. 3501         int nc, fc, fo;  
  5. 3502         int layout, chunk, disks;  
  6. 3503         switch (new) {  
  7. 3504         case geo_old:  
  8. 3505                 layout = mddev->layout;  
  9. 3506                 chunk = mddev->chunk_sectors;  
  10. 3507                 disks = mddev->raid_disks - mddev->delta_disks;  
  11. 3508                 break;  
  12. 3509         case geo_new:  
  13. 3510                 layout = mddev->new_layout;  
  14. 3511                 chunk = mddev->new_chunk_sectors;  
  15. 3512                 disks = mddev->raid_disks;  
  16. 3513                 break;  
  17. 3514         default/* avoid 'may be unused' warnings */  
  18. 3515         case geo_start: /* new when starting reshape - raid_disks not 
  19. 3516                          * updated yet. */  
  20. 3517                 layout = mddev->new_layout;  
  21. 3518                 chunk = mddev->new_chunk_sectors;  
  22. 3519                 disks = mddev->raid_disks + mddev->delta_disks;  
  23. 3520                 break;  
  24. 3521         }  
  25. 3522         if (layout >> 18)  
  26. 3523                 return -1;  
  27. 3524         if (chunk < (PAGE_SIZE >> 9) ||  
  28. 3525             !is_power_of_2(chunk))  
  29. 3526                 return -2;  
  30. 3527         nc = layout & 255;  
  31. 3528         fc = (layout >> 8) & 255;  
  32. 3529         fo = layout & (1<<16);  
  33. 3530         geo->raid_disks = disks;  
  34. 3531         geo->near_copies = nc;  
  35. 3532         geo->far_copies = fc;  
  36. 3533         geo->far_offset = fo;  
  37. 3534         geo->far_set_size = (layout & (1<<17)) ? disks / fc : disks;  
  38. 3535         geo->chunk_mask = chunk - 1;  
  39. 3536         geo->chunk_shift = ffz(~chunk);  
  40. 3537         return nc*fc;  
  41. 3538 }  

raid10有近拷贝和远拷贝的设置,简单地说,近拷贝就是组成raid1的镜像磁盘数,远拷贝就是每个磁盘划分为几部分存镜像数据。
3503行,这里传进来的参数是geo_new,转到3509行。
3510行,raid10的layout,默认是0x102,即near_copies=2, far_copies=1。
3511行,chunk size。
3512行,数据盘个数。
3522-3526行,参数合法性检查。
3527行,计算near_copies。
3528行,计算far_copies。
3529行,计算far_offset。
3537行,返回拷贝数。
从上面的代码可以知道,raid10每一份可以有nc*fc份拷贝,但实际应用中考虑到磁盘的利用率,一般采用nc=2, fc=1。
回到setup_conf函数中,
3563行,申请struct r10conf内存空间。
3568行,申请struct raid10_info内存空间。
3574行,申请一个page页,用于读磁盘的临时空间。
3579行,设置数据拷贝数。
3580行,创建struct r10bio内存池,那为什么要有这样一个新的bio呢?原因是raid10大多数情况下io流分二个步骤,例如写同一份数据到两个磁盘,重建时先读数据再把数据写到另一个磁盘上,所以需要一个大的bio来跟踪io流过程。
3585行,计算磁盘实际用于阵列条带的扇区数和阵列存放远拷贝的跨度大小。假设我们用整个磁盘空间创建阵列raid10,但是由于每个磁盘空间大小有差别,阵列会按最小的磁盘空间来创建,这里实际用于阵列的磁盘空间将小于磁盘空间。再次,如果磁盘空间不是整数倍条块大小,这时多余部分还会被空闲出来。同样地,如果raid10远拷贝为2,这时磁盘条块数不能整除3,那么又有一部分磁盘空间空闲出来。正是因为如此,conf->dev_sectors会小于等于mddev->dev_sectors。理解了这些,那么看calc_sectors函数就容易了:
[cpp]  view plain  copy
  1. 3468 static void calc_sectors(struct r10conf *conf, sector_t size)  
  2. 3469 {  
  3. 3470         /* Calculate the number of sectors-per-device that will 
  4. 3471          * actually be used, and set conf->dev_sectors and 
  5. 3472          * conf->stride 
  6. 3473          */  
  7. 3474   
  8. 3475         size = size >> conf->geo.chunk_shift;  
  9. 3476         sector_div(size, conf->geo.far_copies);  
  10. 3477         size = size * conf->geo.raid_disks;  
  11. 3478         sector_div(size, conf->geo.near_copies);  
  12. 3479         /* 'size' is now the number of chunks in the array */  
  13. 3480         /* calculate "used chunks per device" */  
  14. 3481         size = size * conf->copies;  
  15. 3482   
  16. 3483         /* We need to round up when dividing by raid_disks to 
  17. 3484          * get the stride size. 
  18. 3485          */  
  19. 3486         size = DIV_ROUND_UP_SECTOR_T(size, conf->geo.raid_disks);  
  20. 3487   
  21. 3488         conf->dev_sectors = size << conf->geo.chunk_shift;  
  22. 3489   
  23. 3490         if (conf->geo.far_offset)  
  24. 3491                 conf->geo.stride = 1 << conf->geo.chunk_shift;  
  25. 3492         else {  
  26. 3493                 sector_div(size, conf->geo.far_copies);  
  27. 3494                 conf->geo.stride = size << conf->geo.chunk_shift;  
  28. 3495         }  
  29. 3496 }  

3470行,计算每个磁盘实际使用扇区数,并设置conf->dev_sectors和conf->stride。stride是什么意思呢?大步,跨幅。就是同一磁盘上每个far_copies之间的距离,这里有两个可能,一是远拷贝数据间隔存放,一是远拷贝数据相邻存放。conf->geo.far_offset不为0时表示相邻存放,为0时表示间隔存放,间隔多远呢?就由conf->geo.stride决定。
3475行,函数传入参数size为磁盘空间大小,这里转换为条块数。
3476行,除以远拷贝数,转换为单份数据空间大小。
3477-3478行,乘以数据盘数,再除以近拷贝数,转换为总数据空间大小(没有拷贝)。
3481行,乘以拷贝数,可用阵列空间大小。
3486行,除以数据盘数,每个磁盘用于阵列的实际使用空间大小。
3488行,乘以条块大小,单位由条块数转换为扇区数。
这里为什么又乘又除的呢?这就好比有一张长方形的纸,我们要用这张纸分出四个最大的正方形出来,那么就选个角按45度折起来,然后把之外的部分裁掉。
3490行,远拷贝是相邻存放的,那么跨幅就是一个条块。
3493行,远拷贝是间隔存放的,那么跨幅就是磁盘实际使用空间大小size除以远拷贝数。

再次返回到setup_conf函数中。
3586行,没有reshape操作,reshape等于MaxSector。
3601-3611行,conf初始化。
3607行,创建raid10d主线程。
这样setup_conf函数就结束了,run函数剩余部分都是按步就班。
小结一下,各种级别阵列运行函数建立与磁盘之间的关联,建立数据流通道,申请数据流所需要的资源。不同的是,由于阵列级别差异,阵列与磁盘之间关联不一样,申请资源也不一样。
阵列已经运行起来了,那么第一件事情就是同步了。下一节开始介绍阵列同步。
转载请注明出处:http://blog.csdn.net/liumangxiong
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MDLinux内核中的一个模块,可实现磁盘阵列的软件级RAID,它和RAID0、RAID1、RAID4、RAID5、RAID6一样,就是一种磁盘阵列方案。 MD中最主要的部分是驱动程序,它运行在内核态中。它将多个磁盘设备组合在一起,成为一个逻辑设备,该逻辑设备对应着一个块设备文件。在这个逻辑设备上,可实现磁盘阵列的软件级RAID功能。 MD驱动程序的主要源代码是在/drivers/md目录下的md.c文件中,它包括了MD的全部源代码,还有一些其他相关文件,比如raid5.c等。 在这个文件中,最值得学习的是内核的模块化编程思想。模块化编程是一种将代码划分为模块的软件设计方法,通过将代码划分为不同的模块,实现代码的解耦、可重用、可维护性等目标。 在MD.c中我们还可以看到内核中的锁、内存管理等基本的内核技术的应用。通过对MD.c进行源代码解读,能够深入了解Linux内核的实现原理,特别是MD的RAID功能的实现,对于我们进一步学习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、付费专栏及课程。

余额充值