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

}
原文地址:https://www.cnblogs.com/codestack/p/13444734.html