68127">
我们再来看看内核中数据包的分片都有可能在哪些时刻:
在传输协议中,当构造 skb 用于排队的时候
在传输协议中,但是使用了 NETIF_F_GSO 功能,当即将传递个网卡驱动的时候
在驱动程序里,此时驱动支持 TSO 功能 ( 设置了 NETIF_F_TSO 标志 )
对于支持 GSO 的情况,主要使用了情况 2 或者是情况 2.、3,其中情况二是在硬件不支持 TSO 的情况下,而情况 2、3 则是在硬件支持 TSO 的情况下。
代码中是在 dev_hard_start_xmit 函数里调用 dev_gso_segment 执行分片,这样尽量推迟分片的时间以提高性能:
清单 8、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 { …… } …… }
回页首
接收路径上的优化
LRO (Large Receive Offload)
Linux 在 2.6.24 中加入了支持 IPv4 TCP 协议的 LRO (Large Receive Offload) ,它通过将多个 TCP 数据聚合在一个 skb 结构,在稍后的某个时刻作为一个大数据包交付给上层的网络协议栈,以减少上层协议栈处理 skb 的开销,提高系统接收 TCP 数据包的能力。
当然,这一切都需要网卡驱动程序支持。理解 LRO 的工作原理,需要理解 sk_buff 结构体对于负载的存储方式,在内核中,sk_buff 可以有三种方式保存真实的负载:
数据被保存在 skb->data 指向的由 kmalloc 申请的内存缓冲区中,这个数据区通常被称为线性数据区,数据区长度由函数 skb_headlen 给出
数据被保存在紧随 skb 线性数据区尾部的共享结构体 skb_shared_info 中的成员 frags 所表示的内存页面中,skb_frag_t 的数目由 nr_frags 给出,skb_frags_t 中有数据在内存页面中的偏移量和数据区的大小
数据被保存于 skb_shared_info 中的成员 frag_list 所表示的 skb 分片队列中
合并了多个 skb 的超级 skb,能够一次性通过网络协议栈,而不是多次,这对 CPU 负荷的减轻是显然的。
LRO 的核心结构体如下:
清单 9、LRO 的核心结构体
/* * Large Receive Offload (LRO) Manager * * Fields must be set by driver */ struct net_lro_mgr { struct net_device *dev; struct net_lro_stats stats; /* LRO features */ unsigned long features; #define LRO_F_NAPI 1 /* Pass packets to stack via NAPI */ #define LRO_F_EXTRACT_VLAN_ID 2 /* Set flag if VLAN IDs are extracted from received packets and eth protocol is still ETH_P_8021Q */ /* * Set for generated SKBs that are not added to * the frag list in fragmented mode */ u32 ip_summed; u32 ip_summed_aggr; /* Set in aggregated SKBs: CHECKSUM_UNNECESSARY * or CHECKSUM_NONE */ int max_desc; /* Max number of LRO descriptors */ int max_aggr; /* Max number of LRO packets to be aggregated */ int frag_align_pad; /* Padding required to properly align layer 3 * headers in generated skb when using frags */ struct net_lro_desc *lro_arr; /* Array of LRO descriptors */ /* * Optimized driver functions * * get_skb_header: returns tcp and ip header for packet in SKB */ int (*get_skb_header)(struct sk_buff *skb, void **ip_hdr, void **tcpudp_hdr, u64 *hdr_flags, void *priv); /* hdr_flags: */ #define LRO_IPV4 1 /* ip_hdr is IPv4 header */ #define LRO_TCP 2 /* tcpudp_hdr is TCP header */ /* * get_frag_header: returns mac, tcp and ip header for packet in SKB * * @hdr_flags: Indicate what kind of LRO has to be done * (IPv4/IPv6/TCP/UDP) */ int (*get_frag_header)(struct skb_frag_struct *frag, void **mac_hdr, void **ip_hdr, void **tCPUdp_hdr, u64 *hdr_flags, void *priv); };
在该结构体中:
dev:指向支持 LRO 功能的网络设备
stats:包含一些统计信息,用于查看 LRO 功能的运行情况
本文来源:不详 作者:佚名