68127">
features:控制 LRO 如何将包送给网络协议栈,其中的 LRO_F_NAPI 表明驱动是 NAPI 兼容的,应该使用 netif_receive_skb() 函数,而 LRO_F_EXTRACT_VLAN_ID 表明驱动支持 VLAN
ip_summed:表明是否需要网络协议栈支持 checksum 校验
ip_summed_aggr:表明聚集起来的大数据包是否需要网络协议栈去支持 checksum 校验
max_desc:表明最大数目的 LRO 描述符,注意,每个 LRO 的描述符描述了一路 TCP 流,所以该值表明了做多同时能处理的 TCP 流的数量
max_aggr:是最大数目的包将被聚集成一个超级数据包
lro_arr:是描述符数组,需要驱动自己提供足够的内存或者在内存不足时处理异常
get_skb_header()/get_frag_header():用于快速定位 IP 或者 TCP 的头,一般驱动只提供其中的一个实现
一般在驱动中收包,使用的函数是 netif_rx 或者 netif_receive_skb,但在支持 LRO 的驱动中,需要使用下面的函数,这两个函数将进来的数据包根据 LRO 描述符进行分类,如果可以进行聚集,则聚集为一个超级数据包,否者直接传递给内核,走正常途径。需要 lro_receive_frags 函数的原因是某些驱动直接将数据包放入了内存页,之后去构造 sk_buff,对于这样的驱动,应该使用下面的接口:
清单 10、LRO 收包函数
void lro_receive_skb(struct net_lro_mgr *lro_mgr, struct sk_buff *skb, void *priv); void lro_receive_frags(struct net_lro_mgr *lro_mgr, struct skb_frag_struct *frags, int len, int true_size, void *priv, __wsum sum);
因为 LRO 需要聚集到 max_aggr 数目的数据包,但有些情况下可能导致延迟比较大,这种情况下,可以在聚集了部分包之后,直接传递给网络协议栈处理,这时可以使用下面的函数,也可以在收到某个特殊的包之后,不经过 LRO,直接传递个网络协议栈:
清单 11、LRO flush 函数
void lro_flush_all(struct net_lro_mgr *lro_mgr); void lro_flush_pkt(struct net_lro_mgr *lro_mgr, struct iphdr *iph, struct tcphdr *tcph);
GRO (Generic Receive Offload)
前面的 LRO 的核心在于:在接收路径上,将多个数据包聚合成一个大的数据包,然后传递给网络协议栈处理,但 LRO 的实现中存在一些瑕疵:
数据包合并可能会破坏一些状态
数据包合并条件过于宽泛,导致某些情况下本来需要区分的数据包也被合并了,这对于路由器是不可接收的
在虚拟化条件下,需要使用桥接功能,但 LRO 使得桥接功能无法使用
实现中,只支持 IPv4 的 TCP 协议
而解决这些问题的办法就是新提出的 GRO(Generic Receive Offload),首先,GRO 的合并条件更加的严格和灵活,并且在设计时,就考虑支持所有的传输协议,因此,后续的驱动,都应该使用 GRO 的接口,而不是 LRO,内核可能在所有先有驱动迁移到 GRO 接口之后将 LRO 从内核中移除。而 Linux 网络子系统的维护者 David S. Miller 就明确指出,现在的网卡驱动,有 2 个功能需要使用,一是使用 NAPI 接口以使得中断缓和 (interrupt mitigation) ,以及简单的互斥,二是使用 GRO 的 NAPI 接口去传递数据包给网路协议栈。
本文来源:不详 作者:佚名