• GSO和TSO


    http://www.cnhalo.net/2016/09/13/linux-tcp-gso-tso/

    TSO(TCP Segmentation Offload):

      是一种利用网卡来对大数据包进行自动分段,降低CPU负载的技术。 其主要是延迟分段
    GSO(Generic Segmentation Offload):

      GSO是协议栈是否推迟分段,在发送到网卡之前判断网卡是否支持TSO,如果网卡支持TSO则让网卡分段,否则协议栈分完段再交给驱动。 如果TSO开启,GSO会自动开启

    • GSO开启, TSO开启: 协议栈推迟分段,并直接传递大数据包到网卡,让网卡自动分段
    • GSO开启, TSO关闭: 协议栈推迟分段,在最后发送到网卡前才执行分段
    • GSO关闭, TSO开启: 同GSO开启, TSO开启
    • GSO关闭, TSO关闭: 不推迟分段,在tcp_sendmsg中直接发送MSS大小的数据包

    驱动程序在注册网卡设备的时候默认开启GSO: NETIF_F_GSO

    驱动程序会根据网卡硬件是否支持来设置TSO: NETIF_F_TSO

    可以通过ethtool -K来开关GSO/TSO

    #define NETIF_F_SOFT_FEATURES    (NETIF_F_GSO | NETIF_F_GRO)
    int register_netdevice(struct net_device *dev)
    {
        -----------------------------------------------
    
        /* Transfer changeable features to wanted_features and enable
         * software offloads (GSO and GRO).
         */
        dev->hw_features |= NETIF_F_SOFT_FEATURES;
        dev->features |= NETIF_F_SOFT_FEATURES;//默认开启GRO/GSO
        dev->wanted_features = dev->features & dev->hw_features;
    
        if (!(dev->flags & IFF_LOOPBACK)) {
            dev->hw_features |= NETIF_F_NOCACHE_COPY;
        }
    
        /* Make NETIF_F_HIGHDMA inheritable to VLAN devices.
         */
        dev->vlan_features |= NETIF_F_HIGHDMA;
    
        /* Make NETIF_F_SG inheritable to tunnel devices.
         */
        dev->hw_enc_features |= NETIF_F_SG;
    
        /* Make NETIF_F_SG inheritable to MPLS.
         */
        dev->mpls_features |= NETIF_F_SG;

    GSO/TSO是否开启是保存在dev->features中,而设备和路由关联,当我们查询到路由后就可以把配置保存在sock中
    比如在tcp_v4_connect和tcp_v4_syn_recv_sock都会调用sk_setup_caps来设置GSO/TSO配置

    /* This will initiate an outgoing connection. */
    int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
    {
        --------------------------------
        orig_sport = inet->inet_sport;
        orig_dport = usin->sin_port;
        fl4 = &inet->cork.fl.u.ip4;
        rt = ip_route_connect(fl4, nexthop, inet->inet_saddr,
                      RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
                      IPPROTO_TCP,
                      orig_sport, orig_dport, sk);
        --------------------------------------
    //调用sk_setup_caps来设置GSO/TSO配置
        /* OK, now commit destination to socket.  */
        sk->sk_gso_type = SKB_GSO_TCPV4;
        sk_setup_caps(sk, &rt->dst);
    
        -----------------------------------------
    
        err = tcp_connect(sk);
    
        rt = NULL;
        if (err)
    void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
    {
        /* List of features with software fallbacks. */
    #define NETIF_F_GSO_SOFTWARE    (NETIF_F_TSO | NETIF_F_TSO_ECN | 
                         NETIF_F_TSO6 | NETIF_F_UFO)
        u32 max_segs = 1;
    
        sk_dst_set(sk, dst);
        sk->sk_route_caps = dst->dev->features;
        if (sk->sk_route_caps & NETIF_F_GSO)//软件GSO,默认开启
            sk->sk_route_caps |= NETIF_F_GSO_SOFTWARE;//开启延时gso延时选项,包括NETIF_F_TSO
        sk->sk_route_caps &= ~sk->sk_route_nocaps;
        if (sk_can_gso(sk)) {
            if (dst->header_len) {
                sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
            } else {
                sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM;// 开启gso后,设置sg和校验
                sk->sk_gso_max_size = dst->dev->gso_max_size;//GSO_MAX_SIZE=65536
                max_segs = max_t(u32, dst->dev->gso_max_segs, 1);
            }
        }
        sk->sk_gso_max_segs = max_segs;
    }
    //判断GSO或TSO是否开启
    static inline bool sk_can_gso(const struct sock *sk)
    {
        return net_gso_ok(sk->sk_route_caps, sk->sk_gso_type);
    }
    static inline bool net_gso_ok(netdev_features_t features, int gso_type)
    {
        netdev_features_t feature = gso_type << NETIF_F_GSO_SHIFT;
        //对于tcp4, 判断NETIF_F_TSO是否被设置, 即使硬件不支持TSO,开启GSO的情况下也会被设置
    
        /* check flags correspondence */
        BUILD_BUG_ON(SKB_GSO_TCPV4   != (NETIF_F_TSO >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_UDP     != (NETIF_F_UFO >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_DODGY   != (NETIF_F_GSO_ROBUST >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_TCP_ECN != (NETIF_F_TSO_ECN >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_TCPV6   != (NETIF_F_TSO6 >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_FCOE    != (NETIF_F_FSO >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_GRE     != (NETIF_F_GSO_GRE >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_GRE_CSUM != (NETIF_F_GSO_GRE_CSUM >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_IPIP    != (NETIF_F_GSO_IPIP >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_SIT     != (NETIF_F_GSO_SIT >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL != (NETIF_F_GSO_UDP_TUNNEL >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL_CSUM != (NETIF_F_GSO_UDP_TUNNEL_CSUM >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_TUNNEL_REMCSUM != (NETIF_F_GSO_TUNNEL_REMCSUM >> NETIF_F_GSO_SHIFT));
    
        return (features & feature) == feature;
    }

    对紧急数据包或GSO/TSO都不开启的情况,才不会推迟发送, 默认使用当前MSS
    开启GSO后,tcp_send_mss返回mss和单个skb的GSO大小,为mss的整数倍

    int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
    {
    ------------------------------------------------------------
        /* This should be in poll */
        sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
    
        mss_now = tcp_send_mss(sk, &size_goal, flags);
    
    }
  • 相关阅读:
    分析Ajax并爬取微博列表
    链表01
    Install ping command from ubuntu docker
    In container,I can't use man
    centos install glances
    centos 服务器命令下安装GUI
    firefox浏览器配置
    xshell替代产品
    Linux下将一个文件压缩分包成多个小文件
    Linux生成大文件
  • 原文地址:https://www.cnblogs.com/codestack/p/13444734.html
Copyright © 2020-2023  润新知