• TSO GSO


    以前有过一篇TSO-GSO文章;目前再来回顾一下:

     TSO与GSO的重要区别
    1, TSO只有第一个分片有TCP头和IP头,接着的分段只有IP头。意味着第一个分段丢失,所有分段得重传。
    2, GSO在分段时会调用TCP或UDP的回调函数(udp4_ufo_fragment)为每个分段都加上IP头,由于分段是通过mss设置的(mss由发送端设置),所以长度仍然可能超过mtu值,所以在IP层还得再分片(代码位于dev_hard_start_xmit)。

    收数据

      LRO(Large Receive Offload),TSO是发,LRO是收。将多个TCP分段聚合成一个skb结构,以减小上层协议栈的skb的开销。skb的数据保存在skb->data中,分段的数据保存在skb_shared_info->frag_list中。
      GRO(Generic Receive Offloading),GSO是发,GRO是收。LRO使用发送方和目的地IP地址,IP封包ID,L4协议三者来区分段,对于从同一个SNAT域的两个机器发向同一目的IP的两个封包可能会存在相同的IP封包ID(因为不是全局分配的ID),这样会有所谓的卷绕的bug。GRO采用发送方和目的地IP地址,源/目的端口,L4协议三者来区分作为改进。所以对于后续的驱动都应该使用GRO的接口,而不是LRO。另外,GRO也支持多协议。

    • 1, 物理网卡不支持GRO时, 使用LRO在驱动处合并了多个skb一次性通过网络栈,对CPU负荷的减轻是显然的。
    • 2, 物理网卡不支持LRO时,使用GRO在从驱动接收数据那一刻合并了多个skb一次性通过网络栈,对CPU负荷的减轻是显然的

    关于GRO的相关分析见此篇文章

    ip_queue_xmit--->ip_output--->ip_finish_output--->dev_queue_xmit

    继续IP层的GSO分片

    static int ip_finish_output_gso(struct net *net, struct sock *sk,
                    struct sk_buff *skb, unsigned int mtu)
    {
        netdev_features_t features;
        struct sk_buff *segs;
        int ret = 0;
    
        /* common case: locally created skb or seglen is <= mtu */
        //只有ip forward流程该条件才会不成立,否则该条件成  也就是本地发包是会直接走 ip_finish_output2 发包;
        if (((IPCB(skb)->flags & IPSKB_FORWARDED) == 0) ||
              skb_gso_network_seglen(skb) <= mtu)
            return ip_finish_output2(net, sk, skb);
    
        /* Slowpath -  GSO segment length is exceeding the dst MTU.
         *
         * This can happen in two cases:
         * 1) TCP GRO packet, DF bit not set
         * 2) skb arrived via virtio-net, we thus get TSO/GSO skbs directly
         * from host network stack.
         */
        features = netif_skb_features(skb);
        BUILD_BUG_ON(sizeof(*IPCB(skb)) > SKB_SGO_CB_OFFSET);
        //调用回调函数, 对于TCP则是调用TCP的gso_segment回调函数进行TCP GSO分段 inet_gso_segment--->tcp4_gso_segment
        segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);//skb gso报文分段 ------>此处可以参考GRO的实现
        if (IS_ERR_OR_NULL(segs)) {
            kfree_skb(skb);
            return -ENOMEM;
        }
    
        consume_skb(skb);
    
        do {
            struct sk_buff *nskb = segs->next;
            int err;
    
            segs->next = NULL;
            //分段报文经过ip分片后通过ip_finish_output2发送
            err = ip_fragment(net, sk, segs, mtu, ip_finish_output2);
    
            if (err && ret == 0)
                ret = err;
            segs = nskb;
        } while (segs);
    
        return ret;
    }

       IP层发包后进入二层发包!!

    二层发包GSO

      IP层发包调用__dev_queue_xmit--->sch_direct_xmit--->validate_xmit_skb_list()  检测报文是否需要进行GSO 等分段:

    int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
                struct netdev_queue *txq)
    {
    ///...
            if (netif_needs_gso(skb, features)) {
                if (unlikely(dev_gso_segment(skb, features))) ///GSO(software offload)
                    goto out_kfree_skb;
    
    -------------------------------------
    }
    int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
                struct net_device *dev, struct netdev_queue *txq,
                spinlock_t *root_lock, bool validate)
    {
        int ret = NETDEV_TX_BUSY;
    ---------------------------------------
        /* Note that we validate skb (GSO, checksum, ...) outside of locks */
        if (validate)
            skb = validate_xmit_skb_list(skb, dev);
    -----------------------------------
    }
    static inline bool skb_gso_ok(struct sk_buff *skb, netdev_features_t features)
    {
        return net_gso_ok(features, skb_shinfo(skb)->gso_type) &&
               (!skb_has_frag_list(skb) || (features & NETIF_F_FRAGLIST));
        // //feature包含gso_type 并且skb没有frag_list(---->有frag_list 表示不支持SG)或者feature包含NETIF_F_FRAGLIST(表示SG Scatter/gather IO. )
    }
    
    
    static inline bool netif_needs_gso(struct sk_buff *skb,
                       netdev_features_t features)
    {
        //判断是否需要进行GSO分段,主要 两个参数features(硬件支持的特性)和skb报文。
        //主要判断条件是features是否包含skb->gso_type。那么我们来看下features是怎么得到的,
        //其实是通过netif_skb_features得到的。
    
    //skb 为gso报文   && (gso 没有准备好---且feature不包含skb->gso_type)
    //skb 为gso报文,且skb_ipsummed不为CHECKSUM_PARTIAL和CHECKSUM_UNNECESSARY
        return skb_is_gso(skb) && (!skb_gso_ok(skb, features) ||
            unlikely((skb->ip_summed != CHECKSUM_PARTIAL) &&
                 (skb->ip_summed != CHECKSUM_UNNECESSARY)));
    }

    对于GSO相关逻辑可以参考:mac-ip-tcp-GSO

    /* This data is invariant across clones and lives at
     * the end of the header data, ie. at skb->end.
     */
    struct skb_shared_info {
        unsigned char    nr_frags;
        __u8        tx_flags;
        unsigned short    gso_size;//生成GSO段时的MSS,因为GSO段的长度是与发送段的套接口中合适MSS的倍数
        /* Warning: this field is not always filled in (UFO)! */
        unsigned short    gso_segs;//GSo的长度是gso_size的倍数,即用gso_size 来分割大段时产生的段数
        unsigned short  gso_type;// skb 中数据支持的GSO类型
        struct sk_buff    *frag_list;
        struct skb_shared_hwtstamps hwtstamps;
        u32        tskey;
        __be32          ip6_frag_id;
    
        /*
         * Warning : all fields before dataref are cleared in __alloc_skb()
         */
        atomic_t    dataref;
    
        /* Intermediate layers must ensure that destructor_arg
         * remains valid until skb destructor */
        void *        destructor_arg;
    
        /* must be last field, see pskb_expand_head() */
        skb_frag_t    frags[MAX_SKB_FRAGS];
    };

  • 相关阅读:
    Exsi上Windows主机增加硬盘容量
    第6章:vuerouter,vuecli和单文件组件
    Python之爬取天气预报并生成图表
    第3章:vue生命周期及实例的属性
    Python 获得NOAA全球开放气象数据
    第5章:组件即组件间的通信
    MACBOOK M1 PRO 下运行.NET CORE(MAC下如何与X86_64兼容)
    DOTNET 运行AESGCM程序 ON MACOS(错误ALGORITHM ‘AESGCM’ IS NOT SUPPORTED ON THIS PLATFORM)
    1.3\~1.4 控制措施类型、安全框架
    mac 安装brew带来的种种问题
  • 原文地址:https://www.cnblogs.com/codestack/p/16179673.html
Copyright © 2020-2023  润新知