本文章基于virtio接口及协议介绍TSO(TCP segment offload)功能,其中TSO与CSO(checeksum offload)功能开关有依赖关系,TSO开启时需要CSO开启,CSO可以独立开启。
TSO相关 feature在virtio spec中定义如表1:
表1
bit offset 11 : VIRTIO_NET_F_HOST_TSO4 ;
bit offset 12 : VIRTIO_NET_F_HOST_TSO6 ;
TCP segment 分段条件
对于 ipv4 并且 tcp head 不带 option 字段的报文,mss 计算方法如下:
mss = mtu(1500B) – ip head(20B) – tcp head(20B)=1460B
可以看到当 MTU=1500B 时,MSS=1460B.
当 ip 报文 total length 字段长度大于 MTU 值,即大于 mss+ip head + tcp head 时,需要对 输入报文进行 tcp segment 拆分,ip head 中的 total length 字段如图 1 所示。
图1
对于 ip length 大于 MTU 的报文,如果不做分段处理,报文将会被丢弃。
FPGA角度,对于TSO处理,一般需要有两个通道组成:std通道和 tso 通道;其中 std 通道专门用来计算 cso,而 tso 通道不仅可以进行 tcp segment 拆分,同时也会计算每个 segment cso。 不需要分段 ipv4-tcp 报文分发到 std 通道,分段报文分发到 tso 通道。
TCP segment 分段过程
TCP 分段,数据传输到传输层的时候,受 MSS 限制,将对数据进行分段。每一段分别添加
TCP 首部,IP 首部,MAC 首部:
其中:
数据 1 的长度=MSS。
数据 2 的长度=数据总长度-数据 1 长度
同时,数据 2 的 sequence num 也将增加数据 1 的长度
数据分段过程如下图所示:
图2
上图可以发现,tcp segment 主要功能是将 L4 payload 长度大于 MSS 的包,拆分 L4 payload 长度小于 mss 的多个包,并给每个 segment 的 L4 payload 添加 2 层 3 层 4 层首部,即 MAC, IP, TCP HEAD。拆分之后每个 segment 报文携带的 mac 地址,ip 地址及 tcp 端口端口与拆 分前一致。不同的是 ip 头部中的长度及 crc,以及 tcp 头部中的 seuqence num 不一致。TSO主要功能就是拆分 segment,并且计算新的报文的 length 和 crc。
TCP segment sequence num 计算过程:
下面是根据 tso 功能实测抓取的波形,对应的 tcp segment 拆分,及长度分割。分段前,报文对应 sequence num 以及 ip total length 。
分段前 TCP 包 sequence num 与 ip length,如表3
表3
分段后,TCP每个 segment 对应的 sequence num 及 total length,如表4
表4
其中表4 seq(2) 的 sequence num 计算方法:
sequence num(2) = sequence num(1) + total legth(1) - ip_head(20B) -tcp_head(20B) =
0x5c6a5b9e + 0x5d0 - 0x14 -0x14
= 5C6A 6146
上述分割,是按每个包按最大 1488 分割,这样不会出错,但是可能效率不够高,理论上每个 L4 payload 都分割成 mss 最大值 1460,此时 ip 总包长 1500,再加上 eth mac 及协 议,每个包为 1514 长度。 TCP 重组时,先依次解每一层的头部,到达传输层时,按照之前分段的序列号 seq 进行报文重组。