Linux 内核中RAID5源码详解之RAID模块声明

来源:互联网 发布:中国外汇储备数据 编辑:程序博客网 时间:2024/06/02 01:21

Linux 内核中RAID5源码详解之RAID模块声明


系统整体布局

在讲RAID5之前,我们先来思考下这个问题:我们平时写的C函数,比如说write() 是怎么将数据写到计算机的硬盘上的?整个系统指令执行的流程是什么?我们带着这个问题来了解系统的整体布局。
layout
这是系统的整体布局的示意图,比如说问题中的write() 执行流程则为用户态调用该函数,然后进行系统调用,将指令和参数传给虚拟文件系统层,然后再具体的文件系统上执行,文件系统再将指令和参数转换为写请求(包含地址和数据)传给通用设备层,最后通过设备驱动将数据写到物理硬盘上。可是我们会想了,这个问题和我们要讲的RAID5有啥关系呢?别急嘛,好酒要一点点品,同样了解了整个系统的布局再具体到某一点,应该会理解的更好一点哦~


MD及RAID模块

接下来就来看看MD和RAID模块在上述的布局中起到什么作用。首先如果将MD和RAID模块加入到上图中的话,那加到什么位置呢?下图告诉你答案。
md
从图中可看出,文件系统将请求(写的地址和数据)发送给下层的MD模块,而MD模块则选用一种阵列结构来执行这条指令,于是指令被传送到了RAID上,由于RAID有好几种等级,RAID0,RAID1,RAID5,RAID6,RAID10等等,经过RAID模块时,会将请求中的地址转换为某块盘上的偏移量,同时根据写的数据以及原来的数据计算新的parity(校验位),再将数据和parity写到物理硬盘上。所以MD和RAID模块起到一个承上启下的作用,但是MD和RAID之间有什么联系呢?
这里写图片描述
其实RAID是MD上的一个子模块,上层将请求传到MD,MD再讲请求发送到具体的子模块上,再通过这些子模块执行具体的请求。也可以看成MD实现了下层设备阵列的抽象。


模块的声明

由于内核将MD和RAID模块化,可是怎么通过代码的形式来识别这些模块呢?首先我们看看RAID5模块的声明(MD模块的声明和这个类似,我们最关注的是RAID模块),代码在raid5.c中(其实包含的是raid4、raid5、raid6的源代码)。

static int __init raid5_init(void){    raid5_wq = alloc_workqueue("raid5wq",        WQ_UNBOUND|WQ_MEM_RECLAIM|WQ_CPU_INTENSIVE|WQ_SYSFS, 0);    if (!raid5_wq)        return -ENOMEM;    register_md_personality(&raid6_personality);    register_md_personality(&raid5_personality);    register_md_personality(&raid4_personality);    return 0;}static void raid5_exit(void){    unregister_md_personality(&raid6_personality);    unregister_md_personality(&raid5_personality);    unregister_md_personality(&raid4_personality);    destroy_workqueue(raid5_wq);}module_init(raid5_init);module_exit(raid5_exit);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("RAID4/5/6 (striping with parity) personality for MD");MODULE_ALIAS("md-personality-4"); /* RAID5 */MODULE_ALIAS("md-raid5");MODULE_ALIAS("md-raid4");MODULE_ALIAS("md-level-5");MODULE_ALIAS("md-level-4");MODULE_ALIAS("md-personality-8"); /* RAID6 */MODULE_ALIAS("md-raid6");MODULE_ALIAS("md-level-6");/* This used to be two separate modules, they were: */MODULE_ALIAS("raid5");MODULE_ALIAS("raid6");

结合下面语句的解释看,结果一目了然啦。

Module_init(raid5_init);//模块的初始化
Module_exit(raid5_exit);//模块的反初始化
MODULE_LICENSE(“GPL”);//模块的证书版本
MODULE_DESCRIPTION(XXX);//模块的描述,human-readable
MODULE_ALIAS(xx);//模块的别名

RAID5模块的初始化是由raid5_init() 函数来完成的,追踪此函数发现这条语句register_md_personality(&raid5_personality); ,这条的意思是将raid5_personality注册到md_personality中。nice,现在就来看看raid5_personality是什么:

static struct md_personality raid5_personality ={    .name       = "raid5",    .level      = 5,    .owner      = THIS_MODULE,    .make_request   = make_request,    .run        = run,    .free       = raid5_free,    .status     = status,    .error_handler  = error,    .hot_add_disk   = raid5_add_disk,    .hot_remove_disk= raid5_remove_disk,    .spare_active   = raid5_spare_active,    .sync_request   = sync_request,    .resize     = raid5_resize,    .size       = raid5_size,    .check_reshape  = raid5_check_reshape,    .start_reshape  = raid5_start_reshape,    .finish_reshape = raid5_finish_reshape,    .quiesce    = raid5_quiesce,    .takeover   = raid5_takeover,    .congested  = raid5_congested,    .mergeable_bvec = raid5_mergeable_bvec,};

也许开始看这个时发现不是很懂,那好我们再把md_personality贴出来,看看有什么联系:

struct md_personality{    char *name;    int level;    struct list_head list;    struct module *owner;    void (*make_request)(struct mddev *mddev, struct bio *bio);    int (*run)(struct mddev *mddev);    void (*free)(struct mddev *mddev, void *priv);    void (*status)(struct seq_file *seq, struct mddev *mddev);    /* error_handler must set ->faulty and clear ->in_sync     * if appropriate, and should abort recovery if needed     */    void (*error_handler)(struct mddev *mddev, struct md_rdev *rdev);    int (*hot_add_disk) (struct mddev *mddev, struct md_rdev *rdev);    int (*hot_remove_disk) (struct mddev *mddev, struct md_rdev *rdev);    int (*spare_active) (struct mddev *mddev);    sector_t (*sync_request)(struct mddev *mddev, sector_t sector_nr, int *skipped, int go_faster);    int (*resize) (struct mddev *mddev, sector_t sectors);    sector_t (*size) (struct mddev *mddev, sector_t sectors, int raid_disks);    int (*check_reshape) (struct mddev *mddev);    int (*start_reshape) (struct mddev *mddev);    void (*finish_reshape) (struct mddev *mddev);    /* quiesce moves between quiescence states     * 0 - fully active     * 1 - no new requests allowed     * others - reserved     */    void (*quiesce) (struct mddev *mddev, int state);    /* takeover is used to transition an array from one     * personality to another.  The new personality must be able     * to handle the data in the current layout.     * e.g. 2drive raid1 -> 2drive raid5     *      ndrive raid5 -> degraded n+1drive raid6 with special layout     * If the takeover succeeds, a new 'private' structure is returned.     * This needs to be installed and then ->run used to activate the     * array.     */    void *(*takeover) (struct mddev *mddev);    /* congested implements bdi.congested_fn().     * Will not be called while array is 'suspended' */    int (*congested)(struct mddev *mddev, int bits);    /* mergeable_bvec is use to implement ->merge_bvec_fn */    int (*mergeable_bvec)(struct mddev *mddev,                  struct bvec_merge_data *bvm,                  struct bio_vec *biovec);};

这下清楚了吧,哦,原来这是一个个指针函数,可是这两个是怎么联系到一起的呢?我们再回头看register_md_personality(&raid5_personality):

int register_md_personality(struct md_personality *p){    printk(KERN_INFO "md: %s personality registered for level %d\n",                        p->name, p->level);    spin_lock(&pers_lock);//加锁    list_add_tail(&p->list, &pers_list);//添加到pers_list中    spin_unlock(&pers_lock);//解锁    return 0;}

这个函数最重要的一行就是讲传进来的md_personality结构添加到pers_list中,pers_list维护了整个MD的md_personality结构。可是如何通过MD调用到RAID5呢?细心了解源码,在md_run() 中给出了这种调用关系,由于函数过长,我们只贴出所需要的地方,首先获取对应RAID级别的md_personality结构:pers = find_pers(mddev->level, mddev->clevel); ,追踪这个函数:

static struct md_personality *find_pers(int level, char *clevel){    struct md_personality *pers;    list_for_each_entry(pers, &pers_list, list) {//查找相应的pers        if (level != LEVEL_NONE && pers->level == level)            return pers;        if (strcmp(pers->name, clevel)==0)            return pers;    }    return NULL;}

仔细看注释的那句,居然有pers_list,好眼熟,这不就是register时那个全局list嘛!!!哎呀,好巧好巧,所以这个函数就是在pers_list中一个个比,如果level相同或者name相同,则找到了对应的md_personality结构,返回。

查看md.c中的代码,会发现有很多地方出现了如下形式的语句:md->pers->make_request ,这就是通过模块的声明,将RAID模块声明到MD模块中,通过md_personality的结构调用指针函数,从而实现RAID的功能,嘻嘻,回过头来看看也不是很复杂耶~

至此,RAID模块的声明已经完成,嘻嘻,不难吧,同样,对于模块的删除,在raid5_exit() 中:

static void raid5_exit(void){    unregister_md_personality(&raid6_personality);    unregister_md_personality(&raid5_personality);    unregister_md_personality(&raid4_personality);    destroy_workqueue(raid5_wq);}

追踪unregister_md_personality()

int unregister_md_personality(struct md_personality *p){    printk(KERN_INFO "md: %s personality unregistered\n", p->name);    spin_lock(&pers_lock);    list_del_init(&p->list);    spin_unlock(&pers_lock);    return 0;}

和register相反,只是从链表中删除而已,道理一样。

关于模块的声明和删除已经讲得差不多了,下篇主要介绍下内核中RAID5的stripe_head管理和守护线程raid5d,这是了解RAID5运行原理的基石哦。


0 0
原创粉丝点击