2、注册网络设备
通过register_netdev函数把已完成部分初始化的net_device结构体变量(即某个网络设备实例)注册到Linux内核中,大致过程如下图:
下面将结合linux-2.6.38.8中的代码详细分析网络设备的注册过程。
(1)、获得rtnl信号量
rtnl_lock();
(2)、分配网络设备名(即%d对应的数字)
if(strchr(dev->name,'%')) {
err = dev_alloc_name(dev, dev->name);
if(err
gotoout;
}
(3)、调用实际注册函数
err = register_netdevice(dev);
3.1、初始化dev->addr_list_lock自旋锁并根据dev->type设置其类别
spin_lock_init(&dev->addr_list_lock);
netdev_set_addr_lockdep_class(dev);
3.2、调用init函数
if(dev->netdev_ops->ndo_init) {
ret = dev->netdev_ops->ndo_init(dev);
if(ret) {
if(ret > 0)
ret = -EIO;
gotoout;
}
}
3.3、检测网络设备名是否有效
ret = dev_get_valid_name(dev, dev->name, 0);
if(ret)
gotoerr_uninit;
3.4、为网络设备分配唯一的索引号
dev->ifindex = dev_new_index(net);
if(dev->iflink == -1)
dev->iflink = dev->ifindex;
3.5、设置网络设备特性(dev->features)
if((dev->features & NETIF_F_HW_CSUM) &&
(dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
printk(KERN_NOTICE"%s: mixed HW and IP checksum settings.\n",
dev->name);
dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
}
if((dev->features & NETIF_F_NO_CSUM) &&
(dev->features & (NETIF_F_HW_CSUM|NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
printk(KERN_NOTICE"%s: mixed no checksumming and other settings.\n",
dev->name);
dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM|NETIF_F_HW_CSUM);
}
dev->features = netdev_fix_features(dev->features, dev->name);
if(dev->features & NETIF_F_SG)
dev->features |= NETIF_F_GSO;
dev->vlan_features |= (NETIF_F_GRO | NETIF_F_HIGHDMA);
3.6、通过通知链告知内核其他子系统某种事件的发生(如注册网络设备)
ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev);
ret = notifier_to_errno(ret);
if(ret)
gotoerr_uninit;
ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);
ret = notifier_to_errno(ret);
if(ret) {
rollback_registered(dev);
dev->reg_state = NETREG_UNREGISTERED;
}
3.7、创建网络设备在sysfs文件系统中的入口
ret = netdev_register_kobject(dev);
if(ret)
gotoerr_uninit;
3.8、设置网络设备为已注册状态
dev->reg_state = NETREG_REGISTERED;
3.9、设置网络设备状态为可用
set_bit(__LINK_STATE_PRESENT, &dev->state);
3.10、初始化网络设备的队列规则
dev_init_scheduler(dev);
3.11、获得网络设备的引用计数
dev_hold(dev);
3.12、加入到设备链表(如dev->dev_list、dev->name_hlist、dev->index_hlist)
list_netdevice(dev);