深入理解Linux网络技术内幕-设备注册和初始化(三)
net_device结构是一个非常庞大的数据结构,其中的字段包含了从硬件层、网络层和传输层等各种功能涉及的参数,因此,其初始化也是分批由不同的函数完成的,相当于每个函数负责其中相关联的一些子集字段,大致分为:
- 设备驱动程序:完成与硬件配置相关的IRQ、I/O内存以及I/O端口等字段
- 通用设备类型:对同一类设备的一些共性字段由内核实现的通用类型初始化函数完成,如ether_setup、fddi_setup函数等,对设备类型type、mtu和硬件地址长度等字段进行初始化;
- 其他功能字段:在register_netdevice函数注册时,有接到注册通知的其他相关联模块完成,如队列规则是在register_netdevice中完成初始化的。
设备驱动程序也可以对由类型初始化函数初始过的字段进行改写,如ether_setup设置mtu值后,设备驱动程序可以对这个字段重新进行初始化,但一般没有这个必要。同时,net_device中某些字段对设备无意义也可以不用初始化,因此在调用net_device结构中的函数指针时,要对其值进行判断是否有意义。
net_device结构在内核中的组织
在调用alloc_netdev函数时,可以直接传入私有数据结构的大小与net_device结构一起分配,也可以不需要私有数据则不分配。私有数据结构的大小是不定的,设备类型不同大小不同,而同类型的设备,不同的硬件私有数据结构也可能不一样。
在net_device数据结构中的next指针是指向net_device结构的头部,而非分配内存的头部,因此在实际应用中分配空间后面为了边界对齐的空白空间长度是存储在padded字段中的,除了内存分配和释放,使用的都是net_device结构的头部,而非分配内存的头部。
在内核中,net_device数据结构会被插入到三个链表中,一个是其所属网络空间struct net结构的dev_base_head链表中,这相当于这个网络命名空间的全局链表,另两个是hash链表中。
struct list_head dev_base_head;
struct hlist_head *dev_name_head;
struct hlist_head *dev_index_head;
dev_name_head链表和dev_index_head链表便于通过设备名称和设备ID(dev->ifindex)对设备进行操作时,对设备进行查找。最常见的查找是通过dev_get_by_name和dev_get_by_index对设备进行查找。对这两个链表的访问通过rcu锁进行保护。
struct net_device *dev_get_by_index(struct net *net, int ifindex)
struct net_device *dev_get_by_name(struct net *net, const char *name)
设备状态
net_device数据结构中有多个字段来表示设备当前不同的状态:
flags:用来表示设备接口的状态,如设备是否开启,是否处于混杂模式等
/* Standard interface flags (netdevice->flags). */
#define IFF_UP 0x1 /* interface is up */
#define IFF_BROADCAST 0x2 /* broadcast address valid */
#define IFF_DEBUG 0x4 /* turn on debugging */
#define IFF_LOOPBACK 0x8 /* is a loopback net */
#define IFF_POINTOPOINT 0x10 /* interface is has p-p link */
#define IFF_NOTRAILERS 0x20 /* avoid use of trailers */
#define IFF_RUNNING 0x40 /* interface RFC2863 OPER_UP */
#define IFF_NOARP 0x80 /* no ARP protocol */
#define IFF_PROMISC 0x100 /* receive all packets */
#define IFF_ALLMULTI 0x200 /* receive all multicast packets*/
#define IFF_MASTER 0x400 /* master of a load balancer */
#define IFF_SLAVE 0x800 /* slave of a load balancer */
#define IFF_MULTICAST 0x1000 /* Supports multicast */
#define IFF_PORTSEL 0x2000 /* can set media type */
#define IFF_AUTOMEDIA 0x4000 /* auto media select active */
#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/
#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
#define IFF_DORMANT 0x20000 /* driver signals dormant */
#define IFF_ECHO 0x40000 /* echo sent packets */
#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
reg_state:表示设备的注册状态,表示设备处于注册的不同阶段。
/* register/unregister state machine */
enum { NETREG_UNINITIALIZED=0,
NETREG_REGISTERED, /* completed register_netdevice */
NETREG_UNREGISTERING, /* called unregister_netdevice */
NETREG_UNREGISTERED, /* completed unregister todo */
NETREG_RELEASED, /* called free_netdev */
NETREG_DUMMY, /* dummy device for NAPI poll */
} reg_state:16;
state:表示和队列规则有关的设备状态,如__LINK_STATE_START等。每个网络设备都会被分派一种队列规则,流量控制以此来实现QoS机制。
/* These flag bits are private to the generic network queueing
* layer, they may not be explicitly referenced by any other
* code.
*/
enum netdev_state_t {
__LINK_STATE_START, //设备开启
__LINK_STATE_PRESENT, //设备存在
__LINK_STATE_NOCARRIER,//没有载波
__LINK_STATE_LINKWATCH_PENDING,
__LINK_STATE_DORMANT,
};
其中,某些变量的设置是重叠的,如IFF_UP在flags中被设置和清除时,__LINK_STATE_START标记在state字段中有同样的操作。这两个标记都是在dev_open和dev_close中设置和清除的。