当前位置:网站首页>TCP协议之《TSQ限值tcp_limit_output_bytes》

TCP协议之《TSQ限值tcp_limit_output_bytes》

2022-08-10 03:26:00 程序员扫地僧

TCP在发送大量的数据时,倾向于尽可能多的进行发送直到检测到丢包。这将导致发送设备或者Qdisc流控队列(例如pfifo_fast)中填充大量数据包,势必增大其它流量的延迟时间。tcp_limit_output_bytes参数限制了Qdisc队列或者设备队列中的数据量,以缓解此状况,达到减低队列的内存膨胀bufferbloat和由其所导致的异常RTT时间的作用。

默认值为262144。参见PROC文件tcp_limit_output_bytes的值,以及在初始化函数tcp_sk_init中的赋值。

$ cat /proc/sys/net/ipv4/tcp_limit_output_bytes
262144
$
static struct ctl_table ipv4_net_table[] = {
    {
        .procname   = "tcp_limit_output_bytes",
        .data       = &init_net.ipv4.sysctl_tcp_limit_output_bytes,
        .maxlen     = sizeof(int),
        .mode       = 0644,
        .proc_handler   = proc_dointvec
    },
}
static int __net_init tcp_sk_init(struct net *net)
{
    /* Default TSQ limit of four TSO segments */
    net->ipv4.sysctl_tcp_limit_output_bytes = 262144;
}

当前发送的数据报文skb结构所占用空间的两倍(2*skb->truesize),以及当前大约每毫秒的流量值,其通过sk_pacing_rate计算而来,内核将sk_pacing_shift变量定义为10,将当前每秒钟的流量(sk_pacing_rate)除以2的10次方,得到大约1毫秒的流量值。取两者之间的较大值,但是如果此值大约tcp_limit_output_bytes限定的值,使用tcp_limit_output_bytes作为判定值。之后,如果是重传报文,即factor等于1,将最终的判定值增大一倍。

如果sk_wmem_alloc大于limit说明以及发送了太多的数据在Qdisc或者设备队列,但是如果重传队列为空,此次发送还是允许进行。否则,返回true,禁止发送操作。

static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, unsigned int factor)
{
    unsigned int limit;
 
    limit = max(2 * skb->truesize, sk->sk_pacing_rate >> sk->sk_pacing_shift);
    limit = min_t(u32, limit, sock_net(sk)->ipv4.sysctl_tcp_limit_output_bytes);
    limit <<= factor;
 
    if (refcount_read(&sk->sk_wmem_alloc) > limit) {
        /* Always send skb if rtx queue is empty. No need to wait for TX completion to call us back,
         * after softirq/tasklet schedule. This helps when TX completions are delayed too much.
         */
        if (tcp_rtx_queue_empty(sk))
            return false;
 
        set_bit(TSQ_THROTTLED, &sk->sk_tsq_flags);
        /* It is possible TX completion already happened before we set TSQ_THROTTLED, so we must test again the condition. */
        smp_mb__after_atomic();
        if (refcount_read(&sk->sk_wmem_alloc) > limit)
            return true;
    }
    return false;
}
void sock_init_data(struct socket *sock, struct sock *sk)
{
    sk->sk_max_pacing_rate = ~0U;
    sk->sk_pacing_rate = ~0U;
    sk->sk_pacing_shift = 10;
}

在TCP发送函数tcp_write_xmit和重传函数tcp_xmit_retransmit_queue中,调用检查队列大小的函数tcp_small_queue_check。发送函数传入的第三个factor参数为0,重传函数的factor传入1,即重传时设备或者Qdisc队列容量增大一倍。

static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int push_one, gfp_t gfp)
{
    while ((skb = tcp_send_head(sk))) {
        if (tcp_small_queue_check(sk, skb, 0))
            break;
        if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
            break;
}
void tcp_xmit_retransmit_queue(struct sock *sk)
{
        if (tcp_small_queue_check(sk, skb, 1))
            return;
 
        if (tcp_retransmit_skb(sk, skb, segs))
            return;
}

原网站

版权声明
本文为[程序员扫地僧]所创,转载请带上原文链接,感谢
https://blog.csdn.net/wuyongmao/article/details/126246980