一、中文注釋
/* 在執行通用XDP時,我們必須繞過qdisc層和網絡挖掘點,* 以匹配驅動內XDP的行為。*/
void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog)
{struct net_device *dev = skb->dev; // 獲取skb對應的網絡設備struct netdev_queue *txq; // 聲明網絡設備發送隊列bool free_skb = true; // 初始化一個標志位,用于標記是否需要釋放skbint cpu, rc; // 聲明CPU核心號和返回碼變量txq = netdev_pick_tx(dev, skb, NULL); // 選擇用于發送的隊列cpu = smp_processor_id(); // 獲取當前CPU核心的IDHARD_TX_LOCK(dev, txq, cpu); // 對選定的發送隊列加鎖if (!netif_xmit_stopped(txq)) { // 檢查發送隊列是否被停止rc = netdev_start_xmit(skb, dev, txq, 0); // 開始發送數據包if (dev_xmit_complete(rc)) // 如果發送完成,則不釋放skbfree_skb = false;}HARD_TX_UNLOCK(dev, txq); // 解鎖發送隊列if (free_skb) { // 如果標志位為true,則釋放skbtrace_xdp_exception(dev, xdp_prog, XDP_TX); // 記錄XDP傳輸異常kfree_skb(skb); // 釋放skb}
}
EXPORT_SYMBOL_GPL(generic_xdp_tx); // 導出該符號,GPL協議的模塊可以使用這個函數
這個函數`generic_xdp_tx`用于通用XDP處理的數據包發送。代碼里包含了一系列網絡層的操作和邏輯判斷。它在鎖定CPU和篩選適當的網絡發送隊列之后,嘗試發送數據包,如果發送成功,則記錄結果,否則將數據包內存釋放。
二、XDP
在 Linux 內核 4.9 版本中,XDP 指的是 eXpress Data Path。XDP 是一種高性能、可編程的網絡數據路徑,它允許用戶態程序直接處理在網絡設備驅動程序層收到的包,提供了一種脫離內核網絡棧處理數據包的方法。通過XDP,可以在內核中運行的 eBPF (extended Berkeley Packet Filter) 程序,以決定包應該被丟棄、傳遞到常規的內核網絡棧,還是直接轉發到另一個接口。
XDP可以用于多種場景,比如DDoS攻擊防御、高速包路由、負載均衡等,它通過允許包在被內核完全處理之前就被處理,來顯著減少數據包處理的延遲和CPU占用。這使得XDP尤其適合那些需要非常低延遲和高吞吐量的場合。
在函數`generic_xdp_tx`中,XDP是用來處理網絡數據包,并且在某些情況下,該函數在發送數據包前會繞過正常的內核網絡隊列調度(qdisc layer)和監視點(network taps)。這通常是當在驅動內部使用XDP時要實現的行為。而在這個函數中,我們可以看到涉及到skb(Socket Buffer)的發送邏輯,以及可能的異常記錄和資源清理。這是在軟件層面實現了類似硬件加速XDP的行為,因此被稱為"通用XDP"(generic XDP)。
三、中文講解
這段代碼是 Linux 內核網絡子系統的一部分,它實現了通用 XDP (eXpress Data Path) 傳輸的功能。在不支持驅動程序級別 XDP 的網絡設備上,當你需要處理 XDP 程序的發送行為時,可以使用這個函數。下面是對這段代碼的詳細解釋:
1. 函數簽名 void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog):這個函數接收兩個參數:`skb`(Socket Buffer,網絡數據包的內核表示)和 xdp_prog(與 XDP 程序相關聯的 BPF 程序)。
2. struct net_device *dev = skb->dev;:從 skb 中提取關聯的網絡設備,這個設備表示數據包將要發送的目的地網絡接口。
3. struct netdev_queue *txq;:定義一個指向 netdev_queue 的指針,表示網絡設備的發送隊列。
4. bool free_skb = true;:定義一個布爾變量用來標記是否應該釋放 skb,初始設置為 true,表示默認情況下(如果發送失敗)需要釋放 skb。
5. txq = netdev_pick_tx(dev, skb, NULL);:選擇一個合適的發送隊列來處理 skb。如果無法選擇隊列,可能會返回 NULL。
6. cpu = smp_processor_id();:獲取當前 CPU 的 ID,用于確定發送數據包的 CPU。
7. HARD_TX_LOCK(dev, txq, cpu);:獲取發送路徑上的鎖定,確保數據包發送的臨界區保護。
8. if (!netif_xmit_stopped(txq)) {:檢查是否隊列已經停止發送。如果沒有停止,就嘗試發送數據包。
9. rc = netdev_start_xmit(skb, dev, txq, 0);:調用 netdev_start_xmit 函數發送數據包。
10. if (dev_xmit_complete(rc)):判斷發送函數返回的結果是否表示發送完畢。如果 dev_xmit_complete(rc) 返回 true,則代表數據包已經成功發送,并且不需要釋放 skb。
11. HARD_TX_UNLOCK(dev, txq);:釋放之前獲得的鎖。
12. if (free_skb) {:如果之前沒有發送成功,或者發送函數指示我們需要自行釋放 skb。
13. trace_xdp_exception(dev, xdp_prog, XDP_TX);:如果有需要,記錄 XDP 異常事件(這個調用可能與監控和調試相關)。
14. kfree_skb(skb);:釋放 skb 資源,避免內存泄漏。
15. EXPORT_SYMBOL_GPL(generic_xdp_tx);:導出 generic_xdp_tx 函數,使其可以被模塊使用,同時指明該函數受 GPL 許可證保護。
需要注意的是,這段代碼需要在 Linux 內核的上下文中執行,并且需要相應的內核編程知識和經驗。內核版本不同,細節上可能會有所差異。