linux網路裝置驅動
網路裝置驅動架構
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()
可用於向呼叫者返回鏈路上的載波訊號是否存在。