• TCP协议之《自动阻塞CORK控制》


    当应用程序在使用write或者sendmsg系统调用连续的发送少量数据包时,内核试图将这些小包尽可能的合并在一起发送,以降低总得数据包量。得以实现的前提是,至少有一个同数据流的之前数据包正在Qdisc队列或者网络设备的队列中等待发送。以下详细解释这一点。
     
    一、初始化
    在TCP协议的初始化函数tcp_sk_init中,将网络命名空间的sysctl_tcp_autocorking设置为1,打开自动阻塞功能。可通过查看PROC文件系统的/proc/sys/net/ipv4/tcp_autocorking获得当前设置值,也可进行修改,置零关闭此功能。

    static int __net_init tcp_sk_init(struct net *net)
    {
        net->ipv4.sysctl_tcp_autocorking = 1;
    }
    $ cat /proc/sys/net/ipv4/tcp_autocorking
    1

    二、自动阻塞判断

    核心函数tcp_should_autocork,这里要进行四个判断已确定是否执行自动阻塞autocork。大致解释如下:如果当前SKB的数据还未填满(size_goal为MSS整数倍),并且Qdisc或者网络设备队列中有数据包,将不发送当前的SKB而是CORK住。原因是,Qdisc或者NIC队列中的数据包马上就要发送,当发送完成中断到来前,还可将用户随后要发送的数据合并到当前CORK住的SKB中。中断发生之后,TSQ功能负责数据包的继续发送。

    最后一个判断,意味着如果Qdisc或者NIC队列中仅有ACK报文,autocorking将不能使用,可能会在处理完成ACK报文之后,发送完成中断被延后,将造成当前数据包的延迟。套接口变量sk_wmem_alloc表示该套接口提交到网络层发送的数据总长度,由于ACK报文没有数据部分,所以理论上其在提交发送时sk_wmem_alloc并不增加,但是由于某种原因增加2。

    static bool tcp_should_autocork(struct sock *sk, struct sk_buff *skb, int size_goal)
    {
        return skb->len < size_goal &&
               sock_net(sk)->ipv4.sysctl_tcp_autocorking &&
               skb != tcp_write_queue_head(sk) &&
               refcount_read(&sk->sk_wmem_alloc) > skb->truesize;
    }

    三、判断执行流程

    内核在函数tcp_push中调用tcp_should_autocork判断,如果autocork成立,将设置TCP套接口的TSQ_THROTTLED标志。随后再次判断sk_wmem_alloc的值是否依然大于SKB的truesize,为真直接返回,否则说明发送中断已经发生了,autocork失效,进行数据的发送__tcp_push_pending_frames。

    static void tcp_push(struct sock *sk, int flags, int mss_now, int nonagle, int size_goal)
    {    
        skb = tcp_write_queue_tail(sk);
        if (!skb)
            return;
        if (!(flags & MSG_MORE) || forced_push(tp))
            tcp_mark_push(tp, skb);
        
        tcp_mark_urg(tp, flags);
     
        if (tcp_should_autocork(sk, skb, size_goal)) {           
            if (!test_bit(TSQ_THROTTLED, &sk->sk_tsq_flags)) {   /* avoid atomic op if TSQ_THROTTLED bit is already set */
                NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTOCORKING);
                set_bit(TSQ_THROTTLED, &sk->sk_tsq_flags);
            }
            /* It is possible TX completion already happened before we set TSQ_THROTTLED. */
            if (refcount_read(&sk->sk_wmem_alloc) > skb->truesize)
                return;
        }
        if (flags & MSG_MORE)
            nonagle = TCP_NAGLE_CORK;
     
        __tcp_push_pending_frames(sk, mss_now, nonagle);
    }

    TCP数据发送函数tcp_transmit_skb如下,以上的函数__tcp_push_pending_frames将调用此函数执行发送。由函数skb_is_tcp_pure_ack可知,实际上内核将纯ACK报文的truesize设置为了2,相应套接口的sk_wmem_alloc在发送纯ACK报文时增加了2。特别要注意的是SKB的销毁回调函数,如果是纯ACK报文,其为__sock_wfree,反之,非纯ACK报文,使用tcp_wfree。

    static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, gfp_t gfp_mask)
    {
        skb->destructor = skb_is_tcp_pure_ack(skb) ? __sock_wfree : tcp_wfree;
        skb_set_hash_from_sk(skb, sk);
        refcount_add(skb->truesize, &sk->sk_wmem_alloc);
    }
    static inline bool skb_is_tcp_pure_ack(const struct sk_buff *skb) 
    {               
        return skb->truesize == 2;

     

    四、发送完成中断

    当数据发送完成中断发生时,将调用destructor回调销毁SKB,调用如下的tcp_wfree函数,但是由于此时很可能获得了Qdisc锁,不能直接在此函数中发送之前autocork住的数据。内核调度TSQ的tasklet执行实际的发送。详情见TSQ的介绍:https://blog.csdn.net/sinat_20184565/article/details/89341370。由于纯ACK的销毁回调函数为__sock_wfree,所以在tcp_should_autocork判断函数中,如果Qdisc或者NIC队列中的报文为纯ACK报文,不能使能autocork。

    void tcp_wfree(struct sk_buff *skb)
    {
        WARN_ON(refcount_sub_and_test(skb->truesize - 1, &sk->sk_wmem_alloc));
     
        for (oval = READ_ONCE(sk->sk_tsq_flags);; oval = nval) {
            if (!(oval & TSQF_THROTTLED) || (oval & TSQF_QUEUED))
                goto out;
     
            nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED | TCPF_TSQ_DEFERRED;
            nval = cmpxchg(&sk->sk_tsq_flags, oval, nval);
            if (nval != oval)
                continue;
     
            local_irq_save(flags);
            tsq = this_cpu_ptr(&tsq_tasklet);
            empty = list_empty(&tsq->head);
            list_add(&tp->tsq_node, &tsq->head);
            if (empty)
                tasklet_schedule(&tsq->tasklet);
            local_irq_restore(flags);
            return;
        }
    }

  • 相关阅读:
    新王炸:文生视频Sora模型发布,能否引爆AI芯片热潮
    【数据结构:线性表——2.1 向量】
    项目管理之知识管理
    七、Thymeleaf对象的访问
    两层全连接网络反向传播梯度推导(矩阵形式、sigmoid、最小均方差MSE)
    Spring MVC ViewNameMethodReturnValueHandler原理解析
    asp.net基于net的冰淇淋订单管理系统-计算机毕业设计
    实习生必学git以及详细下载安装步骤
    Java面试问题记录
    查询
  • 原文地址:https://blog.csdn.net/wuyongmao/article/details/126246737