网络子系统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