md模块浅谈(一)加载和卸载

来源:互联网 发布:淘宝付款人数多久更新 编辑:程序博客网 时间:2024/05/22 14:40

先找到模块的入口和出口是哪个函数。

这个很简单,找到下面的代码,就一目了然了。

subsys_initcall(md_init);module_exit(md_exit)
入口
static int __init md_init(void){if (register_blkdev(MD_MAJOR, "md"))return -1;if ((mdp_major=register_blkdev(0, "mdp"))<=0) {unregister_blkdev(MD_MAJOR, "md");return -1;}blk_register_region(MKDEV(MD_MAJOR, 0), 1UL<<MINORBITS, THIS_MODULE,    md_probe, NULL, NULL);blk_register_region(MKDEV(mdp_major, 0), 1UL<<MINORBITS, THIS_MODULE,    md_probe, NULL, NULL);register_reboot_notifier(&md_notifier);raid_table_header = register_sysctl_table(raid_root_table);md_geninit();return 0;}

register_blkdev函数:用来注册一个新的块设备。

第一次用来注册主设备。其中"md”是设备名,宏MD_MAJOR的值是9,这个宏在"major.h"文件中有定义,代表第9号块设备。在Documentation/devices.txt文件中,我们可以找到该设备的说明。

  9 blockMetadisk (RAID) devices  0 = /dev/md0First metadisk group  1 = /dev/md1Second metadisk group    ...The metadisk driver is used to span afilesystem across multiple physical disks.

第二次用来注册patition。最多63个patition。

 

blk_register_region函数:用来创建块的必要结构

 

register_reboot_notifier函数:是把md_notifier这个结构体加到reboot_notifier_list链表中。md_notifier在md模块载入时被初始化成下面的摸样。

static struct notifier_block md_notifier = {.notifier_call= md_notify_reboot,.next= NULL,.priority= INT_MAX, /* before any real devices */};

这样,系统重启时,将会执行刚注册进去的md_notify_reboot函数,而且执行顺序早于其他真实设备。

 

kernel/notifier.c文件中定义了register_reboot_notifier函数。

int register_reboot_notifier(struct notifier_block *nb){return blocking_notifier_chain_register(&reboot_notifier_list, nb);}EXPORT_SYMBOL(register_reboot_notifier);

 

blocking_notifier_chain_register函数定义如下:

int blocking_notifier_chain_register(struct blocking_notifier_head *nh,struct notifier_block *n){int ret;/* * This code gets used during boot-up, when task switching is * not yet working and interrupts must remain disabled.  At * such times we must not call down_write(). */if (unlikely(system_state == SYSTEM_BOOTING))return notifier_chain_register(&nh->head, n);down_write(&nh->rwsem);ret = notifier_chain_register(&nh->head, n);up_write(&nh->rwsem);return ret;}EXPORT_SYMBOL_GPL(blocking_notifier_chain_register);


顺便说一下notifier chain。通过notifier_chain_register、notifier_chain_unregister、notifier_call_chain三个函数实现对重启链上的结点的追加、删除和执行。

模式atomicblockingrawsrcunotifier_chain_registeratomic_notifier_chain_registerblocking_notifier_chain_registerraw_notifier_chain_registersrcu_notifier_chain_registernotifier_chain_unregisteratomic_notifier_chain_unregisterblocking_notifier_chain_unregisterraw_notifier_chain_unregistersrcu_notifier_chain_unregisternotifier_call_chainatomic_notifier_call_chainblocking_notifier_call_chainraw_notifier_call_chainsrcu_notifier_call_chain

  • atomic是通过自旋锁的方式对notifier chain进行保护(使用spin_lock_irqsave、spin_unlock_irqrestore函数)
  • blocking是通过阻塞的方式对notifier chain进行保护(使用write_up、write_down函数)
  • raw是以共享方式访问notifier chain
  • srcu是通过互斥锁的方式对notifier chain进行保护(使用mutex_lock、mutex_unlock函数)
    锁的重量级raw<atocmic<srcu<blocking

翻过头来,看看刚刚注册的md_notify_reboot函数吧,它在机器重启或关机时执行,代码如下:

static int md_notify_reboot(struct notifier_block *this,       unsigned long code, void *x){ struct list_head *tmp; mddev_t *mddev; if ((code == SYS_DOWN) || (code == SYS_HALT) || (code == SYS_POWER_OFF)) {  printk(KERN_INFO "md: stopping all md devices.\n");  for_each_mddev(mddev, tmp)   if (mddev_trylock(mddev)) {    /* Force a switch to readonly even array     * appears to still be in use.  Hence     * the '100'.     */    do_md_stop(mddev, 1, 100);    mddev_unlock(mddev);   }  /*   * certain more exotic SCSI devices are known to be   * volatile wrt too early system reboots. While the   * right place to handle this issue is the given   * driver, we do want to have a safe RAID driver ...   */  mdelay(1000*1); } return NOTIFY_DONE;}


 

register_sysctl_table函数:按照raid_root_table的定义来创建sysctl文件及目录。

  • 在/proc/sys/目录下创建dev目录
  • 在/proc/sys/dev/目录下创建raid目录
  • 最后在/proc/sys/dev/raid目录下创建两个文件speed_limit_max和speed_limit_min,内容分别为200000和1000 

md_geninit函数:创建一个proc文件/proc/mdstat。并注册一些回调函数

static const struct file_operations md_seq_fops = {.owner= THIS_MODULE,.open           = md_seq_open,.read           = seq_read,.llseek         = seq_lseek,.release= seq_release_private,.poll= mdstat_poll,};


 

 出口 
static __exit void md_exit(void){mddev_t *mddev;struct list_head *tmp;blk_unregister_region(MKDEV(MD_MAJOR,0), 1U << MINORBITS);blk_unregister_region(MKDEV(mdp_major,0), 1U << MINORBITS);unregister_blkdev(MD_MAJOR,"md");unregister_blkdev(mdp_major, "mdp");unregister_reboot_notifier(&md_notifier);unregister_sysctl_table(raid_table_header);remove_proc_entry("mdstat", NULL);for_each_mddev(mddev, tmp) {export_array(mddev);mddev->hold_active = 0;}}

基本上是入口的逆过程,这里不再详细描述。

原创粉丝点击