4. TSO(TCP Segmentation Offload)原理:大包分割机制、驱动与硬件协作流程

4.1 TSO 概述与动机

TSO(TCP Segmentation Offload)是一种将 TCP 数据段的分割工作从内核协议栈卸载到网卡硬件执行的优化技术。在标准 TCP 协议栈中,当应用层通过 send() 发送大块数据时,TCP 层需要根据 MSS(Maximum Segment Size)将数据切分为多个段,每个段都要经过 IP 层、邻居子系统、驱动层,最终通过 DMA 送入网卡。这个过程会消耗大量 CPU 周期,尤其是在 10Gbps 及以上高速网络环境下,CPU 会成为瓶颈。

TSO 的核心思想是:让内核协议栈直接构造一个超大的 TCP 报文(称为 Super Packet 或 TSO 报文),其长度可达 64KB 甚至更大,然后一次性交给网卡驱动;网卡硬件负责按照 MSS 将其分割为多个合法的 TCP 段,并完成校验和计算、序列号更新等操作。

关键指标: 启用 TSO 后,单次报文发送的 CPU 开销可降低 40%~60%,吞吐量提升 30%~80%(取决于报文大小和链路速率)。对于 10Gbps 网络,TSO 几乎是必备功能。

4.2 大包分割机制:从应用层到硬件

TSO 的完整数据流分为三个阶段:协议栈构造大包、驱动提交大包、硬件分割发送。下图展示了 TSO 报文在协议栈与硬件之间的流转路径:

TSO 大包分割与硬件协作流程 TSO 大包分割与硬件协作流程 应用层 send() TCP 层构造 Super Packet (长度可达 64KB) IP 层(无分片) 驱动层 网卡硬件分割引擎 Super Packet(64KB) 段1 (MSS) 段2 (MSS) 段3 (MSS) …… 直到所有数据分割完成 内核协议栈(无分割) • 不执行 MSS 分割 • 不计算校验和 • 不更新序列号 • 只构造 skb 大包 • 设置 gso_size 标志 skb_shinfo(skb)->gso_size = MSS

4.3 驱动与硬件协作流程

TSO 的实现依赖于内核、驱动与网卡硬件之间的紧密协作。以下是标准流程的详细步骤:

步骤 组件 操作描述 关键数据结构/寄存器
1 TCP 层 检查 skb 是否满足 TSO 条件(长度 > MSS,且无紧急指针等特殊标志) skb->len、tcp_skb_cb
2 IP 层 设置 IP 头总长度为整个大包长度,不分片标志置位 iph->tot_len、IP_DF
3 邻居子系统 将 skb 传递给驱动层的 ndo_start_xmit 回调 skb->dev、net_device_ops
4 驱动层 检查 skb_shinfo(skb)->gso_type 是否为 SKB_GSO_TCPV4 或 SKB_GSO_TCPV6 skb_shinfo()、gso_type
5 驱动层 将 skb 映射到 DMA 地址,填充发送描述符(Tx Descriptor)的 TSO 相关字段 tx_desc->tcp_seg_len、tx_desc->mss
6 网卡硬件 从描述符中读取 MSS 值,将大包分割为多个 TCP 段,更新每个段的序列号、校验和 硬件分割引擎、TSO Context Descriptor
7 网卡硬件 将分割后的段依次发送到物理链路,并更新发送完成状态 Tx 完成队列、tx_clean 中断

4.4 关键数据结构:skb_shinfo 与 gso_size

TSO 的核心元数据存储在 skb 的共享信息结构体 skb_shared_info 中。驱动通过读取这些字段来指导硬件分割:

struct skb_shared_info {
    __u8            nr_frags;           // 碎片数量
    __u8            tx_flags;           // 发送标志
    unsigned short   gso_size;          // TSO 分割大小(即 MSS 值)
    unsigned short   gso_segs;          // 分割后的段数(硬件填充)
    struct sk_buff   *frag_list;        // 碎片链表
    skb_frag_t      frags[MAX_SKB_FRAGS]; // 数据碎片数组
};
驱动检查逻辑示例(伪代码):
static netdev_tx_t my_start_xmit(struct sk_buff *skb, struct net_device *dev) {
    struct skb_shared_info *shinfo = skb_shinfo(skb);
    
    if (shinfo->gso_size && (shinfo->gso_type & SKB_GSO_TCPV4)) {
        // 设置 TSO 上下文描述符
        tx_desc->tso_enable = 1;
        tx_desc->mss = shinfo->gso_size;
        tx_desc->tcp_hdr_len = tcp_hdrlen(skb);
        tx_desc->ip_hdr_len = ip_hdrlen(skb);
    } else {
        // 普通发送路径
        tx_desc->tso_enable = 0;
    }
    
    // 映射 DMA 并触发发送
    dma_map_single(dev->dev.parent, skb->data, skb->len, DMA_TO_DEVICE);
    writel(tx_desc, dev->base_addr + TX_REG);
    return NETDEV_TX_OK;
}

4.5 硬件分割引擎的工作细节

网卡硬件内部的 TSO 分割引擎通常包含以下逻辑单元:

网卡 TSO 硬件分割引擎内部结构 网卡 TSO 硬件分割引擎内部结构 DMA 引擎 从主机内存读取 Super Packet 协议头解析器 提取 IP/TCP 头偏移 分割引擎核心 按 MSS 切分数据载荷 更新 TCP 序列号 校验 和 发送完成队列(Tx Completion Queue) 每个分割段对应一个完成状态描述符 MSS 值来自描述符 gso_size 字段

4.6 TSO 与 GSO 的关系

GSO(Generic Segmentation Offload)是 TSO 的软件回退方案。当网卡不支持 TSO 硬件卸载时,GSO 在协议栈内部执行分割,但分割时机推迟到 dev_hard_start_xmit 之前,而非 TCP 层。两者的对比:

特性 TSO(硬件卸载) GSO(软件回退)
分割执行者 网卡硬件 内核协议栈(net/core/dev.c)
CPU 开销 极低(仅构造大包) 中等(需要软件分割)
适用场景 支持 TSO 的网卡(如 Intel XL710、Mellanox ConnectX-5) 不支持 TSO 的网卡或虚拟设备(如 bridge、tun/tap)
配置方式 ethtool -K eth0 tso on ethtool -K eth0 gso on
报文大小限制 受硬件描述符限制(通常 64KB) 受 skb->len 和 GSO_MAX_SIZE 限制
注意事项: 在虚拟化环境中(如 KVM 虚拟机),如果宿主机启用了 TSO,但虚拟网卡(virtio-net)不支持硬件分割,则必须依赖 GSO 在宿主机或虚拟机内部完成分割。此时建议同时启用 TSO 和 GSO,确保兼容性。

4.7 TSO 的配置与验证

通过 ethtool 工具可以查看和修改 TSO 状态:

# 查看当前 TSO 状态
ethtool -k eth0 | grep tcp-segmentation-offload
tcp-segmentation-offload: on

# 启用 TSO
ethtool -K eth0 tso on

# 禁用 TSO(用于对比测试)
ethtool -K eth0 tso off

# 查看网卡驱动和固件版本(确认硬件支持)
ethtool -i eth0
driver: i40e
version: 2.12.6
firmware-version: 7.30 0x80008b4b 1.3347.0

验证 TSO 是否生效,可以通过抓包观察报文大小:

# 在发送端抓包,观察 TCP 段大小
tcpdump -i eth0 -nn -c 100 'tcp' | awk '{print $NF}' | sort -n | uniq -c

# 输出示例(TSO 开启时,单个段可达 MSS 大小,如 1460)
# 但抓包看到的是硬件分割后的段,不会出现 64KB 大包
# 可以通过 /proc/net/stat/ 观察分段统计

4.8 TSO 的性能影响与最佳实践

  • 吞吐量提升: 在 10Gbps 链路上,启用 TSO 后单流 TCP 吞吐量可从 6Gbps 提升至 9.5Gbps 以上。
  • CPU 利用率下降: 发送侧 CPU 占用率可降低 50%~70%,释放核心用于应用处理。
  • 延迟影响: TSO 本身不增加延迟,但大包构造可能导致发送队列积压,建议配合 txqueuelen 调整。
  • 兼容性: 某些老旧网卡或虚拟网卡不支持 TSO,需回退到 GSO。
  • 调试建议: 当出现 TCP 重传异常时,可临时关闭 TSO 进行对比测试,排除硬件分割错误。
最佳实践: 在物理服务器上,始终启用 TSO 和 GSO;在容器或虚拟机中,根据虚拟网卡能力决定是否启用 TSO。对于延迟敏感型应用(如高频交易),可考虑关闭 TSO 以减少报文构造的微突发。