内核网络设备的注册与初始化

来源:互联网 发布:计算字数的软件 编辑:程序博客网 时间:2024/04/30 14:21
首先来看如何分配内存给一个网络设备。 

内核通过alloc_netdev来分配内存给一个指定的网络设备: 

Java代码  收藏代码
  1. #define alloc_netdev(sizeof_priv, name, setup) \  
  2.     alloc_netdev_mq(sizeof_priv, name, setup, 1)  
  3.   
  4. struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,  
  5.         void (*setup)(struct net_device *), unsigned int queue_count)  


其中alloc_netdev_mq中的第一个元素是每个网络设备的私有数据(主要是包含一些硬件参数,比如中断之类的)的大小,也就是net_device结构中的priv的大小。第二个参数是设备名,我们传递进来一般都是一个待format的字符串,比如"eth%d",到时多个相同类型网卡设备就会依次为eth0,1(内核会通过dev_alloc_name来进行设置)... 第三个参数setup是一个初始化net_device结构的回调函数。 

可是一般我们不需要直接调用alloc_netdev的,内核提供了一些包装好的函数: 

这里我们只看alloc_etherdev: 

Java代码  收藏代码
  1. #define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)  
  2. struct net_device *alloc_etherdev_mq(int sizeof_priv, unsigned int queue_count)  
  3. {  
  4.     return alloc_netdev_mq(sizeof_priv, "eth%d", ether_setup, queue_count);  
  5. }  


这里实际是根据网卡的类型进行包装,也就类似于oo中的基类,ether_setup初始化一些所有相同类型的网络设备的一些相同配置的域: 

Java代码  收藏代码
  1. void ether_setup(struct net_device *dev)  
  2. {  
  3.     dev->header_ops      = &eth_header_ops;  
  4.   
  5.     dev->change_mtu      = eth_change_mtu;  
  6.     dev->set_mac_address     = eth_mac_addr;  
  7.     dev->validate_addr   = eth_validate_addr;  
  8.   
  9.     dev->type        = ARPHRD_ETHER;  
  10.     dev->hard_header_len     = ETH_HLEN;  
  11.     dev->mtu     = ETH_DATA_LEN;  
  12.     dev->addr_len        = ETH_ALEN;  
  13.     dev->tx_queue_len    = 1000/* Ethernet wants good queues */  
  14.     dev->flags       = IFF_BROADCAST|IFF_MULTICAST;  
  15.   
  16.     memset(dev->broadcast, 0xFF, ETH_ALEN);  
  17.   
  18. }  


接下来我们来看注册网络设备的一些细节。 

Java代码  收藏代码
  1. int register_netdev(struct net_device *dev)  
  2. {  
  3.     int err;  
  4.   
  5.     rtnl_lock();  
  6.   
  7.     /* 
  8.      * If the name is a format string the caller wants us to do a 
  9.      * name allocation. 
  10.      */  
  11.     if (strchr(dev->name, '%')) {  
  12. ///这里通过dev_alloc_name函数来对设备名进行设置。  
  13.         err = dev_alloc_name(dev, dev->name);  
  14.         if (err < 0)  
  15.             goto out;  
  16.     }  
  17. ///注册当前的网络设备到全局的网络设备链表中.下面会详细看这个函数.  
  18.     err = register_netdevice(dev);  
  19. out:  
  20.     rtnl_unlock();  
  21.     return err;  
  22. }  


整个网络设备就是一个链表,他需要很方便的遍历所有设备,以及很快的定位某个指定的设备。为此net_device包含了下面3个链表(有关内核中数据结构的介绍,可以去自己google下): 

Java代码  收藏代码
  1. ///可以根据index来定位设备  
  2. struct hlist_node   index_hlist;  
  3. ///可以根据name来定位设备  
  4. struct hlist_node   name_hlist;  
  5. ///通过dev_list,将此设备插入到全局的dev_base_head中,我们下面会介绍这个。  
  6. struct list_head    dev_list;  


当设备注册成功后,还需要通知内核的其他组件,这里通过netdev_chain类型的notifier chain来通知其他组件。事件是NETDEV_REGISTER..其他设备通过register_netdevice_notifier来注册自己感兴趣的事件到此notifier chain上。 

网络设备(比如打开或关闭一个设备),与用户空间的通信通过rtmsg_ifinfo函数,也就是RTMGRP_LINK的netlink。

每个设备还包含两个状态,一个是state字段,表示排队策略状态(用位图表示),一个是注册状态。 

包的排队策略也就是qos了。。 
Java代码  收藏代码
  1. int register_netdevice(struct net_device *dev)  
  2. {  
  3.     struct hlist_head *head;  
  4.     struct hlist_node *p;  
  5.     int ret;  
  6.     struct net *net;  
  7.   
  8.     BUG_ON(dev_boot_phase);  
  9.     ASSERT_RTNL();  
  10.   
  11.     might_sleep();  
  12.   
  13.     /* When net_device's are persistent, this will be fatal. */  
  14.     BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);  
  15.     BUG_ON(!dev_net(dev));  
  16.     net = dev_net(dev);  
  17.   
  18. ///初始化相关的锁  
  19.     spin_lock_init(&dev->addr_list_lock);  
  20.     netdev_set_addr_lockdep_class(dev);  
  21.     netdev_init_queue_locks(dev);  
  22.   
  23.     dev->iflink = -1;  
  24.   
  25.     /* Init, if this function is available */  
  26.     if (dev->init) {  
  27.         ret = dev->init(dev);  
  28.         if (ret) {  
  29.             if (ret > 0)  
  30.                 ret = -EIO;  
  31.             goto out;  
  32.         }  
  33.     }  
  34.   
  35.     if (!dev_valid_name(dev->name)) {  
  36.         ret = -EINVAL;  
  37.         goto err_uninit;  
  38.     }  
  39. ///给设备分配一个唯一的identifier.  
  40.     dev->ifindex = dev_new_index(net);  
  41.     if (dev->iflink == -1)  
  42.         dev->iflink = dev->ifindex;  
  43.   
  44. ///在全局的链表中检测是否有重复的名字  
  45.     head = dev_name_hash(net, dev->name);  
  46.     hlist_for_each(p, head) {  
  47.         struct net_device *d  
  48.             = hlist_entry(p, struct net_device, name_hlist);  
  49.         if (!strncmp(d->name, dev->name, IFNAMSIZ)) {  
  50.             ret = -EEXIST;  
  51.             goto err_uninit;  
  52.         }  
  53.     }  
  54. ///下面是检测一些特性的组合是否合法。  
  55.     /* Fix illegal checksum combinations */  
  56.     if ((dev->features & NETIF_F_HW_CSUM) &&  
  57.         (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {  
  58.         printk(KERN_NOTICE "%s: mixed HW and IP checksum settings.\n",  
  59.                dev->name);  
  60.         dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);  
  61.     }  
  62.   
  63.     if ((dev->features & NETIF_F_NO_CSUM) &&  
  64.         (dev->features & (NETIF_F_HW_CSUM|NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {  
  65.         printk(KERN_NOTICE "%s: mixed no checksumming and other settings.\n",  
  66.                dev->name);  
  67.         dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM|NETIF_F_HW_CSUM);  
  68.     }  
  69.   
  70.   
  71.     /* Fix illegal SG+CSUM combinations. */  
  72.     if ((dev->features & NETIF_F_SG) &&  
  73.         !(dev->features & NETIF_F_ALL_CSUM)) {  
  74.         printk(KERN_NOTICE "%s: Dropping NETIF_F_SG since no checksum feature.\n",  
  75.                dev->name);  
  76.         dev->features &= ~NETIF_F_SG;  
  77.     }  
  78.   
  79.     /* TSO requires that SG is present as well. */  
  80.     if ((dev->features & NETIF_F_TSO) &&  
  81.         !(dev->features & NETIF_F_SG)) {  
  82.         printk(KERN_NOTICE "%s: Dropping NETIF_F_TSO since no SG feature.\n",  
  83.                dev->name);  
  84.         dev->features &= ~NETIF_F_TSO;  
  85.     }  
  86.     if (dev->features & NETIF_F_UFO) {  
  87.         if (!(dev->features & NETIF_F_HW_CSUM)) {  
  88.             printk(KERN_ERR "%s: Dropping NETIF_F_UFO since no "  
  89.                     "NETIF_F_HW_CSUM feature.\n",  
  90.                             dev->name);  
  91.             dev->features &= ~NETIF_F_UFO;  
  92.         }  
  93.         if (!(dev->features & NETIF_F_SG)) {  
  94.             printk(KERN_ERR "%s: Dropping NETIF_F_UFO since no "  
  95.                     "NETIF_F_SG feature.\n",  
  96.                     dev->name);  
  97.             dev->features &= ~NETIF_F_UFO;  
  98.         }  
  99.     }  
  100.   
  101.     /* Enable software GSO if SG is supported. */  
  102.     if (dev->features & NETIF_F_SG)  
  103.         dev->features |= NETIF_F_GSO;  
  104.   
  105. ///初始化设备驱动的kobject并创建相关的sysfs  
  106.     netdev_initialize_kobject(dev);  
  107.     ret = netdev_register_kobject(dev);  
  108.     if (ret)  
  109.         goto err_uninit;  
  110. ///设置注册状态。  
  111.     dev->reg_state = NETREG_REGISTERED;  
  112.   
  113.     /* 
  114.      *  Default initial state at registry is that the 
  115.      *  device is present. 
  116.      */  
  117.   
  118. ///设置排队策略状态。  
  119.     set_bit(__LINK_STATE_PRESENT, &dev->state);  
  120. ///初始化排队规则  
  121.     dev_init_scheduler(dev);  
  122.     dev_hold(dev);  
  123. ///将相应的链表插入到全局的链表中。紧接着会介绍这个函数  
  124.     list_netdevice(dev);  
  125.   
  126.     /* Notify protocols, that a new device appeared. */  
  127. ///调用netdev_chain通知内核其他子系统。  
  128.     ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);  
  129.     ret = notifier_to_errno(ret);  
  130.     if (ret) {  
  131.         rollback_registered(dev);  
  132.         dev->reg_state = NETREG_UNREGISTERED;  
  133.     }  
  134.   
  135. out:  
  136.     return ret;  
  137.   
  138. err_uninit:  
  139.     if (dev->uninit)  
  140.         dev->uninit(dev);  
  141.     goto out;  
  142. }  


这里要注意有一个全局的struct net init_net;变量,这个变量保存了全局的name,index  hlist以及全局的网络设备链表。 

net结构我们这里所需要的也就三个链表: 

Java代码  收藏代码
  1. ///设备链表  
  2. struct list_head    dev_base_head;  
  3. ///名字为索引的hlist  
  4.     struct hlist_head   *dev_name_head;  
  5. ///index为索引的hlist  
  6.     struct hlist_head   *dev_index_head;  


Java代码  收藏代码
  1. static int list_netdevice(struct net_device *dev)  
  2. {  
  3.     struct net *net = dev_net(dev);  
  4.   
  5.     ASSERT_RTNL();  
  6.   
  7.     write_lock_bh(&dev_base_lock);  
  8. ///插入全局的list  
  9.     list_add_tail(&dev->dev_list, &net->dev_base_head);  
  10. 插入全局的name_list以及index_hlist  
  11.     hlist_add_head(&dev->name_hlist, dev_name_hash(net, dev->name));  
  12.     hlist_add_head(&dev->index_hlist, dev_index_hash(net, dev->ifindex));  
  13.     write_unlock_bh(&dev_base_lock);  
  14.     return 0;  
  15. }  


最终执行完之后,注册函数将会执行rtnl_unlock函数,而此函数则会执行netdev_run_todo方法。也就是完成最终的注册。(要注意,当取消注册这个设备时也会调用这个函数来完成最终的取消注册) 

这里有一个全局的net_todo_list的链表: 
Java代码  收藏代码
  1. static LIST_HEAD(net_todo_list);  


而在取消注册的函数中会调用这个函数: 

Java代码  收藏代码
  1. static void net_set_todo(struct net_device *dev)  
  2. {  
  3.     list_add_tail(&dev->todo_list, &net_todo_list);  
  4. }  


也就是把当前将要取消注册的函数加入到todo_list链表中。 

Java代码  收藏代码
  1. void netdev_run_todo(void)  
  2. {  
  3.     struct list_head list;  
  4.   
  5.     /* Snapshot list, allow later requests */  
  6. ///replace掉net_todo_list用list代替。  
  7.     list_replace_init(&net_todo_list, &list);  
  8.   
  9.     __rtnl_unlock();  
  10. ///当注册设备时没有调用net_set_todo函数来设置net_todo_list,因此list为空,所以就会直接跳过。  
  11.     while (!list_empty(&list)) {  
  12. ///通过todo_list得到当前的device对象。  
  13.         struct net_device *dev  
  14.             = list_entry(list.next, struct net_device, todo_list);  
  15. ///删除此todo_list;  
  16.         list_del(&dev->todo_list);  
  17.   
  18.   
  19.         if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) {  
  20.             printk(KERN_ERR "network todo '%s' but state %d\n",  
  21.                    dev->name, dev->reg_state);  
  22.             dump_stack();  
  23.             continue;  
  24.         }  
  25. ///设置注册状态为NETREG_UNREGISTERED.  
  26.         dev->reg_state = NETREG_UNREGISTERED;  
  27. ///在每个cpu上调用刷新函数。  
  28.         on_each_cpu(flush_backlog, dev, 1);  
  29.   
  30. ///等待引用此设备的所有系统释放资源,也就是引用计数清0.  
  31.         netdev_wait_allrefs(dev);  
  32.   
  33.         /* paranoia */  
  34.         BUG_ON(atomic_read(&dev->refcnt));  
  35.         WARN_ON(dev->ip_ptr);  
  36.         WARN_ON(dev->ip6_ptr);  
  37.         WARN_ON(dev->dn_ptr);  
  38.   
  39.         if (dev->destructor)  
  40.             dev->destructor(dev);  
  41.   
  42.         /* Free network device */  
  43.         kobject_put(&dev->dev.kobj);  
  44.     }  
  45. }  


下面来看netdev_wait_allrefs函数,我们先看它的调用流程: 

 


Java代码  收藏代码
  1. static void netdev_wait_allrefs(struct net_device *dev)  
  2. {  
  3.     unsigned long rebroadcast_time, warning_time;  
  4.   
  5.     rebroadcast_time = warning_time = jiffies;  
  6.     while (atomic_read(&dev->refcnt) != 0) {  
  7.         if (time_after(jiffies, rebroadcast_time + 1 * HZ)) {  
  8.             rtnl_lock();  
  9.   
  10. ///给netdev_chain发送NETDEV_UNREGISTER事件,通知各个子模块释放资源  
  11.             /* Rebroadcast unregister notification */  
  12.             call_netdevice_notifiers(NETDEV_UNREGISTER, dev);  
  13.   
  14.             if (test_bit(__LINK_STATE_LINKWATCH_PENDING,  
  15.                      &dev->state)) {  
  16.                 /* We must not have linkwatch events 
  17.                  * pending on unregister. If this 
  18.                  * happens, we simply run the queue 
  19.                  * unscheduled, resulting in a noop 
  20.                  * for this device. 
  21.                  */  
  22.                 linkwatch_run_queue();  
  23.             }  
  24.   
  25.             __rtnl_unlock();  
  26.   
  27.             rebroadcast_time = jiffies;  
  28.         }  
  29.   
  30.         msleep(250);  
  31.   
  32.         if (time_after(jiffies, warning_time + 10 * HZ)) {  
  33.             printk(KERN_EMERG "unregister_netdevice: "  
  34.                    "waiting for %s to become free. Usage "  
  35.                    "count = %d\n",  
  36.                    dev->name, atomic_read(&dev->refcnt));  
  37.             warning_time = jiffies;  
  38.         }  
  39.     }  
  40. }