免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1279 | 回复: 0
打印 上一主题 下一主题

Multiple device driver 源文件不完全注释 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-07-23 10:35 |只看该作者 |倒序浏览
这篇是2年前写的东西,有不少需要改进的地方;暂且记录下来,在以后的文章中来改进之。:D
本文将从multiple devie driver的加载、卸载、ioctl系统调用的实现等线索,对md驱动中的具体实现加以讨论。
另外,本文将从md驱动中内核线程的使用方面进行一些讨论,借以从更加小粒度的地方了解md driver的具体实现。
在md.c的代码中,还涉及到md随系统启动的管理、与/proc文件系统的交互等等细节,在以下适当地地方将给出讨论。
本文所讨论的linux kernel为Redhat Linux AS 4.0 所使用的版本2.6.9。其中所涉及到的一些部分,可能和其他linux
kernel版本中的md driver有所出入,请注意。
文中深红色加粗字体所表示的是对代码的注释,青色加粗的字体表示的是代码。
1 module加载、卸载
在module加载时,将调用md_init函数;在卸载时,将调用md_exit函数。md_init的原形如下:
int __init md_init(void)
{
……
if (register_blkdev(MAJOR_NR, "md"))/*register md block device. The major
is number 9 with name 'md' have unpartitioned md arrays, one per minor number.
*/

return -1;
if ((mdp_major=register_blkdev(0, "mdp"))proc_fops = &md_seq_fops;/* set proc filesystem operator*/
}
在module卸载的时候调用md_exit函数,释放一些资源,其原形如下:
static __exit void md_exit(void)
与module加载时调用的md_init()函数对应,md_exit()函数解除md和mdp的注册、解除所有md设备在devfs中的注册、解除reboot
notifier、解除系统调用接口和proc文件系统的接口。以上,没有在md_exit()的代码中给出注释,这些可以参照md_init()函数中的注释,对比来看。
2 ioctl系统调用
在md_u.h中定义的ioctl command如下:
#define RAID_VERSION _IOR (MD_MAJOR, 0x10, mdu_version_t)
#define GET_ARRAY_INFO _IOR (MD_MAJOR, 0x11, mdu_array_info_t)
#define GET_DISK_INFO _IOR (MD_MAJOR, 0x12, mdu_disk_info_t)
#define PRINT_RAID_DEBUG _IO (MD_MAJOR, 0x13)
#define RAID_AUTORUN _IO (MD_MAJOR, 0x14)
以上ioctl command获取multiple
device的状态信息。这些信息包括,RAID的level值、mdu_array_info_s结构形式的diskarray信息、mdu_disk_info_s结构形式的disk信息等。
#define CLEAR_ARRAY _IO (MD_MAJOR, 0x20)
#define ADD_NEW_DISK _IOW (MD_MAJOR, 0x21, mdu_disk_info_t)
#define HOT_REMOVE_DISK _IO (MD_MAJOR, 0x22)
#define SET_ARRAY_INFO _IOW (MD_MAJOR, 0x23, mdu_array_info_t)
#define SET_DISK_INFO _IO (MD_MAJOR, 0x24)
#define WRITE_RAID_INFO _IO (MD_MAJOR, 0x25)
#define UNPROTECT_ARRAY _IO (MD_MAJOR, 0x26)
#define PROTECT_ARRAY _IO (MD_MAJOR, 0x27)
#define HOT_ADD_DISK _IO (MD_MAJOR, 0x28)
#define SET_DISK_FAULTY _IO (MD_MAJOR, 0x29)
#define HOT_GENERATE_ERROR _IO (MD_MAJOR, 0x2a)
以上ioctl command配置multiple
device设备。这些command主要就是管理diakarray,涉及配置diskarray的信息、add disk以及set disk
fault等操作。
#define RUN_ARRAY _IOW (MD_MAJOR, 0x30, mdu_param_t)
#define START_ARRAY _IO (MD_MAJOR, 0x31)
#define STOP_ARRAY _IO (MD_MAJOR, 0x32)
#define STOP_ARRAY_RO _IO (MD_MAJOR, 0x33)
#define RESTART_ARRAY_RW _IO (MD_MAJOR, 0x34)
以上ioctl command主要控制diskarray的使用。涉及的操作主要有start, stop, restart等。
通过这些ioctl command可以在user space控制multiple device。如果参考mdadm的源码可以清楚地看出这些ioctl
command是如何调用的。Mdadm的源码分析将在另外一篇文章中给出。
2.1 ioctl函数的实现
在md.c中,ioctl函数对参数作一些检查和设置相应的数据结构,然后调用不同的函数来完成这些系统调用。其具体定义如下:
static int md_ioctl(struct inode *inode, struct file *file,unsigned int
cmd, unsigned long arg)

{
。。。。。。
/* Commands dealing with the RAID driver but not any particular array:
*/

switch (cmd)
{
case RAID_VERSION: err = get_version(argp); goto done;
case PRINT_RAID_DEBUG: err = 0; md_print_devices(); goto done;
#ifndef MODULE
case RAID_AUTORUN: err = 0; autostart_arrays(arg); goto done;
#endif
default:;
}
/* Commands creating/starting a new array:*/
if (cmd == START_ARRAY) {
/* START_ARRAY doesn't need to lock the array as autostart_array does the
locking, and it could even be a different array */

。。。。。。
err = autostart_array(new_decode_dev(arg));
。。。。。。
}
err = mddev_lock(mddev);/*lock the mddev*/
switch (cmd)
{
case SET_ARRAY_INFO:
{
/*copy array info data from userspace*/
if (mddev->pers) {
err = update_array_info(mddev, &info);
/*if error, do some thing*/
}
err = set_array_info(mddev, &info);
。。。。。。
}
/* Commands querying/configuring an existing array:
if we are initialised yet, only ADD_NEW_DISK or STOP_ARRAY is allowed
*/

if (!mddev->raid_disks && cmd != ADD_NEW_DISK && cmd !=
STOP_ARRAY && cmd != RUN_ARRAY) {

err = -ENODEV; goto abort_unlock;
}
/* Commands even a read-only array can execute: */
switch (cmd)
{
case GET_ARRAY_INFO: err = get_array_info(mddev, argp); goto
。。。;
case GET_DISK_INFO: err = get_disk_info(mddev, argp);goto done_unlock;
case RESTART_ARRAY_RW:err = restart_array(mddev);goto done_unlock;
case STOP_ARRAY: err = do_md_stop (mddev, 0); goto done_unlock;
case STOP_ARRAY_RO: err = do_md_stop (mddev, 1); goto done_unlock;
/* We have a problem here : there is no easy way to give a CHS virtual
geometry. We currently pretend that we have a 2 heads 4 sectors (with a BIG
number of cylinders...). This drives dosfs just mad... ;-) */

case HDIO_GETGEO:
if (!loc) {
err = -EINVAL;
goto abort_unlock;
}
err = put_user (2, (char __user *) &loc->heads);
if (err)
goto abort_unlock;
err = put_user (4, (char __user *) &loc->sectors);
if (err)
goto abort_unlock;
err = put_user(get_capacity(mddev->gendisk)/8,
(short __user *) &loc->cylinders);
if (err)
goto abort_unlock;
err = put_user (get_start_sect(inode->i_bdev),
(long __user *) &loc->start);
goto done_unlock;
}
/* The remaining ioctls are changing the state of the superblock, so we do
not allow read-only arrays here: */

switch (cmd)
{
case ADD_NEW_DISK:
{
/*copy mddev info from user space, then…*/
err = add_new_disk(mddev, &info); goto done_unlock;
}
case HOT_REMOVE_DISK:
err = hot_remove_disk(mddev, new_decode_dev(arg));goto done_unlock;
case HOT_ADD_DISK:
err = hot_add_disk(mddev, new_decode_dev(arg)); goto done_unlock;
case SET_DISK_FAULTY:
err = set_disk_faulty(mddev, new_decode_dev(arg)); goto done_unlock;
case RUN_ARRAY:
err = do_md_run (mddev); goto done_unlock;
default:
。。。。。。; goto abort_unlock;
}
。。。。。。
}
从以上ioctl函数的定义,可以找到各个ioctl command对应的处理函数。其中一些的具体实现,在下面给出解释。
2.2 START_ARRAY ioctl具体实现
由以上ioctl的实现函数中可以看出,START_ARRAY系统调用在md.c中是由autostart_array()函数实现的。
在autostart_array()中,首先,依据传进来的参数dev_t
startdev,把md设备在内核中的表示结构(mdk_rdev_t类型)引入到内核空间,这部分由md_import_device()函数实现;接着,对这个md设备进行验证;验证通过把这个设备加入pending_raid_disks链表;然后,循环遍历和加载子设备;最后,调用autorun_devices()启动md设备。
使用autorun_devices()函数启动设备的过程如下:
遍历pending_raid_disks链表,拿出一个挂起的disk,找到与其有相同UUID的disk,把所有这些disk从挂起链表转移到“same_set”链表。在“same_set”链表中找出superblock最新的一个,然后检查其superblock。如果,一切正常,那么run这个设备。
3 对superblock 0.9/1.0的支持
在md.c中,通过以下的结构数组来实现对两种superblock的支持。其具体定义如下:
struct super_type super_types[] = {
[0] = {
.name = "0.90.0",
.owner = THIS_MODULE,
.load_super = super_90_load,
.validate_super = super_90_validate,
.sync_super = super_90_sync,
},
[1] = {
.name = "md-1",
.owner = THIS_MODULE,
.load_super = super_1_load,
.validate_super = super_1_validate,
.sync_super = super_1_sync,
},
};
其中,数组的两个元素分别存储对superblock
0.9.0/1.0的操作函数指针,这些函数在md.c中定义。通过这些函数,可以完成对superblock的操作。
在md_p.h中分别定义了与物理硬盘中存储的数据相对应的superblock结构,它们分别是mdp_superblock_s(表示与0.9v的superblock相对应的数据结构)和mdp_superblock_1(表示与1.0v的superblock相对应的数据结构)。这两个数据结构的具体定义,可以参见md_p.h中的定义和注释。
3.1 对superblock的操作
这些操作涉及到写入、更新、验证、删除等操作。在md.c中,通过
struct super_type {
char *name;
struct module *owner;
int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int
minor_version);

int (*validate_super)(mddev_t *mddev, mdk_rdev_t *rdev);
void (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev);
};
来保存控制superblock的各种操作函数。之所以这样设计,是因为在Linux kernel 2.6.9的md
driver中,可以对superblcok
0.90.0和1.0两种superblcok版本进行操作。这样,就可以使用一个接口,实现对两种版本superblcok的读写、更新控制。其中,具体的superblcok接口函数定义如下:
int load_super(mdk_rdev_t *dev, mdk_rdev_t *refdev, int minor_version)
/*loads and validates a superblock on dev. if refdev != NULL, compare
superblocks on both devices

Return:
0 - dev has a superblock that is compatible with refdev
1 - dev has a superblock that is compatible and newer than refdev so dev
should be used as the refdev in future

-EINVAL superblock incompatible or invalid
-othererror e.g. -EIO
*/
int validate_super(mddev_t *mddev, mdk_rdev_t *dev)
/*verify that dev is acceptable into mddev. The first time,
mddev->raid_disks will be 0, and data fromdev should be merged in. Subsequent
calls check that dev is new enough.

Return 0 or -EINVAL
*/
void sync_super(mddev_t *mddev, mdk_rdev_t *dev)
/*Update the superblock for rdev with data in mddev. This does not write
to disc.*/

super_types[]数组中可以看出在md.c中,所使用的几个函数。以下,对superblock 0.90.0的几个操作函数进行分析。
加载superblock 0.90.0所使用的函数为static int super_90_load(),其具体定义如下:
static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int
minor_version)

{
……
/* calculate the position of the superblock, it's at the end of the
disk.*/

sb_offset = calc_dev_sboffset(rdev->bdev);
rdev->sb_offset = sb_offset;
ret = read_disk_sb(rdev);/*read info. From harddisk.*/
……
sb = (mdp_super_t*)page_address(rdev->sb_page);
/*fill the info of rdev below*/
……
/*check sb*/
……
rdev->preferred_minor = sb->md_minor;
rdev->data_offset = 0;
if (sb->level == MULTIPATH)
rdev->desc_nr = -1;
else
rdev->desc_nr = sb->this_disk.number;
if (refdev == 0)
ret = 1;
else {……
}
rdev->size = calc_dev_size(rdev, sb->chunk_size);
abort:
return ret;
}
在读写数据的时候,使用了bio接口。具体的读写函数如下:
static int sync_page_io(struct block_device *bdev, sector_t sector, int
size,

struct page *page, int rw)
{
struct bio bio;
struct bio_vec vec;
struct completion event;
……/*set bio field*/
submit_bio(rw, &bio);
……
}
其他对于superblock的操作,与以上操作类似,这里就不再说明了。
4 md driver中内核线程的使用
在md.c中,定义了如下包含md driver中的线程的相关数据的结构,其定义如下:
typedef struct mdk_thread_s {
        void (*run) (mddev_t *mddev);/*pointer of thread method*/
        mddev_t *mddev;/*md info that the thread deal with*/
        wait_queue_head_t wqueue;
        unsigned long flags;
        struct completion *event;
        struct task_struct *tsk;
        const char *name;
} mdk_thread_t;
在md.c中,使用mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t
*mddev, const char *name)
函数注册一个内核线程;使用void
md_unregister_thread(mdk_thread_t *thread)
注销一个内核线程。在RAID需要resync, recovery,
start, stop等的时候使用以上函数来操纵线程。
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/5983/showart_344907.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP