1. 程式人生 > >Linux核心TC工具鏈路頻寬設計--CBQ佇列規定

Linux核心TC工具鏈路頻寬設計--CBQ佇列規定

1.1 著名的 CBQ 佇列規定

      除了可以分類之外,CBQ 也是一個整形器。如果想把一個 10Mbps 的連線整形成 1Mbps 的速率,就應該讓鏈路 90%的時間處於閒置狀態,必要的話就強制,以保證 90% 的閒置時間。但閒置時間的測量非常困難,所以 CBQ 就採用了它一個近似值——來自硬體層的兩個傳輸請求之間的毫秒數——來代替它。這個引數可以近似地表徵鏈路的繁忙程度。 

 

1.1.1 CBQ 整形的細節

      CBQ 的工作機制是確認鏈路的閒置時間足夠長,以達到降低鏈路實際頻寬的目的。為此,它要計算兩個資料包的平均傳送間隔。有效閒置時間的測量使用EWMA(exponential weighted moving average, 指數加權移動均值)演算法,也就是說最近處理的資料包的權值比以前的資料包按指數增加。UNIX 的平均負載也是這樣算出來的。計算出來的平均時間值減去 EWMA 測量值,得出的結果叫做“avgidle”。最佳的鏈路負載情況下,這個值應當是 0,資料包嚴格按照計算出來的時間間隔到來。

      在一個過載的鏈路上,avgidle 值應當是負的。如果這個負值太嚴重,CBQ 就會暫時禁止發包,稱為“overlimit”(越限)。相反地,一個閒置的鏈路應該有很大的 avgidle 值,這樣閒置幾個小時後,會造成鏈路允許非常大的頻寬通過。為了避免這種局面,通常用 maxidle 來限制 avgidle 的值不能太大。 

      理論上講,如果發生越限,CBQ 就會禁止發包一段時間(長度就是事先計算出來的傳輸資料包之間的時間間隔),然後通過一個數據包後再次禁止發包。

      下面是配置整形時需要指定的一些引數:

avpkt


      平均包大小,以位元組計。計算 maxidle 時需要,maxidle 從 maxburst 得出。

bandwidth 

      網絡卡的物理頻寬,用來計算閒置時間。

cell 

      包間隔傳送單元的大小。

maxburst 

      這個引數的值決定了計算 maxidle 所使用的資料包的個數。在 avgidle 跌落到 0 之前,這麼多的資料包可以突發傳輸出去。這個值越高,越能夠容納突發傳輸。無法直接設定 maxidle 的值,必須通過這個引數來控制。

minburst 

      如前所述,發生越限時 CBQ 會禁止發包。實現這個的理想方案是根據事先計算出的閒置時間進行延遲之後,發一個數據包。然而,UNIX 的核心一般來說都有一個固定的排程週期(一般不大於10ms),所以最好禁止發包的時間稍長一些,然後突發性地傳輸 minburst 個數據包,而不是一個一個地傳輸。等待的時間叫做 offtime。從大的時間尺度上說,minburst 值越大,整形越精確。但是,從毫秒級的時間尺度上說,就會有越多的突發傳輸。 

minidle 

      如果 avgidle 值降到 0,也就是發生了越限,就需要等待,直到 avgidle 的值足夠大才傳送資料包。為避免因關閉鏈路太久而引起的意外突發傳輸, 在 avgidle 的值太低的時候會被強制設定為 minidle 的值。引數 minidle 的值是以負微秒記的。10 代表 avgidle 被限制在-10us 上。 

mpu 

      最小包傳輸包大小——因為即使是 0 長度的資料包,在乙太網上也要生成封裝成 64 位元組的幀,而需要一定時間去傳輸。為了精確計算閒置時間,CBQ 需要知道這個值。 

rate 

      實際分配的頻寬。

 

1.1.2 CBQ 在分類方面的行為

      除了使用上述 idletime 近似值進行整形之外,CBQ 還可以象 PRIO 佇列那樣,把各種類賦予不同的優先順序,優先權數值小的類會比優先權值大的類被優先處理。每當網絡卡請求把資料包傳送到網路上時,都會開始一個 WRR(weighted round robin,加權輪轉)過程,從優先權值小的類開始。那些佇列中有資料的類就會被分組並被請求出隊。在一個類收到允許若干位元組資料出隊的請求之後,再嘗試下一個相同優先權值的類。

      下面是控制 WRR 過程的一些引數:


allot 

      最大傳輸單元加 MAC 頭的大小。當從外部請求一個 CBQ 發包的時候,它就會按照“priority”引數指定的順序輪流嘗試其內部的每一個類的佇列規定。當輪到一個類發資料時,它只能傳送一定量的資料。“allot”引數就是這個量的基值。 

prio


      CBQ 可以象 PRIO 裝置那樣工作。其中“prio”值較低的類只要有資料就必須先服務,其他類要延後處理。

weight 

      “weight”引數控制 WRR 過程。每個類都輪流取得發包的機會。如果其中一個類要求的頻寬顯著地高於其他的類,就應該讓它每次比其他的類傳送更多的資料。CBQ 會把一個類下面所有的 weight 值加起來後歸一化,所以數值可以任意定,只要保持比例合適就可以。通常把“速率/10”作為引數的值來使用。歸一化值後的值乘以“allot”引數後,決定了每次傳輸多少資料。 

      需要注意的是,在一個 CBQ 內部所有的類都必須使用一致的主號碼。

 

1.1.3 決定鏈路的共享和借用的 CBQ 引數 

      除了純粹地對某種資料流進行限速之外,CBQ 還可以指定哪些類可以向其它哪些類借用或者出借一部分頻寬。

isolated/sharing 

      凡是使用“isolated”選項配置的類,就不會向其兄弟類出借頻寬。選項“sharing”是“isolated”的反義選項。 

bounded/borrow 

      一個類也可以用“bounded”選項配置,意味著它不會向其兄弟類借用頻寬。選項“borrow”是“bounded”的反義選項。 

      一個典型的情況就是一個鏈路上有多個客戶都設定成了“isolated”和“bounded”,那就是說他們都被限制在其要求的速率之下,且互相之間不會借用頻寬。在這樣的一個類的內部的子類之間是可以互相借用頻寬的。

 

1.1.4 配置範例

      這個配置把 WEB 伺服器的流量控制為 5Mbps、SMTP 流量控制在 3Mbps。而且二者一共不得超過 6Mbps,互相之間允許借用頻寬。網絡卡頻寬為 100Mbps。

# sudo tc qdisc add dev enp0s5 root handle 1: cbq bandwidth 100Mbit avpkt 1000 cell 8
# sudo tc class add dev enp0s5 parent 1:0 classid 1:1 cbq bandwidth 100Mbit rate 6Mbit weight 0.6Mbit prio 8 allot 1514 cell 8 maxburst 20 avpkt 1000 bounded

      這裡設定了根為 1:0,並且綁定了類 1:1。也就是說整個頻寬不能超過 6Mbps。

# sudo tc class add dev enp0s5 parent 1:1 classid 1:3 cbq bandwidth 100Mbit rate 5Mbit weight 0.5Mbit prio 5 allot 1514 cell 8 maxburst 20 avpkt 1000
# sudo tc class add dev enp0s5 parent 1:1 classid 1:4 cbq bandwidth 100Mbit rate 3Mbit weight 0.3Mbit prio 5 allot 1514 cell 8 maxburst 20 avpkt 1000

      這裡建立了2 個類。兩個類都沒有配置成“bounded”,但它們都連線到了類 1:1 上,而 1:1 設定了“bounded”。 所以兩個類的總頻寬不會超過 6Mbps。需要注意的是,同一個 CBQ 下面的子類的主號碼都必須與 CBQ 自己的主號碼相一致。

# sudo tc qdisc add dev enp0s5 parent 1:3 handle 30: sfq 
# sudo tc qdisc add dev enp0s5 parent 1:4 handle 40: sfq 

      預設情況下,兩個類都有一個 FIFO 佇列規定。這裡把它換成 SFQ 佇列,以保證每個資料流都公平對待。

# sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 1 u32 match ip sport 80 0xffff flowid 1:3
# sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 1 u32 match ip sport 25 0xffff flowid 1:4

      這裡規定了根上的過濾器,保證資料流被送到正確的佇列規定中去。注意:上面命令先使用了“tc class add”在一個佇列規定中建立了類,然後使用“tc 
qdisc add”在類中建立佇列規定。那些沒有被那兩條規則分類的資料流被 1:0 直接處理,沒有限制。如果 SMTP+web 的總頻寬需求大於 6Mbps,那麼這 6Mbps 頻寬將按照兩個類的 weight 引數的比例情況進行分割:WEB 伺服器得到 5/8 的頻寬,SMTP 得到 3/8 的頻寬。從這個例子來說,可以這麼認為:WEB 資料流總是會得到 5/8*6Mbps=3.75Mbps 的頻寬。 

 

1.2 一個完整的 CBQ 佇列規定例項

流量需求:                                                                                                                               

      流量控制器上的乙太網卡(enp0s5)的IP地址為 10.211.55.17, 在其上建立一個 CBQ 佇列。假設包的平均大小為 1000 位元組,包間隔傳送單元的大小為 8 位元組,可接收衝突的傳送最長包的數目為 20 位元組。                                                                                              

      加入有三種類型的流量需要控制:                                                                                                          

            (1)是發往主機 1 的,其 IP 地址為1 0.211.55.20。其流量頻寬控制在 8Mbit,優先順序為 2;              

            (2)是發往主機 2 的,其 IP 地址為 10.211.55.21。其流量頻寬控制在 1Mbit,優先順序為 1;                                                          

            (3)是發往子網 1 的,其子網號為 10.211.55.0。 子網掩碼為 255.255.255.0。流量頻寬控制在 1Mbit,優先順序為 6。

建立 CBQ 佇列主要分為四個步驟:建立佇列、建立分類、建立過濾器、建立路由。在建立佇列之前,需要對網絡卡的佇列規則進行清除,具體操作如下:

清除網絡卡所有佇列規則

# sudo tc qdisc del dev enp0s5 root 2> /dev/null > /dev/null

建立佇列

# sudo tc qdisc add dev enp0s5 root handle 1: cbq bandwidth 10Mbit avpkt 1000 cell 8 mpu 64

      將一個 cbq 佇列繫結到物理網路裝置 enp0s5 上,其編號為 1:0;物理網路裝置 enp0s5 的實際頻寬為 10Mbit,包的平均大小為 1000 位元組;包間隔傳送單元的大小為 8 位元組,最小傳輸包大小為 64 位元組。

 

建立分類

      建立根分類 1:1,分配頻寬為 10Mbit,優先級別為 8:

#  sudo tc class add dev enp0s5 parent 1:0 classid 1:1 cbq bandwidth 10Mbit rate 10Mbit maxburst 20 allot 1514 prio 8 avpkt 1000 cell 8 weight 1Mbit

      該佇列的最大可用頻寬為 10Mbit,實際分配的頻寬為 10Mbit,可接收衝突的傳送最長包數目為 20 位元組;最大傳輸單元加 MAC 頭的大小為 1514 位元組,優先級別為 8,包的平均大小為 1000 位元組,包間隔傳送單元的大小為 8 位元組,相當於實際頻寬的加權速率為 1Mbit。

      建立分類 1:2,其父類為 1:1,分配頻寬為 8Mbit,優先級別為 2:

#   sudo tc class add dev enp0s5 parent 1:1 classid 1:2 cbq bandwidth 10Mbit rate 8Mbit maxburst 20 allot 1514 prio 2 avpkt 1000 cell 8 weight 800Kbit split 1:0 bounded

      該佇列的最大可用頻寬為 10Mbit,實際分配的頻寬為 8Mbit,可接收衝突的傳送最長包數目為 20 位元組;最大傳輸單元加 MAC 頭的大小為 1514 位元組,優先級別為 2,包的平均大小為 1000 位元組,包間隔傳送單元的大小為 8 位元組,相當於實際頻寬的加權速率為 800Kbit,分類的分離點為 1:0,且不可借用未使用頻寬。

      建立分類 1:3,其父分類為 1:1,分配頻寬為 1Mbit,優先級別為 1:

#   sudo tc class add dev enp0s5 parent 1:1 classid 1:3 cbq bandwidth 10Mbit rate 1Mbit maxburst 20 allot 1514 prio 2 avpkt 1000 cell 8 weight 100Kbit split 1:0

      該佇列的最大可用頻寬是 10Mbit,實際分配的頻寬為 1Mbit,可接收衝突的傳送最長包數目為 20 位元組;最大傳輸單元加 MAC 頭的大小為 1514 位元組,優先級別為 1,包的平均大小為 1000 位元組,包間隔傳送單元的大小為 8 位元組,相當於實際頻寬的加權速率為 100Kbit,分類的分離點為 1:0。

      建立分類 1:4,其父分類為 1:1,分配頻寬為 1Mbit,優先級別為 6:

#   sudo tc class add dev enp0s5 parent 1:1 classid 1:3 cbq bandwidth 10Mbit rate 1Mbit maxburst 20 allot 1514 prio 2 avpkt 1000 cell 8 weight 100Kbit split 1:0

      該佇列的最大可用頻寬為 10Mbit,實際分配的頻寬為 1Mbit,可接收衝突的傳送最長包數目為 20 位元組;最大傳輸單元加 MAC 頭的大小為 1514 位元組,優先級別為 6,包的平均大小為 1000 位元組,包間隔傳送單元的大小為 8 位元組,相當於實際頻寬的加權速率為 100Kbit,分類的分離點為 1:0。

 

建立過濾器

      (1)應用路由分類器到 cbq 佇列的根,父分類編號為 1:0;過濾協議為 ip,優先級別為 100,過濾器為基於路由表:

# sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 100 route

      (2)建立路由對映分類 1:2 , 1:3 , 1:4

# sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 100 route to 2 flowid 1:2

# sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 100 route to 3 flowid 1:3

# sudo tc filter add dev enp0s5 parent 1:0 protocol ip prio 100 route to 4 flowid 1:4

 

建立路由

      (1)發往主機 10.211.55.20 的資料包通過分類 2 轉發(分類 2 的速率 8Mbit)

# sudo ip route add 10.211.55.20 dev enp0s5 via 10.211.55.17 realm 2

      (2)發往主機 10.211.55.21 的資料包通過分類 3 轉發(分類 3 的速率 1Mbit)

# sudo ip route add 10.211.55.21 dev enp0s5 via 10.211.55.17 realm 3

      (3)發往子網 10.211.55.0/24 的資料包通過分類 4 轉發(分類 4 的速率 1Mbit)

# sudo ip route add 10.211.55.0/24 dev enp0s5 via 10.211.55.17 realm 4