TCP的TSQ限值tcp_limit_output_bytes

本文探讨了TCP在发送大量数据时如何避免bufferbloat现象,介绍tcp_limit_output_bytes参数的作用及其在内核中的实现细节。通过限制Qdisc队列或设备队列中的数据量,可以有效降低内存膨胀和异常RTT时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}

 

内核版本 4.15

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值