網絡卡特性混亂及其解決方案

作者

Michał Mirosław <mirq-linux@rere.qmqm.pl>

第一部分:特性集合

曾經的網絡卡僅能按字面意思收發資料包的日子一去不復返了。如今的裝置增加了多項功能和“缺陷”(也可理解為:解除安裝),減輕了作業系統在生成和檢查校驗和、拆分資料包以及對資料包進行分類等方面的各種任務負擔。在 Linux 核心領域,這些能力及其狀態通常被稱為“netdev 特性”。

目前有三組特性與驅動程式相關,另有一組由網路核心層內部使用:

  1. `netdev->hw_features` 集合包含使用者可以請求為特定裝置更改(啟用或停用)狀態的特性。此集合應在 `ndo_init` 回撥中初始化,之後不應更改。

  2. `netdev->features` 集合包含當前為裝置啟用的特性。此集合應僅由網路核心層或在 `ndo_set_features` 回撥的錯誤路徑中更改。

  3. `netdev->vlan_features` 集合包含其狀態由子 VLAN 裝置繼承的特性(限制 `netdev->features` 集合)。無論標籤是在硬體還是軟體中剝離或插入,此集合目前用於所有 VLAN 裝置。

  4. `netdev->wanted_features` 集合包含使用者請求的特性集合。每當此集合或某些裝置特定條件發生變化時,此集合會透過 `ndo_fix_features` 回撥進行過濾。此集合是網路核心層內部使用的,不應在驅動程式中引用。

第二部分:控制已啟用的特性

當需要更改當前特性集合(`netdev->features`)時,會透過呼叫 `ndo_fix_features` 回撥和 `netdev_fix_features()` 來計算和過濾新的集合。如果結果集合與當前集合不同,它會被傳遞給 `ndo_set_features` 回撥,並且(如果回撥成功返回)替換 `netdev->features` 中儲存的值。之後,每當當前集合可能發生變化時,就會發出 `NETDEV_FEAT_CHANGE` 通知。

以下事件觸發重新計算:
  1. 設備註冊後,`ndo_init` 返回成功

  2. 使用者請求更改特性狀態

  3. 呼叫了 netdev_update_features()

`ndo_*_features` 回撥在持有 `rtnl_lock` 的情況下被呼叫。缺失的回撥被視為始終返回成功。

想要觸發重新計算的驅動程式必須在持有 `rtnl_lock` 的情況下呼叫 netdev_update_features()。這不應在 `ndo_*_features` 回撥中完成。驅動程式不應修改 `netdev->features`,除非透過 `ndo_fix_features` 回撥。

第三部分:實現提示

  • ndo_fix_features

特性之間的所有依賴關係應在此處解決。結果集合可能會因網路核心層施加的限制(如 `netdev_fix_features()` 中編碼的)而被進一步縮小。因此,當某個特性的依賴關係不滿足時,更安全的做法是停用該特性,而不是強制啟用其依賴。

此回撥不應修改硬體或驅動程式狀態(應是無狀態的)。它可以在連續的 `ndo_set_features` 呼叫之間被多次呼叫。

回撥不得更改 `NETIF_F_SOFT_FEATURES` 或 `NETIF_F_NEVER_CHANGE` 集合中包含的特性。`NETIF_F_VLAN_CHALLENGED` 是個例外,但必須注意,因為此更改不會影響已經配置的 VLAN。

  • ndo_set_features

硬體應重新配置以匹配傳入的特性集合。除非發生 `ndo_fix_features` 無法可靠檢測到的錯誤情況,否則不應更改該集合。在這種情況下,回撥應更新 `netdev->features` 以匹配最終的硬體狀態。返回的錯誤不會(也無法)傳播到 `dmesg` 以外的任何地方。(注意:成功返回零,大於零表示靜默錯誤。)

第四部分:特性

有關當前特性列表,請參閱 `include/linux/netdev_features.h`。本節描述其中一些特性的語義。

  • 傳送校驗和計算

有關完整描述,請參閱 `include/linux/skbuff.h` 檔案頂部附近的註釋。

注意:`NETIF_F_HW_CSUM` 是 `NETIF_F_IP_CSUM` + `NETIF_F_IPV6_CSUM` 的超集。這意味著裝置可以在資料包的任何位置填充類似 TCP/UDP 的校驗和,無論其中可能存在什麼頭部。

  • 傳送 TCP 分段解除安裝

`NETIF_F_TSO_ECN` 意味著硬體可以正確地分割帶有 CWR 位設定的資料包,無論是 TCPv4(當 `NETIF_F_TSO` 啟用時)還是 TCPv6(`NETIF_F_TSO6`)。

  • 傳送 UDP 分段解除安裝

`NETIF_F_GSO_UDP_L4` 接受一個帶有超過 `gso_size` 負載的單個 UDP 頭部。在分段時,它會沿著 `gso_size` 邊界對負載進行分段,並複製網路和 UDP 頭部(如果最後一個分段小於 `gso_size` 則進行修正)。

  • 高記憶體 DMA 傳送

在相關平臺上,`NETIF_F_HIGHDMA` 表示 `ndo_start_xmit` 可以處理包含高記憶體中分段的 `skb`。

  • 傳送分散/聚集

這些特性表示 `ndo_start_xmit` 可以處理分段的 `skb`:`NETIF_F_SG` 用於頁式 `skb`(`skb_shinfo()->frags`),`NETIF_F_FRAGLIST` 用於鏈式 `skb`(`skb->next/prev` 列表)。

  • 軟體特性

`NETIF_F_SOFT_FEATURES` 中包含的特性是網路堆疊的特性。驅動程式不應根據它們改變行為。

  • VLAN 功能受限

對於無法處理 VLAN 頭部(或幀)的裝置,應設定 `NETIF_F_VLAN_CHALLENGED`。一些驅動程式設定此特性是因為網絡卡無法處理更大的 MTU。[FIXME: 這些情況可以透過在 VLAN 程式碼中僅允許降低 MTU 的 VLAN 來解決。不過,這可能沒有用。]

  • rx-fcs

這要求網絡卡將乙太網幀校驗序列(FCS)附加到 `skb` 資料的末尾。這允許抓包工具和其他工具讀取網絡卡在接收資料包時記錄的 CRC。

  • rx-all

這要求網絡卡接收所有可能的幀,包括錯誤幀(例如錯誤的 FCS 等)。這在嗅探包含錯誤資料包的鏈路時非常有用。如果也將其置於普通混雜模式,某些網絡卡可能會接收更多資料包。

  • rx-gro-hw

這要求網絡卡啟用硬體 GRO(通用接收解除安裝)。硬體 GRO 基本上是 TSO 的確切逆向操作,並且通常比硬體 LRO 更嚴格。經硬體 GRO 合併的資料包流必須能夠透過 GSO 或 TSO 重新分段回完全原始的資料包流。硬體 GRO 依賴於 RXCSUM,因為每個由硬體成功合併的資料包也必須由硬體驗證其校驗和。

  • hsr-tag-ins-offload

對於自動插入 HSR(高可用性無縫冗餘)或 PRP(並行冗餘協議)標籤的裝置,應設定此項。

  • hsr-tag-rm-offload

對於自動移除 HSR(高可用性無縫冗餘)或 PRP(並行冗餘協議)標籤的裝置,應設定此項。

  • hsr-fwd-offload

對於在硬體中將 HSR(高可用性無縫冗餘)幀從一個埠轉發到另一個埠的裝置,應設定此項。

  • hsr-dup-offload

對於在硬體中自動複製傳出 HSR(高可用性無縫冗餘)或 PRP(並行冗餘協議)標籤幀的裝置,應設定此項。

  • netmem-tx

對於支援 `netmem TX` 的裝置,應設定此項。請參閱 網路驅動程式的 Netmem 支援