网络子系统31_网络设备的注册与注销
// 网络设备注册 // 内核中,使用两个全局的hash表,索引net_device,分别为name hash表,index hash表,使用一个链表dev_base,链接所有net_device // 包裹函数,获取rtnl锁,注册任务由register_netdev完成 1.1 int register_netdev(struct net_device *dev) { int err; //获取rtnl锁 rtnl_lock(); //执行注册 err = register_netdevice(dev); //开锁,并继续完成net_todo_list上挂载的dev的注册/注销 rtnl_unlock(); return err; } // 网络设备注册 // 调用路径:register_netdev->register_netdevice // 函数主要任务: // 1.初始化队列锁,传输锁 // 2.分配接口索引 // 3.检查设备名是否唯一 // 4.更新设备SG,TSO特性 // 5.初始化设备的队列规则 // 6.将设备添加到name,index的两个hash表中 // 7.通知netdev_chain // 8.启动下半部 // 注:net_device->queue_lock用于保护设备的规则队列,net_device->xmit_lock用于保护驱动程序的hard_start_xmit // xmit_lock_owner=获取xmit_lock的cpu号 1.2 int register_netdevice(struct net_device *dev) { struct hlist_head *head; struct hlist_node *p; int ret; //初始化设备队列锁,在net_tx_action中会被获取 spin_lock_init(&dev->queue_lock); //初始化hard_xmit保护锁,在qdisc_restart中会被获取 spin_lock_init(&dev->xmit_lock); //锁木有没有拥有者 dev->xmit_lock_owner = -1; //表示设备目前没有分配接口索引 ifindex字段 dev->iflink = -1; //如果驱动程序提供了init例程,则调用 if (dev->init) { ret = dev->init(dev); if (ret) { if (ret > 0) ret = -EIO; goto out_err; } } //设备名非法 if (!dev_valid_name(dev->name)) { ret = -EINVAL; goto out_err; } //分配唯一的接口索引 dev->ifindex = dev_new_index(); if (dev->iflink == -1) //iflink等于接口索引 dev->iflink = dev->ifindex; //检查设备名是否唯一 head = dev_name_hash(dev->name); hlist_for_each(p, head) { struct net_device *d = hlist_entry(p, struct net_device, name_hlist); //检查设备名是否已经被注册 if (!strncmp(d->name, dev->name, IFNAMSIZ)) { ret = -EEXIST; goto out_err; } } //设备支持分散聚集DMA,不支持校验和功能,则取消掉SG if ((dev->features & NETIF_F_SG) && !(dev->features & (NETIF_F_IP_CSUM | NETIF_F_NO_CSUM | NETIF_F_HW_CSUM))) { printk("%s: Dropping NETIF_F_SG since no checksum feature.\n", dev->name); dev->features &= ~NETIF_F_SG; } //TCP 的TSO功能 if ((dev->features & NETIF_F_TSO) && !(dev->features & NETIF_F_SG)) { printk("%s: Dropping NETIF_F_TSO since no SG feature.\n", dev->name); dev->features &= ~NETIF_F_TSO; } //以太网默认提供rebuild_header函数,在ether_setup中设置 if (!dev->rebuild_header) dev->rebuild_header = default_rebuild_header; //设备存在标志,多用于热插拔设备 set_bit(__LINK_STATE_PRESENT, &dev->state); dev->next = NULL; //初始化设备的队列规则 dev_init_scheduler(dev); //获取保护dev_tail,dev_name_head,dev_index_head锁 write_lock_bh(&dev_base_lock); *dev_tail = dev; dev_tail = &dev->next; //将dev添加到hash表中 hlist_add_head(&dev->name_hlist, head); hlist_add_head(&dev->index_hlist, dev_index_hash(dev->ifindex)); //增加dev的引用计数 dev_hold(dev); //将dev标记为NETREG_REGISTERING状态,表示正在注册中 dev->reg_state = NETREG_REGISTERING; write_unlock_bh(&dev_base_lock); //通知netdev_chain中的监听者,有设备注册 notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); //由net_set_todo完成后半部的注册,主要是在sysfs中注册设备 net_set_todo(dev); ret = 0; out: return ret; out_err: free_divert_blk(dev); goto out; } // 分配接口索引 // 确保唯一性的办法,检查新索引是否已经被使用 // 调用路径:register_netdev->register_netdevice->dev_new_index 1.3 static int dev_new_index(void) { static int ifindex; for (;;) { if (++ifindex <= 0) ifindex = 1; //获取第一个没有被设备使用的索引 if (!__dev_get_by_index(ifindex)) return ifindex; } } // 挂载设备到后半部操作链表 // 将设备注册,注销操作,分成两部分,后一部分在释放rtnl锁的情况下进行 2.1 static inline void net_set_todo(struct net_device *dev) { //后半部链表由net_todo_list_lock保护 spin_lock(&net_todo_list_lock); //将设备挂在到net_todo_list上,接收之后的注册 list_add_tail(&dev->todo_list, &net_todo_list); spin_unlock(&net_todo_list_lock); } // 注册、注销后半部入口点: // 在释放rtnl之后,完成剩余操作 // 注:netlink为核心通知用户事件的标准内核接口 2.2 void rtnl_unlock(void) { //释放rtnl_sem信号量 rtnl_shunlock(); //操作net_todo_list netdev_run_todo(); } // 注册与注销的切割操作 // 函数主要内容: // 1.遍历net_todo_list上的设备 // 2.注册操作的后半部: // 2.1 在sysfs中更新相应项 // 3.注销操作后半部: // 3.1 删除sysfs中的相应项 // 3.2 等待net_device的引用计数变为1, // 3.3 向netdev_chain发送注销信息,通知持有该设备的模块释放对其的引用。 // 3.4 调用驱动提供的destructor // 注:netdev_run_todo由net_todo_run_mutex信号量保护,全局同时只有一个实例在运行。 2.3 void netdev_run_todo(void) { struct list_head list = LIST_HEAD_INIT(list); int err; //保证全局只有一个netdev_run_todo在执行 down(&net_todo_run_mutex); //检查net_todo_list上是否有需要执行切割操作的dev if (list_empty(&net_todo_list)) goto out; //获取保护net_todo_list的锁 spin_lock(&net_todo_list_lock); //将net_todo_list合并到本地list上,然后重新初始化net_todo_list list_splice_init(&net_todo_list, &list); spin_unlock(&net_todo_list_lock); while (!list_empty(&list)) { struct net_device *dev = list_entry(list.next, struct net_device, todo_list); //从list上删除dev list_del(&dev->todo_list); //判断dev当前的注册状态 switch(dev->reg_state) { //在register_netdevice中被设置 case NETREG_REGISTERING: //在sysfs中注册设备 err = netdev_register_sysfs(dev); if (err) printk(KERN_ERR "%s: failed sysfs registration (%d)\n", dev->name, err); dev->reg_state = NETREG_REGISTERED; break; //在unregister_netdevice中被设置 case NETREG_UNREGISTERING: //删除sysfs中设备对应的节点 netdev_unregister_sysfs(dev); dev->reg_state = NETREG_UNREGISTERED; //等待dev的引用计数为零,周期性打印unregister消息,并向netdev_chain通知此dev的NETDEV_UNREGISTER消息 netdev_wait_allrefs(dev); //如果设备驱动提供了销毁操作,则调用 if (dev->destructor) dev->destructor(dev); break; default: printk(KERN_ERR "network todo '%s' but state %d\n", dev->name, dev->reg_state); break; } } out: up(&net_todo_run_mutex); } // 使用切割操作的优势主要体现在注销设备时: // 当驱动需要同时注销多个设备时,unregister_netdev会造成严重的锁开销,因为一次上锁解锁只能注销一个设备 // 通过使用unregister_netdevice,手工对rtnl加锁,可以通过一次加锁注销多个net_device,从而降低锁开销。 // // 参考 深入理解linux网络技术内幕 提供的一个例子 //1.rtnl_lock( ); // loop for each device driven by this driver { // ... ... ... // unregister_netdevice(dev); // ... ... ... // } // rtnl_unlock( ); // //2.loop for each device driven by this driver { // ... ... ... // unregister_netdev(dev); // ... ... ... // } //unregister_netdev() //{ // rtnl_lock(); // unregister_netdevice(); // rtnl_unlock(); //} // 注销网络设备 // 函数主要任务: // 1.确保设备已经被关闭 // 2.从name 哈希表,index哈希表删除 // 3.通知netdev_chain设备正在注销 // 4.释放多播列表 // 5.启动后半部操作 3.2 int unregister_netdevice(struct net_device *dev) { struct net_device *d, **dp; if (dev->reg_state == NETREG_UNINITIALIZED) {//注销的设备必须是已经注册过的设备 printk(KERN_DEBUG "unregister_netdevice: device %s/%p never " "was registered\n", dev->name, dev); return -ENODEV; } if (dev->flags & IFF_UP)//设备处于开启状态,需要先关闭设备 dev_close(dev);//修改设备状态,以及修改设备队列规则 for (dp = &dev_base; (d = *dp) != NULL; dp = &d->next) {//遍历网卡设备列表 if (d == dev) { write_lock_bh(&dev_base_lock);//操作dev_base需要获取dev_base_lock锁 hlist_del(&dev->name_hlist);//从index,name链表删除设备 hlist_del(&dev->index_hlist); if (dev_tail == &dev->next) dev_tail = dp; *dp = d->next; write_unlock_bh(&dev_base_lock); break; } } if (!d) { printk(KERN_ERR "unregister net_device: '%s' not found\n", dev->name); return -ENODEV; } dev->reg_state = NETREG_UNREGISTERING;//设置设备为正在注销状态,稍有由后半部完成剩余的注销操作 dev_shutdown(dev);//删除设备队列规则 notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);//通知netdev_chain,设备要注销 dev_mc_discard(dev);//删除多播列表 if (dev->uninit) dev->uninit(dev);//如果设备提供了注销操作,则调用 free_divert_blk(dev);//释放分流器 net_set_todo(dev);//将设备链接到todo链表上 dev_put(dev);//递减引用计数 return 0; } // 删除设备多播列表 void dev_mc_discard(struct net_device *dev) { spin_lock_bh(&dev->xmit_lock); while (dev->mc_list != NULL) {//链表非空 struct dev_mc_list *tmp = dev->mc_list; dev->mc_list = tmp->next;//从链表取下多播控制项 if (tmp->dmi_users > tmp->dmi_gusers)//dmi_users = number of users;dmi_guesers = number of groups printk("dev_mc_discard: multicast leakage! dmi_users=%d\n", tmp->dmi_users); kfree(tmp);//直接kfree } dev->mc_count = 0; spin_unlock_bh(&dev->xmit_lock); }
最后更新:2017-04-03 15:22:11