網卡的 Ring Buffer 詳解

1. 網卡處理數據包流程

網卡處理網絡數據流程圖:linux

圖片來自參考連接1git

上圖中虛線步驟的解釋:github

  1. DMA 將 NIC 接收的數據包逐個寫入 sk_buff ,一個數據包可能佔用多個 sk_buff , sk_buff 讀寫順序遵循FIFO(先入先出)原則。
  2. DMA 讀完數據以後,NIC 會經過 NIC Interrupt Handler 觸發 IRQ (中斷請求)。
  3. NIC driver 註冊 poll 函數。
  4. poll 函數對數據進行檢查,例如將幾個 sk_buff 合併,由於可能同一個數據可能被分散放在多個 sk_buff 中。
  5. poll 函數將 sk_buff 交付上層網絡棧處理。

完整流程:緩存

  1. 系統啓動時 NIC (network interface card)  進行初始化,系統分配內存空間給 Ring Buffer 。
  2. 初始狀態下,Ring Buffer 隊列每一個槽中存放的 Packet Descriptor 指向 sk_buff ,狀態均爲 ready。
  3. DMA 將 NIC 接收的數據包逐個寫入 sk_buff ,一個數據包可能佔用多個 sk_buff ,sk_buff 讀寫順序遵循FIFO(先入先出)原則。
  4. 被寫入數據的 sk_buff 變爲 used 狀態。
  5. DMA 讀完數據以後,NIC 會經過 NIC Interrupt Handler 觸發 IRQ (中斷請求)。
  6. NIC driver 註冊 poll 函數。
  7. poll 函數對數據進行檢查,例如將幾個 sk_buff 合併,由於可能同一個數據可能被分散放在多個 sk_buff 中。
  8. poll 函數將 sk_buff 交付上層網絡棧處理。
  9. poll 函數清理 sk_buff,清理 Ring Buffer 上的 Descriptor 將其指向新分配的 sk_buff 並將狀態設置爲 ready。

2. 多 CPU 下的 Ring Buffer 處理

由於分配給 Ring Buffer 的空間是有限的,當收到的數據包速率大於單個 CPU 處理速度的時候 Ring Buffer 可能被佔滿,佔滿以後再來的新數據包會被自動丟棄。服務器

若是在多核 CPU 的服務器上,網卡內部會有多個 Ring Buffer,NIC 負責將傳進來的數據分配給不一樣的 Ring Buffer,同時觸發的 IRQ 也能夠分配到多個 CPU 上,這樣存在多個 Ring Buffer 的狀況下 Ring Buffer 緩存的數據也同時被多個 CPU 處理,就能提升數據的並行處理能力。網絡

固然,要實現「NIC 負責將傳進來的數據分配給不一樣的 Ring Buffer」,NIC 網卡必須支持 Receive Side Scaling(RSS) 或者叫作 multiqueue 的功能。RSS 除了會影響到 NIC 將 IRQ 發到哪一個 CPU 以外,不會影響別的邏輯了。數據處理過程跟以前描述的是同樣的。tcp

3. Ring Buffer 相關命令

在生產實踐中,因 Ring Buffer 寫滿致使丟包的狀況不少。當環境中的業務流量過大且出現網卡丟包的時候,考慮到 Ring Buffer 寫盡是一個很好的思路。ide

總結下 Ring Buffer 相關的命令:函數

3.1 網卡收到的數據包統計

[root@server-20.140.beishu.polex.io ~ ]$ ethtool -S em1 | more
NIC statistics:
     rx_packets: 35874336743
     tx_packets: 35163830212
     rx_bytes: 6337524253985
     tx_bytes: 3686383656436
     rx_broadcast: 15392577
     tx_broadcast: 873436
     rx_multicast: 45849160
     tx_multicast: 1784024

RX 就是收到數據,TX 是發出數據。spa

3.2 帶有 drop 字樣的統計和 fifo_errors 的統計

[root@server-20.140.beishu.polex.io ~ ]$ ethtool -S em1 | grep -iE "error|drop"
rx_crc_errors: 0
rx_missed_errors: 0
tx_aborted_errors: 0
tx_carrier_errors: 0
tx_window_errors: 0
rx_long_length_errors: 0
rx_short_length_errors: 0
rx_align_errors: 0
dropped_smbus: 0
rx_errors: 0
tx_errors: 0
tx_dropped: 0
rx_length_errors: 0
rx_over_errors: 0
rx_frame_errors: 0
rx_fifo_errors: 79270
tx_fifo_errors: 0
tx_heartbeat_errors: 0
rx_queue_0_drops: 16669
rx_queue_1_drops: 21522
rx_queue_2_drops: 0
rx_queue_3_drops: 5678
rx_queue_4_drops: 5730
rx_queue_5_drops: 14011
rx_queue_6_drops: 15240
rx_queue_7_drops: 420

發送隊列和接收隊列 drop 的數據包數量顯示在這裏。而且全部 queue_drops 加起來等於 rx_fifo_errors。因此整體上能經過 rx_fifo_errors 看到 Ring Buffer 上是否有丟包。若是有的話一方面是看是否須要調整一下每一個隊列數據的分配,或者是否要加大 Ring Buffer 的大小。

3.3 查詢 Ring Buffer 大小

[root@server-20.140.beishu.polex.io ~ ]$ ethtool -g em1
Ring parameters for em1:
Pre-set maximums:
RX: 4096
RX Mini: 0
RX Jumbo: 0
TX: 4096
Current hardware settings:
RX: 256
RX Mini: 0
RX Jumbo: 0
TX: 256

RX 和 TX 最大是 4096,當前值爲 256 。隊列越大丟包的可能越小,但數據延遲會增長。

3.4 調整 Ring Buffer 隊列數量

[root@server-20.140.beishu.polex.io ~ ]$ ethtool -l em1
Channel parameters for em1:
Pre-set maximums:
RX:        0
TX:        0
Other:        1
Combined:    8
Current hardware settings:
RX:        0
TX:        0
Other:        1
Combined:    8

Combined = 8,說明當前 NIC 網卡會使用 8 個進程處理網絡數據。

更改 eth0 網卡 Combined 的值:

ethtool -L eth0 combined 8

須要注意的是,ethtool 的設置操做可能都要重啓一下才能生效。

3.4 調整 Ring Buffer 隊列大小

查看當前 Ring Buffer 大小:

[root@server-20.140.beishu.polex.io ~ ]$ ethtool -g em1
Ring parameters for em1:
Pre-set maximums:
RX:        4096
RX Mini:    0
RX Jumbo:    0
TX:        4096
Current hardware settings:
RX:        256
RX Mini:    0
RX Jumbo:    0
TX:        256

看到 RX 和 TX 最大是 4096,當前值爲 256。隊列越大丟包的可能越小,但數據延遲會增長.

設置 RX 和 TX 隊列大小:

ethtool -G em1 rx 4096
ethtool -G em1 tx 4096

3.5 調整 Ring Buffer 隊列的權重

NIC 若是支持 mutiqueue 的話 NIC 會根據一個 Hash 函數對收到的數據包進行分發。能調整不一樣隊列的權重,用於分配數據。

[root@server-20.140.beishu.polex.io ~ ]$ ethtool -x em1
RX flow hash indirection table for em1 with 8 RX ring(s):
    0:      0     0     0     0     0     0     0     0
    8:      0     0     0     0     0     0     0     0
   16:      1     1     1     1     1     1     1     1
   24:      1     1     1     1     1     1     1     1
   32:      2     2     2     2     2     2     2     2
   40:      2     2     2     2     2     2     2     2
   48:      3     3     3     3     3     3     3     3
   56:      3     3     3     3     3     3     3     3
   64:      4     4     4     4     4     4     4     4
   72:      4     4     4     4     4     4     4     4
   80:      5     5     5     5     5     5     5     5
   88:      5     5     5     5     5     5     5     5
   96:      6     6     6     6     6     6     6     6
  104:      6     6     6     6     6     6     6     6
  112:      7     7     7     7     7     7     7     7
  120:      7     7     7     7     7     7     7     7
RSS hash key:
Operation not supported

個人 NIC 一共有 8 個隊列,一共有 128 個不一樣的 Hash 值,上面就是列出了每一個 Hash 值對應的隊列是什麼。最左側 0 8 16 是爲了能讓你快速的找到某個具體的 Hash 值。好比 Hash 值是 76 的話咱們能當即找到 72 那一行:」72: 4 4 4 4 4 4 4 4」,從左到右第一個是 72 數第 5 個就是 76 這個 Hash 值對應的隊列是 4 。

設置 8 個隊列的權重。加起來不能超過 128 。128 是 indirection table 大小,每一個 NIC 可能不同。

3.6 更改 Ring Buffer Hash Field

分配數據包的時候是按照數據包內的某個字段來進行的,這個字段能進行調整。

[root@server-20.140.beishu.polex.io ~ ]$ ethtool -n em1 rx-flow-hash tcp4
TCP over IPV4 flows use these fields for computing Hash flow key:
IP SA
IP DA
L4 bytes 0 & 1 [TCP/UDP src port]
L4 bytes 2 & 3 [TCP/UDP dst port]

也能夠設置 Hash 字段:查看 tcp4 的 Hash 字段。

ethtool -N em1 rx-flow-hash udp4 sdfn

sdfn 須要查看 ethtool 看其含義,還有不少別的配置值。

3.6 IRQ 統計

/proc/interrupts 能看到每一個 CPU 的 IRQ 統計。通常就是看看 NIC 有沒有支持 multiqueue 以及 NAPI 的 IRQ 合併機制是否生效。看看 IRQ 是否是增加的很快。

 

參考連接:

https://ylgrgyq.github.io/2017/07/23/linux-receive-packet-1/

相關文章
相關標籤/搜索