net_device

net_device結構保存與網絡設備相關的所有信息。每一個網絡設備都對應一個這樣的結構,包括真實設備(例如以太網卡)和虛擬設備(比如bonding或VLAN)。

Bonding,也被稱作EtherChannel(Cisco的術語)和trunking(Sun的術語),允許把一定數量的接口組合起來當作一個新的設備。這個特性在系統需要把多個點對點設備組合起來以獲取更高帶寬時有用。新設備的速度可以成倍增加,一般來說, 新設備的吞吐量是單個設備吞吐量的總和。

VLAN代表虛擬局域網。VLAN的作用是在二層交換機上劃分不同的廣播域,從而把不同廣播域的流量隔離開。它通過在鏈路層上增加一個標記來實現這個功能。你可以在http://www.linuxjournal.com/article/7268找到VLAN的簡介以及它在LINUX中的使用方法。

所有設備的net_device結構都放在一個全局鏈表中,鏈表的頭指針是dev_base。net_device結構的定義在include/linux/netdevice.h中。

與sk_buff類似,net_device結構比較大,而且包含了很多特性相關的參數,這些參數在不同的協議層中使用。出於這個原因,net_device結構的組織會有一些改變,用於優化協議棧的性能。

網絡設備可以分爲不同的類型,比如以太網卡和令牌環網卡。net_device結構中的某些變量對同一類型的設備來說,取值是相同的;而某些變量在同一設備的不同工作模式下,取值必須不同。因此,對幾乎所有類型的設備,linux內核提供了一個通用的函數用於初始化那些在所有模式下取值相同的變量。每一個設備驅動在調用這個函數的同時,還初始化那些在當前模式下取值不同的變量。設備驅動同樣可以覆蓋那些由內核初始化的變量(例如,在優化設備性能時)。

net_device結構中的變量可以分爲以下幾類:

  • Configuration

  • Statistics

  • Device status

  • List management

  • Traffic management

  • Feature specific

  • Generic

  • Function pointers (or VFT)

1. 標識符

net_device結構包括三個標識符:

int ifindex
全局唯一的設備ID。在每個設備註冊時,調用dev_new_index生成。

int iflink
這個變量主要被(虛擬)隧道設備使用,用於標識隧道的真實設備。

unsigned short dev_id
IPV6中使用zSeries OSA網卡時用到這個變量。這個變量用於區分同一設備的不同虛擬實體,這些虛擬實體可以在不同的虛擬操作系統中共享。詳細內容,請參閱net/ipv6/addrconf.c中的註釋。

2. Configuration

有些參數可以在內核初始化此類設備時設置一個缺省值,而有些參數就需要留給設備驅動來填充。設備驅動可以更改缺省值,這個在前面已經說過。有些變量甚至可以在運行時通過命令如ifconfig或ip等來更改。實際上,有些變量,比如base_addr,if_port,dma和irq等通常都是由用戶在加載設備驅動模塊時設置的。但是,這些變量不能被虛擬設備使用。

char name[IFNAMSIZ]
設備名稱(比如,eth0)

unsigned long mem_start
unsigned long mem_end
這兩個變量描述設備與內核通信所用到的內存邊界。它們由設備驅動初始化,並且只能被設備驅動訪問;高層協議不需要關心這塊內存。

unsigned long base_addr
映射到設備內存空間中I/O內存起始地址。

unsigned int irq
設備中斷號。它可以被多個設備共享。設備驅動調用request_irq來分配這個值,並調用free_irq來釋放它。

unsigned char if_port
接口的端口類型。

unsigned char dma
設備所使用的DMA通道。爲獲取和釋放一個DMA通道,內核在kernel/dma.c中定義了兩個函數request_dma和free_dma。爲了在獲取dma通道後,啓用或者停止dma通道,內核定義了兩個函數enable_dma和disable_dma。這兩個函數的實現與體系結構相關,所以在include/asm-architecture下有相關的文件(例如include/asm-i386)。這些函數被ISA設備使用;PCI設備不使用這些函數,它們使用其他函數。

並不是所有的設備都可以使用dma,因爲有些總線不支持dma。

unsigned short flags
unsigned short gflags
unsigned short priv_flags
flags變量中的某些位表示網絡設備所支持的功能(例如是否支持多播IFF_MULTICAST等),其他位表示設備狀態的變化(例如IFF_UP或者 IFF_RUNNING)。你可以在include/linux/if.h中找到flags 的完整列表。設備驅動通常在設備初始化時設置設備所支持的功能,而設備狀態則由內核根據某些外部事件來設置。flags的值可以通過命令ifconfig 查看:

bash# ifconfig lo
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:3924 Metric:1
RX packets:198 errors:0 dropped:0 overruns:0 frame:0
TX packets:198 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0

在這個例子中,UP LOOPBACK RUNNING等名詞分別對應標記IFF_UP, IFF_LOOPBACK,IFF_RUNNING。

priv_flags存儲一些對用戶空間程序不可見的標記。目前,這個變量被VLAN和橋虛擬設備使用。
gflags幾乎不會被使用,保留它只是爲了保持兼容。標記可以通過dev_change_flags函數修改。

int features
另一個使用比特位來表示設備所支持的功能的變量。這個變量與上述的三個變量並不重複。features變量向CPU報告設備所支持的功能,例如設備是否支持高端內存的DMA,是否支持用硬件計算包的校驗和等。所有可能支持的功能列表都在net_device結構中定義。這個變量由設備驅動初始化。你可以在 net_device結構的定義中找到相應的NETIF_F_XXX列表,每個宏都有很好的註釋。

unsigned mtu
MTU的意思是最大傳輸單元,它表示設備可以處理幀的最大長度。表1列出了一些常見網絡設備所支持的MTU值。

Table 1. MTU values for different device types

Device type

MTU

PPP

296

SLIP

296

Ethernet

1,500

ISDN

1,500

PLIP

1,500 (ether_setup)

Wavelan

1,500 (ether_setup)

EtherChannel

2,024

FDDI

4,352

Token Ring 4 MB/s (IEEE 802.5)

4,464

Token Bus (IEEE 802.4)

8,182

Token Ring 16 MB/s (IBM)

17,914

Hyperchannel

65,535

在1998年,Alteon Networks(2000年被Nortel Networks收購)提議將以太網幀的負載大小增加到9K字節。這個提案後來成爲IETF的因特網草案,但是IEEE卻沒有接受它。IEEE規範中超過 1500字節的幀通常被成爲jumbo幀,這種幀用在千兆以太網中以增加通吐量。這是因爲更大的幀意味着用更少的幀可以傳輸更多的數據,從而減少中斷的數量,減少CPU的使用量,並減少頭部數據的佔用率。關於增加以太網的MTU可以帶來的好處,以及爲什麼IEEE沒有接受這個擴展的原因可以在白皮書“在以太網中使用擴展的幀大小”中找到。這篇文章可以在網上搜索, 或者使用這個鏈接:
http://www.ietf.org/proceedings/01aug/I-D/draft-ietf-isis-ext-eth-01.txt

unsigned short type
設備類型(以太網,幀中繼等)。在include/linux/if_arp.h中有完整的類型列表。

unsigned short hard_header_len
以字節爲單位的幀頭部長度。例如,以太網幀的頭是14字節。某種設備所支持幀的頭部長度在相應的設備頭文件中定義。對以太網來說,ETH_HLEN在中定義。

unsigned char broadcast[MAX_ADDR_LEN]
鏈路層廣播地址。

unsigned char dev_addr[MAX_ADDR_LEN]
unsigned char addr_len
dev_addr是設備的鏈路層地址,不要把它和IP地址或者L3地址混淆了。鏈路層地址的長度是addr_len,以字節爲單位。

int promiscuity
混雜模式

2.1. Interface types and ports

有些設備可以支持多種接口(最常見的組合是BNC+RJ45),用戶可以根據需要來選擇使用哪種接口。這個變量用來設置設備的接口類型。如果配置命令沒有指定設備的接口類型,設備驅動就使用缺省的類型。在某些情況下,一個設備驅動可以處理多種接口類型;在這種情況下,設備驅動可以按一定的順序來測試每個接口的類型。下面的代碼片斷展示了一個設備驅動如何根據配置來設置接口的類型:

switch (dev->if_port) {
case IF_PORT_10BASE2:
writeb((readb(addr) & 0xf8) | 1, addr);
break;
case IF_PORT_10BASET:
writeb((readb(addr) & 0xf8), addr);
break; }

2.2. 混雜模式

有些網絡管理任務需要系統接收所有經過共享線纜的幀,而不僅是發給本機的幀;如果一個設備可以接收共享線纜上的所有幀,那麼就可以說它工作在混雜模式下。如果一個應用程序需要測試本地網的網絡性能或者檢查網絡的安全漏洞時,就需要使用混雜模式。混雜模式同樣被網橋代碼使用。當然,這對那些惡意的監聽者也很有用,所有,在本地網上傳輸的數據都是不安全的,除非數據已被加密。

net_device結構中包含一個promiscuity計數器來標識設備是否工作在混雜模式。之所以使用計數器而不是一個標誌位的原因是:可能有多個用戶程序設置設備工作在混雜模式下。因此,每次進入混雜模式,計數器加一;退出混雜模式,計數器減一。只有計數器爲0 時,設備才退出混雜模式。這個變量通常調用函數dev_set_promiscuity來設置。

如果promiscuity不爲0(調用函數dev_set_promiscuity設置),那麼flags變量裏面的 IFF_PROMISC標誌位會被置位,這個標誌位會在配置接口屬性的函數中被檢查。下面是一段摘自drivers/net/3c59x.c的代碼,它展示了根據flags中的標誌位來設置不同接收模式的過程:

static void set_rx_mode(struct net_device *dev)  
{          
 int ioaddr = dev->base_addr;          
 int new_mode;         
     if (dev->flags & IFF_PROMISC) {               
 if (corqscreq_debug > 3)                          
 printk("%s: Setting promiscuous mode./n", dev->name);               
 new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm;          
} else if ((dev->mc_list)    ||    (dev->flags & IFF_ALLMULTI)) {               
 new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast;          
} else               
 new_mode = SetRxFilter | RxStation | RxBroadcast;            
 outw(new_mode, ioaddr + EL3_CMD);    }

如果設置了IFF_PROMISC標誌,new_mode變量會被初始化爲接收發給本機的幀(RxStation),多播幀(RxMulticast),廣播幀(RxBroadcast),和其他所有的幀(RxProm)。EL3_CMD是距離設備內存起始地址的偏移量,它是內核發送給設備的命令所存儲的地址。

3. Statistics

net_device結構裏面沒有包含描述各種統計信息的變量,相反,它包含一個名爲priv的指針,這個指針指向一個設備私有的,描述設備統計信息的數據結構。這個私有數據結構中包括了諸如設備發包數,設備收包數,以及出錯包數等統計信息的變量。

priv所指向的數據結構的定義與設備類型和設備的工作模式有關:因此,不同的以太網卡可能擁有不同的私有數據結構。但是,幾乎所有的結構都包含一個類型是net_device_stats的變量(在include/linux/netdevice.h中定義),它包含了所有設備都具有的常用統計信息,這些信息可以通過函數get_stats來獲取。

無線設備的行爲與有線設備的行爲不同,因此,在無線設備裏沒有net_device_stats類型的變量。相反,無線設備有一個類型是iw_statistics的變量,這個變量的值可以通過函數get_wireless_stats來獲取,這個函數會在後面描述。

priv所指向的數據結構的名稱有時會反映設備的類型(例如,vortex_private代表Vortex和 Boomerang系列,有時也被稱作3c59x系列)。一般情況下,它的名字就是簡單的net_local。但是,net_local中的變量對不同的設備來說,都是不同的。

私有數據結構的複雜度與設備所支持的功能,以及設備驅動作者希望支持多麼複雜的統計功能,或者多麼複雜的設計以提高設備性能有關。例如,drivers/net/3c507.c中的3c507驅動所定義的net_local就比drivers/net/3c59x.c中的 3c59x驅動所定義的vortex_private簡單的多。當然,它們都包含一個類型是net_device_stats的變量。

4. Device Status

爲了控制與設備的交互,每個設備驅動都會維護一些信息,比如:時間戳,接口支持哪些功能的標記等。在SMP系統中,內核需要保證不同cpu對同一設備的併發訪問能夠正確地執行。net_device結構中的以下幾個變量實現這些功能:

unsigned long state
它包含一組被網絡隊列子系統使用的標記。這些標記的值是枚舉類型netdev_state_t中的索引值,這個類型的定義在 include/linux/netdevice.h中,每個標記都是諸如__LINK_STATE_XOFF這樣的常量。每一位都可以通過函數 set_bit和clear_bit設置或清除,但通常情況下,都會有一個包裝函數來隱藏標記位的信息。例如,在網絡隊列子系統停止一個設備隊列時,它調用函數netif_stop_queue,這個函數的定義如下:

static inline void netif_stop_queue(struct net_device *dev)    
{        
 ...        
 set_bit(_ _LINK_STATE_XOFF, &dev->state);    
}

enum {...} reg_state
設備的註冊狀態。


unsigned long trans_start
最後一個幀開始發送的時間(用jiffies度量)。設備驅動在發送之前設置這個變量。這個變量用來檢測網卡是否在給定的時間內把幀發送了出去。太長的發送時間意味着有錯誤發生,在這種情況下,設備驅動會重置網卡。


unsigned long last_rx

接收到最後一個包的時間(用jiffies度量)。目前,這個變量沒有什麼特殊的用途,但是,將來有可能會用到。


struct net_device *master

有些協議允許多個設備組合到一起當做一個設備使用。這些協議包括EQL(串行網絡的覆載均衡),Bonding(又被稱作EtherChannel和trunking),和TEQL(trueequalizer,它是流量控制子系統中的一個排隊策略)。在設備組中,有一個設備被選出來當作主設備,它有特殊的作用。這個變量是一個指向組中主設備的指針。如果設備不是一個組的成員,這個指針被置爲NULL。


spinlock_t xmit_lock



int xmit_lock_owner

xmit_lock用來序列化對設備驅動函數hard_start_xmit的調用。這意味着,每個cpu每次只能調用設備完成一次發送。 xmit_lock_owner是擁有鎖的CPU的ID。在單cpu系統上,這個值是0;在多cpu系統中,如果鎖沒有被佔用,這個值是-1。內核同樣允許不加鎖的發送,前提條件是設備驅動必須支持這個功能


void *atalk_ptr



void *ip_ptr



void *dn_ptr



void *ip6_ptr



void *ec_ptr



void *ax25_ptr

這六個變量指向特定協議的數據結構,每個數據結構都包含協議私有的參數。例如, ip_ptr指向一個in_device類型的結構(儘管ip_ptr的類型是void*),它包含IPv4相關的參數,其中包括設備的IP地址列表等。

5. List Management

net_device結構放在一個全局的鏈表和兩個哈希表中。以下變量用於組成這些鏈表和哈希表:

struct net_device *next

指向全局鏈表中的下一個節點


struct hlist_node name_hlist



struct hlist_node index_hlist

把net_device結構鏈接到兩個哈希表中。

6. Link Layer Multicast

多播是一種將數據傳遞給多個接收者的技術。多播既可以在L3(如ip)上實現,也可以在L2(如以太網)上實現。在這一節,我們關注的是後面一種實現。

鏈路層多播可以通過特殊的多播地址或者在鏈路層頭部填入特殊的控制信息來實現(如果鏈路層協議不支持多播,可以採用模擬的方式實現)。以太網本身就支持多播。多播地址通過一個特定的位與其他地址區分開。這就意味着,有50%的地址是多播地址,它的數量是2的48次方,這是一個巨大的數字。如果一個接口要加入一個多播組(一個多播地址就是一個多播組),它可以簡單地接收所有多播地址的幀,而不是維護一個多播地址列表,並且過濾那些不在列表中的地址。net_device結構中的flags變量中有一位來控制設備是否監聽所有的地址。設備驅動設置或清除這個標記的動作由 all_multi變量控制。

每個net_device結構都會包含一個dev_mc_list類型的變量,這個變量維護設備監聽的多播地址表。添加和刪除多播地址可以分別調用函數dev_mc_add和dev_mc_delete完成。在net_device中,相關的變量還包括:


struct dev_mc_list *mc_list

Pointer to the head of this device's list of dev_mc_list structures.


int mc_count

The number of multicast addresses for this device, which is also the length of the list to which mc_list points.


int allmulti

如果是非零值,那麼設備將監聽所有的多播地址。和promiscuity一樣,這個變量是一個計數器而不僅是一個布爾值。這是因爲多個設備(比如VLAN和bonding設備)可能獨立地要求監聽所有地址。如果這個變量的值從0變爲非零,內核會調用函數 dev_set_allmulti通知設備監聽所有的多播地址。如果這個值變爲0,則停止監聽所有的多播地址。

7. Traffic Management

Linux流量控制子系統的功能已經非常強大,並且已經成爲Linux內核中的一個重要組件。相關的內核選項是 “Device drivers ->Networking support ->Networking options ->QoS and/or fair queueing”。net_device中的相關變量包括:


struct net_device *next_sched



struct Qdisc *qdisc



struct Qdisc *qdisc_sleeping



struct Qdisc *qdisc_ingress



struct list_head qdisc_list

這些變量管理設備的接收,發送隊列,並且可以被不同的cpu訪問。


spinlock_t queue_lock



spinlock_t ingress_lock

流量控制子系統爲每個網絡設備定義了一個私有的發送隊列。queue_lock用於避免併發的訪問ingress_lock用於保護接收隊列。


unsigned long tx_queue_len

設備發送隊列的長度。如果內核中包含了流量控制子系統,這個變量可能沒有什麼用(只有幾個排隊策略會使用它)。表2列出了常見設備的tx_queue_len值。這個值可以通過sysfs文件系統修改(在/sys/class/net/device_name/目錄下)。

Table 2. tx_queue_len values for different device types

Device type

tx_queue_len

Ethernet

1,000

Token Ring

100

EtherChannel

100

Fibre Channel

100

FDDI

100

TEQL (true link equalizer)

100

ISDN

30

HIPPI

25

PLIP

10

SLIP

10

AX25

10

EQL (Equalizer load balancer for serial network interfaces)

5

Generic PPP

3

Bonding

0

Loopback

0

Bridge

0

VLAN

0

a TEQL is one of the queuing disciplines you can configure with Traffic Control (the QoS layer).


用不用tx_queue_len與設備的接收,發送隊列的排隊策略有關。一般情況下都使用FIFO
隊列(先進先出)或者其他一些簡單的隊列。

虛擬設備的隊列長度一般都是0:它們依賴於真實設備來完成包的緩存(loopback設備是一個特例,它們不需要隊列,因爲發給它們的包一般都是在內核中立即發送的)。

8. Feature Specific

就像我們在sk_buff中看到的一樣,net_device中的有些變量是與特性相關的,只有內核包含了這個特性,這些變量纔會有效。[]這些變量只有在內核包含某些特性是纔有效,比如br_port

struct divert_blk *divert

Diverter功能允許你修改進入包的源地址和目的地址。它可以把特定包轉發到某個特定接口或主機。爲了使用這個功能,內核必須包含其他一些功能,比如網橋。這個指針指向的數據結構中包含了這個功能所使用的變量。相應的內核選項是“Device drivers ->Networking support ->Networking options ->Frame Diverter”。


struct net_bridge_port *br_port

設備被配置成網橋接口時所需要的附加信息。相應的內核選項是“Device drivers ->Networking support ->Networking options->802.1d Ethernet Bridging”。


void (*vlan_rx_register)(...)



void (*vlan_rx_add_vid)(...)



void (*vlan_rx_kill_vid)(...)

這三個函數指針被VLAN設備用來註冊設備可以處理的VLAN標記(參見net/8021q/vlan.c),它可以向設備增加一個VLAN或者從設備上刪除一個VLAN。相應的內核選項是“Device drivers ->Networking support ->Networking options->802.1Q VLAN Support”


int netpoll_rx



void (*poll_controller)(...)

9. Generic

除了前面提到的維護設備鏈表的變量外,還有一些變量用於維護net_device結構。需要注意的是,如果這些變量不再有什麼作用,就會被刪掉:


atomic_t refcnt

引用計數。如果計數不爲0,設備就不能被卸載


int watchdog_timeo



struct timer_list watchdog_timer

這些變量,包括前面提到的tx_timeout變量,用於實現 “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

Additional parameters and function pointers used by wireless devices. See also get_wireless_stats.


struct list_head todo_list

註冊和卸載一個網絡設備需要兩個步驟,todo_list在第二個步驟中使用。


struct class_device class_dev

Used by the new generic kernel driver infrastructure.

10. Function Pointers

內核網絡代碼使用了很多函數指針。net_device結構中同樣包括了許多函數指針。這些函數指針主要完成以下幾個功能:

  • Transmit and receive a frame

  • Add or parse the link layer header on a buffer

  • Change a part of the configuration

  • Retrieve statistics

  • Interact with a specific feature

A few function pointers were already introduced in the previous sections when describing the fields used to accomplish a specific task. Here are the generic ones:


struct ethtool_ops *ethtool_ops

設置或獲取不同設備參數的一組函數指針。


int (*init)(...)



void (*uninit)(...)



void (*destructor)(...)



int (*open)(...)



int (*stop)(...)

Used to initialize, clean up, destroy, enable, and disable a device. Not all of them are always used.


struct net_device_stats* (*get_stats)(...)



struct iw_statistics* (*get_wireless_stats)(...)

有些設備統計信息可以通過用戶空間程序顯示,比如ifconfig和ip;而其他統計信息只能被內核使用。有兩個函數用於獲取設備統計信息。get_stats用於操作一個普通設備,而get_wireless_stats
用於操作一個無線設備。


int (*hard_start_xmit)(...)

Used to transmit a frame.


int (*hard_header)(...)



int (*rebuild_header)(...)



int (*hard_header_cache)(...)



void (*header_cache_update)(...)



int (*hard_header_parse)(...)



int (*neigh_setup)(...)

Used by the neighboring layer.


int (*do_ioctl)(...)

ioctl is the system call used to issue commands to devices . This method is called to process some of the ioctl commands .


void (*set_multicast_list)(...)

mc_list和mc_count用於維護L2的多播地址表。這個方法用於設置設備驅動監聽哪些多播地址。通常情況下,它不會被直接調用,而是通過一個包裝函數,比如dev_mc_upload或者不加鎖版本__dev_mc_upload調用。如果一個設備不能維護一個多播地址表,那麼可以簡單地設置它監聽所有的多播地址。


int (*set_mac_address)(...)

Changes the device MAC address. When the device does not provide this capability (as in the case of Bridge virtual devices), it is set to NULL.


int (*set_config)(...)

設置設備參數,比如irq,io_addr,和if_port等。高層參數(比如協議地址)由do_ioctl設置。只有很少的設備使用這個函數。新的設備一般都採用自動探測的方式獲取這些參數。你可以在drivers/net/sis900.c中找到 sis900_set_config,這是一個很好的例子。


int (*change_mtu)(...)

修改設備的MTU。修改mtu不會對設備驅動有任何影響,它只是讓協議棧軟件可以根據新的 mtu正確地處理分片。


void (*tx_timeout)(...)

在watchdog定時器超時後調用這個函數。它用來確定發送一個幀的時間是否太長。如果這個函數沒有定義,watchdog定時器就不會被啓用。


int (*accept_fastpath)(...)

快速交換(又被稱作FASTROUTE)是內核的一個功能,它允許設備驅動在中斷上下文中用一個小的緩存來快速路由進入的包(跳過協議層軟件)。從2.6.8版開始, 內核已經不支持這個功能了。這個函數用於測試一個設備是否支持快速交換功能。

     http://blog.chinaunix.net/u/26185/showart_455200.html


發佈了0 篇原創文章 · 獲贊 8 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章