天天看點

delay-ack對BBR帶寬采集的影響

BBR算法帶寬時延積BDP是最為重要的一個變量,而公式BDP = Bw * min_RTT,即等于鍊路帶寬乘以最小傳輸時延,Bw和min_RTT的準确性直接影響了最終BBR算法的傳輸效果。 min_RTT的估算,是通過不斷比較記錄一個最小的rtt值,如果超過10s沒有更新過min_RTT,則進入到Probe_RTT模式中,将發送資料量減小到4個包,來探測目前的最小min_RTT值。這種方式其實也會存在很多問題,比如直播服務來說,經常會出現隔10秒鐘,碼率下降的問題,但這個不是本文讨論的重點。 BBR算法另一個需要計算的重要變量帶寬Bw,帶寬給人的直覺印象是資料包通過網絡傳輸的速度,BBR算法中的定義為: 帶寬 = delivered / interval; delivered 為時間T内傳遞到對端的資料包個數,這裡的傳遞包括對端ack确認的資料包,SACK确認的資料包以及D-SACK确認的資料包。 interval為輸出時間間隔,max(snd_interval, ack_interval),即發送時間間隔與接收時間間隔的最大值。

delay-ack對BBR帶寬采集的影響

具體用圖來描述會比較直覺,比如上圖中,可以分為三步來說。

1,tcp收到a資料包的确認,會在傳輸控制塊中記錄最近被傳遞到對端的資料包a的傳遞時間T1,發送時間為Ta,此時傳遞到對端的資料包總數為D1,。 2,收到a的确認後,又可以發送一個新的資料包b,此時會将T1,Ta,D1,以及資料包b的發送時間Tb,記錄在資料包b的skb資訊裡。 3,資料包b被确認時,此時的時間為T2,傳遞到對端的資料包個數為D2,會從資料包b的skb裡取出T1,Ta,Tb, D1,計算發送時間間隔Tb-Ta,接收時間間隔T2-T1,傳遞資料量D2-D1, 帶寬=(D2-D1) /max(T2-T1, Ta-Tb)。

看懂了上面的帶寬計算過程後,來看看delay-ack對帶寬計算的影響吧,假設傳輸過程如下圖,紅色代表原始資料包發送,一共發送了6個資料包,而灰色的代表了ack資料包,一共兩個ack,一個确認了skb1和skb2,一個确認了skb3到skb6。

delay-ack對BBR帶寬采集的影響

發送skb3,skb4,skb5,skb6的skb資訊塊的D1是相同的,即發送時傳遞到對端的資料包個數為2。而當收到第二個ack資料包時,因為這時一下确認了4個包,對于接收時間間隔是相同的,但是對于發送時間間隔是不同的,因為skb3到skb6的發送時間是不相同的,核心中選擇的是第一個資料包skb3的發送時間,在tcp_clean_rtx_queue()函數中,會調用tcp_rate_skb_delivered()來進行采樣,紅色标出的位置說明,隻有一個ack确認的所有資料包中的記錄D1不相同,才會更新rs->interval_us,也就是發送時間間隔。直覺來說T_snd < T_ack,是以計算帶寬時似乎影響不大,因為即使選擇skb6,計算出來的T_snd 也大機率是小于T_ack。

void tcp_rate_skb_delivered(struct sock *sk, struct sk_buff *skb,
			    struct rate_sample *rs)
{
	struct tcp_sock *tp = tcp_sk(sk);
	struct tcp_skb_cb *scb = TCP_SKB_CB(skb);

	if (!scb->tx.delivered_mstamp.v64)
		return;

	if (!rs->prior_delivered ||
	    after(scb->tx.delivered, rs->prior_delivered)) {
		rs->prior_delivered  = scb->tx.delivered;
		rs->prior_mstamp     = scb->tx.delivered_mstamp;
		rs->is_app_limited   = scb->tx.is_app_limited;
		rs->is_retrans	     = scb->sacked & TCPCB_RETRANS;

		/* Find the duration of the "send phase" of this window: */
		rs->interval_us      = skb_mstamp_us_delta(
						&skb->skb_mstamp,
						&scb->tx.first_tx_mstamp);//delivered 與prior_delivered 發送時間差

		/* Record send time of most recently ACKed packet: */
		tp->first_tx_mstamp  = skb->skb_mstamp;
	}
	/* Mark off the skb delivered once it's sacked to avoid being
	 * used again when it's cumulatively acked. For acked packets
	 * we don't need to reset since it'll be freed soon.
	 */
	if (scb->sacked & TCPCB_SACKED_ACKED)
		scb->tx.delivered_mstamp.v64 = 0;
}
           

但是上圖中第一ack确認也是delay_ack,也是skb資訊塊中記錄相同的傳遞到對端的數量0。是以第一個ack進行中記錄了最近被傳遞到對端的資料包的發送時間是skb1的發送時間,并不是skb2的發送時間。是以得出了下圖,可以看到這時候T_snd似乎更大了,假如前一個ack累計确認了100個包,第二ack就确認了兩個資料包,那很有可能第二個計算出來的帶寬值偏小了。當然,這種誤差隻存在連續的ack都是累計确認了好多個資料包的情況下,并且Bw的估算是是取十輪中的最大值,這種情形的下影響确實比較小。

delay-ack對BBR帶寬采集的影響

BBR作者也提供了一個patch,但是還未并入到master中,可以看到當記錄tx.delivered相同時,是會更新使用最晚發送的資料包的發送時間,上圖中就是skb2和skb6的對應的發送時間。

diff --git a/net/ipv4/tcp_rate.c b/net/ipv4/tcp_rate.c
index ed02e11ed537d..7be4979c607ad 100644
--- a/net/ipv4/tcp_rate.c
+++ b/net/ipv4/tcp_rate.c
@@ -84,7 +84,9 @@ void tcp_rate_skb_delivered(struct sock *sk, struct sk_buff *skb,
 		return;
 
 	if (!rs->prior_delivered ||
-	    after(scb->tx.delivered, rs->prior_delivered)) {
+	    after(scb->tx.delivered, rs->prior_delivered) ||
+	    (scb->tx.delivered == rs->prior_delivered &&
+	     skb->skb_mstamp > tp->first_tx_mstamp)) {
 		rs->prior_delivered_ce  = scb->tx.delivered_ce;
 		rs->prior_delivered  = scb->tx.delivered;
 		rs->prior_mstamp     = scb->tx.delivered_mstamp;
--