技术开发 频道

看Linux网管员如何进行网络性能优化

  发送路径上的优化

  TSO (TCP Segmentation Offload)

  TSO (TCP Segmentation Offload) 是一种利用网卡分割大数据包,减小 CPU 负荷的一种技术,也被叫做 LSO (Large segment offload) ,如果数据包的类型只能是 TCP,则被称之为 TSO,如果硬件支持 TSO 功能的话,也需要同时支持硬件的 TCP 校验计算和分散 - 聚集 (Scatter Gather) 功能。

  可以看到 TSO 的实现,需要一些基本条件,而这些其实是由软件和硬件结合起来完成的,对于硬件,具体说来,硬件能够对大的数据包进行分片,分片之后,还要能够对每个分片附着相关的头部。TSO 的支持主要有需要以下几步:

  1、如果网路适配器支持 TSO 功能,需要声明网卡的能力支持 TSO,这是通过以 NETIF_F_TSO 标志设置 net_device structure 的 features 字段来表明,例如,在 benet(drivers/net/benet/be_main.c) 网卡的驱动程序中,设置 NETIF_F_TSO 的代码如下:

  benet 网卡驱动声明支持 TSO 功能

static void be_netdev_init(struct net_device *netdev)
{
     struct be_adapter
*adapter = netdev_priv(netdev);

     netdev
->features |= NETIF_F_SG | NETIF_F_HW_VLAN_RX | NETIF_F_TSO |
         NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | NETIF_F_HW_CSUM |
         NETIF_F_GRO | NETIF_F_TSO6;

     netdev
->vlan_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_HW_CSUM;

     netdev
->flags |= IFF_MULTICAST;

     adapter
->rx_csum = true;

    
/* Default settings for Rx and Tx flow control */
     adapter
->rx_fc = true;
     adapter
->tx_fc = true;

     netif_set_gso_max_size(netdev,
65535);

     BE_SET_NETDEV_OPS(netdev,
&be_netdev_ops);

     SET_ETHTOOL_OPS(netdev,
&be_ethtool_ops);

     netif_napi_add(netdev,
&adapter->rx_eq.napi, be_poll_rx,
         BE_NAPI_WEIGHT);
     netif_napi_add(netdev,
&adapter->tx_eq.napi, be_poll_tx_mcc,
         BE_NAPI_WEIGHT);

     netif_carrier_off(netdev);
     netif_stop_queue(netdev);
}

  在代码中,同时也用 netif_set_gso_max_size 函数设置了 net_device 的 gso_max_size 字段。该字段表明网络接口一次能处理的最大 buffer 大小,一般该值为 64Kb,这意味着只要 TCP 的数据大小不超过 64Kb,就不用在内核中分片,而只需一次性的推送到网络接口,由网络接口去执行分片功能。

  2、当一个 TCP 的 socket 被创建,其中一个职责是设置该连接的能力,在网络层的 socket 的表示是 struck sock,其中有一个字段 sk_route_caps 标示该连接的能力,在 TCP 的三路握手完成之后,将基于网络接口的能力和连接来设置该字段。

  网路层对 TSO 功能支持的设定

/* This will initiate an outgoing connection. */
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
         ……

    
/* OK, now commit destination to socket.  */
     sk
->sk_gso_type = SKB_GSO_TCPV4;
     sk_setup_caps(sk,
&rt->dst);

         ……
}

  代码中的 sk_setup_caps() 函数则设置了上面所说的 sk_route_caps 字段,同时也检查了硬件是否支持分散 - 聚集功能和硬件校验计算功能。需要这 2 个功能的原因是:Buffer 可能不在一个内存页面上,所以需要分散 - 聚集功能,而分片后的每个分段需要重新计算 checksum,因此需要硬件支持校验计算。

  3、现在,一切的准备工作都已经做好了,当实际的数据需要传输时,需要使用我们设置好的 gso_max_size,我们知道,TCP 向 IP 层发送数据会考虑 mss,使得发送的 IP 包在 MTU 内,不用分片。而 TSO 设置的 gso_max_size 就影响该过程,这主要是在计算 mss_now 字段时使用。如果内核不支持 TSO 功能,mss_now 的最大值为“MTU – HLENS”,而在支持 TSO 的情况下,mss_now 的最大值为“gso_max_size -HLENS”,这样,从网络层带驱动的路径就被打通了。

  GSO (Generic Segmentation Offload)

  TSO 是使得网络协议栈能够将大块 buffer 推送至网卡,然后网卡执行分片工作,这样减轻了 CPU 的负荷,但 TSO 需要硬件来实现分片功能;而性能上的提高,主要是因为延缓分片而减轻了 CPU 的负载,因此,可以考虑将 TSO 技术一般化,因为其本质实际是延缓分片,这种技术,在 Linux 中被叫做 GSO(Generic Segmentation Offload),它比 TSO 更通用,原因在于它不需要硬件的支持分片就可使用,对于支持 TSO 功能的硬件,则先经过 GSO 功能,然后使用网卡的硬件分片能力执行分片;而对于不支持 TSO 功能的网卡,将分片的执行,放在了将数据推送的网卡的前一刻,也就是在调用驱动的 xmit 函数前。

  我们再来看看内核中数据包的分片都有可能在哪些时刻:

  1、在传输协议中,当构造 skb 用于排队的时候

  2、在传输协议中,但是使用了 NETIF_F_GSO 功能,当即将传递个网卡驱动的时候

  3、在驱动程序里,此时驱动支持 TSO 功能 ( 设置了 NETIF_F_TSO 标志 )

  对于支持 GSO 的情况,主要使用了情况 2 或者是情况 2.、3,其中情况二是在硬件不支持 TSO 的情况下,而情况 2、3 则是在硬件支持 TSO 的情况下。

  代码中是在 dev_hard_start_xmit 函数里调用 dev_gso_segment 执行分片,这样尽量推迟分片的时间以提高性能:

  GSO 中的分片

int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
             struct netdev_queue
*txq)
{
……
        
if (netif_needs_gso(dev, skb)) {
            
if (unlikely(dev_gso_segment(skb)))
                
goto out_kfree_skb;
            
if (skb->next)
                
goto gso;
         }
else {
            ……

         }

        ……

}
0
相关文章