TCP-Hybla擁塞算法

TCP Hybla算法的改進是對於RTT較長的連接(例如衛星和無線網絡)可獲得與參考TCP連接(如,有線網絡)相同的瞬時發送速率B(t)。TCP的發送速率計算如下(W(t)表示t時刻的發送窗口值):

B(t)=W(t)/RTT(1) \tag{1} B(t)=W(t)/RTT

對於傳統TCP,發送窗口計算如下:

W(t)={2t/RTT if 0t<tγ,SSttγRTT+γ if t>tγ,CA(2) \tag{2} W(t)=\begin{cases} 2^{t/RTT} & \text{ if } 0\leq t< t_{\gamma }, SS \\ \frac{t-t_{\gamma } }{RTT}+\gamma & \text{ if } t> t_{\gamma }, CA \end{cases}

可見,發送窗口W(t)和發送速率B(t)都是與RTT成比例的,如果RTT較長的連接要達到與參考連接相同的效果,需要滿足兩個條件,第一,根據以上公式2,窗口W(t)要擺脫與RTT的關係;第二,根據公式1,要達到相同的發送速率B(t),需要對較長的RTT連接進行補償,消除RTT的影響。對於TCP Hybla算法,其引入了變量ρ,其值定義如下:

ρ=RTT/RTT0(3) \tag{3} \rho = RTT/RTT_{0}

其中RTT0爲參考連接的RTT值,爲了使Hybla連接達到與參考連接相同的性能,將窗口增長函數(公式2)修改如下。首先,將窗口增長時間乘以rho,對於SlowStart階段,時間爲t; 對於擁塞避免階段,時長爲t-tγ,其中tγ爲窗口達到ssthresh時的時間值,γ爲ssthresh。這樣,窗口值W(t)將取決於選定的RTT0,不再依賴於RTT時長。再一步,繼續將窗口值W(t)乘以rho,這樣,在計算髮送速率B(t)時,消除了RTT的影響。

WH(t)={ρ2ρt/RTT if 0t<tγ,SSρ[ρttγRTT+γ] if t>tγ,CA(4) \tag{4} W^{H}(t)=\begin{cases} \rho 2^{\rho t/RTT} & \text{ if } 0\leq t< t_{\gamma },SS \\ \rho \left [ \rho \frac{t-t_{\gamma }}{RTT}+\gamma \right ] & \text{ if } t> t_{\gamma },CA \end{cases}

根據第二步的修改,原本的慢啓動閾值γ,將變爲ργ,對於所有的連接,不論RTT的值爲多少,到達閾值γ的時間都相同:

tγ=RTT0log2γ(5) \tag{5}t_{\gamma }=RTT_{0}\log_2\gamma

由公式4,可得到Hybla的報文發送速率如下,可見其與實際的RTT沒有關聯。

BH(t)={2t/RTT0RTT0 if 0t<tγ,SS1RTT0[ttγRTT0+γ] if t>tγ,CA(6) \tag{6} B^{H}(t)=\begin{cases} \frac{2^{t/RTT_{0}}}{RTT_{0}} & \text{ if } 0\leq t< t_{\gamma },SS \\ \frac{1}{RTT_{0}}\left [ \frac{t-t_{\gamma }}{RTT_{0}}+\gamma \right ] & \text{ if } t> t_{\gamma },CA \end{cases}

將以上公式4,轉換爲標準TCP的實現,即每個ACK報文對應的窗口增長值,得到以下公式:

Wi+1H={WiH+2ρ1 SS WiH+ρ2/WiH CA (7) \tag{7} W_{i+1}^{H}=\begin{cases} W_{i}^{H}+2^{\rho }-1 & \text{ SS } \\ W_{i}^{H}+ \rho^{2} / W_{i}^{H} & \text{ CA } \end{cases}

TCP-Hybla將rho的最小值定義爲1,此時,窗口增長值與標準TCP相同。

Hybla預設值

內核中將參考RTT時長(rtt0),默認設置爲25毫秒,可通過模塊參數在加載時進行修改。

/* Hybla reference round trip time (default= 1/40 sec = 25 ms), in ms */
static int rtt0 = 25;
module_param(rtt0, int, 0644);
MODULE_PARM_DESC(rtt0, "reference rout trip time (ms)");

rho值計算

由於srtt_us中保存的爲真實的SRTT值左移3位的值,所以,rho_3ls中保存的rho值也爲真實的RHO值左移3位的結果,rho_3ls的值最小爲8,即真實的rho值最小爲1(8 >> 3)。除了計算出rho值爲,如下也計算了rho值的平方值。

/* This is called to refresh values for hybla parameters */
static inline void hybla_recalc_param (struct sock *sk)
{
    struct hybla *ca = inet_csk_ca(sk);

    ca->rho_3ls = max_t(u32,
                tcp_sk(sk)->srtt_us / (rtt0 * USEC_PER_MSEC),
                8U);
    ca->rho = ca->rho_3ls >> 3;
    ca->rho2_7ls = (ca->rho_3ls * ca->rho_3ls) << 1;
    ca->rho2 = ca->rho2_7ls >> 7;
}

Hybla窗口增長

函數hybla_cong_avoid在ACK報文處理流程中被調用,首先,如果當前的SRTT(Smoothed RTT)小於Hybla記錄的最小RTT值,重新計算rho,並且更新Hybla的最小RTT值。之後,判斷當前套接口的發送是否受到擁塞窗口的限制,如果沒有,不進行窗口調整。其次,如果Hybla沒有使能,調用TCP-Reno算法處理窗口增長。

/* TCP Hybla main routine.
 * This is the algorithm behavior:
 *     o Recalc Hybla parameters if min_rtt has changed
 *     o Give cwnd a new value based on the model proposed
 *     o remember increments <1
 */
static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked)
{
    struct tcp_sock *tp = tcp_sk(sk);
    struct hybla *ca = inet_csk_ca(sk);
    u32 increment, odd, rho_fractions;
    int is_slowstart = 0;

    /*  Recalculate rho only if this srtt is the lowest */
    if (tp->srtt_us < ca->minrtt_us) {
        hybla_recalc_param(sk);
        ca->minrtt_us = tp->srtt_us;
    }

    if (!tcp_is_cwnd_limited(sk))
        return;

    if (!ca->hybla_en) {
        tcp_reno_cong_avoid(sk, ack, acked);
        return;
    }

如果rho值爲零,重新計算其值,參見函數hybla_recalc_param的介紹。

    if (ca->rho == 0)
        hybla_recalc_param(sk);

按照以上公式(),在慢啓動階段,窗口的增長值爲2^RHO - 1,以下計算將RHO拆分爲兩個部分:整數部分和分數部分。其中整數部分爲rho,在計算窗口增長值時,這部分通過左移計算。對於分數部分rho_fractions(左移3位後的值),由函數hybla_fraction計算冪值。

注意一下計算的increment爲真實增長值左移7位的結果,計算公式2^RHO - 1最後的減1,變爲減128(1 << 7);

    rho_fractions = ca->rho_3ls - (ca->rho << 3);

    if (tcp_in_slow_start(tp)) {
        /*
         * slow start
         *      INC = 2^RHO - 1
         * This is done by splitting the rho parameter
         * into 2 parts: an integer part and a fraction part.
         * Inrement<<7 is estimated by doing:
         *         [2^(int+fract)]<<7
         * that is equal to:
         *         (2^int)  *  [(2^fract) <<7]
         * 2^int is straightly computed as 1<<int,
         * while we will use hybla_slowstart_fraction_increment() to
         * calculate 2^fract in a <<7 value.
         */
        is_slowstart = 1;
        increment = ((1 << min(ca->rho, 16U)) *
            hybla_fraction(rho_fractions)) - 128;
    } else {

對於擁塞避免階段,窗口增加值的計算公式爲RHO^2 / W,使用變量rho2_7ls除以snd_cwnd,得到的即爲左移7位的窗口增長值increment,如果得到的值小於128,即小於1(128 >> 7),將snd_cwnd_cnt遞增一。

        /*
         * congestion avoidance
         * INC = RHO^2 / W
         * as long as increment is estimated as (rho<<7)/window
         * it already is <<7 and we can easily count its fractions.
         */
        increment = ca->rho2_7ls / tp->snd_cwnd;
        if (increment < 128)
            tp->snd_cwnd_cnt++;
    }

將擁塞窗口snd_cwnd加上以上計算的增加值(increment >> 7),對於增加值中小於1的部分,將其增加到變量snd_cwnd_cents中,當其中的值大於1時,將擁塞窗口snd_cwnd增加一。

    odd = increment % 128;
    tp->snd_cwnd += increment >> 7;
    ca->snd_cwnd_cents += odd;

    /* check when fractions goes >=128 and increase cwnd by 1. */
    while (ca->snd_cwnd_cents >= 128) {
        tp->snd_cwnd++;
        ca->snd_cwnd_cents -= 128;
        tp->snd_cwnd_cnt = 0;
    }

最後,如果以上操作沒有增加snd_cwnd窗口值,但是,snd_cwnd_cnt計數已經大於等於snd_cwnd,將擁塞窗口遞增一。確保慢啓動階段的窗口值小於閾值ssthresh,以及窗口值不能大於窗口鉗制值。

    /* check when cwnd has not been incremented for a while */
    if (increment == 0 && odd == 0 && tp->snd_cwnd_cnt >= tp->snd_cwnd) {
        tp->snd_cwnd++;
        tp->snd_cwnd_cnt = 0;
    }
    /* clamp down slowstart cwnd to ssthresh value. */
    if (is_slowstart)
        tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh);

    tp->snd_cwnd = min_t(u32, tp->snd_cwnd, tp->snd_cwnd_clamp);
}

如下函數hybla_fraction,由於odds值的計算爲:(ca->rho_3ls - (ca->rho << 3)),可見其僅佔用了3位,odds的值最大爲7。根據公式(2^fract <<7)計算,其中fract爲[0, 0.1, … 0.7],得到的結果爲:[128, 137, 147, 158, 169, 181, 194, 208],與以下函數中的值不相同,暫不知何故?

static inline u32 hybla_fraction(u32 odds)
{
    static const u32 fractions[] = {
        128, 139, 152, 165, 181, 197, 215, 234,
    };

    return (odds < ARRAY_SIZE(fractions)) ? fractions[odds] : 128;
}

Hybla擁塞狀態

如下函數hybla_state,僅在TCP_CA_Open狀態開啓Hybla算法。

static void hybla_state(struct sock *sk, u8 ca_state)
{
    struct hybla *ca = inet_csk_ca(sk);

    ca->hybla_en = (ca_state == TCP_CA_Open);
}

內核版本 5.0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章