网络设备驱动架构
Linux网络设备驱动程序的体系结构,依次为网络协议接口层,网络设备接口层,提供实际功能的设备驱动功能层以及网络设备与媒介层。
网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层协议是ARP,还是IP,都通过dev_queue_xmit()
函数发送数据,并通过netif_rx()
函数接受数据。
网络设备接口层向协议接口层提供统一的用于描述具体网络属性和操作的结构体net_device
,该结构体是设备驱动功能层中各函数的容器。
设备驱动功能层的各函数是网络设备接口层net_device
数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,它通过hard_start_xmit()
函数启动发送操作,并通过网络设备上的中断触发操作。
网络设备与媒介层是完成数据包发送和接收的物理实体,包括网络适配器和具体的传输媒介,网络适配器被设备驱动功能层中的函数在物理上驱动。
在设计具体的网络设备驱动时,需要完成的主要工作时编写设备驱动功能层的相关函数以填充net_device
数据结构的内容并将net_device
注册如内核。
struct net_device {
char name[IFNAMSIZ];
struct hlist_node name_hlist;
char *ifalias;
/* * I/O specific fields * FIXME: Merge these and struct ifmap into one */
unsigned long mem_end;
unsigned long mem_start;
unsigned long base_addr;
int irq;
atomic_t carrier_changes;
/* * Some hardware also needs these fields (state,dev_list, * napi_list,unreg_list,close_list) but they are not * part of the usual set specified in Space.c. */
unsigned long state;
struct list_head dev_list;
struct list_head napi_list;
struct list_head unreg_list;
struct list_head close_list;
struct list_head ptype_all;
struct list_head ptype_specific;
struct {
struct list_head upper;
struct list_head lower;
} adj_list;
struct {
struct list_head upper;
struct list_head lower;
} all_adj_list;
netdev_features_t features;
netdev_features_t hw_features;
netdev_features_t wanted_features;
netdev_features_t vlan_features;
netdev_features_t hw_enc_features;
netdev_features_t mpls_features;
netdev_features_t gso_partial_features;
int ifindex;
int group;
struct net_device_stats stats;
atomic_long_t rx_dropped;
atomic_long_t tx_dropped;
atomic_long_t rx_nohandler;
.....
/* for setting kernel sock attribute on TCP connection setup */
#define GSO_MAX_SIZE 65536
unsigned int gso_max_size;
#define GSO_MAX_SEGS 65535
u16 gso_max_segs;
#ifdef CONFIG_DCB
const struct dcbnl_rtnl_ops *dcbnl_ops;
#endif
u8 num_tc;
struct netdev_tc_txq tc_to_txq[TC_MAX_QUEUE];
u8 prio_tc_map[TC_BITMASK + 1];
#if IS_ENABLED(CONFIG_FCOE)
unsigned int fcoe_ddp_xid;
#endif
#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
struct netprio_map __rcu *priomap;
#endif
struct phy_device *phydev;
struct lock_class_key *qdisc_tx_busylock;
bool proto_down;
};
网络设备驱动的注册于注销
网络设备的注册与注销由register_netdev()
和ungister_netdev()
函数完成。
net_device
结构体的分配和网络设备驱动的注册需在网络设备驱动初始化是进行,而net_device
结构体的释放和网络设备驱动的注销在设备或驱动被移除的时候执行。
net_device_ops
结构体是网络设备的一系列硬件操作行数的集合。
struct net_device_ops {
int (*ndo_init)(struct net_device *dev);
void (*ndo_uninit)(struct net_device *dev);
int (*ndo_open)(struct net_device *dev);
int (*ndo_stop)(struct net_device *dev);
netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
struct net_device *dev);
netdev_features_t (*ndo_features_check)(struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features);
u16 (*ndo_select_queue)(struct net_device *dev,
struct sk_buff *skb,
void *accel_priv,
select_queue_fallback_t fallback);
void (*ndo_change_rx_flags)(struct net_device *dev,
int flags);
void (*ndo_set_rx_mode)(struct net_device *dev);
int (*ndo_set_mac_address)(struct net_device *dev,
void *addr);
int (*ndo_validate_addr)(struct net_device *dev);
int (*ndo_do_ioctl)(struct net_device *dev,
struct ifreq *ifr, int cmd);
int (*ndo_set_config)(struct net_device *dev,
struct ifmap *map);
int (*ndo_change_mtu)(struct net_device *dev,
int new_mtu);
int (*ndo_neigh_setup)(struct net_device *dev,
struct neigh_parms *);
void (*ndo_tx_timeout) (struct net_device *dev);
struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev,
struct rtnl_link_stats64 *storage);
struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
int (*ndo_vlan_rx_add_vid)(struct net_device *dev,
__be16 proto, u16 vid);
int (*ndo_vlan_rx_kill_vid)(struct net_device *dev,
__be16 proto, u16 vid);
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*ndo_poll_controller)(struct net_device *dev);
int (*ndo_netpoll_setup)(struct net_device *dev,
struct netpoll_info *info);
void (*ndo_netpoll_cleanup)(struct net_device *dev);
#endif
......
int (*ndo_get_lock_subclass)(struct net_device *dev);
int (*ndo_set_tx_maxrate)(struct net_device *dev,
int queue_index,
u32 maxrate);
int (*ndo_get_iflink)(const struct net_device *dev);
int (*ndo_change_proto_down)(struct net_device *dev,
bool proto_down);
int (*ndo_fill_metadata_dst)(struct net_device *dev,
struct sk_buff *skb);
void (*ndo_set_rx_headroom)(struct net_device *dev,
int needed_headroom);
};
网络设备的初始化
网络设备的初始化
进行硬件上的准备工作,检查网络设备是否存在,如果存在,则检测设备所使用的硬件资源。
进行软件接口上的准备工作,分配net_device
结构体并对齐数据和函数指针成员赋值。
获得设备的私有信息指针并初始化各成员的值,如果私有信息包括自旋锁或信号量等并发或同步机制,则需对其进行初始化。
网络设备的打开与释放
网络设备的打开
使能设备使用的硬件资源,申请I/O区域,中断和DMA通道等
调用linux内核提供的netif_start_queue()
函数,激活设备发送队列
网络设备关闭
调用linux内核提供的netif_stop_queue()
函数,停止设备传输包
释放设备所使用的I/O驱动,中断和DMA资源。
网络设备驱动包发送
linux网络子系统在发送数据包时,会用驱动程序提供的hard_start_transmit()
函数,该函数用于启动数据包的发送。在设备初始化的时候,这个函数指针需被初始化以指向设备的xxx_tx()
函数。
数据包发送流程
网络设备驱动程序从上层协议传递过来的sk_buff参数获得数据包的有效数据和长度,将有效数据放入临时缓冲区。
对于以太网,如果有效数据的长度小于以太网冲突检测所要求数据帧的最小长度ETH_ZLEN,则给临时缓冲区的末尾填充0。
设置硬件的寄存器,驱使网络设备进行数据发送操作。
数据接收流程
网络设备接收数据的主要方法是由中断引发设备的中断处理函数,中断处理函数判断中断类型,如果为接收中断,则读取接收到的数据,分配sk_buffer
数据结构和数据缓冲区,将接收到的数据复制到数据缓冲区,并调用netif_rx()
函数将sk_buffer
传递给上层协议。
网络连接状态
网络适配器硬件电路可以检测出链路上是否有载波,载波反映了网络的连接是否正常,网络设备驱动可以通过netif_carrier_on()
和netif_carrier_off()
函数改变设备的连接状态,如果驱动检测到连接状态发生变化,也应该以netif_carrier_on()
和netif_carrier_off()
函数显式地通知内核。
函数netif_carrier_ok()
可用于向调用者返回链路上的载波信号是否存在。