文章目录
-
- 网络设备:net_device数据结构
-
- net_device数据结构定义文件的位置
- net_device数据结构分布
- 标识符
-
- int ifindex
- unsigned short dev_id
- 配置
-
- char name[IFNAMSIZ]
- unsigned long mem_start
- unsigned long mem_end
- unsigned long base_addr
- unsigned char if_port
- unsigned char dma
- unsigned short flags
- unsigned short gflags
- unsigned short priv_flags
- u64 features
- unsigned int mtu
- unsigned short type
- unsigned short hard_header_len
- unsigned char broadcast[MAX_ADDR_LEN]
- unsigned char dev_addr[MAX_ADDR_LEN]
- unsigned char addr_len
- int promiscuity
- 统计数据
- 设备状态
-
- unsigned long state
- enum { ... } reg_state
- unsigned long trans_start
- spinlock xmit_lock
- int xmit_lock_owner
- void *atalk_ptr
- void *ip_ptr
- void *dn_ptr
- void *ip6_ptr
- void *ec_ptr
- 列表管理
-
- struct net_device *next
- struct hlist_node name_hlist
- struct hlist_node index_hlist
- 链路层多播
-
- int allmulti
- struct dev_mc_list *mc_list
- int mc_count
- 流量管理
-
- struct net_device *net_sched
- struct Qdisc *qdisc
- struct Qdisc *qdisc_sleeping
- struct Qdisc *qdisc_ingress
- struct list_head qdisc_list
- spinlock_t queue_lock
- spinlock_t ingress_lock
- unsigned long tx_queue_len
- 功能专用
-
- struct divert_blk *divert
- struct net_bridge_port *br_port
- void (*vlan_rx_register)(...)
- void (*vlan_rx_add_vid)(...)
- void (*vlan_rx_kill_vid)(...)
- 通用
-
- int watchdog_timeo
- struct timer_list watchdog_timer
- int (*poll)(...)
- struct list_head poll_list
- int quota
- int weight
- const struct iw_handler_def *wireless_handlers
- struct iw_public_data *wireless_data
- struct list_head todo_list
- 函数指针
-
- struct ethtool_ops *ethtool_ops
- struct net_device_ops *netdev_ops;
- 参考文献
网络设备:net_device数据结构
net_device具体网络设备的所有信息都存储在数据结构中。无论是真实的设备(如 Ethernet)或虚拟设备(如Bonding 或 VLAN)
net_device数据结构定义文件的位置
net_device定义数据结构include/linux/netdevice.h Linux它提供了一个可以初始化某些参数的通用函数,使其值在所有模型之间相同。除了为模型设置每个设备驱动程序外,还将使用此函数。
net_device数据结构分布
net_device结构的字段分为:
标识符
net_device结构有三个标识符
int ifindex
独一无二的ID,当设备以dev_new_index注册时分配给每个设备。
unsigned short dev_id
可以由不同来区分OS同时共享同一设备的许多虚拟实例
配置
char name[IFNAMSIZ]
设备名称(如:eth0)
unsigned long mem_start
unsigned long mem_end
描述设备用于与内核沟通的共享内存
unsigned long base_addr
设备自有内存映射I/O内存的起始地址
unsigned char if_port
该接口使用的端口类型
有些设备有一个以上的连接器(组合BNC RJ45)允许用户根据需要选择其中一种。设备数用于设备的端口类型。 当设备驱动程序没有通过配置命令强制选择特定的端口类型时,就会简单地选择默认的端口类型。 可以处理不同接口模型的驱动程序
case IF_PORT_10BASET:
dev->if_port = map->port;
netif_carrier_off(dev);
status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
mdio_write(dev, mii_phy->phy_addr,
MII_CONTROL, status & ~(MII_CNTL_SPEED |
MII_CNTL_AUTO));
break;
case IF_PORT_100BASET:
case IF_PORT_100BASETX:
dev->if_port = map->port;
netif_carrier_off(dev);
status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL);
mdio_write(dev, mii_phy->phy_addr,
MII_CONTROL, (status & ~MII_CNTL_SPEED) |
MII_CNTL_SPEED);
unsigned char dma
设备所使用的DMA通道。 文件:arch/arm/kernel/dma.c 内存获取DMA:request_dma 内核释放DMA:free_dma 开启DMA:enable_dma 关闭DMA:disable_dma
unsigned short flags
unsigned short gflags
unsigned short priv_flags
flags字段中的某些位代表网络设备的功能(如IFF_MULTICAST),其他位代表状态的改变(IFF_UP或IFF_RUNNING)。所有的完整列表文件在<include/uapi/linux/if.h> 设备驱动程序通常会在初始化期间设置这些功能,有内核管理,以响应外部事件。(如:ifconfig) UP LOOPBACK RUNNING相当于IFF_UP IFF_LOOPBACK IFF_RUNNING priv_flags用于存储用户空间不可见的标识,由虚拟设备(VLAN和Bridge)使用 gflags几乎不用
u64 features
用于存储其他一些设备功能。features可报告适配卡的功能,以便与CPU通信
unsigned int mtu
MTU(Maximum Transmission Unit)代表最大传输单元 ,表示设备能处理的帧的最大尺寸 Ethernet 帧规范把最大有效载荷尺寸定在1500个字节。 有时发现Ethernet MTU定义为1518或1514:第一个帧最大尺寸包含报头在内;第二个包含报头但不含帧检查序列(4个字节的校验和)
unsigned short type
设备所属的类型(PPP、CAN等)完整列表<include/uapi/linux/if_arp.h>
unsigned short hard_header_len
以字节为单位的设备头的大小。(如Ethernet报头是14字节)完整列表</usr/include/linux/if_ether.h>
unsigned char broadcast[MAX_ADDR_LEN]
链路层广播地址
unsigned char dev_addr[MAX_ADDR_LEN]
unsigned char addr_len
dev_addr是设备链路层地址,地址的字节长度由addr_len指定。addr_len的值依赖于设备的类型。Ethernet地址都是6个字节
int promiscuity
某些网络管理任务会要求一个系统接收在一条共享缆线传播的所有帧,而不是仅限于地址直接指定给系统的帧。 一个设备如果可以接收所有封包,就意味着处于混杂模式 net_device结构包含一个名为promiscuity计数器,表示一个设备处于混杂模式中。采用计数器的模式而不是标识的模式放入原因在于。多个客户程序可能都会要求混杂模式。进入混杂模式时递增计数器,而退出该模式时递减计数器。除非计数器为零,否则该设备不会退出混杂模式 该字段的操作通过dev_set_promiscuity 根据flags字段中的标识而设置不同接收模式
static void set_rx_mode(struct net_device *dev)
{
struct vortex_private *vp = netdev_priv(dev);
void __iomem *ioaddr = vp->ioaddr;
int new_mode;
if (dev->flags & IFF_PROMISC) {
// 当IFF_PROMISC标志位被置位时,就会对new_mode进行初始化
if (vortex_debug > 3)
pr_notice("%s: Setting promiscuous mode.\n", dev->name);
new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm;
} else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) {
new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast;
} else
new_mode = SetRxFilter | RxStation | RxBroadcast;
iowrite16(new_mode, ioaddr + EL3_CMD); // 接收地址指定网卡的数据流RxStation、多播数据流RxMulticast、广播数据流RxBroadcast等
// EL3_CMD是对ioaddr内存地址的偏移量,代表与设备交互时命令应拷贝到哪里
}
统计数据
net_device结构没有提供一个用来记录统计数据的收集字段(瑞芯微RK包含struct net_device_stats),而是引入一个由驱动程序设置的prive指针,指向一个存储有关接口信息的私有数据结构。私有数据由统计数据组成(如 已收发的封包数目,以及已经发生的错误数目) 几乎不同的Ethernet卡使用不同的私有结构。私有结构的格式取决于设备的类型以及特定的模型。 net_device_stats包含所有网络设备共有的统计数据,可以通过get_stats方式获取 私有数据结构复杂度多少,依赖于适配卡的功能,以及设备驱动程序编写者打算采用多精密的统计数据和复杂的设计以提高性能
设备状态
为了控制与NIC之间的交互,每个设备驱动程序都必须维护一些信息,如表示接口需要哪些行为的时间戳和标识。
unsigned long state
由网络队列子系统多使用的一组标识。其索引值是enum netdev_state_t(<include/linux/netdevice.h>)中的常数。 位的设置和清除都使用通用的函数set_bit和clear_bit
set_bit(__LINK_STATE_START, &dev->state);
clear_bit(__LINK_STATE_START, &dev->state);
enum { … } reg_state
设备的注册状态
unsigned long trans_start
最近的一个帧传输启动的时间(以jiffies测量)。设备驱动程序会在传输前设置此值。如果在一段给定时间后传输没有完成,这个字段用于检测适配卡的问题。传输时间长就意味着有地方出错了,驱动程序通常复位适配卡 。
spinlock xmit_lock
int xmit_lock_owner
xmit_lock锁使驱动程序函数hard_start_xmit的访问串行化。每个CPU一次只能对任何定的一个设备做一次传输。 xmit_lock_owner是持有该锁的CPU的ID
void *atalk_ptr
void *ip_ptr
void *dn_ptr
void *ip6_ptr
void *ec_ptr
这6个字段都是指针,指向特定协议专用的数据结构,每个数据结构都包含一些该协议私有的参数。 如,ip_ptr指向一个类型为in_device的数据结构,包含各种不同的与IPv4相关的参数IP地址列表等
列表管理
net_device数据结构均被插入到一个全局列表和两个hash表中
struct net_device *next
把每个net_device数据结构链接为全局列表中的下一个(next)元素。
struct hlist_node name_hlist
struct hlist_node index_hlist
把net_device结构链接至两个hash表的bucket列表
链路层多播
多播是一种用于数据传递多位接收者的机制。多播可以在L3网络层(IP)以及L2链路层(Ethernet)中使用。 链路层多播的传送,可以通过在链路层报头中使用特殊地址或者控制信息。Ethernet本身就支持多播(Ethernet地址如何划分为单播、多播或广播) 利用一个特定位把多播地址和其他范围的地址区分开来。这意味着可能地址中有50%是多播(如2^48的50%是很大的数),这比维护一份很长的列表更有效率
net_decive数据结构中的flags之一就是用于表示该设备是否监听所有地址。
int allmulti
用于监听所有地址。当此变量从0变为非零时,就会调用dev_set_allmulti函数,以指示该接口监听所有多播地址。
每个设备都会为其监听的每个链路层多播地址保存一个dev_mc_list结构的实例。链路层多播地址可以分别用。
struct dev_mc_list *mc_list
指向此设备的dev_mc_list结构列表表头的指针
int mc_count
此设备的多播地址数目,也就是mc_list所指的列表长度
流量管理
struct net_device *net_sched
用于软中断之一使用
struct Qdisc *qdisc
struct Qdisc *qdisc_sleeping
struct Qdisc *qdisc_ingress
struct list_head qdisc_list
用于管理入口和出口的封包队列
spinlock_t queue_lock
spinlock_t ingress_lock
流量控制子系统为网络设备定义了一个私有的出口队列 queue_lock用于避免对出口队列的并发访问;ingress_lock用于避免入口队列的并发访问
unsigned long tx_queue_len
设备的传送队列的长度。当内核支持流量控制时,可以不用tx_queue_len。可以通过文件系统控制 不同设备类型的tx_queue_len值
功能专用
struct divert_blk *divert
分流器是一种功能,允许你改变输入的封包的源和目的地址。因此,有可能以此配置所指定的特征特征重新发送流量至不同的接口或不同的主机。
struct net_bridge_port *br_port
此设备配置成桥接端口时,就需要额外的信息。
void (*vlan_rx_register)(…)
void (*vlan_rx_add_vid)(…)
void (*vlan_rx_kill_vid)(…)
函数指针由VLAN代码使用
通用
int watchdog_timeo
struct timer_list watchdog_timer
实现看门狗定时器
int (*poll)(…)
struct list_head poll_list
int quota
int weight
由NAPI功能使用
const struct iw_handler_def *wireless_handlers
struct iw_public_data *wireless_data
用于无线设备的参数和指针函数
struct list_head todo_list
网络设备的注册和除名。todo_list用于处理第二步骤
函数指针
net_device数据结构中也有函数指针,这些函数主要用于:传输和接收帧;在缓冲区上添加或解析链路层报头;改变配置的一部分;获取统计数据;与特定功能交互
struct ethtool_ops *ethtool_ops
指向一组函数指针的指针,用于设置或取出不同设备参数的配置
struct net_device_ops *netdev_ops;
里面包含初始化、清理、销毁、开启以及关闭设备,设置mac地址等函数
参考文献
《Understanding Linux Network Internals》